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 27c0572bd..770a41690 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -792,14 +792,23 @@ public class InsnGen { } private void makeRefLambda(CodeWriter code, InvokeCustomNode customNode) { - InvokeNode invokeInsn = (InvokeNode) customNode.getCallInsn(); - MethodInfo callMth = invokeInsn.getCallMth(); - if (customNode.getHandleType() == MethodHandleType.INVOKE_STATIC) { + InsnNode callInsn = customNode.getCallInsn(); + if (callInsn instanceof ConstructorInsn) { + MethodInfo callMth = ((ConstructorInsn) callInsn).getCallMth(); useClass(code, callMth.getDeclClass()); - } else { - code.add("this"); + code.add("::new"); + return; + } + if (callInsn instanceof InvokeNode) { + InvokeNode invokeInsn = (InvokeNode) callInsn; + MethodInfo callMth = invokeInsn.getCallMth(); + if (customNode.getHandleType() == MethodHandleType.INVOKE_STATIC) { + useClass(code, callMth.getDeclClass()); + } else { + code.add("this"); + } + code.add("::").add(callMth.getAlias()); } - code.add("::").add(callMth.getAlias()); } private void makeSimpleLambda(CodeWriter code, InvokeCustomNode customNode) { 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 index e7e0d61a6..a8f95eee0 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomBuilder.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomBuilder.java @@ -17,6 +17,7 @@ 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.instructions.mods.ConstructorInsn; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; @@ -61,44 +62,18 @@ public class InvokeCustomBuilder { invokeCustomNode.setImplMthInfo(implMthInfo); MethodInfo callMthInfo = MethodInfo.fromRef(root, callMthHandle.getMethodRef()); + InvokeNode invokeNode = buildInvokeNode(methodHandleType, invokeCustomNode, callMthInfo); - InvokeType invokeType = convertInvokeType(methodHandleType); - int callArgsCount = callMthInfo.getArgsCount(); - boolean instanceCall = invokeType != InvokeType.STATIC; - if (instanceCall) { - callArgsCount++; - } - 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 - int callArgNum = argsCount; - if (instanceCall) { - callArgNum--; // start from instance type - } - List callArgTypes = callMthInfo.getArgumentsTypes(); - for (int i = argsCount; i < callArgsCount; i++) { - ArgType argType; - if (callArgNum < 0) { - // instance arg type - argType = callMthInfo.getDeclClass().getType(); - } else { - argType = callArgTypes.get(callArgNum++); - } - callInsn.addArg(new NamedArg("v" + i, argType)); - } + if (methodHandleType == MethodHandleType.INVOKE_CONSTRUCTOR) { + ConstructorInsn ctrInsn = new ConstructorInsn(mth, invokeNode); + invokeCustomNode.setCallInsn(ctrInsn); + } else { + invokeCustomNode.setCallInsn(invokeNode); } MethodNode callMth = root.resolveMethod(callMthInfo); if (callMth != null) { - callInsn.addAttr(callMth); + invokeCustomNode.getCallInsn().addAttr(callMth); if (callMth.getAccessFlags().isSynthetic() && callMth.getUseIn().size() <= 1 && callMth.getParentClass().equals(mth.getParentClass())) { @@ -121,6 +96,44 @@ public class InvokeCustomBuilder { return invokeCustomNode; } + @NotNull + private static InvokeNode buildInvokeNode(MethodHandleType methodHandleType, InvokeCustomNode invokeCustomNode, + MethodInfo callMthInfo) { + InvokeType invokeType = convertInvokeType(methodHandleType); + int callArgsCount = callMthInfo.getArgsCount(); + boolean instanceCall = invokeType != InvokeType.STATIC; + if (instanceCall) { + callArgsCount++; + } + InvokeNode invokeNode = new InvokeNode(callMthInfo, invokeType, callArgsCount); + + // copy insn args + int argsCount = invokeCustomNode.getArgsCount(); + for (int i = 0; i < argsCount; i++) { + InsnArg arg = invokeCustomNode.getArg(i); + invokeNode.addArg(arg.duplicate()); + } + if (callArgsCount > argsCount) { + // fill remaining args with NamedArg + int callArgNum = argsCount; + if (instanceCall) { + callArgNum--; // start from instance type + } + List callArgTypes = callMthInfo.getArgumentsTypes(); + for (int i = argsCount; i < callArgsCount; i++) { + ArgType argType; + if (callArgNum < 0) { + // instance arg type + argType = callMthInfo.getDeclClass().getType(); + } else { + argType = callArgTypes.get(callArgNum++); + } + invokeNode.addArg(new NamedArg("v" + i, argType)); + } + } + return invokeNode; + } + /** * Expect LambdaMetafactory.metafactory method */ @@ -149,6 +162,7 @@ public class InvokeCustomBuilder { case INVOKE_INSTANCE: return InvokeType.VIRTUAL; case INVOKE_DIRECT: + case INVOKE_CONSTRUCTOR: return InvokeType.DIRECT; case INVOKE_INTERFACE: return InvokeType.INTERFACE; diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java index 7a900d340..602bac75e 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java @@ -7,6 +7,7 @@ import jadx.core.dex.info.MethodInfo; import jadx.core.dex.instructions.BaseInvokeNode; import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.InvokeNode; +import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; @@ -26,33 +27,27 @@ public final class ConstructorInsn extends BaseInvokeNode { public ConstructorInsn(MethodNode mth, InvokeNode invoke) { super(InsnType.CONSTRUCTOR, invoke.getArgsCount() - 1); this.callMth = invoke.getCallMth(); - ClassInfo classType = callMth.getDeclClass(); - RegisterArg instanceArg = (RegisterArg) invoke.getArg(0); - - if (instanceArg.isThis()) { - if (classType.equals(mth.getParentClass().getClassInfo())) { - if (callMth.getShortId().equals(mth.getMethodInfo().getShortId())) { - // self constructor - callType = CallType.SELF; - } else { - callType = CallType.THIS; - } - } else { - callType = CallType.SUPER; - } - } else { - callType = CallType.CONSTRUCTOR; - setResult(instanceArg); - // convert from 'use' to 'assign' - instanceArg.getSVar().setAssign(instanceArg); - } - instanceArg.getSVar().removeUse(instanceArg); + this.callType = getCallType(mth, callMth.getDeclClass(), invoke.getArg(0)); int argsCount = invoke.getArgsCount(); for (int i = 1; i < argsCount; i++) { addArg(invoke.getArg(i)); } } + private CallType getCallType(MethodNode mth, ClassInfo classType, InsnArg instanceArg) { + if (instanceArg.isThis()) { + if (classType.equals(mth.getParentClass().getClassInfo())) { + if (callMth.getShortId().equals(mth.getMethodInfo().getShortId())) { + // self constructor + return CallType.SELF; + } + return CallType.THIS; + } + return CallType.SUPER; + } + return CallType.CONSTRUCTOR; + } + public ConstructorInsn(MethodInfo callMth, CallType callType) { super(InsnType.CONSTRUCTOR, callMth.getArgsCount()); this.callMth = callMth; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java index 388ca08a8..0c0fa6ca0 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java @@ -60,8 +60,16 @@ public class ConstructorVisitor extends AbstractVisitor { if (!callMth.isConstructor()) { return; } - InsnNode instArgAssignInsn = ((RegisterArg) inv.getArg(0)).getAssignInsn(); + RegisterArg instanceArg = ((RegisterArg) inv.getArg(0)); + InsnNode instArgAssignInsn = instanceArg.getAssignInsn(); ConstructorInsn co = new ConstructorInsn(mth, inv); + if (co.isNewInstance()) { + co.setResult(instanceArg); + // convert from 'use' to 'assign' + instanceArg.getSVar().setAssign(instanceArg); + } + instanceArg.getSVar().removeUse(instanceArg); + co.rebindArgs(); boolean remove = false; if (co.isSuper() && (co.getArgsCount() == 0 || parentClass.isEnum())) { diff --git a/jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaConstructor.java b/jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaConstructor.java new file mode 100644 index 000000000..0a7b7b0ce --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaConstructor.java @@ -0,0 +1,38 @@ +package jadx.tests.integration.java8; + +import java.util.function.Supplier; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestLambdaConstructor extends IntegrationTest { + + public static class TestCls { + + public Supplier test() { + return RuntimeException::new; + } + + public void check() throws Exception { + assertThat(test().get()).isInstanceOf(RuntimeException.class); + } + } + + @Test + public void test() { + assertThat(getClassNode(TestCls.class)) + .code() + .containsOne("return RuntimeException::new;"); + } + + @Test + public void testFallback() { + setFallback(); + assertThat(getClassNode(TestCls.class)) + .code() + .containsOne("RuntimeException::new"); + } +}