diff options
| author | 2020-06-03 13:39:42 -0400 | |
|---|---|---|
| committer | 2020-06-03 18:39:42 +0100 | |
| commit | 0f47403d0220757fed189b76e2071e25b1025cb8 (patch) | |
| tree | 879bf72c4476f0a5e0d82da99d7ff2b2276bcaca /src/main/java/cuchaz/enigma/analysis/index | |
| parent | Fix search dialog hanging for a short time sometimes (#250) (diff) | |
| download | enigma-fork-0f47403d0220757fed189b76e2071e25b1025cb8.tar.gz enigma-fork-0f47403d0220757fed189b76e2071e25b1025cb8.tar.xz enigma-fork-0f47403d0220757fed189b76e2071e25b1025cb8.zip | |
Split GUI code to separate module (#242)
* Split into modules
* Post merge compile fixes
Co-authored-by: modmuss50 <modmuss50@gmail.com>
Diffstat (limited to 'src/main/java/cuchaz/enigma/analysis/index')
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 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import com.google.common.collect.Maps; | ||
| 4 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 5 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 6 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 7 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 8 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 9 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 10 | |||
| 11 | import javax.annotation.Nullable; | ||
| 12 | import java.util.*; | ||
| 13 | |||
| 14 | public class BridgeMethodIndex implements JarIndexer { | ||
| 15 | private final EntryIndex entryIndex; | ||
| 16 | private final InheritanceIndex inheritanceIndex; | ||
| 17 | private final ReferenceIndex referenceIndex; | ||
| 18 | |||
| 19 | private final Map<MethodEntry, MethodEntry> bridgeToSpecialized = Maps.newHashMap(); | ||
| 20 | private final Map<MethodEntry, MethodEntry> specializedToBridge = Maps.newHashMap(); | ||
| 21 | |||
| 22 | public BridgeMethodIndex(EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex) { | ||
| 23 | this.entryIndex = entryIndex; | ||
| 24 | this.inheritanceIndex = inheritanceIndex; | ||
| 25 | this.referenceIndex = referenceIndex; | ||
| 26 | } | ||
| 27 | |||
| 28 | public void findBridgeMethods() { | ||
| 29 | // look for access and bridged methods | ||
| 30 | for (MethodEntry methodEntry : entryIndex.getMethods()) { | ||
| 31 | MethodDefEntry methodDefEntry = (MethodDefEntry) methodEntry; | ||
| 32 | |||
| 33 | AccessFlags access = methodDefEntry.getAccess(); | ||
| 34 | if (access == null || !access.isSynthetic()) { | ||
| 35 | continue; | ||
| 36 | } | ||
| 37 | |||
| 38 | indexSyntheticMethod(methodDefEntry, access); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | @Override | ||
| 43 | public void processIndex(JarIndex index) { | ||
| 44 | Map<MethodEntry, MethodEntry> copiedAccessToBridge = new HashMap<>(specializedToBridge); | ||
| 45 | |||
| 46 | for (Map.Entry<MethodEntry, MethodEntry> entry : copiedAccessToBridge.entrySet()) { | ||
| 47 | MethodEntry specializedEntry = entry.getKey(); | ||
| 48 | MethodEntry bridgeEntry = entry.getValue(); | ||
| 49 | if (bridgeEntry.getName().equals(specializedEntry.getName())) { | ||
| 50 | continue; | ||
| 51 | } | ||
| 52 | |||
| 53 | MethodEntry renamedSpecializedEntry = specializedEntry.withName(bridgeEntry.getName()); | ||
| 54 | specializedToBridge.put(renamedSpecializedEntry, specializedToBridge.get(specializedEntry)); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | private void indexSyntheticMethod(MethodDefEntry syntheticMethod, AccessFlags access) { | ||
| 59 | MethodEntry specializedMethod = findSpecializedMethod(syntheticMethod); | ||
| 60 | if (specializedMethod == null) { | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | |||
| 64 | if (access.isBridge() || isPotentialBridge(syntheticMethod, specializedMethod)) { | ||
| 65 | bridgeToSpecialized.put(syntheticMethod, specializedMethod); | ||
| 66 | specializedToBridge.put(specializedMethod, syntheticMethod); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | private MethodEntry findSpecializedMethod(MethodEntry method) { | ||
| 71 | // we want to find all compiler-added methods that directly call another with no processing | ||
| 72 | |||
| 73 | // get all the methods that we call | ||
| 74 | final Collection<MethodEntry> referencedMethods = referenceIndex.getMethodsReferencedBy(method); | ||
| 75 | |||
| 76 | // is there just one? | ||
| 77 | if (referencedMethods.size() != 1) { | ||
| 78 | return null; | ||
| 79 | } | ||
| 80 | |||
| 81 | return referencedMethods.stream().findFirst().orElse(null); | ||
| 82 | } | ||
| 83 | |||
| 84 | private boolean isPotentialBridge(MethodDefEntry bridgeMethod, MethodEntry specializedMethod) { | ||
| 85 | // Bridge methods only exist for inheritance purposes, if we're private, final, or static, we cannot be inherited | ||
| 86 | AccessFlags bridgeAccess = bridgeMethod.getAccess(); | ||
| 87 | if (bridgeAccess.isPrivate() || bridgeAccess.isFinal() || bridgeAccess.isStatic()) { | ||
| 88 | return false; | ||
| 89 | } | ||
| 90 | |||
| 91 | MethodDescriptor bridgeDesc = bridgeMethod.getDesc(); | ||
| 92 | MethodDescriptor specializedDesc = specializedMethod.getDesc(); | ||
| 93 | List<TypeDescriptor> bridgeArguments = bridgeDesc.getArgumentDescs(); | ||
| 94 | List<TypeDescriptor> specializedArguments = specializedDesc.getArgumentDescs(); | ||
| 95 | |||
| 96 | // A bridge method will always have the same number of arguments | ||
| 97 | if (bridgeArguments.size() != specializedArguments.size()) { | ||
| 98 | return false; | ||
| 99 | } | ||
| 100 | |||
| 101 | // Check that all argument types are bridge-compatible | ||
| 102 | for (int i = 0; i < bridgeArguments.size(); i++) { | ||
| 103 | if (!areTypesBridgeCompatible(bridgeArguments.get(i), specializedArguments.get(i))) { | ||
| 104 | return false; | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | // Check that the return type is bridge-compatible | ||
| 109 | return areTypesBridgeCompatible(bridgeDesc.getReturnDesc(), specializedDesc.getReturnDesc()); | ||
| 110 | } | ||
| 111 | |||
| 112 | private boolean areTypesBridgeCompatible(TypeDescriptor bridgeDesc, TypeDescriptor specializedDesc) { | ||
| 113 | if (bridgeDesc.equals(specializedDesc)) { | ||
| 114 | return true; | ||
| 115 | } | ||
| 116 | |||
| 117 | // Either the descs will be equal, or they are both types and different through a generic | ||
| 118 | if (bridgeDesc.isType() && specializedDesc.isType()) { | ||
| 119 | ClassEntry bridgeType = bridgeDesc.getTypeEntry(); | ||
| 120 | ClassEntry accessedType = specializedDesc.getTypeEntry(); | ||
| 121 | |||
| 122 | // If the given types are completely unrelated to each other, this can't be bridge compatible | ||
| 123 | InheritanceIndex.Relation relation = inheritanceIndex.computeClassRelation(accessedType, bridgeType); | ||
| 124 | return relation != InheritanceIndex.Relation.UNRELATED; | ||
| 125 | } | ||
| 126 | |||
| 127 | return false; | ||
| 128 | } | ||
| 129 | |||
| 130 | public boolean isBridgeMethod(MethodEntry entry) { | ||
| 131 | return bridgeToSpecialized.containsKey(entry); | ||
| 132 | } | ||
| 133 | |||
| 134 | public boolean isSpecializedMethod(MethodEntry entry) { | ||
| 135 | return specializedToBridge.containsKey(entry); | ||
| 136 | } | ||
| 137 | |||
| 138 | @Nullable | ||
| 139 | public MethodEntry getBridgeFromSpecialized(MethodEntry specialized) { | ||
| 140 | return specializedToBridge.get(specialized); | ||
| 141 | } | ||
| 142 | |||
| 143 | public MethodEntry getSpecializedFromBridge(MethodEntry bridge) { | ||
| 144 | return bridgeToSpecialized.get(bridge); | ||
| 145 | } | ||
| 146 | |||
| 147 | /** Includes "renamed specialized -> bridge" entries. */ | ||
| 148 | public Map<MethodEntry, MethodEntry> getSpecializedToBridge() { | ||
| 149 | return Collections.unmodifiableMap(specializedToBridge); | ||
| 150 | } | ||
| 151 | |||
| 152 | /** Only "bridge -> original name" entries. **/ | ||
| 153 | public Map<MethodEntry, MethodEntry> getBridgeToSpecialized() { | ||
| 154 | return Collections.unmodifiableMap(bridgeToSpecialized); | ||
| 155 | } | ||
| 156 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java b/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java deleted file mode 100644 index 9a2726e..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java +++ /dev/null | |||
| @@ -1,102 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 4 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 5 | |||
| 6 | import javax.annotation.Nullable; | ||
| 7 | import java.util.Collection; | ||
| 8 | import java.util.HashMap; | ||
| 9 | import java.util.Map; | ||
| 10 | |||
| 11 | public class EntryIndex implements JarIndexer { | ||
| 12 | private Map<ClassEntry, AccessFlags> classes = new HashMap<>(); | ||
| 13 | private Map<FieldEntry, AccessFlags> fields = new HashMap<>(); | ||
| 14 | private Map<MethodEntry, AccessFlags> methods = new HashMap<>(); | ||
| 15 | private Map<ClassEntry, ClassDefEntry> definitions = new HashMap<>(); | ||
| 16 | |||
| 17 | @Override | ||
| 18 | public void indexClass(ClassDefEntry classEntry) { | ||
| 19 | definitions.put(classEntry, classEntry); | ||
| 20 | classes.put(classEntry, classEntry.getAccess()); | ||
| 21 | } | ||
| 22 | |||
| 23 | @Override | ||
| 24 | public void indexMethod(MethodDefEntry methodEntry) { | ||
| 25 | methods.put(methodEntry, methodEntry.getAccess()); | ||
| 26 | } | ||
| 27 | |||
| 28 | @Override | ||
| 29 | public void indexField(FieldDefEntry fieldEntry) { | ||
| 30 | fields.put(fieldEntry, fieldEntry.getAccess()); | ||
| 31 | } | ||
| 32 | |||
| 33 | public boolean hasClass(ClassEntry entry) { | ||
| 34 | return classes.containsKey(entry); | ||
| 35 | } | ||
| 36 | |||
| 37 | public boolean hasMethod(MethodEntry entry) { | ||
| 38 | return methods.containsKey(entry); | ||
| 39 | } | ||
| 40 | |||
| 41 | public boolean hasField(FieldEntry entry) { | ||
| 42 | return fields.containsKey(entry); | ||
| 43 | } | ||
| 44 | |||
| 45 | public boolean hasEntry(Entry<?> entry) { | ||
| 46 | if (entry instanceof ClassEntry) { | ||
| 47 | return hasClass((ClassEntry) entry); | ||
| 48 | } else if (entry instanceof MethodEntry) { | ||
| 49 | return hasMethod((MethodEntry) entry); | ||
| 50 | } else if (entry instanceof FieldEntry) { | ||
| 51 | return hasField((FieldEntry) entry); | ||
| 52 | } else if (entry instanceof LocalVariableEntry) { | ||
| 53 | return hasMethod(((LocalVariableEntry) entry).getParent()); | ||
| 54 | } | ||
| 55 | |||
| 56 | return false; | ||
| 57 | } | ||
| 58 | |||
| 59 | @Nullable | ||
| 60 | public AccessFlags getMethodAccess(MethodEntry entry) { | ||
| 61 | return methods.get(entry); | ||
| 62 | } | ||
| 63 | |||
| 64 | @Nullable | ||
| 65 | public AccessFlags getFieldAccess(FieldEntry entry) { | ||
| 66 | return fields.get(entry); | ||
| 67 | } | ||
| 68 | |||
| 69 | @Nullable | ||
| 70 | public AccessFlags getClassAccess(ClassEntry entry) { | ||
| 71 | return classes.get(entry); | ||
| 72 | } | ||
| 73 | |||
| 74 | @Nullable | ||
| 75 | public AccessFlags getEntryAccess(Entry<?> entry) { | ||
| 76 | if (entry instanceof MethodEntry) { | ||
| 77 | return getMethodAccess((MethodEntry) entry); | ||
| 78 | } else if (entry instanceof FieldEntry) { | ||
| 79 | return getFieldAccess((FieldEntry) entry); | ||
| 80 | } else if (entry instanceof LocalVariableEntry) { | ||
| 81 | return getMethodAccess(((LocalVariableEntry) entry).getParent()); | ||
| 82 | } | ||
| 83 | |||
| 84 | return null; | ||
| 85 | } | ||
| 86 | |||
| 87 | public ClassDefEntry getDefinition(ClassEntry entry) { | ||
| 88 | return definitions.get(entry); | ||
| 89 | } | ||
| 90 | |||
| 91 | public Collection<ClassEntry> getClasses() { | ||
| 92 | return classes.keySet(); | ||
| 93 | } | ||
| 94 | |||
| 95 | public Collection<MethodEntry> getMethods() { | ||
| 96 | return methods.keySet(); | ||
| 97 | } | ||
| 98 | |||
| 99 | public Collection<FieldEntry> getFields() { | ||
| 100 | return fields.keySet(); | ||
| 101 | } | ||
| 102 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java deleted file mode 100644 index f9cb23c..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java +++ /dev/null | |||
| @@ -1,40 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | ||
| 4 | import cuchaz.enigma.translation.representation.entry.FieldDefEntry; | ||
| 5 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 6 | import org.objectweb.asm.ClassVisitor; | ||
| 7 | import org.objectweb.asm.FieldVisitor; | ||
| 8 | import org.objectweb.asm.MethodVisitor; | ||
| 9 | |||
| 10 | public class IndexClassVisitor extends ClassVisitor { | ||
| 11 | private final JarIndexer indexer; | ||
| 12 | private ClassDefEntry classEntry; | ||
| 13 | |||
| 14 | public IndexClassVisitor(JarIndex indexer, int api) { | ||
| 15 | super(api); | ||
| 16 | this.indexer = indexer; | ||
| 17 | } | ||
| 18 | |||
| 19 | @Override | ||
| 20 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { | ||
| 21 | classEntry = ClassDefEntry.parse(access, name, signature, superName, interfaces); | ||
| 22 | indexer.indexClass(classEntry); | ||
| 23 | |||
| 24 | super.visit(version, access, name, signature, superName, interfaces); | ||
| 25 | } | ||
| 26 | |||
| 27 | @Override | ||
| 28 | public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { | ||
| 29 | indexer.indexField(FieldDefEntry.parse(classEntry, access, name, desc, signature)); | ||
| 30 | |||
| 31 | return super.visitField(access, name, desc, signature, value); | ||
| 32 | } | ||
| 33 | |||
| 34 | @Override | ||
| 35 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { | ||
| 36 | indexer.indexMethod(MethodDefEntry.parse(classEntry, access, name, desc, signature)); | ||
| 37 | |||
| 38 | return super.visitMethod(access, name, desc, signature, exceptions); | ||
| 39 | } | ||
| 40 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java b/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java deleted file mode 100644 index f3d419e..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java +++ /dev/null | |||
| @@ -1,180 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.analysis.IndexSimpleVerifier; | ||
| 4 | import cuchaz.enigma.analysis.InterpreterPair; | ||
| 5 | import cuchaz.enigma.analysis.MethodNodeWithAction; | ||
| 6 | import cuchaz.enigma.analysis.ReferenceTargetType; | ||
| 7 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 8 | import cuchaz.enigma.translation.representation.Lambda; | ||
| 9 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 10 | import cuchaz.enigma.translation.representation.Signature; | ||
| 11 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 12 | import org.objectweb.asm.*; | ||
| 13 | import org.objectweb.asm.tree.AbstractInsnNode; | ||
| 14 | import org.objectweb.asm.tree.FieldInsnNode; | ||
| 15 | import org.objectweb.asm.tree.InvokeDynamicInsnNode; | ||
| 16 | import org.objectweb.asm.tree.MethodInsnNode; | ||
| 17 | import org.objectweb.asm.tree.analysis.*; | ||
| 18 | |||
| 19 | import java.util.List; | ||
| 20 | import java.util.stream.Collectors; | ||
| 21 | |||
| 22 | public class IndexReferenceVisitor extends ClassVisitor { | ||
| 23 | private final JarIndexer indexer; | ||
| 24 | private final EntryIndex entryIndex; | ||
| 25 | private final InheritanceIndex inheritanceIndex; | ||
| 26 | private ClassEntry classEntry; | ||
| 27 | private String className; | ||
| 28 | |||
| 29 | public IndexReferenceVisitor(JarIndexer indexer, EntryIndex entryIndex, InheritanceIndex inheritanceIndex, int api) { | ||
| 30 | super(api); | ||
| 31 | this.indexer = indexer; | ||
| 32 | this.entryIndex = entryIndex; | ||
| 33 | this.inheritanceIndex = inheritanceIndex; | ||
| 34 | } | ||
| 35 | |||
| 36 | @Override | ||
| 37 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { | ||
| 38 | classEntry = new ClassEntry(name); | ||
| 39 | className = name; | ||
| 40 | } | ||
| 41 | |||
| 42 | @Override | ||
| 43 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { | ||
| 44 | MethodDefEntry entry = new MethodDefEntry(classEntry, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access)); | ||
| 45 | return new MethodNodeWithAction(api, access, name, desc, signature, exceptions, methodNode -> { | ||
| 46 | try { | ||
| 47 | new Analyzer<>(new MethodInterpreter(entry, indexer, entryIndex, inheritanceIndex)).analyze(className, methodNode); | ||
| 48 | } catch (AnalyzerException e) { | ||
| 49 | throw new RuntimeException(e); | ||
| 50 | } | ||
| 51 | }); | ||
| 52 | } | ||
| 53 | |||
| 54 | private static class MethodInterpreter extends InterpreterPair<BasicValue, SourceValue> { | ||
| 55 | private final MethodDefEntry callerEntry; | ||
| 56 | private JarIndexer indexer; | ||
| 57 | |||
| 58 | public MethodInterpreter(MethodDefEntry callerEntry, JarIndexer indexer, EntryIndex entryIndex, InheritanceIndex inheritanceIndex) { | ||
| 59 | super(new IndexSimpleVerifier(entryIndex, inheritanceIndex), new SourceInterpreter()); | ||
| 60 | this.callerEntry = callerEntry; | ||
| 61 | this.indexer = indexer; | ||
| 62 | } | ||
| 63 | |||
| 64 | @Override | ||
| 65 | public PairValue<BasicValue, SourceValue> newOperation(AbstractInsnNode insn) throws AnalyzerException { | ||
| 66 | if (insn.getOpcode() == Opcodes.GETSTATIC) { | ||
| 67 | FieldInsnNode field = (FieldInsnNode) insn; | ||
| 68 | indexer.indexFieldReference(callerEntry, FieldEntry.parse(field.owner, field.name, field.desc), ReferenceTargetType.none()); | ||
| 69 | } | ||
| 70 | |||
| 71 | return super.newOperation(insn); | ||
| 72 | } | ||
| 73 | |||
| 74 | @Override | ||
| 75 | public PairValue<BasicValue, SourceValue> unaryOperation(AbstractInsnNode insn, PairValue<BasicValue, SourceValue> value) throws AnalyzerException { | ||
| 76 | if (insn.getOpcode() == Opcodes.PUTSTATIC) { | ||
| 77 | FieldInsnNode field = (FieldInsnNode) insn; | ||
| 78 | indexer.indexFieldReference(callerEntry, FieldEntry.parse(field.owner, field.name, field.desc), ReferenceTargetType.none()); | ||
| 79 | } | ||
| 80 | |||
| 81 | if (insn.getOpcode() == Opcodes.GETFIELD) { | ||
| 82 | FieldInsnNode field = (FieldInsnNode) insn; | ||
| 83 | indexer.indexFieldReference(callerEntry, FieldEntry.parse(field.owner, field.name, field.desc), getReferenceTargetType(value, insn)); | ||
| 84 | } | ||
| 85 | |||
| 86 | return super.unaryOperation(insn, value); | ||
| 87 | } | ||
| 88 | |||
| 89 | |||
| 90 | @Override | ||
| 91 | public PairValue<BasicValue, SourceValue> binaryOperation(AbstractInsnNode insn, PairValue<BasicValue, SourceValue> value1, PairValue<BasicValue, SourceValue> value2) throws AnalyzerException { | ||
| 92 | if (insn.getOpcode() == Opcodes.PUTFIELD) { | ||
| 93 | FieldInsnNode field = (FieldInsnNode) insn; | ||
| 94 | FieldEntry fieldEntry = FieldEntry.parse(field.owner, field.name, field.desc); | ||
| 95 | indexer.indexFieldReference(callerEntry, fieldEntry, ReferenceTargetType.none()); | ||
| 96 | } | ||
| 97 | |||
| 98 | return super.binaryOperation(insn, value1, value2); | ||
| 99 | } | ||
| 100 | |||
| 101 | @Override | ||
| 102 | public PairValue<BasicValue, SourceValue> naryOperation(AbstractInsnNode insn, List<? extends PairValue<BasicValue, SourceValue>> values) throws AnalyzerException { | ||
| 103 | if (insn.getOpcode() == Opcodes.INVOKEINTERFACE || insn.getOpcode() == Opcodes.INVOKESPECIAL || insn.getOpcode() == Opcodes.INVOKEVIRTUAL) { | ||
| 104 | MethodInsnNode methodInsn = (MethodInsnNode) insn; | ||
| 105 | indexer.indexMethodReference(callerEntry, MethodEntry.parse(methodInsn.owner, methodInsn.name, methodInsn.desc), getReferenceTargetType(values.get(0), insn)); | ||
| 106 | } | ||
| 107 | |||
| 108 | if (insn.getOpcode() == Opcodes.INVOKESTATIC) { | ||
| 109 | MethodInsnNode methodInsn = (MethodInsnNode) insn; | ||
| 110 | indexer.indexMethodReference(callerEntry, MethodEntry.parse(methodInsn.owner, methodInsn.name, methodInsn.desc), ReferenceTargetType.none()); | ||
| 111 | } | ||
| 112 | |||
| 113 | if (insn.getOpcode() == Opcodes.INVOKEDYNAMIC) { | ||
| 114 | InvokeDynamicInsnNode invokeDynamicInsn = (InvokeDynamicInsnNode) insn; | ||
| 115 | List<AbstractInsnNode> args = values.stream().map(v -> v.right.insns.stream().findFirst().orElseThrow(AssertionError::new)).collect(Collectors.toList()); | ||
| 116 | |||
| 117 | if ("java/lang/invoke/LambdaMetafactory".equals(invokeDynamicInsn.bsm.getOwner()) && "metafactory".equals(invokeDynamicInsn.bsm.getName())) { | ||
| 118 | Type samMethodType = (Type) invokeDynamicInsn.bsmArgs[0]; | ||
| 119 | Handle implMethod = (Handle) invokeDynamicInsn.bsmArgs[1]; | ||
| 120 | Type instantiatedMethodType = (Type) invokeDynamicInsn.bsmArgs[2]; | ||
| 121 | |||
| 122 | ReferenceTargetType targetType; | ||
| 123 | if (implMethod.getTag() != Opcodes.H_GETSTATIC && implMethod.getTag() != Opcodes.H_PUTFIELD && implMethod.getTag() != Opcodes.H_INVOKESTATIC) { | ||
| 124 | if (instantiatedMethodType.getArgumentTypes().length < Type.getArgumentTypes(implMethod.getDesc()).length) { | ||
| 125 | targetType = getReferenceTargetType(values.get(0), insn); | ||
| 126 | } else { | ||
| 127 | targetType = ReferenceTargetType.none(); // no "this" argument | ||
| 128 | } | ||
| 129 | } else { | ||
| 130 | targetType = ReferenceTargetType.none(); | ||
| 131 | } | ||
| 132 | |||
| 133 | indexer.indexLambda(callerEntry, new Lambda( | ||
| 134 | invokeDynamicInsn.name, | ||
| 135 | new MethodDescriptor(invokeDynamicInsn.desc), | ||
| 136 | new MethodDescriptor(samMethodType.getDescriptor()), | ||
| 137 | getHandleEntry(implMethod), | ||
| 138 | new MethodDescriptor(instantiatedMethodType.getDescriptor()) | ||
| 139 | ), targetType); | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | return super.naryOperation(insn, values); | ||
| 144 | } | ||
| 145 | |||
| 146 | private ReferenceTargetType getReferenceTargetType(PairValue<BasicValue, SourceValue> target, AbstractInsnNode insn) throws AnalyzerException { | ||
| 147 | if (target.left == BasicValue.UNINITIALIZED_VALUE) { | ||
| 148 | return ReferenceTargetType.uninitialized(); | ||
| 149 | } | ||
| 150 | |||
| 151 | if (target.left.getType().getSort() == Type.OBJECT) { | ||
| 152 | return ReferenceTargetType.classType(new ClassEntry(target.left.getType().getInternalName())); | ||
| 153 | } | ||
| 154 | |||
| 155 | if (target.left.getType().getSort() == Type.ARRAY) { | ||
| 156 | return ReferenceTargetType.classType(new ClassEntry("java/lang/Object")); | ||
| 157 | } | ||
| 158 | |||
| 159 | throw new AnalyzerException(insn, "called method on or accessed field of non-object type"); | ||
| 160 | } | ||
| 161 | |||
| 162 | private static ParentedEntry<?> getHandleEntry(Handle handle) { | ||
| 163 | switch (handle.getTag()) { | ||
| 164 | case Opcodes.H_GETFIELD: | ||
| 165 | case Opcodes.H_GETSTATIC: | ||
| 166 | case Opcodes.H_PUTFIELD: | ||
| 167 | case Opcodes.H_PUTSTATIC: | ||
| 168 | return FieldEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc()); | ||
| 169 | case Opcodes.H_INVOKEINTERFACE: | ||
| 170 | case Opcodes.H_INVOKESPECIAL: | ||
| 171 | case Opcodes.H_INVOKESTATIC: | ||
| 172 | case Opcodes.H_INVOKEVIRTUAL: | ||
| 173 | case Opcodes.H_NEWINVOKESPECIAL: | ||
| 174 | return MethodEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc()); | ||
| 175 | } | ||
| 176 | |||
| 177 | throw new RuntimeException("Invalid handle tag " + handle.getTag()); | ||
| 178 | } | ||
| 179 | } | ||
| 180 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java b/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java deleted file mode 100644 index 1ab2abd..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java +++ /dev/null | |||
| @@ -1,127 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.analysis.index; | ||
| 13 | |||
| 14 | import com.google.common.collect.HashMultimap; | ||
| 15 | import com.google.common.collect.Multimap; | ||
| 16 | import com.google.common.collect.Sets; | ||
| 17 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | ||
| 18 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 19 | |||
| 20 | import java.util.Collection; | ||
| 21 | import java.util.HashSet; | ||
| 22 | import java.util.LinkedList; | ||
| 23 | import java.util.Set; | ||
| 24 | |||
| 25 | public class InheritanceIndex implements JarIndexer { | ||
| 26 | private final EntryIndex entryIndex; | ||
| 27 | |||
| 28 | private Multimap<ClassEntry, ClassEntry> classParents = HashMultimap.create(); | ||
| 29 | private Multimap<ClassEntry, ClassEntry> classChildren = HashMultimap.create(); | ||
| 30 | |||
| 31 | public InheritanceIndex(EntryIndex entryIndex) { | ||
| 32 | this.entryIndex = entryIndex; | ||
| 33 | } | ||
| 34 | |||
| 35 | @Override | ||
| 36 | public void indexClass(ClassDefEntry classEntry) { | ||
| 37 | if (classEntry.isJre()) { | ||
| 38 | return; | ||
| 39 | } | ||
| 40 | |||
| 41 | ClassEntry superClass = classEntry.getSuperClass(); | ||
| 42 | if (superClass != null && !superClass.getName().equals("java/lang/Object")) { | ||
| 43 | indexParent(classEntry, superClass); | ||
| 44 | } | ||
| 45 | |||
| 46 | for (ClassEntry interfaceEntry : classEntry.getInterfaces()) { | ||
| 47 | indexParent(classEntry, interfaceEntry); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | private void indexParent(ClassEntry childEntry, ClassEntry parentEntry) { | ||
| 52 | classParents.put(childEntry, parentEntry); | ||
| 53 | classChildren.put(parentEntry, childEntry); | ||
| 54 | } | ||
| 55 | |||
| 56 | public Collection<ClassEntry> getParents(ClassEntry classEntry) { | ||
| 57 | return classParents.get(classEntry); | ||
| 58 | } | ||
| 59 | |||
| 60 | public Collection<ClassEntry> getChildren(ClassEntry classEntry) { | ||
| 61 | return classChildren.get(classEntry); | ||
| 62 | } | ||
| 63 | |||
| 64 | public Collection<ClassEntry> getDescendants(ClassEntry classEntry) { | ||
| 65 | Collection<ClassEntry> descendants = new HashSet<>(); | ||
| 66 | |||
| 67 | LinkedList<ClassEntry> descendantQueue = new LinkedList<>(); | ||
| 68 | descendantQueue.push(classEntry); | ||
| 69 | |||
| 70 | while (!descendantQueue.isEmpty()) { | ||
| 71 | ClassEntry descendant = descendantQueue.pop(); | ||
| 72 | Collection<ClassEntry> children = getChildren(descendant); | ||
| 73 | |||
| 74 | children.forEach(descendantQueue::push); | ||
| 75 | descendants.addAll(children); | ||
| 76 | } | ||
| 77 | |||
| 78 | return descendants; | ||
| 79 | } | ||
| 80 | |||
| 81 | public Set<ClassEntry> getAncestors(ClassEntry classEntry) { | ||
| 82 | Set<ClassEntry> ancestors = Sets.newHashSet(); | ||
| 83 | |||
| 84 | LinkedList<ClassEntry> ancestorQueue = new LinkedList<>(); | ||
| 85 | ancestorQueue.push(classEntry); | ||
| 86 | |||
| 87 | while (!ancestorQueue.isEmpty()) { | ||
| 88 | ClassEntry ancestor = ancestorQueue.pop(); | ||
| 89 | Collection<ClassEntry> parents = getParents(ancestor); | ||
| 90 | |||
| 91 | parents.forEach(ancestorQueue::push); | ||
| 92 | ancestors.addAll(parents); | ||
| 93 | } | ||
| 94 | |||
| 95 | return ancestors; | ||
| 96 | } | ||
| 97 | |||
| 98 | public Relation computeClassRelation(ClassEntry classEntry, ClassEntry potentialAncestor) { | ||
| 99 | if (potentialAncestor.getName().equals("java/lang/Object")) return Relation.RELATED; | ||
| 100 | if (!entryIndex.hasClass(classEntry)) return Relation.UNKNOWN; | ||
| 101 | |||
| 102 | for (ClassEntry ancestor : getAncestors(classEntry)) { | ||
| 103 | if (potentialAncestor.equals(ancestor)) { | ||
| 104 | return Relation.RELATED; | ||
| 105 | } else if (!entryIndex.hasClass(ancestor)) { | ||
| 106 | return Relation.UNKNOWN; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | return Relation.UNRELATED; | ||
| 111 | } | ||
| 112 | |||
| 113 | public boolean isParent(ClassEntry classEntry) { | ||
| 114 | return classChildren.containsKey(classEntry); | ||
| 115 | } | ||
| 116 | |||
| 117 | public boolean hasParents(ClassEntry classEntry) { | ||
| 118 | Collection<ClassEntry> parents = classParents.get(classEntry); | ||
| 119 | return parents != null && !parents.isEmpty(); | ||
| 120 | } | ||
| 121 | |||
| 122 | public enum Relation { | ||
| 123 | RELATED, | ||
| 124 | UNRELATED, | ||
| 125 | UNKNOWN | ||
| 126 | } | ||
| 127 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java deleted file mode 100644 index e401c2f..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java +++ /dev/null | |||
| @@ -1,171 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.analysis.index; | ||
| 13 | |||
| 14 | import com.google.common.collect.HashMultimap; | ||
| 15 | import com.google.common.collect.Multimap; | ||
| 16 | import cuchaz.enigma.ProgressListener; | ||
| 17 | import cuchaz.enigma.analysis.ClassCache; | ||
| 18 | import cuchaz.enigma.analysis.ReferenceTargetType; | ||
| 19 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 20 | import cuchaz.enigma.translation.mapping.IndexEntryResolver; | ||
| 21 | import cuchaz.enigma.translation.representation.Lambda; | ||
| 22 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 23 | import cuchaz.enigma.utils.I18n; | ||
| 24 | |||
| 25 | import cuchaz.enigma.utils.Utils; | ||
| 26 | import org.objectweb.asm.ClassReader; | ||
| 27 | import org.objectweb.asm.Opcodes; | ||
| 28 | |||
| 29 | import java.util.Arrays; | ||
| 30 | import java.util.Collection; | ||
| 31 | |||
| 32 | public class JarIndex implements JarIndexer { | ||
| 33 | private final EntryIndex entryIndex; | ||
| 34 | private final InheritanceIndex inheritanceIndex; | ||
| 35 | private final ReferenceIndex referenceIndex; | ||
| 36 | private final BridgeMethodIndex bridgeMethodIndex; | ||
| 37 | private final PackageVisibilityIndex packageVisibilityIndex; | ||
| 38 | private final EntryResolver entryResolver; | ||
| 39 | |||
| 40 | private final Collection<JarIndexer> indexers; | ||
| 41 | |||
| 42 | private final Multimap<String, MethodDefEntry> methodImplementations = HashMultimap.create(); | ||
| 43 | |||
| 44 | public JarIndex(EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex, PackageVisibilityIndex packageVisibilityIndex) { | ||
| 45 | this.entryIndex = entryIndex; | ||
| 46 | this.inheritanceIndex = inheritanceIndex; | ||
| 47 | this.referenceIndex = referenceIndex; | ||
| 48 | this.bridgeMethodIndex = bridgeMethodIndex; | ||
| 49 | this.packageVisibilityIndex = packageVisibilityIndex; | ||
| 50 | this.indexers = Arrays.asList(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, packageVisibilityIndex); | ||
| 51 | this.entryResolver = new IndexEntryResolver(this); | ||
| 52 | } | ||
| 53 | |||
| 54 | public static JarIndex empty() { | ||
| 55 | EntryIndex entryIndex = new EntryIndex(); | ||
| 56 | InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); | ||
| 57 | ReferenceIndex referenceIndex = new ReferenceIndex(); | ||
| 58 | BridgeMethodIndex bridgeMethodIndex = new BridgeMethodIndex(entryIndex, inheritanceIndex, referenceIndex); | ||
| 59 | PackageVisibilityIndex packageVisibilityIndex = new PackageVisibilityIndex(); | ||
| 60 | return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, packageVisibilityIndex); | ||
| 61 | } | ||
| 62 | |||
| 63 | public void indexJar(ClassCache classCache, ProgressListener progress) { | ||
| 64 | progress.init(4, I18n.translate("progress.jar.indexing")); | ||
| 65 | |||
| 66 | progress.step(1, I18n.translate("progress.jar.indexing.entries")); | ||
| 67 | classCache.visit(() -> new IndexClassVisitor(this, Utils.ASM_VERSION), ClassReader.SKIP_CODE); | ||
| 68 | |||
| 69 | progress.step(2, I18n.translate("progress.jar.indexing.references")); | ||
| 70 | classCache.visit(() -> new IndexReferenceVisitor(this, entryIndex, inheritanceIndex, Utils.ASM_VERSION), 0); | ||
| 71 | |||
| 72 | progress.step(3, I18n.translate("progress.jar.indexing.methods")); | ||
| 73 | bridgeMethodIndex.findBridgeMethods(); | ||
| 74 | |||
| 75 | progress.step(4, I18n.translate("progress.jar.indexing.process")); | ||
| 76 | processIndex(this); | ||
| 77 | } | ||
| 78 | |||
| 79 | @Override | ||
| 80 | public void processIndex(JarIndex index) { | ||
| 81 | indexers.forEach(indexer -> indexer.processIndex(index)); | ||
| 82 | } | ||
| 83 | |||
| 84 | @Override | ||
| 85 | public void indexClass(ClassDefEntry classEntry) { | ||
| 86 | if (classEntry.isJre()) { | ||
| 87 | return; | ||
| 88 | } | ||
| 89 | |||
| 90 | for (ClassEntry interfaceEntry : classEntry.getInterfaces()) { | ||
| 91 | if (classEntry.equals(interfaceEntry)) { | ||
| 92 | throw new IllegalArgumentException("Class cannot be its own interface! " + classEntry); | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | indexers.forEach(indexer -> indexer.indexClass(classEntry)); | ||
| 97 | } | ||
| 98 | |||
| 99 | @Override | ||
| 100 | public void indexField(FieldDefEntry fieldEntry) { | ||
| 101 | if (fieldEntry.getParent().isJre()) { | ||
| 102 | return; | ||
| 103 | } | ||
| 104 | |||
| 105 | indexers.forEach(indexer -> indexer.indexField(fieldEntry)); | ||
| 106 | } | ||
| 107 | |||
| 108 | @Override | ||
| 109 | public void indexMethod(MethodDefEntry methodEntry) { | ||
| 110 | if (methodEntry.getParent().isJre()) { | ||
| 111 | return; | ||
| 112 | } | ||
| 113 | |||
| 114 | indexers.forEach(indexer -> indexer.indexMethod(methodEntry)); | ||
| 115 | |||
| 116 | if (!methodEntry.isConstructor()) { | ||
| 117 | methodImplementations.put(methodEntry.getParent().getFullName(), methodEntry); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | @Override | ||
| 122 | public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry, ReferenceTargetType targetType) { | ||
| 123 | if (callerEntry.getParent().isJre()) { | ||
| 124 | return; | ||
| 125 | } | ||
| 126 | |||
| 127 | indexers.forEach(indexer -> indexer.indexMethodReference(callerEntry, referencedEntry, targetType)); | ||
| 128 | } | ||
| 129 | |||
| 130 | @Override | ||
| 131 | public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry, ReferenceTargetType targetType) { | ||
| 132 | if (callerEntry.getParent().isJre()) { | ||
| 133 | return; | ||
| 134 | } | ||
| 135 | |||
| 136 | indexers.forEach(indexer -> indexer.indexFieldReference(callerEntry, referencedEntry, targetType)); | ||
| 137 | } | ||
| 138 | |||
| 139 | @Override | ||
| 140 | public void indexLambda(MethodDefEntry callerEntry, Lambda lambda, ReferenceTargetType targetType) { | ||
| 141 | if (callerEntry.getParent().isJre()) { | ||
| 142 | return; | ||
| 143 | } | ||
| 144 | |||
| 145 | indexers.forEach(indexer -> indexer.indexLambda(callerEntry, lambda, targetType)); | ||
| 146 | } | ||
| 147 | |||
| 148 | public EntryIndex getEntryIndex() { | ||
| 149 | return entryIndex; | ||
| 150 | } | ||
| 151 | |||
| 152 | public InheritanceIndex getInheritanceIndex() { | ||
| 153 | return this.inheritanceIndex; | ||
| 154 | } | ||
| 155 | |||
| 156 | public ReferenceIndex getReferenceIndex() { | ||
| 157 | return referenceIndex; | ||
| 158 | } | ||
| 159 | |||
| 160 | public BridgeMethodIndex getBridgeMethodIndex() { | ||
| 161 | return bridgeMethodIndex; | ||
| 162 | } | ||
| 163 | |||
| 164 | public PackageVisibilityIndex getPackageVisibilityIndex() { | ||
| 165 | return packageVisibilityIndex; | ||
| 166 | } | ||
| 167 | |||
| 168 | public EntryResolver getEntryResolver() { | ||
| 169 | return entryResolver; | ||
| 170 | } | ||
| 171 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java deleted file mode 100644 index f17e7c9..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java +++ /dev/null | |||
| @@ -1,28 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.analysis.ReferenceTargetType; | ||
| 4 | import cuchaz.enigma.translation.representation.Lambda; | ||
| 5 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 6 | |||
| 7 | public interface JarIndexer { | ||
| 8 | default void indexClass(ClassDefEntry classEntry) { | ||
| 9 | } | ||
| 10 | |||
| 11 | default void indexField(FieldDefEntry fieldEntry) { | ||
| 12 | } | ||
| 13 | |||
| 14 | default void indexMethod(MethodDefEntry methodEntry) { | ||
| 15 | } | ||
| 16 | |||
| 17 | default void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry, ReferenceTargetType targetType) { | ||
| 18 | } | ||
| 19 | |||
| 20 | default void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry, ReferenceTargetType targetType) { | ||
| 21 | } | ||
| 22 | |||
| 23 | default void indexLambda(MethodDefEntry callerEntry, Lambda lambda, ReferenceTargetType targetType) { | ||
| 24 | } | ||
| 25 | |||
| 26 | default void processIndex(JarIndex index) { | ||
| 27 | } | ||
| 28 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java b/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java deleted file mode 100644 index 63eb730..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java +++ /dev/null | |||
| @@ -1,147 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import com.google.common.collect.HashMultimap; | ||
| 4 | import com.google.common.collect.Lists; | ||
| 5 | import com.google.common.collect.Maps; | ||
| 6 | import com.google.common.collect.Sets; | ||
| 7 | import cuchaz.enigma.analysis.EntryReference; | ||
| 8 | import cuchaz.enigma.analysis.ReferenceTargetType; | ||
| 9 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 10 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 11 | |||
| 12 | import java.util.*; | ||
| 13 | |||
| 14 | public class PackageVisibilityIndex implements JarIndexer { | ||
| 15 | private static boolean requiresSamePackage(AccessFlags entryAcc, EntryReference ref, InheritanceIndex inheritanceIndex) { | ||
| 16 | if (entryAcc.isPublic()) { | ||
| 17 | return false; | ||
| 18 | } | ||
| 19 | |||
| 20 | if (entryAcc.isProtected()) { | ||
| 21 | ClassEntry contextClass = ref.context.getContainingClass(); | ||
| 22 | ClassEntry referencedClass = ref.entry.getContainingClass(); | ||
| 23 | |||
| 24 | if (!inheritanceIndex.getAncestors(contextClass).contains(referencedClass)) { | ||
| 25 | return true; // access to protected member not in superclass | ||
| 26 | } | ||
| 27 | |||
| 28 | if (ref.targetType.getKind() == ReferenceTargetType.Kind.NONE) { | ||
| 29 | return false; // access to superclass or static superclass member | ||
| 30 | } | ||
| 31 | |||
| 32 | // access to instance member only valid if target's class assignable to context class | ||
| 33 | return !(ref.targetType.getKind() == ReferenceTargetType.Kind.UNINITIALIZED || | ||
| 34 | ((ReferenceTargetType.ClassType) ref.targetType).getEntry().equals(contextClass) || | ||
| 35 | inheritanceIndex.getAncestors(((ReferenceTargetType.ClassType) ref.targetType).getEntry()).contains(contextClass)); | ||
| 36 | } | ||
| 37 | |||
| 38 | return true; | ||
| 39 | } | ||
| 40 | |||
| 41 | private final HashMultimap<ClassEntry, ClassEntry> connections = HashMultimap.create(); | ||
| 42 | private final List<Set<ClassEntry>> partitions = Lists.newArrayList(); | ||
| 43 | private final Map<ClassEntry, Set<ClassEntry>> classPartitions = Maps.newHashMap(); | ||
| 44 | |||
| 45 | private void addConnection(ClassEntry classA, ClassEntry classB) { | ||
| 46 | if (classA != classB) { | ||
| 47 | connections.put(classA, classB); | ||
| 48 | connections.put(classB, classA); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | private void buildPartition(Set<ClassEntry> unassignedClasses, Set<ClassEntry> partition, ClassEntry member) { | ||
| 53 | for (ClassEntry connected : connections.get(member)) { | ||
| 54 | if (unassignedClasses.remove(connected)) { | ||
| 55 | partition.add(connected); | ||
| 56 | buildPartition(unassignedClasses, partition, connected); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | private void addConnections(EntryIndex entryIndex, ReferenceIndex referenceIndex, InheritanceIndex inheritanceIndex) { | ||
| 62 | for (FieldEntry entry : entryIndex.getFields()) { | ||
| 63 | AccessFlags entryAcc = entryIndex.getFieldAccess(entry); | ||
| 64 | if (!entryAcc.isPublic() && !entryAcc.isPrivate()) { | ||
| 65 | for (EntryReference<FieldEntry, MethodDefEntry> ref : referenceIndex.getReferencesToField(entry)) { | ||
| 66 | if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) { | ||
| 67 | addConnection(ref.entry.getContainingClass(), ref.context.getContainingClass()); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | for (MethodEntry entry : entryIndex.getMethods()) { | ||
| 74 | AccessFlags entryAcc = entryIndex.getMethodAccess(entry); | ||
| 75 | if (!entryAcc.isPublic() && !entryAcc.isPrivate()) { | ||
| 76 | for (EntryReference<MethodEntry, MethodDefEntry> ref : referenceIndex.getReferencesToMethod(entry)) { | ||
| 77 | if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) { | ||
| 78 | addConnection(ref.entry.getContainingClass(), ref.context.getContainingClass()); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | for (ClassEntry entry : entryIndex.getClasses()) { | ||
| 85 | AccessFlags entryAcc = entryIndex.getClassAccess(entry); | ||
| 86 | if (!entryAcc.isPublic() && !entryAcc.isPrivate()) { | ||
| 87 | for (EntryReference<ClassEntry, FieldDefEntry> ref : referenceIndex.getFieldTypeReferencesToClass(entry)) { | ||
| 88 | if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) { | ||
| 89 | addConnection(ref.entry.getContainingClass(), ref.context.getContainingClass()); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | for (EntryReference<ClassEntry, MethodDefEntry> ref : referenceIndex.getMethodTypeReferencesToClass(entry)) { | ||
| 94 | if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) { | ||
| 95 | addConnection(ref.entry.getContainingClass(), ref.context.getContainingClass()); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | for (ClassEntry parent : inheritanceIndex.getParents(entry)) { | ||
| 101 | AccessFlags parentAcc = entryIndex.getClassAccess(parent); | ||
| 102 | if (parentAcc != null && !parentAcc.isPublic() && !parentAcc.isPrivate()) { | ||
| 103 | addConnection(entry, parent); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | ClassEntry outerClass = entry.getOuterClass(); | ||
| 108 | if (outerClass != null) { | ||
| 109 | addConnection(entry, outerClass); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | private void addPartitions(EntryIndex entryIndex) { | ||
| 115 | Set<ClassEntry> unassignedClasses = Sets.newHashSet(entryIndex.getClasses()); | ||
| 116 | while (!unassignedClasses.isEmpty()) { | ||
| 117 | Iterator<ClassEntry> iterator = unassignedClasses.iterator(); | ||
| 118 | ClassEntry initialEntry = iterator.next(); | ||
| 119 | iterator.remove(); | ||
| 120 | |||
| 121 | HashSet<ClassEntry> partition = Sets.newHashSet(); | ||
| 122 | partition.add(initialEntry); | ||
| 123 | buildPartition(unassignedClasses, partition, initialEntry); | ||
| 124 | partitions.add(partition); | ||
| 125 | for (ClassEntry entry : partition) { | ||
| 126 | classPartitions.put(entry, partition); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | public Collection<Set<ClassEntry>> getPartitions() { | ||
| 132 | return partitions; | ||
| 133 | } | ||
| 134 | |||
| 135 | public Set<ClassEntry> getPartition(ClassEntry classEntry) { | ||
| 136 | return classPartitions.get(classEntry); | ||
| 137 | } | ||
| 138 | |||
| 139 | @Override | ||
| 140 | public void processIndex(JarIndex index) { | ||
| 141 | EntryIndex entryIndex = index.getEntryIndex(); | ||
| 142 | ReferenceIndex referenceIndex = index.getReferenceIndex(); | ||
| 143 | InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); | ||
| 144 | addConnections(entryIndex, referenceIndex, inheritanceIndex); | ||
| 145 | addPartitions(entryIndex); | ||
| 146 | } | ||
| 147 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java b/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java deleted file mode 100644 index b6797c2..0000000 --- a/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java +++ /dev/null | |||
| @@ -1,148 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import com.google.common.collect.HashMultimap; | ||
| 4 | import com.google.common.collect.Multimap; | ||
| 5 | import cuchaz.enigma.analysis.EntryReference; | ||
| 6 | import cuchaz.enigma.analysis.ReferenceTargetType; | ||
| 7 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | ||
| 8 | import cuchaz.enigma.translation.representation.Lambda; | ||
| 9 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 10 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 11 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 12 | |||
| 13 | import java.util.Collection; | ||
| 14 | import java.util.Map; | ||
| 15 | |||
| 16 | public class ReferenceIndex implements JarIndexer { | ||
| 17 | private Multimap<MethodEntry, MethodEntry> methodReferences = HashMultimap.create(); | ||
| 18 | |||
| 19 | private Multimap<MethodEntry, EntryReference<MethodEntry, MethodDefEntry>> referencesToMethods = HashMultimap.create(); | ||
| 20 | private Multimap<ClassEntry, EntryReference<ClassEntry, MethodDefEntry>> referencesToClasses = HashMultimap.create(); | ||
| 21 | private Multimap<FieldEntry, EntryReference<FieldEntry, MethodDefEntry>> referencesToFields = HashMultimap.create(); | ||
| 22 | private Multimap<ClassEntry, EntryReference<ClassEntry, FieldDefEntry>> fieldTypeReferences = HashMultimap.create(); | ||
| 23 | private Multimap<ClassEntry, EntryReference<ClassEntry, MethodDefEntry>> methodTypeReferences = HashMultimap.create(); | ||
| 24 | |||
| 25 | @Override | ||
| 26 | public void indexMethod(MethodDefEntry methodEntry) { | ||
| 27 | indexMethodDescriptor(methodEntry, methodEntry.getDesc()); | ||
| 28 | } | ||
| 29 | |||
| 30 | private void indexMethodDescriptor(MethodDefEntry entry, MethodDescriptor descriptor) { | ||
| 31 | for (TypeDescriptor typeDescriptor : descriptor.getArgumentDescs()) { | ||
| 32 | indexMethodTypeDescriptor(entry, typeDescriptor); | ||
| 33 | } | ||
| 34 | indexMethodTypeDescriptor(entry, descriptor.getReturnDesc()); | ||
| 35 | } | ||
| 36 | |||
| 37 | private void indexMethodTypeDescriptor(MethodDefEntry method, TypeDescriptor typeDescriptor) { | ||
| 38 | if (typeDescriptor.isType()) { | ||
| 39 | ClassEntry referencedClass = typeDescriptor.getTypeEntry(); | ||
| 40 | methodTypeReferences.put(referencedClass, new EntryReference<>(referencedClass, referencedClass.getName(), method)); | ||
| 41 | } else if (typeDescriptor.isArray()) { | ||
| 42 | indexMethodTypeDescriptor(method, typeDescriptor.getArrayType()); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | @Override | ||
| 47 | public void indexField(FieldDefEntry fieldEntry) { | ||
| 48 | indexFieldTypeDescriptor(fieldEntry, fieldEntry.getDesc()); | ||
| 49 | } | ||
| 50 | |||
| 51 | private void indexFieldTypeDescriptor(FieldDefEntry field, TypeDescriptor typeDescriptor) { | ||
| 52 | if (typeDescriptor.isType()) { | ||
| 53 | ClassEntry referencedClass = typeDescriptor.getTypeEntry(); | ||
| 54 | fieldTypeReferences.put(referencedClass, new EntryReference<>(referencedClass, referencedClass.getName(), field)); | ||
| 55 | } else if (typeDescriptor.isArray()) { | ||
| 56 | indexFieldTypeDescriptor(field, typeDescriptor.getArrayType()); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | @Override | ||
| 61 | public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry, ReferenceTargetType targetType) { | ||
| 62 | referencesToMethods.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry, targetType)); | ||
| 63 | methodReferences.put(callerEntry, referencedEntry); | ||
| 64 | |||
| 65 | if (referencedEntry.isConstructor()) { | ||
| 66 | ClassEntry referencedClass = referencedEntry.getParent(); | ||
| 67 | referencesToClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedEntry.getName(), callerEntry, targetType)); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | @Override | ||
| 72 | public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry, ReferenceTargetType targetType) { | ||
| 73 | referencesToFields.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry, targetType)); | ||
| 74 | } | ||
| 75 | |||
| 76 | @Override | ||
| 77 | public void indexLambda(MethodDefEntry callerEntry, Lambda lambda, ReferenceTargetType targetType) { | ||
| 78 | if (lambda.getImplMethod() instanceof MethodEntry) { | ||
| 79 | indexMethodReference(callerEntry, (MethodEntry) lambda.getImplMethod(), targetType); | ||
| 80 | } else { | ||
| 81 | indexFieldReference(callerEntry, (FieldEntry) lambda.getImplMethod(), targetType); | ||
| 82 | } | ||
| 83 | |||
| 84 | indexMethodDescriptor(callerEntry, lambda.getInvokedType()); | ||
| 85 | indexMethodDescriptor(callerEntry, lambda.getSamMethodType()); | ||
| 86 | indexMethodDescriptor(callerEntry, lambda.getInstantiatedMethodType()); | ||
| 87 | } | ||
| 88 | |||
| 89 | @Override | ||
| 90 | public void processIndex(JarIndex index) { | ||
| 91 | methodReferences = remapReferences(index, methodReferences); | ||
| 92 | referencesToMethods = remapReferencesTo(index, referencesToMethods); | ||
| 93 | referencesToClasses = remapReferencesTo(index, referencesToClasses); | ||
| 94 | referencesToFields = remapReferencesTo(index, referencesToFields); | ||
| 95 | fieldTypeReferences = remapReferencesTo(index, fieldTypeReferences); | ||
| 96 | methodTypeReferences = remapReferencesTo(index, methodTypeReferences); | ||
| 97 | } | ||
| 98 | |||
| 99 | private <K extends Entry<?>, V extends Entry<?>> Multimap<K, V> remapReferences(JarIndex index, Multimap<K, V> multimap) { | ||
| 100 | final int keySetSize = multimap.keySet().size(); | ||
| 101 | Multimap<K, V> resolved = HashMultimap.create(multimap.keySet().size(), keySetSize == 0 ? 0 : multimap.size() / keySetSize); | ||
| 102 | for (Map.Entry<K, V> entry : multimap.entries()) { | ||
| 103 | resolved.put(remap(index, entry.getKey()), remap(index, entry.getValue())); | ||
| 104 | } | ||
| 105 | return resolved; | ||
| 106 | } | ||
| 107 | |||
| 108 | private <E extends Entry<?>, C extends Entry<?>> Multimap<E, EntryReference<E, C>> remapReferencesTo(JarIndex index, Multimap<E, EntryReference<E, C>> multimap) { | ||
| 109 | final int keySetSize = multimap.keySet().size(); | ||
| 110 | Multimap<E, EntryReference<E, C>> resolved = HashMultimap.create(keySetSize, keySetSize == 0 ? 0 : multimap.size() / keySetSize); | ||
| 111 | for (Map.Entry<E, EntryReference<E, C>> entry : multimap.entries()) { | ||
| 112 | resolved.put(remap(index, entry.getKey()), remap(index, entry.getValue())); | ||
| 113 | } | ||
| 114 | return resolved; | ||
| 115 | } | ||
| 116 | |||
| 117 | private <E extends Entry<?>> E remap(JarIndex index, E entry) { | ||
| 118 | return index.getEntryResolver().resolveFirstEntry(entry, ResolutionStrategy.RESOLVE_CLOSEST); | ||
| 119 | } | ||
| 120 | |||
| 121 | private <E extends Entry<?>, C extends Entry<?>> EntryReference<E, C> remap(JarIndex index, EntryReference<E, C> reference) { | ||
| 122 | return index.getEntryResolver().resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST); | ||
| 123 | } | ||
| 124 | |||
| 125 | public Collection<MethodEntry> getMethodsReferencedBy(MethodEntry entry) { | ||
| 126 | return methodReferences.get(entry); | ||
| 127 | } | ||
| 128 | |||
| 129 | public Collection<EntryReference<FieldEntry, MethodDefEntry>> getReferencesToField(FieldEntry entry) { | ||
| 130 | return referencesToFields.get(entry); | ||
| 131 | } | ||
| 132 | |||
| 133 | public Collection<EntryReference<ClassEntry, MethodDefEntry>> getReferencesToClass(ClassEntry entry) { | ||
| 134 | return referencesToClasses.get(entry); | ||
| 135 | } | ||
| 136 | |||
| 137 | public Collection<EntryReference<MethodEntry, MethodDefEntry>> getReferencesToMethod(MethodEntry entry) { | ||
| 138 | return referencesToMethods.get(entry); | ||
| 139 | } | ||
| 140 | |||
| 141 | public Collection<EntryReference<ClassEntry, FieldDefEntry>> getFieldTypeReferencesToClass(ClassEntry entry) { | ||
| 142 | return fieldTypeReferences.get(entry); | ||
| 143 | } | ||
| 144 | |||
| 145 | public Collection<EntryReference<ClassEntry, MethodDefEntry>> getMethodTypeReferencesToClass(ClassEntry entry) { | ||
| 146 | return methodTypeReferences.get(entry); | ||
| 147 | } | ||
| 148 | } | ||