diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index 32cdbc3..637260c 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -107,6 +107,10 @@ public class MethodProcessorRunnable implements Runnable { if (ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) { DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.Severity.WARN); + if(!ExceptionDeobfuscator.handleMultipleEntryExceptionRanges(graph)) { + DecompilerContext.getLogger().writeMessage("Found multiple entry exception ranges which could not be splitted", IFernflowerLogger.Severity.WARN); + } + ExceptionDeobfuscator.insertDummyExceptionHandlerBlocks(graph, cl.getBytecodeVersion()); } RootStatement root = DomHelper.parseGraph(graph); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java index 0bade66..f23fc78 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java @@ -178,7 +178,7 @@ public class SimplifyExprentsHelper { } // expr++ and expr-- - if (isIPPorIMM(current, next)) { + if (isIPPorIMM(current, next) || isIPPorIMM2(current, next)) { list.remove(index + 1); res = true; continue; @@ -458,6 +458,48 @@ public class SimplifyExprentsHelper { return false; } + private static boolean isIPPorIMM2(Exprent first, Exprent second) { + + if (first.type != Exprent.EXPRENT_ASSIGNMENT || second.type != Exprent.EXPRENT_ASSIGNMENT) { + return false; + } + + AssignmentExprent af = (AssignmentExprent)first; + AssignmentExprent as = (AssignmentExprent)second; + + if(as.getRight().type != Exprent.EXPRENT_FUNCTION) { + return false; + } + + FunctionExprent func = (FunctionExprent)as.getRight(); + + if(func.getFuncType() != FunctionExprent.FUNCTION_ADD && func.getFuncType() != FunctionExprent.FUNCTION_SUB) { + return false; + } + + Exprent econd = func.getLstOperands().get(0); + Exprent econst = func.getLstOperands().get(1); + + if(econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST && func.getFuncType() == FunctionExprent.FUNCTION_ADD) { + econd = econst; + econst = func.getLstOperands().get(0); + } + + if(econst.type == Exprent.EXPRENT_CONST && ((ConstExprent)econst).hasValueOne()) { + if(af.getLeft().equals(econd) && af.getRight().equals(as.getLeft()) && (af.getLeft().getExprentUse() & Exprent.MULTIPLE_USES) != 0) { + int type = func.getFuncType() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_IPP : FunctionExprent.FUNCTION_IMM; + + FunctionExprent ret = new FunctionExprent(type, af.getRight(), func.bytecode); + ret.setImplicitType(VarType.VARTYPE_INT); + + af.setRight(ret); + return true; + } + } + + return false; + } + private static boolean isMonitorExit(Exprent first) { if (first.type == Exprent.EXPRENT_MONITOR) { MonitorExprent expr = (MonitorExprent)first; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java index 89babeb..d8b587f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java @@ -134,8 +134,9 @@ public class DominatorTreeExceptionFilter { exit = childid; } else { - // exit = map.containsKey(handler)?-1:mapChild.get(handler); FIXME: Eclipse bug? - exit = map.containsKey(handler) ? -1 : mapChild.get(handler); + // after replacing 'new Integer(-1)' with '-1' Eclipse throws a NullPointerException on the following line + // could be a bug in Eclipse or some obscure specification glitch, FIXME: needs further investigation + exit = map.containsKey(handler) ? new Integer(-1) : mapChild.get(handler); } if (exit != null) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java index 676f448..7f7c9aa 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java @@ -8,6 +8,8 @@ import org.jetbrains.java.decompiler.code.SimpleInstructionSequence; import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.modules.decompiler.decompose.GenericDominatorEngine; import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraph; import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode; @@ -233,7 +235,7 @@ public class ExceptionDeobfuscator { if (rangeList.contains(handler)) { // TODO: better removing strategy - List lstRemBlocks = getReachableBlocksRestricted(range, engine); + List lstRemBlocks = getReachableBlocksRestricted(range.getHandler(), range, engine); if (lstRemBlocks.size() < rangeList.size() || rangeList.size() == 1) { for (BasicBlock block : lstRemBlocks) { @@ -249,22 +251,21 @@ public class ExceptionDeobfuscator { } } - private static List getReachableBlocksRestricted(ExceptionRangeCFG range, GenericDominatorEngine engine) { + private static List getReachableBlocksRestricted(BasicBlock start, ExceptionRangeCFG range, GenericDominatorEngine engine) { List lstRes = new ArrayList<>(); LinkedList stack = new LinkedList<>(); Set setVisited = new HashSet<>(); - BasicBlock handler = range.getHandler(); - stack.addFirst(handler); + stack.addFirst(start); while (!stack.isEmpty()) { BasicBlock block = stack.removeFirst(); setVisited.add(block); - if (range.getProtectedRange().contains(block) && engine.isDominator(block, handler)) { + if (range.getProtectedRange().contains(block) && engine.isDominator(block, start)) { lstRes.add(block); List lstSuccs = new ArrayList<>(block.getSuccs()); @@ -308,4 +309,139 @@ public class ExceptionDeobfuscator { return false; } + + public static boolean handleMultipleEntryExceptionRanges(ControlFlowGraph graph) { + + GenericDominatorEngine engine = new GenericDominatorEngine(new IGraph() { + public List getReversePostOrderList() { + return graph.getReversePostOrder(); + } + + public Set getRoots() { + return new HashSet<>(Collections.singletonList(graph.getFirst())); + } + }); + + engine.initialize(); + + boolean found = false; + + while(true) { + found = false; + boolean splitted = false; + + for(ExceptionRangeCFG range : graph.getExceptions()) { + Set setEntries = getRangeEntries(range); + + if(setEntries.size() > 1) { // multiple-entry protected range + found = true; + + if(splitExceptionRange(range, setEntries, graph, engine)) { + splitted = true; + break; + } + } + } + + if(!splitted) { + break; + } + } + + return !found; + } + + private static Set getRangeEntries(ExceptionRangeCFG range) { + + Set setEntries = new HashSet<>(); + Set setRange= new HashSet<>(range.getProtectedRange()); + + for(BasicBlock block : range.getProtectedRange()) { + Set setPreds = new HashSet<>(block.getPreds()); + setPreds.removeAll(setRange); + + if (!setPreds.isEmpty()) { + setEntries.add(block); + } + } + + return setEntries; + } + + private static boolean splitExceptionRange(ExceptionRangeCFG range, Set setEntries, ControlFlowGraph graph, GenericDominatorEngine engine) { + + for(BasicBlock entry : setEntries) { + List lstSubrangeBlocks = getReachableBlocksRestricted(entry, range, engine); + if(!lstSubrangeBlocks.isEmpty() && lstSubrangeBlocks.size() < range.getProtectedRange().size()) { + // add new range + ExceptionRangeCFG subRange = new ExceptionRangeCFG(lstSubrangeBlocks, range.getHandler(), range.getExceptionTypes()); + graph.getExceptions().add(subRange); + // shrink the original range + range.getProtectedRange().removeAll(lstSubrangeBlocks); + return true; + } else { + // should not happen + DecompilerContext.getLogger().writeMessage("Inconsistency found while splitting protected range", IFernflowerLogger.Severity.WARN); + } + } + + return false; + } + + public static void insertDummyExceptionHandlerBlocks(ControlFlowGraph graph, int bytecode_version) { + + Map> mapRanges = new HashMap<>(); + for (ExceptionRangeCFG range : graph.getExceptions()) { + mapRanges.computeIfAbsent(range.getHandler(), k -> new HashSet<>()).add(range); + } + + for (Entry> ent : mapRanges.entrySet()) { + BasicBlock handler = ent.getKey(); + Set ranges = ent.getValue(); + + if(ranges.size() == 1) { + continue; + } + + for(ExceptionRangeCFG range : ranges) { + + // add some dummy instructions to prevent optimizing away the empty block + SimpleInstructionSequence seq = new SimpleInstructionSequence(); + seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}), -1); + seq.addInstruction(Instruction.create(CodeConstants.opc_pop, false, CodeConstants.GROUP_GENERAL, bytecode_version, null), -1); + + BasicBlock dummyBlock = new BasicBlock(++graph.last_id); + dummyBlock.setSeq(seq); + + graph.getBlocks().addWithKey(dummyBlock, dummyBlock.id); + + // only exception predecessors from this range considered + List lstPredExceptions = new ArrayList<>(handler.getPredExceptions()); + lstPredExceptions.retainAll(range.getProtectedRange()); + + // replace predecessors + for (BasicBlock pred : lstPredExceptions) { + pred.replaceSuccessor(handler, dummyBlock); + } + + // replace handler + range.setHandler(dummyBlock); + // add common exception edges + Set commonHandlers = new HashSet<>(handler.getSuccExceptions()); + for(BasicBlock pred : lstPredExceptions) { + commonHandlers.retainAll(pred.getSuccExceptions()); + } + // TODO: more sanity checks? + for(BasicBlock commonHandler : commonHandlers) { + ExceptionRangeCFG commonRange = graph.getExceptionRange(commonHandler, handler); + + dummyBlock.addSuccessorException(commonHandler); + commonRange.getProtectedRange().add(dummyBlock); + } + + dummyBlock.addSuccessor(handler); + } + } + } + } \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java index 264347e..642a86b 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java @@ -54,7 +54,7 @@ public class FieldExprent extends Exprent { @Override public int getExprentUse() { - return instance == null ? Exprent.MULTIPLE_USES : instance.getExprentUse() & Exprent.MULTIPLE_USES; + return 0; // multiple references to a field considered dangerous in a multithreaded environment, thus no Exprent.MULTIPLE_USES set here } @Override diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index 607c9a5..6c1468a 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -112,6 +112,7 @@ public class SingleClassesTest { @Test public void testPrivateEmptyConstructor() { doTest("pkg/TestPrivateEmptyConstructor"); } @Test public void testSynchronizedUnprotected() { doTest("pkg/TestSynchronizedUnprotected"); } @Test public void testInterfaceSuper() { doTest("pkg/TestInterfaceSuper"); } + @Test public void testFieldSingleAccess() { doTest("pkg/TestFieldSingleAccess"); } // TODO: fix all below //@Test public void testPackageInfo() { doTest("pkg/package-info"); } @@ -124,6 +125,7 @@ public class SingleClassesTest { @Test public void testGroovyTrait() { doTest("pkg/TestGroovyTrait"); } @Test public void testPrivateClasses() { doTest("pkg/PrivateClasses"); } @Test public void testSuspendLambda() { doTest("pkg/TestSuspendLambdaKt"); } + @Test public void testNamedSuspendFun2Kt() { doTest("pkg/TestNamedSuspendFun2Kt"); } private void doTest(String testFile, String... companionFiles) { ConsoleDecompiler decompiler = fixture.getDecompiler(); diff --git a/testData/classes/pkg/TestFieldSingleAccess.class b/testData/classes/pkg/TestFieldSingleAccess.class new file mode 100644 index 0000000..edc30ab Binary files /dev/null and b/testData/classes/pkg/TestFieldSingleAccess.class differ diff --git a/testData/classes/pkg/TestNamedSuspendFun2Kt$foo2$1.class b/testData/classes/pkg/TestNamedSuspendFun2Kt$foo2$1.class new file mode 100644 index 0000000..af894aa Binary files /dev/null and b/testData/classes/pkg/TestNamedSuspendFun2Kt$foo2$1.class differ diff --git a/testData/classes/pkg/TestNamedSuspendFun2Kt.class b/testData/classes/pkg/TestNamedSuspendFun2Kt.class new file mode 100644 index 0000000..83f3b75 Binary files /dev/null and b/testData/classes/pkg/TestNamedSuspendFun2Kt.class differ diff --git a/testData/results/TestFieldSingleAccess.dec b/testData/results/TestFieldSingleAccess.dec new file mode 100644 index 0000000..268c866 --- /dev/null +++ b/testData/results/TestFieldSingleAccess.dec @@ -0,0 +1,40 @@ +package pkg; + +public final class TestFieldSingleAccess { + public Integer field; + + public final void test() { + Integer var10000 = this.field; + if (var10000 != null) { + System.out.println(var10000); + } + + } + + public final void test1() { + synchronized(this.field) { + System.out.println('1'); + } + } +} + +class 'pkg/TestFieldSingleAccess' { + method 'test ()V' { + 1 6 + 5 7 + 8 8 + c 8 + f 11 + } + + method 'test1 ()V' { + 1 14 + 6 14 + 7 15 + a 15 + c 15 + 19 17 + } +} + +Lines mapping: diff --git a/testData/results/TestMethodReferenceSameName.dec b/testData/results/TestMethodReferenceSameName.dec index 3449a8c..2592d18 100644 --- a/testData/results/TestMethodReferenceSameName.dec +++ b/testData/results/TestMethodReferenceSameName.dec @@ -3,7 +3,6 @@ public class TestMethodReferenceSameName { private void foo() { TestMethodReferenceSameName.R1 var10000 = this.r;// 5 - this.r.getClass(); (var10000::foo).run(); }// 6 @@ -16,19 +15,18 @@ public class TestMethodReferenceSameName { class 'TestMethodReferenceSameName' { method 'foo ()V' { 1 4 - 5 5 - e 6 - 13 7 + e 5 + 13 6 } } class 'TestMethodReferenceSameName$R1' { method 'foo ()V' { - 0 11 + 0 10 } } Lines mapping: 5 <-> 5 -6 <-> 8 -9 <-> 12 +6 <-> 7 +9 <-> 11 diff --git a/testData/results/TestNamedSuspendFun2Kt.dec b/testData/results/TestNamedSuspendFun2Kt.dec new file mode 100644 index 0000000..8c4d7c5 --- /dev/null +++ b/testData/results/TestNamedSuspendFun2Kt.dec @@ -0,0 +1,301 @@ +import kotlin.Metadata; +import kotlin.SuccessOrFailure.Failure; +import kotlin.coroutines.Continuation; +import kotlin.coroutines.intrinsics.IntrinsicsKt; +import kotlin.coroutines.jvm.internal.ContinuationImpl; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@Metadata( + mv = {1, 1, 11}, + bv = {1, 0, 2}, + k = 2, + xi = 2, + d1 = {"\u0000\n\n\u0000\n\u0002\u0010\b\n\u0002\b\u0002\u001a\u0011\u0010\u0000\u001a\u00020\u0001H\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\u0002\u001a\u0011\u0010\u0003\u001a\u00020\u0001H\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\u0002\u0082\u0002\u0004\n\u0002\b\u0019"}, + d2 = {"bar", "", "(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", "foo2"} +) +public final class TestNamedSuspendFun2Kt { + @Nullable + public static final Object foo2(@NotNull Continuation var0) { + @Metadata( + mv = {1, 1, 11}, + bv = {1, 0, 2}, + k = 3, + xi = 2, + d1 = {"\u0000\u0010\n\u0000\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\u0010\b\u0010\u0000\u001a\u0004\u0018\u00010\u00012\f\u0010\u0002\u001a\b\u0012\u0004\u0012\u00020\u00040\u0003H\u0086@ø\u0001\u0000"}, + d2 = {"foo2", "", "continuation", "Lkotlin/coroutines/Continuation;", ""} + ) + final class NamelessClass_1 extends ContinuationImpl { + int label; + int I$0; + Object L$0; + + @Nullable + public final Object invokeSuspend(@NotNull Object result) { + this.data = result; + this.label |= -2147483648; + return TestNamedSuspendFun2Kt.foo2(this); + } + + NamelessClass_1(Continuation var1) { + super(var1); + } + } + + NamelessClass_1 var3; + label463: { + if (var0 instanceof NamelessClass_1) { + var3 = (NamelessClass_1)var0; + if ((var3.getLabel() & -2147483648) != 0) { + var3.setLabel(var3.getLabel() - -2147483648); + break label463; + } + } + + var3 = new NamelessClass_1(var0); + } + + Object var4; + int x; + label491: { + Throwable var1; + Throwable var10000; + label472: { + Object var2 = var3.data; + var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();// 2 + boolean var10001; + Object var22; + switch(var3.label) { + case 0: + if (var2 instanceof Failure) { + throw ((Failure)var2).exception; + } + break; + case 1: + try { + if (var2 instanceof Failure) { + throw ((Failure)var2).exception; + } + + var22 = var2; + } catch (Throwable var19) { + var10000 = var19; + var10001 = false; + break label472; + } + + try { + x = ((Number)var22).intValue();// 6 + if (x == 0) { + break label491; + } + } catch (Throwable var17) { + var10000 = var17; + var10001 = false; + break label472; + } + + var3.label = 3; + if (bar(var3) == var4) { + return var4; + } + break; + case 2: + x = var3.I$0; + if (var2 instanceof Failure) { + throw ((Failure)var2).exception; + } + + return 1;// 11 + case 3: + if (var2 instanceof Failure) { + throw ((Failure)var2).exception; + } + break; + case 4: + var1 = (Throwable)var3.L$0; + if (var2 instanceof Failure) { + throw ((Failure)var2).exception; + } + + throw var1;// 9 + default: + throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); + } + + do { + try { + var3.label = 1;// 5 + var22 = bar(var3); + } catch (Throwable var18) { + var10000 = var18; + var10001 = false; + break label472; + } + + if (var22 == var4) { + return var4; + } + + try { + x = ((Number)var22).intValue(); + if (x == 0) { + break label491; + } + } catch (Throwable var20) { + var10000 = var20; + var10001 = false; + break label472; + } + + var3.label = 3; + } while(bar(var3) != var4); + + return var4; + } + + var1 = var10000; + var3.L$0 = var1; + var3.label = 4; + if (bar(var3) == var4) {// 8 + return var4; + } + + throw var1; + } + + var3.I$0 = x; + var3.label = 2; + if (bar(var3) == var4) { + return var4; + } else { + return 1; + } + } + + @Nullable + public static final Object bar(@NotNull Continuation var0) { + return 0;// 14 + } +} + +class 'TestNamedSuspendFun2Kt$foo2$1' { + method 'invokeSuspend (Ljava/lang/Object;)Ljava/lang/Object;' { + 2 34 + a 35 + d 35 + 11 36 + 14 36 + } + + method ' (Lkotlin/coroutines/Continuation;)V' { + 2 40 + 5 41 + } +} + +class 'TestNamedSuspendFun2Kt' { + method 'foo2 (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;' { + 1 46 + 4 46 + 8 47 + b 47 + d 48 + 10 48 + 12 48 + 13 48 + 18 49 + 1b 49 + 1d 49 + 1e 49 + 21 50 + 2c 54 + 2e 63 + 31 63 + 32 64 + 35 64 + 38 67 + 3b 67 + 5e 69 + 61 69 + 64 70 + 67 70 + 6a 70 + 6f 127 + 70 127 + 73 128 + 79 135 + 7e 136 + 81 75 + 84 75 + 87 76 + 8a 76 + 8d 76 + 90 87 + 93 87 + 96 87 + 98 88 + 9e 166 + a2 167 + a3 167 + a6 168 + ac 168 + b1 169 + b3 103 + b6 103 + b9 104 + bc 104 + bf 105 + c2 105 + c5 105 + c9 108 + ce 97 + cf 97 + d2 98 + d8 98 + dd 99 + e0 110 + e3 110 + e6 111 + e9 111 + ec 111 + f3 156 + f7 157 + fb 158 + fc 158 + ff 159 + 105 159 + 10a 160 + 10c 115 + 10f 115 + 112 115 + 115 116 + 118 116 + 11b 117 + 11e 117 + 121 117 + 126 120 + 12a 108 + 12b 108 + 133 122 + 138 122 + } + + method 'bar (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;' { + 0 177 + 1 177 + 4 177 + } +} + +Lines mapping: +2 <-> 65 +5 <-> 128 +6 <-> 88 +8 <-> 160 +9 <-> 121 +11 <-> 109 +14 <-> 178 +Not mapped: +3 +4 diff --git a/testData/src/pkg/TestFieldSingleAccess.jasm b/testData/src/pkg/TestFieldSingleAccess.jasm new file mode 100644 index 0000000..57b203c --- /dev/null +++ b/testData/src/pkg/TestFieldSingleAccess.jasm @@ -0,0 +1,67 @@ +/** + * This code can be assembled with asmtools + * using asmtools jasm -g *.jasm command line. + */ +package pkg; + +super public final class TestFieldSingleAccess + version 52:0 +{ + +public Field field:"Ljava/lang/Integer;"; + +public Method "":"()V" + stack 1 locals 1 +{ + aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; +} + +public final Method test:"()V" + stack 2 locals 1 +{ + aload_0; + getfield Field field:"Ljava/lang/Integer;"; + dup; + ifnull L17; + getstatic Field java/lang/System.out:"Ljava/io/PrintStream;"; + swap; + invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/Object;)V"; + L17: stack_frame_type same; + return; +} + +public final Method test1:"()V" + stack 2 locals 3 +{ + aload_0; + getfield Field field:"Ljava/lang/Integer;"; + dup; + astore_1; + monitorenter; + try t0; + getstatic Field java/lang/System.out:"Ljava/io/PrintStream;"; + bipush 49; + invokevirtual Method java/io/PrintStream.println:"(C)V"; + aload_1; + monitorexit; + endtry t0; + goto L25; + catch t0 #0; + catch t1 #0; + try t1; + stack_frame_type full; + locals_map class TestFieldSingleAccess, class java/lang/Object; + stack_map class java/lang/Throwable; + astore_2; + aload_1; + monitorexit; + endtry t1; + aload_2; + athrow; + L25: stack_frame_type chop1; + return; +} + +} // end Class TestFieldSingleAccess diff --git a/testData/src/pkg/TestNamedSuspendFun2.kt b/testData/src/pkg/TestNamedSuspendFun2.kt new file mode 100644 index 0000000..670a91f --- /dev/null +++ b/testData/src/pkg/TestNamedSuspendFun2.kt @@ -0,0 +1,14 @@ + +suspend fun foo2(): Int { + while (true) { + try { + val x = bar() + if (x == 0) break + } finally { + bar() + } + } + return 1 +} + +suspend fun bar(): Int = 0