feat(java-input): support jsr/ret opcodes (#2039)
This commit is contained in:
@@ -51,6 +51,7 @@ import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.instructions.args.Named;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.args.SSAVar;
|
||||
import jadx.core.dex.instructions.java.JsrNode;
|
||||
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
||||
import jadx.core.dex.instructions.mods.TernaryInsn;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
@@ -633,6 +634,17 @@ public class InsnGen {
|
||||
}
|
||||
break;
|
||||
|
||||
case JAVA_JSR:
|
||||
fallbackOnlyInsn(insn);
|
||||
code.add("jsr -> ").add(MethodGen.getLabelName(((JsrNode) insn).getTarget()));
|
||||
break;
|
||||
|
||||
case JAVA_RET:
|
||||
fallbackOnlyInsn(insn);
|
||||
code.add("ret ");
|
||||
addArg(code, insn.getArg(0));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new CodegenException(mth, "Unknown instruction: " + insn.getType());
|
||||
}
|
||||
|
||||
@@ -104,4 +104,6 @@ public enum AFlag {
|
||||
CLASS_UNLOADED, // class was completely unloaded
|
||||
|
||||
DONT_UNLOAD_CLASS, // don't unload class after code generation (only for tests and debug!)
|
||||
|
||||
RESOLVE_JAVA_JSR,
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import jadx.api.plugins.input.insns.custom.IArrayPayload;
|
||||
import jadx.api.plugins.input.insns.custom.ICustomPayload;
|
||||
import jadx.api.plugins.input.insns.custom.ISwitchPayload;
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.JadxError;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
@@ -23,6 +24,7 @@ import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.java.JsrNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
@@ -332,6 +334,16 @@ public class InsnDecoder {
|
||||
case GOTO:
|
||||
return new GotoNode(insn.getTarget());
|
||||
|
||||
case JAVA_JSR:
|
||||
method.add(AFlag.RESOLVE_JAVA_JSR);
|
||||
JsrNode jsr = new JsrNode(insn.getTarget());
|
||||
jsr.setResult(InsnArg.reg(insn, 0, ArgType.UNKNOWN_INT));
|
||||
return jsr;
|
||||
|
||||
case JAVA_RET:
|
||||
method.add(AFlag.RESOLVE_JAVA_JSR);
|
||||
return insn(InsnType.JAVA_RET, null, InsnArg.reg(insn, 0, ArgType.UNKNOWN_INT));
|
||||
|
||||
case THROW:
|
||||
return insn(InsnType.THROW, null, InsnArg.reg(insn, 0, ArgType.THROWABLE));
|
||||
|
||||
|
||||
@@ -71,5 +71,9 @@ public enum InsnType {
|
||||
PHI,
|
||||
|
||||
// fake insn to keep arguments which will be used in regions codegen
|
||||
REGION_ARG
|
||||
REGION_ARG,
|
||||
|
||||
// Java specific dynamic jump instructions
|
||||
JAVA_JSR,
|
||||
JAVA_RET,
|
||||
}
|
||||
|
||||
@@ -69,6 +69,8 @@ public abstract class ArgType {
|
||||
public static final ArgType INT_BOOLEAN = unknown(PrimitiveType.INT, PrimitiveType.BOOLEAN);
|
||||
public static final ArgType BYTE_BOOLEAN = unknown(PrimitiveType.BYTE, PrimitiveType.BOOLEAN);
|
||||
|
||||
public static final ArgType UNKNOWN_INT = unknown(PrimitiveType.INT);
|
||||
|
||||
protected int hash;
|
||||
|
||||
private static ArgType primitive(PrimitiveType stype) {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package jadx.core.dex.instructions.java;
|
||||
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.TargetInsnNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
|
||||
public class JsrNode extends TargetInsnNode {
|
||||
|
||||
protected final int target;
|
||||
|
||||
public JsrNode(int target) {
|
||||
this(InsnType.JAVA_JSR, target, 0);
|
||||
}
|
||||
|
||||
protected JsrNode(InsnType type, int target, int argsCount) {
|
||||
super(type, argsCount);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public int getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsnNode copy() {
|
||||
return copyCommonParams(new JsrNode(target));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return baseString() + " -> " + InsnUtils.formatOffset(target) + attributesString();
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ public class FallbackModeVisitor extends AbstractVisitor {
|
||||
case RETURN:
|
||||
case IF:
|
||||
case GOTO:
|
||||
case JAVA_JSR:
|
||||
case MOVE:
|
||||
case MOVE_EXCEPTION:
|
||||
case ARITH: // ??
|
||||
|
||||
@@ -60,6 +60,11 @@ public class MoveInlineVisitor extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
SSAVar ssaVar = resultArg.getSVar();
|
||||
if (ssaVar.getUseList().isEmpty()) {
|
||||
// unused result
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ssaVar.isUsedInPhi()) {
|
||||
return false;
|
||||
// TODO: review conditions of 'up' move inline (test TestMoveInline)
|
||||
|
||||
@@ -15,6 +15,7 @@ import jadx.core.dex.instructions.SwitchData;
|
||||
import jadx.core.dex.instructions.SwitchInsn;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.java.JsrNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.visitors.blocks.BlockSplitter;
|
||||
@@ -73,6 +74,14 @@ public class ProcessInstructionsVisitor extends AbstractVisitor {
|
||||
addJump(mth, insnByOffset, offset, ((GotoNode) insn).getTarget());
|
||||
break;
|
||||
|
||||
case JAVA_JSR:
|
||||
addJump(mth, insnByOffset, offset, ((JsrNode) insn).getTarget());
|
||||
int onRet = getNextInsnOffset(insnByOffset, offset);
|
||||
if (onRet != -1) {
|
||||
addJump(mth, insnByOffset, offset, onRet);
|
||||
}
|
||||
break;
|
||||
|
||||
case INVOKE:
|
||||
if (insn.getResult() == null) {
|
||||
ArgType retType = ((BaseInvokeNode) insn).getCallMth().getReturnType();
|
||||
|
||||
@@ -28,7 +28,9 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
public class BlockSplitter extends AbstractVisitor {
|
||||
|
||||
// leave these instructions alone in block node
|
||||
/**
|
||||
* Leave these instructions alone in the block node
|
||||
*/
|
||||
private static final Set<InsnType> SEPARATE_INSNS = EnumSet.of(
|
||||
InsnType.RETURN,
|
||||
InsnType.IF,
|
||||
@@ -42,6 +44,18 @@ public class BlockSplitter extends AbstractVisitor {
|
||||
return SEPARATE_INSNS.contains(insnType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split without connecting to the next block
|
||||
*/
|
||||
private static final Set<InsnType> SPLIT_WITHOUT_CONNECT = EnumSet.of(
|
||||
InsnType.RETURN,
|
||||
InsnType.THROW,
|
||||
InsnType.GOTO,
|
||||
InsnType.IF,
|
||||
InsnType.SWITCH,
|
||||
InsnType.JAVA_JSR,
|
||||
InsnType.JAVA_RET);
|
||||
|
||||
@Override
|
||||
public void visit(MethodNode mth) {
|
||||
if (mth.isNoCode()) {
|
||||
@@ -54,6 +68,10 @@ public class BlockSplitter extends AbstractVisitor {
|
||||
addTempConnectionsForExcHandlers(mth, blocksMap);
|
||||
|
||||
expandMoveMulti(mth);
|
||||
if (mth.contains(AFlag.RESOLVE_JAVA_JSR)) {
|
||||
ResolveJavaJSR.process(mth);
|
||||
}
|
||||
|
||||
removeJumpAttr(mth);
|
||||
removeInsns(mth);
|
||||
removeEmptyDetachedBlocks(mth);
|
||||
@@ -88,27 +106,16 @@ public class BlockSplitter extends AbstractVisitor {
|
||||
curBlock = connectNewBlock(mth, curBlock, insnOffset);
|
||||
} else {
|
||||
InsnType prevType = prevInsn.getType();
|
||||
switch (prevType) {
|
||||
case RETURN:
|
||||
case THROW:
|
||||
case GOTO:
|
||||
case IF:
|
||||
case SWITCH:
|
||||
// split without connect to next block
|
||||
curBlock = startNewBlock(mth, insnOffset);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isSeparate(prevType)
|
||||
|| isSeparate(insn.getType())
|
||||
|| insn.contains(AFlag.TRY_ENTER)
|
||||
|| prevInsn.contains(AFlag.TRY_LEAVE)
|
||||
|| insn.contains(AType.EXC_HANDLER)
|
||||
|| isSplitByJump(prevInsn, insn)
|
||||
|| isDoWhile(blocksMap, curBlock, insn)) {
|
||||
curBlock = connectNewBlock(mth, curBlock, insnOffset);
|
||||
}
|
||||
break;
|
||||
if (SPLIT_WITHOUT_CONNECT.contains(prevType)) {
|
||||
curBlock = startNewBlock(mth, insnOffset);
|
||||
} else if (isSeparate(prevType)
|
||||
|| isSeparate(insn.getType())
|
||||
|| insn.contains(AFlag.TRY_ENTER)
|
||||
|| prevInsn.contains(AFlag.TRY_LEAVE)
|
||||
|| insn.contains(AType.EXC_HANDLER)
|
||||
|| isSplitByJump(prevInsn, insn)
|
||||
|| isDoWhile(blocksMap, curBlock, insn)) {
|
||||
curBlock = connectNewBlock(mth, curBlock, insnOffset);
|
||||
}
|
||||
}
|
||||
blocksMap.put(insnOffset, curBlock);
|
||||
@@ -201,6 +208,33 @@ public class BlockSplitter extends AbstractVisitor {
|
||||
to.copyAttributesFrom(from);
|
||||
}
|
||||
|
||||
static List<BlockNode> copyBlocksTree(MethodNode mth, List<BlockNode> blocks) {
|
||||
List<BlockNode> copyBlocks = new ArrayList<>(blocks.size());
|
||||
Map<BlockNode, BlockNode> map = new HashMap<>();
|
||||
for (BlockNode block : blocks) {
|
||||
BlockNode newBlock = startNewBlock(mth, block.getStartOffset());
|
||||
copyBlockData(block, newBlock);
|
||||
copyBlocks.add(newBlock);
|
||||
map.put(block, newBlock);
|
||||
}
|
||||
for (BlockNode block : blocks) {
|
||||
BlockNode newBlock = getNewBlock(block, map);
|
||||
for (BlockNode successor : block.getSuccessors()) {
|
||||
BlockNode newSuccessor = getNewBlock(successor, map);
|
||||
BlockSplitter.connect(newBlock, newSuccessor);
|
||||
}
|
||||
}
|
||||
return copyBlocks;
|
||||
}
|
||||
|
||||
private static BlockNode getNewBlock(BlockNode block, Map<BlockNode, BlockNode> map) {
|
||||
BlockNode newBlock = map.get(block);
|
||||
if (newBlock == null) {
|
||||
throw new JadxRuntimeException("Copy blocks tree failed. Missing block for connection: " + block);
|
||||
}
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
static void replaceTarget(BlockNode source, BlockNode oldTarget, BlockNode newTarget) {
|
||||
InsnNode lastInsn = BlockUtils.getLastInsn(source);
|
||||
if (lastInsn instanceof TargetInsnNode) {
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
package jadx.core.dex.visitors.blocks;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
import jadx.core.utils.ListUtils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
/**
|
||||
* Duplicate code to resolve java jsr/ret.
|
||||
* JSR (jump subroutine) allows executing the same code from different places.
|
||||
* Used mostly for 'finally' blocks, deprecated in Java 7.
|
||||
*/
|
||||
public class ResolveJavaJSR {
|
||||
|
||||
public static void process(MethodNode mth) {
|
||||
int blocksCount = mth.getBasicBlocks().size();
|
||||
int k = 0;
|
||||
while (true) {
|
||||
boolean changed = resolve(mth);
|
||||
if (!changed) {
|
||||
break;
|
||||
}
|
||||
if (k++ > blocksCount) {
|
||||
throw new JadxRuntimeException("Fail to resolve jsr instructions");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean resolve(MethodNode mth) {
|
||||
List<BlockNode> blocks = mth.getBasicBlocks();
|
||||
int blocksCount = blocks.size();
|
||||
for (BlockNode block : blocks) {
|
||||
if (BlockUtils.checkLastInsnType(block, InsnType.JAVA_RET)) {
|
||||
resolveForRetBlock(mth, block);
|
||||
if (blocksCount != mth.getBasicBlocks().size()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void resolveForRetBlock(MethodNode mth, BlockNode retBlock) {
|
||||
BlockUtils.visitPredecessorsUntil(mth, retBlock, startBlock -> {
|
||||
List<BlockNode> preds = startBlock.getPredecessors();
|
||||
if (preds.size() > 1
|
||||
&& preds.stream().allMatch(p -> BlockUtils.checkLastInsnType(p, InsnType.JAVA_JSR))) {
|
||||
List<BlockNode> jsrBlocks = new ArrayList<>(preds);
|
||||
List<BlockNode> dupBlocks = BlockUtils.collectAllSuccessors(mth, startBlock, false);
|
||||
removeInsns(retBlock, startBlock, jsrBlocks);
|
||||
processBlocks(mth, retBlock, startBlock, jsrBlocks, dupBlocks);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private static void removeInsns(BlockNode retBlock, BlockNode startBlock, List<BlockNode> jsrBlocks) {
|
||||
InsnNode retInsn = ListUtils.removeLast(retBlock.getInstructions());
|
||||
if (retInsn != null && retInsn.getType() == InsnType.JAVA_RET) {
|
||||
InsnArg retArg = retInsn.getArg(0);
|
||||
if (retArg.isRegister()) {
|
||||
int regNum = ((RegisterArg) retArg).getRegNum();
|
||||
InsnNode startInsn = BlockUtils.getFirstInsn(startBlock);
|
||||
if (startInsn != null
|
||||
&& startInsn.getType() == InsnType.MOVE
|
||||
&& startInsn.getResult().getRegNum() == regNum) {
|
||||
startBlock.getInstructions().remove(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
jsrBlocks.forEach(p -> ListUtils.removeLast(p.getInstructions()));
|
||||
}
|
||||
|
||||
private static void processBlocks(MethodNode mth, BlockNode retBlock, BlockNode startBlock,
|
||||
List<BlockNode> jsrBlocks, List<BlockNode> dupBlocks) {
|
||||
BlockNode first = null;
|
||||
for (BlockNode jsrBlock : jsrBlocks) {
|
||||
if (first == null) {
|
||||
first = jsrBlock;
|
||||
} else {
|
||||
BlockNode pathBlock = BlockUtils.selectOther(startBlock, jsrBlock.getSuccessors());
|
||||
BlockSplitter.removeConnection(jsrBlock, startBlock);
|
||||
BlockSplitter.removeConnection(jsrBlock, pathBlock);
|
||||
List<BlockNode> newBlocks = BlockSplitter.copyBlocksTree(mth, dupBlocks);
|
||||
BlockNode newStart = newBlocks.get(dupBlocks.indexOf(startBlock));
|
||||
BlockNode newRetBlock = newBlocks.get(dupBlocks.indexOf(retBlock));
|
||||
BlockSplitter.connect(jsrBlock, newStart);
|
||||
BlockSplitter.connect(newRetBlock, pathBlock);
|
||||
}
|
||||
}
|
||||
if (first != null) {
|
||||
BlockNode pathBlock = BlockUtils.selectOther(startBlock, first.getSuccessors());
|
||||
BlockSplitter.removeConnection(first, pathBlock);
|
||||
BlockSplitter.connect(retBlock, pathBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -481,19 +481,28 @@ public class BlockUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static List<BlockNode> collectAllSuccessors(MethodNode mth, BlockNode startBlock, boolean clean) {
|
||||
List<BlockNode> list = new ArrayList<>(mth.getBasicBlocks().size());
|
||||
dfsVisit(mth, startBlock, clean, list::add);
|
||||
return list;
|
||||
}
|
||||
|
||||
public static void dfsVisit(MethodNode mth, Consumer<BlockNode> visitor) {
|
||||
dfsVisit(mth, mth.getEnterBlock(), false, visitor);
|
||||
}
|
||||
|
||||
private static void dfsVisit(MethodNode mth, BlockNode startBlock, boolean clean, Consumer<BlockNode> visitor) {
|
||||
BitSet visited = newBlocksBitSet(mth);
|
||||
Deque<BlockNode> queue = new ArrayDeque<>();
|
||||
BlockNode enterBlock = mth.getEnterBlock();
|
||||
queue.addLast(enterBlock);
|
||||
visited.set(mth.getEnterBlock().getId());
|
||||
queue.addLast(startBlock);
|
||||
visited.set(startBlock.getId());
|
||||
while (true) {
|
||||
BlockNode current = queue.pollLast();
|
||||
if (current == null) {
|
||||
return;
|
||||
}
|
||||
visitor.accept(current);
|
||||
List<BlockNode> successors = current.getSuccessors();
|
||||
List<BlockNode> successors = clean ? current.getCleanSuccessors() : current.getSuccessors();
|
||||
int count = successors.size();
|
||||
for (int i = count - 1; i >= 0; i--) { // to preserve order in queue
|
||||
BlockNode next = successors.get(i);
|
||||
|
||||
@@ -49,6 +49,14 @@ public class ListUtils {
|
||||
return list.get(list.size() - 1);
|
||||
}
|
||||
|
||||
public static <T> @Nullable T removeLast(List<T> list) {
|
||||
int size = list.size();
|
||||
if (size == 0) {
|
||||
return null;
|
||||
}
|
||||
return list.remove(size - 1);
|
||||
}
|
||||
|
||||
public static <T extends Comparable<T>> List<T> distinctMergeSortedLists(List<T> first, List<T> second) {
|
||||
if (first.isEmpty()) {
|
||||
return second;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package jadx.tests.integration.others;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.tests.api.RaungTest;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
public class TestJavaJSR extends RaungTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
assertThat(getClassNodeFromRaung())
|
||||
.code()
|
||||
.containsLines(2,
|
||||
"InputStream in = url.openStream();",
|
||||
"try {",
|
||||
indent() + "return call(in);",
|
||||
"} finally {",
|
||||
indent() + "in.close();",
|
||||
"}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
.version 45.3
|
||||
.class others/TestJavaJSR
|
||||
|
||||
.method public test(Ljava/net/URL;)Ljava/lang/String;
|
||||
.throw java/io/IOException
|
||||
.max stack 2
|
||||
.max locals 6
|
||||
|
||||
.local 0 "this" Lothers/TestJavaJSR;
|
||||
.local 1 "url" Ljava/net/URL;
|
||||
.line 88
|
||||
aload 1
|
||||
invokevirtual java/net/URL openStream ()Ljava/io/InputStream;
|
||||
astore 2
|
||||
:L0
|
||||
.local 2 "in" Ljava/io/InputStream;
|
||||
.line 89
|
||||
.line 90
|
||||
aload 0
|
||||
aload 2
|
||||
invokevirtual others/TestJavaJSR call (Ljava/io/InputStream;)Ljava/lang/String;
|
||||
astore 3
|
||||
jsr :L3
|
||||
aload 3
|
||||
areturn
|
||||
:L1
|
||||
.catch all :L0 .. :L1 goto :L1
|
||||
.line 89
|
||||
astore 4
|
||||
jsr :L3
|
||||
aload 4
|
||||
athrow
|
||||
:L3
|
||||
astore 5
|
||||
.line 92
|
||||
aload 2
|
||||
invokevirtual java/io/InputStream close ()V
|
||||
.line 89
|
||||
ret 5
|
||||
.end method
|
||||
|
||||
.method public call(Ljava/io/InputStream;)Ljava/lang/String;
|
||||
.throw java/io/IOException
|
||||
.max stack 1
|
||||
.max locals 2
|
||||
|
||||
ldc ""
|
||||
areturn
|
||||
.end method
|
||||
@@ -189,4 +189,8 @@ public enum Opcode {
|
||||
|
||||
CONST_METHOD_HANDLE,
|
||||
CONST_METHOD_TYPE,
|
||||
|
||||
// Java specific dynamic jump instructions
|
||||
JAVA_JSR,
|
||||
JAVA_RET,
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ dependencies {
|
||||
api(project(":jadx-core"))
|
||||
|
||||
// show bytecode disassemble
|
||||
implementation("io.github.skylot:raung-disasm:0.1.0")
|
||||
implementation("io.github.skylot:raung-disasm:0.1.1")
|
||||
|
||||
testImplementation(project(":jadx-core"))
|
||||
}
|
||||
|
||||
+3
@@ -253,6 +253,8 @@ public class JavaInsnsRegister {
|
||||
register(arr, 0xa6, "if_acmpne", 2, 2, Opcode.IF_NE, cmp());
|
||||
|
||||
register(arr, 0xa7, "goto", 2, 0, Opcode.GOTO, s -> s.jump(s.s2()));
|
||||
register(arr, 0xa8, "jsr", 2, 1, Opcode.JAVA_JSR, s -> s.push(0).jump(s.s2()));
|
||||
register(arr, 0xa9, "ret", 1, 1, Opcode.JAVA_RET, s -> s.local(0, s.u1()));
|
||||
|
||||
register(arr, 0xaa, "tableswitch", -1, 1, Opcode.PACKED_SWITCH, new TableSwitchDecoder());
|
||||
register(arr, 0xab, "lookupswitch", -1, 1, Opcode.SPARSE_SWITCH, new LookupSwitchDecoder());
|
||||
@@ -294,6 +296,7 @@ public class JavaInsnsRegister {
|
||||
register(arr, 0xc7, "ifnonnull", 2, 1, Opcode.IF_NEZ, zeroCmp());
|
||||
|
||||
register(arr, 0xc8, "goto_w", 4, 0, Opcode.GOTO, s -> s.jump(s.reader().readS4()));
|
||||
register(arr, 0xc9, "jsr_w", 4, 1, Opcode.JAVA_JSR, s -> s.push(0).jump(s.reader().readS4()));
|
||||
}
|
||||
|
||||
private static void dup2x1(CodeDecodeState s) {
|
||||
|
||||
@@ -5,5 +5,5 @@ plugins {
|
||||
dependencies {
|
||||
api(project(":jadx-core"))
|
||||
|
||||
implementation("io.github.skylot:raung-asm:0.1.0")
|
||||
implementation("io.github.skylot:raung-asm:0.1.1")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user