summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Runemoro2019-11-02 17:23:14 -0400
committerGravatar modmuss502019-11-02 21:23:14 +0000
commit422b10b419eb010b612931e8b250b9dd1b7424ed (patch)
treeb9c1d3a7add5b8d01d5b0196d6c29ad00273987c /src
parentFix array class translation (#173) (diff)
downloadenigma-422b10b419eb010b612931e8b250b9dd1b7424ed.tar.gz
enigma-422b10b419eb010b612931e8b250b9dd1b7424ed.tar.xz
enigma-422b10b419eb010b612931e8b250b9dd1b7424ed.zip
Check protected method/field target in visibility index (#157)
Diffstat (limited to 'src')
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryReference.java14
-rw-r--r--src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java153
-rw-r--r--src/main/java/cuchaz/enigma/analysis/InterpreterPair.java138
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java19
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java74
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java6
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java156
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/JarIndex.java15
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java7
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java32
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java17
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java4
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 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.analysis.index.EntryIndex;
4import cuchaz.enigma.analysis.index.InheritanceIndex;
5import cuchaz.enigma.translation.representation.AccessFlags;
6import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
7import cuchaz.enigma.translation.representation.entry.ClassEntry;
8import org.objectweb.asm.Type;
9import org.objectweb.asm.tree.analysis.BasicValue;
10import org.objectweb.asm.tree.analysis.SimpleVerifier;
11
12import java.util.Set;
13
14public 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 @@
1package cuchaz.enigma.analysis;
2
3import org.objectweb.asm.Opcodes;
4import org.objectweb.asm.Type;
5import org.objectweb.asm.tree.AbstractInsnNode;
6import org.objectweb.asm.tree.analysis.AnalyzerException;
7import org.objectweb.asm.tree.analysis.Interpreter;
8import org.objectweb.asm.tree.analysis.Value;
9
10import java.util.List;
11import java.util.Objects;
12import java.util.stream.Collectors;
13
14public 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 @@
1package cuchaz.enigma.analysis;
2
3import org.objectweb.asm.tree.MethodNode;
4
5import java.util.function.Consumer;
6
7public class MethodNodeWithAction extends MethodNode {
8 private final Consumer<MethodNode> action;
9
10 public MethodNodeWithAction(int api, int access, String name, String descriptor, String signature, String[] exceptions, Consumer<MethodNode> action) {
11 super(api, access, name, descriptor, signature, exceptions);
12 this.action = action;
13 }
14
15 @Override
16 public void visitEnd() {
17 action.accept(this);
18 }
19}
diff --git a/src/main/java/cuchaz/enigma/analysis/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 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.translation.representation.entry.ClassEntry;
4
5public abstract class ReferenceTargetType {
6 private static final None NONE = new None();
7 private static final Uninitialized UNINITIALIZED = new Uninitialized();
8
9 public abstract Kind getKind();
10
11 public static None none() {
12 return NONE;
13 }
14
15 public static Uninitialized uninitialized() {
16 return UNINITIALIZED;
17 }
18
19 public static ClassType classType(ClassEntry name) {
20 return new ClassType(name);
21 }
22
23 public enum Kind {
24 NONE,
25 UNINITIALIZED,
26 CLASS_TYPE
27 }
28
29 public static class None extends ReferenceTargetType {
30 @Override
31 public Kind getKind() {
32 return Kind.NONE;
33 }
34
35 @Override
36 public String toString() {
37 return "(none)";
38 }
39 }
40
41 public static class Uninitialized extends ReferenceTargetType {
42 @Override
43 public Kind getKind() {
44 return Kind.UNINITIALIZED;
45 }
46
47 @Override
48 public String toString() {
49 return "(uninitialized)";
50 }
51 }
52
53 public static class ClassType extends ReferenceTargetType {
54 private final ClassEntry entry;
55
56 private ClassType(ClassEntry entry) {
57 this.entry = entry;
58 }
59
60 public ClassEntry getEntry() {
61 return entry;
62 }
63
64 @Override
65 public Kind getKind() {
66 return Kind.CLASS_TYPE;
67 }
68
69 @Override
70 public String toString() {
71 return entry.toString();
72 }
73 }
74}
diff --git a/src/main/java/cuchaz/enigma/analysis/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 @@
1package cuchaz.enigma.analysis.index; 1package cuchaz.enigma.analysis.index;
2 2
3import cuchaz.enigma.analysis.IndexSimpleVerifier;
4import cuchaz.enigma.analysis.InterpreterPair;
5import cuchaz.enigma.analysis.MethodNodeWithAction;
6import cuchaz.enigma.analysis.ReferenceTargetType;
3import cuchaz.enigma.translation.representation.AccessFlags; 7import cuchaz.enigma.translation.representation.AccessFlags;
4import cuchaz.enigma.translation.representation.Lambda; 8import cuchaz.enigma.translation.representation.Lambda;
5import cuchaz.enigma.translation.representation.MethodDescriptor; 9import cuchaz.enigma.translation.representation.MethodDescriptor;
6import cuchaz.enigma.translation.representation.Signature; 10import cuchaz.enigma.translation.representation.Signature;
7import cuchaz.enigma.translation.representation.entry.*; 11import cuchaz.enigma.translation.representation.entry.*;
8import org.objectweb.asm.*; 12import org.objectweb.asm.*;
13import org.objectweb.asm.tree.AbstractInsnNode;
14import org.objectweb.asm.tree.FieldInsnNode;
15import org.objectweb.asm.tree.InvokeDynamicInsnNode;
16import org.objectweb.asm.tree.MethodInsnNode;
17import org.objectweb.asm.tree.analysis.*;
18
19import java.util.List;
20import java.util.stream.Collectors;
9 21
10public class IndexReferenceVisitor extends ClassVisitor { 22public 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;
15import com.google.common.collect.Multimap; 15import com.google.common.collect.Multimap;
16import cuchaz.enigma.ProgressListener; 16import cuchaz.enigma.ProgressListener;
17import cuchaz.enigma.analysis.ClassCache; 17import cuchaz.enigma.analysis.ClassCache;
18import cuchaz.enigma.analysis.ReferenceTargetType;
18import cuchaz.enigma.translation.mapping.EntryResolver; 19import cuchaz.enigma.translation.mapping.EntryResolver;
19import cuchaz.enigma.translation.mapping.IndexEntryResolver; 20import cuchaz.enigma.translation.mapping.IndexEntryResolver;
20import cuchaz.enigma.translation.representation.Lambda; 21import 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 @@
1package cuchaz.enigma.analysis.index; 1package cuchaz.enigma.analysis.index;
2 2
3import cuchaz.enigma.analysis.ReferenceTargetType;
3import cuchaz.enigma.translation.representation.Lambda; 4import cuchaz.enigma.translation.representation.Lambda;
4import cuchaz.enigma.translation.representation.entry.*; 5import 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;
5import com.google.common.collect.Maps; 5import com.google.common.collect.Maps;
6import com.google.common.collect.Sets; 6import com.google.common.collect.Sets;
7import cuchaz.enigma.analysis.EntryReference; 7import cuchaz.enigma.analysis.EntryReference;
8import cuchaz.enigma.analysis.ReferenceTargetType;
8import cuchaz.enigma.translation.representation.AccessFlags; 9import cuchaz.enigma.translation.representation.AccessFlags;
9import cuchaz.enigma.translation.representation.entry.*; 10import cuchaz.enigma.translation.representation.entry.*;
10 11
@@ -12,12 +13,29 @@ import java.util.*;
12 13
13public class PackageVisibilityIndex implements JarIndexer { 14public 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;
3import com.google.common.collect.HashMultimap; 3import com.google.common.collect.HashMultimap;
4import com.google.common.collect.Multimap; 4import com.google.common.collect.Multimap;
5import cuchaz.enigma.analysis.EntryReference; 5import cuchaz.enigma.analysis.EntryReference;
6import cuchaz.enigma.analysis.ReferenceTargetType;
6import cuchaz.enigma.translation.mapping.ResolutionStrategy; 7import cuchaz.enigma.translation.mapping.ResolutionStrategy;
7import cuchaz.enigma.translation.representation.Lambda; 8import cuchaz.enigma.translation.representation.Lambda;
8import cuchaz.enigma.translation.representation.MethodDescriptor; 9import 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;