[FIR] Add CallsInPlace contract analyzer

This commit is contained in:
Oleg Ivanov
2020-07-30 17:13:11 +03:00
parent cc9c5b9e3c
commit 4367d6631f
39 changed files with 4021 additions and 2064 deletions
@@ -0,0 +1,119 @@
digraph inAnonymousObject_kt {
graph [nodesep=3]
node [shape=box penwidth=2]
edge [penwidth=2]
subgraph cluster_0 {
color=red
0 [label="Enter function foo" style="filled" fillcolor=red];
subgraph cluster_1 {
color=blue
1 [label="Enter block"];
2 [label="Exit anonymous object"];
3 [label="Variable declaration: lval obj: R|<anonymous>|"];
4 [label="Access variable R|<local>/obj|"];
5 [label="Function call: R|<local>/obj|.R|/<anonymous>.run|()"];
6 [label="Function call: R|<local>/d|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
7 [label="Exit block"];
}
8 [label="Exit function foo" style="filled" fillcolor=red];
}
subgraph cluster_2 {
color=blue
9 [label="Enter class <anonymous object>" style="filled" fillcolor=red];
subgraph cluster_3 {
color=blue
10 [label="Part of class initialization"];
subgraph cluster_4 {
color=blue
11 [label="Part of class initialization"];
12 [label="Exit class <anonymous object>" style="filled" fillcolor=red];
}
0 -> {1};
1 -> {2};
1 -> {13 18 16 21 23 29} [color=red];
2 -> {3};
2 -> {13 29 9} [color=green];
2 -> {13 29 9} [style=dashed];
3 -> {4};
4 -> {5};
5 -> {6};
6 -> {7};
7 -> {8};
9 -> {10} [color=green];
10 -> {11} [style=dotted];
10 -> {18} [color=green];
10 -> {18} [style=dashed];
11 -> {12} [style=dotted];
11 -> {23} [color=green];
11 -> {23} [style=dashed];
subgraph cluster_5 {
color=red
13 [label="Enter function <init>" style="filled" fillcolor=red];
14 [label="Delegated constructor call: super<R|kotlin/Any|>()"];
15 [label="Exit function <init>" style="filled" fillcolor=red];
}
13 -> {14};
14 -> {15};
subgraph cluster_6 {
color=red
16 [label="Enter function getter" style="filled" fillcolor=red];
17 [label="Exit function getter" style="filled" fillcolor=red];
}
16 -> {17};
subgraph cluster_7 {
color=red
18 [label="Enter property" style="filled" fillcolor=red];
19 [label="Access variable R|<local>/a|"];
20 [label="Exit property" style="filled" fillcolor=red];
}
18 -> {19};
19 -> {20};
20 -> {11} [color=green];
subgraph cluster_8 {
color=red
21 [label="Enter function getter" style="filled" fillcolor=red];
22 [label="Exit function getter" style="filled" fillcolor=red];
}
21 -> {22};
subgraph cluster_9 {
color=red
23 [label="Enter init block" style="filled" fillcolor=red];
subgraph cluster_10 {
color=blue
24 [label="Enter block"];
25 [label="Access variable R|<local>/b|"];
26 [label="Assignment: R|/<anonymous>.leaked|"];
27 [label="Exit block"];
}
28 [label="Exit init block" style="filled" fillcolor=red];
}
23 -> {24};
24 -> {25};
25 -> {26};
26 -> {27};
27 -> {28};
28 -> {12} [color=green];
subgraph cluster_11 {
color=red
29 [label="Enter function run" style="filled" fillcolor=red];
subgraph cluster_12 {
color=blue
30 [label="Enter block"];
31 [label="Function call: R|<local>/c|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
32 [label="Exit block"];
}
33 [label="Exit function run" style="filled" fillcolor=red];
}
29 -> {30};
30 -> {31};
31 -> {32};
32 -> {33};
}
@@ -0,0 +1,31 @@
// !DUMP_CFG
import kotlin.contracts.*
@ExperimentalContracts
fun foo(a: () -> Unit, b: () -> Unit, c: () -> Unit, d: () -> Unit) {
<!LEAKED_IN_PLACE_LAMBDA, LEAKED_IN_PLACE_LAMBDA, LEAKED_IN_PLACE_LAMBDA!>contract {
callsInPlace(a, InvocationKind.AT_MOST_ONCE)
callsInPlace(b, InvocationKind.AT_MOST_ONCE)
callsInPlace(c, InvocationKind.AT_MOST_ONCE)
callsInPlace(d, InvocationKind.AT_MOST_ONCE)
}<!>
val obj = object : Runnable {
val leakedVal = <!LEAKED_IN_PLACE_LAMBDA!>a<!>
val leaked: Any
init {
leaked = <!LEAKED_IN_PLACE_LAMBDA!>b<!>
}
override fun run() {
<!LEAKED_IN_PLACE_LAMBDA!>c()<!>
}
}
obj.run()
d()
}
@@ -0,0 +1,35 @@
FILE: inAnonymousObject.kt
@R|kotlin/contracts/ExperimentalContracts|() public final fun foo(a: R|() -> kotlin/Unit|, b: R|() -> kotlin/Unit|, c: R|() -> kotlin/Unit|, d: R|() -> kotlin/Unit|): R|kotlin/Unit|
[R|Contract description]
<
CallsInPlace(a, AT_MOST_ONCE)
CallsInPlace(b, AT_MOST_ONCE)
CallsInPlace(c, AT_MOST_ONCE)
CallsInPlace(d, AT_MOST_ONCE)
>
{
[StubStatement]
lval obj: R|<anonymous>| = object : R|java/lang/Runnable| {
private constructor(): R|<anonymous>| {
super<R|kotlin/Any|>()
}
public final val leakedVal: R|() -> kotlin/Unit| = R|<local>/a|
public get(): R|() -> kotlin/Unit|
public final val leaked: R|kotlin/Any|
public get(): R|kotlin/Any|
init {
this@R|/<anonymous>|.R|/<anonymous>.leaked| = R|<local>/b|
}
public final override fun run(): R|kotlin/Unit| {
R|<local>/c|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
}
}
R|<local>/obj|.R|/<anonymous>.run|()
R|<local>/d|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
}
@@ -0,0 +1,126 @@
digraph inLocalClass_kt {
graph [nodesep=3]
node [shape=box penwidth=2]
edge [penwidth=2]
subgraph cluster_0 {
color=red
0 [label="Enter function foo" style="filled" fillcolor=red];
subgraph cluster_1 {
color=blue
1 [label="Enter block"];
2 [label="Exit local class foo"];
3 [label="Function call: R|/LocalClass.LocalClass|()"];
4 [label="Function call: R|/LocalClass.LocalClass|().R|/LocalClass.run|()"];
5 [label="Function call: R|<local>/e|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
6 [label="Exit block"];
}
7 [label="Exit function foo" style="filled" fillcolor=red];
}
subgraph cluster_2 {
color=blue
8 [label="Enter class LocalClass" style="filled" fillcolor=red];
subgraph cluster_3 {
color=blue
9 [label="Part of class initialization"];
subgraph cluster_4 {
color=blue
10 [label="Part of class initialization"];
11 [label="Exit class LocalClass" style="filled" fillcolor=red];
}
0 -> {1};
1 -> {2};
1 -> {14 12 17 19 25 31} [color=red];
2 -> {3};
2 -> {19 31 8} [color=green];
2 -> {19 31 8} [style=dashed];
3 -> {4};
4 -> {5};
5 -> {6};
6 -> {7};
8 -> {9} [color=green];
9 -> {10} [style=dotted];
9 -> {14} [color=green];
9 -> {14} [style=dashed];
10 -> {11} [style=dotted];
10 -> {25} [color=green];
10 -> {25} [style=dashed];
subgraph cluster_5 {
color=red
12 [label="Enter function getter" style="filled" fillcolor=red];
13 [label="Exit function getter" style="filled" fillcolor=red];
}
12 -> {13};
subgraph cluster_6 {
color=red
14 [label="Enter property" style="filled" fillcolor=red];
15 [label="Access variable R|<local>/a|"];
16 [label="Exit property" style="filled" fillcolor=red];
}
14 -> {15};
15 -> {16};
16 -> {10} [color=green];
subgraph cluster_7 {
color=red
17 [label="Enter function getter" style="filled" fillcolor=red];
18 [label="Exit function getter" style="filled" fillcolor=red];
}
17 -> {18};
subgraph cluster_8 {
color=red
19 [label="Enter function <init>" style="filled" fillcolor=red];
20 [label="Delegated constructor call: super<R|kotlin/Any|>()"];
subgraph cluster_9 {
color=blue
21 [label="Enter block"];
22 [label="Function call: R|<local>/b|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
23 [label="Exit block"];
}
24 [label="Exit function <init>" style="filled" fillcolor=red];
}
19 -> {20};
20 -> {21};
21 -> {22};
22 -> {23};
23 -> {24};
subgraph cluster_10 {
color=red
25 [label="Enter init block" style="filled" fillcolor=red];
subgraph cluster_11 {
color=blue
26 [label="Enter block"];
27 [label="Access variable R|<local>/c|"];
28 [label="Assignment: R|/LocalClass.leaked|"];
29 [label="Exit block"];
}
30 [label="Exit init block" style="filled" fillcolor=red];
}
25 -> {26};
26 -> {27};
27 -> {28};
28 -> {29};
29 -> {30};
30 -> {11} [color=green];
subgraph cluster_12 {
color=red
31 [label="Enter function run" style="filled" fillcolor=red];
subgraph cluster_13 {
color=blue
32 [label="Enter block"];
33 [label="Function call: R|<local>/d|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
34 [label="Exit block"];
}
35 [label="Exit function run" style="filled" fillcolor=red];
}
31 -> {32};
32 -> {33};
33 -> {34};
34 -> {35};
}
@@ -0,0 +1,35 @@
// !DUMP_CFG
import kotlin.contracts.*
@ExperimentalContracts
fun foo(a: () -> Unit, b: () -> Unit, c: () -> Unit, d: () -> Unit, e: () -> Unit) {
<!LEAKED_IN_PLACE_LAMBDA, LEAKED_IN_PLACE_LAMBDA, LEAKED_IN_PLACE_LAMBDA, LEAKED_IN_PLACE_LAMBDA!>contract {
callsInPlace(a, InvocationKind.AT_MOST_ONCE)
callsInPlace(b, InvocationKind.AT_MOST_ONCE)
callsInPlace(c, InvocationKind.AT_MOST_ONCE)
callsInPlace(d, InvocationKind.AT_MOST_ONCE)
callsInPlace(e, InvocationKind.AT_MOST_ONCE)
}<!>
class LocalClass {
val leakedVal = <!LEAKED_IN_PLACE_LAMBDA!>a<!>
val leaked: Any
constructor() {
<!LEAKED_IN_PLACE_LAMBDA!>b()<!>
}
init {
leaked = <!LEAKED_IN_PLACE_LAMBDA!>c<!>
}
fun run() {
<!LEAKED_IN_PLACE_LAMBDA!>d()<!>
}
}
LocalClass().run()
e()
}
@@ -0,0 +1,37 @@
FILE: inLocalClass.kt
@R|kotlin/contracts/ExperimentalContracts|() public final fun foo(a: R|() -> kotlin/Unit|, b: R|() -> kotlin/Unit|, c: R|() -> kotlin/Unit|, d: R|() -> kotlin/Unit|, e: R|() -> kotlin/Unit|): R|kotlin/Unit|
[R|Contract description]
<
CallsInPlace(a, AT_MOST_ONCE)
CallsInPlace(b, AT_MOST_ONCE)
CallsInPlace(c, AT_MOST_ONCE)
CallsInPlace(d, AT_MOST_ONCE)
CallsInPlace(e, AT_MOST_ONCE)
>
{
[StubStatement]
local final class LocalClass : R|kotlin/Any| {
public final val leakedVal: R|() -> kotlin/Unit| = R|<local>/a|
public get(): R|() -> kotlin/Unit|
public final val leaked: R|kotlin/Any|
public get(): R|kotlin/Any|
public constructor(): R|LocalClass| {
super<R|kotlin/Any|>()
R|<local>/b|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
}
init {
this@R|/LocalClass|.R|/LocalClass.leaked| = R|<local>/c|
}
public final fun run(): R|kotlin/Unit| {
R|<local>/d|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
}
}
R|/LocalClass.LocalClass|().R|/LocalClass.run|()
R|<local>/e|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
}
@@ -0,0 +1,47 @@
digraph inLocalFunction_kt {
graph [nodesep=3]
node [shape=box penwidth=2]
edge [penwidth=2]
subgraph cluster_0 {
color=red
0 [label="Enter function foo" style="filled" fillcolor=red];
subgraph cluster_1 {
color=blue
1 [label="Enter block"];
2 [label="Local function declaration foo"];
3 [label="Function call: R|<local>/localFun|()"];
4 [label="Function call: R|<local>/b|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
5 [label="Exit block"];
}
6 [label="Exit function foo" style="filled" fillcolor=red];
}
subgraph cluster_2 {
color=blue
7 [label="Enter function localFun" style="filled" fillcolor=red];
subgraph cluster_3 {
color=blue
8 [label="Enter block"];
9 [label="Access variable R|<local>/a|"];
10 [label="Function call: R|<local>/a|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
11 [label="Function call: R|<local>/a|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
12 [label="Exit block"];
}
13 [label="Exit function localFun" style="filled" fillcolor=red];
}
0 -> {1};
1 -> {2};
2 -> {3};
2 -> {7} [color=red];
2 -> {7} [style=dashed];
3 -> {4};
4 -> {5};
5 -> {6};
7 -> {8};
8 -> {9};
9 -> {10};
10 -> {11};
11 -> {12};
12 -> {13};
}
@@ -0,0 +1,18 @@
// !DUMP_CFG
import kotlin.contracts.*
@ExperimentalContracts
fun foo(a: () -> Unit, b: () -> Unit) {
<!LEAKED_IN_PLACE_LAMBDA!>contract {
callsInPlace(a, InvocationKind.AT_MOST_ONCE)
}<!>
fun localFun() {
<!LEAKED_IN_PLACE_LAMBDA!>a<!>.invoke()
<!LEAKED_IN_PLACE_LAMBDA!>a()<!>
}
localFun()
b()
}
@@ -0,0 +1,16 @@
FILE: inLocalFunction.kt
@R|kotlin/contracts/ExperimentalContracts|() public final fun foo(a: R|() -> kotlin/Unit|, b: R|() -> kotlin/Unit|): R|kotlin/Unit|
[R|Contract description]
<
CallsInPlace(a, AT_MOST_ONCE)
>
{
[StubStatement]
local final fun localFun(): R|kotlin/Unit| {
R|<local>/a|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
R|<local>/a|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
}
R|<local>/localFun|()
R|<local>/b|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
}
@@ -0,0 +1,97 @@
digraph toLocalVariables_kt {
graph [nodesep=3]
node [shape=box penwidth=2]
edge [penwidth=2]
subgraph cluster_0 {
color=red
0 [label="Enter function bar" style="filled" fillcolor=red];
subgraph cluster_1 {
color=blue
1 [label="Enter block"];
2 [label="Exit block"];
}
3 [label="Exit function bar" style="filled" fillcolor=red];
}
0 -> {1};
1 -> {2};
2 -> {3};
subgraph cluster_2 {
color=red
4 [label="Enter function foo" style="filled" fillcolor=red];
subgraph cluster_3 {
color=blue
5 [label="Enter block"];
subgraph cluster_4 {
color=blue
6 [label="Enter when"];
subgraph cluster_5 {
color=blue
7 [label="Enter when branch condition "];
8 [label="Const: Boolean(true)"];
9 [label="Exit when branch condition"];
}
subgraph cluster_6 {
color=blue
10 [label="Enter when branch condition else"];
11 [label="Exit when branch condition"];
}
12 [label="Enter when branch result"];
subgraph cluster_7 {
color=blue
13 [label="Enter block"];
14 [label="Access variable R|<local>/y|"];
15 [label="Variable declaration: lval yCopy: R|() -> kotlin/Unit|"];
16 [label="Function call: R|<local>/yCopy|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
17 [label="Exit block"];
}
18 [label="Exit when branch result"];
19 [label="Enter when branch result"];
subgraph cluster_8 {
color=blue
20 [label="Enter block"];
21 [label="Access variable R|<local>/x|"];
22 [label="Function call: R|/bar|(...)"];
23 [label="Exit block"];
}
24 [label="Exit when branch result"];
25 [label="Exit when"];
}
26 [label="Variable declaration: lval zCopy: R|() -> kotlin/Unit|"];
27 [label="Access variable R|<local>/z|"];
28 [label="Assignment: R|<local>/zCopy|"];
29 [label="Function call: R|<local>/zCopy|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
30 [label="Exit block"];
}
31 [label="Exit function foo" style="filled" fillcolor=red];
}
4 -> {5};
5 -> {6};
6 -> {7};
7 -> {8};
8 -> {9};
9 -> {19 10};
10 -> {11};
11 -> {12};
12 -> {13};
13 -> {14};
14 -> {15};
15 -> {16};
16 -> {17};
17 -> {18};
18 -> {25};
19 -> {20};
20 -> {21};
21 -> {22};
22 -> {23};
23 -> {24};
24 -> {25};
25 -> {26};
26 -> {27};
27 -> {28};
28 -> {29};
29 -> {30};
30 -> {31};
}
@@ -0,0 +1,26 @@
// !DUMP_CFG
import kotlin.contracts.*
fun bar(x: () -> Unit) {
}
@ExperimentalContracts
fun foo(x: () -> Unit, y: () -> Unit, z: () -> Unit) {
<!LEAKED_IN_PLACE_LAMBDA, LEAKED_IN_PLACE_LAMBDA, LEAKED_IN_PLACE_LAMBDA!>contract {
callsInPlace(x, InvocationKind.AT_MOST_ONCE)
callsInPlace(y, InvocationKind.AT_MOST_ONCE)
callsInPlace(z, InvocationKind.AT_MOST_ONCE)
}<!>
if (true) {
bar(<!LEAKED_IN_PLACE_LAMBDA!>x<!>)
} else {
val yCopy = <!LEAKED_IN_PLACE_LAMBDA!>y<!>
yCopy()
}
val zCopy: () -> Unit
zCopy = <!LEAKED_IN_PLACE_LAMBDA!>z<!>
zCopy()
}
@@ -0,0 +1,26 @@
FILE: toLocalVariables.kt
public final fun bar(x: R|() -> kotlin/Unit|): R|kotlin/Unit| {
}
@R|kotlin/contracts/ExperimentalContracts|() public final fun foo(x: R|() -> kotlin/Unit|, y: R|() -> kotlin/Unit|, z: R|() -> kotlin/Unit|): R|kotlin/Unit|
[R|Contract description]
<
CallsInPlace(x, AT_MOST_ONCE)
CallsInPlace(y, AT_MOST_ONCE)
CallsInPlace(z, AT_MOST_ONCE)
>
{
[StubStatement]
when () {
Boolean(true) -> {
R|/bar|(R|<local>/x|)
}
else -> {
lval yCopy: R|() -> kotlin/Unit| = R|<local>/y|
R|<local>/yCopy|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
}
}
lval zCopy: R|() -> kotlin/Unit|
R|<local>/zCopy| = R|<local>/z|
R|<local>/zCopy|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
}