fix: allow constructor invoke as lambda
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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<ArgType> 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<ArgType> 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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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())) {
|
||||
|
||||
@@ -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<Exception> 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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user