diff options
| author | 2019-11-02 17:23:14 -0400 | |
|---|---|---|
| committer | 2019-11-02 21:23:14 +0000 | |
| commit | 422b10b419eb010b612931e8b250b9dd1b7424ed (patch) | |
| tree | b9c1d3a7add5b8d01d5b0196d6c29ad00273987c /src | |
| parent | Fix array class translation (#173) (diff) | |
| download | enigma-422b10b419eb010b612931e8b250b9dd1b7424ed.tar.gz enigma-422b10b419eb010b612931e8b250b9dd1b7424ed.tar.xz enigma-422b10b419eb010b612931e8b250b9dd1b7424ed.zip | |
Check protected method/field target in visibility index (#157)
Diffstat (limited to 'src')
12 files changed, 580 insertions, 55 deletions
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryReference.java b/src/main/java/cuchaz/enigma/analysis/EntryReference.java index e122210c..2e738c01 100644 --- a/src/main/java/cuchaz/enigma/analysis/EntryReference.java +++ b/src/main/java/cuchaz/enigma/analysis/EntryReference.java | |||
| @@ -29,6 +29,7 @@ public class EntryReference<E extends Entry<?>, C extends Entry<?>> implements T | |||
| 29 | private static final List<String> CONSTRUCTOR_NON_NAMES = Arrays.asList("this", "super", "static"); | 29 | private static final List<String> CONSTRUCTOR_NON_NAMES = Arrays.asList("this", "super", "static"); |
| 30 | public E entry; | 30 | public E entry; |
| 31 | public C context; | 31 | public C context; |
| 32 | public ReferenceTargetType targetType; | ||
| 32 | 33 | ||
| 33 | private boolean sourceName; | 34 | private boolean sourceName; |
| 34 | 35 | ||
| @@ -37,12 +38,17 @@ public class EntryReference<E extends Entry<?>, C extends Entry<?>> implements T | |||
| 37 | } | 38 | } |
| 38 | 39 | ||
| 39 | public EntryReference(E entry, String sourceName, C context) { | 40 | public EntryReference(E entry, String sourceName, C context) { |
| 41 | this(entry, sourceName, context, ReferenceTargetType.none()); | ||
| 42 | } | ||
| 43 | |||
| 44 | public EntryReference(E entry, String sourceName, C context, ReferenceTargetType targetType) { | ||
| 40 | if (entry == null) { | 45 | if (entry == null) { |
| 41 | throw new IllegalArgumentException("Entry cannot be null!"); | 46 | throw new IllegalArgumentException("Entry cannot be null!"); |
| 42 | } | 47 | } |
| 43 | 48 | ||
| 44 | this.entry = entry; | 49 | this.entry = entry; |
| 45 | this.context = context; | 50 | this.context = context; |
| 51 | this.targetType = targetType; | ||
| 46 | 52 | ||
| 47 | this.sourceName = sourceName != null && !sourceName.isEmpty(); | 53 | this.sourceName = sourceName != null && !sourceName.isEmpty(); |
| 48 | if (entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor() && CONSTRUCTOR_NON_NAMES.contains(sourceName)) { | 54 | if (entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor() && CONSTRUCTOR_NON_NAMES.contains(sourceName)) { |
| @@ -54,6 +60,7 @@ public class EntryReference<E extends Entry<?>, C extends Entry<?>> implements T | |||
| 54 | this.entry = entry; | 60 | this.entry = entry; |
| 55 | this.context = context; | 61 | this.context = context; |
| 56 | this.sourceName = other.sourceName; | 62 | this.sourceName = other.sourceName; |
| 63 | this.targetType = other.targetType; | ||
| 57 | } | 64 | } |
| 58 | 65 | ||
| 59 | public ClassEntry getLocationClassEntry() { | 66 | public ClassEntry getLocationClassEntry() { |
| @@ -112,10 +119,17 @@ public class EntryReference<E extends Entry<?>, C extends Entry<?>> implements T | |||
| 112 | public String toString() { | 119 | public String toString() { |
| 113 | StringBuilder buf = new StringBuilder(); | 120 | StringBuilder buf = new StringBuilder(); |
| 114 | buf.append(entry); | 121 | buf.append(entry); |
| 122 | |||
| 115 | if (context != null) { | 123 | if (context != null) { |
| 116 | buf.append(" called from "); | 124 | buf.append(" called from "); |
| 117 | buf.append(context); | 125 | buf.append(context); |
| 118 | } | 126 | } |
| 127 | |||
| 128 | if (targetType != null && targetType.getKind() != ReferenceTargetType.Kind.NONE) { | ||
| 129 | buf.append(" on target of type "); | ||
| 130 | buf.append(targetType); | ||
| 131 | } | ||
| 132 | |||
| 119 | return buf.toString(); | 133 | return buf.toString(); |
| 120 | } | 134 | } |
| 121 | 135 | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java b/src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java new file mode 100644 index 00000000..da2b5efe --- /dev/null +++ b/src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java | |||
| @@ -0,0 +1,153 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import cuchaz.enigma.analysis.index.EntryIndex; | ||
| 4 | import cuchaz.enigma.analysis.index.InheritanceIndex; | ||
| 5 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 6 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | ||
| 7 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 8 | import org.objectweb.asm.Type; | ||
| 9 | import org.objectweb.asm.tree.analysis.BasicValue; | ||
| 10 | import org.objectweb.asm.tree.analysis.SimpleVerifier; | ||
| 11 | |||
| 12 | import java.util.Set; | ||
| 13 | |||
| 14 | public class IndexSimpleVerifier extends SimpleVerifier { | ||
| 15 | private static final Type OBJECT_TYPE = Type.getType("Ljava/lang/Object;"); | ||
| 16 | private final EntryIndex entryIndex; | ||
| 17 | private final InheritanceIndex inheritanceIndex; | ||
| 18 | |||
| 19 | public IndexSimpleVerifier(EntryIndex entryIndex, InheritanceIndex inheritanceIndex) { | ||
| 20 | super(ASM7, null, null, null, false); | ||
| 21 | this.entryIndex = entryIndex; | ||
| 22 | this.inheritanceIndex = inheritanceIndex; | ||
| 23 | } | ||
| 24 | |||
| 25 | @Override | ||
| 26 | protected boolean isSubTypeOf(BasicValue value, BasicValue expected) { | ||
| 27 | Type expectedType = expected.getType(); | ||
| 28 | Type type = value.getType(); | ||
| 29 | switch (expectedType.getSort()) { | ||
| 30 | case Type.INT: | ||
| 31 | case Type.FLOAT: | ||
| 32 | case Type.LONG: | ||
| 33 | case Type.DOUBLE: | ||
| 34 | return type.equals(expectedType); | ||
| 35 | case Type.ARRAY: | ||
| 36 | case Type.OBJECT: | ||
| 37 | if (type.equals(NULL_TYPE)) { | ||
| 38 | return true; | ||
| 39 | } else if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { | ||
| 40 | if (isAssignableFrom(expectedType, type)) { | ||
| 41 | return true; | ||
| 42 | } else if (isInterface(expectedType)) { | ||
| 43 | return isAssignableFrom(OBJECT_TYPE, type); | ||
| 44 | } else { | ||
| 45 | return false; | ||
| 46 | } | ||
| 47 | } else { | ||
| 48 | return false; | ||
| 49 | } | ||
| 50 | default: | ||
| 51 | throw new AssertionError(); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | @Override | ||
| 56 | protected boolean isInterface(Type type) { | ||
| 57 | AccessFlags classAccess = entryIndex.getClassAccess(new ClassEntry(type.getInternalName())); | ||
| 58 | if (classAccess != null) { | ||
| 59 | return classAccess.isInterface(); | ||
| 60 | } | ||
| 61 | |||
| 62 | Class<?> clazz = getClass(type); | ||
| 63 | if (clazz != null) { | ||
| 64 | return clazz.isInterface(); | ||
| 65 | } | ||
| 66 | |||
| 67 | return false; | ||
| 68 | } | ||
| 69 | |||
| 70 | @Override | ||
| 71 | protected Type getSuperClass(Type type) { | ||
| 72 | ClassDefEntry definition = entryIndex.getDefinition(new ClassEntry(type.getInternalName())); | ||
| 73 | if (definition != null) { | ||
| 74 | return Type.getType('L' + definition.getSuperClass().getFullName() + ';'); | ||
| 75 | } | ||
| 76 | |||
| 77 | Class<?> clazz = getClass(type); | ||
| 78 | if (clazz != null) { | ||
| 79 | return Type.getType(clazz.getSuperclass()); | ||
| 80 | } | ||
| 81 | |||
| 82 | return OBJECT_TYPE; | ||
| 83 | } | ||
| 84 | |||
| 85 | @Override | ||
| 86 | protected boolean isAssignableFrom(Type type1, Type type2) { | ||
| 87 | if (type1.equals(type2)) { | ||
| 88 | return true; | ||
| 89 | } | ||
| 90 | |||
| 91 | if (type2.equals(NULL_TYPE)) { | ||
| 92 | return true; | ||
| 93 | } | ||
| 94 | |||
| 95 | if (type1.getSort() == Type.ARRAY) { | ||
| 96 | return type2.getSort() == Type.ARRAY && isAssignableFrom(Type.getType(type1.getDescriptor().substring(1)), Type.getType(type2.getDescriptor().substring(1))); | ||
| 97 | } | ||
| 98 | |||
| 99 | if (type2.getSort() == Type.ARRAY) { | ||
| 100 | return type1.equals(OBJECT_TYPE); | ||
| 101 | } | ||
| 102 | |||
| 103 | if (type1.getSort() == Type.OBJECT && type2.getSort() == Type.OBJECT) { | ||
| 104 | if (type1.equals(OBJECT_TYPE)) { | ||
| 105 | return true; | ||
| 106 | } | ||
| 107 | |||
| 108 | ClassEntry class1 = new ClassEntry(type1.getInternalName()); | ||
| 109 | ClassEntry class2 = new ClassEntry(type2.getInternalName()); | ||
| 110 | |||
| 111 | if (entryIndex.hasClass(class1) && entryIndex.hasClass(class2)) { | ||
| 112 | return inheritanceIndex.getAncestors(class2).contains(class1); | ||
| 113 | } | ||
| 114 | |||
| 115 | Class<?> class1Class = getClass(Type.getType('L' + class1.getFullName() + ';')); | ||
| 116 | Class<?> class2Class = getClass(Type.getType('L' + class2.getFullName() + ';')); | ||
| 117 | |||
| 118 | if (class1Class == null) { | ||
| 119 | return true; // missing classes to find out | ||
| 120 | } | ||
| 121 | |||
| 122 | if (class2Class != null) { | ||
| 123 | return class1Class.isAssignableFrom(class2Class); | ||
| 124 | } | ||
| 125 | |||
| 126 | if (entryIndex.hasClass(class2)) { | ||
| 127 | Set<ClassEntry> ancestors = inheritanceIndex.getAncestors(class2); | ||
| 128 | |||
| 129 | for (ClassEntry ancestorEntry : ancestors) { | ||
| 130 | Class<?> ancestor = getClass(Type.getType('L' + ancestorEntry.getFullName() + ';')); | ||
| 131 | if (ancestor == null || class1Class.isAssignableFrom(ancestor)) { | ||
| 132 | return true; // assignable, or missing classes to find out | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | return false; | ||
| 137 | } | ||
| 138 | |||
| 139 | return true; // missing classes to find out | ||
| 140 | } | ||
| 141 | |||
| 142 | return false; | ||
| 143 | } | ||
| 144 | |||
| 145 | @Override | ||
| 146 | protected final Class<?> getClass(Type type) { | ||
| 147 | try { | ||
| 148 | return Class.forName(type.getSort() == Type.ARRAY ? type.getDescriptor().replace('/', '.') : type.getClassName(), false, null); | ||
| 149 | } catch (ClassNotFoundException e) { | ||
| 150 | return null; | ||
| 151 | } | ||
| 152 | } | ||
| 153 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/InterpreterPair.java b/src/main/java/cuchaz/enigma/analysis/InterpreterPair.java new file mode 100644 index 00000000..af74c854 --- /dev/null +++ b/src/main/java/cuchaz/enigma/analysis/InterpreterPair.java | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import org.objectweb.asm.Opcodes; | ||
| 4 | import org.objectweb.asm.Type; | ||
| 5 | import org.objectweb.asm.tree.AbstractInsnNode; | ||
| 6 | import org.objectweb.asm.tree.analysis.AnalyzerException; | ||
| 7 | import org.objectweb.asm.tree.analysis.Interpreter; | ||
| 8 | import org.objectweb.asm.tree.analysis.Value; | ||
| 9 | |||
| 10 | import java.util.List; | ||
| 11 | import java.util.Objects; | ||
| 12 | import java.util.stream.Collectors; | ||
| 13 | |||
| 14 | public class InterpreterPair<V extends Value, W extends Value> extends Interpreter<InterpreterPair.PairValue<V, W>> { | ||
| 15 | private final Interpreter<V> left; | ||
| 16 | private final Interpreter<W> right; | ||
| 17 | |||
| 18 | public InterpreterPair(Interpreter<V> left, Interpreter<W> right) { | ||
| 19 | super(Opcodes.ASM7); | ||
| 20 | this.left = left; | ||
| 21 | this.right = right; | ||
| 22 | } | ||
| 23 | |||
| 24 | @Override | ||
| 25 | public PairValue<V, W> newValue(Type type) { | ||
| 26 | return pair( | ||
| 27 | left.newValue(type), | ||
| 28 | right.newValue(type) | ||
| 29 | ); | ||
| 30 | } | ||
| 31 | |||
| 32 | @Override | ||
| 33 | public PairValue<V, W> newOperation(AbstractInsnNode insn) throws AnalyzerException { | ||
| 34 | return pair( | ||
| 35 | left.newOperation(insn), | ||
| 36 | right.newOperation(insn) | ||
| 37 | ); | ||
| 38 | } | ||
| 39 | |||
| 40 | @Override | ||
| 41 | public PairValue<V, W> copyOperation(AbstractInsnNode insn, PairValue<V, W> value) throws AnalyzerException { | ||
| 42 | return pair( | ||
| 43 | left.copyOperation(insn, value.left), | ||
| 44 | right.copyOperation(insn, value.right) | ||
| 45 | ); | ||
| 46 | } | ||
| 47 | |||
| 48 | @Override | ||
| 49 | public PairValue<V, W> unaryOperation(AbstractInsnNode insn, PairValue<V, W> value) throws AnalyzerException { | ||
| 50 | return pair( | ||
| 51 | left.unaryOperation(insn, value.left), | ||
| 52 | right.unaryOperation(insn, value.right) | ||
| 53 | ); | ||
| 54 | } | ||
| 55 | |||
| 56 | @Override | ||
| 57 | public PairValue<V, W> binaryOperation(AbstractInsnNode insn, PairValue<V, W> value1, PairValue<V, W> value2) throws AnalyzerException { | ||
| 58 | return pair( | ||
| 59 | left.binaryOperation(insn, value1.left, value2.left), | ||
| 60 | right.binaryOperation(insn, value1.right, value2.right) | ||
| 61 | ); | ||
| 62 | } | ||
| 63 | |||
| 64 | @Override | ||
| 65 | public PairValue<V, W> ternaryOperation(AbstractInsnNode insn, PairValue<V, W> value1, PairValue<V, W> value2, PairValue<V, W> value3) throws AnalyzerException { | ||
| 66 | return pair( | ||
| 67 | left.ternaryOperation(insn, value1.left, value2.left, value3.left), | ||
| 68 | right.ternaryOperation(insn, value1.right, value2.right, value3.right) | ||
| 69 | ); | ||
| 70 | } | ||
| 71 | |||
| 72 | @Override | ||
| 73 | public PairValue<V, W> naryOperation(AbstractInsnNode insn, List<? extends PairValue<V, W>> values) throws AnalyzerException { | ||
| 74 | return pair( | ||
| 75 | left.naryOperation(insn, values.stream().map(v -> v.left).collect(Collectors.toList())), | ||
| 76 | right.naryOperation(insn, values.stream().map(v -> v.right).collect(Collectors.toList())) | ||
| 77 | ); | ||
| 78 | } | ||
| 79 | |||
| 80 | @Override | ||
| 81 | public void returnOperation(AbstractInsnNode insn, PairValue<V, W> value, PairValue<V, W> expected) throws AnalyzerException { | ||
| 82 | left.returnOperation(insn, value.left, expected.left); | ||
| 83 | right.returnOperation(insn, value.right, expected.right); | ||
| 84 | } | ||
| 85 | |||
| 86 | @Override | ||
| 87 | public PairValue<V, W> merge(PairValue<V, W> value1, PairValue<V, W> value2) { | ||
| 88 | return pair( | ||
| 89 | left.merge(value1.left, value2.left), | ||
| 90 | right.merge(value1.right, value2.right) | ||
| 91 | ); | ||
| 92 | } | ||
| 93 | |||
| 94 | private PairValue<V, W> pair(V left, W right) { | ||
| 95 | if (left == null && right == null) { | ||
| 96 | return null; | ||
| 97 | } | ||
| 98 | |||
| 99 | if (left != null && right != null && left.getSize() != right.getSize()) { | ||
| 100 | throw new IllegalStateException("sizes don't match"); | ||
| 101 | } | ||
| 102 | |||
| 103 | return new PairValue<>(left, right); | ||
| 104 | } | ||
| 105 | |||
| 106 | public static final class PairValue<V extends Value, W extends Value> implements Value { | ||
| 107 | public final V left; | ||
| 108 | public final W right; | ||
| 109 | |||
| 110 | public PairValue(V left, W right) { | ||
| 111 | if (left == null && right == null) { | ||
| 112 | throw new IllegalArgumentException("should use null rather than pair of nulls"); | ||
| 113 | } | ||
| 114 | |||
| 115 | if (left != null && right != null && left.getSize() != right.getSize()) { | ||
| 116 | throw new IllegalArgumentException("sizes don't match"); | ||
| 117 | } | ||
| 118 | |||
| 119 | this.left = left; | ||
| 120 | this.right = right; | ||
| 121 | } | ||
| 122 | |||
| 123 | @Override | ||
| 124 | public boolean equals(Object o) { | ||
| 125 | return o instanceof InterpreterPair.PairValue && Objects.equals(left, ((PairValue) o).left) && Objects.equals(right, ((PairValue) o).right); | ||
| 126 | } | ||
| 127 | |||
| 128 | @Override | ||
| 129 | public int hashCode() { | ||
| 130 | return left.hashCode() * 31 + right.hashCode(); | ||
| 131 | } | ||
| 132 | |||
| 133 | @Override | ||
| 134 | public int getSize() { | ||
| 135 | return (left == null ? right : left).getSize(); | ||
| 136 | } | ||
| 137 | } | ||
| 138 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java b/src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java new file mode 100644 index 00000000..81171038 --- /dev/null +++ b/src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import org.objectweb.asm.tree.MethodNode; | ||
| 4 | |||
| 5 | import java.util.function.Consumer; | ||
| 6 | |||
| 7 | public class MethodNodeWithAction extends MethodNode { | ||
| 8 | private final Consumer<MethodNode> action; | ||
| 9 | |||
| 10 | public MethodNodeWithAction(int api, int access, String name, String descriptor, String signature, String[] exceptions, Consumer<MethodNode> action) { | ||
| 11 | super(api, access, name, descriptor, signature, exceptions); | ||
| 12 | this.action = action; | ||
| 13 | } | ||
| 14 | |||
| 15 | @Override | ||
| 16 | public void visitEnd() { | ||
| 17 | action.accept(this); | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java b/src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java new file mode 100644 index 00000000..5b19d189 --- /dev/null +++ b/src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java | |||
| @@ -0,0 +1,74 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 4 | |||
| 5 | public abstract class ReferenceTargetType { | ||
| 6 | private static final None NONE = new None(); | ||
| 7 | private static final Uninitialized UNINITIALIZED = new Uninitialized(); | ||
| 8 | |||
| 9 | public abstract Kind getKind(); | ||
| 10 | |||
| 11 | public static None none() { | ||
| 12 | return NONE; | ||
| 13 | } | ||
| 14 | |||
| 15 | public static Uninitialized uninitialized() { | ||
| 16 | return UNINITIALIZED; | ||
| 17 | } | ||
| 18 | |||
| 19 | public static ClassType classType(ClassEntry name) { | ||
| 20 | return new ClassType(name); | ||
| 21 | } | ||
| 22 | |||
| 23 | public enum Kind { | ||
| 24 | NONE, | ||
| 25 | UNINITIALIZED, | ||
| 26 | CLASS_TYPE | ||
| 27 | } | ||
| 28 | |||
| 29 | public static class None extends ReferenceTargetType { | ||
| 30 | @Override | ||
| 31 | public Kind getKind() { | ||
| 32 | return Kind.NONE; | ||
| 33 | } | ||
| 34 | |||
| 35 | @Override | ||
| 36 | public String toString() { | ||
| 37 | return "(none)"; | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | public static class Uninitialized extends ReferenceTargetType { | ||
| 42 | @Override | ||
| 43 | public Kind getKind() { | ||
| 44 | return Kind.UNINITIALIZED; | ||
| 45 | } | ||
| 46 | |||
| 47 | @Override | ||
| 48 | public String toString() { | ||
| 49 | return "(uninitialized)"; | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | public static class ClassType extends ReferenceTargetType { | ||
| 54 | private final ClassEntry entry; | ||
| 55 | |||
| 56 | private ClassType(ClassEntry entry) { | ||
| 57 | this.entry = entry; | ||
| 58 | } | ||
| 59 | |||
| 60 | public ClassEntry getEntry() { | ||
| 61 | return entry; | ||
| 62 | } | ||
| 63 | |||
| 64 | @Override | ||
| 65 | public Kind getKind() { | ||
| 66 | return Kind.CLASS_TYPE; | ||
| 67 | } | ||
| 68 | |||
| 69 | @Override | ||
| 70 | public String toString() { | ||
| 71 | return entry.toString(); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java b/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java index 31c6f54f..9a2726e5 100644 --- a/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java | |||
| @@ -12,9 +12,11 @@ public class EntryIndex implements JarIndexer { | |||
| 12 | private Map<ClassEntry, AccessFlags> classes = new HashMap<>(); | 12 | private Map<ClassEntry, AccessFlags> classes = new HashMap<>(); |
| 13 | private Map<FieldEntry, AccessFlags> fields = new HashMap<>(); | 13 | private Map<FieldEntry, AccessFlags> fields = new HashMap<>(); |
| 14 | private Map<MethodEntry, AccessFlags> methods = new HashMap<>(); | 14 | private Map<MethodEntry, AccessFlags> methods = new HashMap<>(); |
| 15 | private Map<ClassEntry, ClassDefEntry> definitions = new HashMap<>(); | ||
| 15 | 16 | ||
| 16 | @Override | 17 | @Override |
| 17 | public void indexClass(ClassDefEntry classEntry) { | 18 | public void indexClass(ClassDefEntry classEntry) { |
| 19 | definitions.put(classEntry, classEntry); | ||
| 18 | classes.put(classEntry, classEntry.getAccess()); | 20 | classes.put(classEntry, classEntry.getAccess()); |
| 19 | } | 21 | } |
| 20 | 22 | ||
| @@ -82,6 +84,10 @@ public class EntryIndex implements JarIndexer { | |||
| 82 | return null; | 84 | return null; |
| 83 | } | 85 | } |
| 84 | 86 | ||
| 87 | public ClassDefEntry getDefinition(ClassEntry entry) { | ||
| 88 | return definitions.get(entry); | ||
| 89 | } | ||
| 90 | |||
| 85 | public Collection<ClassEntry> getClasses() { | 91 | public Collection<ClassEntry> getClasses() { |
| 86 | return classes.keySet(); | 92 | return classes.keySet(); |
| 87 | } | 93 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java b/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java index b730a8a1..f3d419ee 100644 --- a/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java | |||
| @@ -1,52 +1,162 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | 1 | package cuchaz.enigma.analysis.index; |
| 2 | 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; | ||
| 3 | import cuchaz.enigma.translation.representation.AccessFlags; | 7 | import cuchaz.enigma.translation.representation.AccessFlags; |
| 4 | import cuchaz.enigma.translation.representation.Lambda; | 8 | import cuchaz.enigma.translation.representation.Lambda; |
| 5 | import cuchaz.enigma.translation.representation.MethodDescriptor; | 9 | import cuchaz.enigma.translation.representation.MethodDescriptor; |
| 6 | import cuchaz.enigma.translation.representation.Signature; | 10 | import cuchaz.enigma.translation.representation.Signature; |
| 7 | import cuchaz.enigma.translation.representation.entry.*; | 11 | import cuchaz.enigma.translation.representation.entry.*; |
| 8 | import org.objectweb.asm.*; | 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; | ||
| 9 | 21 | ||
| 10 | public class IndexReferenceVisitor extends ClassVisitor { | 22 | public class IndexReferenceVisitor extends ClassVisitor { |
| 11 | private final JarIndexer indexer; | 23 | private final JarIndexer indexer; |
| 24 | private final EntryIndex entryIndex; | ||
| 25 | private final InheritanceIndex inheritanceIndex; | ||
| 12 | private ClassEntry classEntry; | 26 | private ClassEntry classEntry; |
| 27 | private String className; | ||
| 13 | 28 | ||
| 14 | public IndexReferenceVisitor(JarIndexer indexer, int api) { | 29 | public IndexReferenceVisitor(JarIndexer indexer, EntryIndex entryIndex, InheritanceIndex inheritanceIndex, int api) { |
| 15 | super(api); | 30 | super(api); |
| 16 | this.indexer = indexer; | 31 | this.indexer = indexer; |
| 32 | this.entryIndex = entryIndex; | ||
| 33 | this.inheritanceIndex = inheritanceIndex; | ||
| 17 | } | 34 | } |
| 18 | 35 | ||
| 19 | @Override | 36 | @Override |
| 20 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { | 37 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { |
| 21 | this.classEntry = new ClassEntry(name); | 38 | classEntry = new ClassEntry(name); |
| 39 | className = name; | ||
| 22 | } | 40 | } |
| 23 | 41 | ||
| 24 | @Override | 42 | @Override |
| 25 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { | 43 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { |
| 26 | MethodDefEntry entry = new MethodDefEntry(classEntry, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access)); | 44 | MethodDefEntry entry = new MethodDefEntry(classEntry, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access)); |
| 27 | return new Method(this.indexer, entry, this.api); | 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 | }); | ||
| 28 | } | 52 | } |
| 29 | 53 | ||
| 30 | private static class Method extends MethodVisitor { | 54 | private static class MethodInterpreter extends InterpreterPair<BasicValue, SourceValue> { |
| 31 | private final JarIndexer indexer; | ||
| 32 | private final MethodDefEntry callerEntry; | 55 | private final MethodDefEntry callerEntry; |
| 56 | private JarIndexer indexer; | ||
| 33 | 57 | ||
| 34 | public Method(JarIndexer indexer, MethodDefEntry callerEntry, int api) { | 58 | public MethodInterpreter(MethodDefEntry callerEntry, JarIndexer indexer, EntryIndex entryIndex, InheritanceIndex inheritanceIndex) { |
| 35 | super(api); | 59 | super(new IndexSimpleVerifier(entryIndex, inheritanceIndex), new SourceInterpreter()); |
| 36 | this.indexer = indexer; | ||
| 37 | this.callerEntry = callerEntry; | 60 | this.callerEntry = callerEntry; |
| 61 | this.indexer = indexer; | ||
| 38 | } | 62 | } |
| 39 | 63 | ||
| 40 | @Override | 64 | @Override |
| 41 | public void visitFieldInsn(int opcode, String owner, String name, String desc) { | 65 | public PairValue<BasicValue, SourceValue> newOperation(AbstractInsnNode insn) throws AnalyzerException { |
| 42 | FieldEntry fieldEntry = FieldEntry.parse(owner, name, desc); | 66 | if (insn.getOpcode() == Opcodes.GETSTATIC) { |
| 43 | this.indexer.indexFieldReference(callerEntry, fieldEntry); | 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); | ||
| 44 | } | 72 | } |
| 45 | 73 | ||
| 46 | @Override | 74 | @Override |
| 47 | public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { | 75 | public PairValue<BasicValue, SourceValue> unaryOperation(AbstractInsnNode insn, PairValue<BasicValue, SourceValue> value) throws AnalyzerException { |
| 48 | MethodEntry methodEntry = MethodEntry.parse(owner, name, desc); | 76 | if (insn.getOpcode() == Opcodes.PUTSTATIC) { |
| 49 | this.indexer.indexMethodReference(callerEntry, methodEntry); | 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"); | ||
| 50 | } | 160 | } |
| 51 | 161 | ||
| 52 | private static ParentedEntry<?> getHandleEntry(Handle handle) { | 162 | private static ParentedEntry<?> getHandleEntry(Handle handle) { |
| @@ -63,24 +173,8 @@ public class IndexReferenceVisitor extends ClassVisitor { | |||
| 63 | case Opcodes.H_NEWINVOKESPECIAL: | 173 | case Opcodes.H_NEWINVOKESPECIAL: |
| 64 | return MethodEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc()); | 174 | return MethodEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc()); |
| 65 | } | 175 | } |
| 66 | throw new RuntimeException("Invalid handle tag " + handle.getTag()); | ||
| 67 | } | ||
| 68 | 176 | ||
| 69 | @Override | 177 | throw new RuntimeException("Invalid handle tag " + handle.getTag()); |
| 70 | public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { | ||
| 71 | if ("java/lang/invoke/LambdaMetafactory".equals(bsm.getOwner()) && "metafactory".equals(bsm.getName())) { | ||
| 72 | Type samMethodType = (Type) bsmArgs[0]; | ||
| 73 | Handle implMethod = (Handle) bsmArgs[1]; | ||
| 74 | Type instantiatedMethodType = (Type) bsmArgs[2]; | ||
| 75 | |||
| 76 | this.indexer.indexLambda(callerEntry, new Lambda( | ||
| 77 | name, | ||
| 78 | new MethodDescriptor(desc), | ||
| 79 | new MethodDescriptor(samMethodType.getDescriptor()), | ||
| 80 | getHandleEntry(implMethod), | ||
| 81 | new MethodDescriptor(instantiatedMethodType.getDescriptor()) | ||
| 82 | )); | ||
| 83 | } | ||
| 84 | } | 178 | } |
| 85 | } | 179 | } |
| 86 | } | 180 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java index 763282b8..8e92dd86 100644 --- a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java | |||
| @@ -15,6 +15,7 @@ import com.google.common.collect.HashMultimap; | |||
| 15 | import com.google.common.collect.Multimap; | 15 | import com.google.common.collect.Multimap; |
| 16 | import cuchaz.enigma.ProgressListener; | 16 | import cuchaz.enigma.ProgressListener; |
| 17 | import cuchaz.enigma.analysis.ClassCache; | 17 | import cuchaz.enigma.analysis.ClassCache; |
| 18 | import cuchaz.enigma.analysis.ReferenceTargetType; | ||
| 18 | import cuchaz.enigma.translation.mapping.EntryResolver; | 19 | import cuchaz.enigma.translation.mapping.EntryResolver; |
| 19 | import cuchaz.enigma.translation.mapping.IndexEntryResolver; | 20 | import cuchaz.enigma.translation.mapping.IndexEntryResolver; |
| 20 | import cuchaz.enigma.translation.representation.Lambda; | 21 | import cuchaz.enigma.translation.representation.Lambda; |
| @@ -63,7 +64,7 @@ public class JarIndex implements JarIndexer { | |||
| 63 | classCache.visit(() -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE); | 64 | classCache.visit(() -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE); |
| 64 | 65 | ||
| 65 | progress.step(2, "Entry references..."); | 66 | progress.step(2, "Entry references..."); |
| 66 | classCache.visit(() -> new IndexReferenceVisitor(this, Opcodes.ASM5), ClassReader.SKIP_FRAMES); | 67 | classCache.visit(() -> new IndexReferenceVisitor(this, entryIndex, inheritanceIndex, Opcodes.ASM5), 0); |
| 67 | 68 | ||
| 68 | progress.step(3, "Bridge methods..."); | 69 | progress.step(3, "Bridge methods..."); |
| 69 | bridgeMethodIndex.findBridgeMethods(); | 70 | bridgeMethodIndex.findBridgeMethods(); |
| @@ -115,30 +116,30 @@ public class JarIndex implements JarIndexer { | |||
| 115 | } | 116 | } |
| 116 | 117 | ||
| 117 | @Override | 118 | @Override |
| 118 | public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) { | 119 | public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry, ReferenceTargetType targetType) { |
| 119 | if (callerEntry.getParent().isJre()) { | 120 | if (callerEntry.getParent().isJre()) { |
| 120 | return; | 121 | return; |
| 121 | } | 122 | } |
| 122 | 123 | ||
| 123 | indexers.forEach(indexer -> indexer.indexMethodReference(callerEntry, referencedEntry)); | 124 | indexers.forEach(indexer -> indexer.indexMethodReference(callerEntry, referencedEntry, targetType)); |
| 124 | } | 125 | } |
| 125 | 126 | ||
| 126 | @Override | 127 | @Override |
| 127 | public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) { | 128 | public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry, ReferenceTargetType targetType) { |
| 128 | if (callerEntry.getParent().isJre()) { | 129 | if (callerEntry.getParent().isJre()) { |
| 129 | return; | 130 | return; |
| 130 | } | 131 | } |
| 131 | 132 | ||
| 132 | indexers.forEach(indexer -> indexer.indexFieldReference(callerEntry, referencedEntry)); | 133 | indexers.forEach(indexer -> indexer.indexFieldReference(callerEntry, referencedEntry, targetType)); |
| 133 | } | 134 | } |
| 134 | 135 | ||
| 135 | @Override | 136 | @Override |
| 136 | public void indexLambda(MethodDefEntry callerEntry, Lambda lambda) { | 137 | public void indexLambda(MethodDefEntry callerEntry, Lambda lambda, ReferenceTargetType targetType) { |
| 137 | if (callerEntry.getParent().isJre()) { | 138 | if (callerEntry.getParent().isJre()) { |
| 138 | return; | 139 | return; |
| 139 | } | 140 | } |
| 140 | 141 | ||
| 141 | indexers.forEach(indexer -> indexer.indexLambda(callerEntry, lambda)); | 142 | indexers.forEach(indexer -> indexer.indexLambda(callerEntry, lambda, targetType)); |
| 142 | } | 143 | } |
| 143 | 144 | ||
| 144 | public EntryIndex getEntryIndex() { | 145 | public EntryIndex getEntryIndex() { |
diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java index 4f885b39..f17e7c98 100644 --- a/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java +++ b/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | 1 | package cuchaz.enigma.analysis.index; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.analysis.ReferenceTargetType; | ||
| 3 | import cuchaz.enigma.translation.representation.Lambda; | 4 | import cuchaz.enigma.translation.representation.Lambda; |
| 4 | import cuchaz.enigma.translation.representation.entry.*; | 5 | import cuchaz.enigma.translation.representation.entry.*; |
| 5 | 6 | ||
| @@ -13,13 +14,13 @@ public interface JarIndexer { | |||
| 13 | default void indexMethod(MethodDefEntry methodEntry) { | 14 | default void indexMethod(MethodDefEntry methodEntry) { |
| 14 | } | 15 | } |
| 15 | 16 | ||
| 16 | default void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) { | 17 | default void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry, ReferenceTargetType targetType) { |
| 17 | } | 18 | } |
| 18 | 19 | ||
| 19 | default void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) { | 20 | default void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry, ReferenceTargetType targetType) { |
| 20 | } | 21 | } |
| 21 | 22 | ||
| 22 | default void indexLambda(MethodDefEntry callerEntry, Lambda lambda) { | 23 | default void indexLambda(MethodDefEntry callerEntry, Lambda lambda, ReferenceTargetType targetType) { |
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | default void processIndex(JarIndex index) { | 26 | default void processIndex(JarIndex index) { |
diff --git a/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java b/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java index da28ac41..63eb7300 100644 --- a/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java | |||
| @@ -5,6 +5,7 @@ import com.google.common.collect.Lists; | |||
| 5 | import com.google.common.collect.Maps; | 5 | import com.google.common.collect.Maps; |
| 6 | import com.google.common.collect.Sets; | 6 | import com.google.common.collect.Sets; |
| 7 | import cuchaz.enigma.analysis.EntryReference; | 7 | import cuchaz.enigma.analysis.EntryReference; |
| 8 | import cuchaz.enigma.analysis.ReferenceTargetType; | ||
| 8 | import cuchaz.enigma.translation.representation.AccessFlags; | 9 | import cuchaz.enigma.translation.representation.AccessFlags; |
| 9 | import cuchaz.enigma.translation.representation.entry.*; | 10 | import cuchaz.enigma.translation.representation.entry.*; |
| 10 | 11 | ||
| @@ -12,12 +13,29 @@ import java.util.*; | |||
| 12 | 13 | ||
| 13 | public class PackageVisibilityIndex implements JarIndexer { | 14 | public class PackageVisibilityIndex implements JarIndexer { |
| 14 | private static boolean requiresSamePackage(AccessFlags entryAcc, EntryReference ref, InheritanceIndex inheritanceIndex) { | 15 | private static boolean requiresSamePackage(AccessFlags entryAcc, EntryReference ref, InheritanceIndex inheritanceIndex) { |
| 15 | if (entryAcc.isPublic()) return false; | 16 | if (entryAcc.isPublic()) { |
| 17 | return false; | ||
| 18 | } | ||
| 19 | |||
| 16 | if (entryAcc.isProtected()) { | 20 | if (entryAcc.isProtected()) { |
| 17 | Set<ClassEntry> callerAncestors = inheritanceIndex.getAncestors(ref.context.getContainingClass()); | 21 | ClassEntry contextClass = ref.context.getContainingClass(); |
| 18 | return !callerAncestors.contains(ref.entry.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)); | ||
| 19 | } | 36 | } |
| 20 | return !entryAcc.isPrivate(); // if isPrivate is false, it must be package-private | 37 | |
| 38 | return true; | ||
| 21 | } | 39 | } |
| 22 | 40 | ||
| 23 | private final HashMultimap<ClassEntry, ClassEntry> connections = HashMultimap.create(); | 41 | private final HashMultimap<ClassEntry, ClassEntry> connections = HashMultimap.create(); |
| @@ -25,8 +43,10 @@ public class PackageVisibilityIndex implements JarIndexer { | |||
| 25 | private final Map<ClassEntry, Set<ClassEntry>> classPartitions = Maps.newHashMap(); | 43 | private final Map<ClassEntry, Set<ClassEntry>> classPartitions = Maps.newHashMap(); |
| 26 | 44 | ||
| 27 | private void addConnection(ClassEntry classA, ClassEntry classB) { | 45 | private void addConnection(ClassEntry classA, ClassEntry classB) { |
| 28 | connections.put(classA, classB); | 46 | if (classA != classB) { |
| 29 | connections.put(classB, classA); | 47 | connections.put(classA, classB); |
| 48 | connections.put(classB, classA); | ||
| 49 | } | ||
| 30 | } | 50 | } |
| 31 | 51 | ||
| 32 | private void buildPartition(Set<ClassEntry> unassignedClasses, Set<ClassEntry> partition, ClassEntry member) { | 52 | private void buildPartition(Set<ClassEntry> unassignedClasses, Set<ClassEntry> partition, ClassEntry member) { |
diff --git a/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java b/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java index f54c90dd..b26b08b0 100644 --- a/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java | |||
| @@ -3,6 +3,7 @@ package cuchaz.enigma.analysis.index; | |||
| 3 | import com.google.common.collect.HashMultimap; | 3 | import com.google.common.collect.HashMultimap; |
| 4 | import com.google.common.collect.Multimap; | 4 | import com.google.common.collect.Multimap; |
| 5 | import cuchaz.enigma.analysis.EntryReference; | 5 | import cuchaz.enigma.analysis.EntryReference; |
| 6 | import cuchaz.enigma.analysis.ReferenceTargetType; | ||
| 6 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | 7 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; |
| 7 | import cuchaz.enigma.translation.representation.Lambda; | 8 | import cuchaz.enigma.translation.representation.Lambda; |
| 8 | import cuchaz.enigma.translation.representation.MethodDescriptor; | 9 | import cuchaz.enigma.translation.representation.MethodDescriptor; |
| @@ -57,27 +58,27 @@ public class ReferenceIndex implements JarIndexer { | |||
| 57 | } | 58 | } |
| 58 | 59 | ||
| 59 | @Override | 60 | @Override |
| 60 | public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) { | 61 | public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry, ReferenceTargetType targetType) { |
| 61 | referencesToMethods.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry)); | 62 | referencesToMethods.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry, targetType)); |
| 62 | methodReferences.put(callerEntry, referencedEntry); | 63 | methodReferences.put(callerEntry, referencedEntry); |
| 63 | 64 | ||
| 64 | if (referencedEntry.isConstructor()) { | 65 | if (referencedEntry.isConstructor()) { |
| 65 | ClassEntry referencedClass = referencedEntry.getParent(); | 66 | ClassEntry referencedClass = referencedEntry.getParent(); |
| 66 | referencesToClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedEntry.getName(), callerEntry)); | 67 | referencesToClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedEntry.getName(), callerEntry, targetType)); |
| 67 | } | 68 | } |
| 68 | } | 69 | } |
| 69 | 70 | ||
| 70 | @Override | 71 | @Override |
| 71 | public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) { | 72 | public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry, ReferenceTargetType targetType) { |
| 72 | referencesToFields.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry)); | 73 | referencesToFields.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry, targetType)); |
| 73 | } | 74 | } |
| 74 | 75 | ||
| 75 | @Override | 76 | @Override |
| 76 | public void indexLambda(MethodDefEntry callerEntry, Lambda lambda) { | 77 | public void indexLambda(MethodDefEntry callerEntry, Lambda lambda, ReferenceTargetType targetType) { |
| 77 | if (lambda.getImplMethod() instanceof MethodEntry) { | 78 | if (lambda.getImplMethod() instanceof MethodEntry) { |
| 78 | indexMethodReference(callerEntry, (MethodEntry) lambda.getImplMethod()); | 79 | indexMethodReference(callerEntry, (MethodEntry) lambda.getImplMethod(), targetType); |
| 79 | } else { | 80 | } else { |
| 80 | indexFieldReference(callerEntry, (FieldEntry) lambda.getImplMethod()); | 81 | indexFieldReference(callerEntry, (FieldEntry) lambda.getImplMethod(), targetType); |
| 81 | } | 82 | } |
| 82 | 83 | ||
| 83 | indexMethodDescriptor(callerEntry, lambda.getInvokedType()); | 84 | indexMethodDescriptor(callerEntry, lambda.getInvokedType()); |
diff --git a/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java b/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java index 0534edda..b280eef2 100644 --- a/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java +++ b/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java | |||
| @@ -47,6 +47,10 @@ public class AccessFlags { | |||
| 47 | return (flags & Opcodes.ACC_FINAL) != 0; | 47 | return (flags & Opcodes.ACC_FINAL) != 0; |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | public boolean isInterface() { | ||
| 51 | return (flags & Opcodes.ACC_INTERFACE) != 0; | ||
| 52 | } | ||
| 53 | |||
| 50 | public AccessFlags setPrivate() { | 54 | public AccessFlags setPrivate() { |
| 51 | this.setVisibility(Opcodes.ACC_PRIVATE); | 55 | this.setVisibility(Opcodes.ACC_PRIVATE); |
| 52 | return this; | 56 | return this; |