From c3ff7141ab962d2cc1621594e12d5fef79eb3a9b Mon Sep 17 00:00:00 2001 From: upnotes Date: Wed, 3 Oct 2018 17:19:32 +0200 Subject: [PATCH 1/2] Preventing duplicate field access --- .../decompiler/SimplifyExprentsHelper.java | 44 +++++++++++- .../DominatorTreeExceptionFilter.java | 5 +- .../modules/decompiler/exps/FieldExprent.java | 2 +- .../classes/pkg/TestFieldSingleAccess.class | Bin 0 -> 588 bytes testData/results/TestFieldSingleAccess.dec | 40 +++++++++++ .../results/TestMethodReferenceSameName.dec | 12 ++-- testData/src/pkg/TestFieldSingleAccess.jasm | 67 ++++++++++++++++++ 7 files changed, 159 insertions(+), 11 deletions(-) create mode 100644 testData/classes/pkg/TestFieldSingleAccess.class create mode 100644 testData/results/TestFieldSingleAccess.dec create mode 100644 testData/src/pkg/TestFieldSingleAccess.jasm 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/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/testData/classes/pkg/TestFieldSingleAccess.class b/testData/classes/pkg/TestFieldSingleAccess.class new file mode 100644 index 0000000000000000000000000000000000000000..edc30abd1f133b25e84ae96652ce8b99d37245b6 GIT binary patch literal 588 zcmZutT}vB56g{)~m}I-ws7b4-U$rg7SPX%_MFbH9L9wzy@JZHn8aHlt!|rJ5FY2=* z3O-n<;6s0s585*Zzn~1;}|nxx`_99S418YHYym9C<|n2 zvs(h$+OkO_UGKZdo*P7+P%ZhsO4IqSmwp$>F9cDb z7X@o=QXGFt@HlFFNMp|`L(xQ!i^`*O(gEuHi6dk*60U+{1MZyDaW8paQN4c&KuT>Tum zV-`$l=CS<=76Cx1ev0h&HS7~)cFrISlK)}kxIt1TA1RxpWZ0 5 -6 <-> 8 -9 <-> 12 +6 <-> 7 +9 <-> 11 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 From 95cefbcfd2988a95eabd52fbc518e6cce4041938 Mon Sep 17 00:00:00 2001 From: upnotes Date: Wed, 3 Oct 2018 17:21:01 +0200 Subject: [PATCH 2/2] Handling some cases of obfuscated exception ranges --- .../main/rels/MethodProcessorRunnable.java | 4 + .../deobfuscator/ExceptionDeobfuscator.java | 146 ++++++++- .../java/decompiler/SingleClassesTest.java | 2 + .../pkg/TestNamedSuspendFun2Kt$foo2$1.class | Bin 0 -> 1285 bytes .../classes/pkg/TestNamedSuspendFun2Kt.class | Bin 0 -> 2266 bytes testData/results/TestNamedSuspendFun2Kt.dec | 301 ++++++++++++++++++ testData/src/pkg/TestNamedSuspendFun2.kt | 14 + 7 files changed, 462 insertions(+), 5 deletions(-) create mode 100644 testData/classes/pkg/TestNamedSuspendFun2Kt$foo2$1.class create mode 100644 testData/classes/pkg/TestNamedSuspendFun2Kt.class create mode 100644 testData/results/TestNamedSuspendFun2Kt.dec create mode 100644 testData/src/pkg/TestNamedSuspendFun2.kt 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/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/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index f2b28b9..a84907e 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -113,6 +113,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"); } @@ -125,6 +126,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/TestNamedSuspendFun2Kt$foo2$1.class b/testData/classes/pkg/TestNamedSuspendFun2Kt$foo2$1.class new file mode 100644 index 0000000000000000000000000000000000000000..af894aa4864ac73cf37419aa819cb105056645d1 GIT binary patch literal 1285 zcmah|-%k@k5dQXhrKJ{WE58vzsn{0fN=5tu7!8qdv7!;u7oM)|TCTnBk=wh*$3FSj z_@IeI;)5aj<{xF8Ek(gL+O#vXGt-&bZ@&HU^UGHN1w3Y$+2hjRv6{TP8^}X0s#}35 zJonRYT(^*3j({;34cB*UVN_ht4SZX0Y1CUy!xldGgyk5Ut{`t<`L-*}=Ajcoh+)VU zEw{nDMHv<{#k$q9495~R<7K(dEB;!x*Ts-7x?atwbHD6awvdJ;gzI-|OJgT+9INc` zHHP_rrMbS!q%6H^`4&T}w^1B}hz&x+C5A8+ygO2MA$+O$M;J&5cqsCgZ6;g00L3nB9&JpUU5o`@* z(9X5DHi_yo!|F*p5j$zev;+yICp5C7behs9Se_N)32iEx(oljC{m@%#5LzKKohMmTQr~o|ToJ;pSWe0EY(=L%B^ah( z2Sns1H$}^qHo2bup&s$_@8~Pbqh~{J;6hq#*A2W1-?CN3;b)@axY8Ci0`tJF(!ER} zxVPz8QgYh)WVdLkry=shLY;8c6Hrto3yPc_z0%e9I({8VgfT$tSf%I&X^r$~Zt)25 zT<#MRsbu>IiQF;9KTv?-KFP7uBurqEPLz%*D#q}D>;cjd(hunuq5UEXBodg$RkBr& zBxza`!m9dvGE{g5*Sg^p)m9@6=pF_A^i#3F7h>&`xy#1f2sge68K@)FAu1u}jC DV{I&u literal 0 HcmV?d00001 diff --git a/testData/classes/pkg/TestNamedSuspendFun2Kt.class b/testData/classes/pkg/TestNamedSuspendFun2Kt.class new file mode 100644 index 0000000000000000000000000000000000000000..83f3b7536be54f5b4df62787a306ccaa00c12df5 GIT binary patch literal 2266 zcmah~&2v*#6#v~MZPG`8wm@qsA4P&`OCKr7htOKVkV+s;k(6SsiZ96}Jo@?`^WIBg z;YY`XYj+N^(;1m@BMUw{G9v+9IIi6LN4UT>@psd-gr*}i@11+jz31be-#Pc*KcD~d zD}Y0g0$o$m^RvdhEauAIb!ivJ%l4sjei%Za{hG00BrL-&B_{LNWP!6mfzTDlIV5nn zKQrg}mT4ynj_Z_t)0SQ$>DXk-hHpCd$UtVT{fIy;OG_ZwKah@M1ELx!JSiz;qPt=uirs0#Cdr+ZxiDy6QSNYPE`@ z9|LWOV~_~v4L6D*v}y=If2o18<@vmH!$=TvW;tzGWf{dmRg7VlZ2wd zhh@o81D)s3WjC%`A%Qlm0o#X#{2T}isQ<@xM?pqbChqG{?)$K(+~k` zv#q}&bWRnwG@_bpE5@g9qEQ(#wfCp4a6+URm587#3%qetq({B~e*?`h9DlUCjsA znST0JAVjks<&4$h!RcP_>>Zf%+5JhU$eX1z;}EfF!!=c(OerF;Im3Hmxfkj)8wqt` zZo;@;m)mk?$u@j8qQKc#ox4%J0p69$j;(+5^j;|Qa+;TEd%^Tf?&j2stB3c}+ST)h z%UjWx?n`P~&MCVEIc};K}Qx4Eh*@iIB5`0Ga8MDv=Yr!fy)-_j)$uLH`tLUmJkZ(4x za+7O=ugo<>LzVkktA|gRNdTAeDaTf#6Xs~}5A*U+$+1^?nbVTJ%BMul0xdyR0W!;I zDm!4)t9=ELr(yszC~(whrf~!x#3E^ma)z-cX89B#Tl*$)enomejSI*rGNs7HSqKl) RxHOB9hanFicxd&|@*nHr6l4GZ literal 0 HcmV?d00001 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/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