[FIR] Only track assignment nodes for uncaught exception edges in CFG
UncaughtExceptionPath edges are used to influence smart-casting within catch and finally blocks. Previously these edges were added from every node which could throw an exception. But only assignment nodes influence smart-casts by resetting inference back to some less specific type. Therefore, instead of tracking every possible node which could throw an exception - even though almost every statement node can - only add edges from assignment nodes to catch and finally blocks. This fixes many missing exception cases and also reduces the total number of incoming edges to catch and finally blocks. #KT-56872 Fixed
This commit is contained in:
@@ -97,13 +97,11 @@ digraph initializationInTry_kt {
|
||||
17 -> {18};
|
||||
18 -> {19};
|
||||
19 -> {20};
|
||||
19 -> {26} [label="onUncaughtException"];
|
||||
20 -> {21};
|
||||
20 -> {26} [label="onUncaughtException"];
|
||||
21 -> {22};
|
||||
22 -> {23};
|
||||
22 -> {26} [label="onUncaughtException"];
|
||||
23 -> {24};
|
||||
23 -> {26} [label="onUncaughtException"];
|
||||
24 -> {25};
|
||||
25 -> {26};
|
||||
26 -> {27};
|
||||
@@ -167,11 +165,10 @@ digraph initializationInTry_kt {
|
||||
40 -> {41};
|
||||
41 -> {42};
|
||||
42 -> {43};
|
||||
42 -> {48} [label="onUncaughtException"];
|
||||
43 -> {44};
|
||||
44 -> {45};
|
||||
44 -> {48} [label="onUncaughtException"];
|
||||
45 -> {46};
|
||||
45 -> {48} [label="onUncaughtException"];
|
||||
46 -> {47};
|
||||
47 -> {48};
|
||||
48 -> {49};
|
||||
|
||||
@@ -151,7 +151,7 @@ digraph complex_kt {
|
||||
23 -> {24 29};
|
||||
24 -> {25};
|
||||
25 -> {26};
|
||||
26 -> {27 29};
|
||||
26 -> {27};
|
||||
27 -> {28};
|
||||
28 -> {29 38};
|
||||
29 -> {30};
|
||||
|
||||
+2
-2
@@ -184,12 +184,12 @@ digraph inplaceLambdaInControlFlowExpressions_kt {
|
||||
45 -> {46} [style=dashed];
|
||||
46 -> {47};
|
||||
47 -> {48};
|
||||
48 -> {49 55};
|
||||
48 -> {49};
|
||||
49 -> {50};
|
||||
50 -> {51};
|
||||
51 -> {52} [color=green];
|
||||
51 -> {61} [color=red];
|
||||
52 -> {53 55};
|
||||
52 -> {53};
|
||||
53 -> {54};
|
||||
54 -> {55 61};
|
||||
55 -> {56};
|
||||
|
||||
+1
-1
@@ -333,7 +333,7 @@ digraph postponedLambdaInReturn_kt {
|
||||
88 -> {89};
|
||||
89 -> {90};
|
||||
90 -> {91} [color=green];
|
||||
91 -> {92 96};
|
||||
91 -> {92};
|
||||
92 -> {109};
|
||||
92 -> {93} [style=dotted];
|
||||
93 -> {94} [style=dotted];
|
||||
|
||||
@@ -228,11 +228,11 @@ digraph tryCatch_kt {
|
||||
subgraph cluster_32 {
|
||||
color=blue
|
||||
86 [label="Catch enter"];
|
||||
87 [label="Variable declaration: e: R|kotlin/RuntimeException|"];
|
||||
87 [label="Variable declaration: e: R|kotlin/Exception|"];
|
||||
subgraph cluster_33 {
|
||||
color=blue
|
||||
88 [label="Enter block"];
|
||||
89 [label="Jump: break@@@[Boolean(true)] "];
|
||||
89 [label="Jump: continue@@@[Boolean(true)] "];
|
||||
90 [label="Stub" style="filled" fillcolor=gray];
|
||||
91 [label="Exit block" style="filled" fillcolor=gray];
|
||||
}
|
||||
@@ -241,11 +241,11 @@ digraph tryCatch_kt {
|
||||
subgraph cluster_34 {
|
||||
color=blue
|
||||
93 [label="Catch enter"];
|
||||
94 [label="Variable declaration: e: R|kotlin/Exception|"];
|
||||
94 [label="Variable declaration: e: R|kotlin/RuntimeException|"];
|
||||
subgraph cluster_35 {
|
||||
color=blue
|
||||
95 [label="Enter block"];
|
||||
96 [label="Jump: continue@@@[Boolean(true)] "];
|
||||
96 [label="Jump: break@@@[Boolean(true)] "];
|
||||
97 [label="Stub" style="filled" fillcolor=gray];
|
||||
98 [label="Exit block" style="filled" fillcolor=gray];
|
||||
}
|
||||
@@ -297,7 +297,7 @@ digraph tryCatch_kt {
|
||||
71 -> {72};
|
||||
72 -> {73};
|
||||
73 -> {74};
|
||||
74 -> {75 86 93};
|
||||
74 -> {75};
|
||||
75 -> {76 77};
|
||||
76 -> {83};
|
||||
77 -> {78};
|
||||
@@ -313,7 +313,7 @@ digraph tryCatch_kt {
|
||||
86 -> {87};
|
||||
87 -> {88};
|
||||
88 -> {89};
|
||||
89 -> {105};
|
||||
89 -> {49} [color=green style=dashed];
|
||||
89 -> {90} [style=dotted];
|
||||
90 -> {91} [style=dotted];
|
||||
91 -> {92} [style=dotted];
|
||||
@@ -321,7 +321,7 @@ digraph tryCatch_kt {
|
||||
93 -> {94};
|
||||
94 -> {95};
|
||||
95 -> {96};
|
||||
96 -> {49} [color=green style=dashed];
|
||||
96 -> {105};
|
||||
96 -> {97} [style=dotted];
|
||||
97 -> {98} [style=dotted];
|
||||
98 -> {99} [style=dotted];
|
||||
|
||||
+1
-4
@@ -70,7 +70,6 @@ digraph variableInitializedInTryBlock_kt {
|
||||
6 -> {7};
|
||||
7 -> {8};
|
||||
8 -> {9};
|
||||
8 -> {23} [label="onUncaughtException"];
|
||||
9 -> {10 13};
|
||||
10 -> {11};
|
||||
11 -> {23} [label="return@/test"];
|
||||
@@ -80,13 +79,11 @@ digraph variableInitializedInTryBlock_kt {
|
||||
14 -> {15};
|
||||
15 -> {16};
|
||||
16 -> {17};
|
||||
16 -> {23} [label="onUncaughtException"];
|
||||
17 -> {18};
|
||||
17 -> {23} [label="onUncaughtException"];
|
||||
18 -> {19};
|
||||
19 -> {20};
|
||||
19 -> {23} [label="onUncaughtException"];
|
||||
20 -> {21};
|
||||
20 -> {23} [label="onUncaughtException"];
|
||||
21 -> {22};
|
||||
22 -> {23};
|
||||
23 -> {24};
|
||||
|
||||
-2
@@ -123,7 +123,6 @@ finally {
|
||||
25 -> {26} [color=green];
|
||||
25 -> {33} [color=red];
|
||||
26 -> {27};
|
||||
26 -> {29} [label="onUncaughtException"];
|
||||
27 -> {28};
|
||||
28 -> {29};
|
||||
29 -> {30};
|
||||
@@ -212,7 +211,6 @@ finally {
|
||||
50 -> {51} [color=green];
|
||||
50 -> {58} [color=red];
|
||||
51 -> {52};
|
||||
51 -> {54} [label="onUncaughtException"];
|
||||
52 -> {53};
|
||||
53 -> {54};
|
||||
54 -> {55};
|
||||
|
||||
+19
-18
@@ -1103,21 +1103,6 @@ class ControlFlowGraphBuilder {
|
||||
return node
|
||||
}
|
||||
|
||||
private fun addExceptionEdgesFrom(node: CFGNode<*>) {
|
||||
if (!node.canThrow) return
|
||||
|
||||
val nextCatch = catchNodes.topOrNull()
|
||||
if (!nextCatch.isNullOrEmpty() && nextCatch.first().level > levelOfNextExceptionCatchingGraph()) {
|
||||
for (catchEnterNode in nextCatch) {
|
||||
addEdge(node, catchEnterNode, propagateDeadness = false)
|
||||
}
|
||||
}
|
||||
val nextFinally = finallyEnterNodes.topOrNull()
|
||||
if (nextFinally != null && nextFinally.level > levelOfNextExceptionCatchingGraph()) {
|
||||
addEdge(node, nextFinally, propagateDeadness = false, label = UncaughtExceptionPath)
|
||||
}
|
||||
}
|
||||
|
||||
// Called-in-place function graphs are effectively inlined, exceptions go to enclosing function.
|
||||
private fun levelOfNextExceptionCatchingGraph(): Int =
|
||||
graphs.all().first { it.kind != ControlFlowGraph.Kind.AnonymousFunctionCalledInPlace }.exitNode.level
|
||||
@@ -1228,7 +1213,25 @@ class ControlFlowGraphBuilder {
|
||||
}
|
||||
|
||||
fun exitVariableAssignment(assignment: FirVariableAssignment): VariableAssignmentNode {
|
||||
return createVariableAssignmentNode(assignment).also { addNewSimpleNode(it) }
|
||||
val node = createVariableAssignmentNode(assignment).also { addNewSimpleNode(it) }
|
||||
|
||||
// Create edges from each assignment to any catch and/or finally blocks. Assignment is the only thing which can downgrade
|
||||
// smart-casting, and exceptions can be thrown almost anywhere, so it is easier to track assignment than exceptions. This makes sure
|
||||
// that any smart-cast downgrade which happens in a try block is recognized in catch and finally blocks.
|
||||
val nextCatch = catchNodes.topOrNull()
|
||||
if (!nextCatch.isNullOrEmpty()) {
|
||||
val kind = if (nextCatch.first().level > levelOfNextExceptionCatchingGraph()) EdgeKind.Forward else EdgeKind.DfgForward
|
||||
for (catchEnterNode in nextCatch) {
|
||||
addEdge(node, catchEnterNode, preferredKind = kind, propagateDeadness = false)
|
||||
}
|
||||
}
|
||||
val nextFinally = finallyEnterNodes.topOrNull()
|
||||
if (nextFinally != null) {
|
||||
val kind = if (nextFinally.level > levelOfNextExceptionCatchingGraph()) EdgeKind.Forward else EdgeKind.DfgForward
|
||||
addEdge(node, nextFinally, preferredKind = kind, propagateDeadness = false, label = UncaughtExceptionPath)
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
fun exitThrowExceptionNode(throwExpression: FirThrowExpression): ThrowExceptionNode {
|
||||
@@ -1380,7 +1383,6 @@ class ControlFlowGraphBuilder {
|
||||
private fun addNewSimpleNode(node: CFGNode<*>, isDead: Boolean = false) {
|
||||
addEdge(lastNodes.pop(), node, preferredKind = if (isDead) EdgeKind.DeadForward else EdgeKind.Forward)
|
||||
lastNodes.push(node)
|
||||
addExceptionEdgesFrom(node)
|
||||
}
|
||||
|
||||
private fun addNonSuccessfullyTerminatingNode(node: CFGNode<*>) {
|
||||
@@ -1388,7 +1390,6 @@ class ControlFlowGraphBuilder {
|
||||
val stub = createStubNode()
|
||||
addEdge(node, stub)
|
||||
lastNodes.push(stub)
|
||||
addExceptionEdgesFrom(node)
|
||||
}
|
||||
|
||||
private fun popAndAddEdge(to: CFGNode<*>, preferredKind: EdgeKind = EdgeKind.Forward) {
|
||||
|
||||
@@ -32,8 +32,6 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
|
||||
@OptIn(CfgInternals::class)
|
||||
val id = owner.nodeCount++
|
||||
|
||||
open val canThrow: Boolean get() = false
|
||||
|
||||
// a ---> b ---> d
|
||||
// \-> c -/
|
||||
// Normal CFG semantics: a, then either b or c, then d
|
||||
@@ -653,27 +651,18 @@ class BinaryOrExitNode(owner: ControlFlowGraph, override val fir: FirBinaryLogic
|
||||
// ----------------------------------- Operator call -----------------------------------
|
||||
|
||||
class TypeOperatorCallNode(owner: ControlFlowGraph, override val fir: FirTypeOperatorCall, level: Int) : CFGNode<FirTypeOperatorCall>(owner, level) {
|
||||
override val canThrow: Boolean
|
||||
get() = fir.operation == FirOperation.AS
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitTypeOperatorCallNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class ComparisonExpressionNode(owner: ControlFlowGraph, override val fir: FirComparisonExpression, level: Int) : CFGNode<FirComparisonExpression>(owner, level) {
|
||||
override val canThrow: Boolean
|
||||
get() = true // TODO? only overridden compareTo
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitComparisonExpressionNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class EqualityOperatorCallNode(owner: ControlFlowGraph, override val fir: FirEqualityOperatorCall, level: Int) : AbstractBinaryExitNode<FirEqualityOperatorCall>(owner, level) {
|
||||
override val canThrow: Boolean
|
||||
get() = true // TODO? only overridden equals
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitEqualityOperatorCallNode(this, data)
|
||||
}
|
||||
@@ -696,9 +685,6 @@ class ConstExpressionNode(owner: ControlFlowGraph, override val fir: FirConstExp
|
||||
|
||||
class CheckNotNullCallNode(owner: ControlFlowGraph, override val fir: FirCheckNotNullCall, level: Int)
|
||||
: CFGNode<FirCheckNotNullCall>(owner, level) {
|
||||
override val canThrow: Boolean
|
||||
get() = true
|
||||
|
||||
override val isUnion: Boolean
|
||||
get() = true
|
||||
|
||||
@@ -714,9 +700,6 @@ class QualifiedAccessNode(
|
||||
override val fir: FirQualifiedAccessExpression,
|
||||
level: Int
|
||||
) : CFGNode<FirQualifiedAccessExpression>(owner, level) {
|
||||
override val canThrow: Boolean
|
||||
get() = fir.toResolvedCallableSymbol() is FirPropertySymbol
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitQualifiedAccessNode(this, data)
|
||||
}
|
||||
@@ -734,10 +717,6 @@ class ResolvedQualifierNode(
|
||||
|
||||
class FunctionCallNode(owner: ControlFlowGraph, override val fir: FirFunctionCall, level: Int)
|
||||
: CFGNode<FirFunctionCall>(owner, level) {
|
||||
|
||||
override val canThrow: Boolean
|
||||
get() = true
|
||||
|
||||
override val isUnion: Boolean
|
||||
get() = true
|
||||
|
||||
@@ -768,9 +747,6 @@ class DelegatedConstructorCallNode(owner: ControlFlowGraph, override val fir: Fi
|
||||
override val isUnion: Boolean
|
||||
get() = true
|
||||
|
||||
override val canThrow: Boolean
|
||||
get() = true // shouldn't matter since delegated constructor calls aren't wrapped in try-finally, but still
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitDelegatedConstructorCallNode(this, data)
|
||||
}
|
||||
@@ -792,9 +768,6 @@ class ThrowExceptionNode(
|
||||
override val fir: FirThrowExpression,
|
||||
level: Int
|
||||
) : CFGNode<FirThrowExpression>(owner, level) {
|
||||
override val canThrow: Boolean
|
||||
get() = true
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitThrowExceptionNode(this, data)
|
||||
}
|
||||
@@ -910,9 +883,6 @@ class FakeExpressionEnterNode(owner: ControlFlowGraph, level: Int) : CFGNode<Fir
|
||||
// ----------------------------------- Smart-cast node -----------------------------------
|
||||
|
||||
class SmartCastExpressionExitNode(owner: ControlFlowGraph, override val fir: FirSmartCastExpression, level: Int) : CFGNode<FirSmartCastExpression>(owner, level) {
|
||||
override val canThrow: Boolean
|
||||
get() = fir.resolvedType.isNothing
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitSmartCastExpressionExitNode(this, data)
|
||||
}
|
||||
|
||||
Vendored
+1
-3
@@ -206,7 +206,6 @@ digraph singleReturnFromTry_kt {
|
||||
54 -> {55} [color=green];
|
||||
54 -> {63} [style=dotted];
|
||||
55 -> {56} [style=dotted];
|
||||
56 -> {59} [style=dotted label="onUncaughtException"];
|
||||
56 -> {57} [style=dotted];
|
||||
57 -> {58} [style=dotted];
|
||||
58 -> {59} [style=dotted];
|
||||
@@ -384,8 +383,7 @@ digraph singleReturnFromTry_kt {
|
||||
104 -> {105} [color=green];
|
||||
104 -> {119} [color=red];
|
||||
105 -> {106} [style=dotted];
|
||||
106 -> {115} [style=dotted label="onUncaughtException"];
|
||||
106 -> {107 109} [style=dotted];
|
||||
106 -> {107} [style=dotted];
|
||||
107 -> {108} [style=dotted];
|
||||
108 -> {109 115} [style=dotted];
|
||||
109 -> {110};
|
||||
|
||||
+502
@@ -0,0 +1,502 @@
|
||||
digraph smartCastInCatch_fir_kt {
|
||||
graph [nodesep=3]
|
||||
node [shape=box penwidth=2]
|
||||
edge [penwidth=2]
|
||||
|
||||
subgraph cluster_0 {
|
||||
color=red
|
||||
0 [label="Enter file smartCastInCatch.fir.kt" style="filled" fillcolor=red];
|
||||
1 [label="Exit file smartCastInCatch.fir.kt" style="filled" fillcolor=red];
|
||||
}
|
||||
0 -> {1} [color=green];
|
||||
|
||||
subgraph cluster_1 {
|
||||
color=red
|
||||
2 [label="Enter function exc" style="filled" fillcolor=red];
|
||||
subgraph cluster_2 {
|
||||
color=blue
|
||||
3 [label="Enter block"];
|
||||
subgraph cluster_3 {
|
||||
color=blue
|
||||
4 [label="Enter when"];
|
||||
subgraph cluster_4 {
|
||||
color=blue
|
||||
5 [label="Enter when branch condition "];
|
||||
6 [label="Access variable R|<local>/flag|"];
|
||||
7 [label="Exit when branch condition"];
|
||||
}
|
||||
8 [label="Synthetic else branch"];
|
||||
9 [label="Enter when branch result"];
|
||||
subgraph cluster_5 {
|
||||
color=blue
|
||||
10 [label="Enter block"];
|
||||
11 [label="Function call: R|java/lang/RuntimeException.RuntimeException|()" style="filled" fillcolor=yellow];
|
||||
12 [label="Throw: throw R|java/lang/RuntimeException.RuntimeException|()"];
|
||||
13 [label="Stub" style="filled" fillcolor=gray];
|
||||
14 [label="Exit block" style="filled" fillcolor=gray];
|
||||
}
|
||||
15 [label="Exit when branch result" style="filled" fillcolor=gray];
|
||||
16 [label="Exit when"];
|
||||
}
|
||||
17 [label="Exit block"];
|
||||
}
|
||||
18 [label="Exit function exc" style="filled" fillcolor=red];
|
||||
}
|
||||
2 -> {3};
|
||||
3 -> {4};
|
||||
4 -> {5};
|
||||
5 -> {6};
|
||||
6 -> {7};
|
||||
7 -> {8 9};
|
||||
8 -> {16};
|
||||
9 -> {10};
|
||||
10 -> {11};
|
||||
11 -> {12};
|
||||
12 -> {13} [style=dotted];
|
||||
13 -> {14} [style=dotted];
|
||||
14 -> {15} [style=dotted];
|
||||
15 -> {16} [style=dotted];
|
||||
16 -> {17};
|
||||
17 -> {18};
|
||||
|
||||
subgraph cluster_6 {
|
||||
color=red
|
||||
19 [label="Enter function notNull" style="filled" fillcolor=red];
|
||||
subgraph cluster_7 {
|
||||
color=blue
|
||||
20 [label="Enter block"];
|
||||
21 [label="Function call: this@R|/notNull|.R|kotlin/Any.toString|()" style="filled" fillcolor=yellow];
|
||||
22 [label="Jump: ^notNull this@R|/notNull|.R|kotlin/Any.toString|()"];
|
||||
23 [label="Stub" style="filled" fillcolor=gray];
|
||||
24 [label="Exit block" style="filled" fillcolor=gray];
|
||||
}
|
||||
25 [label="Exit function notNull" style="filled" fillcolor=red];
|
||||
}
|
||||
19 -> {20};
|
||||
20 -> {21};
|
||||
21 -> {22};
|
||||
22 -> {25};
|
||||
22 -> {23} [style=dotted];
|
||||
23 -> {24} [style=dotted];
|
||||
24 -> {25} [style=dotted];
|
||||
|
||||
subgraph cluster_8 {
|
||||
color=red
|
||||
26 [label="Enter function test" style="filled" fillcolor=red];
|
||||
subgraph cluster_9 {
|
||||
color=blue
|
||||
27 [label="Enter block"];
|
||||
28 [label="Variable declaration: lvar x: R|kotlin/Any?|"];
|
||||
29 [label="Const: String()"];
|
||||
30 [label="Assignment: R|<local>/x|"];
|
||||
subgraph cluster_10 {
|
||||
color=blue
|
||||
31 [label="Try expression enter"];
|
||||
subgraph cluster_11 {
|
||||
color=blue
|
||||
32 [label="Try main block enter"];
|
||||
subgraph cluster_12 {
|
||||
color=blue
|
||||
33 [label="Enter block"];
|
||||
34 [label="Const: Null(null)"];
|
||||
35 [label="Assignment: R|<local>/x|"];
|
||||
36 [label="Access variable R|<local>/flag|"];
|
||||
37 [label="Function call: R|/exc|(...)" style="filled" fillcolor=yellow];
|
||||
38 [label="Const: Int(1)"];
|
||||
39 [label="Assignment: R|<local>/x|"];
|
||||
40 [label="Access variable R|<local>/flag|"];
|
||||
41 [label="Function call: R|<local>/flag|.R|kotlin/Boolean.not|()" style="filled" fillcolor=yellow];
|
||||
42 [label="Function call: R|/exc|(...)" style="filled" fillcolor=yellow];
|
||||
43 [label="Const: String()"];
|
||||
44 [label="Assignment: R|<local>/x|"];
|
||||
45 [label="Exit block"];
|
||||
}
|
||||
46 [label="Try main block exit"];
|
||||
}
|
||||
subgraph cluster_13 {
|
||||
color=blue
|
||||
47 [label="Catch enter"];
|
||||
48 [label="Variable declaration: e: R|kotlin/Throwable|"];
|
||||
subgraph cluster_14 {
|
||||
color=blue
|
||||
49 [label="Enter block"];
|
||||
50 [label="Access variable R|<local>/x|"];
|
||||
51 [label="Smart cast: R|<local>/x|"];
|
||||
52 [label="Access variable <Unresolved name: length>#"];
|
||||
53 [label="Access variable R|<local>/x|"];
|
||||
54 [label="Smart cast: R|<local>/x|"];
|
||||
55 [label="Function call: R|<local>/x|.<Unresolved name: inc>#()" style="filled" fillcolor=yellow];
|
||||
56 [label="Exit block"];
|
||||
}
|
||||
57 [label="Catch exit"];
|
||||
}
|
||||
58 [label="Try expression exit"];
|
||||
}
|
||||
59 [label="Exit block"];
|
||||
}
|
||||
60 [label="Exit function test" style="filled" fillcolor=red];
|
||||
}
|
||||
26 -> {27};
|
||||
27 -> {28};
|
||||
28 -> {29};
|
||||
29 -> {30};
|
||||
30 -> {31};
|
||||
31 -> {32 47};
|
||||
32 -> {33};
|
||||
33 -> {34};
|
||||
34 -> {35};
|
||||
35 -> {36 47};
|
||||
36 -> {37};
|
||||
37 -> {38};
|
||||
38 -> {39};
|
||||
39 -> {40 47};
|
||||
40 -> {41};
|
||||
41 -> {42};
|
||||
42 -> {43};
|
||||
43 -> {44};
|
||||
44 -> {45 47};
|
||||
45 -> {46};
|
||||
46 -> {47 58};
|
||||
47 -> {48};
|
||||
48 -> {49};
|
||||
49 -> {50};
|
||||
50 -> {51};
|
||||
51 -> {52};
|
||||
52 -> {53};
|
||||
53 -> {54};
|
||||
54 -> {55};
|
||||
55 -> {56};
|
||||
56 -> {57};
|
||||
57 -> {58};
|
||||
58 -> {59};
|
||||
59 -> {60};
|
||||
|
||||
subgraph cluster_15 {
|
||||
color=red
|
||||
61 [label="Enter function testGetClassThrows" style="filled" fillcolor=red];
|
||||
subgraph cluster_16 {
|
||||
color=blue
|
||||
62 [label="Enter block"];
|
||||
63 [label="Access qualifier kotlin/String"];
|
||||
64 [label="::class call"];
|
||||
65 [label="Variable declaration: lvar x: R|kotlin/reflect/KClass<kotlin/String>?|"];
|
||||
66 [label="Access variable R|<local>/x|"];
|
||||
67 [label="Type operator: (R|<local>/x| as R|kotlin/reflect/KClass<kotlin/String>|)"];
|
||||
subgraph cluster_17 {
|
||||
color=blue
|
||||
68 [label="Try expression enter"];
|
||||
subgraph cluster_18 {
|
||||
color=blue
|
||||
69 [label="Try main block enter"];
|
||||
subgraph cluster_19 {
|
||||
color=blue
|
||||
70 [label="Enter block"];
|
||||
71 [label="Const: Null(null)"];
|
||||
72 [label="Assignment: R|<local>/x|"];
|
||||
73 [label="Access qualifier kotlin/String"];
|
||||
74 [label="::class call"];
|
||||
75 [label="Assignment: R|<local>/x|"];
|
||||
76 [label="Exit block"];
|
||||
}
|
||||
77 [label="Try main block exit"];
|
||||
}
|
||||
subgraph cluster_20 {
|
||||
color=blue
|
||||
78 [label="Catch enter"];
|
||||
79 [label="Variable declaration: e: R|kotlin/Throwable|"];
|
||||
subgraph cluster_21 {
|
||||
color=blue
|
||||
80 [label="Enter block"];
|
||||
81 [label="Access variable R|<local>/x|"];
|
||||
82 [label="Function call: R|<local>/x|.R|/notNull<Inapplicable(UNSAFE_CALL): /notNull>#|()" style="filled" fillcolor=yellow];
|
||||
83 [label="Exit block"];
|
||||
}
|
||||
84 [label="Catch exit"];
|
||||
}
|
||||
85 [label="Try expression exit"];
|
||||
}
|
||||
86 [label="Exit block"];
|
||||
}
|
||||
87 [label="Exit function testGetClassThrows" style="filled" fillcolor=red];
|
||||
}
|
||||
61 -> {62};
|
||||
62 -> {63};
|
||||
63 -> {64};
|
||||
64 -> {65};
|
||||
65 -> {66};
|
||||
66 -> {67};
|
||||
67 -> {68};
|
||||
68 -> {69 78};
|
||||
69 -> {70};
|
||||
70 -> {71};
|
||||
71 -> {72};
|
||||
72 -> {73 78};
|
||||
73 -> {74};
|
||||
74 -> {75};
|
||||
75 -> {76 78};
|
||||
76 -> {77};
|
||||
77 -> {78 85};
|
||||
78 -> {79};
|
||||
79 -> {80};
|
||||
80 -> {81};
|
||||
81 -> {82};
|
||||
82 -> {83};
|
||||
83 -> {84};
|
||||
84 -> {85};
|
||||
85 -> {86};
|
||||
86 -> {87};
|
||||
|
||||
subgraph cluster_22 {
|
||||
color=red
|
||||
88 [label="Enter function testMemberReferenceThrows" style="filled" fillcolor=red];
|
||||
subgraph cluster_23 {
|
||||
color=blue
|
||||
89 [label="Enter block"];
|
||||
90 [label="Const: String()"];
|
||||
91 [label="Variable declaration: lvar x: R|kotlin/Any?|"];
|
||||
92 [label="Access variable R|<local>/x|"];
|
||||
93 [label="Type operator: (R|<local>/x| as R|kotlin/Any|)"];
|
||||
subgraph cluster_24 {
|
||||
color=blue
|
||||
94 [label="Try expression enter"];
|
||||
subgraph cluster_25 {
|
||||
color=blue
|
||||
95 [label="Try main block enter"];
|
||||
subgraph cluster_26 {
|
||||
color=blue
|
||||
96 [label="Enter block"];
|
||||
97 [label="Const: Null(null)"];
|
||||
98 [label="Assignment: R|<local>/x|"];
|
||||
99 [label="Access qualifier kotlin/String"];
|
||||
100 [label="Callable reference: Q|kotlin/String|::R|kotlin/String.length|"];
|
||||
101 [label="Assignment: R|<local>/x|"];
|
||||
102 [label="Exit block"];
|
||||
}
|
||||
103 [label="Try main block exit"];
|
||||
}
|
||||
subgraph cluster_27 {
|
||||
color=blue
|
||||
104 [label="Catch enter"];
|
||||
105 [label="Variable declaration: ex: R|kotlin/Throwable|"];
|
||||
subgraph cluster_28 {
|
||||
color=blue
|
||||
106 [label="Enter block"];
|
||||
107 [label="Access variable R|<local>/x|"];
|
||||
108 [label="Function call: R|<local>/x|.R|/notNull<Inapplicable(UNSAFE_CALL): /notNull>#|()" style="filled" fillcolor=yellow];
|
||||
109 [label="Exit block"];
|
||||
}
|
||||
110 [label="Catch exit"];
|
||||
}
|
||||
111 [label="Try expression exit"];
|
||||
}
|
||||
112 [label="Exit block"];
|
||||
}
|
||||
113 [label="Exit function testMemberReferenceThrows" style="filled" fillcolor=red];
|
||||
}
|
||||
88 -> {89};
|
||||
89 -> {90};
|
||||
90 -> {91};
|
||||
91 -> {92};
|
||||
92 -> {93};
|
||||
93 -> {94};
|
||||
94 -> {95 104};
|
||||
95 -> {96};
|
||||
96 -> {97};
|
||||
97 -> {98};
|
||||
98 -> {99 104};
|
||||
99 -> {100};
|
||||
100 -> {101};
|
||||
101 -> {102 104};
|
||||
102 -> {103};
|
||||
103 -> {104 111};
|
||||
104 -> {105};
|
||||
105 -> {106};
|
||||
106 -> {107};
|
||||
107 -> {108};
|
||||
108 -> {109};
|
||||
109 -> {110};
|
||||
110 -> {111};
|
||||
111 -> {112};
|
||||
112 -> {113};
|
||||
|
||||
subgraph cluster_29 {
|
||||
color=red
|
||||
114 [label="Enter function testExceptionBeforeLambda" style="filled" fillcolor=red];
|
||||
subgraph cluster_30 {
|
||||
color=blue
|
||||
115 [label="Enter block"];
|
||||
116 [label="Const: String()"];
|
||||
117 [label="Variable declaration: lvar x: R|kotlin/String?|"];
|
||||
118 [label="Access variable R|<local>/x|"];
|
||||
119 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
|
||||
subgraph cluster_31 {
|
||||
color=blue
|
||||
120 [label="Try expression enter"];
|
||||
subgraph cluster_32 {
|
||||
color=blue
|
||||
121 [label="Try main block enter"];
|
||||
subgraph cluster_33 {
|
||||
color=blue
|
||||
122 [label="Enter block"];
|
||||
123 [label="Const: Null(null)"];
|
||||
124 [label="Assignment: R|<local>/x|"];
|
||||
125 [label="Postponed enter to lambda"];
|
||||
subgraph cluster_34 {
|
||||
color=blue
|
||||
126 [label="Enter function <anonymous>" style="filled" fillcolor=red];
|
||||
subgraph cluster_35 {
|
||||
color=blue
|
||||
127 [label="Enter block"];
|
||||
128 [label="Const: String()"];
|
||||
129 [label="Assignment: R|<local>/x|"];
|
||||
130 [label="Exit block"];
|
||||
}
|
||||
131 [label="Exit function <anonymous>" style="filled" fillcolor=red];
|
||||
}
|
||||
132 [label="Postponed exit from lambda"];
|
||||
133 [label="Function call: R|kotlin/run|<R|kotlin/Unit|>(...)" style="filled" fillcolor=yellow];
|
||||
134 [label="Exit block"];
|
||||
}
|
||||
135 [label="Try main block exit"];
|
||||
}
|
||||
subgraph cluster_36 {
|
||||
color=blue
|
||||
136 [label="Catch enter"];
|
||||
137 [label="Variable declaration: ex: R|kotlin/Throwable|"];
|
||||
subgraph cluster_37 {
|
||||
color=blue
|
||||
138 [label="Enter block"];
|
||||
139 [label="Access variable R|<local>/x|"];
|
||||
140 [label="Function call: R|<local>/x|.R|/notNull<Inapplicable(UNSAFE_CALL): /notNull>#|()" style="filled" fillcolor=yellow];
|
||||
141 [label="Exit block"];
|
||||
}
|
||||
142 [label="Catch exit"];
|
||||
}
|
||||
143 [label="Try expression exit"];
|
||||
}
|
||||
144 [label="Exit block"];
|
||||
}
|
||||
145 [label="Exit function testExceptionBeforeLambda" style="filled" fillcolor=red];
|
||||
}
|
||||
114 -> {115};
|
||||
115 -> {116};
|
||||
116 -> {117};
|
||||
117 -> {118};
|
||||
118 -> {119};
|
||||
119 -> {120};
|
||||
120 -> {121 136};
|
||||
121 -> {122};
|
||||
122 -> {123};
|
||||
123 -> {124};
|
||||
124 -> {125 136};
|
||||
125 -> {126 133};
|
||||
125 -> {132} [style=dotted];
|
||||
125 -> {126} [style=dashed];
|
||||
126 -> {127};
|
||||
127 -> {128};
|
||||
128 -> {129};
|
||||
129 -> {130 136};
|
||||
130 -> {131};
|
||||
131 -> {132};
|
||||
132 -> {133} [color=green];
|
||||
132 -> {143} [color=red];
|
||||
133 -> {134};
|
||||
134 -> {135};
|
||||
135 -> {136 143};
|
||||
136 -> {137};
|
||||
137 -> {138};
|
||||
138 -> {139};
|
||||
139 -> {140};
|
||||
140 -> {141};
|
||||
141 -> {142};
|
||||
142 -> {143};
|
||||
143 -> {144};
|
||||
144 -> {145};
|
||||
|
||||
subgraph cluster_38 {
|
||||
color=red
|
||||
146 [label="Enter function testExceptionWithinLocalFunction" style="filled" fillcolor=red];
|
||||
subgraph cluster_39 {
|
||||
color=blue
|
||||
147 [label="Enter block"];
|
||||
148 [label="Const: String()"];
|
||||
149 [label="Variable declaration: lvar x: R|kotlin/Any|"];
|
||||
150 [label="Access variable R|<local>/x|"];
|
||||
151 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
|
||||
subgraph cluster_40 {
|
||||
color=blue
|
||||
152 [label="Try expression enter"];
|
||||
subgraph cluster_41 {
|
||||
color=blue
|
||||
153 [label="Try main block enter"];
|
||||
subgraph cluster_42 {
|
||||
color=blue
|
||||
154 [label="Enter block"];
|
||||
155 [label="Local function declaration"];
|
||||
156 [label="Exit block"];
|
||||
}
|
||||
157 [label="Try main block exit"];
|
||||
}
|
||||
subgraph cluster_43 {
|
||||
color=blue
|
||||
158 [label="Enter function local" style="filled" fillcolor=red];
|
||||
subgraph cluster_44 {
|
||||
color=blue
|
||||
159 [label="Enter block"];
|
||||
160 [label="Const: Int(1)"];
|
||||
161 [label="Assignment: R|<local>/x|"];
|
||||
162 [label="Exit block"];
|
||||
}
|
||||
163 [label="Exit function local" style="filled" fillcolor=red];
|
||||
}
|
||||
subgraph cluster_45 {
|
||||
color=blue
|
||||
164 [label="Catch enter"];
|
||||
165 [label="Variable declaration: e: R|kotlin/Exception|"];
|
||||
subgraph cluster_46 {
|
||||
color=blue
|
||||
166 [label="Enter block"];
|
||||
167 [label="Access variable R|<local>/x|"];
|
||||
168 [label="Smart cast: R|<local>/x|"];
|
||||
169 [label="Access variable <Unresolved name: length>#"];
|
||||
170 [label="Exit block"];
|
||||
}
|
||||
171 [label="Catch exit"];
|
||||
}
|
||||
172 [label="Try expression exit"];
|
||||
}
|
||||
173 [label="Exit block"];
|
||||
}
|
||||
174 [label="Exit function testExceptionWithinLocalFunction" style="filled" fillcolor=red];
|
||||
}
|
||||
146 -> {147};
|
||||
147 -> {148};
|
||||
148 -> {149};
|
||||
149 -> {150};
|
||||
150 -> {151};
|
||||
151 -> {152};
|
||||
152 -> {153 164};
|
||||
153 -> {154};
|
||||
154 -> {155};
|
||||
155 -> {156 158};
|
||||
155 -> {158} [style=dashed];
|
||||
156 -> {157};
|
||||
157 -> {164 172};
|
||||
158 -> {159};
|
||||
159 -> {160};
|
||||
160 -> {161};
|
||||
161 -> {162};
|
||||
161 -> {164} [color=red];
|
||||
162 -> {163};
|
||||
164 -> {165};
|
||||
165 -> {166};
|
||||
166 -> {167};
|
||||
167 -> {168};
|
||||
168 -> {169};
|
||||
169 -> {170};
|
||||
170 -> {171};
|
||||
171 -> {172};
|
||||
172 -> {173};
|
||||
173 -> {174};
|
||||
|
||||
}
|
||||
+54
@@ -1,9 +1,14 @@
|
||||
// !SKIP_TXT
|
||||
// DUMP_CFG
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
fun exc(flag: Boolean) {
|
||||
if (flag) throw RuntimeException()
|
||||
}
|
||||
|
||||
fun Any.notNull() = toString()
|
||||
|
||||
fun test(flag: Boolean) {
|
||||
var x: Any?
|
||||
x = ""
|
||||
@@ -19,3 +24,52 @@ fun test(flag: Boolean) {
|
||||
x.<!UNRESOLVED_REFERENCE!>inc<!>()
|
||||
}
|
||||
}
|
||||
|
||||
fun testGetClassThrows() {
|
||||
var x: KClass<String>? = String::class
|
||||
x as KClass<String>
|
||||
try {
|
||||
x = null
|
||||
x = String::class
|
||||
} catch (e: Throwable) {
|
||||
// bad - get class call can throw
|
||||
x<!UNSAFE_CALL!>.<!>notNull()
|
||||
}
|
||||
}
|
||||
|
||||
fun testMemberReferenceThrows() {
|
||||
var x: Any? = ""
|
||||
x as Any
|
||||
try {
|
||||
x = null
|
||||
x = String::length
|
||||
} catch (ex: Throwable) {
|
||||
// bad - get callable reference throw
|
||||
x<!UNSAFE_CALL!>.<!>notNull()
|
||||
}
|
||||
}
|
||||
|
||||
fun testExceptionBeforeLambda() {
|
||||
var x: String? = ""
|
||||
x as String
|
||||
try {
|
||||
x = null
|
||||
run { x = "" }
|
||||
} catch (ex: Throwable) {
|
||||
// bad - `run` could throw before running lambda
|
||||
x<!UNSAFE_CALL!>.<!>notNull()
|
||||
}
|
||||
}
|
||||
|
||||
fun testExceptionWithinLocalFunction() {
|
||||
var x: Any = ""
|
||||
x as String
|
||||
try {
|
||||
fun local() {
|
||||
x = 1
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// bad - `local` could be run and reset smartcasting
|
||||
x.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
// !SKIP_TXT
|
||||
// DUMP_CFG
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
fun exc(flag: Boolean) {
|
||||
if (flag) throw RuntimeException()
|
||||
}
|
||||
|
||||
fun Any.notNull() = toString()
|
||||
|
||||
fun test(flag: Boolean) {
|
||||
var x: Any?
|
||||
x = ""
|
||||
@@ -19,3 +24,52 @@ fun test(flag: Boolean) {
|
||||
x.<!UNRESOLVED_REFERENCE!>inc<!>()
|
||||
}
|
||||
}
|
||||
|
||||
fun testGetClassThrows() {
|
||||
var x: KClass<String>? = String::class
|
||||
x as KClass<String>
|
||||
try {
|
||||
<!UNUSED_VALUE!>x =<!> null
|
||||
x = String::class
|
||||
} catch (e: Throwable) {
|
||||
// bad - get class call can throw
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.notNull()
|
||||
}
|
||||
}
|
||||
|
||||
fun testMemberReferenceThrows() {
|
||||
var x: Any? = ""
|
||||
x as Any
|
||||
try {
|
||||
<!UNUSED_VALUE!>x =<!> null
|
||||
x = String::length
|
||||
} catch (ex: Throwable) {
|
||||
// bad - get callable reference throw
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.notNull()
|
||||
}
|
||||
}
|
||||
|
||||
fun testExceptionBeforeLambda() {
|
||||
var x: String? = ""
|
||||
x as String
|
||||
try {
|
||||
x = null
|
||||
run { x = "" }
|
||||
} catch (ex: Throwable) {
|
||||
// bad - `run` could throw before running lambda
|
||||
<!SMARTCAST_IMPOSSIBLE!>x<!>.notNull()
|
||||
}
|
||||
}
|
||||
|
||||
fun testExceptionWithinLocalFunction() {
|
||||
var x: Any = ""
|
||||
x as String
|
||||
try {
|
||||
fun local() {
|
||||
x = 1
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// bad - `local` could be run and reset smartcasting
|
||||
<!SMARTCAST_IMPOSSIBLE!>x<!>.length
|
||||
}
|
||||
}
|
||||
|
||||
+6
-32
@@ -179,9 +179,7 @@ digraph castInTryWithJump_fir_kt {
|
||||
25 -> {26};
|
||||
26 -> {27};
|
||||
27 -> {28};
|
||||
27 -> {33} [label="onUncaughtException"];
|
||||
28 -> {29};
|
||||
28 -> {33} [label="onUncaughtException"];
|
||||
29 -> {33} [label="break"];
|
||||
29 -> {30} [style=dotted];
|
||||
30 -> {31} [style=dotted];
|
||||
@@ -194,9 +192,7 @@ digraph castInTryWithJump_fir_kt {
|
||||
36 -> {37};
|
||||
37 -> {38};
|
||||
38 -> {39};
|
||||
38 -> {42} [label="onUncaughtException"];
|
||||
39 -> {40};
|
||||
39 -> {42} [label="onUncaughtException"];
|
||||
40 -> {41};
|
||||
41 -> {42};
|
||||
42 -> {43};
|
||||
@@ -307,10 +303,8 @@ digraph castInTryWithJump_fir_kt {
|
||||
81 -> {97} [label="onUncaughtException"];
|
||||
82 -> {83};
|
||||
83 -> {84};
|
||||
84 -> {85 88};
|
||||
84 -> {97} [label="onUncaughtException"];
|
||||
85 -> {86 88};
|
||||
85 -> {97} [label="onUncaughtException"];
|
||||
84 -> {85};
|
||||
85 -> {86};
|
||||
86 -> {87};
|
||||
87 -> {88 97};
|
||||
88 -> {89};
|
||||
@@ -318,9 +312,7 @@ digraph castInTryWithJump_fir_kt {
|
||||
89 -> {90};
|
||||
90 -> {91};
|
||||
91 -> {92};
|
||||
91 -> {97} [label="onUncaughtException"];
|
||||
92 -> {93};
|
||||
92 -> {97} [label="onUncaughtException"];
|
||||
93 -> {97} [label="return@/returnInCatch"];
|
||||
93 -> {94} [style=dotted];
|
||||
94 -> {95} [style=dotted];
|
||||
@@ -458,9 +450,7 @@ digraph castInTryWithJump_fir_kt {
|
||||
119 -> {120};
|
||||
120 -> {121};
|
||||
121 -> {122};
|
||||
121 -> {125} [label="onUncaughtException"];
|
||||
122 -> {123};
|
||||
122 -> {125} [label="onUncaughtException"];
|
||||
123 -> {124};
|
||||
124 -> {125};
|
||||
125 -> {126};
|
||||
@@ -469,10 +459,8 @@ digraph castInTryWithJump_fir_kt {
|
||||
127 -> {143} [label="onUncaughtException"];
|
||||
128 -> {129};
|
||||
129 -> {130};
|
||||
130 -> {131 134};
|
||||
130 -> {143} [label="onUncaughtException"];
|
||||
131 -> {132 134};
|
||||
131 -> {143} [label="onUncaughtException"];
|
||||
130 -> {131};
|
||||
131 -> {132};
|
||||
132 -> {133};
|
||||
133 -> {134 143};
|
||||
134 -> {135};
|
||||
@@ -480,9 +468,7 @@ digraph castInTryWithJump_fir_kt {
|
||||
135 -> {136};
|
||||
136 -> {137};
|
||||
137 -> {138};
|
||||
137 -> {143} [label="onUncaughtException"];
|
||||
138 -> {139};
|
||||
138 -> {143} [label="onUncaughtException"];
|
||||
139 -> {143} [label="return@/returnInCatch_insideFinally"];
|
||||
139 -> {140} [style=dotted];
|
||||
140 -> {141} [style=dotted];
|
||||
@@ -629,10 +615,8 @@ digraph castInTryWithJump_fir_kt {
|
||||
187 -> {203} [label="onUncaughtException"];
|
||||
188 -> {189};
|
||||
189 -> {190};
|
||||
190 -> {191 194};
|
||||
190 -> {203} [label="onUncaughtException"];
|
||||
191 -> {192 194};
|
||||
191 -> {203} [label="onUncaughtException"];
|
||||
190 -> {191};
|
||||
191 -> {192};
|
||||
192 -> {193};
|
||||
193 -> {194 203};
|
||||
194 -> {195};
|
||||
@@ -640,9 +624,7 @@ digraph castInTryWithJump_fir_kt {
|
||||
195 -> {196};
|
||||
196 -> {197};
|
||||
197 -> {198};
|
||||
197 -> {203} [label="onUncaughtException"];
|
||||
198 -> {199};
|
||||
198 -> {203} [label="onUncaughtException"];
|
||||
199 -> {203} [label="break"];
|
||||
199 -> {200} [style=dotted];
|
||||
200 -> {201} [style=dotted];
|
||||
@@ -789,32 +771,24 @@ digraph castInTryWithJump_fir_kt {
|
||||
237 -> {238};
|
||||
238 -> {239};
|
||||
239 -> {240};
|
||||
239 -> {264} [label="onUncaughtException"];
|
||||
240 -> {241};
|
||||
240 -> {264} [label="onUncaughtException"];
|
||||
241 -> {242};
|
||||
241 -> {249} [label="onUncaughtException"];
|
||||
242 -> {243};
|
||||
243 -> {244};
|
||||
244 -> {245};
|
||||
244 -> {249} [label="onUncaughtException"];
|
||||
245 -> {246};
|
||||
246 -> {247};
|
||||
246 -> {249} [label="onUncaughtException"];
|
||||
247 -> {248};
|
||||
248 -> {249};
|
||||
249 -> {250};
|
||||
250 -> {251};
|
||||
251 -> {252};
|
||||
251 -> {264} [label="onUncaughtException"];
|
||||
252 -> {253};
|
||||
253 -> {254};
|
||||
253 -> {264} [label="onUncaughtException"];
|
||||
254 -> {255};
|
||||
254 -> {264} [label="onUncaughtException"];
|
||||
255 -> {256};
|
||||
256 -> {257};
|
||||
256 -> {264} [label="onUncaughtException"];
|
||||
257 -> {264} [label="return@/returnInFinally_insideTry_nonLocal"];
|
||||
257 -> {258} [style=dotted];
|
||||
258 -> {259} [style=dotted];
|
||||
|
||||
Reference in New Issue
Block a user