diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index 1d80e7c4ed2..a2aba3ffa66 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -51,6 +51,7 @@ 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 @@ private void makeInsnBody(ICodeWriter code, InsnNode insn, Set state) thr } 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()); } diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java b/jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java index 25781e858c9..944d4fbf820 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java @@ -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, } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java index 4ec4048ca23..b5e6a54663c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java @@ -15,6 +15,7 @@ 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.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 @@ protected InsnNode decode(InsnData insn) throws DecodeException { 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)); diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnType.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnType.java index 88461799e18..2a01fe3ce8c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnType.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnType.java @@ -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, } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java index 9d7025c092e..a36f4db5d13 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java @@ -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) { diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/java/JsrNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/java/JsrNode.java new file mode 100644 index 00000000000..2cf6f110a1f --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/java/JsrNode.java @@ -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(); + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/FallbackModeVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/FallbackModeVisitor.java index ae96d91b677..84676d3a11b 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/FallbackModeVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/FallbackModeVisitor.java @@ -33,6 +33,7 @@ public void visit(MethodNode mth) throws JadxException { case RETURN: case IF: case GOTO: + case JAVA_JSR: case MOVE: case MOVE_EXCEPTION: case ARITH: // ?? diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/MoveInlineVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/MoveInlineVisitor.java index f7ae0bff54a..c06dbd21a36 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/MoveInlineVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/MoveInlineVisitor.java @@ -60,6 +60,11 @@ private static boolean processMove(MethodNode mth, InsnNode move) { } } 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) diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ProcessInstructionsVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ProcessInstructionsVisitor.java index c9c9ba4f269..0eb7a3a0090 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ProcessInstructionsVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ProcessInstructionsVisitor.java @@ -15,6 +15,7 @@ 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 @@ private static void initJumps(MethodNode mth, InsnNode[] insnByOffset) { 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(); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockSplitter.java b/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockSplitter.java index e9003e8ff70..d1fc23f4aac 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockSplitter.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockSplitter.java @@ -28,7 +28,9 @@ public class BlockSplitter extends AbstractVisitor { - // leave these instructions alone in block node + /** + * Leave these instructions alone in the block node + */ private static final Set SEPARATE_INSNS = EnumSet.of( InsnType.RETURN, InsnType.IF, @@ -42,6 +44,18 @@ public static boolean isSeparate(InsnType insnType) { return SEPARATE_INSNS.contains(insnType); } + /** + * Split without connecting to the next block + */ + private static final Set 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 void visit(MethodNode mth) { 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 @@ private static Map splitBasicBlocks(MethodNode mth) { 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 @@ static void copyBlockData(BlockNode from, BlockNode to) { to.copyAttributesFrom(from); } + static List copyBlocksTree(MethodNode mth, List blocks) { + List copyBlocks = new ArrayList<>(blocks.size()); + Map 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 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) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/ResolveJavaJSR.java b/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/ResolveJavaJSR.java new file mode 100644 index 00000000000..2aaf258fa63 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/ResolveJavaJSR.java @@ -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 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 preds = startBlock.getPredecessors(); + if (preds.size() > 1 + && preds.stream().allMatch(p -> BlockUtils.checkLastInsnType(p, InsnType.JAVA_JSR))) { + List jsrBlocks = new ArrayList<>(preds); + List 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 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 jsrBlocks, List 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 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); + } + } +} diff --git a/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java b/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java index e0e48b7f71e..ec7000516a7 100644 --- a/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java @@ -481,19 +481,28 @@ public static boolean visitBlocksOnPath(MethodNode mth, BlockNode start, BlockNo } } + public static List collectAllSuccessors(MethodNode mth, BlockNode startBlock, boolean clean) { + List list = new ArrayList<>(mth.getBasicBlocks().size()); + dfsVisit(mth, startBlock, clean, list::add); + return list; + } + public static void dfsVisit(MethodNode mth, Consumer visitor) { + dfsVisit(mth, mth.getEnterBlock(), false, visitor); + } + + private static void dfsVisit(MethodNode mth, BlockNode startBlock, boolean clean, Consumer visitor) { BitSet visited = newBlocksBitSet(mth); Deque 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 successors = current.getSuccessors(); + List 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); diff --git a/jadx-core/src/main/java/jadx/core/utils/ListUtils.java b/jadx-core/src/main/java/jadx/core/utils/ListUtils.java index c4a50efc674..44a0546eafc 100644 --- a/jadx-core/src/main/java/jadx/core/utils/ListUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/ListUtils.java @@ -49,6 +49,14 @@ public static T last(List list) { return list.get(list.size() - 1); } + public static @Nullable T removeLast(List list) { + int size = list.size(); + if (size == 0) { + return null; + } + return list.remove(size - 1); + } + public static > List distinctMergeSortedLists(List first, List second) { if (first.isEmpty()) { return second; diff --git a/jadx-core/src/test/java/jadx/tests/integration/others/TestJavaJSR.java b/jadx-core/src/test/java/jadx/tests/integration/others/TestJavaJSR.java new file mode 100644 index 00000000000..522a5dbe9ab --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/others/TestJavaJSR.java @@ -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();", + "}"); + } +} diff --git a/jadx-core/src/test/raung/others/TestJavaJSR.raung b/jadx-core/src/test/raung/others/TestJavaJSR.raung new file mode 100644 index 00000000000..83ebf978042 --- /dev/null +++ b/jadx-core/src/test/raung/others/TestJavaJSR.raung @@ -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 diff --git a/jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/insns/Opcode.java b/jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/insns/Opcode.java index 94dc539c054..35157f995ec 100644 --- a/jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/insns/Opcode.java +++ b/jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/insns/Opcode.java @@ -189,4 +189,8 @@ public enum Opcode { CONST_METHOD_HANDLE, CONST_METHOD_TYPE, + + // Java specific dynamic jump instructions + JAVA_JSR, + JAVA_RET, } diff --git a/jadx-plugins/jadx-java-input/build.gradle.kts b/jadx-plugins/jadx-java-input/build.gradle.kts index b6ca9f927c5..fbf53724ac1 100644 --- a/jadx-plugins/jadx-java-input/build.gradle.kts +++ b/jadx-plugins/jadx-java-input/build.gradle.kts @@ -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")) } diff --git a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/JavaInsnsRegister.java b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/JavaInsnsRegister.java index 92af7f3340c..83ecf278454 100644 --- a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/JavaInsnsRegister.java +++ b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/JavaInsnsRegister.java @@ -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) { diff --git a/jadx-plugins/jadx-raung-input/build.gradle.kts b/jadx-plugins/jadx-raung-input/build.gradle.kts index 3b2d00170c2..3d749ef2f24 100644 --- a/jadx-plugins/jadx-raung-input/build.gradle.kts +++ b/jadx-plugins/jadx-raung-input/build.gradle.kts @@ -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") }