summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/analysis/index
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/index
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/index')
-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
9 files changed, 0 insertions, 1099 deletions
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}