-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: output unknown
invoke-custom
as polymorphic call (#1760)
- Loading branch information
Showing
14 changed files
with
541 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 98 additions & 0 deletions
98
jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomRawNode.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package jadx.core.dex.instructions; | ||
|
||
import java.util.List; | ||
|
||
import org.jetbrains.annotations.Nullable; | ||
|
||
import jadx.api.plugins.input.data.annotations.EncodedValue; | ||
import jadx.api.plugins.input.insns.InsnData; | ||
import jadx.core.dex.info.MethodInfo; | ||
import jadx.core.dex.instructions.args.InsnArg; | ||
import jadx.core.dex.instructions.invokedynamic.CustomRawCall; | ||
import jadx.core.dex.nodes.InsnNode; | ||
import jadx.core.utils.InsnUtils; | ||
import jadx.core.utils.Utils; | ||
|
||
/** | ||
* Information for raw invoke-custom instruction.<br> | ||
* Output will be formatted as polymorphic call with equivalent semantic | ||
* Contains two parts: | ||
* - resolve: treated as additional invoke insn (uses only constant args) | ||
* - invoke: call of resolved method (base for this invoke) | ||
* <br> | ||
* See {@link CustomRawCall} class for build details | ||
*/ | ||
public class InvokeCustomRawNode extends InvokeNode { | ||
private final InvokeNode resolve; | ||
private List<EncodedValue> callSiteValues; | ||
|
||
public InvokeCustomRawNode(InvokeNode resolve, MethodInfo mthInfo, InsnData insn, boolean isRange) { | ||
super(mthInfo, insn, InvokeType.CUSTOM_RAW, false, isRange); | ||
this.resolve = resolve; | ||
} | ||
|
||
public InvokeCustomRawNode(InvokeNode resolve, MethodInfo mthInfo, InvokeType invokeType, int argsCount) { | ||
super(mthInfo, invokeType, argsCount); | ||
this.resolve = resolve; | ||
} | ||
|
||
public InvokeNode getResolveInvoke() { | ||
return resolve; | ||
} | ||
|
||
public void setCallSiteValues(List<EncodedValue> callSiteValues) { | ||
this.callSiteValues = callSiteValues; | ||
} | ||
|
||
public List<EncodedValue> getCallSiteValues() { | ||
return callSiteValues; | ||
} | ||
|
||
@Override | ||
public InsnNode copy() { | ||
InvokeCustomRawNode copy = new InvokeCustomRawNode(resolve, getCallMth(), getInvokeType(), getArgsCount()); | ||
copyCommonParams(copy); | ||
copy.setCallSiteValues(callSiteValues); | ||
return copy; | ||
} | ||
|
||
@Override | ||
public boolean isStaticCall() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public int getFirstArgOffset() { | ||
return 0; | ||
} | ||
|
||
@Override | ||
public @Nullable InsnArg getInstanceArg() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public boolean isSame(InsnNode obj) { | ||
if (this == obj) { | ||
return true; | ||
} | ||
if (obj instanceof InvokeCustomRawNode) { | ||
return super.isSame(obj) && resolve.isSame(((InvokeCustomRawNode) obj).resolve); | ||
} | ||
return false; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
StringBuilder sb = new StringBuilder(); | ||
sb.append(InsnUtils.formatOffset(offset)).append(": INVOKE_CUSTOM "); | ||
if (getResult() != null) { | ||
sb.append(getResult()).append(" = "); | ||
} | ||
if (!appendArgs(sb)) { | ||
sb.append('\n'); | ||
} | ||
sb.append(" call-site: \n ").append(Utils.listToString(callSiteValues, "\n ")).append('\n'); | ||
return sb.toString(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,4 +8,5 @@ public enum InvokeType { | |
SUPER, | ||
POLYMORPHIC, | ||
CUSTOM, | ||
CUSTOM_RAW, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
jadx-core/src/main/java/jadx/core/dex/instructions/invokedynamic/CustomRawCall.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package jadx.core.dex.instructions.invokedynamic; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import jadx.api.plugins.input.data.IMethodHandle; | ||
import jadx.api.plugins.input.data.IMethodProto; | ||
import jadx.api.plugins.input.data.annotations.EncodedValue; | ||
import jadx.api.plugins.input.insns.InsnData; | ||
import jadx.core.dex.info.ClassInfo; | ||
import jadx.core.dex.info.MethodInfo; | ||
import jadx.core.dex.instructions.ConstStringNode; | ||
import jadx.core.dex.instructions.InvokeCustomRawNode; | ||
import jadx.core.dex.instructions.InvokeNode; | ||
import jadx.core.dex.instructions.InvokeType; | ||
import jadx.core.dex.instructions.args.ArgType; | ||
import jadx.core.dex.instructions.args.InsnArg; | ||
import jadx.core.dex.nodes.InsnNode; | ||
import jadx.core.dex.nodes.MethodNode; | ||
import jadx.core.dex.nodes.RootNode; | ||
import jadx.core.utils.exceptions.JadxRuntimeException; | ||
|
||
import static jadx.core.utils.EncodedValueUtils.buildLookupArg; | ||
import static jadx.core.utils.EncodedValueUtils.convertToInsnArg; | ||
|
||
/** | ||
* Show `invoke-custom` similar to polymorphic call | ||
*/ | ||
public class CustomRawCall { | ||
|
||
public static InsnNode build(MethodNode mth, InsnData insn, boolean isRange, List<EncodedValue> values) { | ||
IMethodHandle resolveHandle = (IMethodHandle) values.get(0).getValue(); | ||
String invokeName = (String) values.get(1).getValue(); | ||
IMethodProto invokeProto = (IMethodProto) values.get(2).getValue(); | ||
List<InsnArg> resolveArgs = buildArgs(mth, values); | ||
|
||
if (resolveHandle.getType().isField()) { | ||
throw new JadxRuntimeException("Field handle not yet supported"); | ||
} | ||
|
||
RootNode root = mth.root(); | ||
MethodInfo resolveMth = MethodInfo.fromRef(root, resolveHandle.getMethodRef()); | ||
InvokeType resolveInvokeType = InvokeCustomUtils.convertInvokeType(resolveHandle.getType()); | ||
InvokeNode resolve = new InvokeNode(resolveMth, resolveInvokeType, resolveArgs.size()); | ||
resolveArgs.forEach(resolve::addArg); | ||
|
||
ClassInfo invokeCls = ClassInfo.fromType(root, ArgType.OBJECT); // type will be known at runtime | ||
MethodInfo invokeMth = MethodInfo.fromMethodProto(root, invokeCls, invokeName, invokeProto); | ||
InvokeCustomRawNode customRawNode = new InvokeCustomRawNode(resolve, invokeMth, insn, isRange); | ||
customRawNode.setCallSiteValues(values); | ||
return customRawNode; | ||
} | ||
|
||
private static List<InsnArg> buildArgs(MethodNode mth, List<EncodedValue> values) { | ||
int valuesCount = values.size(); | ||
List<InsnArg> list = new ArrayList<>(valuesCount); | ||
RootNode root = mth.root(); | ||
list.add(buildLookupArg(root)); // use `java.lang.invoke.MethodHandles.lookup()` as first arg | ||
for (int i = 1; i < valuesCount; i++) { | ||
EncodedValue value = values.get(i); | ||
try { | ||
list.add(convertToInsnArg(root, value)); | ||
} catch (Exception e) { | ||
mth.addWarnComment("Failed to build arg in invoke-custom insn: " + value, e); | ||
list.add(InsnArg.wrapArg(new ConstStringNode(value.toString()))); | ||
} | ||
} | ||
return list; | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
jadx-core/src/main/java/jadx/core/dex/instructions/invokedynamic/InvokeCustomUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package jadx.core.dex.instructions.invokedynamic; | ||
|
||
import jadx.api.plugins.input.data.MethodHandleType; | ||
import jadx.core.dex.instructions.InvokeType; | ||
import jadx.core.utils.exceptions.JadxRuntimeException; | ||
|
||
public class InvokeCustomUtils { | ||
|
||
public static InvokeType convertInvokeType(MethodHandleType type) { | ||
switch (type) { | ||
case INVOKE_STATIC: | ||
return InvokeType.STATIC; | ||
case INVOKE_INSTANCE: | ||
return InvokeType.VIRTUAL; | ||
case INVOKE_DIRECT: | ||
case INVOKE_CONSTRUCTOR: | ||
return InvokeType.DIRECT; | ||
case INVOKE_INTERFACE: | ||
return InvokeType.INTERFACE; | ||
|
||
default: | ||
throw new JadxRuntimeException("Unsupported method handle type: " + type); | ||
} | ||
} | ||
} |
Oops, something went wrong.