提交 69574918 编写于 作者: S Skylot

fix: allow constructor invoke as lambda

上级 f6783e8f
......@@ -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,21 +62,56 @@ public class InvokeCustomBuilder {
invokeCustomNode.setImplMthInfo(implMthInfo);
MethodInfo callMthInfo = MethodInfo.fromRef(root, callMthHandle.getMethodRef());
InvokeNode invokeNode = buildInvokeNode(methodHandleType, invokeCustomNode, callMthInfo);
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) {
invokeCustomNode.getCallInsn().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);
}
}
if (!invokeCustomNode.isInlineInsn()) {
IMethodProto effectiveMthProto = (IMethodProto) values.get(5).getValue();
List<ArgType> args = Utils.collectionMap(effectiveMthProto.getArgTypes(), ArgType::parse);
boolean sameArgs = args.equals(callMthInfo.getArgumentsTypes());
invokeCustomNode.setUseRef(sameArgs);
}
// prevent args inlining into not generated invoke custom node
for (InsnArg arg : invokeCustomNode.getArguments()) {
arg.add(AFlag.DONT_INLINE);
}
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 callInsn = new InvokeNode(callMthInfo, invokeType, callArgsCount);
invokeCustomNode.setCallInsn(callInsn);
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);
callInsn.addArg(arg.duplicate());
invokeNode.addArg(arg.duplicate());
}
if (callArgsCount > argsCount) {
// fill remaining args with NamedArg
......@@ -92,33 +128,10 @@ public class InvokeCustomBuilder {
} else {
argType = callArgTypes.get(callArgNum++);
}
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);
invokeNode.addArg(new NamedArg("v" + i, argType));
}
}
if (!invokeCustomNode.isInlineInsn()) {
IMethodProto effectiveMthProto = (IMethodProto) values.get(5).getValue();
List<ArgType> args = Utils.collectionMap(effectiveMthProto.getArgTypes(), ArgType::parse);
boolean sameArgs = args.equals(callMthInfo.getArgumentsTypes());
invokeCustomNode.setUseRef(sameArgs);
}
// prevent args inlining into not generated invoke custom node
for (InsnArg arg : invokeCustomNode.getArguments()) {
arg.add(AFlag.DONT_INLINE);
}
return invokeCustomNode;
return invokeNode;
}
/**
......@@ -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,31 +27,25 @@ 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);
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
callType = CallType.SELF;
} else {
callType = CallType.THIS;
return CallType.SELF;
}
} else {
callType = CallType.SUPER;
return CallType.THIS;
}
} else {
callType = CallType.CONSTRUCTOR;
setResult(instanceArg);
// convert from 'use' to 'assign'
instanceArg.getSVar().setAssign(instanceArg);
}
instanceArg.getSVar().removeUse(instanceArg);
int argsCount = invoke.getArgsCount();
for (int i = 1; i < argsCount; i++) {
addArg(invoke.getArg(i));
return CallType.SUPER;
}
return CallType.CONSTRUCTOR;
}
public ConstructorInsn(MethodInfo callMth, CallType callType) {
......
......@@ -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())) {
......
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");
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册