From 422b10b419eb010b612931e8b250b9dd1b7424ed Mon Sep 17 00:00:00 2001 From: Runemoro Date: Sat, 2 Nov 2019 17:23:14 -0400 Subject: Check protected method/field target in visibility index (#157) --- .../cuchaz/enigma/analysis/EntryReference.java | 14 ++ .../enigma/analysis/IndexSimpleVerifier.java | 153 ++++++++++++++++++++ .../cuchaz/enigma/analysis/InterpreterPair.java | 138 ++++++++++++++++++ .../enigma/analysis/MethodNodeWithAction.java | 19 +++ .../enigma/analysis/ReferenceTargetType.java | 74 ++++++++++ .../cuchaz/enigma/analysis/index/EntryIndex.java | 6 + .../analysis/index/IndexReferenceVisitor.java | 156 +++++++++++++++++---- .../cuchaz/enigma/analysis/index/JarIndex.java | 15 +- .../cuchaz/enigma/analysis/index/JarIndexer.java | 7 +- .../analysis/index/PackageVisibilityIndex.java | 32 ++++- .../enigma/analysis/index/ReferenceIndex.java | 17 +-- .../translation/representation/AccessFlags.java | 4 + 12 files changed, 580 insertions(+), 55 deletions(-) create mode 100644 src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java create mode 100644 src/main/java/cuchaz/enigma/analysis/InterpreterPair.java create mode 100644 src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java create mode 100644 src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java (limited to 'src/main/java/cuchaz') 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, C extends Entry> implements T private static final List CONSTRUCTOR_NON_NAMES = Arrays.asList("this", "super", "static"); public E entry; public C context; + public ReferenceTargetType targetType; private boolean sourceName; @@ -37,12 +38,17 @@ public class EntryReference, C extends Entry> implements T } public EntryReference(E entry, String sourceName, C context) { + this(entry, sourceName, context, ReferenceTargetType.none()); + } + + public EntryReference(E entry, String sourceName, C context, ReferenceTargetType targetType) { if (entry == null) { throw new IllegalArgumentException("Entry cannot be null!"); } this.entry = entry; this.context = context; + this.targetType = targetType; this.sourceName = sourceName != null && !sourceName.isEmpty(); if (entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor() && CONSTRUCTOR_NON_NAMES.contains(sourceName)) { @@ -54,6 +60,7 @@ public class EntryReference, C extends Entry> implements T this.entry = entry; this.context = context; this.sourceName = other.sourceName; + this.targetType = other.targetType; } public ClassEntry getLocationClassEntry() { @@ -112,10 +119,17 @@ public class EntryReference, C extends Entry> implements T public String toString() { StringBuilder buf = new StringBuilder(); buf.append(entry); + if (context != null) { buf.append(" called from "); buf.append(context); } + + if (targetType != null && targetType.getKind() != ReferenceTargetType.Kind.NONE) { + buf.append(" on target of type "); + buf.append(targetType); + } + return buf.toString(); } 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 @@ +package cuchaz.enigma.analysis; + +import cuchaz.enigma.analysis.index.EntryIndex; +import cuchaz.enigma.analysis.index.InheritanceIndex; +import cuchaz.enigma.translation.representation.AccessFlags; +import cuchaz.enigma.translation.representation.entry.ClassDefEntry; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.analysis.BasicValue; +import org.objectweb.asm.tree.analysis.SimpleVerifier; + +import java.util.Set; + +public class IndexSimpleVerifier extends SimpleVerifier { + private static final Type OBJECT_TYPE = Type.getType("Ljava/lang/Object;"); + private final EntryIndex entryIndex; + private final InheritanceIndex inheritanceIndex; + + public IndexSimpleVerifier(EntryIndex entryIndex, InheritanceIndex inheritanceIndex) { + super(ASM7, null, null, null, false); + this.entryIndex = entryIndex; + this.inheritanceIndex = inheritanceIndex; + } + + @Override + protected boolean isSubTypeOf(BasicValue value, BasicValue expected) { + Type expectedType = expected.getType(); + Type type = value.getType(); + switch (expectedType.getSort()) { + case Type.INT: + case Type.FLOAT: + case Type.LONG: + case Type.DOUBLE: + return type.equals(expectedType); + case Type.ARRAY: + case Type.OBJECT: + if (type.equals(NULL_TYPE)) { + return true; + } else if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { + if (isAssignableFrom(expectedType, type)) { + return true; + } else if (isInterface(expectedType)) { + return isAssignableFrom(OBJECT_TYPE, type); + } else { + return false; + } + } else { + return false; + } + default: + throw new AssertionError(); + } + } + + @Override + protected boolean isInterface(Type type) { + AccessFlags classAccess = entryIndex.getClassAccess(new ClassEntry(type.getInternalName())); + if (classAccess != null) { + return classAccess.isInterface(); + } + + Class clazz = getClass(type); + if (clazz != null) { + return clazz.isInterface(); + } + + return false; + } + + @Override + protected Type getSuperClass(Type type) { + ClassDefEntry definition = entryIndex.getDefinition(new ClassEntry(type.getInternalName())); + if (definition != null) { + return Type.getType('L' + definition.getSuperClass().getFullName() + ';'); + } + + Class clazz = getClass(type); + if (clazz != null) { + return Type.getType(clazz.getSuperclass()); + } + + return OBJECT_TYPE; + } + + @Override + protected boolean isAssignableFrom(Type type1, Type type2) { + if (type1.equals(type2)) { + return true; + } + + if (type2.equals(NULL_TYPE)) { + return true; + } + + if (type1.getSort() == Type.ARRAY) { + return type2.getSort() == Type.ARRAY && isAssignableFrom(Type.getType(type1.getDescriptor().substring(1)), Type.getType(type2.getDescriptor().substring(1))); + } + + if (type2.getSort() == Type.ARRAY) { + return type1.equals(OBJECT_TYPE); + } + + if (type1.getSort() == Type.OBJECT && type2.getSort() == Type.OBJECT) { + if (type1.equals(OBJECT_TYPE)) { + return true; + } + + ClassEntry class1 = new ClassEntry(type1.getInternalName()); + ClassEntry class2 = new ClassEntry(type2.getInternalName()); + + if (entryIndex.hasClass(class1) && entryIndex.hasClass(class2)) { + return inheritanceIndex.getAncestors(class2).contains(class1); + } + + Class class1Class = getClass(Type.getType('L' + class1.getFullName() + ';')); + Class class2Class = getClass(Type.getType('L' + class2.getFullName() + ';')); + + if (class1Class == null) { + return true; // missing classes to find out + } + + if (class2Class != null) { + return class1Class.isAssignableFrom(class2Class); + } + + if (entryIndex.hasClass(class2)) { + Set ancestors = inheritanceIndex.getAncestors(class2); + + for (ClassEntry ancestorEntry : ancestors) { + Class ancestor = getClass(Type.getType('L' + ancestorEntry.getFullName() + ';')); + if (ancestor == null || class1Class.isAssignableFrom(ancestor)) { + return true; // assignable, or missing classes to find out + } + } + + return false; + } + + return true; // missing classes to find out + } + + return false; + } + + @Override + protected final Class getClass(Type type) { + try { + return Class.forName(type.getSort() == Type.ARRAY ? type.getDescriptor().replace('/', '.') : type.getClassName(), false, null); + } catch (ClassNotFoundException e) { + return null; + } + } +} 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 @@ +package cuchaz.enigma.analysis; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.analysis.AnalyzerException; +import org.objectweb.asm.tree.analysis.Interpreter; +import org.objectweb.asm.tree.analysis.Value; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class InterpreterPair extends Interpreter> { + private final Interpreter left; + private final Interpreter right; + + public InterpreterPair(Interpreter left, Interpreter right) { + super(Opcodes.ASM7); + this.left = left; + this.right = right; + } + + @Override + public PairValue newValue(Type type) { + return pair( + left.newValue(type), + right.newValue(type) + ); + } + + @Override + public PairValue newOperation(AbstractInsnNode insn) throws AnalyzerException { + return pair( + left.newOperation(insn), + right.newOperation(insn) + ); + } + + @Override + public PairValue copyOperation(AbstractInsnNode insn, PairValue value) throws AnalyzerException { + return pair( + left.copyOperation(insn, value.left), + right.copyOperation(insn, value.right) + ); + } + + @Override + public PairValue unaryOperation(AbstractInsnNode insn, PairValue value) throws AnalyzerException { + return pair( + left.unaryOperation(insn, value.left), + right.unaryOperation(insn, value.right) + ); + } + + @Override + public PairValue binaryOperation(AbstractInsnNode insn, PairValue value1, PairValue value2) throws AnalyzerException { + return pair( + left.binaryOperation(insn, value1.left, value2.left), + right.binaryOperation(insn, value1.right, value2.right) + ); + } + + @Override + public PairValue ternaryOperation(AbstractInsnNode insn, PairValue value1, PairValue value2, PairValue value3) throws AnalyzerException { + return pair( + left.ternaryOperation(insn, value1.left, value2.left, value3.left), + right.ternaryOperation(insn, value1.right, value2.right, value3.right) + ); + } + + @Override + public PairValue naryOperation(AbstractInsnNode insn, List> values) throws AnalyzerException { + return pair( + left.naryOperation(insn, values.stream().map(v -> v.left).collect(Collectors.toList())), + right.naryOperation(insn, values.stream().map(v -> v.right).collect(Collectors.toList())) + ); + } + + @Override + public void returnOperation(AbstractInsnNode insn, PairValue value, PairValue expected) throws AnalyzerException { + left.returnOperation(insn, value.left, expected.left); + right.returnOperation(insn, value.right, expected.right); + } + + @Override + public PairValue merge(PairValue value1, PairValue value2) { + return pair( + left.merge(value1.left, value2.left), + right.merge(value1.right, value2.right) + ); + } + + private PairValue pair(V left, W right) { + if (left == null && right == null) { + return null; + } + + if (left != null && right != null && left.getSize() != right.getSize()) { + throw new IllegalStateException("sizes don't match"); + } + + return new PairValue<>(left, right); + } + + public static final class PairValue implements Value { + public final V left; + public final W right; + + public PairValue(V left, W right) { + if (left == null && right == null) { + throw new IllegalArgumentException("should use null rather than pair of nulls"); + } + + if (left != null && right != null && left.getSize() != right.getSize()) { + throw new IllegalArgumentException("sizes don't match"); + } + + this.left = left; + this.right = right; + } + + @Override + public boolean equals(Object o) { + return o instanceof InterpreterPair.PairValue && Objects.equals(left, ((PairValue) o).left) && Objects.equals(right, ((PairValue) o).right); + } + + @Override + public int hashCode() { + return left.hashCode() * 31 + right.hashCode(); + } + + @Override + public int getSize() { + return (left == null ? right : left).getSize(); + } + } +} 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 @@ +package cuchaz.enigma.analysis; + +import org.objectweb.asm.tree.MethodNode; + +import java.util.function.Consumer; + +public class MethodNodeWithAction extends MethodNode { + private final Consumer action; + + public MethodNodeWithAction(int api, int access, String name, String descriptor, String signature, String[] exceptions, Consumer action) { + super(api, access, name, descriptor, signature, exceptions); + this.action = action; + } + + @Override + public void visitEnd() { + action.accept(this); + } +} 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 @@ +package cuchaz.enigma.analysis; + +import cuchaz.enigma.translation.representation.entry.ClassEntry; + +public abstract class ReferenceTargetType { + private static final None NONE = new None(); + private static final Uninitialized UNINITIALIZED = new Uninitialized(); + + public abstract Kind getKind(); + + public static None none() { + return NONE; + } + + public static Uninitialized uninitialized() { + return UNINITIALIZED; + } + + public static ClassType classType(ClassEntry name) { + return new ClassType(name); + } + + public enum Kind { + NONE, + UNINITIALIZED, + CLASS_TYPE + } + + public static class None extends ReferenceTargetType { + @Override + public Kind getKind() { + return Kind.NONE; + } + + @Override + public String toString() { + return "(none)"; + } + } + + public static class Uninitialized extends ReferenceTargetType { + @Override + public Kind getKind() { + return Kind.UNINITIALIZED; + } + + @Override + public String toString() { + return "(uninitialized)"; + } + } + + public static class ClassType extends ReferenceTargetType { + private final ClassEntry entry; + + private ClassType(ClassEntry entry) { + this.entry = entry; + } + + public ClassEntry getEntry() { + return entry; + } + + @Override + public Kind getKind() { + return Kind.CLASS_TYPE; + } + + @Override + public String toString() { + return entry.toString(); + } + } +} 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 { private Map classes = new HashMap<>(); private Map fields = new HashMap<>(); private Map methods = new HashMap<>(); + private Map definitions = new HashMap<>(); @Override public void indexClass(ClassDefEntry classEntry) { + definitions.put(classEntry, classEntry); classes.put(classEntry, classEntry.getAccess()); } @@ -82,6 +84,10 @@ public class EntryIndex implements JarIndexer { return null; } + public ClassDefEntry getDefinition(ClassEntry entry) { + return definitions.get(entry); + } + public Collection getClasses() { return classes.keySet(); } 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 @@ package cuchaz.enigma.analysis.index; +import cuchaz.enigma.analysis.IndexSimpleVerifier; +import cuchaz.enigma.analysis.InterpreterPair; +import cuchaz.enigma.analysis.MethodNodeWithAction; +import cuchaz.enigma.analysis.ReferenceTargetType; import cuchaz.enigma.translation.representation.AccessFlags; import cuchaz.enigma.translation.representation.Lambda; import cuchaz.enigma.translation.representation.MethodDescriptor; import cuchaz.enigma.translation.representation.Signature; import cuchaz.enigma.translation.representation.entry.*; import org.objectweb.asm.*; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.InvokeDynamicInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.analysis.*; + +import java.util.List; +import java.util.stream.Collectors; public class IndexReferenceVisitor extends ClassVisitor { private final JarIndexer indexer; + private final EntryIndex entryIndex; + private final InheritanceIndex inheritanceIndex; private ClassEntry classEntry; + private String className; - public IndexReferenceVisitor(JarIndexer indexer, int api) { + public IndexReferenceVisitor(JarIndexer indexer, EntryIndex entryIndex, InheritanceIndex inheritanceIndex, int api) { super(api); this.indexer = indexer; + this.entryIndex = entryIndex; + this.inheritanceIndex = inheritanceIndex; } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - this.classEntry = new ClassEntry(name); + classEntry = new ClassEntry(name); + className = name; } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodDefEntry entry = new MethodDefEntry(classEntry, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access)); - return new Method(this.indexer, entry, this.api); + return new MethodNodeWithAction(api, access, name, desc, signature, exceptions, methodNode -> { + try { + new Analyzer<>(new MethodInterpreter(entry, indexer, entryIndex, inheritanceIndex)).analyze(className, methodNode); + } catch (AnalyzerException e) { + throw new RuntimeException(e); + } + }); } - private static class Method extends MethodVisitor { - private final JarIndexer indexer; + private static class MethodInterpreter extends InterpreterPair { private final MethodDefEntry callerEntry; + private JarIndexer indexer; - public Method(JarIndexer indexer, MethodDefEntry callerEntry, int api) { - super(api); - this.indexer = indexer; + public MethodInterpreter(MethodDefEntry callerEntry, JarIndexer indexer, EntryIndex entryIndex, InheritanceIndex inheritanceIndex) { + super(new IndexSimpleVerifier(entryIndex, inheritanceIndex), new SourceInterpreter()); this.callerEntry = callerEntry; + this.indexer = indexer; } @Override - public void visitFieldInsn(int opcode, String owner, String name, String desc) { - FieldEntry fieldEntry = FieldEntry.parse(owner, name, desc); - this.indexer.indexFieldReference(callerEntry, fieldEntry); + public PairValue newOperation(AbstractInsnNode insn) throws AnalyzerException { + if (insn.getOpcode() == Opcodes.GETSTATIC) { + FieldInsnNode field = (FieldInsnNode) insn; + indexer.indexFieldReference(callerEntry, FieldEntry.parse(field.owner, field.name, field.desc), ReferenceTargetType.none()); + } + + return super.newOperation(insn); } @Override - public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { - MethodEntry methodEntry = MethodEntry.parse(owner, name, desc); - this.indexer.indexMethodReference(callerEntry, methodEntry); + public PairValue unaryOperation(AbstractInsnNode insn, PairValue value) throws AnalyzerException { + if (insn.getOpcode() == Opcodes.PUTSTATIC) { + FieldInsnNode field = (FieldInsnNode) insn; + indexer.indexFieldReference(callerEntry, FieldEntry.parse(field.owner, field.name, field.desc), ReferenceTargetType.none()); + } + + if (insn.getOpcode() == Opcodes.GETFIELD) { + FieldInsnNode field = (FieldInsnNode) insn; + indexer.indexFieldReference(callerEntry, FieldEntry.parse(field.owner, field.name, field.desc), getReferenceTargetType(value, insn)); + } + + return super.unaryOperation(insn, value); + } + + + @Override + public PairValue binaryOperation(AbstractInsnNode insn, PairValue value1, PairValue value2) throws AnalyzerException { + if (insn.getOpcode() == Opcodes.PUTFIELD) { + FieldInsnNode field = (FieldInsnNode) insn; + FieldEntry fieldEntry = FieldEntry.parse(field.owner, field.name, field.desc); + indexer.indexFieldReference(callerEntry, fieldEntry, ReferenceTargetType.none()); + } + + return super.binaryOperation(insn, value1, value2); + } + + @Override + public PairValue naryOperation(AbstractInsnNode insn, List> values) throws AnalyzerException { + if (insn.getOpcode() == Opcodes.INVOKEINTERFACE || insn.getOpcode() == Opcodes.INVOKESPECIAL || insn.getOpcode() == Opcodes.INVOKEVIRTUAL) { + MethodInsnNode methodInsn = (MethodInsnNode) insn; + indexer.indexMethodReference(callerEntry, MethodEntry.parse(methodInsn.owner, methodInsn.name, methodInsn.desc), getReferenceTargetType(values.get(0), insn)); + } + + if (insn.getOpcode() == Opcodes.INVOKESTATIC) { + MethodInsnNode methodInsn = (MethodInsnNode) insn; + indexer.indexMethodReference(callerEntry, MethodEntry.parse(methodInsn.owner, methodInsn.name, methodInsn.desc), ReferenceTargetType.none()); + } + + if (insn.getOpcode() == Opcodes.INVOKEDYNAMIC) { + InvokeDynamicInsnNode invokeDynamicInsn = (InvokeDynamicInsnNode) insn; + List args = values.stream().map(v -> v.right.insns.stream().findFirst().orElseThrow(AssertionError::new)).collect(Collectors.toList()); + + if ("java/lang/invoke/LambdaMetafactory".equals(invokeDynamicInsn.bsm.getOwner()) && "metafactory".equals(invokeDynamicInsn.bsm.getName())) { + Type samMethodType = (Type) invokeDynamicInsn.bsmArgs[0]; + Handle implMethod = (Handle) invokeDynamicInsn.bsmArgs[1]; + Type instantiatedMethodType = (Type) invokeDynamicInsn.bsmArgs[2]; + + ReferenceTargetType targetType; + if (implMethod.getTag() != Opcodes.H_GETSTATIC && implMethod.getTag() != Opcodes.H_PUTFIELD && implMethod.getTag() != Opcodes.H_INVOKESTATIC) { + if (instantiatedMethodType.getArgumentTypes().length < Type.getArgumentTypes(implMethod.getDesc()).length) { + targetType = getReferenceTargetType(values.get(0), insn); + } else { + targetType = ReferenceTargetType.none(); // no "this" argument + } + } else { + targetType = ReferenceTargetType.none(); + } + + indexer.indexLambda(callerEntry, new Lambda( + invokeDynamicInsn.name, + new MethodDescriptor(invokeDynamicInsn.desc), + new MethodDescriptor(samMethodType.getDescriptor()), + getHandleEntry(implMethod), + new MethodDescriptor(instantiatedMethodType.getDescriptor()) + ), targetType); + } + } + + return super.naryOperation(insn, values); + } + + private ReferenceTargetType getReferenceTargetType(PairValue target, AbstractInsnNode insn) throws AnalyzerException { + if (target.left == BasicValue.UNINITIALIZED_VALUE) { + return ReferenceTargetType.uninitialized(); + } + + if (target.left.getType().getSort() == Type.OBJECT) { + return ReferenceTargetType.classType(new ClassEntry(target.left.getType().getInternalName())); + } + + if (target.left.getType().getSort() == Type.ARRAY) { + return ReferenceTargetType.classType(new ClassEntry("java/lang/Object")); + } + + throw new AnalyzerException(insn, "called method on or accessed field of non-object type"); } private static ParentedEntry getHandleEntry(Handle handle) { @@ -63,24 +173,8 @@ public class IndexReferenceVisitor extends ClassVisitor { case Opcodes.H_NEWINVOKESPECIAL: return MethodEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc()); } - throw new RuntimeException("Invalid handle tag " + handle.getTag()); - } - @Override - public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { - if ("java/lang/invoke/LambdaMetafactory".equals(bsm.getOwner()) && "metafactory".equals(bsm.getName())) { - Type samMethodType = (Type) bsmArgs[0]; - Handle implMethod = (Handle) bsmArgs[1]; - Type instantiatedMethodType = (Type) bsmArgs[2]; - - this.indexer.indexLambda(callerEntry, new Lambda( - name, - new MethodDescriptor(desc), - new MethodDescriptor(samMethodType.getDescriptor()), - getHandleEntry(implMethod), - new MethodDescriptor(instantiatedMethodType.getDescriptor()) - )); - } + throw new RuntimeException("Invalid handle tag " + handle.getTag()); } } } 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; import com.google.common.collect.Multimap; import cuchaz.enigma.ProgressListener; import cuchaz.enigma.analysis.ClassCache; +import cuchaz.enigma.analysis.ReferenceTargetType; import cuchaz.enigma.translation.mapping.EntryResolver; import cuchaz.enigma.translation.mapping.IndexEntryResolver; import cuchaz.enigma.translation.representation.Lambda; @@ -63,7 +64,7 @@ public class JarIndex implements JarIndexer { classCache.visit(() -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE); progress.step(2, "Entry references..."); - classCache.visit(() -> new IndexReferenceVisitor(this, Opcodes.ASM5), ClassReader.SKIP_FRAMES); + classCache.visit(() -> new IndexReferenceVisitor(this, entryIndex, inheritanceIndex, Opcodes.ASM5), 0); progress.step(3, "Bridge methods..."); bridgeMethodIndex.findBridgeMethods(); @@ -115,30 +116,30 @@ public class JarIndex implements JarIndexer { } @Override - public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) { + public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry, ReferenceTargetType targetType) { if (callerEntry.getParent().isJre()) { return; } - indexers.forEach(indexer -> indexer.indexMethodReference(callerEntry, referencedEntry)); + indexers.forEach(indexer -> indexer.indexMethodReference(callerEntry, referencedEntry, targetType)); } @Override - public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) { + public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry, ReferenceTargetType targetType) { if (callerEntry.getParent().isJre()) { return; } - indexers.forEach(indexer -> indexer.indexFieldReference(callerEntry, referencedEntry)); + indexers.forEach(indexer -> indexer.indexFieldReference(callerEntry, referencedEntry, targetType)); } @Override - public void indexLambda(MethodDefEntry callerEntry, Lambda lambda) { + public void indexLambda(MethodDefEntry callerEntry, Lambda lambda, ReferenceTargetType targetType) { if (callerEntry.getParent().isJre()) { return; } - indexers.forEach(indexer -> indexer.indexLambda(callerEntry, lambda)); + indexers.forEach(indexer -> indexer.indexLambda(callerEntry, lambda, targetType)); } 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 @@ package cuchaz.enigma.analysis.index; +import cuchaz.enigma.analysis.ReferenceTargetType; import cuchaz.enigma.translation.representation.Lambda; import cuchaz.enigma.translation.representation.entry.*; @@ -13,13 +14,13 @@ public interface JarIndexer { default void indexMethod(MethodDefEntry methodEntry) { } - default void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) { + default void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry, ReferenceTargetType targetType) { } - default void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) { + default void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry, ReferenceTargetType targetType) { } - default void indexLambda(MethodDefEntry callerEntry, Lambda lambda) { + default void indexLambda(MethodDefEntry callerEntry, Lambda lambda, ReferenceTargetType targetType) { } 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; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.ReferenceTargetType; import cuchaz.enigma.translation.representation.AccessFlags; import cuchaz.enigma.translation.representation.entry.*; @@ -12,12 +13,29 @@ import java.util.*; public class PackageVisibilityIndex implements JarIndexer { private static boolean requiresSamePackage(AccessFlags entryAcc, EntryReference ref, InheritanceIndex inheritanceIndex) { - if (entryAcc.isPublic()) return false; + if (entryAcc.isPublic()) { + return false; + } + if (entryAcc.isProtected()) { - Set callerAncestors = inheritanceIndex.getAncestors(ref.context.getContainingClass()); - return !callerAncestors.contains(ref.entry.getContainingClass()); + ClassEntry contextClass = ref.context.getContainingClass(); + ClassEntry referencedClass = ref.entry.getContainingClass(); + + if (!inheritanceIndex.getAncestors(contextClass).contains(referencedClass)) { + return true; // access to protected member not in superclass + } + + if (ref.targetType.getKind() == ReferenceTargetType.Kind.NONE) { + return false; // access to superclass or static superclass member + } + + // access to instance member only valid if target's class assignable to context class + return !(ref.targetType.getKind() == ReferenceTargetType.Kind.UNINITIALIZED || + ((ReferenceTargetType.ClassType) ref.targetType).getEntry().equals(contextClass) || + inheritanceIndex.getAncestors(((ReferenceTargetType.ClassType) ref.targetType).getEntry()).contains(contextClass)); } - return !entryAcc.isPrivate(); // if isPrivate is false, it must be package-private + + return true; } private final HashMultimap connections = HashMultimap.create(); @@ -25,8 +43,10 @@ public class PackageVisibilityIndex implements JarIndexer { private final Map> classPartitions = Maps.newHashMap(); private void addConnection(ClassEntry classA, ClassEntry classB) { - connections.put(classA, classB); - connections.put(classB, classA); + if (classA != classB) { + connections.put(classA, classB); + connections.put(classB, classA); + } } private void buildPartition(Set unassignedClasses, Set 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; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.ReferenceTargetType; import cuchaz.enigma.translation.mapping.ResolutionStrategy; import cuchaz.enigma.translation.representation.Lambda; import cuchaz.enigma.translation.representation.MethodDescriptor; @@ -57,27 +58,27 @@ public class ReferenceIndex implements JarIndexer { } @Override - public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) { - referencesToMethods.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry)); + public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry, ReferenceTargetType targetType) { + referencesToMethods.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry, targetType)); methodReferences.put(callerEntry, referencedEntry); if (referencedEntry.isConstructor()) { ClassEntry referencedClass = referencedEntry.getParent(); - referencesToClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedEntry.getName(), callerEntry)); + referencesToClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedEntry.getName(), callerEntry, targetType)); } } @Override - public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) { - referencesToFields.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry)); + public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry, ReferenceTargetType targetType) { + referencesToFields.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry, targetType)); } @Override - public void indexLambda(MethodDefEntry callerEntry, Lambda lambda) { + public void indexLambda(MethodDefEntry callerEntry, Lambda lambda, ReferenceTargetType targetType) { if (lambda.getImplMethod() instanceof MethodEntry) { - indexMethodReference(callerEntry, (MethodEntry) lambda.getImplMethod()); + indexMethodReference(callerEntry, (MethodEntry) lambda.getImplMethod(), targetType); } else { - indexFieldReference(callerEntry, (FieldEntry) lambda.getImplMethod()); + indexFieldReference(callerEntry, (FieldEntry) lambda.getImplMethod(), targetType); } 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 { return (flags & Opcodes.ACC_FINAL) != 0; } + public boolean isInterface() { + return (flags & Opcodes.ACC_INTERFACE) != 0; + } + public AccessFlags setPrivate() { this.setVisibility(Opcodes.ACC_PRIVATE); return this; -- cgit v1.2.3