summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/analysis
diff options
context:
space:
mode:
authorGravatar Runemoro2020-06-03 13:39:42 -0400
committerGravatar GitHub2020-06-03 18:39:42 +0100
commit0f47403d0220757fed189b76e2071e25b1025cb8 (patch)
tree879bf72c4476f0a5e0d82da99d7ff2b2276bcaca /src/main/java/cuchaz/enigma/analysis
parentFix search dialog hanging for a short time sometimes (#250) (diff)
downloadenigma-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')
-rw-r--r--src/main/java/cuchaz/enigma/analysis/Access.java43
-rw-r--r--src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java157
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassCache.java127
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java72
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java72
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java94
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryReference.java140
-rw-r--r--src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java83
-rw-r--r--src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java154
-rw-r--r--src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java74
-rw-r--r--src/main/java/cuchaz/enigma/analysis/InterpreterPair.java131
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java85
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java95
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java19
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java113
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java74
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java20
-rw-r--r--src/main/java/cuchaz/enigma/analysis/Token.java72
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java156
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java102
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java40
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java180
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java127
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/JarIndex.java171
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java28
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java147
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java148
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
12package cuchaz.enigma.analysis;
13
14import cuchaz.enigma.translation.representation.AccessFlags;
15
16import java.lang.reflect.Modifier;
17
18public 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 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.api.EnigmaPlugin;
4import cuchaz.enigma.api.EnigmaPluginContext;
5import cuchaz.enigma.api.service.JarIndexerService;
6import cuchaz.enigma.api.service.NameProposalService;
7import cuchaz.enigma.source.DecompilerService;
8import cuchaz.enigma.source.Decompilers;
9import cuchaz.enigma.source.procyon.ProcyonDecompiler;
10import cuchaz.enigma.translation.representation.TypeDescriptor;
11import cuchaz.enigma.translation.representation.entry.ClassEntry;
12import cuchaz.enigma.translation.representation.entry.Entry;
13import cuchaz.enigma.translation.representation.entry.FieldEntry;
14import cuchaz.enigma.utils.Pair;
15import cuchaz.enigma.utils.Utils;
16import org.objectweb.asm.ClassReader;
17import org.objectweb.asm.ClassVisitor;
18import org.objectweb.asm.FieldVisitor;
19import org.objectweb.asm.MethodVisitor;
20import org.objectweb.asm.Opcodes;
21import org.objectweb.asm.tree.AbstractInsnNode;
22import org.objectweb.asm.tree.FieldInsnNode;
23import org.objectweb.asm.tree.InsnList;
24import org.objectweb.asm.tree.LdcInsnNode;
25import org.objectweb.asm.tree.MethodInsnNode;
26import org.objectweb.asm.tree.MethodNode;
27import org.objectweb.asm.tree.analysis.Analyzer;
28import org.objectweb.asm.tree.analysis.Frame;
29import org.objectweb.asm.tree.analysis.SourceInterpreter;
30import org.objectweb.asm.tree.analysis.SourceValue;
31
32import java.util.ArrayList;
33import java.util.HashMap;
34import java.util.HashSet;
35import java.util.List;
36import java.util.Map;
37import java.util.Optional;
38import java.util.Set;
39
40public 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 @@
1package cuchaz.enigma.analysis;
2
3import com.google.common.cache.Cache;
4import com.google.common.cache.CacheBuilder;
5import com.google.common.collect.ImmutableSet;
6import cuchaz.enigma.ClassProvider;
7import cuchaz.enigma.ProgressListener;
8import cuchaz.enigma.analysis.index.JarIndex;
9import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor;
10import cuchaz.enigma.utils.Utils;
11import org.objectweb.asm.ClassReader;
12import org.objectweb.asm.ClassVisitor;
13import org.objectweb.asm.Opcodes;
14import org.objectweb.asm.tree.ClassNode;
15
16import javax.annotation.Nullable;
17import java.io.IOException;
18import java.nio.file.FileSystem;
19import java.nio.file.FileSystems;
20import java.nio.file.Files;
21import java.nio.file.Path;
22import java.util.concurrent.ExecutionException;
23import java.util.concurrent.TimeUnit;
24import java.util.function.Supplier;
25
26public 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
12package cuchaz.enigma.analysis;
13
14import com.google.common.collect.Lists;
15import cuchaz.enigma.analysis.index.InheritanceIndex;
16import cuchaz.enigma.analysis.index.JarIndex;
17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.representation.entry.ClassEntry;
19import cuchaz.enigma.translation.representation.entry.MethodEntry;
20
21import javax.swing.tree.DefaultMutableTreeNode;
22import java.util.Collection;
23import java.util.List;
24
25public 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
12package cuchaz.enigma.analysis;
13
14import com.google.common.collect.Lists;
15import cuchaz.enigma.analysis.index.InheritanceIndex;
16import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.representation.entry.ClassEntry;
18
19import javax.swing.tree.DefaultMutableTreeNode;
20import java.util.List;
21
22public 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
12package cuchaz.enigma.analysis;
13
14import com.google.common.collect.Sets;
15import cuchaz.enigma.analysis.index.JarIndex;
16import cuchaz.enigma.analysis.index.ReferenceIndex;
17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.representation.entry.ClassEntry;
19import cuchaz.enigma.translation.representation.entry.Entry;
20import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
21
22import javax.swing.tree.DefaultMutableTreeNode;
23import javax.swing.tree.TreeNode;
24import java.util.Set;
25
26public 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
12package cuchaz.enigma.analysis;
13
14import cuchaz.enigma.translation.Translatable;
15import cuchaz.enigma.translation.Translator;
16import cuchaz.enigma.translation.mapping.EntryMapping;
17import cuchaz.enigma.translation.mapping.EntryResolver;
18import cuchaz.enigma.translation.mapping.EntryMap;
19import cuchaz.enigma.translation.representation.entry.ClassEntry;
20import cuchaz.enigma.translation.representation.entry.Entry;
21import cuchaz.enigma.translation.representation.entry.MethodEntry;
22import cuchaz.enigma.utils.Utils;
23
24import java.util.Arrays;
25import java.util.List;
26
27public 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
12package cuchaz.enigma.analysis;
13
14import cuchaz.enigma.analysis.index.JarIndex;
15import cuchaz.enigma.analysis.index.ReferenceIndex;
16import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.representation.entry.FieldEntry;
18import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
19import cuchaz.enigma.translation.representation.entry.MethodEntry;
20
21import javax.swing.tree.DefaultMutableTreeNode;
22
23public 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 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.analysis.index.EntryIndex;
4import cuchaz.enigma.analysis.index.InheritanceIndex;
5import cuchaz.enigma.translation.representation.AccessFlags;
6import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
7import cuchaz.enigma.translation.representation.entry.ClassEntry;
8import cuchaz.enigma.utils.Utils;
9import org.objectweb.asm.Type;
10import org.objectweb.asm.tree.analysis.BasicValue;
11import org.objectweb.asm.tree.analysis.SimpleVerifier;
12
13import java.util.Set;
14
15public 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 @@
1package cuchaz.enigma.analysis;
2
3import com.google.common.collect.Lists;
4import cuchaz.enigma.analysis.index.JarIndex;
5import cuchaz.enigma.translation.Translator;
6import cuchaz.enigma.translation.mapping.EntryResolver;
7import cuchaz.enigma.translation.mapping.ResolutionStrategy;
8import cuchaz.enigma.translation.representation.entry.ClassEntry;
9import cuchaz.enigma.translation.representation.entry.MethodEntry;
10
11import java.util.Collection;
12import java.util.List;
13
14public 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 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.utils.Utils;
4import org.objectweb.asm.Opcodes;
5import org.objectweb.asm.Type;
6import org.objectweb.asm.tree.AbstractInsnNode;
7import org.objectweb.asm.tree.analysis.AnalyzerException;
8import org.objectweb.asm.tree.analysis.Interpreter;
9import org.objectweb.asm.tree.analysis.Value;
10
11import java.util.List;
12import java.util.Objects;
13import java.util.stream.Collectors;
14
15public 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
12package cuchaz.enigma.analysis;
13
14import com.google.common.collect.Lists;
15import cuchaz.enigma.analysis.index.EntryIndex;
16import cuchaz.enigma.analysis.index.InheritanceIndex;
17import cuchaz.enigma.analysis.index.JarIndex;
18import cuchaz.enigma.translation.Translator;
19import cuchaz.enigma.translation.representation.entry.ClassEntry;
20import cuchaz.enigma.translation.representation.entry.MethodEntry;
21
22import javax.swing.tree.DefaultMutableTreeNode;
23import java.util.Collection;
24import java.util.List;
25
26public 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
12package cuchaz.enigma.analysis;
13
14import cuchaz.enigma.analysis.index.EntryIndex;
15import cuchaz.enigma.analysis.index.InheritanceIndex;
16import cuchaz.enigma.analysis.index.JarIndex;
17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.representation.entry.ClassEntry;
19import cuchaz.enigma.translation.representation.entry.MethodEntry;
20
21import javax.swing.tree.DefaultMutableTreeNode;
22
23public 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 @@
1package cuchaz.enigma.analysis;
2
3import org.objectweb.asm.tree.MethodNode;
4
5import java.util.function.Consumer;
6
7public 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
12package cuchaz.enigma.analysis;
13
14import com.google.common.collect.Sets;
15import cuchaz.enigma.analysis.index.JarIndex;
16import cuchaz.enigma.analysis.index.ReferenceIndex;
17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.mapping.EntryResolver;
19import cuchaz.enigma.translation.representation.entry.Entry;
20import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
21import cuchaz.enigma.translation.representation.entry.MethodEntry;
22
23import javax.swing.tree.DefaultMutableTreeNode;
24import javax.swing.tree.TreeNode;
25import java.util.ArrayList;
26import java.util.Collection;
27import java.util.Set;
28
29public 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 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.translation.representation.entry.ClassEntry;
4
5public 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
12package cuchaz.enigma.analysis;
13
14import cuchaz.enigma.translation.representation.entry.Entry;
15
16public 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
12package cuchaz.enigma.analysis;
13
14public 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 @@
1package cuchaz.enigma.analysis.index;
2
3import com.google.common.collect.Maps;
4import cuchaz.enigma.translation.representation.AccessFlags;
5import cuchaz.enigma.translation.representation.MethodDescriptor;
6import cuchaz.enigma.translation.representation.TypeDescriptor;
7import cuchaz.enigma.translation.representation.entry.ClassEntry;
8import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
9import cuchaz.enigma.translation.representation.entry.MethodEntry;
10
11import javax.annotation.Nullable;
12import java.util.*;
13
14public 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 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.translation.representation.AccessFlags;
4import cuchaz.enigma.translation.representation.entry.*;
5
6import javax.annotation.Nullable;
7import java.util.Collection;
8import java.util.HashMap;
9import java.util.Map;
10
11public 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 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
4import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
5import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
6import org.objectweb.asm.ClassVisitor;
7import org.objectweb.asm.FieldVisitor;
8import org.objectweb.asm.MethodVisitor;
9
10public 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 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.analysis.IndexSimpleVerifier;
4import cuchaz.enigma.analysis.InterpreterPair;
5import cuchaz.enigma.analysis.MethodNodeWithAction;
6import cuchaz.enigma.analysis.ReferenceTargetType;
7import cuchaz.enigma.translation.representation.AccessFlags;
8import cuchaz.enigma.translation.representation.Lambda;
9import cuchaz.enigma.translation.representation.MethodDescriptor;
10import cuchaz.enigma.translation.representation.Signature;
11import cuchaz.enigma.translation.representation.entry.*;
12import org.objectweb.asm.*;
13import org.objectweb.asm.tree.AbstractInsnNode;
14import org.objectweb.asm.tree.FieldInsnNode;
15import org.objectweb.asm.tree.InvokeDynamicInsnNode;
16import org.objectweb.asm.tree.MethodInsnNode;
17import org.objectweb.asm.tree.analysis.*;
18
19import java.util.List;
20import java.util.stream.Collectors;
21
22public 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
12package cuchaz.enigma.analysis.index;
13
14import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Multimap;
16import com.google.common.collect.Sets;
17import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
18import cuchaz.enigma.translation.representation.entry.ClassEntry;
19
20import java.util.Collection;
21import java.util.HashSet;
22import java.util.LinkedList;
23import java.util.Set;
24
25public 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
12package cuchaz.enigma.analysis.index;
13
14import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Multimap;
16import cuchaz.enigma.ProgressListener;
17import cuchaz.enigma.analysis.ClassCache;
18import cuchaz.enigma.analysis.ReferenceTargetType;
19import cuchaz.enigma.translation.mapping.EntryResolver;
20import cuchaz.enigma.translation.mapping.IndexEntryResolver;
21import cuchaz.enigma.translation.representation.Lambda;
22import cuchaz.enigma.translation.representation.entry.*;
23import cuchaz.enigma.utils.I18n;
24
25import cuchaz.enigma.utils.Utils;
26import org.objectweb.asm.ClassReader;
27import org.objectweb.asm.Opcodes;
28
29import java.util.Arrays;
30import java.util.Collection;
31
32public 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 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.analysis.ReferenceTargetType;
4import cuchaz.enigma.translation.representation.Lambda;
5import cuchaz.enigma.translation.representation.entry.*;
6
7public 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 @@
1package cuchaz.enigma.analysis.index;
2
3import com.google.common.collect.HashMultimap;
4import com.google.common.collect.Lists;
5import com.google.common.collect.Maps;
6import com.google.common.collect.Sets;
7import cuchaz.enigma.analysis.EntryReference;
8import cuchaz.enigma.analysis.ReferenceTargetType;
9import cuchaz.enigma.translation.representation.AccessFlags;
10import cuchaz.enigma.translation.representation.entry.*;
11
12import java.util.*;
13
14public 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 @@
1package cuchaz.enigma.analysis.index;
2
3import com.google.common.collect.HashMultimap;
4import com.google.common.collect.Multimap;
5import cuchaz.enigma.analysis.EntryReference;
6import cuchaz.enigma.analysis.ReferenceTargetType;
7import cuchaz.enigma.translation.mapping.ResolutionStrategy;
8import cuchaz.enigma.translation.representation.Lambda;
9import cuchaz.enigma.translation.representation.MethodDescriptor;
10import cuchaz.enigma.translation.representation.TypeDescriptor;
11import cuchaz.enigma.translation.representation.entry.*;
12
13import java.util.Collection;
14import java.util.Map;
15
16public 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}