diff options
| author | 2020-06-03 13:39:42 -0400 | |
|---|---|---|
| committer | 2020-06-03 18:39:42 +0100 | |
| commit | 0f47403d0220757fed189b76e2071e25b1025cb8 (patch) | |
| tree | 879bf72c4476f0a5e0d82da99d7ff2b2276bcaca /src/main/java/cuchaz/enigma/analysis | |
| parent | Fix search dialog hanging for a short time sometimes (#250) (diff) | |
| download | enigma-fork-0f47403d0220757fed189b76e2071e25b1025cb8.tar.gz enigma-fork-0f47403d0220757fed189b76e2071e25b1025cb8.tar.xz enigma-fork-0f47403d0220757fed189b76e2071e25b1025cb8.zip | |
Split GUI code to separate module (#242)
* Split into modules
* Post merge compile fixes
Co-authored-by: modmuss50 <modmuss50@gmail.com>
Diffstat (limited to 'src/main/java/cuchaz/enigma/analysis')
27 files changed, 0 insertions, 2724 deletions
diff --git a/src/main/java/cuchaz/enigma/analysis/Access.java b/src/main/java/cuchaz/enigma/analysis/Access.java deleted file mode 100644 index 82ca669..0000000 --- a/src/main/java/cuchaz/enigma/analysis/Access.java +++ /dev/null | |||
| @@ -1,43 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 15 | |||
| 16 | import java.lang.reflect.Modifier; | ||
| 17 | |||
| 18 | public enum Access { | ||
| 19 | |||
| 20 | PUBLIC, PROTECTED, PACKAGE, PRIVATE; | ||
| 21 | |||
| 22 | public static Access get(AccessFlags flags) { | ||
| 23 | return get(flags.getFlags()); | ||
| 24 | } | ||
| 25 | |||
| 26 | public static Access get(int modifiers) { | ||
| 27 | boolean isPublic = Modifier.isPublic(modifiers); | ||
| 28 | boolean isProtected = Modifier.isProtected(modifiers); | ||
| 29 | boolean isPrivate = Modifier.isPrivate(modifiers); | ||
| 30 | |||
| 31 | if (isPublic && !isProtected && !isPrivate) { | ||
| 32 | return PUBLIC; | ||
| 33 | } else if (!isPublic && isProtected && !isPrivate) { | ||
| 34 | return PROTECTED; | ||
| 35 | } else if (!isPublic && !isProtected && isPrivate) { | ||
| 36 | return PRIVATE; | ||
| 37 | } else if (!isPublic && !isProtected && !isPrivate) { | ||
| 38 | return PACKAGE; | ||
| 39 | } | ||
| 40 | // assume public by default | ||
| 41 | return PUBLIC; | ||
| 42 | } | ||
| 43 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java b/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java deleted file mode 100644 index dc3f553..0000000 --- a/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java +++ /dev/null | |||
| @@ -1,157 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import cuchaz.enigma.api.EnigmaPlugin; | ||
| 4 | import cuchaz.enigma.api.EnigmaPluginContext; | ||
| 5 | import cuchaz.enigma.api.service.JarIndexerService; | ||
| 6 | import cuchaz.enigma.api.service.NameProposalService; | ||
| 7 | import cuchaz.enigma.source.DecompilerService; | ||
| 8 | import cuchaz.enigma.source.Decompilers; | ||
| 9 | import cuchaz.enigma.source.procyon.ProcyonDecompiler; | ||
| 10 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 11 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 12 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 14 | import cuchaz.enigma.utils.Pair; | ||
| 15 | import cuchaz.enigma.utils.Utils; | ||
| 16 | import org.objectweb.asm.ClassReader; | ||
| 17 | import org.objectweb.asm.ClassVisitor; | ||
| 18 | import org.objectweb.asm.FieldVisitor; | ||
| 19 | import org.objectweb.asm.MethodVisitor; | ||
| 20 | import org.objectweb.asm.Opcodes; | ||
| 21 | import org.objectweb.asm.tree.AbstractInsnNode; | ||
| 22 | import org.objectweb.asm.tree.FieldInsnNode; | ||
| 23 | import org.objectweb.asm.tree.InsnList; | ||
| 24 | import org.objectweb.asm.tree.LdcInsnNode; | ||
| 25 | import org.objectweb.asm.tree.MethodInsnNode; | ||
| 26 | import org.objectweb.asm.tree.MethodNode; | ||
| 27 | import org.objectweb.asm.tree.analysis.Analyzer; | ||
| 28 | import org.objectweb.asm.tree.analysis.Frame; | ||
| 29 | import org.objectweb.asm.tree.analysis.SourceInterpreter; | ||
| 30 | import org.objectweb.asm.tree.analysis.SourceValue; | ||
| 31 | |||
| 32 | import java.util.ArrayList; | ||
| 33 | import java.util.HashMap; | ||
| 34 | import java.util.HashSet; | ||
| 35 | import java.util.List; | ||
| 36 | import java.util.Map; | ||
| 37 | import java.util.Optional; | ||
| 38 | import java.util.Set; | ||
| 39 | |||
| 40 | public final class BuiltinPlugin implements EnigmaPlugin { | ||
| 41 | |||
| 42 | public BuiltinPlugin() { | ||
| 43 | } | ||
| 44 | |||
| 45 | @Override | ||
| 46 | public void init(EnigmaPluginContext ctx) { | ||
| 47 | registerEnumNamingService(ctx); | ||
| 48 | registerDecompilerServices(ctx); | ||
| 49 | } | ||
| 50 | |||
| 51 | private void registerEnumNamingService(EnigmaPluginContext ctx) { | ||
| 52 | final Map<Entry<?>, String> names = new HashMap<>(); | ||
| 53 | final EnumFieldNameFindingVisitor visitor = new EnumFieldNameFindingVisitor(names); | ||
| 54 | |||
| 55 | ctx.registerService("enigma:enum_initializer_indexer", JarIndexerService.TYPE, ctx1 -> (classCache, jarIndex) -> classCache.visit(() -> visitor, ClassReader.SKIP_FRAMES)); | ||
| 56 | ctx.registerService("enigma:enum_name_proposer", NameProposalService.TYPE, ctx1 -> (obfEntry, remapper) -> Optional.ofNullable(names.get(obfEntry))); | ||
| 57 | } | ||
| 58 | |||
| 59 | private void registerDecompilerServices(EnigmaPluginContext ctx) { | ||
| 60 | ctx.registerService("enigma:procyon", DecompilerService.TYPE, ctx1 -> Decompilers.PROCYON); | ||
| 61 | ctx.registerService("enigma:cfr", DecompilerService.TYPE, ctx1 -> Decompilers.CFR); | ||
| 62 | } | ||
| 63 | |||
| 64 | private static final class EnumFieldNameFindingVisitor extends ClassVisitor { | ||
| 65 | |||
| 66 | private ClassEntry clazz; | ||
| 67 | private String className; | ||
| 68 | private final Map<Entry<?>, String> mappings; | ||
| 69 | private final Set<Pair<String, String>> enumFields = new HashSet<>(); | ||
| 70 | private final List<MethodNode> classInits = new ArrayList<>(); | ||
| 71 | |||
| 72 | EnumFieldNameFindingVisitor(Map<Entry<?>, String> mappings) { | ||
| 73 | super(Utils.ASM_VERSION); | ||
| 74 | this.mappings = mappings; | ||
| 75 | } | ||
| 76 | |||
| 77 | @Override | ||
| 78 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { | ||
| 79 | super.visit(version, access, name, signature, superName, interfaces); | ||
| 80 | this.className = name; | ||
| 81 | this.clazz = new ClassEntry(name); | ||
| 82 | this.enumFields.clear(); | ||
| 83 | this.classInits.clear(); | ||
| 84 | } | ||
| 85 | |||
| 86 | @Override | ||
| 87 | public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { | ||
| 88 | if ((access & Opcodes.ACC_ENUM) != 0) { | ||
| 89 | if (!enumFields.add(new Pair<>(name, descriptor))) { | ||
| 90 | throw new IllegalArgumentException("Found two enum fields with the same name \"" + name + "\" and desc \"" + descriptor + "\"!"); | ||
| 91 | } | ||
| 92 | } | ||
| 93 | return super.visitField(access, name, descriptor, signature, value); | ||
| 94 | } | ||
| 95 | |||
| 96 | @Override | ||
| 97 | public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { | ||
| 98 | if ("<clinit>".equals(name)) { | ||
| 99 | MethodNode node = new MethodNode(api, access, name, descriptor, signature, exceptions); | ||
| 100 | classInits.add(node); | ||
| 101 | return node; | ||
| 102 | } | ||
| 103 | return super.visitMethod(access, name, descriptor, signature, exceptions); | ||
| 104 | } | ||
| 105 | |||
| 106 | @Override | ||
| 107 | public void visitEnd() { | ||
| 108 | super.visitEnd(); | ||
| 109 | try { | ||
| 110 | collectResults(); | ||
| 111 | } catch (Exception ex) { | ||
| 112 | throw new RuntimeException(ex); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | private void collectResults() throws Exception { | ||
| 117 | String owner = className; | ||
| 118 | Analyzer<SourceValue> analyzer = new Analyzer<>(new SourceInterpreter()); | ||
| 119 | |||
| 120 | for (MethodNode mn : classInits) { | ||
| 121 | Frame<SourceValue>[] frames = analyzer.analyze(className, mn); | ||
| 122 | |||
| 123 | InsnList instrs = mn.instructions; | ||
| 124 | for (int i = 1; i < instrs.size(); i++) { | ||
| 125 | AbstractInsnNode instr1 = instrs.get(i - 1); | ||
| 126 | AbstractInsnNode instr2 = instrs.get(i); | ||
| 127 | String s = null; | ||
| 128 | |||
| 129 | if (instr2.getOpcode() == Opcodes.PUTSTATIC | ||
| 130 | && ((FieldInsnNode) instr2).owner.equals(owner) | ||
| 131 | && enumFields.contains(new Pair<>(((FieldInsnNode) instr2).name, ((FieldInsnNode) instr2).desc)) | ||
| 132 | && instr1.getOpcode() == Opcodes.INVOKESPECIAL | ||
| 133 | && "<init>".equals(((MethodInsnNode) instr1).name)) { | ||
| 134 | |||
| 135 | for (int j = 0; j < frames[i - 1].getStackSize(); j++) { | ||
| 136 | SourceValue sv = frames[i - 1].getStack(j); | ||
| 137 | for (AbstractInsnNode ci : sv.insns) { | ||
| 138 | if (ci instanceof LdcInsnNode && ((LdcInsnNode) ci).cst instanceof String) { | ||
| 139 | //if (s == null || !s.equals(((LdcInsnNode) ci).cst)) { | ||
| 140 | if (s == null) { | ||
| 141 | s = (String) (((LdcInsnNode) ci).cst); | ||
| 142 | // stringsFound++; | ||
| 143 | } | ||
| 144 | } | ||
| 145 | } | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | if (s != null) { | ||
| 150 | mappings.put(new FieldEntry(clazz, ((FieldInsnNode) instr2).name, new TypeDescriptor(((FieldInsnNode) instr2).desc)), s); | ||
| 151 | } | ||
| 152 | // report otherwise? | ||
| 153 | } | ||
| 154 | } | ||
| 155 | } | ||
| 156 | } | ||
| 157 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassCache.java b/src/main/java/cuchaz/enigma/analysis/ClassCache.java deleted file mode 100644 index f694bf3..0000000 --- a/src/main/java/cuchaz/enigma/analysis/ClassCache.java +++ /dev/null | |||
| @@ -1,127 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import com.google.common.cache.Cache; | ||
| 4 | import com.google.common.cache.CacheBuilder; | ||
| 5 | import com.google.common.collect.ImmutableSet; | ||
| 6 | import cuchaz.enigma.ClassProvider; | ||
| 7 | import cuchaz.enigma.ProgressListener; | ||
| 8 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 9 | import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor; | ||
| 10 | import cuchaz.enigma.utils.Utils; | ||
| 11 | import org.objectweb.asm.ClassReader; | ||
| 12 | import org.objectweb.asm.ClassVisitor; | ||
| 13 | import org.objectweb.asm.Opcodes; | ||
| 14 | import org.objectweb.asm.tree.ClassNode; | ||
| 15 | |||
| 16 | import javax.annotation.Nullable; | ||
| 17 | import java.io.IOException; | ||
| 18 | import java.nio.file.FileSystem; | ||
| 19 | import java.nio.file.FileSystems; | ||
| 20 | import java.nio.file.Files; | ||
| 21 | import java.nio.file.Path; | ||
| 22 | import java.util.concurrent.ExecutionException; | ||
| 23 | import java.util.concurrent.TimeUnit; | ||
| 24 | import java.util.function.Supplier; | ||
| 25 | |||
| 26 | public final class ClassCache implements AutoCloseable, ClassProvider { | ||
| 27 | private final FileSystem fileSystem; | ||
| 28 | private final ImmutableSet<String> classNames; | ||
| 29 | |||
| 30 | private final Cache<String, ClassNode> nodeCache = CacheBuilder.newBuilder() | ||
| 31 | .maximumSize(128) | ||
| 32 | .expireAfterAccess(1, TimeUnit.MINUTES) | ||
| 33 | .build(); | ||
| 34 | |||
| 35 | private ClassCache(FileSystem fileSystem, ImmutableSet<String> classNames) { | ||
| 36 | this.fileSystem = fileSystem; | ||
| 37 | this.classNames = classNames; | ||
| 38 | } | ||
| 39 | |||
| 40 | public static ClassCache of(Path jarPath) throws IOException { | ||
| 41 | FileSystem fileSystem = FileSystems.newFileSystem(jarPath, (ClassLoader) null); | ||
| 42 | ImmutableSet<String> classNames = collectClassNames(fileSystem); | ||
| 43 | |||
| 44 | return new ClassCache(fileSystem, classNames); | ||
| 45 | } | ||
| 46 | |||
| 47 | private static ImmutableSet<String> collectClassNames(FileSystem fileSystem) throws IOException { | ||
| 48 | ImmutableSet.Builder<String> classNames = ImmutableSet.builder(); | ||
| 49 | for (Path root : fileSystem.getRootDirectories()) { | ||
| 50 | Files.walk(root).map(Path::toString) | ||
| 51 | .forEach(path -> { | ||
| 52 | if (path.endsWith(".class")) { | ||
| 53 | String name = path.substring(1, path.length() - ".class".length()); | ||
| 54 | classNames.add(name); | ||
| 55 | } | ||
| 56 | }); | ||
| 57 | } | ||
| 58 | |||
| 59 | return classNames.build(); | ||
| 60 | } | ||
| 61 | |||
| 62 | @Nullable | ||
| 63 | @Override | ||
| 64 | public ClassNode getClassNode(String name) { | ||
| 65 | if (!classNames.contains(name)) { | ||
| 66 | return null; | ||
| 67 | } | ||
| 68 | |||
| 69 | try { | ||
| 70 | return nodeCache.get(name, () -> parseNode(name)); | ||
| 71 | } catch (ExecutionException e) { | ||
| 72 | throw new RuntimeException(e); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | private ClassNode parseNode(String name) throws IOException { | ||
| 77 | ClassReader reader = getReader(name); | ||
| 78 | |||
| 79 | ClassNode node = new ClassNode(); | ||
| 80 | |||
| 81 | LocalVariableFixVisitor visitor = new LocalVariableFixVisitor(Utils.ASM_VERSION, node); | ||
| 82 | reader.accept(visitor, 0); | ||
| 83 | |||
| 84 | return node; | ||
| 85 | } | ||
| 86 | |||
| 87 | private ClassReader getReader(String name) throws IOException { | ||
| 88 | Path path = fileSystem.getPath(name + ".class"); | ||
| 89 | byte[] bytes = Files.readAllBytes(path); | ||
| 90 | return new ClassReader(bytes); | ||
| 91 | } | ||
| 92 | |||
| 93 | public int getClassCount() { | ||
| 94 | return classNames.size(); | ||
| 95 | } | ||
| 96 | |||
| 97 | public void visit(Supplier<ClassVisitor> visitorSupplier, int readFlags) { | ||
| 98 | for (String className : classNames) { | ||
| 99 | ClassVisitor visitor = visitorSupplier.get(); | ||
| 100 | |||
| 101 | ClassNode cached = nodeCache.getIfPresent(className); | ||
| 102 | if (cached != null) { | ||
| 103 | cached.accept(visitor); | ||
| 104 | continue; | ||
| 105 | } | ||
| 106 | |||
| 107 | try { | ||
| 108 | ClassReader reader = getReader(className); | ||
| 109 | reader.accept(visitor, readFlags); | ||
| 110 | } catch (IOException e) { | ||
| 111 | System.out.println("Failed to visit class " + className); | ||
| 112 | e.printStackTrace(); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | @Override | ||
| 118 | public void close() throws IOException { | ||
| 119 | this.fileSystem.close(); | ||
| 120 | } | ||
| 121 | |||
| 122 | public JarIndex index(ProgressListener progress) { | ||
| 123 | JarIndex index = JarIndex.empty(); | ||
| 124 | index.indexJar(this, progress); | ||
| 125 | return index; | ||
| 126 | } | ||
| 127 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java deleted file mode 100644 index 0fc44ca..0000000 --- a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java +++ /dev/null | |||
| @@ -1,72 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import cuchaz.enigma.analysis.index.InheritanceIndex; | ||
| 16 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 17 | import cuchaz.enigma.translation.Translator; | ||
| 18 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 20 | |||
| 21 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 22 | import java.util.Collection; | ||
| 23 | import java.util.List; | ||
| 24 | |||
| 25 | public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { | ||
| 26 | private final Translator translator; | ||
| 27 | private final ClassEntry entry; | ||
| 28 | |||
| 29 | public ClassImplementationsTreeNode(Translator translator, ClassEntry entry) { | ||
| 30 | this.translator = translator; | ||
| 31 | this.entry = entry; | ||
| 32 | } | ||
| 33 | |||
| 34 | public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { | ||
| 35 | // is this the node? | ||
| 36 | if (node.entry.equals(entry.getParent())) { | ||
| 37 | return node; | ||
| 38 | } | ||
| 39 | |||
| 40 | // recurse | ||
| 41 | for (int i = 0; i < node.getChildCount(); i++) { | ||
| 42 | ClassImplementationsTreeNode foundNode = findNode((ClassImplementationsTreeNode) node.getChildAt(i), entry); | ||
| 43 | if (foundNode != null) { | ||
| 44 | return foundNode; | ||
| 45 | } | ||
| 46 | } | ||
| 47 | return null; | ||
| 48 | } | ||
| 49 | |||
| 50 | public ClassEntry getClassEntry() { | ||
| 51 | return this.entry; | ||
| 52 | } | ||
| 53 | |||
| 54 | @Override | ||
| 55 | public String toString() { | ||
| 56 | return translator.translate(entry).toString(); | ||
| 57 | } | ||
| 58 | |||
| 59 | public void load(JarIndex index) { | ||
| 60 | // get all method implementations | ||
| 61 | List<ClassImplementationsTreeNode> nodes = Lists.newArrayList(); | ||
| 62 | InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); | ||
| 63 | |||
| 64 | Collection<ClassEntry> inheritors = inheritanceIndex.getChildren(entry); | ||
| 65 | for (ClassEntry inheritor : inheritors) { | ||
| 66 | nodes.add(new ClassImplementationsTreeNode(translator, inheritor)); | ||
| 67 | } | ||
| 68 | |||
| 69 | // add them to this node | ||
| 70 | nodes.forEach(this::add); | ||
| 71 | } | ||
| 72 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java deleted file mode 100644 index 7904c5f..0000000 --- a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java +++ /dev/null | |||
| @@ -1,72 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import cuchaz.enigma.analysis.index.InheritanceIndex; | ||
| 16 | import cuchaz.enigma.translation.Translator; | ||
| 17 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 18 | |||
| 19 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 20 | import java.util.List; | ||
| 21 | |||
| 22 | public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { | ||
| 23 | private final Translator translator; | ||
| 24 | private final ClassEntry obfClassEntry; | ||
| 25 | |||
| 26 | public ClassInheritanceTreeNode(Translator translator, String obfClassName) { | ||
| 27 | this.translator = translator; | ||
| 28 | this.obfClassEntry = new ClassEntry(obfClassName); | ||
| 29 | } | ||
| 30 | |||
| 31 | public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { | ||
| 32 | // is this the node? | ||
| 33 | if (node.getObfClassName().equals(entry.getFullName())) { | ||
| 34 | return node; | ||
| 35 | } | ||
| 36 | |||
| 37 | // recurse | ||
| 38 | for (int i = 0; i < node.getChildCount(); i++) { | ||
| 39 | ClassInheritanceTreeNode foundNode = findNode((ClassInheritanceTreeNode) node.getChildAt(i), entry); | ||
| 40 | if (foundNode != null) { | ||
| 41 | return foundNode; | ||
| 42 | } | ||
| 43 | } | ||
| 44 | return null; | ||
| 45 | } | ||
| 46 | |||
| 47 | public String getObfClassName() { | ||
| 48 | return this.obfClassEntry.getFullName(); | ||
| 49 | } | ||
| 50 | |||
| 51 | @Override | ||
| 52 | public String toString() { | ||
| 53 | return translator.translate(obfClassEntry).getFullName(); | ||
| 54 | } | ||
| 55 | |||
| 56 | public void load(InheritanceIndex ancestries, boolean recurse) { | ||
| 57 | // get all the child nodes | ||
| 58 | List<ClassInheritanceTreeNode> nodes = Lists.newArrayList(); | ||
| 59 | for (ClassEntry inheritor : ancestries.getChildren(this.obfClassEntry)) { | ||
| 60 | nodes.add(new ClassInheritanceTreeNode(translator, inheritor.getFullName())); | ||
| 61 | } | ||
| 62 | |||
| 63 | // add them to this node | ||
| 64 | nodes.forEach(this::add); | ||
| 65 | |||
| 66 | if (recurse) { | ||
| 67 | for (ClassInheritanceTreeNode node : nodes) { | ||
| 68 | node.load(ancestries, true); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java deleted file mode 100644 index 90d8a6c..0000000 --- a/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java +++ /dev/null | |||
| @@ -1,94 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import com.google.common.collect.Sets; | ||
| 15 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 16 | import cuchaz.enigma.analysis.index.ReferenceIndex; | ||
| 17 | import cuchaz.enigma.translation.Translator; | ||
| 18 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 21 | |||
| 22 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 23 | import javax.swing.tree.TreeNode; | ||
| 24 | import java.util.Set; | ||
| 25 | |||
| 26 | public class ClassReferenceTreeNode extends DefaultMutableTreeNode | ||
| 27 | implements ReferenceTreeNode<ClassEntry, MethodDefEntry> { | ||
| 28 | |||
| 29 | private Translator deobfuscatingTranslator; | ||
| 30 | private ClassEntry entry; | ||
| 31 | private EntryReference<ClassEntry, MethodDefEntry> reference; | ||
| 32 | |||
| 33 | public ClassReferenceTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { | ||
| 34 | this.deobfuscatingTranslator = deobfuscatingTranslator; | ||
| 35 | this.entry = entry; | ||
| 36 | this.reference = null; | ||
| 37 | } | ||
| 38 | |||
| 39 | public ClassReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<ClassEntry, MethodDefEntry> reference) { | ||
| 40 | this.deobfuscatingTranslator = deobfuscatingTranslator; | ||
| 41 | this.entry = reference.entry; | ||
| 42 | this.reference = reference; | ||
| 43 | } | ||
| 44 | |||
| 45 | @Override | ||
| 46 | public ClassEntry getEntry() { | ||
| 47 | return this.entry; | ||
| 48 | } | ||
| 49 | |||
| 50 | @Override | ||
| 51 | public EntryReference<ClassEntry, MethodDefEntry> getReference() { | ||
| 52 | return this.reference; | ||
| 53 | } | ||
| 54 | |||
| 55 | @Override | ||
| 56 | public String toString() { | ||
| 57 | if (this.reference != null) { | ||
| 58 | return String.format("%s", this.deobfuscatingTranslator.translate(this.reference.context)); | ||
| 59 | } | ||
| 60 | return this.deobfuscatingTranslator.translate(this.entry).getFullName(); | ||
| 61 | } | ||
| 62 | |||
| 63 | public void load(JarIndex index, boolean recurse) { | ||
| 64 | ReferenceIndex referenceIndex = index.getReferenceIndex(); | ||
| 65 | |||
| 66 | // get all the child nodes | ||
| 67 | for (EntryReference<ClassEntry, MethodDefEntry> reference : referenceIndex.getReferencesToClass(this.entry)) { | ||
| 68 | add(new ClassReferenceTreeNode(this.deobfuscatingTranslator, reference)); | ||
| 69 | } | ||
| 70 | |||
| 71 | if (recurse && this.children != null) { | ||
| 72 | for (Object child : this.children) { | ||
| 73 | if (child instanceof ClassReferenceTreeNode) { | ||
| 74 | ClassReferenceTreeNode node = (ClassReferenceTreeNode) child; | ||
| 75 | |||
| 76 | // don't recurse into ancestor | ||
| 77 | Set<Entry<?>> ancestors = Sets.newHashSet(); | ||
| 78 | TreeNode n = node; | ||
| 79 | while (n.getParent() != null) { | ||
| 80 | n = n.getParent(); | ||
| 81 | if (n instanceof ClassReferenceTreeNode) { | ||
| 82 | ancestors.add(((ClassReferenceTreeNode) n).getEntry()); | ||
| 83 | } | ||
| 84 | } | ||
| 85 | if (ancestors.contains(node.getEntry())) { | ||
| 86 | continue; | ||
| 87 | } | ||
| 88 | |||
| 89 | node.load(index, true); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | } | ||
| 93 | } | ||
| 94 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryReference.java b/src/main/java/cuchaz/enigma/analysis/EntryReference.java deleted file mode 100644 index 2e738c0..0000000 --- a/src/main/java/cuchaz/enigma/analysis/EntryReference.java +++ /dev/null | |||
| @@ -1,140 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import cuchaz.enigma.translation.Translatable; | ||
| 15 | import cuchaz.enigma.translation.Translator; | ||
| 16 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 17 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 18 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 21 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 22 | import cuchaz.enigma.utils.Utils; | ||
| 23 | |||
| 24 | import java.util.Arrays; | ||
| 25 | import java.util.List; | ||
| 26 | |||
| 27 | public class EntryReference<E extends Entry<?>, C extends Entry<?>> implements Translatable { | ||
| 28 | |||
| 29 | private static final List<String> CONSTRUCTOR_NON_NAMES = Arrays.asList("this", "super", "static"); | ||
| 30 | public E entry; | ||
| 31 | public C context; | ||
| 32 | public ReferenceTargetType targetType; | ||
| 33 | |||
| 34 | private boolean sourceName; | ||
| 35 | |||
| 36 | public EntryReference(E entry, String sourceName) { | ||
| 37 | this(entry, sourceName, null); | ||
| 38 | } | ||
| 39 | |||
| 40 | public EntryReference(E entry, String sourceName, C context) { | ||
| 41 | this(entry, sourceName, context, ReferenceTargetType.none()); | ||
| 42 | } | ||
| 43 | |||
| 44 | public EntryReference(E entry, String sourceName, C context, ReferenceTargetType targetType) { | ||
| 45 | if (entry == null) { | ||
| 46 | throw new IllegalArgumentException("Entry cannot be null!"); | ||
| 47 | } | ||
| 48 | |||
| 49 | this.entry = entry; | ||
| 50 | this.context = context; | ||
| 51 | this.targetType = targetType; | ||
| 52 | |||
| 53 | this.sourceName = sourceName != null && !sourceName.isEmpty(); | ||
| 54 | if (entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor() && CONSTRUCTOR_NON_NAMES.contains(sourceName)) { | ||
| 55 | this.sourceName = false; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | public EntryReference(E entry, C context, EntryReference<E, C> other) { | ||
| 60 | this.entry = entry; | ||
| 61 | this.context = context; | ||
| 62 | this.sourceName = other.sourceName; | ||
| 63 | this.targetType = other.targetType; | ||
| 64 | } | ||
| 65 | |||
| 66 | public ClassEntry getLocationClassEntry() { | ||
| 67 | if (context != null) { | ||
| 68 | return context.getContainingClass(); | ||
| 69 | } | ||
| 70 | return entry.getContainingClass(); | ||
| 71 | } | ||
| 72 | |||
| 73 | public boolean isNamed() { | ||
| 74 | return this.sourceName; | ||
| 75 | } | ||
| 76 | |||
| 77 | public Entry<?> getNameableEntry() { | ||
| 78 | if (entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor()) { | ||
| 79 | // renaming a constructor really means renaming the class | ||
| 80 | return entry.getContainingClass(); | ||
| 81 | } | ||
| 82 | return entry; | ||
| 83 | } | ||
| 84 | |||
| 85 | public String getNameableName() { | ||
| 86 | return getNameableEntry().getName(); | ||
| 87 | } | ||
| 88 | |||
| 89 | @Override | ||
| 90 | public int hashCode() { | ||
| 91 | if (context != null) { | ||
| 92 | return Utils.combineHashesOrdered(entry.hashCode(), context.hashCode()); | ||
| 93 | } | ||
| 94 | return entry.hashCode(); | ||
| 95 | } | ||
| 96 | |||
| 97 | @Override | ||
| 98 | public boolean equals(Object other) { | ||
| 99 | return other instanceof EntryReference && equals((EntryReference<?, ?>) other); | ||
| 100 | } | ||
| 101 | |||
| 102 | public boolean equals(EntryReference<?, ?> other) { | ||
| 103 | // check entry first | ||
| 104 | boolean isEntrySame = entry.equals(other.entry); | ||
| 105 | if (!isEntrySame) { | ||
| 106 | return false; | ||
| 107 | } | ||
| 108 | |||
| 109 | // check caller | ||
| 110 | if (context == null && other.context == null) { | ||
| 111 | return true; | ||
| 112 | } else if (context != null && other.context != null) { | ||
| 113 | return context.equals(other.context); | ||
| 114 | } | ||
| 115 | return false; | ||
| 116 | } | ||
| 117 | |||
| 118 | @Override | ||
| 119 | public String toString() { | ||
| 120 | StringBuilder buf = new StringBuilder(); | ||
| 121 | buf.append(entry); | ||
| 122 | |||
| 123 | if (context != null) { | ||
| 124 | buf.append(" called from "); | ||
| 125 | buf.append(context); | ||
| 126 | } | ||
| 127 | |||
| 128 | if (targetType != null && targetType.getKind() != ReferenceTargetType.Kind.NONE) { | ||
| 129 | buf.append(" on target of type "); | ||
| 130 | buf.append(targetType); | ||
| 131 | } | ||
| 132 | |||
| 133 | return buf.toString(); | ||
| 134 | } | ||
| 135 | |||
| 136 | @Override | ||
| 137 | public Translatable translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) { | ||
| 138 | return new EntryReference<>(translator.translate(entry), translator.translate(context), this); | ||
| 139 | } | ||
| 140 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java deleted file mode 100644 index 4beab7f..0000000 --- a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java +++ /dev/null | |||
| @@ -1,83 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 15 | import cuchaz.enigma.analysis.index.ReferenceIndex; | ||
| 16 | import cuchaz.enigma.translation.Translator; | ||
| 17 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 18 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 20 | |||
| 21 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 22 | |||
| 23 | public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, MethodDefEntry> { | ||
| 24 | |||
| 25 | private final Translator translator; | ||
| 26 | private FieldEntry entry; | ||
| 27 | private EntryReference<FieldEntry, MethodDefEntry> reference; | ||
| 28 | |||
| 29 | public FieldReferenceTreeNode(Translator translator, FieldEntry entry) { | ||
| 30 | this.translator = translator; | ||
| 31 | this.entry = entry; | ||
| 32 | this.reference = null; | ||
| 33 | } | ||
| 34 | |||
| 35 | private FieldReferenceTreeNode(Translator translator, EntryReference<FieldEntry, MethodDefEntry> reference) { | ||
| 36 | this.translator = translator; | ||
| 37 | this.entry = reference.entry; | ||
| 38 | this.reference = reference; | ||
| 39 | } | ||
| 40 | |||
| 41 | @Override | ||
| 42 | public FieldEntry getEntry() { | ||
| 43 | return this.entry; | ||
| 44 | } | ||
| 45 | |||
| 46 | @Override | ||
| 47 | public EntryReference<FieldEntry, MethodDefEntry> getReference() { | ||
| 48 | return this.reference; | ||
| 49 | } | ||
| 50 | |||
| 51 | @Override | ||
| 52 | public String toString() { | ||
| 53 | if (this.reference != null) { | ||
| 54 | return String.format("%s", translator.translate(this.reference.context)); | ||
| 55 | } | ||
| 56 | return translator.translate(entry).toString(); | ||
| 57 | } | ||
| 58 | |||
| 59 | public void load(JarIndex index, boolean recurse) { | ||
| 60 | ReferenceIndex referenceIndex = index.getReferenceIndex(); | ||
| 61 | |||
| 62 | // get all the child nodes | ||
| 63 | if (this.reference == null) { | ||
| 64 | for (EntryReference<FieldEntry, MethodDefEntry> reference : referenceIndex.getReferencesToField(this.entry)) { | ||
| 65 | add(new FieldReferenceTreeNode(translator, reference)); | ||
| 66 | } | ||
| 67 | } else { | ||
| 68 | for (EntryReference<MethodEntry, MethodDefEntry> reference : referenceIndex.getReferencesToMethod(this.reference.context)) { | ||
| 69 | add(new MethodReferenceTreeNode(translator, reference)); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | if (recurse && children != null) { | ||
| 74 | for (Object node : children) { | ||
| 75 | if (node instanceof MethodReferenceTreeNode) { | ||
| 76 | ((MethodReferenceTreeNode) node).load(index, true, false); | ||
| 77 | } else if (node instanceof FieldReferenceTreeNode) { | ||
| 78 | ((FieldReferenceTreeNode) node).load(index, true); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java b/src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java deleted file mode 100644 index 80a7154..0000000 --- a/src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java +++ /dev/null | |||
| @@ -1,154 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import cuchaz.enigma.analysis.index.EntryIndex; | ||
| 4 | import cuchaz.enigma.analysis.index.InheritanceIndex; | ||
| 5 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 6 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | ||
| 7 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 8 | import cuchaz.enigma.utils.Utils; | ||
| 9 | import org.objectweb.asm.Type; | ||
| 10 | import org.objectweb.asm.tree.analysis.BasicValue; | ||
| 11 | import org.objectweb.asm.tree.analysis.SimpleVerifier; | ||
| 12 | |||
| 13 | import java.util.Set; | ||
| 14 | |||
| 15 | public class IndexSimpleVerifier extends SimpleVerifier { | ||
| 16 | private static final Type OBJECT_TYPE = Type.getType("Ljava/lang/Object;"); | ||
| 17 | private final EntryIndex entryIndex; | ||
| 18 | private final InheritanceIndex inheritanceIndex; | ||
| 19 | |||
| 20 | public IndexSimpleVerifier(EntryIndex entryIndex, InheritanceIndex inheritanceIndex) { | ||
| 21 | super(Utils.ASM_VERSION, null, null, null, false); | ||
| 22 | this.entryIndex = entryIndex; | ||
| 23 | this.inheritanceIndex = inheritanceIndex; | ||
| 24 | } | ||
| 25 | |||
| 26 | @Override | ||
| 27 | protected boolean isSubTypeOf(BasicValue value, BasicValue expected) { | ||
| 28 | Type expectedType = expected.getType(); | ||
| 29 | Type type = value.getType(); | ||
| 30 | switch (expectedType.getSort()) { | ||
| 31 | case Type.INT: | ||
| 32 | case Type.FLOAT: | ||
| 33 | case Type.LONG: | ||
| 34 | case Type.DOUBLE: | ||
| 35 | return type.equals(expectedType); | ||
| 36 | case Type.ARRAY: | ||
| 37 | case Type.OBJECT: | ||
| 38 | if (type.equals(NULL_TYPE)) { | ||
| 39 | return true; | ||
| 40 | } else if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { | ||
| 41 | if (isAssignableFrom(expectedType, type)) { | ||
| 42 | return true; | ||
| 43 | } else if (isInterface(expectedType)) { | ||
| 44 | return isAssignableFrom(OBJECT_TYPE, type); | ||
| 45 | } else { | ||
| 46 | return false; | ||
| 47 | } | ||
| 48 | } else { | ||
| 49 | return false; | ||
| 50 | } | ||
| 51 | default: | ||
| 52 | throw new AssertionError(); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | @Override | ||
| 57 | protected boolean isInterface(Type type) { | ||
| 58 | AccessFlags classAccess = entryIndex.getClassAccess(new ClassEntry(type.getInternalName())); | ||
| 59 | if (classAccess != null) { | ||
| 60 | return classAccess.isInterface(); | ||
| 61 | } | ||
| 62 | |||
| 63 | Class<?> clazz = getClass(type); | ||
| 64 | if (clazz != null) { | ||
| 65 | return clazz.isInterface(); | ||
| 66 | } | ||
| 67 | |||
| 68 | return false; | ||
| 69 | } | ||
| 70 | |||
| 71 | @Override | ||
| 72 | protected Type getSuperClass(Type type) { | ||
| 73 | ClassDefEntry definition = entryIndex.getDefinition(new ClassEntry(type.getInternalName())); | ||
| 74 | if (definition != null) { | ||
| 75 | return Type.getType('L' + definition.getSuperClass().getFullName() + ';'); | ||
| 76 | } | ||
| 77 | |||
| 78 | Class<?> clazz = getClass(type); | ||
| 79 | if (clazz != null) { | ||
| 80 | return Type.getType(clazz.getSuperclass()); | ||
| 81 | } | ||
| 82 | |||
| 83 | return OBJECT_TYPE; | ||
| 84 | } | ||
| 85 | |||
| 86 | @Override | ||
| 87 | protected boolean isAssignableFrom(Type type1, Type type2) { | ||
| 88 | if (type1.equals(type2)) { | ||
| 89 | return true; | ||
| 90 | } | ||
| 91 | |||
| 92 | if (type2.equals(NULL_TYPE)) { | ||
| 93 | return true; | ||
| 94 | } | ||
| 95 | |||
| 96 | if (type1.getSort() == Type.ARRAY) { | ||
| 97 | return type2.getSort() == Type.ARRAY && isAssignableFrom(Type.getType(type1.getDescriptor().substring(1)), Type.getType(type2.getDescriptor().substring(1))); | ||
| 98 | } | ||
| 99 | |||
| 100 | if (type2.getSort() == Type.ARRAY) { | ||
| 101 | return type1.equals(OBJECT_TYPE); | ||
| 102 | } | ||
| 103 | |||
| 104 | if (type1.getSort() == Type.OBJECT && type2.getSort() == Type.OBJECT) { | ||
| 105 | if (type1.equals(OBJECT_TYPE)) { | ||
| 106 | return true; | ||
| 107 | } | ||
| 108 | |||
| 109 | ClassEntry class1 = new ClassEntry(type1.getInternalName()); | ||
| 110 | ClassEntry class2 = new ClassEntry(type2.getInternalName()); | ||
| 111 | |||
| 112 | if (entryIndex.hasClass(class1) && entryIndex.hasClass(class2)) { | ||
| 113 | return inheritanceIndex.getAncestors(class2).contains(class1); | ||
| 114 | } | ||
| 115 | |||
| 116 | Class<?> class1Class = getClass(Type.getType('L' + class1.getFullName() + ';')); | ||
| 117 | Class<?> class2Class = getClass(Type.getType('L' + class2.getFullName() + ';')); | ||
| 118 | |||
| 119 | if (class1Class == null) { | ||
| 120 | return true; // missing classes to find out | ||
| 121 | } | ||
| 122 | |||
| 123 | if (class2Class != null) { | ||
| 124 | return class1Class.isAssignableFrom(class2Class); | ||
| 125 | } | ||
| 126 | |||
| 127 | if (entryIndex.hasClass(class2)) { | ||
| 128 | Set<ClassEntry> ancestors = inheritanceIndex.getAncestors(class2); | ||
| 129 | |||
| 130 | for (ClassEntry ancestorEntry : ancestors) { | ||
| 131 | Class<?> ancestor = getClass(Type.getType('L' + ancestorEntry.getFullName() + ';')); | ||
| 132 | if (ancestor == null || class1Class.isAssignableFrom(ancestor)) { | ||
| 133 | return true; // assignable, or missing classes to find out | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | return false; | ||
| 138 | } | ||
| 139 | |||
| 140 | return true; // missing classes to find out | ||
| 141 | } | ||
| 142 | |||
| 143 | return false; | ||
| 144 | } | ||
| 145 | |||
| 146 | @Override | ||
| 147 | protected final Class<?> getClass(Type type) { | ||
| 148 | try { | ||
| 149 | return Class.forName(type.getSort() == Type.ARRAY ? type.getDescriptor().replace('/', '.') : type.getClassName(), false, null); | ||
| 150 | } catch (ClassNotFoundException e) { | ||
| 151 | return null; | ||
| 152 | } | ||
| 153 | } | ||
| 154 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java b/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java deleted file mode 100644 index 0c2dfd7..0000000 --- a/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java +++ /dev/null | |||
| @@ -1,74 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import com.google.common.collect.Lists; | ||
| 4 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 5 | import cuchaz.enigma.translation.Translator; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 7 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | ||
| 8 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 9 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 10 | |||
| 11 | import java.util.Collection; | ||
| 12 | import java.util.List; | ||
| 13 | |||
| 14 | public class IndexTreeBuilder { | ||
| 15 | private final JarIndex index; | ||
| 16 | |||
| 17 | public IndexTreeBuilder(JarIndex index) { | ||
| 18 | this.index = index; | ||
| 19 | } | ||
| 20 | |||
| 21 | public ClassInheritanceTreeNode buildClassInheritance(Translator translator, ClassEntry obfClassEntry) { | ||
| 22 | // get the root node | ||
| 23 | List<String> ancestry = Lists.newArrayList(); | ||
| 24 | ancestry.add(obfClassEntry.getFullName()); | ||
| 25 | for (ClassEntry classEntry : index.getInheritanceIndex().getAncestors(obfClassEntry)) { | ||
| 26 | ancestry.add(classEntry.getFullName()); | ||
| 27 | } | ||
| 28 | |||
| 29 | ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(translator, ancestry.get(ancestry.size() - 1)); | ||
| 30 | |||
| 31 | // expand all children recursively | ||
| 32 | rootNode.load(index.getInheritanceIndex(), true); | ||
| 33 | |||
| 34 | return rootNode; | ||
| 35 | } | ||
| 36 | |||
| 37 | public ClassImplementationsTreeNode buildClassImplementations(Translator translator, ClassEntry obfClassEntry) { | ||
| 38 | if (index.getInheritanceIndex().isParent(obfClassEntry)) { | ||
| 39 | ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(translator, obfClassEntry); | ||
| 40 | node.load(index); | ||
| 41 | return node; | ||
| 42 | } | ||
| 43 | return null; | ||
| 44 | } | ||
| 45 | |||
| 46 | public MethodInheritanceTreeNode buildMethodInheritance(Translator translator, MethodEntry obfMethodEntry) { | ||
| 47 | MethodEntry resolvedEntry = index.getEntryResolver().resolveFirstEntry(obfMethodEntry, ResolutionStrategy.RESOLVE_ROOT); | ||
| 48 | |||
| 49 | // make a root node at the base | ||
| 50 | MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( | ||
| 51 | translator, resolvedEntry, | ||
| 52 | index.getEntryIndex().hasMethod(resolvedEntry) | ||
| 53 | ); | ||
| 54 | |||
| 55 | // expand the full tree | ||
| 56 | rootNode.load(index); | ||
| 57 | |||
| 58 | return rootNode; | ||
| 59 | } | ||
| 60 | |||
| 61 | public List<MethodImplementationsTreeNode> buildMethodImplementations(Translator translator, MethodEntry obfMethodEntry) { | ||
| 62 | EntryResolver resolver = index.getEntryResolver(); | ||
| 63 | Collection<MethodEntry> resolvedEntries = resolver.resolveEntry(obfMethodEntry, ResolutionStrategy.RESOLVE_ROOT); | ||
| 64 | |||
| 65 | List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); | ||
| 66 | for (MethodEntry resolvedEntry : resolvedEntries) { | ||
| 67 | MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(translator, resolvedEntry); | ||
| 68 | node.load(index); | ||
| 69 | nodes.add(node); | ||
| 70 | } | ||
| 71 | |||
| 72 | return nodes; | ||
| 73 | } | ||
| 74 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/InterpreterPair.java b/src/main/java/cuchaz/enigma/analysis/InterpreterPair.java deleted file mode 100644 index 8a1c238..0000000 --- a/src/main/java/cuchaz/enigma/analysis/InterpreterPair.java +++ /dev/null | |||
| @@ -1,131 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import cuchaz.enigma.utils.Utils; | ||
| 4 | import org.objectweb.asm.Opcodes; | ||
| 5 | import org.objectweb.asm.Type; | ||
| 6 | import org.objectweb.asm.tree.AbstractInsnNode; | ||
| 7 | import org.objectweb.asm.tree.analysis.AnalyzerException; | ||
| 8 | import org.objectweb.asm.tree.analysis.Interpreter; | ||
| 9 | import org.objectweb.asm.tree.analysis.Value; | ||
| 10 | |||
| 11 | import java.util.List; | ||
| 12 | import java.util.Objects; | ||
| 13 | import java.util.stream.Collectors; | ||
| 14 | |||
| 15 | public class InterpreterPair<V extends Value, W extends Value> extends Interpreter<InterpreterPair.PairValue<V, W>> { | ||
| 16 | private final Interpreter<V> left; | ||
| 17 | private final Interpreter<W> right; | ||
| 18 | |||
| 19 | public InterpreterPair(Interpreter<V> left, Interpreter<W> right) { | ||
| 20 | super(Utils.ASM_VERSION); | ||
| 21 | this.left = left; | ||
| 22 | this.right = right; | ||
| 23 | } | ||
| 24 | |||
| 25 | @Override | ||
| 26 | public PairValue<V, W> newValue(Type type) { | ||
| 27 | return pair( | ||
| 28 | left.newValue(type), | ||
| 29 | right.newValue(type) | ||
| 30 | ); | ||
| 31 | } | ||
| 32 | |||
| 33 | @Override | ||
| 34 | public PairValue<V, W> newOperation(AbstractInsnNode insn) throws AnalyzerException { | ||
| 35 | return pair( | ||
| 36 | left.newOperation(insn), | ||
| 37 | right.newOperation(insn) | ||
| 38 | ); | ||
| 39 | } | ||
| 40 | |||
| 41 | @Override | ||
| 42 | public PairValue<V, W> copyOperation(AbstractInsnNode insn, PairValue<V, W> value) throws AnalyzerException { | ||
| 43 | return pair( | ||
| 44 | left.copyOperation(insn, value.left), | ||
| 45 | right.copyOperation(insn, value.right) | ||
| 46 | ); | ||
| 47 | } | ||
| 48 | |||
| 49 | @Override | ||
| 50 | public PairValue<V, W> unaryOperation(AbstractInsnNode insn, PairValue<V, W> value) throws AnalyzerException { | ||
| 51 | return pair( | ||
| 52 | left.unaryOperation(insn, value.left), | ||
| 53 | right.unaryOperation(insn, value.right) | ||
| 54 | ); | ||
| 55 | } | ||
| 56 | |||
| 57 | @Override | ||
| 58 | public PairValue<V, W> binaryOperation(AbstractInsnNode insn, PairValue<V, W> value1, PairValue<V, W> value2) throws AnalyzerException { | ||
| 59 | return pair( | ||
| 60 | left.binaryOperation(insn, value1.left, value2.left), | ||
| 61 | right.binaryOperation(insn, value1.right, value2.right) | ||
| 62 | ); | ||
| 63 | } | ||
| 64 | |||
| 65 | @Override | ||
| 66 | public PairValue<V, W> ternaryOperation(AbstractInsnNode insn, PairValue<V, W> value1, PairValue<V, W> value2, PairValue<V, W> value3) throws AnalyzerException { | ||
| 67 | return pair( | ||
| 68 | left.ternaryOperation(insn, value1.left, value2.left, value3.left), | ||
| 69 | right.ternaryOperation(insn, value1.right, value2.right, value3.right) | ||
| 70 | ); | ||
| 71 | } | ||
| 72 | |||
| 73 | @Override | ||
| 74 | public PairValue<V, W> naryOperation(AbstractInsnNode insn, List<? extends PairValue<V, W>> values) throws AnalyzerException { | ||
| 75 | return pair( | ||
| 76 | left.naryOperation(insn, values.stream().map(v -> v.left).collect(Collectors.toList())), | ||
| 77 | right.naryOperation(insn, values.stream().map(v -> v.right).collect(Collectors.toList())) | ||
| 78 | ); | ||
| 79 | } | ||
| 80 | |||
| 81 | @Override | ||
| 82 | public void returnOperation(AbstractInsnNode insn, PairValue<V, W> value, PairValue<V, W> expected) throws AnalyzerException { | ||
| 83 | left.returnOperation(insn, value.left, expected.left); | ||
| 84 | right.returnOperation(insn, value.right, expected.right); | ||
| 85 | } | ||
| 86 | |||
| 87 | @Override | ||
| 88 | public PairValue<V, W> merge(PairValue<V, W> value1, PairValue<V, W> value2) { | ||
| 89 | return pair( | ||
| 90 | left.merge(value1.left, value2.left), | ||
| 91 | right.merge(value1.right, value2.right) | ||
| 92 | ); | ||
| 93 | } | ||
| 94 | |||
| 95 | private PairValue<V, W> pair(V left, W right) { | ||
| 96 | if (left == null && right == null) { | ||
| 97 | return null; | ||
| 98 | } | ||
| 99 | |||
| 100 | return new PairValue<>(left, right); | ||
| 101 | } | ||
| 102 | |||
| 103 | public static final class PairValue<V extends Value, W extends Value> implements Value { | ||
| 104 | public final V left; | ||
| 105 | public final W right; | ||
| 106 | |||
| 107 | public PairValue(V left, W right) { | ||
| 108 | if (left == null && right == null) { | ||
| 109 | throw new IllegalArgumentException("should use null rather than pair of nulls"); | ||
| 110 | } | ||
| 111 | |||
| 112 | this.left = left; | ||
| 113 | this.right = right; | ||
| 114 | } | ||
| 115 | |||
| 116 | @Override | ||
| 117 | public boolean equals(Object o) { | ||
| 118 | return o instanceof InterpreterPair.PairValue && Objects.equals(left, ((PairValue) o).left) && Objects.equals(right, ((PairValue) o).right); | ||
| 119 | } | ||
| 120 | |||
| 121 | @Override | ||
| 122 | public int hashCode() { | ||
| 123 | return left.hashCode() * 31 + right.hashCode(); | ||
| 124 | } | ||
| 125 | |||
| 126 | @Override | ||
| 127 | public int getSize() { | ||
| 128 | return (left == null ? right : left).getSize(); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java deleted file mode 100644 index b09f7ac..0000000 --- a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java +++ /dev/null | |||
| @@ -1,85 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import cuchaz.enigma.analysis.index.EntryIndex; | ||
| 16 | import cuchaz.enigma.analysis.index.InheritanceIndex; | ||
| 17 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 18 | import cuchaz.enigma.translation.Translator; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 21 | |||
| 22 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 23 | import java.util.Collection; | ||
| 24 | import java.util.List; | ||
| 25 | |||
| 26 | public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { | ||
| 27 | |||
| 28 | private final Translator translator; | ||
| 29 | private MethodEntry entry; | ||
| 30 | |||
| 31 | public MethodImplementationsTreeNode(Translator translator, MethodEntry entry) { | ||
| 32 | this.translator = translator; | ||
| 33 | if (entry == null) { | ||
| 34 | throw new IllegalArgumentException("Entry cannot be null!"); | ||
| 35 | } | ||
| 36 | |||
| 37 | this.entry = entry; | ||
| 38 | } | ||
| 39 | |||
| 40 | public static MethodImplementationsTreeNode findNode(MethodImplementationsTreeNode node, MethodEntry entry) { | ||
| 41 | // is this the node? | ||
| 42 | if (node.getMethodEntry().equals(entry)) { | ||
| 43 | return node; | ||
| 44 | } | ||
| 45 | |||
| 46 | // recurse | ||
| 47 | for (int i = 0; i < node.getChildCount(); i++) { | ||
| 48 | MethodImplementationsTreeNode foundNode = findNode((MethodImplementationsTreeNode) node.getChildAt(i), entry); | ||
| 49 | if (foundNode != null) { | ||
| 50 | return foundNode; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | return null; | ||
| 54 | } | ||
| 55 | |||
| 56 | public MethodEntry getMethodEntry() { | ||
| 57 | return this.entry; | ||
| 58 | } | ||
| 59 | |||
| 60 | @Override | ||
| 61 | public String toString() { | ||
| 62 | MethodEntry translatedEntry = translator.translate(entry); | ||
| 63 | String className = translatedEntry.getParent().getFullName(); | ||
| 64 | String methodName = translatedEntry.getName(); | ||
| 65 | return className + "." + methodName + "()"; | ||
| 66 | } | ||
| 67 | |||
| 68 | public void load(JarIndex index) { | ||
| 69 | // get all method implementations | ||
| 70 | List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); | ||
| 71 | EntryIndex entryIndex = index.getEntryIndex(); | ||
| 72 | InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); | ||
| 73 | |||
| 74 | Collection<ClassEntry> descendants = inheritanceIndex.getDescendants(entry.getParent()); | ||
| 75 | for (ClassEntry inheritor : descendants) { | ||
| 76 | MethodEntry methodEntry = entry.withParent(inheritor); | ||
| 77 | if (entryIndex.hasMethod(methodEntry)) { | ||
| 78 | nodes.add(new MethodImplementationsTreeNode(translator, methodEntry)); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | // add them to this node | ||
| 83 | nodes.forEach(this::add); | ||
| 84 | } | ||
| 85 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java deleted file mode 100644 index e77b5cc..0000000 --- a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java +++ /dev/null | |||
| @@ -1,95 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import cuchaz.enigma.analysis.index.EntryIndex; | ||
| 15 | import cuchaz.enigma.analysis.index.InheritanceIndex; | ||
| 16 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 17 | import cuchaz.enigma.translation.Translator; | ||
| 18 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 20 | |||
| 21 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 22 | |||
| 23 | public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { | ||
| 24 | |||
| 25 | private final Translator translator; | ||
| 26 | private MethodEntry entry; | ||
| 27 | private boolean implemented; | ||
| 28 | |||
| 29 | public MethodInheritanceTreeNode(Translator translator, MethodEntry entry, boolean implemented) { | ||
| 30 | this.translator = translator; | ||
| 31 | this.entry = entry; | ||
| 32 | this.implemented = implemented; | ||
| 33 | } | ||
| 34 | |||
| 35 | public static MethodInheritanceTreeNode findNode(MethodInheritanceTreeNode node, MethodEntry entry) { | ||
| 36 | // is this the node? | ||
| 37 | if (node.getMethodEntry().equals(entry)) { | ||
| 38 | return node; | ||
| 39 | } | ||
| 40 | |||
| 41 | // recurse | ||
| 42 | for (int i = 0; i < node.getChildCount(); i++) { | ||
| 43 | MethodInheritanceTreeNode foundNode = findNode((MethodInheritanceTreeNode) node.getChildAt(i), entry); | ||
| 44 | if (foundNode != null) { | ||
| 45 | return foundNode; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | return null; | ||
| 49 | } | ||
| 50 | |||
| 51 | public MethodEntry getMethodEntry() { | ||
| 52 | return this.entry; | ||
| 53 | } | ||
| 54 | |||
| 55 | public boolean isImplemented() { | ||
| 56 | return this.implemented; | ||
| 57 | } | ||
| 58 | |||
| 59 | @Override | ||
| 60 | public String toString() { | ||
| 61 | MethodEntry translatedEntry = translator.translate(entry); | ||
| 62 | String className = translatedEntry.getContainingClass().getFullName(); | ||
| 63 | |||
| 64 | if (!this.implemented) { | ||
| 65 | return className; | ||
| 66 | } else { | ||
| 67 | String methodName = translatedEntry.getName(); | ||
| 68 | return className + "." + methodName + "()"; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Returns true if there is sub-node worthy to display. | ||
| 74 | */ | ||
| 75 | public boolean load(JarIndex index) { | ||
| 76 | // get all the child nodes | ||
| 77 | EntryIndex entryIndex = index.getEntryIndex(); | ||
| 78 | InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); | ||
| 79 | |||
| 80 | boolean ret = false; | ||
| 81 | for (ClassEntry inheritorEntry : inheritanceIndex.getChildren(this.entry.getParent())) { | ||
| 82 | MethodEntry methodEntry = new MethodEntry(inheritorEntry, this.entry.getName(), this.entry.getDesc()); | ||
| 83 | |||
| 84 | MethodInheritanceTreeNode node = new MethodInheritanceTreeNode(translator, methodEntry, entryIndex.hasMethod(methodEntry)); | ||
| 85 | boolean childOverride = node.load(index); | ||
| 86 | |||
| 87 | if (childOverride || node.implemented) { | ||
| 88 | this.add(node); | ||
| 89 | ret = true; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | return ret; | ||
| 94 | } | ||
| 95 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java b/src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java deleted file mode 100644 index 8117103..0000000 --- a/src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java +++ /dev/null | |||
| @@ -1,19 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import org.objectweb.asm.tree.MethodNode; | ||
| 4 | |||
| 5 | import java.util.function.Consumer; | ||
| 6 | |||
| 7 | public class MethodNodeWithAction extends MethodNode { | ||
| 8 | private final Consumer<MethodNode> action; | ||
| 9 | |||
| 10 | public MethodNodeWithAction(int api, int access, String name, String descriptor, String signature, String[] exceptions, Consumer<MethodNode> action) { | ||
| 11 | super(api, access, name, descriptor, signature, exceptions); | ||
| 12 | this.action = action; | ||
| 13 | } | ||
| 14 | |||
| 15 | @Override | ||
| 16 | public void visitEnd() { | ||
| 17 | action.accept(this); | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java deleted file mode 100644 index 8995eb5..0000000 --- a/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java +++ /dev/null | |||
| @@ -1,113 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import com.google.common.collect.Sets; | ||
| 15 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 16 | import cuchaz.enigma.analysis.index.ReferenceIndex; | ||
| 17 | import cuchaz.enigma.translation.Translator; | ||
| 18 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 21 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 22 | |||
| 23 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 24 | import javax.swing.tree.TreeNode; | ||
| 25 | import java.util.ArrayList; | ||
| 26 | import java.util.Collection; | ||
| 27 | import java.util.Set; | ||
| 28 | |||
| 29 | public class MethodReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<MethodEntry, MethodDefEntry> { | ||
| 30 | |||
| 31 | private final Translator translator; | ||
| 32 | private MethodEntry entry; | ||
| 33 | private EntryReference<MethodEntry, MethodDefEntry> reference; | ||
| 34 | |||
| 35 | public MethodReferenceTreeNode(Translator translator, MethodEntry entry) { | ||
| 36 | this.translator = translator; | ||
| 37 | this.entry = entry; | ||
| 38 | this.reference = null; | ||
| 39 | } | ||
| 40 | |||
| 41 | public MethodReferenceTreeNode(Translator translator, EntryReference<MethodEntry, MethodDefEntry> reference) { | ||
| 42 | this.translator = translator; | ||
| 43 | this.entry = reference.entry; | ||
| 44 | this.reference = reference; | ||
| 45 | } | ||
| 46 | |||
| 47 | @Override | ||
| 48 | public MethodEntry getEntry() { | ||
| 49 | return this.entry; | ||
| 50 | } | ||
| 51 | |||
| 52 | @Override | ||
| 53 | public EntryReference<MethodEntry, MethodDefEntry> getReference() { | ||
| 54 | return this.reference; | ||
| 55 | } | ||
| 56 | |||
| 57 | @Override | ||
| 58 | public String toString() { | ||
| 59 | if (this.reference != null) { | ||
| 60 | return String.format("%s", translator.translate(this.reference.context)); | ||
| 61 | } | ||
| 62 | return translator.translate(this.entry).getName(); | ||
| 63 | } | ||
| 64 | |||
| 65 | public void load(JarIndex index, boolean recurse, boolean recurseMethod) { | ||
| 66 | // get all the child nodes | ||
| 67 | Collection<EntryReference<MethodEntry, MethodDefEntry>> references = getReferences(index, recurseMethod); | ||
| 68 | |||
| 69 | for (EntryReference<MethodEntry, MethodDefEntry> reference : references) { | ||
| 70 | add(new MethodReferenceTreeNode(translator, reference)); | ||
| 71 | } | ||
| 72 | |||
| 73 | if (recurse && this.children != null) { | ||
| 74 | for (Object child : this.children) { | ||
| 75 | if (child instanceof MethodReferenceTreeNode) { | ||
| 76 | MethodReferenceTreeNode node = (MethodReferenceTreeNode) child; | ||
| 77 | |||
| 78 | // don't recurse into ancestor | ||
| 79 | Set<Entry<?>> ancestors = Sets.newHashSet(); | ||
| 80 | TreeNode n = node; | ||
| 81 | while (n.getParent() != null) { | ||
| 82 | n = n.getParent(); | ||
| 83 | if (n instanceof MethodReferenceTreeNode) { | ||
| 84 | ancestors.add(((MethodReferenceTreeNode) n).getEntry()); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | if (ancestors.contains(node.getEntry())) { | ||
| 88 | continue; | ||
| 89 | } | ||
| 90 | |||
| 91 | node.load(index, true, false); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | private Collection<EntryReference<MethodEntry, MethodDefEntry>> getReferences(JarIndex index, boolean recurseMethod) { | ||
| 98 | ReferenceIndex referenceIndex = index.getReferenceIndex(); | ||
| 99 | |||
| 100 | if (recurseMethod) { | ||
| 101 | Collection<EntryReference<MethodEntry, MethodDefEntry>> references = new ArrayList<>(); | ||
| 102 | |||
| 103 | EntryResolver entryResolver = index.getEntryResolver(); | ||
| 104 | for (MethodEntry methodEntry : entryResolver.resolveEquivalentMethods(entry)) { | ||
| 105 | references.addAll(referenceIndex.getReferencesToMethod(methodEntry)); | ||
| 106 | } | ||
| 107 | |||
| 108 | return references; | ||
| 109 | } else { | ||
| 110 | return referenceIndex.getReferencesToMethod(entry); | ||
| 111 | } | ||
| 112 | } | ||
| 113 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java b/src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java deleted file mode 100644 index 5b19d18..0000000 --- a/src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java +++ /dev/null | |||
| @@ -1,74 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 4 | |||
| 5 | public abstract class ReferenceTargetType { | ||
| 6 | private static final None NONE = new None(); | ||
| 7 | private static final Uninitialized UNINITIALIZED = new Uninitialized(); | ||
| 8 | |||
| 9 | public abstract Kind getKind(); | ||
| 10 | |||
| 11 | public static None none() { | ||
| 12 | return NONE; | ||
| 13 | } | ||
| 14 | |||
| 15 | public static Uninitialized uninitialized() { | ||
| 16 | return UNINITIALIZED; | ||
| 17 | } | ||
| 18 | |||
| 19 | public static ClassType classType(ClassEntry name) { | ||
| 20 | return new ClassType(name); | ||
| 21 | } | ||
| 22 | |||
| 23 | public enum Kind { | ||
| 24 | NONE, | ||
| 25 | UNINITIALIZED, | ||
| 26 | CLASS_TYPE | ||
| 27 | } | ||
| 28 | |||
| 29 | public static class None extends ReferenceTargetType { | ||
| 30 | @Override | ||
| 31 | public Kind getKind() { | ||
| 32 | return Kind.NONE; | ||
| 33 | } | ||
| 34 | |||
| 35 | @Override | ||
| 36 | public String toString() { | ||
| 37 | return "(none)"; | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | public static class Uninitialized extends ReferenceTargetType { | ||
| 42 | @Override | ||
| 43 | public Kind getKind() { | ||
| 44 | return Kind.UNINITIALIZED; | ||
| 45 | } | ||
| 46 | |||
| 47 | @Override | ||
| 48 | public String toString() { | ||
| 49 | return "(uninitialized)"; | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | public static class ClassType extends ReferenceTargetType { | ||
| 54 | private final ClassEntry entry; | ||
| 55 | |||
| 56 | private ClassType(ClassEntry entry) { | ||
| 57 | this.entry = entry; | ||
| 58 | } | ||
| 59 | |||
| 60 | public ClassEntry getEntry() { | ||
| 61 | return entry; | ||
| 62 | } | ||
| 63 | |||
| 64 | @Override | ||
| 65 | public Kind getKind() { | ||
| 66 | return Kind.CLASS_TYPE; | ||
| 67 | } | ||
| 68 | |||
| 69 | @Override | ||
| 70 | public String toString() { | ||
| 71 | return entry.toString(); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java deleted file mode 100644 index c0a3a75..0000000 --- a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java +++ /dev/null | |||
| @@ -1,20 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 15 | |||
| 16 | public interface ReferenceTreeNode<E extends Entry<?>, C extends Entry<?>> { | ||
| 17 | E getEntry(); | ||
| 18 | |||
| 19 | EntryReference<E, C> getReference(); | ||
| 20 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/Token.java b/src/main/java/cuchaz/enigma/analysis/Token.java deleted file mode 100644 index f0155e5..0000000 --- a/src/main/java/cuchaz/enigma/analysis/Token.java +++ /dev/null | |||
| @@ -1,72 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | public class Token implements Comparable<Token> { | ||
| 15 | |||
| 16 | public int start; | ||
| 17 | public int end; | ||
| 18 | public String text; | ||
| 19 | |||
| 20 | public Token(int start, int end, String text) { | ||
| 21 | this.start = start; | ||
| 22 | this.end = end; | ||
| 23 | this.text = text; | ||
| 24 | } | ||
| 25 | |||
| 26 | public int getRenameOffset(String to) { | ||
| 27 | int length = this.end - this.start; | ||
| 28 | return to.length() - length; | ||
| 29 | } | ||
| 30 | |||
| 31 | public void rename(StringBuffer source, String to) { | ||
| 32 | int oldEnd = this.end; | ||
| 33 | this.text = to; | ||
| 34 | this.end = this.start + to.length(); | ||
| 35 | |||
| 36 | source.replace(start, oldEnd, to); | ||
| 37 | } | ||
| 38 | |||
| 39 | public Token move(int offset) { | ||
| 40 | Token token = new Token(this.start + offset, this.end + offset, null); | ||
| 41 | token.text = text; | ||
| 42 | return token; | ||
| 43 | } | ||
| 44 | |||
| 45 | public boolean contains(int pos) { | ||
| 46 | return pos >= start && pos <= end; | ||
| 47 | } | ||
| 48 | |||
| 49 | @Override | ||
| 50 | public int compareTo(Token other) { | ||
| 51 | return start - other.start; | ||
| 52 | } | ||
| 53 | |||
| 54 | @Override | ||
| 55 | public boolean equals(Object other) { | ||
| 56 | return other instanceof Token && equals((Token) other); | ||
| 57 | } | ||
| 58 | |||
| 59 | @Override | ||
| 60 | public int hashCode() { | ||
| 61 | return start * 37 + end; | ||
| 62 | } | ||
| 63 | |||
| 64 | public boolean equals(Token other) { | ||
| 65 | return start == other.start && end == other.end && text.equals(other.text); | ||
| 66 | } | ||
| 67 | |||
| 68 | @Override | ||
| 69 | public String toString() { | ||
| 70 | return String.format("[%d,%d]", start, end); | ||
| 71 | } | ||
| 72 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java b/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java deleted file mode 100644 index a4b1aac..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java +++ /dev/null | |||
| @@ -1,156 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import com.google.common.collect.Maps; | ||
| 4 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 5 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 6 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 7 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 8 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 9 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 10 | |||
| 11 | import javax.annotation.Nullable; | ||
| 12 | import java.util.*; | ||
| 13 | |||
| 14 | public class BridgeMethodIndex implements JarIndexer { | ||
| 15 | private final EntryIndex entryIndex; | ||
| 16 | private final InheritanceIndex inheritanceIndex; | ||
| 17 | private final ReferenceIndex referenceIndex; | ||
| 18 | |||
| 19 | private final Map<MethodEntry, MethodEntry> bridgeToSpecialized = Maps.newHashMap(); | ||
| 20 | private final Map<MethodEntry, MethodEntry> specializedToBridge = Maps.newHashMap(); | ||
| 21 | |||
| 22 | public BridgeMethodIndex(EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex) { | ||
| 23 | this.entryIndex = entryIndex; | ||
| 24 | this.inheritanceIndex = inheritanceIndex; | ||
| 25 | this.referenceIndex = referenceIndex; | ||
| 26 | } | ||
| 27 | |||
| 28 | public void findBridgeMethods() { | ||
| 29 | // look for access and bridged methods | ||
| 30 | for (MethodEntry methodEntry : entryIndex.getMethods()) { | ||
| 31 | MethodDefEntry methodDefEntry = (MethodDefEntry) methodEntry; | ||
| 32 | |||
| 33 | AccessFlags access = methodDefEntry.getAccess(); | ||
| 34 | if (access == null || !access.isSynthetic()) { | ||
| 35 | continue; | ||
| 36 | } | ||
| 37 | |||
| 38 | indexSyntheticMethod(methodDefEntry, access); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | @Override | ||
| 43 | public void processIndex(JarIndex index) { | ||
| 44 | Map<MethodEntry, MethodEntry> copiedAccessToBridge = new HashMap<>(specializedToBridge); | ||
| 45 | |||
| 46 | for (Map.Entry<MethodEntry, MethodEntry> entry : copiedAccessToBridge.entrySet()) { | ||
| 47 | MethodEntry specializedEntry = entry.getKey(); | ||
| 48 | MethodEntry bridgeEntry = entry.getValue(); | ||
| 49 | if (bridgeEntry.getName().equals(specializedEntry.getName())) { | ||
| 50 | continue; | ||
| 51 | } | ||
| 52 | |||
| 53 | MethodEntry renamedSpecializedEntry = specializedEntry.withName(bridgeEntry.getName()); | ||
| 54 | specializedToBridge.put(renamedSpecializedEntry, specializedToBridge.get(specializedEntry)); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | private void indexSyntheticMethod(MethodDefEntry syntheticMethod, AccessFlags access) { | ||
| 59 | MethodEntry specializedMethod = findSpecializedMethod(syntheticMethod); | ||
| 60 | if (specializedMethod == null) { | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | |||
| 64 | if (access.isBridge() || isPotentialBridge(syntheticMethod, specializedMethod)) { | ||
| 65 | bridgeToSpecialized.put(syntheticMethod, specializedMethod); | ||
| 66 | specializedToBridge.put(specializedMethod, syntheticMethod); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | private MethodEntry findSpecializedMethod(MethodEntry method) { | ||
| 71 | // we want to find all compiler-added methods that directly call another with no processing | ||
| 72 | |||
| 73 | // get all the methods that we call | ||
| 74 | final Collection<MethodEntry> referencedMethods = referenceIndex.getMethodsReferencedBy(method); | ||
| 75 | |||
| 76 | // is there just one? | ||
| 77 | if (referencedMethods.size() != 1) { | ||
| 78 | return null; | ||
| 79 | } | ||
| 80 | |||
| 81 | return referencedMethods.stream().findFirst().orElse(null); | ||
| 82 | } | ||
| 83 | |||
| 84 | private boolean isPotentialBridge(MethodDefEntry bridgeMethod, MethodEntry specializedMethod) { | ||
| 85 | // Bridge methods only exist for inheritance purposes, if we're private, final, or static, we cannot be inherited | ||
| 86 | AccessFlags bridgeAccess = bridgeMethod.getAccess(); | ||
| 87 | if (bridgeAccess.isPrivate() || bridgeAccess.isFinal() || bridgeAccess.isStatic()) { | ||
| 88 | return false; | ||
| 89 | } | ||
| 90 | |||
| 91 | MethodDescriptor bridgeDesc = bridgeMethod.getDesc(); | ||
| 92 | MethodDescriptor specializedDesc = specializedMethod.getDesc(); | ||
| 93 | List<TypeDescriptor> bridgeArguments = bridgeDesc.getArgumentDescs(); | ||
| 94 | List<TypeDescriptor> specializedArguments = specializedDesc.getArgumentDescs(); | ||
| 95 | |||
| 96 | // A bridge method will always have the same number of arguments | ||
| 97 | if (bridgeArguments.size() != specializedArguments.size()) { | ||
| 98 | return false; | ||
| 99 | } | ||
| 100 | |||
| 101 | // Check that all argument types are bridge-compatible | ||
| 102 | for (int i = 0; i < bridgeArguments.size(); i++) { | ||
| 103 | if (!areTypesBridgeCompatible(bridgeArguments.get(i), specializedArguments.get(i))) { | ||
| 104 | return false; | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | // Check that the return type is bridge-compatible | ||
| 109 | return areTypesBridgeCompatible(bridgeDesc.getReturnDesc(), specializedDesc.getReturnDesc()); | ||
| 110 | } | ||
| 111 | |||
| 112 | private boolean areTypesBridgeCompatible(TypeDescriptor bridgeDesc, TypeDescriptor specializedDesc) { | ||
| 113 | if (bridgeDesc.equals(specializedDesc)) { | ||
| 114 | return true; | ||
| 115 | } | ||
| 116 | |||
| 117 | // Either the descs will be equal, or they are both types and different through a generic | ||
| 118 | if (bridgeDesc.isType() && specializedDesc.isType()) { | ||
| 119 | ClassEntry bridgeType = bridgeDesc.getTypeEntry(); | ||
| 120 | ClassEntry accessedType = specializedDesc.getTypeEntry(); | ||
| 121 | |||
| 122 | // If the given types are completely unrelated to each other, this can't be bridge compatible | ||
| 123 | InheritanceIndex.Relation relation = inheritanceIndex.computeClassRelation(accessedType, bridgeType); | ||
| 124 | return relation != InheritanceIndex.Relation.UNRELATED; | ||
| 125 | } | ||
| 126 | |||
| 127 | return false; | ||
| 128 | } | ||
| 129 | |||
| 130 | public boolean isBridgeMethod(MethodEntry entry) { | ||
| 131 | return bridgeToSpecialized.containsKey(entry); | ||
| 132 | } | ||
| 133 | |||
| 134 | public boolean isSpecializedMethod(MethodEntry entry) { | ||
| 135 | return specializedToBridge.containsKey(entry); | ||
| 136 | } | ||
| 137 | |||
| 138 | @Nullable | ||
| 139 | public MethodEntry getBridgeFromSpecialized(MethodEntry specialized) { | ||
| 140 | return specializedToBridge.get(specialized); | ||
| 141 | } | ||
| 142 | |||
| 143 | public MethodEntry getSpecializedFromBridge(MethodEntry bridge) { | ||
| 144 | return bridgeToSpecialized.get(bridge); | ||
| 145 | } | ||
| 146 | |||
| 147 | /** Includes "renamed specialized -> bridge" entries. */ | ||
| 148 | public Map<MethodEntry, MethodEntry> getSpecializedToBridge() { | ||
| 149 | return Collections.unmodifiableMap(specializedToBridge); | ||
| 150 | } | ||
| 151 | |||
| 152 | /** Only "bridge -> original name" entries. **/ | ||
| 153 | public Map<MethodEntry, MethodEntry> getBridgeToSpecialized() { | ||
| 154 | return Collections.unmodifiableMap(bridgeToSpecialized); | ||
| 155 | } | ||
| 156 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java b/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java deleted file mode 100644 index 9a2726e..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java +++ /dev/null | |||
| @@ -1,102 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 4 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 5 | |||
| 6 | import javax.annotation.Nullable; | ||
| 7 | import java.util.Collection; | ||
| 8 | import java.util.HashMap; | ||
| 9 | import java.util.Map; | ||
| 10 | |||
| 11 | public class EntryIndex implements JarIndexer { | ||
| 12 | private Map<ClassEntry, AccessFlags> classes = new HashMap<>(); | ||
| 13 | private Map<FieldEntry, AccessFlags> fields = new HashMap<>(); | ||
| 14 | private Map<MethodEntry, AccessFlags> methods = new HashMap<>(); | ||
| 15 | private Map<ClassEntry, ClassDefEntry> definitions = new HashMap<>(); | ||
| 16 | |||
| 17 | @Override | ||
| 18 | public void indexClass(ClassDefEntry classEntry) { | ||
| 19 | definitions.put(classEntry, classEntry); | ||
| 20 | classes.put(classEntry, classEntry.getAccess()); | ||
| 21 | } | ||
| 22 | |||
| 23 | @Override | ||
| 24 | public void indexMethod(MethodDefEntry methodEntry) { | ||
| 25 | methods.put(methodEntry, methodEntry.getAccess()); | ||
| 26 | } | ||
| 27 | |||
| 28 | @Override | ||
| 29 | public void indexField(FieldDefEntry fieldEntry) { | ||
| 30 | fields.put(fieldEntry, fieldEntry.getAccess()); | ||
| 31 | } | ||
| 32 | |||
| 33 | public boolean hasClass(ClassEntry entry) { | ||
| 34 | return classes.containsKey(entry); | ||
| 35 | } | ||
| 36 | |||
| 37 | public boolean hasMethod(MethodEntry entry) { | ||
| 38 | return methods.containsKey(entry); | ||
| 39 | } | ||
| 40 | |||
| 41 | public boolean hasField(FieldEntry entry) { | ||
| 42 | return fields.containsKey(entry); | ||
| 43 | } | ||
| 44 | |||
| 45 | public boolean hasEntry(Entry<?> entry) { | ||
| 46 | if (entry instanceof ClassEntry) { | ||
| 47 | return hasClass((ClassEntry) entry); | ||
| 48 | } else if (entry instanceof MethodEntry) { | ||
| 49 | return hasMethod((MethodEntry) entry); | ||
| 50 | } else if (entry instanceof FieldEntry) { | ||
| 51 | return hasField((FieldEntry) entry); | ||
| 52 | } else if (entry instanceof LocalVariableEntry) { | ||
| 53 | return hasMethod(((LocalVariableEntry) entry).getParent()); | ||
| 54 | } | ||
| 55 | |||
| 56 | return false; | ||
| 57 | } | ||
| 58 | |||
| 59 | @Nullable | ||
| 60 | public AccessFlags getMethodAccess(MethodEntry entry) { | ||
| 61 | return methods.get(entry); | ||
| 62 | } | ||
| 63 | |||
| 64 | @Nullable | ||
| 65 | public AccessFlags getFieldAccess(FieldEntry entry) { | ||
| 66 | return fields.get(entry); | ||
| 67 | } | ||
| 68 | |||
| 69 | @Nullable | ||
| 70 | public AccessFlags getClassAccess(ClassEntry entry) { | ||
| 71 | return classes.get(entry); | ||
| 72 | } | ||
| 73 | |||
| 74 | @Nullable | ||
| 75 | public AccessFlags getEntryAccess(Entry<?> entry) { | ||
| 76 | if (entry instanceof MethodEntry) { | ||
| 77 | return getMethodAccess((MethodEntry) entry); | ||
| 78 | } else if (entry instanceof FieldEntry) { | ||
| 79 | return getFieldAccess((FieldEntry) entry); | ||
| 80 | } else if (entry instanceof LocalVariableEntry) { | ||
| 81 | return getMethodAccess(((LocalVariableEntry) entry).getParent()); | ||
| 82 | } | ||
| 83 | |||
| 84 | return null; | ||
| 85 | } | ||
| 86 | |||
| 87 | public ClassDefEntry getDefinition(ClassEntry entry) { | ||
| 88 | return definitions.get(entry); | ||
| 89 | } | ||
| 90 | |||
| 91 | public Collection<ClassEntry> getClasses() { | ||
| 92 | return classes.keySet(); | ||
| 93 | } | ||
| 94 | |||
| 95 | public Collection<MethodEntry> getMethods() { | ||
| 96 | return methods.keySet(); | ||
| 97 | } | ||
| 98 | |||
| 99 | public Collection<FieldEntry> getFields() { | ||
| 100 | return fields.keySet(); | ||
| 101 | } | ||
| 102 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java deleted file mode 100644 index f9cb23c..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java +++ /dev/null | |||
| @@ -1,40 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | ||
| 4 | import cuchaz.enigma.translation.representation.entry.FieldDefEntry; | ||
| 5 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 6 | import org.objectweb.asm.ClassVisitor; | ||
| 7 | import org.objectweb.asm.FieldVisitor; | ||
| 8 | import org.objectweb.asm.MethodVisitor; | ||
| 9 | |||
| 10 | public class IndexClassVisitor extends ClassVisitor { | ||
| 11 | private final JarIndexer indexer; | ||
| 12 | private ClassDefEntry classEntry; | ||
| 13 | |||
| 14 | public IndexClassVisitor(JarIndex indexer, int api) { | ||
| 15 | super(api); | ||
| 16 | this.indexer = indexer; | ||
| 17 | } | ||
| 18 | |||
| 19 | @Override | ||
| 20 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { | ||
| 21 | classEntry = ClassDefEntry.parse(access, name, signature, superName, interfaces); | ||
| 22 | indexer.indexClass(classEntry); | ||
| 23 | |||
| 24 | super.visit(version, access, name, signature, superName, interfaces); | ||
| 25 | } | ||
| 26 | |||
| 27 | @Override | ||
| 28 | public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { | ||
| 29 | indexer.indexField(FieldDefEntry.parse(classEntry, access, name, desc, signature)); | ||
| 30 | |||
| 31 | return super.visitField(access, name, desc, signature, value); | ||
| 32 | } | ||
| 33 | |||
| 34 | @Override | ||
| 35 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { | ||
| 36 | indexer.indexMethod(MethodDefEntry.parse(classEntry, access, name, desc, signature)); | ||
| 37 | |||
| 38 | return super.visitMethod(access, name, desc, signature, exceptions); | ||
| 39 | } | ||
| 40 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java b/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java deleted file mode 100644 index f3d419e..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java +++ /dev/null | |||
| @@ -1,180 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.analysis.IndexSimpleVerifier; | ||
| 4 | import cuchaz.enigma.analysis.InterpreterPair; | ||
| 5 | import cuchaz.enigma.analysis.MethodNodeWithAction; | ||
| 6 | import cuchaz.enigma.analysis.ReferenceTargetType; | ||
| 7 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 8 | import cuchaz.enigma.translation.representation.Lambda; | ||
| 9 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 10 | import cuchaz.enigma.translation.representation.Signature; | ||
| 11 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 12 | import org.objectweb.asm.*; | ||
| 13 | import org.objectweb.asm.tree.AbstractInsnNode; | ||
| 14 | import org.objectweb.asm.tree.FieldInsnNode; | ||
| 15 | import org.objectweb.asm.tree.InvokeDynamicInsnNode; | ||
| 16 | import org.objectweb.asm.tree.MethodInsnNode; | ||
| 17 | import org.objectweb.asm.tree.analysis.*; | ||
| 18 | |||
| 19 | import java.util.List; | ||
| 20 | import java.util.stream.Collectors; | ||
| 21 | |||
| 22 | public class IndexReferenceVisitor extends ClassVisitor { | ||
| 23 | private final JarIndexer indexer; | ||
| 24 | private final EntryIndex entryIndex; | ||
| 25 | private final InheritanceIndex inheritanceIndex; | ||
| 26 | private ClassEntry classEntry; | ||
| 27 | private String className; | ||
| 28 | |||
| 29 | public IndexReferenceVisitor(JarIndexer indexer, EntryIndex entryIndex, InheritanceIndex inheritanceIndex, int api) { | ||
| 30 | super(api); | ||
| 31 | this.indexer = indexer; | ||
| 32 | this.entryIndex = entryIndex; | ||
| 33 | this.inheritanceIndex = inheritanceIndex; | ||
| 34 | } | ||
| 35 | |||
| 36 | @Override | ||
| 37 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { | ||
| 38 | classEntry = new ClassEntry(name); | ||
| 39 | className = name; | ||
| 40 | } | ||
| 41 | |||
| 42 | @Override | ||
| 43 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { | ||
| 44 | MethodDefEntry entry = new MethodDefEntry(classEntry, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access)); | ||
| 45 | return new MethodNodeWithAction(api, access, name, desc, signature, exceptions, methodNode -> { | ||
| 46 | try { | ||
| 47 | new Analyzer<>(new MethodInterpreter(entry, indexer, entryIndex, inheritanceIndex)).analyze(className, methodNode); | ||
| 48 | } catch (AnalyzerException e) { | ||
| 49 | throw new RuntimeException(e); | ||
| 50 | } | ||
| 51 | }); | ||
| 52 | } | ||
| 53 | |||
| 54 | private static class MethodInterpreter extends InterpreterPair<BasicValue, SourceValue> { | ||
| 55 | private final MethodDefEntry callerEntry; | ||
| 56 | private JarIndexer indexer; | ||
| 57 | |||
| 58 | public MethodInterpreter(MethodDefEntry callerEntry, JarIndexer indexer, EntryIndex entryIndex, InheritanceIndex inheritanceIndex) { | ||
| 59 | super(new IndexSimpleVerifier(entryIndex, inheritanceIndex), new SourceInterpreter()); | ||
| 60 | this.callerEntry = callerEntry; | ||
| 61 | this.indexer = indexer; | ||
| 62 | } | ||
| 63 | |||
| 64 | @Override | ||
| 65 | public PairValue<BasicValue, SourceValue> newOperation(AbstractInsnNode insn) throws AnalyzerException { | ||
| 66 | if (insn.getOpcode() == Opcodes.GETSTATIC) { | ||
| 67 | FieldInsnNode field = (FieldInsnNode) insn; | ||
| 68 | indexer.indexFieldReference(callerEntry, FieldEntry.parse(field.owner, field.name, field.desc), ReferenceTargetType.none()); | ||
| 69 | } | ||
| 70 | |||
| 71 | return super.newOperation(insn); | ||
| 72 | } | ||
| 73 | |||
| 74 | @Override | ||
| 75 | public PairValue<BasicValue, SourceValue> unaryOperation(AbstractInsnNode insn, PairValue<BasicValue, SourceValue> value) throws AnalyzerException { | ||
| 76 | if (insn.getOpcode() == Opcodes.PUTSTATIC) { | ||
| 77 | FieldInsnNode field = (FieldInsnNode) insn; | ||
| 78 | indexer.indexFieldReference(callerEntry, FieldEntry.parse(field.owner, field.name, field.desc), ReferenceTargetType.none()); | ||
| 79 | } | ||
| 80 | |||
| 81 | if (insn.getOpcode() == Opcodes.GETFIELD) { | ||
| 82 | FieldInsnNode field = (FieldInsnNode) insn; | ||
| 83 | indexer.indexFieldReference(callerEntry, FieldEntry.parse(field.owner, field.name, field.desc), getReferenceTargetType(value, insn)); | ||
| 84 | } | ||
| 85 | |||
| 86 | return super.unaryOperation(insn, value); | ||
| 87 | } | ||
| 88 | |||
| 89 | |||
| 90 | @Override | ||
| 91 | public PairValue<BasicValue, SourceValue> binaryOperation(AbstractInsnNode insn, PairValue<BasicValue, SourceValue> value1, PairValue<BasicValue, SourceValue> value2) throws AnalyzerException { | ||
| 92 | if (insn.getOpcode() == Opcodes.PUTFIELD) { | ||
| 93 | FieldInsnNode field = (FieldInsnNode) insn; | ||
| 94 | FieldEntry fieldEntry = FieldEntry.parse(field.owner, field.name, field.desc); | ||
| 95 | indexer.indexFieldReference(callerEntry, fieldEntry, ReferenceTargetType.none()); | ||
| 96 | } | ||
| 97 | |||
| 98 | return super.binaryOperation(insn, value1, value2); | ||
| 99 | } | ||
| 100 | |||
| 101 | @Override | ||
| 102 | public PairValue<BasicValue, SourceValue> naryOperation(AbstractInsnNode insn, List<? extends PairValue<BasicValue, SourceValue>> values) throws AnalyzerException { | ||
| 103 | if (insn.getOpcode() == Opcodes.INVOKEINTERFACE || insn.getOpcode() == Opcodes.INVOKESPECIAL || insn.getOpcode() == Opcodes.INVOKEVIRTUAL) { | ||
| 104 | MethodInsnNode methodInsn = (MethodInsnNode) insn; | ||
| 105 | indexer.indexMethodReference(callerEntry, MethodEntry.parse(methodInsn.owner, methodInsn.name, methodInsn.desc), getReferenceTargetType(values.get(0), insn)); | ||
| 106 | } | ||
| 107 | |||
| 108 | if (insn.getOpcode() == Opcodes.INVOKESTATIC) { | ||
| 109 | MethodInsnNode methodInsn = (MethodInsnNode) insn; | ||
| 110 | indexer.indexMethodReference(callerEntry, MethodEntry.parse(methodInsn.owner, methodInsn.name, methodInsn.desc), ReferenceTargetType.none()); | ||
| 111 | } | ||
| 112 | |||
| 113 | if (insn.getOpcode() == Opcodes.INVOKEDYNAMIC) { | ||
| 114 | InvokeDynamicInsnNode invokeDynamicInsn = (InvokeDynamicInsnNode) insn; | ||
| 115 | List<AbstractInsnNode> args = values.stream().map(v -> v.right.insns.stream().findFirst().orElseThrow(AssertionError::new)).collect(Collectors.toList()); | ||
| 116 | |||
| 117 | if ("java/lang/invoke/LambdaMetafactory".equals(invokeDynamicInsn.bsm.getOwner()) && "metafactory".equals(invokeDynamicInsn.bsm.getName())) { | ||
| 118 | Type samMethodType = (Type) invokeDynamicInsn.bsmArgs[0]; | ||
| 119 | Handle implMethod = (Handle) invokeDynamicInsn.bsmArgs[1]; | ||
| 120 | Type instantiatedMethodType = (Type) invokeDynamicInsn.bsmArgs[2]; | ||
| 121 | |||
| 122 | ReferenceTargetType targetType; | ||
| 123 | if (implMethod.getTag() != Opcodes.H_GETSTATIC && implMethod.getTag() != Opcodes.H_PUTFIELD && implMethod.getTag() != Opcodes.H_INVOKESTATIC) { | ||
| 124 | if (instantiatedMethodType.getArgumentTypes().length < Type.getArgumentTypes(implMethod.getDesc()).length) { | ||
| 125 | targetType = getReferenceTargetType(values.get(0), insn); | ||
| 126 | } else { | ||
| 127 | targetType = ReferenceTargetType.none(); // no "this" argument | ||
| 128 | } | ||
| 129 | } else { | ||
| 130 | targetType = ReferenceTargetType.none(); | ||
| 131 | } | ||
| 132 | |||
| 133 | indexer.indexLambda(callerEntry, new Lambda( | ||
| 134 | invokeDynamicInsn.name, | ||
| 135 | new MethodDescriptor(invokeDynamicInsn.desc), | ||
| 136 | new MethodDescriptor(samMethodType.getDescriptor()), | ||
| 137 | getHandleEntry(implMethod), | ||
| 138 | new MethodDescriptor(instantiatedMethodType.getDescriptor()) | ||
| 139 | ), targetType); | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | return super.naryOperation(insn, values); | ||
| 144 | } | ||
| 145 | |||
| 146 | private ReferenceTargetType getReferenceTargetType(PairValue<BasicValue, SourceValue> target, AbstractInsnNode insn) throws AnalyzerException { | ||
| 147 | if (target.left == BasicValue.UNINITIALIZED_VALUE) { | ||
| 148 | return ReferenceTargetType.uninitialized(); | ||
| 149 | } | ||
| 150 | |||
| 151 | if (target.left.getType().getSort() == Type.OBJECT) { | ||
| 152 | return ReferenceTargetType.classType(new ClassEntry(target.left.getType().getInternalName())); | ||
| 153 | } | ||
| 154 | |||
| 155 | if (target.left.getType().getSort() == Type.ARRAY) { | ||
| 156 | return ReferenceTargetType.classType(new ClassEntry("java/lang/Object")); | ||
| 157 | } | ||
| 158 | |||
| 159 | throw new AnalyzerException(insn, "called method on or accessed field of non-object type"); | ||
| 160 | } | ||
| 161 | |||
| 162 | private static ParentedEntry<?> getHandleEntry(Handle handle) { | ||
| 163 | switch (handle.getTag()) { | ||
| 164 | case Opcodes.H_GETFIELD: | ||
| 165 | case Opcodes.H_GETSTATIC: | ||
| 166 | case Opcodes.H_PUTFIELD: | ||
| 167 | case Opcodes.H_PUTSTATIC: | ||
| 168 | return FieldEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc()); | ||
| 169 | case Opcodes.H_INVOKEINTERFACE: | ||
| 170 | case Opcodes.H_INVOKESPECIAL: | ||
| 171 | case Opcodes.H_INVOKESTATIC: | ||
| 172 | case Opcodes.H_INVOKEVIRTUAL: | ||
| 173 | case Opcodes.H_NEWINVOKESPECIAL: | ||
| 174 | return MethodEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc()); | ||
| 175 | } | ||
| 176 | |||
| 177 | throw new RuntimeException("Invalid handle tag " + handle.getTag()); | ||
| 178 | } | ||
| 179 | } | ||
| 180 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java b/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java deleted file mode 100644 index 1ab2abd..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java +++ /dev/null | |||
| @@ -1,127 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.analysis.index; | ||
| 13 | |||
| 14 | import com.google.common.collect.HashMultimap; | ||
| 15 | import com.google.common.collect.Multimap; | ||
| 16 | import com.google.common.collect.Sets; | ||
| 17 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | ||
| 18 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 19 | |||
| 20 | import java.util.Collection; | ||
| 21 | import java.util.HashSet; | ||
| 22 | import java.util.LinkedList; | ||
| 23 | import java.util.Set; | ||
| 24 | |||
| 25 | public class InheritanceIndex implements JarIndexer { | ||
| 26 | private final EntryIndex entryIndex; | ||
| 27 | |||
| 28 | private Multimap<ClassEntry, ClassEntry> classParents = HashMultimap.create(); | ||
| 29 | private Multimap<ClassEntry, ClassEntry> classChildren = HashMultimap.create(); | ||
| 30 | |||
| 31 | public InheritanceIndex(EntryIndex entryIndex) { | ||
| 32 | this.entryIndex = entryIndex; | ||
| 33 | } | ||
| 34 | |||
| 35 | @Override | ||
| 36 | public void indexClass(ClassDefEntry classEntry) { | ||
| 37 | if (classEntry.isJre()) { | ||
| 38 | return; | ||
| 39 | } | ||
| 40 | |||
| 41 | ClassEntry superClass = classEntry.getSuperClass(); | ||
| 42 | if (superClass != null && !superClass.getName().equals("java/lang/Object")) { | ||
| 43 | indexParent(classEntry, superClass); | ||
| 44 | } | ||
| 45 | |||
| 46 | for (ClassEntry interfaceEntry : classEntry.getInterfaces()) { | ||
| 47 | indexParent(classEntry, interfaceEntry); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | private void indexParent(ClassEntry childEntry, ClassEntry parentEntry) { | ||
| 52 | classParents.put(childEntry, parentEntry); | ||
| 53 | classChildren.put(parentEntry, childEntry); | ||
| 54 | } | ||
| 55 | |||
| 56 | public Collection<ClassEntry> getParents(ClassEntry classEntry) { | ||
| 57 | return classParents.get(classEntry); | ||
| 58 | } | ||
| 59 | |||
| 60 | public Collection<ClassEntry> getChildren(ClassEntry classEntry) { | ||
| 61 | return classChildren.get(classEntry); | ||
| 62 | } | ||
| 63 | |||
| 64 | public Collection<ClassEntry> getDescendants(ClassEntry classEntry) { | ||
| 65 | Collection<ClassEntry> descendants = new HashSet<>(); | ||
| 66 | |||
| 67 | LinkedList<ClassEntry> descendantQueue = new LinkedList<>(); | ||
| 68 | descendantQueue.push(classEntry); | ||
| 69 | |||
| 70 | while (!descendantQueue.isEmpty()) { | ||
| 71 | ClassEntry descendant = descendantQueue.pop(); | ||
| 72 | Collection<ClassEntry> children = getChildren(descendant); | ||
| 73 | |||
| 74 | children.forEach(descendantQueue::push); | ||
| 75 | descendants.addAll(children); | ||
| 76 | } | ||
| 77 | |||
| 78 | return descendants; | ||
| 79 | } | ||
| 80 | |||
| 81 | public Set<ClassEntry> getAncestors(ClassEntry classEntry) { | ||
| 82 | Set<ClassEntry> ancestors = Sets.newHashSet(); | ||
| 83 | |||
| 84 | LinkedList<ClassEntry> ancestorQueue = new LinkedList<>(); | ||
| 85 | ancestorQueue.push(classEntry); | ||
| 86 | |||
| 87 | while (!ancestorQueue.isEmpty()) { | ||
| 88 | ClassEntry ancestor = ancestorQueue.pop(); | ||
| 89 | Collection<ClassEntry> parents = getParents(ancestor); | ||
| 90 | |||
| 91 | parents.forEach(ancestorQueue::push); | ||
| 92 | ancestors.addAll(parents); | ||
| 93 | } | ||
| 94 | |||
| 95 | return ancestors; | ||
| 96 | } | ||
| 97 | |||
| 98 | public Relation computeClassRelation(ClassEntry classEntry, ClassEntry potentialAncestor) { | ||
| 99 | if (potentialAncestor.getName().equals("java/lang/Object")) return Relation.RELATED; | ||
| 100 | if (!entryIndex.hasClass(classEntry)) return Relation.UNKNOWN; | ||
| 101 | |||
| 102 | for (ClassEntry ancestor : getAncestors(classEntry)) { | ||
| 103 | if (potentialAncestor.equals(ancestor)) { | ||
| 104 | return Relation.RELATED; | ||
| 105 | } else if (!entryIndex.hasClass(ancestor)) { | ||
| 106 | return Relation.UNKNOWN; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | return Relation.UNRELATED; | ||
| 111 | } | ||
| 112 | |||
| 113 | public boolean isParent(ClassEntry classEntry) { | ||
| 114 | return classChildren.containsKey(classEntry); | ||
| 115 | } | ||
| 116 | |||
| 117 | public boolean hasParents(ClassEntry classEntry) { | ||
| 118 | Collection<ClassEntry> parents = classParents.get(classEntry); | ||
| 119 | return parents != null && !parents.isEmpty(); | ||
| 120 | } | ||
| 121 | |||
| 122 | public enum Relation { | ||
| 123 | RELATED, | ||
| 124 | UNRELATED, | ||
| 125 | UNKNOWN | ||
| 126 | } | ||
| 127 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java deleted file mode 100644 index e401c2f..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java +++ /dev/null | |||
| @@ -1,171 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.analysis.index; | ||
| 13 | |||
| 14 | import com.google.common.collect.HashMultimap; | ||
| 15 | import com.google.common.collect.Multimap; | ||
| 16 | import cuchaz.enigma.ProgressListener; | ||
| 17 | import cuchaz.enigma.analysis.ClassCache; | ||
| 18 | import cuchaz.enigma.analysis.ReferenceTargetType; | ||
| 19 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 20 | import cuchaz.enigma.translation.mapping.IndexEntryResolver; | ||
| 21 | import cuchaz.enigma.translation.representation.Lambda; | ||
| 22 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 23 | import cuchaz.enigma.utils.I18n; | ||
| 24 | |||
| 25 | import cuchaz.enigma.utils.Utils; | ||
| 26 | import org.objectweb.asm.ClassReader; | ||
| 27 | import org.objectweb.asm.Opcodes; | ||
| 28 | |||
| 29 | import java.util.Arrays; | ||
| 30 | import java.util.Collection; | ||
| 31 | |||
| 32 | public class JarIndex implements JarIndexer { | ||
| 33 | private final EntryIndex entryIndex; | ||
| 34 | private final InheritanceIndex inheritanceIndex; | ||
| 35 | private final ReferenceIndex referenceIndex; | ||
| 36 | private final BridgeMethodIndex bridgeMethodIndex; | ||
| 37 | private final PackageVisibilityIndex packageVisibilityIndex; | ||
| 38 | private final EntryResolver entryResolver; | ||
| 39 | |||
| 40 | private final Collection<JarIndexer> indexers; | ||
| 41 | |||
| 42 | private final Multimap<String, MethodDefEntry> methodImplementations = HashMultimap.create(); | ||
| 43 | |||
| 44 | public JarIndex(EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex, PackageVisibilityIndex packageVisibilityIndex) { | ||
| 45 | this.entryIndex = entryIndex; | ||
| 46 | this.inheritanceIndex = inheritanceIndex; | ||
| 47 | this.referenceIndex = referenceIndex; | ||
| 48 | this.bridgeMethodIndex = bridgeMethodIndex; | ||
| 49 | this.packageVisibilityIndex = packageVisibilityIndex; | ||
| 50 | this.indexers = Arrays.asList(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, packageVisibilityIndex); | ||
| 51 | this.entryResolver = new IndexEntryResolver(this); | ||
| 52 | } | ||
| 53 | |||
| 54 | public static JarIndex empty() { | ||
| 55 | EntryIndex entryIndex = new EntryIndex(); | ||
| 56 | InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); | ||
| 57 | ReferenceIndex referenceIndex = new ReferenceIndex(); | ||
| 58 | BridgeMethodIndex bridgeMethodIndex = new BridgeMethodIndex(entryIndex, inheritanceIndex, referenceIndex); | ||
| 59 | PackageVisibilityIndex packageVisibilityIndex = new PackageVisibilityIndex(); | ||
| 60 | return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, packageVisibilityIndex); | ||
| 61 | } | ||
| 62 | |||
| 63 | public void indexJar(ClassCache classCache, ProgressListener progress) { | ||
| 64 | progress.init(4, I18n.translate("progress.jar.indexing")); | ||
| 65 | |||
| 66 | progress.step(1, I18n.translate("progress.jar.indexing.entries")); | ||
| 67 | classCache.visit(() -> new IndexClassVisitor(this, Utils.ASM_VERSION), ClassReader.SKIP_CODE); | ||
| 68 | |||
| 69 | progress.step(2, I18n.translate("progress.jar.indexing.references")); | ||
| 70 | classCache.visit(() -> new IndexReferenceVisitor(this, entryIndex, inheritanceIndex, Utils.ASM_VERSION), 0); | ||
| 71 | |||
| 72 | progress.step(3, I18n.translate("progress.jar.indexing.methods")); | ||
| 73 | bridgeMethodIndex.findBridgeMethods(); | ||
| 74 | |||
| 75 | progress.step(4, I18n.translate("progress.jar.indexing.process")); | ||
| 76 | processIndex(this); | ||
| 77 | } | ||
| 78 | |||
| 79 | @Override | ||
| 80 | public void processIndex(JarIndex index) { | ||
| 81 | indexers.forEach(indexer -> indexer.processIndex(index)); | ||
| 82 | } | ||
| 83 | |||
| 84 | @Override | ||
| 85 | public void indexClass(ClassDefEntry classEntry) { | ||
| 86 | if (classEntry.isJre()) { | ||
| 87 | return; | ||
| 88 | } | ||
| 89 | |||
| 90 | for (ClassEntry interfaceEntry : classEntry.getInterfaces()) { | ||
| 91 | if (classEntry.equals(interfaceEntry)) { | ||
| 92 | throw new IllegalArgumentException("Class cannot be its own interface! " + classEntry); | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | indexers.forEach(indexer -> indexer.indexClass(classEntry)); | ||
| 97 | } | ||
| 98 | |||
| 99 | @Override | ||
| 100 | public void indexField(FieldDefEntry fieldEntry) { | ||
| 101 | if (fieldEntry.getParent().isJre()) { | ||
| 102 | return; | ||
| 103 | } | ||
| 104 | |||
| 105 | indexers.forEach(indexer -> indexer.indexField(fieldEntry)); | ||
| 106 | } | ||
| 107 | |||
| 108 | @Override | ||
| 109 | public void indexMethod(MethodDefEntry methodEntry) { | ||
| 110 | if (methodEntry.getParent().isJre()) { | ||
| 111 | return; | ||
| 112 | } | ||
| 113 | |||
| 114 | indexers.forEach(indexer -> indexer.indexMethod(methodEntry)); | ||
| 115 | |||
| 116 | if (!methodEntry.isConstructor()) { | ||
| 117 | methodImplementations.put(methodEntry.getParent().getFullName(), methodEntry); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | @Override | ||
| 122 | public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry, ReferenceTargetType targetType) { | ||
| 123 | if (callerEntry.getParent().isJre()) { | ||
| 124 | return; | ||
| 125 | } | ||
| 126 | |||
| 127 | indexers.forEach(indexer -> indexer.indexMethodReference(callerEntry, referencedEntry, targetType)); | ||
| 128 | } | ||
| 129 | |||
| 130 | @Override | ||
| 131 | public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry, ReferenceTargetType targetType) { | ||
| 132 | if (callerEntry.getParent().isJre()) { | ||
| 133 | return; | ||
| 134 | } | ||
| 135 | |||
| 136 | indexers.forEach(indexer -> indexer.indexFieldReference(callerEntry, referencedEntry, targetType)); | ||
| 137 | } | ||
| 138 | |||
| 139 | @Override | ||
| 140 | public void indexLambda(MethodDefEntry callerEntry, Lambda lambda, ReferenceTargetType targetType) { | ||
| 141 | if (callerEntry.getParent().isJre()) { | ||
| 142 | return; | ||
| 143 | } | ||
| 144 | |||
| 145 | indexers.forEach(indexer -> indexer.indexLambda(callerEntry, lambda, targetType)); | ||
| 146 | } | ||
| 147 | |||
| 148 | public EntryIndex getEntryIndex() { | ||
| 149 | return entryIndex; | ||
| 150 | } | ||
| 151 | |||
| 152 | public InheritanceIndex getInheritanceIndex() { | ||
| 153 | return this.inheritanceIndex; | ||
| 154 | } | ||
| 155 | |||
| 156 | public ReferenceIndex getReferenceIndex() { | ||
| 157 | return referenceIndex; | ||
| 158 | } | ||
| 159 | |||
| 160 | public BridgeMethodIndex getBridgeMethodIndex() { | ||
| 161 | return bridgeMethodIndex; | ||
| 162 | } | ||
| 163 | |||
| 164 | public PackageVisibilityIndex getPackageVisibilityIndex() { | ||
| 165 | return packageVisibilityIndex; | ||
| 166 | } | ||
| 167 | |||
| 168 | public EntryResolver getEntryResolver() { | ||
| 169 | return entryResolver; | ||
| 170 | } | ||
| 171 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java deleted file mode 100644 index f17e7c9..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java +++ /dev/null | |||
| @@ -1,28 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.analysis.ReferenceTargetType; | ||
| 4 | import cuchaz.enigma.translation.representation.Lambda; | ||
| 5 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 6 | |||
| 7 | public interface JarIndexer { | ||
| 8 | default void indexClass(ClassDefEntry classEntry) { | ||
| 9 | } | ||
| 10 | |||
| 11 | default void indexField(FieldDefEntry fieldEntry) { | ||
| 12 | } | ||
| 13 | |||
| 14 | default void indexMethod(MethodDefEntry methodEntry) { | ||
| 15 | } | ||
| 16 | |||
| 17 | default void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry, ReferenceTargetType targetType) { | ||
| 18 | } | ||
| 19 | |||
| 20 | default void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry, ReferenceTargetType targetType) { | ||
| 21 | } | ||
| 22 | |||
| 23 | default void indexLambda(MethodDefEntry callerEntry, Lambda lambda, ReferenceTargetType targetType) { | ||
| 24 | } | ||
| 25 | |||
| 26 | default void processIndex(JarIndex index) { | ||
| 27 | } | ||
| 28 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java b/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java deleted file mode 100644 index 63eb730..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java +++ /dev/null | |||
| @@ -1,147 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import com.google.common.collect.HashMultimap; | ||
| 4 | import com.google.common.collect.Lists; | ||
| 5 | import com.google.common.collect.Maps; | ||
| 6 | import com.google.common.collect.Sets; | ||
| 7 | import cuchaz.enigma.analysis.EntryReference; | ||
| 8 | import cuchaz.enigma.analysis.ReferenceTargetType; | ||
| 9 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 10 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 11 | |||
| 12 | import java.util.*; | ||
| 13 | |||
| 14 | public class PackageVisibilityIndex implements JarIndexer { | ||
| 15 | private static boolean requiresSamePackage(AccessFlags entryAcc, EntryReference ref, InheritanceIndex inheritanceIndex) { | ||
| 16 | if (entryAcc.isPublic()) { | ||
| 17 | return false; | ||
| 18 | } | ||
| 19 | |||
| 20 | if (entryAcc.isProtected()) { | ||
| 21 | ClassEntry contextClass = ref.context.getContainingClass(); | ||
| 22 | ClassEntry referencedClass = ref.entry.getContainingClass(); | ||
| 23 | |||
| 24 | if (!inheritanceIndex.getAncestors(contextClass).contains(referencedClass)) { | ||
| 25 | return true; // access to protected member not in superclass | ||
| 26 | } | ||
| 27 | |||
| 28 | if (ref.targetType.getKind() == ReferenceTargetType.Kind.NONE) { | ||
| 29 | return false; // access to superclass or static superclass member | ||
| 30 | } | ||
| 31 | |||
| 32 | // access to instance member only valid if target's class assignable to context class | ||
| 33 | return !(ref.targetType.getKind() == ReferenceTargetType.Kind.UNINITIALIZED || | ||
| 34 | ((ReferenceTargetType.ClassType) ref.targetType).getEntry().equals(contextClass) || | ||
| 35 | inheritanceIndex.getAncestors(((ReferenceTargetType.ClassType) ref.targetType).getEntry()).contains(contextClass)); | ||
| 36 | } | ||
| 37 | |||
| 38 | return true; | ||
| 39 | } | ||
| 40 | |||
| 41 | private final HashMultimap<ClassEntry, ClassEntry> connections = HashMultimap.create(); | ||
| 42 | private final List<Set<ClassEntry>> partitions = Lists.newArrayList(); | ||
| 43 | private final Map<ClassEntry, Set<ClassEntry>> classPartitions = Maps.newHashMap(); | ||
| 44 | |||
| 45 | private void addConnection(ClassEntry classA, ClassEntry classB) { | ||
| 46 | if (classA != classB) { | ||
| 47 | connections.put(classA, classB); | ||
| 48 | connections.put(classB, classA); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | private void buildPartition(Set<ClassEntry> unassignedClasses, Set<ClassEntry> partition, ClassEntry member) { | ||
| 53 | for (ClassEntry connected : connections.get(member)) { | ||
| 54 | if (unassignedClasses.remove(connected)) { | ||
| 55 | partition.add(connected); | ||
| 56 | buildPartition(unassignedClasses, partition, connected); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | private void addConnections(EntryIndex entryIndex, ReferenceIndex referenceIndex, InheritanceIndex inheritanceIndex) { | ||
| 62 | for (FieldEntry entry : entryIndex.getFields()) { | ||
| 63 | AccessFlags entryAcc = entryIndex.getFieldAccess(entry); | ||
| 64 | if (!entryAcc.isPublic() && !entryAcc.isPrivate()) { | ||
| 65 | for (EntryReference<FieldEntry, MethodDefEntry> ref : referenceIndex.getReferencesToField(entry)) { | ||
| 66 | if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) { | ||
| 67 | addConnection(ref.entry.getContainingClass(), ref.context.getContainingClass()); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | for (MethodEntry entry : entryIndex.getMethods()) { | ||
| 74 | AccessFlags entryAcc = entryIndex.getMethodAccess(entry); | ||
| 75 | if (!entryAcc.isPublic() && !entryAcc.isPrivate()) { | ||
| 76 | for (EntryReference<MethodEntry, MethodDefEntry> ref : referenceIndex.getReferencesToMethod(entry)) { | ||
| 77 | if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) { | ||
| 78 | addConnection(ref.entry.getContainingClass(), ref.context.getContainingClass()); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | for (ClassEntry entry : entryIndex.getClasses()) { | ||
| 85 | AccessFlags entryAcc = entryIndex.getClassAccess(entry); | ||
| 86 | if (!entryAcc.isPublic() && !entryAcc.isPrivate()) { | ||
| 87 | for (EntryReference<ClassEntry, FieldDefEntry> ref : referenceIndex.getFieldTypeReferencesToClass(entry)) { | ||
| 88 | if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) { | ||
| 89 | addConnection(ref.entry.getContainingClass(), ref.context.getContainingClass()); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | for (EntryReference<ClassEntry, MethodDefEntry> ref : referenceIndex.getMethodTypeReferencesToClass(entry)) { | ||
| 94 | if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) { | ||
| 95 | addConnection(ref.entry.getContainingClass(), ref.context.getContainingClass()); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | for (ClassEntry parent : inheritanceIndex.getParents(entry)) { | ||
| 101 | AccessFlags parentAcc = entryIndex.getClassAccess(parent); | ||
| 102 | if (parentAcc != null && !parentAcc.isPublic() && !parentAcc.isPrivate()) { | ||
| 103 | addConnection(entry, parent); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | ClassEntry outerClass = entry.getOuterClass(); | ||
| 108 | if (outerClass != null) { | ||
| 109 | addConnection(entry, outerClass); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | private void addPartitions(EntryIndex entryIndex) { | ||
| 115 | Set<ClassEntry> unassignedClasses = Sets.newHashSet(entryIndex.getClasses()); | ||
| 116 | while (!unassignedClasses.isEmpty()) { | ||
| 117 | Iterator<ClassEntry> iterator = unassignedClasses.iterator(); | ||
| 118 | ClassEntry initialEntry = iterator.next(); | ||
| 119 | iterator.remove(); | ||
| 120 | |||
| 121 | HashSet<ClassEntry> partition = Sets.newHashSet(); | ||
| 122 | partition.add(initialEntry); | ||
| 123 | buildPartition(unassignedClasses, partition, initialEntry); | ||
| 124 | partitions.add(partition); | ||
| 125 | for (ClassEntry entry : partition) { | ||
| 126 | classPartitions.put(entry, partition); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | public Collection<Set<ClassEntry>> getPartitions() { | ||
| 132 | return partitions; | ||
| 133 | } | ||
| 134 | |||
| 135 | public Set<ClassEntry> getPartition(ClassEntry classEntry) { | ||
| 136 | return classPartitions.get(classEntry); | ||
| 137 | } | ||
| 138 | |||
| 139 | @Override | ||
| 140 | public void processIndex(JarIndex index) { | ||
| 141 | EntryIndex entryIndex = index.getEntryIndex(); | ||
| 142 | ReferenceIndex referenceIndex = index.getReferenceIndex(); | ||
| 143 | InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); | ||
| 144 | addConnections(entryIndex, referenceIndex, inheritanceIndex); | ||
| 145 | addPartitions(entryIndex); | ||
| 146 | } | ||
| 147 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java b/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java deleted file mode 100644 index b6797c2..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java +++ /dev/null | |||
| @@ -1,148 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import com.google.common.collect.HashMultimap; | ||
| 4 | import com.google.common.collect.Multimap; | ||
| 5 | import cuchaz.enigma.analysis.EntryReference; | ||
| 6 | import cuchaz.enigma.analysis.ReferenceTargetType; | ||
| 7 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | ||
| 8 | import cuchaz.enigma.translation.representation.Lambda; | ||
| 9 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 10 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 11 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 12 | |||
| 13 | import java.util.Collection; | ||
| 14 | import java.util.Map; | ||
| 15 | |||
| 16 | public class ReferenceIndex implements JarIndexer { | ||
| 17 | private Multimap<MethodEntry, MethodEntry> methodReferences = HashMultimap.create(); | ||
| 18 | |||
| 19 | private Multimap<MethodEntry, EntryReference<MethodEntry, MethodDefEntry>> referencesToMethods = HashMultimap.create(); | ||
| 20 | private Multimap<ClassEntry, EntryReference<ClassEntry, MethodDefEntry>> referencesToClasses = HashMultimap.create(); | ||
| 21 | private Multimap<FieldEntry, EntryReference<FieldEntry, MethodDefEntry>> referencesToFields = HashMultimap.create(); | ||
| 22 | private Multimap<ClassEntry, EntryReference<ClassEntry, FieldDefEntry>> fieldTypeReferences = HashMultimap.create(); | ||
| 23 | private Multimap<ClassEntry, EntryReference<ClassEntry, MethodDefEntry>> methodTypeReferences = HashMultimap.create(); | ||
| 24 | |||
| 25 | @Override | ||
| 26 | public void indexMethod(MethodDefEntry methodEntry) { | ||
| 27 | indexMethodDescriptor(methodEntry, methodEntry.getDesc()); | ||
| 28 | } | ||
| 29 | |||
| 30 | private void indexMethodDescriptor(MethodDefEntry entry, MethodDescriptor descriptor) { | ||
| 31 | for (TypeDescriptor typeDescriptor : descriptor.getArgumentDescs()) { | ||
| 32 | indexMethodTypeDescriptor(entry, typeDescriptor); | ||
| 33 | } | ||
| 34 | indexMethodTypeDescriptor(entry, descriptor.getReturnDesc()); | ||
| 35 | } | ||
| 36 | |||
| 37 | private void indexMethodTypeDescriptor(MethodDefEntry method, TypeDescriptor typeDescriptor) { | ||
| 38 | if (typeDescriptor.isType()) { | ||
| 39 | ClassEntry referencedClass = typeDescriptor.getTypeEntry(); | ||
| 40 | methodTypeReferences.put(referencedClass, new EntryReference<>(referencedClass, referencedClass.getName(), method)); | ||
| 41 | } else if (typeDescriptor.isArray()) { | ||
| 42 | indexMethodTypeDescriptor(method, typeDescriptor.getArrayType()); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | @Override | ||
| 47 | public void indexField(FieldDefEntry fieldEntry) { | ||
| 48 | indexFieldTypeDescriptor(fieldEntry, fieldEntry.getDesc()); | ||
| 49 | } | ||
| 50 | |||
| 51 | private void indexFieldTypeDescriptor(FieldDefEntry field, TypeDescriptor typeDescriptor) { | ||
| 52 | if (typeDescriptor.isType()) { | ||
| 53 | ClassEntry referencedClass = typeDescriptor.getTypeEntry(); | ||
| 54 | fieldTypeReferences.put(referencedClass, new EntryReference<>(referencedClass, referencedClass.getName(), field)); | ||
| 55 | } else if (typeDescriptor.isArray()) { | ||
| 56 | indexFieldTypeDescriptor(field, typeDescriptor.getArrayType()); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | @Override | ||
| 61 | public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry, ReferenceTargetType targetType) { | ||
| 62 | referencesToMethods.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry, targetType)); | ||
| 63 | methodReferences.put(callerEntry, referencedEntry); | ||
| 64 | |||
| 65 | if (referencedEntry.isConstructor()) { | ||
| 66 | ClassEntry referencedClass = referencedEntry.getParent(); | ||
| 67 | referencesToClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedEntry.getName(), callerEntry, targetType)); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | @Override | ||
| 72 | public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry, ReferenceTargetType targetType) { | ||
| 73 | referencesToFields.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry, targetType)); | ||
| 74 | } | ||
| 75 | |||
| 76 | @Override | ||
| 77 | public void indexLambda(MethodDefEntry callerEntry, Lambda lambda, ReferenceTargetType targetType) { | ||
| 78 | if (lambda.getImplMethod() instanceof MethodEntry) { | ||
| 79 | indexMethodReference(callerEntry, (MethodEntry) lambda.getImplMethod(), targetType); | ||
| 80 | } else { | ||
| 81 | indexFieldReference(callerEntry, (FieldEntry) lambda.getImplMethod(), targetType); | ||
| 82 | } | ||
| 83 | |||
| 84 | indexMethodDescriptor(callerEntry, lambda.getInvokedType()); | ||
| 85 | indexMethodDescriptor(callerEntry, lambda.getSamMethodType()); | ||
| 86 | indexMethodDescriptor(callerEntry, lambda.getInstantiatedMethodType()); | ||
| 87 | } | ||
| 88 | |||
| 89 | @Override | ||
| 90 | public void processIndex(JarIndex index) { | ||
| 91 | methodReferences = remapReferences(index, methodReferences); | ||
| 92 | referencesToMethods = remapReferencesTo(index, referencesToMethods); | ||
| 93 | referencesToClasses = remapReferencesTo(index, referencesToClasses); | ||
| 94 | referencesToFields = remapReferencesTo(index, referencesToFields); | ||
| 95 | fieldTypeReferences = remapReferencesTo(index, fieldTypeReferences); | ||
| 96 | methodTypeReferences = remapReferencesTo(index, methodTypeReferences); | ||
| 97 | } | ||
| 98 | |||
| 99 | private <K extends Entry<?>, V extends Entry<?>> Multimap<K, V> remapReferences(JarIndex index, Multimap<K, V> multimap) { | ||
| 100 | final int keySetSize = multimap.keySet().size(); | ||
| 101 | Multimap<K, V> resolved = HashMultimap.create(multimap.keySet().size(), keySetSize == 0 ? 0 : multimap.size() / keySetSize); | ||
| 102 | for (Map.Entry<K, V> entry : multimap.entries()) { | ||
| 103 | resolved.put(remap(index, entry.getKey()), remap(index, entry.getValue())); | ||
| 104 | } | ||
| 105 | return resolved; | ||
| 106 | } | ||
| 107 | |||
| 108 | private <E extends Entry<?>, C extends Entry<?>> Multimap<E, EntryReference<E, C>> remapReferencesTo(JarIndex index, Multimap<E, EntryReference<E, C>> multimap) { | ||
| 109 | final int keySetSize = multimap.keySet().size(); | ||
| 110 | Multimap<E, EntryReference<E, C>> resolved = HashMultimap.create(keySetSize, keySetSize == 0 ? 0 : multimap.size() / keySetSize); | ||
| 111 | for (Map.Entry<E, EntryReference<E, C>> entry : multimap.entries()) { | ||
| 112 | resolved.put(remap(index, entry.getKey()), remap(index, entry.getValue())); | ||
| 113 | } | ||
| 114 | return resolved; | ||
| 115 | } | ||
| 116 | |||
| 117 | private <E extends Entry<?>> E remap(JarIndex index, E entry) { | ||
| 118 | return index.getEntryResolver().resolveFirstEntry(entry, ResolutionStrategy.RESOLVE_CLOSEST); | ||
| 119 | } | ||
| 120 | |||
| 121 | private <E extends Entry<?>, C extends Entry<?>> EntryReference<E, C> remap(JarIndex index, EntryReference<E, C> reference) { | ||
| 122 | return index.getEntryResolver().resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST); | ||
| 123 | } | ||
| 124 | |||
| 125 | public Collection<MethodEntry> getMethodsReferencedBy(MethodEntry entry) { | ||
| 126 | return methodReferences.get(entry); | ||
| 127 | } | ||
| 128 | |||
| 129 | public Collection<EntryReference<FieldEntry, MethodDefEntry>> getReferencesToField(FieldEntry entry) { | ||
| 130 | return referencesToFields.get(entry); | ||
| 131 | } | ||
| 132 | |||
| 133 | public Collection<EntryReference<ClassEntry, MethodDefEntry>> getReferencesToClass(ClassEntry entry) { | ||
| 134 | return referencesToClasses.get(entry); | ||
| 135 | } | ||
| 136 | |||
| 137 | public Collection<EntryReference<MethodEntry, MethodDefEntry>> getReferencesToMethod(MethodEntry entry) { | ||
| 138 | return referencesToMethods.get(entry); | ||
| 139 | } | ||
| 140 | |||
| 141 | public Collection<EntryReference<ClassEntry, FieldDefEntry>> getFieldTypeReferencesToClass(ClassEntry entry) { | ||
| 142 | return fieldTypeReferences.get(entry); | ||
| 143 | } | ||
| 144 | |||
| 145 | public Collection<EntryReference<ClassEntry, MethodDefEntry>> getMethodTypeReferencesToClass(ClassEntry entry) { | ||
| 146 | return methodTypeReferences.get(entry); | ||
| 147 | } | ||
| 148 | } | ||