From 3dfaec50333e6a541d0fc4d962418e6b1b388300 Mon Sep 17 00:00:00 2001 From: Skylot Date: Thu, 14 Jan 2021 20:01:53 +0000 Subject: [PATCH] feat: initial support for 'invoke-custom' instruction (#384) --- .../main/java/jadx/core/codegen/ClassGen.java | 2 +- .../main/java/jadx/core/codegen/InsnGen.java | 92 ++++++++++++- .../main/java/jadx/core/codegen/NameGen.java | 4 + .../java/jadx/core/dex/info/MethodInfo.java | 7 + .../core/dex/instructions/InsnDecoder.java | 7 + .../dex/instructions/InvokeCustomBuilder.java | 128 ++++++++++++++++++ .../dex/instructions/InvokeCustomNode.java | 90 ++++++++++++ .../core/dex/instructions/InvokeNode.java | 12 +- .../core/dex/instructions/InvokeType.java | 2 + .../core/dex/instructions/args/ArgType.java | 4 + .../jadx/core/dex/visitors/ClassModifier.java | 2 +- .../jadx/core/dex/visitors/InlineMethods.java | 9 +- .../visitors/shrink/CodeShrinkVisitor.java | 3 + .../integration/java8/TestLambdaStatic.java | 78 +++++++++++ .../plugins/input/dex/insns/DexInsnData.java | 8 ++ .../input/dex/sections/DexCallSite.java | 39 ++++++ .../input/dex/sections/DexClassData.java | 8 +- .../plugins/input/dex/sections/DexHeader.java | 33 +++++ .../input/dex/sections/DexMethodProto.java | 31 +++++ .../input/dex/sections/SectionReader.java | 73 +++++++++- .../annotations/AnnotationsParser.java | 4 + .../annotations/EncodedValueParser.java | 30 ++-- .../jadx/plugins/input/dex/utils/Utils.java | 3 + .../api/plugins/input/data/ICallSite.java | 12 ++ .../api/plugins/input/data/IMethodHandle.java | 12 ++ .../api/plugins/input/data/IMethodProto.java | 10 ++ .../plugins/input/data/MethodHandleType.java | 26 ++++ .../input/data/annotations/EncodedType.java | 2 + .../input/data/impl/FieldRefHandle.java | 37 +++++ .../input/data/impl/MethodRefHandle.java | 42 ++++++ .../api/plugins/input/insns/InsnData.java | 3 + 31 files changed, 779 insertions(+), 34 deletions(-) create mode 100644 jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomBuilder.java create mode 100644 jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomNode.java create mode 100644 jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaStatic.java create mode 100644 jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexCallSite.java create mode 100644 jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexMethodProto.java create mode 100644 jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/ICallSite.java create mode 100644 jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/IMethodHandle.java create mode 100644 jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/IMethodProto.java create mode 100644 jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/MethodHandleType.java create mode 100644 jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/impl/FieldRefHandle.java create mode 100644 jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/impl/MethodRefHandle.java diff --git a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java index e2aaff525..e67bf94ad 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java @@ -254,7 +254,7 @@ public class ClassGen { private void addInnerClsAndMethods(CodeWriter clsCode) { Stream.of(cls.getInnerClasses(), cls.getMethods()) .flatMap(Collection::stream) - .filter(node -> !node.contains(AFlag.DONT_GENERATE)) + .filter(node -> !node.contains(AFlag.DONT_GENERATE) || fallback) .sorted(Comparator.comparingInt(LineAttrNode::getSourceLine)) .forEach(node -> { if (node instanceof ClassNode) { diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index a33fc7c9a..54137789d 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -30,6 +30,7 @@ import jadx.core.dex.instructions.GotoNode; import jadx.core.dex.instructions.IfNode; import jadx.core.dex.instructions.IndexInsnNode; import jadx.core.dex.instructions.InsnType; +import jadx.core.dex.instructions.InvokeCustomNode; import jadx.core.dex.instructions.InvokeNode; import jadx.core.dex.instructions.InvokeType; import jadx.core.dex.instructions.NewArrayNode; @@ -700,11 +701,15 @@ public class InsnGen { } private void makeInvoke(InvokeNode insn, CodeWriter code) throws CodegenException { + InvokeType type = insn.getInvokeType(); + if (type == InvokeType.CUSTOM) { + makeInvokeLambda(code, (InvokeCustomNode) insn); + return; + } MethodInfo callMth = insn.getCallMth(); MethodNode callMthNode = mth.root().deepResolveMethod(callMth); int k = 0; - InvokeType type = insn.getInvokeType(); switch (type) { case DIRECT: case VIRTUAL: @@ -746,6 +751,91 @@ public class InsnGen { generateMethodArguments(code, insn, k, callMthNode); } + private void makeInvokeLambda(CodeWriter code, InvokeCustomNode customNode) throws CodegenException { + if (fallback || !customNode.isInlineInsn()) { + makeSimpleLambda(code, customNode); + return; + } + MethodNode callMth = (MethodNode) customNode.getCallInsn().get(AType.METHOD_DETAILS); + makeInlinedLambdaMethod(code, customNode, callMth); + } + + private void makeSimpleLambda(CodeWriter code, InvokeCustomNode customNode) { + try { + InsnNode callInsn = customNode.getCallInsn(); + MethodInfo implMthInfo = customNode.getImplMthInfo(); + int implArgsCount = implMthInfo.getArgsCount(); + if (implArgsCount == 0) { + code.add("()"); + } else { + code.add('('); + // rename lambda args + int callArgsCount = callInsn.getArgsCount(); + int startArg = callArgsCount - implArgsCount; + if (startArg < 0) { + System.out.println(); + } + for (int i = startArg; i < callArgsCount; i++) { + if (i != startArg) { + code.add(", "); + } + addArg(code, callInsn.getArg(i)); + } + code.add(')'); + } + code.add(" -> {"); + if (fallback) { + code.add(" // ").add(implMthInfo.toString()); + } + code.incIndent(); + code.startLine(); + if (!implMthInfo.getReturnType().isVoid()) { + code.add("return "); + } + makeInsn(callInsn, code, Flags.INLINE); + code.add(";"); + + code.decIndent(); + code.startLine('}'); + } catch (Exception e) { + throw new JadxRuntimeException("Failed to generate 'invoke-custom' instruction: " + e.getMessage(), e); + } + } + + private void makeInlinedLambdaMethod(CodeWriter code, InvokeCustomNode customNode, MethodNode callMth) throws CodegenException { + MethodGen callMthGen = new MethodGen(mgen.getClassGen(), callMth); + NameGen nameGen = callMthGen.getNameGen(); + nameGen.inheritUsedNames(this.mgen.getNameGen()); + + List implArgs = customNode.getImplMthInfo().getArgumentsTypes(); + List callArgs = callMth.getArgRegs(); + if (implArgs.isEmpty()) { + code.add("()"); + } else { + int callArgsCount = callArgs.size(); + int startArg = callArgsCount - implArgs.size(); + for (int i = startArg; i < callArgsCount; i++) { + if (i != startArg) { + code.add(", "); + } + CodeVar argCodeVar = callArgs.get(i).getSVar().getCodeVar(); + code.add(nameGen.assignArg(argCodeVar)); + } + } + // force set external arg names into call method args + int extArgsCount = customNode.getArgsCount(); + for (int i = 0; i < extArgsCount; i++) { + RegisterArg extArg = (RegisterArg) customNode.getArg(i); + callArgs.get(i).setName(extArg.getName()); + } + code.add(" -> {"); + code.incIndent(); + callMthGen.addInstructions(code); + + code.decIndent(); + code.startLine('}'); + } + @Nullable private ClassInfo getClassForSuperCall(CodeWriter code, MethodInfo callMth) { ClassNode useCls = mth.getParentClass(); diff --git a/jadx-core/src/main/java/jadx/core/codegen/NameGen.java b/jadx-core/src/main/java/jadx/core/codegen/NameGen.java index d15fc2996..3916822be 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/NameGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/NameGen.java @@ -59,6 +59,10 @@ public class NameGen { addNamesUsedInClass(); } + public void inheritUsedNames(NameGen otherNameGen) { + varNames.addAll(otherNameGen.varNames); + } + private void addNamesUsedInClass() { ClassNode parentClass = mth.getParentClass(); for (FieldNode field : parentClass.getFields()) { diff --git a/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java index 508dab3f5..c635087ff 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java @@ -5,6 +5,7 @@ import java.util.Objects; import org.jetbrains.annotations.Nullable; +import jadx.api.plugins.input.data.IMethodProto; import jadx.api.plugins.input.data.IMethodRef; import jadx.core.codegen.TypeGen; import jadx.core.dex.instructions.args.ArgType; @@ -52,6 +53,12 @@ public final class MethodInfo implements Comparable { return root.getInfoStorage().putMethod(newMth); } + public static MethodInfo fromMethodProto(RootNode root, ClassInfo declClass, String name, IMethodProto proto) { + List args = Utils.collectionMap(proto.getArgTypes(), ArgType::parse); + ArgType returnType = ArgType.parse(proto.getReturnType()); + return fromDetails(root, declClass, name, args, returnType); + } + public String makeSignature(boolean includeRetType) { return makeSignature(false, includeRetType); } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java index 703003e42..c9dd3d93c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java @@ -418,6 +418,8 @@ public class InsnDecoder { return invoke(insn, InvokeType.SUPER, false); case INVOKE_VIRTUAL: return invoke(insn, InvokeType.VIRTUAL, false); + case INVOKE_CUSTOM: + return invoke(insn, InvokeType.CUSTOM, false); case INVOKE_DIRECT_RANGE: return invoke(insn, InvokeType.DIRECT, true); @@ -427,6 +429,8 @@ public class InsnDecoder { return invoke(insn, InvokeType.SUPER, true); case INVOKE_VIRTUAL_RANGE: return invoke(insn, InvokeType.VIRTUAL, true); + case INVOKE_CUSTOM_RANGE: + return invoke(insn, InvokeType.CUSTOM, true); case NEW_INSTANCE: ArgType clsType = ArgType.parse(insn.getIndexAsType()); @@ -524,6 +528,9 @@ public class InsnDecoder { } private InsnNode invoke(InsnData insn, InvokeType type, boolean isRange) { + if (type == InvokeType.CUSTOM) { + return InvokeCustomBuilder.build(method, insn, isRange); + } MethodInfo mthInfo = MethodInfo.fromRef(root, insn.getIndexAsMethod()); return new InvokeNode(mthInfo, insn, type, isRange); } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomBuilder.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomBuilder.java new file mode 100644 index 000000000..fd1bca23b --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomBuilder.java @@ -0,0 +1,128 @@ +package jadx.core.dex.instructions; + +import java.util.List; + +import jadx.api.plugins.input.data.ICallSite; +import jadx.api.plugins.input.data.IMethodHandle; +import jadx.api.plugins.input.data.IMethodProto; +import jadx.api.plugins.input.data.IMethodRef; +import jadx.api.plugins.input.data.MethodHandleType; +import jadx.api.plugins.input.data.annotations.EncodedValue; +import jadx.api.plugins.input.insns.InsnData; +import jadx.core.dex.attributes.AFlag; +import jadx.core.dex.info.ClassInfo; +import jadx.core.dex.info.MethodInfo; +import jadx.core.dex.instructions.args.ArgType; +import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.instructions.args.NamedArg; +import jadx.core.dex.nodes.InsnNode; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.nodes.RootNode; +import jadx.core.utils.exceptions.JadxRuntimeException; + +public class InvokeCustomBuilder { + + public static InsnNode build(MethodNode mth, InsnData insn, boolean isRange) { + try { + ICallSite callSite = insn.getIndexAsCallSite(); + callSite.load(); + List values = callSite.getValues(); + if (!checkLinkerMethod(values)) { + throw new JadxRuntimeException("Failed to process invoke-custom instruction: " + callSite); + } + IMethodHandle callMthHandle = (IMethodHandle) values.get(4).getValue(); + MethodHandleType methodHandleType = callMthHandle.getType(); + if (methodHandleType.isField()) { + throw new JadxRuntimeException("Not yet supported"); + } + RootNode root = mth.root(); + IMethodProto lambdaProto = (IMethodProto) values.get(2).getValue(); + MethodInfo lambdaInfo = MethodInfo.fromMethodProto(root, mth.getParentClass().getClassInfo(), "", lambdaProto); + + InvokeCustomNode invokeCustomNode = new InvokeCustomNode(lambdaInfo, insn, false, isRange); + invokeCustomNode.setHandleType(methodHandleType); + + ClassInfo implCls = ClassInfo.fromType(root, lambdaInfo.getReturnType()); + String implName = (String) values.get(1).getValue(); + IMethodProto implProto = (IMethodProto) values.get(3).getValue(); + invokeCustomNode.setImplMthInfo(MethodInfo.fromMethodProto(root, implCls, implName, implProto)); + + MethodInfo callMthInfo = MethodInfo.fromRef(root, callMthHandle.getMethodRef()); + + InvokeType invokeType = convertInvokeType(methodHandleType); + int callArgsCount = callMthInfo.getArgsCount(); + InvokeNode callInsn = new InvokeNode(callMthInfo, invokeType, callArgsCount); + invokeCustomNode.setCallInsn(callInsn); + + // copy insn args + int argsCount = invokeCustomNode.getArgsCount(); + for (int i = 0; i < argsCount; i++) { + InsnArg arg = invokeCustomNode.getArg(i); + callInsn.addArg(arg.duplicate()); + } + if (callArgsCount > argsCount) { + // fill remaining args with NamedArg + for (int i = argsCount; i < callArgsCount; i++) { + ArgType argType = callMthInfo.getArgumentsTypes().get(i); + callInsn.addArg(new NamedArg("v" + i, argType)); + } + } + + MethodNode callMth = root.resolveMethod(callMthInfo); + if (callMth != null) { + callInsn.addAttr(callMth); + if (callMth.getAccessFlags().isSynthetic() + && callMth.getUseIn().size() <= 1 + && callMth.getParentClass().equals(mth.getParentClass())) { + // inline only synthetic methods from same class + callMth.add(AFlag.DONT_GENERATE); + invokeCustomNode.setInlineInsn(true); + } + } + // prevent args inlining into not generated invoke custom node + for (InsnArg arg : invokeCustomNode.getArguments()) { + arg.add(AFlag.DONT_INLINE); + } + return invokeCustomNode; + } catch (Exception e) { + throw new JadxRuntimeException("'invoke-custom' instruction processing error: " + e.getMessage(), e); + } + } + + /** + * Expect LambdaMetafactory.metafactory method + */ + private static boolean checkLinkerMethod(List values) { + if (values.size() < 6) { + return false; + } + IMethodHandle methodHandle = (IMethodHandle) values.get(0).getValue(); + if (methodHandle.getType() != MethodHandleType.INVOKE_STATIC) { + return false; + } + IMethodRef methodRef = methodHandle.getMethodRef(); + if (!methodRef.getName().equals("metafactory")) { + return false; + } + if (!methodRef.getParentClassType().equals("Ljava/lang/invoke/LambdaMetafactory;")) { + return false; + } + return true; + } + + private static InvokeType convertInvokeType(MethodHandleType type) { + switch (type) { + case INVOKE_STATIC: + return InvokeType.STATIC; + case INVOKE_INSTANCE: + return InvokeType.VIRTUAL; + case INVOKE_DIRECT: + return InvokeType.DIRECT; + case INVOKE_INTERFACE: + return InvokeType.INTERFACE; + + default: + throw new JadxRuntimeException("Unsupported method handle type: " + type); + } + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomNode.java new file mode 100644 index 000000000..40ab67839 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomNode.java @@ -0,0 +1,90 @@ +package jadx.core.dex.instructions; + +import org.jetbrains.annotations.Nullable; + +import jadx.api.plugins.input.data.MethodHandleType; +import jadx.api.plugins.input.insns.InsnData; +import jadx.core.dex.info.MethodInfo; +import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.nodes.InsnNode; +import jadx.core.utils.InsnUtils; + +public class InvokeCustomNode extends InvokeNode { + private MethodInfo implMthInfo; + private MethodHandleType handleType; + private InsnNode callInsn; + private boolean inlineInsn; + + public InvokeCustomNode(MethodInfo lambdaInfo, InsnData insn, boolean instanceCall, boolean isRange) { + super(lambdaInfo, insn, InvokeType.CUSTOM, instanceCall, isRange); + } + + public MethodInfo getImplMthInfo() { + return implMthInfo; + } + + public void setImplMthInfo(MethodInfo implMthInfo) { + this.implMthInfo = implMthInfo; + } + + public MethodHandleType getHandleType() { + return handleType; + } + + public void setHandleType(MethodHandleType handleType) { + this.handleType = handleType; + } + + public InsnNode getCallInsn() { + return callInsn; + } + + public void setCallInsn(InsnNode callInsn) { + this.callInsn = callInsn; + } + + public boolean isInlineInsn() { + return inlineInsn; + } + + public void setInlineInsn(boolean inlineInsn) { + this.inlineInsn = inlineInsn; + } + + @Nullable + public BaseInvokeNode getInvokeCall() { + if (callInsn.getType() == InsnType.INVOKE) { + return (BaseInvokeNode) callInsn; + } + return null; + } + + @Override + public @Nullable InsnArg getInstanceArg() { + return null; + } + + @Override + public boolean isStaticCall() { + return true; + } + + @Override + public int getFirstArgOffset() { + return 0; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(InsnUtils.formatOffset(offset)).append(": INVOKE_CUSTOM "); + if (getResult() != null) { + sb.append(getResult()).append(" = "); + } + appendArgs(sb); + sb.append("\n handle type: ").append(handleType); + sb.append("\n lambda: ").append(implMthInfo); + sb.append("\n call insn: ").append(callInsn); + return sb.toString(); + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java index 75b2f2aa1..b0061b271 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java @@ -8,18 +8,22 @@ import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.nodes.InsnNode; -public final class InvokeNode extends BaseInvokeNode { +public class InvokeNode extends BaseInvokeNode { private final InvokeType type; private final MethodInfo mth; - public InvokeNode(MethodInfo mth, InsnData insn, InvokeType type, boolean isRange) { - super(InsnType.INVOKE, mth.getArgsCount() + (type == InvokeType.STATIC ? 0 : 1)); + public InvokeNode(MethodInfo mthInfo, InsnData insn, InvokeType invokeType, boolean isRange) { + this(mthInfo, insn, invokeType, invokeType != InvokeType.STATIC, isRange); + } + + public InvokeNode(MethodInfo mth, InsnData insn, InvokeType type, boolean instanceCall, boolean isRange) { + super(InsnType.INVOKE, mth.getArgsCount() + (instanceCall ? 1 : 0)); this.mth = mth; this.type = type; int k = isRange ? insn.getReg(0) : 0; - if (type != InvokeType.STATIC) { + if (instanceCall) { int r = isRange ? k : insn.getReg(k); addReg(r, mth.getDeclClass().getType()); k++; diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeType.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeType.java index 44ac02514..5692ba019 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeType.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeType.java @@ -6,4 +6,6 @@ public enum InvokeType { VIRTUAL, INTERFACE, SUPER, + POLYMORPHIC, + CUSTOM, } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java index 7ae233c09..64b7e8834 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java @@ -815,6 +815,10 @@ public abstract class ArgType { return false; } + public boolean isVoid() { + return isPrimitive() && getPrimitiveType() == PrimitiveType.VOID; + } + /** * Recursively visit all subtypes of this type. * To exit return non-null value. diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java index abdd2e357..d0b63467a 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java @@ -139,7 +139,7 @@ public class ClassModifier extends AbstractVisitor { } private static void removeSyntheticMethods(MethodNode mth) { - if (mth.isNoCode()) { + if (mth.isNoCode() || mth.contains(AFlag.DONT_GENERATE)) { return; } AccessInfo af = mth.getAccessFlags(); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/InlineMethods.java b/jadx-core/src/main/java/jadx/core/dex/visitors/InlineMethods.java index 87d007227..9c00f5d92 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/InlineMethods.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/InlineMethods.java @@ -9,7 +9,6 @@ import org.slf4j.LoggerFactory; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.nodes.MethodInlineAttr; -import jadx.core.dex.info.MethodInfo; import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.InvokeNode; import jadx.core.dex.instructions.args.ArgType; @@ -49,11 +48,11 @@ public class InlineMethods extends AbstractVisitor { } private void processInvokeInsn(MethodNode mth, BlockNode block, InvokeNode insn) { - MethodInfo callMthInfo = insn.getCallMth(); - MethodNode callMth = mth.root().deepResolveMethod(callMthInfo); - if (callMth == null) { + IMethodDetails callMthDetails = insn.get(AType.METHOD_DETAILS); + if (!(callMthDetails instanceof MethodNode)) { return; } + MethodNode callMth = (MethodNode) callMthDetails; try { // TODO: sort inner classes process order by dependencies! MethodInlineAttr mia = MarkMethodsForInline.process(callMth); @@ -67,7 +66,7 @@ public class InlineMethods extends AbstractVisitor { } inlineMethod(mth, callMth, mia, block, insn); } catch (Exception e) { - throw new JadxRuntimeException("Failed to process method for inline: " + callMthInfo, e); + throw new JadxRuntimeException("Failed to process method for inline: " + callMth, e); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/shrink/CodeShrinkVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/shrink/CodeShrinkVisitor.java index 8b0d77814..c681087a6 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/shrink/CodeShrinkVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/shrink/CodeShrinkVisitor.java @@ -76,6 +76,9 @@ public class CodeShrinkVisitor extends AbstractVisitor { private static void checkInline(MethodNode mth, BlockNode block, InsnList insnList, List wrapList, ArgsInfo argsInfo, RegisterArg arg) { + if (arg.contains(AFlag.DONT_INLINE)) { + return; + } SSAVar sVar = arg.getSVar(); if (sVar == null || sVar.getAssign().contains(AFlag.DONT_INLINE)) { return; diff --git a/jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaStatic.java b/jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaStatic.java new file mode 100644 index 000000000..e3e739301 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaStatic.java @@ -0,0 +1,78 @@ +package jadx.tests.integration.java8; + +import java.util.concurrent.Callable; +import java.util.function.Function; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestLambdaStatic extends IntegrationTest { + + public static class TestCls { + public Callable test1() { + return () -> "test"; + } + + public Callable test2(String str) { + return () -> str; + } + + public Function test3(String a) { + return (b) -> Integer.parseInt(a) - Integer.parseInt(b); + } + + public Function test4() { + return Integer::parseInt; + } + + @SuppressWarnings("Convert2MethodRef") + public Function test4a() { + return s -> Integer.parseInt(s); + } + + public Function test5() { + String str = Integer.toString(3); + return (s) -> Integer.parseInt(str) - Integer.parseInt(s); + } + + public void check() throws Exception { + assertThat(test1().call()).isEqualTo("test"); + assertThat(test2("a").call()).isEqualTo("a"); + assertThat(test3("3").apply("1")).isEqualTo(2); + assertThat(test4().apply("5")).isEqualTo(5); + assertThat(test4a().apply("7")).isEqualTo(7); + assertThat(test5().apply("1")).isEqualTo(2); + } + } + + @Test + public void test() { + assertThat(getClassNode(TestCls.class)) + .code() + .doesNotContain("lambda$") + .doesNotContain("renamed") + .containsLines(2, + "return () -> {", + indent() + "return \"test\";", + "};") + .containsLines(2, + "return () -> {", + indent() + "return str;", + "};"); + } + + @Test + public void testNoDebug() { + noDebugInfo(); + getClassNode(TestCls.class); + } + + @Test + public void testFallback() { + setFallback(); + getClassNode(TestCls.class); + } +} diff --git a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexInsnData.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexInsnData.java index 3e13633c6..52d231932 100644 --- a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexInsnData.java +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexInsnData.java @@ -2,6 +2,7 @@ package jadx.plugins.input.dex.insns; import org.jetbrains.annotations.Nullable; +import jadx.api.plugins.input.data.ICallSite; import jadx.api.plugins.input.data.IFieldData; import jadx.api.plugins.input.data.IMethodRef; import jadx.api.plugins.input.insns.InsnData; @@ -14,6 +15,7 @@ import jadx.plugins.input.dex.sections.SectionReader; public class DexInsnData implements InsnData { private final DexCodeReader codeData; private final SectionReader externalReader; + private final SectionReader secondExtReader; private DexInsnInfo insnInfo; private boolean decoded; @@ -32,6 +34,7 @@ public class DexInsnData implements InsnData { public DexInsnData(DexCodeReader codeData, SectionReader externalReader) { this.codeData = codeData; this.externalReader = externalReader; + this.secondExtReader = externalReader.copy(); } @Override @@ -110,6 +113,11 @@ public class DexInsnData implements InsnData { return externalReader.getMethodRef(index); } + @Override + public ICallSite getIndexAsCallSite() { + return externalReader.getCallSite(index, secondExtReader); + } + @Nullable @Override public ICustomPayload getPayload() { diff --git a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexCallSite.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexCallSite.java new file mode 100644 index 000000000..8d189a7f4 --- /dev/null +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexCallSite.java @@ -0,0 +1,39 @@ +package jadx.plugins.input.dex.sections; + +import java.util.List; + +import jadx.api.plugins.input.data.ICallSite; +import jadx.api.plugins.input.data.IMethodHandle; +import jadx.api.plugins.input.data.IMethodRef; +import jadx.api.plugins.input.data.annotations.EncodedValue; + +public class DexCallSite implements ICallSite { + + private final List values; + + public DexCallSite(List values) { + this.values = values; + } + + @Override + public List getValues() { + return values; + } + + @Override + public void load() { + for (EncodedValue value : values) { + Object obj = value.getValue(); + if (obj instanceof IMethodRef) { + ((IMethodRef) obj).load(); + } else if (obj instanceof IMethodHandle) { + ((IMethodHandle) obj).load(); + } + } + } + + @Override + public String toString() { + return "CallSite{" + values + '}'; + } +} diff --git a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexClassData.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexClassData.java index c02a7f5e0..93b90926d 100644 --- a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexClassData.java +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexClassData.java @@ -1,6 +1,5 @@ package jadx.plugins.input.dex.sections; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -175,12 +174,7 @@ public class DexClassData implements IClassData { return Collections.emptyList(); } in.absPos(staticValuesOff); - int count = in.readUleb128(); - List list = new ArrayList<>(count); - for (int i = 0; i < count; i++) { - list.add(annotationsParser.parseEncodedValue(in)); - } - return list; + return annotationsParser.parseEncodedArray(in); } @Override diff --git a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexHeader.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexHeader.java index 3dc24259d..5eb26e9eb 100644 --- a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexHeader.java +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexHeader.java @@ -16,6 +16,9 @@ public class DexHeader { private final int methodIdsOff; private final int methodIdsSize; + private int callSiteOff; + private int methodHandleOff; + public DexHeader(SectionReader buf) { byte[] magic = buf.readByteArray(4); version = buf.readString(3); @@ -45,6 +48,28 @@ public class DexHeader { classDefsOff = buf.readInt(); int dataSize = buf.readInt(); int dataOff = buf.readInt(); + + readMapList(buf, mapListOff); + } + + private void readMapList(SectionReader buf, int mapListOff) { + buf.absPos(mapListOff); + int size = buf.readInt(); + for (int i = 0; i < size; i++) { + int type = buf.readUShort(); + buf.skip(6); + int offset = buf.readInt(); + + switch (type) { + case 0x0007: + callSiteOff = offset; + break; + + case 0x0008: + methodHandleOff = offset; + break; + } + } } public String getVersion() { @@ -94,4 +119,12 @@ public class DexHeader { public int getMethodIdsSize() { return methodIdsSize; } + + public int getCallSiteOff() { + return callSiteOff; + } + + public int getMethodHandleOff() { + return methodHandleOff; + } } diff --git a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexMethodProto.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexMethodProto.java new file mode 100644 index 000000000..7a2ad9ba6 --- /dev/null +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexMethodProto.java @@ -0,0 +1,31 @@ +package jadx.plugins.input.dex.sections; + +import java.util.List; + +import jadx.api.plugins.input.data.IMethodProto; +import jadx.plugins.input.dex.utils.Utils; + +public class DexMethodProto implements IMethodProto { + private final List argTypes; + private final String returnType; + + public DexMethodProto(List argTypes, String returnType) { + this.returnType = returnType; + this.argTypes = argTypes; + } + + @Override + public List getArgTypes() { + return argTypes; + } + + @Override + public String getReturnType() { + return returnType; + } + + @Override + public String toString() { + return "(" + Utils.listToStr(argTypes) + ")" + returnType; + } +} diff --git a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/SectionReader.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/SectionReader.java index 093efbcd4..6fe3bcebf 100644 --- a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/SectionReader.java +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/SectionReader.java @@ -9,8 +9,14 @@ import java.util.List; import org.jetbrains.annotations.Nullable; +import jadx.api.plugins.input.data.ICallSite; import jadx.api.plugins.input.data.IFieldData; +import jadx.api.plugins.input.data.IMethodHandle; +import jadx.api.plugins.input.data.MethodHandleType; +import jadx.api.plugins.input.data.impl.FieldRefHandle; +import jadx.api.plugins.input.data.impl.MethodRefHandle; import jadx.plugins.input.dex.DexReader; +import jadx.plugins.input.dex.sections.annotations.EncodedValueParser; import jadx.plugins.input.dex.utils.Leb128; import jadx.plugins.input.dex.utils.MUtf8; @@ -119,6 +125,13 @@ public class SectionReader { return new String(readByteArray(len), StandardCharsets.US_ASCII); } + private List readTypeListAt(int paramsOff) { + if (paramsOff == 0) { + return Collections.emptyList(); + } + return absPos(paramsOff).readTypeList(); + } + public List readTypeList() { int size = readInt(); if (size == 0) { @@ -180,6 +193,50 @@ public class SectionReader { return methodRef; } + public ICallSite getCallSite(int idx, SectionReader ext) { + int callSiteOff = dexReader.getHeader().getCallSiteOff(); + absPos(callSiteOff + idx * 4); + absPos(readInt()); + return new DexCallSite(EncodedValueParser.parseEncodedArray(this, ext)); + } + + public IMethodHandle getMethodHandle(int idx) { + int methodHandleOff = dexReader.getHeader().getMethodHandleOff(); + absPos(methodHandleOff + idx * 8); + MethodHandleType handleType = getMethodHandleType(readUShort()); + skip(2); + int refId = readUShort(); + if (handleType.isField()) { + return new FieldRefHandle(handleType, getFieldData(refId)); + } + return new MethodRefHandle(handleType, getMethodRef(refId)); + } + + private MethodHandleType getMethodHandleType(int type) { + switch (type) { + case 0x00: + return MethodHandleType.STATIC_PUT; + case 0x01: + return MethodHandleType.STATIC_GET; + case 0x02: + return MethodHandleType.INSTANCE_PUT; + case 0x03: + return MethodHandleType.INSTANCE_GET; + case 0x04: + return MethodHandleType.INVOKE_STATIC; + case 0x05: + return MethodHandleType.INVOKE_INSTANCE; + case 0x06: + return MethodHandleType.INVOKE_CONSTRUCTOR; + case 0x07: + return MethodHandleType.INVOKE_DIRECT; + case 0x08: + return MethodHandleType.INVOKE_INTERFACE; + default: + throw new IllegalArgumentException("Unknown method handle type: 0x" + Integer.toHexString(type)); + } + } + public void initMethodRef(int idx, DexMethodRef methodRef) { methodRef.initUniqId(dexReader, idx); methodRef.setDexIdx(idx); @@ -200,18 +257,22 @@ public class SectionReader { int returnTypeIdx = readInt(); int paramsOff = readInt(); - List argTypes; - if (paramsOff == 0) { - argTypes = Collections.emptyList(); - } else { - argTypes = absPos(paramsOff).readTypeList(); - } + List argTypes = readTypeListAt(paramsOff); methodRef.setParentClassType(getType(classTypeIdx)); methodRef.setName(getString(nameIdx)); methodRef.setReturnType(getType(returnTypeIdx)); methodRef.setArgTypes(argTypes); } + public DexMethodProto getMethodProto(int idx) { + int protoIdsOff = dexReader.getHeader().getProtoIdsOff(); + absPos(protoIdsOff + idx * 12); + skip(4); // shortyIdx + int returnTypeIdx = readInt(); + int paramsOff = readInt(); + return new DexMethodProto(readTypeListAt(paramsOff), getType(returnTypeIdx)); + } + public List getMethodParamTypes(int idx) { DexHeader header = dexReader.getHeader(); int methodIdsOff = header.getMethodIdsOff(); diff --git a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/annotations/AnnotationsParser.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/annotations/AnnotationsParser.java index d723e2c0e..18106b2d1 100644 --- a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/annotations/AnnotationsParser.java +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/annotations/AnnotationsParser.java @@ -168,4 +168,8 @@ public class AnnotationsParser { public EncodedValue parseEncodedValue(SectionReader in) { return EncodedValueParser.parseValue(in, ext); } + + public List parseEncodedArray(SectionReader in) { + return EncodedValueParser.parseEncodedArray(in, ext); + } } diff --git a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/annotations/EncodedValueParser.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/annotations/EncodedValueParser.java index 014f63179..82704e9cf 100644 --- a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/annotations/EncodedValueParser.java +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/annotations/EncodedValueParser.java @@ -17,6 +17,8 @@ public class EncodedValueParser { private static final int ENCODED_LONG = 0x06; private static final int ENCODED_FLOAT = 0x10; private static final int ENCODED_DOUBLE = 0x11; + private static final int ENCODED_METHOD_TYPE = 0x15; + private static final int ENCODED_METHOD_HANDLE = 0x16; private static final int ENCODED_STRING = 0x17; private static final int ENCODED_TYPE = 0x18; private static final int ENCODED_FIELD = 0x19; @@ -62,29 +64,39 @@ public class EncodedValueParser { case ENCODED_TYPE: return new EncodedValue(EncodedType.ENCODED_TYPE, ext.getType(parseUnsignedInt(in, size))); - case ENCODED_METHOD: - return new EncodedValue(EncodedType.ENCODED_METHOD, ext.getMethodRef(parseUnsignedInt(in, size))); - case ENCODED_FIELD: case ENCODED_ENUM: return new EncodedValue(EncodedType.ENCODED_FIELD, ext.getFieldData(parseUnsignedInt(in, size))); case ENCODED_ARRAY: - int count = in.readUleb128(); - List values = new ArrayList<>(count); - for (int i = 0; i < count; i++) { - values.add(parseValue(in, ext)); - } - return new EncodedValue(EncodedType.ENCODED_ARRAY, values); + return new EncodedValue(EncodedType.ENCODED_ARRAY, parseEncodedArray(in, ext)); case ENCODED_ANNOTATION: return new EncodedValue(EncodedType.ENCODED_ANNOTATION, AnnotationsParser.readAnnotation(in, ext, false)); + case ENCODED_METHOD: + return new EncodedValue(EncodedType.ENCODED_METHOD, ext.getMethodRef(parseUnsignedInt(in, size))); + + case ENCODED_METHOD_TYPE: + return new EncodedValue(EncodedType.ENCODED_METHOD_TYPE, ext.getMethodProto(parseUnsignedInt(in, size))); + + case ENCODED_METHOD_HANDLE: + return new EncodedValue(EncodedType.ENCODED_METHOD_HANDLE, ext.getMethodHandle(parseUnsignedInt(in, size))); + default: throw new DexException("Unknown encoded value type: 0x" + Integer.toHexString(type)); } } + public static List parseEncodedArray(SectionReader in, SectionReader ext) { + int count = in.readUleb128(); + List values = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + values.add(parseValue(in, ext)); + } + return values; + } + private static int parseUnsignedInt(SectionReader in, int byteCount) { return (int) parseNumber(in, byteCount, false, 0); } diff --git a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/utils/Utils.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/utils/Utils.java index fc04b24d2..216e0d94a 100644 --- a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/utils/Utils.java +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/utils/Utils.java @@ -9,6 +9,9 @@ public class Utils { if (collection == null) { return "null"; } + if (collection.isEmpty()) { + return ""; + } StringBuilder sb = new StringBuilder(); Iterator it = collection.iterator(); if (it.hasNext()) { diff --git a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/ICallSite.java b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/ICallSite.java new file mode 100644 index 000000000..da2d1b6b4 --- /dev/null +++ b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/ICallSite.java @@ -0,0 +1,12 @@ +package jadx.api.plugins.input.data; + +import java.util.List; + +import jadx.api.plugins.input.data.annotations.EncodedValue; + +public interface ICallSite { + + List getValues(); + + void load(); +} diff --git a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/IMethodHandle.java b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/IMethodHandle.java new file mode 100644 index 000000000..9c936e95e --- /dev/null +++ b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/IMethodHandle.java @@ -0,0 +1,12 @@ +package jadx.api.plugins.input.data; + +public interface IMethodHandle { + + MethodHandleType getType(); + + IFieldData getFieldRef(); + + IMethodRef getMethodRef(); + + void load(); +} diff --git a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/IMethodProto.java b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/IMethodProto.java new file mode 100644 index 000000000..f936c3a6c --- /dev/null +++ b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/IMethodProto.java @@ -0,0 +1,10 @@ +package jadx.api.plugins.input.data; + +import java.util.List; + +public interface IMethodProto { + + String getReturnType(); + + List getArgTypes(); +} diff --git a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/MethodHandleType.java b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/MethodHandleType.java new file mode 100644 index 000000000..3248b6fa8 --- /dev/null +++ b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/MethodHandleType.java @@ -0,0 +1,26 @@ +package jadx.api.plugins.input.data; + +public enum MethodHandleType { + STATIC_PUT, + STATIC_GET, + INSTANCE_PUT, + INSTANCE_GET, + INVOKE_STATIC, + INVOKE_INSTANCE, + INVOKE_DIRECT, + INVOKE_CONSTRUCTOR, + INVOKE_INTERFACE; + + public boolean isField() { + switch (this) { + case STATIC_PUT: + case STATIC_GET: + case INSTANCE_PUT: + case INSTANCE_GET: + return true; + + default: + return false; + } + } +} diff --git a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/annotations/EncodedType.java b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/annotations/EncodedType.java index 0a773a682..0c1f21c48 100644 --- a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/annotations/EncodedType.java +++ b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/annotations/EncodedType.java @@ -15,6 +15,8 @@ public enum EncodedType { ENCODED_ENUM, ENCODED_FIELD, ENCODED_METHOD, + ENCODED_METHOD_TYPE, + ENCODED_METHOD_HANDLE, ENCODED_ARRAY, ENCODED_ANNOTATION } diff --git a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/impl/FieldRefHandle.java b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/impl/FieldRefHandle.java new file mode 100644 index 000000000..fa16eb636 --- /dev/null +++ b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/impl/FieldRefHandle.java @@ -0,0 +1,37 @@ +package jadx.api.plugins.input.data.impl; + +import jadx.api.plugins.input.data.IFieldData; +import jadx.api.plugins.input.data.IMethodHandle; +import jadx.api.plugins.input.data.IMethodRef; +import jadx.api.plugins.input.data.MethodHandleType; + +public class FieldRefHandle implements IMethodHandle { + + private final IFieldData fieldRef; + private final MethodHandleType type; + + public FieldRefHandle(MethodHandleType type, IFieldData fieldRef) { + this.fieldRef = fieldRef; + this.type = type; + } + + @Override + public MethodHandleType getType() { + return type; + } + + @Override + public IFieldData getFieldRef() { + return fieldRef; + } + + @Override + public IMethodRef getMethodRef() { + return null; + } + + @Override + public void load() { + // already loaded + } +} diff --git a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/impl/MethodRefHandle.java b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/impl/MethodRefHandle.java new file mode 100644 index 000000000..5b3cebe7c --- /dev/null +++ b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/impl/MethodRefHandle.java @@ -0,0 +1,42 @@ +package jadx.api.plugins.input.data.impl; + +import jadx.api.plugins.input.data.IFieldData; +import jadx.api.plugins.input.data.IMethodHandle; +import jadx.api.plugins.input.data.IMethodRef; +import jadx.api.plugins.input.data.MethodHandleType; + +public class MethodRefHandle implements IMethodHandle { + + private final IMethodRef methodRef; + private final MethodHandleType type; + + public MethodRefHandle(MethodHandleType type, IMethodRef methodRef) { + this.methodRef = methodRef; + this.type = type; + } + + @Override + public MethodHandleType getType() { + return type; + } + + @Override + public IMethodRef getMethodRef() { + return methodRef; + } + + @Override + public IFieldData getFieldRef() { + return null; + } + + @Override + public void load() { + methodRef.load(); + } + + @Override + public String toString() { + return type + ": " + methodRef; + } +} diff --git a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/insns/InsnData.java b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/insns/InsnData.java index 4d50fa488..20fe106a8 100644 --- a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/insns/InsnData.java +++ b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/insns/InsnData.java @@ -2,6 +2,7 @@ package jadx.api.plugins.input.insns; import org.jetbrains.annotations.Nullable; +import jadx.api.plugins.input.data.ICallSite; import jadx.api.plugins.input.data.IFieldData; import jadx.api.plugins.input.data.IMethodRef; import jadx.api.plugins.input.insns.custom.ICustomPayload; @@ -36,6 +37,8 @@ public interface InsnData { IMethodRef getIndexAsMethod(); + ICallSite getIndexAsCallSite(); + @Nullable ICustomPayload getPayload(); }