fix: allow constructor invoke as lambda

This commit is contained in:
Skylot
2021-02-02 18:27:36 +00:00
parent f6783e8f5e
commit 69574918b5
5 changed files with 125 additions and 61 deletions
@@ -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");
}
}