mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-13 00:21:27 +00:00
Compare commits
835 Commits
rr/faster_
...
ssa/native
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99b395b0fa | ||
|
|
0c5bf63844 | ||
|
|
fd7c4c0c2d | ||
|
|
4a62466671 | ||
|
|
47d0211370 | ||
|
|
5819959cce | ||
|
|
1cd321a90f | ||
|
|
16f41bd80c | ||
|
|
8ab546ba51 | ||
|
|
a5e59e09ee | ||
|
|
6d7eb2bd21 | ||
|
|
21f7e16ee6 | ||
|
|
fb1eac0985 | ||
|
|
0a6e51e47f | ||
|
|
f37d880964 | ||
|
|
301f446433 | ||
|
|
a8077aebb0 | ||
|
|
753ba99b04 | ||
|
|
a0553f4dfd | ||
|
|
c3a327e118 | ||
|
|
1a5552bef8 | ||
|
|
6660c9b26b | ||
|
|
ab158a53c3 | ||
|
|
1cdbbad367 | ||
|
|
5e87d753b7 | ||
|
|
a7cc275c7d | ||
|
|
12d694de46 | ||
|
|
16f0ba8e46 | ||
|
|
49c412b022 | ||
|
|
6ea2dea28e | ||
|
|
e7a08b14e3 | ||
|
|
7e1c776376 | ||
|
|
4049cf94b1 | ||
|
|
da72f26d94 | ||
|
|
e18c3517a9 | ||
|
|
cd08dc49a0 | ||
|
|
4edc1239ac | ||
|
|
af12d61388 | ||
|
|
3cd43dced4 | ||
|
|
a17a61341b | ||
|
|
68f14fdd87 | ||
|
|
5a26e79b08 | ||
|
|
b77dc4136b | ||
|
|
64ebddcbc6 | ||
|
|
68e7476765 | ||
|
|
1e95717b8d | ||
|
|
f64a0efa8b | ||
|
|
26ede5d885 | ||
|
|
9fb41b6334 | ||
|
|
367db345a8 | ||
|
|
dda9bb93f6 | ||
|
|
3d4e861f05 | ||
|
|
6c246738a5 | ||
|
|
dff392f2cd | ||
|
|
1857096071 | ||
|
|
50a343f91e | ||
|
|
cdfe8d60a4 | ||
|
|
0c35a3b699 | ||
|
|
486c6b3c15 | ||
|
|
ebb340fe68 | ||
|
|
8d764fa50e | ||
|
|
0fb398d45e | ||
|
|
f2ffead881 | ||
|
|
2c1c24c042 | ||
|
|
cd6384eb20 | ||
|
|
e85940a1ac | ||
|
|
ed0e3b0fed | ||
|
|
f8503ba1bf | ||
|
|
55663a0fb1 | ||
|
|
32c51cbb45 | ||
|
|
15c41b2610 | ||
|
|
edd2ca775b | ||
|
|
5096e8c5c4 | ||
|
|
7a99f9ff2e | ||
|
|
9be941def2 | ||
|
|
9acdcc7590 | ||
|
|
bf362abb57 | ||
|
|
6b18f33f92 | ||
|
|
b01c13a4df | ||
|
|
ec90649854 | ||
|
|
d88a665fa8 | ||
|
|
2f2e608502 | ||
|
|
492b8acf93 | ||
|
|
69fe7e66a5 | ||
|
|
1808f14677 | ||
|
|
ac2dffa74e | ||
|
|
a630273af2 | ||
|
|
bc9b2fd78b | ||
|
|
23b315446f | ||
|
|
b85a796492 | ||
|
|
f9607292b5 | ||
|
|
b46a42be33 | ||
|
|
7bddc2a815 | ||
|
|
4e4d36f85a | ||
|
|
a21d281c19 | ||
|
|
a04913a197 | ||
|
|
73a8b4544a | ||
|
|
b617aca8ee | ||
|
|
ca98417a0c | ||
|
|
b1bcbaf48f | ||
|
|
f6413c41a0 | ||
|
|
2e2edf398c | ||
|
|
8e32bbbe90 | ||
|
|
23c438aa03 | ||
|
|
63d1e195e0 | ||
|
|
9e0ba8c3d2 | ||
|
|
c56b6455f1 | ||
|
|
9b9a529b7f | ||
|
|
d346953de9 | ||
|
|
1679da45ab | ||
|
|
db12a96e54 | ||
|
|
39c6be86cb | ||
|
|
06d3c6f233 | ||
|
|
e75ca75e3e | ||
|
|
94af3adb4b | ||
|
|
1b98723b3f | ||
|
|
d124239025 | ||
|
|
dd3eb53904 | ||
|
|
3acb96e474 | ||
|
|
c57302abba | ||
|
|
1338675833 | ||
|
|
06001fc091 | ||
|
|
d3905fd763 | ||
|
|
3ec9599bc4 | ||
|
|
445e5122c1 | ||
|
|
ebe96ec79a | ||
|
|
a8f7bf5fbc | ||
|
|
2e3b576342 | ||
|
|
aef45f0997 | ||
|
|
d7f5015faa | ||
|
|
cd487efbe9 | ||
|
|
623b289616 | ||
|
|
58110709f3 | ||
|
|
28c473074c | ||
|
|
cd26ef2bb5 | ||
|
|
608b88996a | ||
|
|
1760befa37 | ||
|
|
81ce59cf48 | ||
|
|
e159392d22 | ||
|
|
5455800de4 | ||
|
|
747cc7d55d | ||
|
|
784273ac5b | ||
|
|
493fdbd418 | ||
|
|
8ad368e990 | ||
|
|
40d19b50c5 | ||
|
|
bfcd954dd3 | ||
|
|
b8330deefa | ||
|
|
5ec19c2417 | ||
|
|
1a1f6b0c0b | ||
|
|
017448e359 | ||
|
|
d3ddeef67f | ||
|
|
b3dbca7ea6 | ||
|
|
06ee84f809 | ||
|
|
5b9ce7e823 | ||
|
|
280c445783 | ||
|
|
bcf6202863 | ||
|
|
45d31fdba2 | ||
|
|
3bc0eaff59 | ||
|
|
0d1380c232 | ||
|
|
2ca3adbcb2 | ||
|
|
c19598c2fc | ||
|
|
9736cc162b | ||
|
|
fd92b851a2 | ||
|
|
1c678be0d3 | ||
|
|
cfc4e715e4 | ||
|
|
c1fb40a436 | ||
|
|
672b972b38 | ||
|
|
ac387b1f26 | ||
|
|
2c31d2e67d | ||
|
|
8c51c3c5c3 | ||
|
|
512a851c65 | ||
|
|
85a2a3255d | ||
|
|
cdc0f36859 | ||
|
|
33251f6d50 | ||
|
|
5873511f64 | ||
|
|
e2e0358505 | ||
|
|
1607a8231a | ||
|
|
927723c766 | ||
|
|
cc531149e5 | ||
|
|
eb94167ff2 | ||
|
|
170184dce4 | ||
|
|
206457d9ff | ||
|
|
7e04bb4bf1 | ||
|
|
766857881a | ||
|
|
2f0f88062a | ||
|
|
a28138eb72 | ||
|
|
b6c3132614 | ||
|
|
627af332b1 | ||
|
|
e6af3ff6a8 | ||
|
|
a70fc99130 | ||
|
|
22d202e657 | ||
|
|
755f847ab9 | ||
|
|
3c9dcdbbee | ||
|
|
8a812996dd | ||
|
|
1d913a6bf0 | ||
|
|
3b8cb4b00d | ||
|
|
13cf9329b2 | ||
|
|
7e71bd8f1c | ||
|
|
307de0be89 | ||
|
|
eccbf38061 | ||
|
|
fb801bdc33 | ||
|
|
54957ead5c | ||
|
|
1d2d1f9e8d | ||
|
|
afacff326d | ||
|
|
cb37a05b79 | ||
|
|
e64f7ffb98 | ||
|
|
75c76e2b57 | ||
|
|
f7f51cf38e | ||
|
|
4396482fed | ||
|
|
46a0c4be4b | ||
|
|
fba480875f | ||
|
|
edc134ef2d | ||
|
|
ba8f2e6bf3 | ||
|
|
c1afb6354b | ||
|
|
d670743a2f | ||
|
|
1b81018b69 | ||
|
|
cd99c35649 | ||
|
|
bcf6582af7 | ||
|
|
758859f198 | ||
|
|
eec5f99e35 | ||
|
|
552a012e65 | ||
|
|
3625c953ae | ||
|
|
9a39b1f4d1 | ||
|
|
c07dcb27df | ||
|
|
5485a6d6cd | ||
|
|
b1c5c10233 | ||
|
|
37d25a58e8 | ||
|
|
6c7f04ffc6 | ||
|
|
a97b4ea4ae | ||
|
|
dac4fe7507 | ||
|
|
3d81eba32b | ||
|
|
bc5a79ffcc | ||
|
|
209c0fe819 | ||
|
|
f8af127a4e | ||
|
|
89b3013294 | ||
|
|
c821aba3bd | ||
|
|
45ffe58b9c | ||
|
|
069e040af9 | ||
|
|
11902c7f8f | ||
|
|
ca7334acb9 | ||
|
|
376b420d1a | ||
|
|
e0643a4185 | ||
|
|
1599a049aa | ||
|
|
b9dd73220c | ||
|
|
1d491fdce6 | ||
|
|
684ef871ee | ||
|
|
263b876e6e | ||
|
|
c7272f6986 | ||
|
|
4726dcce40 | ||
|
|
7e2f15f532 | ||
|
|
e495c722c7 | ||
|
|
0026560bd7 | ||
|
|
f737d8002e | ||
|
|
e242ad955b | ||
|
|
b5ea811b9f | ||
|
|
20afb268d1 | ||
|
|
3d4186bdb1 | ||
|
|
e949a0d38d | ||
|
|
2574c00dd9 | ||
|
|
5a7298808e | ||
|
|
3dcee8fda8 | ||
|
|
7add820f0d | ||
|
|
398b545801 | ||
|
|
67f814c99f | ||
|
|
d174e8c3d2 | ||
|
|
9bfe502afd | ||
|
|
15648da2b4 | ||
|
|
11f70412e7 | ||
|
|
297e0a9f43 | ||
|
|
6ca87dc43f | ||
|
|
aeea7147fe | ||
|
|
28cf9898ef | ||
|
|
6cd0d81561 | ||
|
|
b1eeb1fb27 | ||
|
|
b1251c1716 | ||
|
|
4a601ebf95 | ||
|
|
74d8e16d09 | ||
|
|
a0449892b2 | ||
|
|
3d3c70141c | ||
|
|
6aed492703 | ||
|
|
bcf08e3293 | ||
|
|
d375c52830 | ||
|
|
331681b40b | ||
|
|
5db8ec6551 | ||
|
|
4ffed54f76 | ||
|
|
18bb287d3b | ||
|
|
d64cb24643 | ||
|
|
3a210f6c81 | ||
|
|
cbef031780 | ||
|
|
a4a1d35021 | ||
|
|
afd9b4935a | ||
|
|
4f15bd4817 | ||
|
|
44e1b61e6c | ||
|
|
3932acf843 | ||
|
|
0ce3dd117e | ||
|
|
0af918be4f | ||
|
|
1002d076b3 | ||
|
|
3cf4b8b108 | ||
|
|
3fc5405d57 | ||
|
|
09c31b0900 | ||
|
|
6482abc602 | ||
|
|
524189132b | ||
|
|
fe74dd2689 | ||
|
|
295638b26e | ||
|
|
1978bfcd85 | ||
|
|
a9abf3b9b6 | ||
|
|
334d518aba | ||
|
|
f815f63fc5 | ||
|
|
0c5fca31ec | ||
|
|
96cc74a752 | ||
|
|
668bb4fd71 | ||
|
|
b9decc3b30 | ||
|
|
4ad88679fd | ||
|
|
6ce2f8eb14 | ||
|
|
10efbeb0c9 | ||
|
|
7852d01da6 | ||
|
|
d41c6e900a | ||
|
|
e5ca646de4 | ||
|
|
1af1a3c84e | ||
|
|
c8cd000563 | ||
|
|
8972fc5158 | ||
|
|
78475a5d9c | ||
|
|
b10fdb919f | ||
|
|
cc56acc2c2 | ||
|
|
b150cc9537 | ||
|
|
42ea17b151 | ||
|
|
15808cb376 | ||
|
|
4f3d47eed3 | ||
|
|
0716c557fe | ||
|
|
574c607f1c | ||
|
|
9638af042d | ||
|
|
c2fc017d96 | ||
|
|
b44fd1a6fe | ||
|
|
06a8156376 | ||
|
|
3326cbd9c0 | ||
|
|
59fefb6214 | ||
|
|
af67e950c4 | ||
|
|
23392b73a9 | ||
|
|
cf20e64c61 | ||
|
|
6fe8dd1254 | ||
|
|
a9882a86c0 | ||
|
|
cd09c8ba51 | ||
|
|
1966915e92 | ||
|
|
dbdc6176f0 | ||
|
|
6dd4164f1e | ||
|
|
32426da625 | ||
|
|
06b23d5937 | ||
|
|
7b6dddf012 | ||
|
|
c3344549a8 | ||
|
|
cb92413cd8 | ||
|
|
f16b1c2d69 | ||
|
|
395b2119a1 | ||
|
|
11f951e6d1 | ||
|
|
8d27f102e7 | ||
|
|
469b3b9d02 | ||
|
|
22ccb7a943 | ||
|
|
e924ee3150 | ||
|
|
ee0b64cb29 | ||
|
|
e897c60ef1 | ||
|
|
79565da904 | ||
|
|
82a70a205b | ||
|
|
a22ef02e47 | ||
|
|
4abbac1ff1 | ||
|
|
02d8c7527e | ||
|
|
6f0bf766f2 | ||
|
|
c8386ad1c7 | ||
|
|
13b5f87f3a | ||
|
|
d7368c341e | ||
|
|
adbeda12a6 | ||
|
|
79ff02fd97 | ||
|
|
0925e1b497 | ||
|
|
29ac9b33ca | ||
|
|
5004735366 | ||
|
|
d7b10f31b4 | ||
|
|
e691de6e4c | ||
|
|
126f6eff28 | ||
|
|
8578f0beea | ||
|
|
60195114c1 | ||
|
|
4c58954967 | ||
|
|
13cb3c138a | ||
|
|
b6cb393796 | ||
|
|
cf3bd016be | ||
|
|
dc8dbad0bc | ||
|
|
7567597be6 | ||
|
|
93f9d9dacd | ||
|
|
3eaa452f9e | ||
|
|
6ab632f6ad | ||
|
|
c46a393a19 | ||
|
|
e56deb4525 | ||
|
|
b2f3485d7c | ||
|
|
dabc983f6a | ||
|
|
efce3fc2e0 | ||
|
|
46d1b63f70 | ||
|
|
38cecf8b12 | ||
|
|
29dbaa4ae1 | ||
|
|
d8b7b7b2dc | ||
|
|
4e06814bc5 | ||
|
|
015c2d1875 | ||
|
|
28344c8530 | ||
|
|
bc75a21852 | ||
|
|
4e66fd29e0 | ||
|
|
6cd3d84e74 | ||
|
|
f81f28569f | ||
|
|
d46f7738c6 | ||
|
|
d8569b6a03 | ||
|
|
af865544ff | ||
|
|
c526145a48 | ||
|
|
80140207b5 | ||
|
|
468fe4196d | ||
|
|
0eba74a9d2 | ||
|
|
f34a079699 | ||
|
|
2538caa84f | ||
|
|
ebde1e5491 | ||
|
|
fc0ae851a2 | ||
|
|
d90e3618f9 | ||
|
|
0f84525bdc | ||
|
|
9ccdffe8ad | ||
|
|
59ad7e0e04 | ||
|
|
c597ee0e34 | ||
|
|
c559adc0fb | ||
|
|
5b40f291bd | ||
|
|
d4e1ecd9d3 | ||
|
|
748a2d2e7c | ||
|
|
e9a2997f7e | ||
|
|
cea6081d36 | ||
|
|
ec4cbfef59 | ||
|
|
dcd61c292d | ||
|
|
d5e2ac0efc | ||
|
|
3c3e51c6de | ||
|
|
5291648d39 | ||
|
|
bd71fbe982 | ||
|
|
37050e0616 | ||
|
|
32b380e187 | ||
|
|
872b6b7e81 | ||
|
|
a7961a4a45 | ||
|
|
97bfc49ed6 | ||
|
|
1188d311b3 | ||
|
|
77f0bd3834 | ||
|
|
bb718f2e0b | ||
|
|
fa9f0d588d | ||
|
|
2266a348c3 | ||
|
|
42b2605c2a | ||
|
|
1a262c9c31 | ||
|
|
920d57563b | ||
|
|
cd51264940 | ||
|
|
979f5e8443 | ||
|
|
edf9f5e647 | ||
|
|
956bf22191 | ||
|
|
1654824467 | ||
|
|
73bb6d5d34 | ||
|
|
6bbfe0d503 | ||
|
|
2656c94535 | ||
|
|
ba5bd0b069 | ||
|
|
ad2fabb7cb | ||
|
|
afa8e16c25 | ||
|
|
5338705402 | ||
|
|
cbe0de6111 | ||
|
|
ca9cbf7eb7 | ||
|
|
0a5991d6e7 | ||
|
|
fb27459b2e | ||
|
|
e177cecd50 | ||
|
|
b37cdae966 | ||
|
|
84d06f35e9 | ||
|
|
7d8360f38b | ||
|
|
adc1ca76f4 | ||
|
|
bfc3f35d94 | ||
|
|
8803a69c9d | ||
|
|
922fcc4049 | ||
|
|
c7c78e0e1a | ||
|
|
6e093b0beb | ||
|
|
a525f3f357 | ||
|
|
66ddd15798 | ||
|
|
a78fcd6b64 | ||
|
|
fbbbc1c092 | ||
|
|
a783ee9ae7 | ||
|
|
0d39442a5e | ||
|
|
1c7a27ce42 | ||
|
|
e8b025f267 | ||
|
|
572f08151a | ||
|
|
02aebd6565 | ||
|
|
601465fa81 | ||
|
|
493fa1c1e3 | ||
|
|
023c775188 | ||
|
|
7dda04a1c9 | ||
|
|
44256db824 | ||
|
|
017c8d8211 | ||
|
|
0923d13d55 | ||
|
|
6c49a18770 | ||
|
|
0ac96b4973 | ||
|
|
b584fed93d | ||
|
|
adfeab1bd0 | ||
|
|
d17f984edf | ||
|
|
7c73840e4a | ||
|
|
ff9643b70e | ||
|
|
153df1dd1a | ||
|
|
db10732d6c | ||
|
|
4ae51e1d02 | ||
|
|
02d4c866ca | ||
|
|
a7e81d5154 | ||
|
|
bbd21da835 | ||
|
|
0f3f56f676 | ||
|
|
8fd2ddad12 | ||
|
|
c0949fbc18 | ||
|
|
9fdd3dc53f | ||
|
|
71def0666e | ||
|
|
afb85026c4 | ||
|
|
b264a2e5dd | ||
|
|
17bbedbc50 | ||
|
|
ca40cbede5 | ||
|
|
d43d0071a4 | ||
|
|
08a1134fff | ||
|
|
90e06bf403 | ||
|
|
a66f3d26fd | ||
|
|
ba48f80e53 | ||
|
|
b42d6a3e85 | ||
|
|
4c3fb8697b | ||
|
|
2c068b8c5a | ||
|
|
da4113af88 | ||
|
|
ed07e1c778 | ||
|
|
8e890eba3e | ||
|
|
7422571c72 | ||
|
|
947e97511b | ||
|
|
4ce3f87361 | ||
|
|
329991217a | ||
|
|
7b5a3f8d1b | ||
|
|
8e6f84d5cd | ||
|
|
a353719a6b | ||
|
|
199ec60742 | ||
|
|
5206b45ce3 | ||
|
|
fd537e7d5a | ||
|
|
17fc1da719 | ||
|
|
8dad8fa813 | ||
|
|
fa1d09c778 | ||
|
|
972211f8e6 | ||
|
|
1fa74ef939 | ||
|
|
89bd52c7d2 | ||
|
|
de73622af9 | ||
|
|
c2e2068682 | ||
|
|
b9c549803d | ||
|
|
c168a561df | ||
|
|
4f73ebbcbd | ||
|
|
ca214bef30 | ||
|
|
04d9d243de | ||
|
|
4a09fba3a4 | ||
|
|
e4d2351e03 | ||
|
|
448376f073 | ||
|
|
9c6943b8c4 | ||
|
|
b11201be81 | ||
|
|
504449f03e | ||
|
|
0627dbcb78 | ||
|
|
5283f7b7c6 | ||
|
|
3636118743 | ||
|
|
847c58d574 | ||
|
|
5f7803f2db | ||
|
|
e20b354dbd | ||
|
|
0a6d010d1c | ||
|
|
67128c022a | ||
|
|
b2550f69bc | ||
|
|
26043f3968 | ||
|
|
92a73d7636 | ||
|
|
87130edfa2 | ||
|
|
f723389565 | ||
|
|
ebf837c135 | ||
|
|
1a40164ef0 | ||
|
|
8bfaa39a5c | ||
|
|
10d9988824 | ||
|
|
83895c49c5 | ||
|
|
63fc3645ab | ||
|
|
2fbb700ec6 | ||
|
|
b307358f69 | ||
|
|
f5d8535dc5 | ||
|
|
354a06b94a | ||
|
|
3c2e266c31 | ||
|
|
e0b1d0db53 | ||
|
|
807f031dcc | ||
|
|
229dfd3f5f | ||
|
|
391c4db87c | ||
|
|
2397650c24 | ||
|
|
8f1d07084b | ||
|
|
e4992176c1 | ||
|
|
767af0dae0 | ||
|
|
0e2d765a2d | ||
|
|
b3d7ed569d | ||
|
|
ca4410aa53 | ||
|
|
edaf925b19 | ||
|
|
033a9f1589 | ||
|
|
e45b13d582 | ||
|
|
5ee87e126d | ||
|
|
d72a399982 | ||
|
|
90d15f156b | ||
|
|
bab5a4a6c9 | ||
|
|
9148094bbd | ||
|
|
d9483ccb08 | ||
|
|
d18ade088d | ||
|
|
9142b03df5 | ||
|
|
ca5ebdc13c | ||
|
|
cdbd0eb932 | ||
|
|
824d6ab5cc | ||
|
|
1e2547d0d0 | ||
|
|
588952bc9a | ||
|
|
a1bd728aef | ||
|
|
55f7bb2756 | ||
|
|
d36c56497e | ||
|
|
262c9e88d1 | ||
|
|
ee68962c02 | ||
|
|
4fe846fb81 | ||
|
|
1347ee8c7d | ||
|
|
d2ad421e0b | ||
|
|
8f484fcf88 | ||
|
|
cba224b7b8 | ||
|
|
baaa615e09 | ||
|
|
70e4472c99 | ||
|
|
d2a7434cff | ||
|
|
f34726ff91 | ||
|
|
2ae546576c | ||
|
|
b1d17cfd7b | ||
|
|
e316cd04b6 | ||
|
|
699d53e7f9 | ||
|
|
0c419eac1a | ||
|
|
0f216f4d37 | ||
|
|
a544d6948b | ||
|
|
f84adcb378 | ||
|
|
ca0b8be53b | ||
|
|
0ed9b75428 | ||
|
|
b442e598d3 | ||
|
|
f1ac5796ed | ||
|
|
2d0d83a54b | ||
|
|
6af042c1dd | ||
|
|
95492f7370 | ||
|
|
7250aed3b5 | ||
|
|
c9c82ab3a6 | ||
|
|
c8094f7439 | ||
|
|
f590c3201a | ||
|
|
70fe984738 | ||
|
|
cc5ba4c0af | ||
|
|
0cccb76652 | ||
|
|
235359e028 | ||
|
|
390eda1b9d | ||
|
|
7854e9c3d7 | ||
|
|
1187e786f4 | ||
|
|
5f3f2e762a | ||
|
|
1fe1c197ee | ||
|
|
29f6ec4f40 | ||
|
|
38fb5e16ef | ||
|
|
805778e782 | ||
|
|
7cbd6908f9 | ||
|
|
228100ef09 | ||
|
|
a919aab3ab | ||
|
|
ee07ff7982 | ||
|
|
caa13b33b1 | ||
|
|
b93dff003f | ||
|
|
38d6c8ded0 | ||
|
|
d0f207071c | ||
|
|
352c624601 | ||
|
|
046730c62c | ||
|
|
c401582770 | ||
|
|
8c524c2f89 | ||
|
|
6cb6abee98 | ||
|
|
5684b6977a | ||
|
|
c5d783596d | ||
|
|
20d50cfee7 | ||
|
|
6706ee87ad | ||
|
|
f9cb0d61a8 | ||
|
|
99491014e4 | ||
|
|
2c9857b880 | ||
|
|
59dcef81f7 | ||
|
|
66c9d658e4 | ||
|
|
b505486063 | ||
|
|
50d150ace5 | ||
|
|
3e823d62a9 | ||
|
|
d4e1cded59 | ||
|
|
2574dc907c | ||
|
|
263e40e753 | ||
|
|
45a384bb77 | ||
|
|
ce79fc77c9 | ||
|
|
f95c50064c | ||
|
|
19467861c9 | ||
|
|
2d91917ac7 | ||
|
|
566640aa6d | ||
|
|
1f3004a667 | ||
|
|
d84cc4333c | ||
|
|
b706e776ad | ||
|
|
a63f218dd3 | ||
|
|
48a2b23b3a | ||
|
|
c3515cc338 | ||
|
|
10f9c217fd | ||
|
|
4267852030 | ||
|
|
e9b177352c | ||
|
|
2baf344f5f | ||
|
|
1eb951749d | ||
|
|
fa2e787f76 | ||
|
|
91ecaa32e3 | ||
|
|
77fcb12af8 | ||
|
|
f19dfd9d2a | ||
|
|
c678d4b506 | ||
|
|
7ba8e7d1d3 | ||
|
|
4fe240d1b4 | ||
|
|
970d7b5a78 | ||
|
|
55489b6249 | ||
|
|
eb69153fbc | ||
|
|
84bd347841 | ||
|
|
810def829c | ||
|
|
bb38eaae61 | ||
|
|
0a9498f7e2 | ||
|
|
91c39ed01c | ||
|
|
707e1c7f8d | ||
|
|
a55eacd8db | ||
|
|
be0a0a7784 | ||
|
|
f671061328 | ||
|
|
02f2f6c148 | ||
|
|
4eb534e39b | ||
|
|
563f8c8541 | ||
|
|
66fa624ab7 | ||
|
|
4711577b17 | ||
|
|
a4426cd40d | ||
|
|
e1c8f302a4 | ||
|
|
78e249c2d5 | ||
|
|
ab2aaec4fe | ||
|
|
4e870b6796 | ||
|
|
6700179ee4 | ||
|
|
a0418c3146 | ||
|
|
6869fbbfe5 | ||
|
|
2fd6c55fc8 | ||
|
|
df9a3c0300 | ||
|
|
173813f7cf | ||
|
|
bade6cb611 | ||
|
|
2333b1bcf6 | ||
|
|
cbdd0f579a | ||
|
|
0083ef053b | ||
|
|
fe18e3fa31 | ||
|
|
2ca241c82c | ||
|
|
698c0bb2a9 | ||
|
|
a44048e713 | ||
|
|
4003cd2832 | ||
|
|
a24eab9745 | ||
|
|
5526281c54 | ||
|
|
56866e6927 | ||
|
|
50f7594d9e | ||
|
|
104188c035 | ||
|
|
7958307d28 | ||
|
|
25192cd639 | ||
|
|
a49b36f6b1 | ||
|
|
c816d6f2e5 | ||
|
|
18d74e62a6 | ||
|
|
35677f4727 | ||
|
|
8a0941a85a | ||
|
|
98bd6f5d3e | ||
|
|
2a6355b64c | ||
|
|
73539cf342 | ||
|
|
cb847b945d | ||
|
|
7cbea12c8a | ||
|
|
d020948933 | ||
|
|
543ce06d85 | ||
|
|
82f268d611 | ||
|
|
3235b09a92 | ||
|
|
b547870d71 | ||
|
|
ea5157cadc | ||
|
|
d6adac8dd0 | ||
|
|
3afed7f972 | ||
|
|
c6525974d0 | ||
|
|
03e5dc6117 | ||
|
|
4e11c670c6 | ||
|
|
7ca2a83f08 | ||
|
|
33cf058b55 | ||
|
|
8f06e59d3b | ||
|
|
559e7d223a | ||
|
|
d9d2a9cc31 | ||
|
|
a7549be95e | ||
|
|
4cee44cd6e | ||
|
|
508d3bd9c0 | ||
|
|
fa21132704 | ||
|
|
c6ab195a87 | ||
|
|
0130d18ea9 | ||
|
|
058ca16af7 | ||
|
|
8553c6ef58 | ||
|
|
5dc6e1b473 | ||
|
|
83023c2073 | ||
|
|
ec99585eb9 | ||
|
|
1b3046a61b | ||
|
|
000a437315 | ||
|
|
bba0dfb469 | ||
|
|
34f5b85ae5 | ||
|
|
e9f2d574d5 | ||
|
|
f02f3c76ae | ||
|
|
5358d4f07c | ||
|
|
f19a501cc7 | ||
|
|
5690b4d8c2 | ||
|
|
de4c2d35ab | ||
|
|
68a3c5f378 | ||
|
|
414e735a67 | ||
|
|
9b00776dba | ||
|
|
c5ce52eef5 | ||
|
|
1e758c6767 | ||
|
|
e3b219b68f | ||
|
|
c0f5d09759 | ||
|
|
d00fba3718 | ||
|
|
ed44497a9b | ||
|
|
bce1075fc4 | ||
|
|
272284680e | ||
|
|
f768d5b453 | ||
|
|
0310f7cb0b | ||
|
|
8782399ffb | ||
|
|
334e34b70b | ||
|
|
20088994c1 | ||
|
|
525c5b886f | ||
|
|
e03cb372b7 | ||
|
|
48ce8c049d | ||
|
|
3833c833ef | ||
|
|
002f4210f7 | ||
|
|
5ee865437d | ||
|
|
ba541bd53b | ||
|
|
e0570d98b2 | ||
|
|
76f0684d7c | ||
|
|
6c734289be | ||
|
|
eff7c375ce | ||
|
|
aef9701661 | ||
|
|
cf2b4dd277 | ||
|
|
ca84aa4f2c | ||
|
|
c1a5ce6e61 | ||
|
|
a8e379a025 | ||
|
|
c648356887 | ||
|
|
6dc75c2e51 | ||
|
|
bea90e0578 | ||
|
|
3b513ba299 | ||
|
|
9ebd665c96 | ||
|
|
a383d45534 | ||
|
|
60458214a5 | ||
|
|
e29a67b3a3 | ||
|
|
f8528c5cca | ||
|
|
83c1a75c1e | ||
|
|
54b9ad6bf5 | ||
|
|
db61665ab8 | ||
|
|
a710a8d10f | ||
|
|
865ad3698b | ||
|
|
d8f839a7e4 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -12,7 +12,7 @@
|
||||
/android-studio/sdk
|
||||
out/
|
||||
/tmp
|
||||
kotlin-ide/
|
||||
/intellij
|
||||
workspace.xml
|
||||
*.versionsBackup
|
||||
/idea/testData/debugger/tinyApp/classes*
|
||||
@@ -68,3 +68,4 @@ distTmp/
|
||||
outTmp/
|
||||
/test.output
|
||||
/kotlin-native/dist
|
||||
kotlin-ide/
|
||||
|
||||
1
.idea/dictionaries/igor.xml
generated
1
.idea/dictionaries/igor.xml
generated
@@ -1,6 +1,7 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="igor">
|
||||
<words>
|
||||
<w>addr</w>
|
||||
<w>descr</w>
|
||||
<w>exprs</w>
|
||||
</words>
|
||||
|
||||
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
@@ -13,6 +13,6 @@
|
||||
</option>
|
||||
</component>
|
||||
<component name="KotlinCompilerSettings">
|
||||
<option name="additionalArguments" value="-version -Xallow-kotlin-package -Xskip-metadata-version-check -Xread-deserialized-contracts" />
|
||||
<option name="additionalArguments" value="-version -Xallow-kotlin-package -Xskip-metadata-version-check" />
|
||||
</component>
|
||||
</project>
|
||||
2
.idea/runConfigurations/Test__Commonizer.xml
generated
2
.idea/runConfigurations/Test__Commonizer.xml
generated
@@ -4,7 +4,7 @@
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="--tests "org.jetbrains.kotlin.gradle.CommonizerHierarchicalIT" --tests "org.jetbrains.kotlin.gradle.CommonizerIT" --tests "org.jetbrains.kotlin.commonizer.**"" />
|
||||
<option name="scriptParameters" value="--tests "org.jetbrains.kotlin.gradle.CommonizerHierarchicalIT" --tests "org.jetbrains.kotlin.gradle.CommonizerIT" --tests "org.jetbrains.kotlin.commonizer.**" --tests "org.jetbrains.kotlin.gradle.native.CocoaPodsIT.testCinteropCommonization*"" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
|
||||
@@ -56,7 +56,7 @@ Alternatively, it is still possible to only provide required JDKs via environmen
|
||||
from environmental variables - disable Gradle toolchain auto-detection by passing `-Porg.gradle.java.installations.auto-detect=false` option
|
||||
(or put it into `$GRADLE_USER_HOME/gradle.properties`).
|
||||
|
||||
For local development, if you're not working on bytecode generation or the standard library, it's OK to avoid installing JDK 1.6 and JDK 1.7.
|
||||
For local development, if you're not working on the standard library, it's OK to avoid installing JDK 1.6 and JDK 1.7.
|
||||
Add `kotlin.build.isObsoleteJdkOverrideEnabled=true` to the `local.properties` file, so build will only use JDK 1.8+. Note, that in this
|
||||
case, build will have Gradle remote build cache misses for some tasks.
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.build.GeneratedJvmClass
|
||||
import org.jetbrains.kotlin.incremental.storage.*
|
||||
import org.jetbrains.kotlin.inline.inlineFunctionsJvmNames
|
||||
import org.jetbrains.kotlin.load.kotlin.FileBasedKotlinClass
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.JvmPackagePartProto
|
||||
@@ -39,12 +40,11 @@ import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import java.io.File
|
||||
import java.security.MessageDigest
|
||||
import java.util.*
|
||||
|
||||
val KOTLIN_CACHE_DIRECTORY_NAME = "kotlin"
|
||||
|
||||
open class IncrementalJvmCache(
|
||||
private val targetDataRoot: File,
|
||||
targetDataRoot: File,
|
||||
targetOutputDir: File?,
|
||||
pathConverter: FileToPathConverter
|
||||
) : AbstractIncrementalCache<JvmClassName>(
|
||||
@@ -114,32 +114,43 @@ open class IncrementalJvmCache(
|
||||
}
|
||||
|
||||
open fun saveFileToCache(generatedClass: GeneratedJvmClass, changesCollector: ChangesCollector) {
|
||||
val sourceFiles: Collection<File> = generatedClass.sourceFiles
|
||||
val kotlinClass: LocalFileKotlinClass = generatedClass.outputClass
|
||||
val className = kotlinClass.className
|
||||
saveClassToCache(KotlinClassInfo.createFrom(generatedClass.outputClass), generatedClass.sourceFiles, changesCollector)
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves information about the given (kotlinc-generated) class to this cache, and stores changes between this class and its previous
|
||||
* version into the given [ChangesCollector].
|
||||
*
|
||||
* @param kotlinClassInfo A kotlin-generated class
|
||||
* @param sourceFiles The source files that the given class was generated from, or `null` if this information is not available
|
||||
* @param changesCollector A [ChangesCollector]
|
||||
*/
|
||||
fun saveClassToCache(kotlinClassInfo: KotlinClassInfo, sourceFiles: List<File>?, changesCollector: ChangesCollector) {
|
||||
val className = kotlinClassInfo.className
|
||||
|
||||
dirtyOutputClassesMap.notDirty(className)
|
||||
sourceFiles.forEach {
|
||||
sourceFiles?.forEach {
|
||||
sourceToClassesMap.add(it, className)
|
||||
}
|
||||
|
||||
internalNameToSource[className.internalName] = sourceFiles
|
||||
sourceFiles?.let { internalNameToSource[className.internalName] = it }
|
||||
|
||||
if (kotlinClass.classId.isLocal) return
|
||||
if (kotlinClassInfo.classId.isLocal) return
|
||||
|
||||
val header = kotlinClass.classHeader
|
||||
when (header.kind) {
|
||||
when (kotlinClassInfo.classKind) {
|
||||
KotlinClassHeader.Kind.FILE_FACADE -> {
|
||||
assert(sourceFiles.size == 1) { "Package part from several source files: $sourceFiles" }
|
||||
if (sourceFiles != null) {
|
||||
assert(sourceFiles.size == 1) { "Package part from several source files: $sourceFiles" }
|
||||
}
|
||||
packagePartMap.addPackagePart(className)
|
||||
|
||||
protoMap.process(kotlinClass, changesCollector)
|
||||
constantsMap.process(kotlinClass, changesCollector)
|
||||
inlineFunctionsMap.process(kotlinClass, changesCollector)
|
||||
protoMap.process(kotlinClassInfo, changesCollector)
|
||||
constantsMap.process(kotlinClassInfo, changesCollector)
|
||||
inlineFunctionsMap.process(kotlinClassInfo, changesCollector)
|
||||
}
|
||||
KotlinClassHeader.Kind.MULTIFILE_CLASS -> {
|
||||
val partNames = kotlinClass.classHeader.data?.toList()
|
||||
?: throw AssertionError("Multifile class has no parts: ${kotlinClass.className}")
|
||||
val partNames = kotlinClassInfo.classHeaderData?.toList()
|
||||
?: throw AssertionError("Multifile class has no parts: $className")
|
||||
multifileFacadeToParts[className] = partNames
|
||||
// When a class is replaced with a facade with the same name,
|
||||
// the class' proto wouldn't ever be deleted,
|
||||
@@ -154,25 +165,29 @@ open class IncrementalJvmCache(
|
||||
internalNameToSource.remove(className.internalName)
|
||||
|
||||
// TODO NO_CHANGES? (delegates only)
|
||||
constantsMap.process(kotlinClass, changesCollector)
|
||||
inlineFunctionsMap.process(kotlinClass, changesCollector)
|
||||
constantsMap.process(kotlinClassInfo, changesCollector)
|
||||
inlineFunctionsMap.process(kotlinClassInfo, changesCollector)
|
||||
}
|
||||
KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> {
|
||||
assert(sourceFiles.size == 1) { "Multifile class part from several source files: $sourceFiles" }
|
||||
if (sourceFiles != null) {
|
||||
assert(sourceFiles.size == 1) { "Multifile class part from several source files: $sourceFiles" }
|
||||
}
|
||||
packagePartMap.addPackagePart(className)
|
||||
partToMultifileFacade.set(className.internalName, header.multifileClassName!!)
|
||||
partToMultifileFacade.set(className.internalName, kotlinClassInfo.multifileClassName!!)
|
||||
|
||||
protoMap.process(kotlinClass, changesCollector)
|
||||
constantsMap.process(kotlinClass, changesCollector)
|
||||
inlineFunctionsMap.process(kotlinClass, changesCollector)
|
||||
protoMap.process(kotlinClassInfo, changesCollector)
|
||||
constantsMap.process(kotlinClassInfo, changesCollector)
|
||||
inlineFunctionsMap.process(kotlinClassInfo, changesCollector)
|
||||
}
|
||||
KotlinClassHeader.Kind.CLASS -> {
|
||||
assert(sourceFiles.size == 1) { "Class is expected to have only one source file: $sourceFiles" }
|
||||
addToClassStorage(kotlinClass, sourceFiles.first())
|
||||
if (sourceFiles != null) {
|
||||
assert(sourceFiles.size == 1) { "Class is expected to have only one source file: $sourceFiles" }
|
||||
addToClassStorage(kotlinClassInfo, sourceFiles.first())
|
||||
}
|
||||
|
||||
protoMap.process(kotlinClass, changesCollector)
|
||||
constantsMap.process(kotlinClass, changesCollector)
|
||||
inlineFunctionsMap.process(kotlinClass, changesCollector)
|
||||
protoMap.process(kotlinClassInfo, changesCollector)
|
||||
constantsMap.process(kotlinClassInfo, changesCollector)
|
||||
inlineFunctionsMap.process(kotlinClassInfo, changesCollector)
|
||||
}
|
||||
KotlinClassHeader.Kind.UNKNOWN, KotlinClassHeader.Kind.SYNTHETIC_CLASS -> {
|
||||
}
|
||||
@@ -278,8 +293,8 @@ open class IncrementalJvmCache(
|
||||
private inner class ProtoMap(storageFile: File) : BasicStringMap<ProtoMapValue>(storageFile, ProtoMapValueExternalizer) {
|
||||
|
||||
@Synchronized
|
||||
fun process(kotlinClass: LocalFileKotlinClass, changesCollector: ChangesCollector) {
|
||||
return put(kotlinClass, changesCollector)
|
||||
fun process(kotlinClassInfo: KotlinClassInfo, changesCollector: ChangesCollector) {
|
||||
return put(kotlinClassInfo, changesCollector)
|
||||
}
|
||||
|
||||
// A module mapping (.kotlin_module file) is stored in a cache,
|
||||
@@ -295,19 +310,17 @@ open class IncrementalJvmCache(
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun put(kotlinClass: LocalFileKotlinClass, changesCollector: ChangesCollector) {
|
||||
val header = kotlinClass.classHeader
|
||||
|
||||
val key = kotlinClass.className.internalName
|
||||
private fun put(kotlinClassInfo: KotlinClassInfo, changesCollector: ChangesCollector) {
|
||||
val key = kotlinClassInfo.className.internalName
|
||||
val oldData = storage[key]
|
||||
val newData = ProtoMapValue(
|
||||
header.kind != KotlinClassHeader.Kind.CLASS,
|
||||
BitEncoding.decodeBytes(header.data!!),
|
||||
header.strings!!
|
||||
kotlinClassInfo.classKind != KotlinClassHeader.Kind.CLASS,
|
||||
BitEncoding.decodeBytes(kotlinClassInfo.classHeaderData!!),
|
||||
kotlinClassInfo.classHeaderStrings!!
|
||||
)
|
||||
storage[key] = newData
|
||||
|
||||
val packageFqName = kotlinClass.className.packageFqName
|
||||
val packageFqName = kotlinClassInfo.className.packageFqName
|
||||
changesCollector.collectProtoChanges(oldData?.toProtoData(packageFqName), newData.toProtoData(packageFqName), packageProtoKey = key)
|
||||
}
|
||||
|
||||
@@ -368,31 +381,16 @@ open class IncrementalJvmCache(
|
||||
|
||||
// todo: reuse code with InlineFunctionsMap?
|
||||
private inner class ConstantsMap(storageFile: File) : BasicStringMap<Map<String, Any>>(storageFile, ConstantsMapExternalizer) {
|
||||
private fun getConstantsMap(bytes: ByteArray): Map<String, Any> {
|
||||
val result = HashMap<String, Any>()
|
||||
|
||||
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.API_VERSION) {
|
||||
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
|
||||
val staticFinal = Opcodes.ACC_STATIC or Opcodes.ACC_FINAL or Opcodes.ACC_PRIVATE
|
||||
if (value != null && access and staticFinal == Opcodes.ACC_STATIC or Opcodes.ACC_FINAL) {
|
||||
result[name] = value
|
||||
}
|
||||
return null
|
||||
}
|
||||
}, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
operator fun contains(className: JvmClassName): Boolean =
|
||||
className.internalName in storage
|
||||
|
||||
@Synchronized
|
||||
fun process(kotlinClass: LocalFileKotlinClass, changesCollector: ChangesCollector) {
|
||||
val key = kotlinClass.className.internalName
|
||||
fun process(kotlinClassInfo: KotlinClassInfo, changesCollector: ChangesCollector) {
|
||||
val key = kotlinClassInfo.className.internalName
|
||||
val oldMap = storage[key] ?: emptyMap()
|
||||
|
||||
val newMap = getConstantsMap(kotlinClass.fileContents)
|
||||
val newMap = kotlinClassInfo.constantsMap
|
||||
if (newMap.isNotEmpty()) {
|
||||
storage[key] = newMap
|
||||
} else {
|
||||
@@ -401,8 +399,18 @@ open class IncrementalJvmCache(
|
||||
|
||||
for (const in oldMap.keys + newMap.keys) {
|
||||
//Constant can be declared via companion object or via const field declaration
|
||||
changesCollector.collectMemberIfValueWasChanged(kotlinClass.scopeFqName(companion = true), const, oldMap[const], newMap[const])
|
||||
changesCollector.collectMemberIfValueWasChanged(kotlinClass.scopeFqName(companion = false), const, oldMap[const], newMap[const])
|
||||
changesCollector.collectMemberIfValueWasChanged(
|
||||
kotlinClassInfo.scopeFqName(companion = true),
|
||||
const,
|
||||
oldMap[const],
|
||||
newMap[const]
|
||||
)
|
||||
changesCollector.collectMemberIfValueWasChanged(
|
||||
kotlinClassInfo.scopeFqName(companion = false),
|
||||
const,
|
||||
oldMap[const],
|
||||
newMap[const]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,67 +498,20 @@ open class IncrementalJvmCache(
|
||||
value.dumpCollection()
|
||||
}
|
||||
|
||||
private fun addToClassStorage(kotlinClass: LocalFileKotlinClass, srcFile: File) {
|
||||
val (nameResolver, proto) = JvmProtoBufUtil.readClassDataFrom(kotlinClass.classHeader.data!!, kotlinClass.classHeader.strings!!)
|
||||
private fun addToClassStorage(classInfo: KotlinClassInfo, srcFile: File) {
|
||||
val (nameResolver, proto) = JvmProtoBufUtil.readClassDataFrom(classInfo.classHeaderData!!, classInfo.classHeaderStrings!!)
|
||||
addToClassStorage(proto, nameResolver, srcFile)
|
||||
}
|
||||
|
||||
private inner class InlineFunctionsMap(storageFile: File) :
|
||||
BasicStringMap<Map<String, Long>>(storageFile, StringToLongMapExternalizer) {
|
||||
private fun getInlineFunctionsMap(header: KotlinClassHeader, bytes: ByteArray): Map<String, Long> {
|
||||
val inlineFunctions = inlineFunctionsJvmNames(header)
|
||||
if (inlineFunctions.isEmpty()) return emptyMap()
|
||||
|
||||
val result = HashMap<String, Long>()
|
||||
var dummyVersion: Int = -1
|
||||
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.API_VERSION) {
|
||||
|
||||
override fun visit(
|
||||
version: Int,
|
||||
access: Int,
|
||||
name: String?,
|
||||
signature: String?,
|
||||
superName: String?,
|
||||
interfaces: Array<out String>?
|
||||
) {
|
||||
super.visit(version, access, name, signature, superName, interfaces)
|
||||
dummyVersion = version
|
||||
}
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String,
|
||||
desc: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor? {
|
||||
val dummyClassWriter = ClassWriter(0)
|
||||
dummyClassWriter.visit(dummyVersion, 0, "dummy", null, AsmTypes.OBJECT_TYPE.internalName, null)
|
||||
|
||||
return object : MethodVisitor(Opcodes.API_VERSION, dummyClassWriter.visitMethod(0, name, desc, null, exceptions)) {
|
||||
override fun visitEnd() {
|
||||
val jvmName = name + desc
|
||||
if (jvmName !in inlineFunctions) return
|
||||
|
||||
val dummyBytes = dummyClassWriter.toByteArray()!!
|
||||
|
||||
val hash = dummyBytes.md5()
|
||||
result[jvmName] = hash
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, 0)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun process(kotlinClass: LocalFileKotlinClass, changesCollector: ChangesCollector) {
|
||||
val key = kotlinClass.className.internalName
|
||||
fun process(kotlinClassInfo: KotlinClassInfo, changesCollector: ChangesCollector) {
|
||||
val key = kotlinClassInfo.className.internalName
|
||||
val oldMap = storage[key] ?: emptyMap()
|
||||
|
||||
val newMap = getInlineFunctionsMap(kotlinClass.classHeader, kotlinClass.fileContents)
|
||||
val newMap = kotlinClassInfo.inlineFunctionsMap
|
||||
if (newMap.isNotEmpty()) {
|
||||
storage[key] = newMap
|
||||
} else {
|
||||
@@ -559,7 +520,7 @@ open class IncrementalJvmCache(
|
||||
|
||||
for (fn in oldMap.keys + newMap.keys) {
|
||||
changesCollector.collectMemberIfValueWasChanged(
|
||||
kotlinClass.scopeFqName(),
|
||||
kotlinClassInfo.scopeFqName(),
|
||||
functionNameBySignature(fn),
|
||||
oldMap[fn],
|
||||
newMap[fn]
|
||||
@@ -602,13 +563,6 @@ sealed class ChangeInfo(val fqName: FqName) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun LocalFileKotlinClass.scopeFqName(companion: Boolean = false) = when (classHeader.kind) {
|
||||
KotlinClassHeader.Kind.CLASS -> {
|
||||
className.fqNameForClassNameWithoutDollars.let { if (companion) it.child(DEFAULT_NAME_FOR_COMPANION_OBJECT) else it }
|
||||
}
|
||||
else -> className.packageFqName
|
||||
}
|
||||
|
||||
fun ByteArray.md5(): Long {
|
||||
val d = MessageDigest.getInstance("MD5").digest(this)!!
|
||||
return ((d[0].toLong() and 0xFFL)
|
||||
@@ -640,3 +594,125 @@ fun <K : Comparable<K>, V> Map<K, V>.dumpMap(dumpValue: (V) -> String): String =
|
||||
@TestOnly
|
||||
fun <T : Comparable<T>> Collection<T>.dumpCollection(): String =
|
||||
"[${sorted().joinToString(", ", transform = Any::toString)}]"
|
||||
|
||||
/**
|
||||
* Minimal information about a kotlinc-generated class that will be used to compute recompilation-triggered changes to support incremental
|
||||
* compilation (see [IncrementalJvmCache.saveClassToCache]).
|
||||
*
|
||||
* It's important that this class contain only the minimal required information, as it will be part of the classpath snapshot of the
|
||||
* `KotlinCompile` task and the task needs to support compile avoidance. For example, this class should contain public method signatures,
|
||||
* and should not contain private method signatures, or method implementations.
|
||||
*/
|
||||
class KotlinClassInfo private constructor(
|
||||
val classId: ClassId,
|
||||
val classKind: KotlinClassHeader.Kind,
|
||||
val classHeaderData: Array<String>?,
|
||||
val classHeaderStrings: Array<String>?,
|
||||
@Suppress("SpellCheckingInspection") val multifileClassName: String?,
|
||||
val constantsMap: LinkedHashMap<String, Any>,
|
||||
val inlineFunctionsMap: LinkedHashMap<String, Long>
|
||||
) {
|
||||
|
||||
val className: JvmClassName by lazy { JvmClassName.byClassId(classId) }
|
||||
|
||||
fun scopeFqName(companion: Boolean = false) = when (classKind) {
|
||||
KotlinClassHeader.Kind.CLASS -> {
|
||||
className.fqNameForClassNameWithoutDollars.let { if (companion) it.child(DEFAULT_NAME_FOR_COMPANION_OBJECT) else it }
|
||||
}
|
||||
else -> className.packageFqName
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun createFrom(kotlinClass: LocalFileKotlinClass): KotlinClassInfo {
|
||||
return KotlinClassInfo(
|
||||
kotlinClass.classId,
|
||||
kotlinClass.classHeader.kind,
|
||||
kotlinClass.classHeader.data,
|
||||
kotlinClass.classHeader.strings,
|
||||
kotlinClass.classHeader.multifileClassName,
|
||||
getConstantsMap(kotlinClass.fileContents),
|
||||
getInlineFunctionsMap(kotlinClass.classHeader, kotlinClass.fileContents)
|
||||
)
|
||||
}
|
||||
|
||||
/** Creates [KotlinClassInfo] from the given classContents, or returns `null` if the class is not a kotlinc-generated class. */
|
||||
fun tryCreateFrom(classContents: ByteArray): KotlinClassInfo? {
|
||||
return FileBasedKotlinClass.create(classContents) { classId, _, classHeader, _ ->
|
||||
KotlinClassInfo(
|
||||
classId,
|
||||
classHeader.kind,
|
||||
classHeader.data,
|
||||
classHeader.strings,
|
||||
classHeader.multifileClassName,
|
||||
getConstantsMap(classContents),
|
||||
getInlineFunctionsMap(classHeader, classContents)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getConstantsMap(bytes: ByteArray): LinkedHashMap<String, Any> {
|
||||
val result = LinkedHashMap<String, Any>()
|
||||
|
||||
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.API_VERSION) {
|
||||
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
|
||||
val staticFinal = Opcodes.ACC_STATIC or Opcodes.ACC_FINAL or Opcodes.ACC_PRIVATE
|
||||
if (value != null && access and staticFinal == Opcodes.ACC_STATIC or Opcodes.ACC_FINAL) {
|
||||
result[name] = value
|
||||
}
|
||||
return null
|
||||
}
|
||||
}, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun getInlineFunctionsMap(header: KotlinClassHeader, bytes: ByteArray): LinkedHashMap<String, Long> {
|
||||
val inlineFunctions = inlineFunctionsJvmNames(header)
|
||||
if (inlineFunctions.isEmpty()) return LinkedHashMap()
|
||||
|
||||
val result = LinkedHashMap<String, Long>()
|
||||
var dummyVersion: Int = -1
|
||||
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.API_VERSION) {
|
||||
|
||||
override fun visit(
|
||||
version: Int,
|
||||
access: Int,
|
||||
name: String?,
|
||||
signature: String?,
|
||||
superName: String?,
|
||||
interfaces: Array<out String>?
|
||||
) {
|
||||
super.visit(version, access, name, signature, superName, interfaces)
|
||||
dummyVersion = version
|
||||
}
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String,
|
||||
desc: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor {
|
||||
val dummyClassWriter = ClassWriter(0)
|
||||
dummyClassWriter.visit(dummyVersion, 0, "dummy", null, AsmTypes.OBJECT_TYPE.internalName, null)
|
||||
|
||||
return object : MethodVisitor(Opcodes.API_VERSION, dummyClassWriter.visitMethod(0, name, desc, null, exceptions)) {
|
||||
override fun visitEnd() {
|
||||
val jvmName = name + desc
|
||||
if (jvmName !in inlineFunctions) return
|
||||
|
||||
val dummyBytes = dummyClassWriter.toByteArray()!!
|
||||
|
||||
val hash = dummyBytes.md5()
|
||||
result[jvmName] = hash
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, 0)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -48,13 +48,18 @@ open class LookupStorage(
|
||||
|
||||
@Volatile
|
||||
private var size: Int = 0
|
||||
private var oldSize: Int = 0
|
||||
|
||||
init {
|
||||
try {
|
||||
if (countersFile.exists()) {
|
||||
val lines = countersFile.readLines()
|
||||
size = lines[0].toInt()
|
||||
size = lines.firstOrNull()?.toIntOrNull() ?: throw IOException("$countersFile exists, but it is empty. " +
|
||||
"Counters file is corrupted")
|
||||
oldSize = size
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
throw IOException("Could not read $countersFile", e)
|
||||
}
|
||||
@@ -120,13 +125,15 @@ open class LookupStorage(
|
||||
@Synchronized
|
||||
override fun flush(memoryCachesOnly: Boolean) {
|
||||
try {
|
||||
if (size > 0) {
|
||||
if (!countersFile.exists()) {
|
||||
countersFile.parentFile.mkdirs()
|
||||
countersFile.createNewFile()
|
||||
}
|
||||
if (size != oldSize) {
|
||||
if (size > 0) {
|
||||
if (!countersFile.exists()) {
|
||||
countersFile.parentFile.mkdirs()
|
||||
countersFile.createNewFile()
|
||||
}
|
||||
|
||||
countersFile.writeText("$size\n")
|
||||
countersFile.writeText("$size\n0")
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
super.flush(memoryCachesOnly)
|
||||
|
||||
@@ -33,15 +33,19 @@ class CachingLazyStorage<K, V>(
|
||||
private val valueExternalizer: DataExternalizer<V>
|
||||
) : LazyStorage<K, V> {
|
||||
private var storage: PersistentHashMap<K, V>? = null
|
||||
private var isStorageFileExist = true
|
||||
|
||||
private fun getStorageIfExists(): PersistentHashMap<K, V>? {
|
||||
if (storage != null) return storage
|
||||
|
||||
if (!isStorageFileExist) return null
|
||||
|
||||
if (storageFile.exists()) {
|
||||
storage = createMap()
|
||||
return storage
|
||||
}
|
||||
|
||||
isStorageFileExist = false
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.incremental.storage
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.util.io.DataExternalizer
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
||||
import com.intellij.util.io.IOUtil
|
||||
@@ -26,7 +27,6 @@ import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
||||
import java.io.DataInput
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutput
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@@ -162,7 +162,7 @@ object ConstantsMapExternalizer : DataExternalizer<Map<String, Any>> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun read(input: DataInput): Map<String, Any>? {
|
||||
override fun read(input: DataInput): Map<String, Any> {
|
||||
val size = input.readInt()
|
||||
val map = HashMap<String, Any>(size)
|
||||
|
||||
@@ -197,15 +197,33 @@ object IntExternalizer : DataExternalizer<Int> {
|
||||
}
|
||||
}
|
||||
|
||||
object PathStringDescriptor : EnumeratorStringDescriptor() {
|
||||
override fun getHashCode(value: String) = FileUtil.pathHashCode(value)
|
||||
|
||||
override fun isEqual(val1: String, val2: String?) = FileUtil.pathsEqual(val1, val2)
|
||||
// Should be consistent with org.jetbrains.jps.incremental.storage.PathStringDescriptor for correct work of portable caches
|
||||
object PathStringDescriptor : EnumeratorStringDescriptor() {
|
||||
private const val PORTABLE_CACHES_PROPERTY = "org.jetbrains.jps.portable.caches"
|
||||
private val PORTABLE_CACHES = java.lang.Boolean.getBoolean(PORTABLE_CACHES_PROPERTY)
|
||||
|
||||
override fun getHashCode(path: String): Int {
|
||||
if (!PORTABLE_CACHES) return FileUtil.pathHashCode(path)
|
||||
// On case insensitive OS hash calculated from value converted to lower case
|
||||
return if (StringUtil.isEmpty(path)) 0 else FileUtil.toCanonicalPath(path).hashCode()
|
||||
}
|
||||
|
||||
override fun isEqual(val1: String, val2: String?): Boolean {
|
||||
if (!PORTABLE_CACHES) return FileUtil.pathsEqual(val1, val2)
|
||||
// On case insensitive OS hash calculated from path converted to lower case
|
||||
if (val1 == val2) return true
|
||||
if (val2 == null) return false
|
||||
|
||||
val path1 = FileUtil.toCanonicalPath(val1)
|
||||
val path2 = FileUtil.toCanonicalPath(val2)
|
||||
return path1 == path2
|
||||
}
|
||||
}
|
||||
|
||||
open class CollectionExternalizer<T>(
|
||||
private val elementExternalizer: DataExternalizer<T>,
|
||||
private val newCollection: () -> MutableCollection<T>
|
||||
private val elementExternalizer: DataExternalizer<T>,
|
||||
private val newCollection: () -> MutableCollection<T>
|
||||
) : DataExternalizer<Collection<T>> {
|
||||
override fun read(input: DataInput): Collection<T> {
|
||||
val result = newCollection()
|
||||
|
||||
@@ -29,7 +29,7 @@ buildscript {
|
||||
dependencies {
|
||||
bootstrapCompilerClasspath(kotlin("compiler-embeddable", bootstrapKotlinVersion))
|
||||
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.31")
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.32")
|
||||
classpath(kotlin("gradle-plugin", bootstrapKotlinVersion))
|
||||
classpath(kotlin("serialization", bootstrapKotlinVersion))
|
||||
classpath("org.jetbrains.dokka:dokka-gradle-plugin:0.9.17")
|
||||
@@ -148,11 +148,9 @@ extra["versions.kotlinx-collections-immutable-jvm"] = immutablesVersion
|
||||
extra["versions.ktor-network"] = "1.0.1"
|
||||
|
||||
if (!project.hasProperty("versions.kotlin-native")) {
|
||||
extra["versions.kotlin-native"] = "1.6.0-dev-248"
|
||||
extra["versions.kotlin-native"] = "1.6.0-dev-2972"
|
||||
}
|
||||
|
||||
val effectSystemEnabled by extra(project.getBooleanProperty("kotlin.compiler.effectSystemEnabled") ?: false)
|
||||
val newInferenceEnabled by extra(project.getBooleanProperty("kotlin.compiler.newInferenceEnabled") ?: false)
|
||||
val useJvmFir by extra(project.kotlinBuildProperties.useFir)
|
||||
|
||||
val intellijSeparateSdks = project.getBooleanProperty("intellijSeparateSdks") ?: false
|
||||
@@ -161,11 +159,7 @@ extra["intellijSeparateSdks"] = intellijSeparateSdks
|
||||
|
||||
extra["IntellijCoreDependencies"] =
|
||||
listOf(
|
||||
when {
|
||||
Platform[203].orHigher() -> "asm-all-9.0"
|
||||
Platform[202].orHigher() -> "asm-all-8.0.1"
|
||||
else -> "asm-all-7.0.1"
|
||||
},
|
||||
"asm-all-9.0",
|
||||
"guava",
|
||||
"jdom",
|
||||
"jna",
|
||||
@@ -321,15 +315,7 @@ extra["compilerArtifactsForIde"] = listOf(
|
||||
|
||||
// TODO: fix remaining warnings and remove this property.
|
||||
extra["tasksWithWarnings"] = listOf(
|
||||
":kotlin-stdlib:compileTestKotlin",
|
||||
":kotlin-stdlib-jdk7:compileTestKotlin",
|
||||
":kotlin-stdlib-jdk8:compileTestKotlin",
|
||||
":plugins:uast-kotlin-base:compileKotlin",
|
||||
":plugins:uast-kotlin-base:compileTestKotlin",
|
||||
":plugins:uast-kotlin:compileKotlin",
|
||||
":plugins:uast-kotlin:compileTestKotlin",
|
||||
":plugins:uast-kotlin-fir:compileKotlin",
|
||||
":plugins:uast-kotlin-fir:compileTestKotlin"
|
||||
":kotlin-gradle-plugin:compileKotlin"
|
||||
)
|
||||
|
||||
val tasksWithWarnings: List<String> by extra
|
||||
@@ -449,9 +435,14 @@ allprojects {
|
||||
project.configureShadowJarSubstitutionInCompileClasspath()
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile> {
|
||||
options.compilerArgs.add("-Xlint:deprecation")
|
||||
options.compilerArgs.add("-Xlint:unchecked")
|
||||
options.compilerArgs.add("-Werror")
|
||||
}
|
||||
|
||||
val commonCompilerArgs = listOfNotNull(
|
||||
"-Xopt-in=kotlin.RequiresOptIn",
|
||||
"-Xread-deserialized-contracts",
|
||||
"-progressive".takeIf { hasProperty("test.progressive.mode") }
|
||||
)
|
||||
|
||||
@@ -467,7 +458,8 @@ allprojects {
|
||||
"-Xjvm-default=compatibility",
|
||||
"-Xno-optimized-callable-references",
|
||||
"-Xno-kotlin-nothing-value-exception",
|
||||
"-Xnormalize-constructor-calls=enable"
|
||||
"-Xskip-runtime-version-check",
|
||||
"-Xsuppress-deprecated-jvm-target-warning" // Remove as soon as there are no modules for JDK 1.6 & 1.7
|
||||
)
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
|
||||
@@ -481,16 +473,11 @@ allprojects {
|
||||
}
|
||||
}
|
||||
|
||||
if (!kotlinBuildProperties.isInJpsBuildIdeaSync && !kotlinBuildProperties.useFir && !kotlinBuildProperties.disableWerror) {
|
||||
// For compiler and stdlib, allWarningsAsErrors is configured in the corresponding "root" projects
|
||||
// (compiler/build.gradle.kts and libraries/commonConfiguration.gradle).
|
||||
val projectsWithWarningsAsErrors = listOf("core", "plugins").map { File(it).absoluteFile }
|
||||
if (projectsWithWarningsAsErrors.any(projectDir::startsWith)) {
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
|
||||
if (path !in tasksWithWarnings) {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors = true
|
||||
}
|
||||
if (!kotlinBuildProperties.disableWerror) {
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
|
||||
if (path !in tasksWithWarnings) {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -734,6 +721,8 @@ tasks {
|
||||
dependsOn(":kotlin-scripting-jsr223-test:embeddableTest")
|
||||
dependsOn(":kotlin-main-kts-test:test")
|
||||
dependsOn(":kotlin-main-kts-test:testWithIr")
|
||||
dependsOn(":kotlin-scripting-ide-services-test:test")
|
||||
dependsOn(":kotlin-scripting-ide-services-test:embeddableTest")
|
||||
dependsOn(":kotlin-scripting-js-test:test")
|
||||
}
|
||||
|
||||
@@ -1137,4 +1126,4 @@ afterEvaluate {
|
||||
"https://cache-redirector.jetbrains.com/github.com/yarnpkg/yarn/releases/download"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.31")
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.32")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${project.bootstrapKotlinVersion}")
|
||||
classpath("org.jetbrains.kotlin:kotlin-sam-with-receiver:${project.bootstrapKotlinVersion}")
|
||||
}
|
||||
@@ -143,7 +143,7 @@ java {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib", embeddedKotlinVersion))
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${project.bootstrapKotlinVersion}")
|
||||
implementation("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.31")
|
||||
implementation("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.32")
|
||||
implementation("com.gradle.publish:plugin-publish-plugin:0.14.0")
|
||||
|
||||
implementation("net.rubygrapefruit:native-platform:${property("versions.native-platform")}")
|
||||
|
||||
@@ -79,7 +79,6 @@ val jpsStandalone by configurations.creating
|
||||
val jpsStandaloneForIde by configurations.creating
|
||||
val intellijCore by configurations.creating
|
||||
val intellijCoreForIde by configurations.creating
|
||||
val nodeJSPlugin by configurations.creating
|
||||
|
||||
/**
|
||||
* Special repository for annotations.jar required for idea runtime only.
|
||||
|
||||
@@ -19,7 +19,17 @@ val KotlinBuildProperties.jarCompression: Boolean get() = getBoolean("kotlin.bui
|
||||
|
||||
val KotlinBuildProperties.ignoreTestFailures: Boolean get() = getBoolean("ignoreTestFailures", isTeamcityBuild)
|
||||
|
||||
val KotlinBuildProperties.disableWerror: Boolean get() = getBoolean("kotlin.build.disable.werror", false)
|
||||
val KotlinBuildProperties.disableWerror: Boolean
|
||||
get() = getBoolean("kotlin.build.disable.werror") || useFir || isInJpsBuildIdeaSync || getBoolean("test.progressive.mode")
|
||||
|
||||
val KotlinBuildProperties.pathToKotlinModularizedTestData: String?
|
||||
get() = getOrNull("kotlin.fir.modularized.testdata.kotlin") as? String
|
||||
|
||||
val KotlinBuildProperties.pathToIntellijModularizedTestData: String?
|
||||
get() = getOrNull("kotlin.fir.modularized.testdata.intellij") as? String
|
||||
|
||||
val KotlinBuildProperties.pathToYoutrackModularizedTestData: String?
|
||||
get() = getOrNull("kotlin.fir.modularized.testdata.youtrack") as? String
|
||||
|
||||
val KotlinBuildProperties.isObsoleteJdkOverrideEnabled: Boolean
|
||||
get() = getBoolean("kotlin.build.isObsoleteJdkOverrideEnabled", false)
|
||||
|
||||
@@ -89,4 +89,4 @@ val Project.isIdeaActive
|
||||
get() = providers.systemProperty("idea.active").forUseAtConfigurationTime().isPresent
|
||||
|
||||
val Project.intellijCommunityDir: File
|
||||
get() = rootDir.resolve("kotlin-ide/intellij/community").takeIf { it.isDirectory } ?: rootDir.resolve("kotlin-ide/intellij")
|
||||
get() = rootDir.resolve("intellij/community").takeIf { it.isDirectory } ?: rootDir.resolve("intellij")
|
||||
@@ -26,7 +26,7 @@ fun CompatibilityPredicate.or(other: CompatibilityPredicate): CompatibilityPredi
|
||||
}
|
||||
|
||||
enum class Platform : CompatibilityPredicate {
|
||||
P202, P203;
|
||||
P203;
|
||||
|
||||
val version: Int = name.drop(1).toInt()
|
||||
|
||||
@@ -43,10 +43,7 @@ enum class Platform : CompatibilityPredicate {
|
||||
}
|
||||
|
||||
enum class Ide(val platform: Platform) : CompatibilityPredicate {
|
||||
IJ202(Platform.P202),
|
||||
IJ203(Platform.P203),
|
||||
|
||||
AS42(Platform.P202);
|
||||
IJ203(Platform.P203);
|
||||
|
||||
val kind = Kind.values().first { it.shortName == name.take(2) }
|
||||
val version = name.dropWhile { !it.isDigit() }.toInt()
|
||||
|
||||
@@ -22,7 +22,8 @@ enum class JdkMajorVersion(
|
||||
JDK_10(10, mandatory = false, overrideMajorVersion = 11),
|
||||
JDK_11(11, mandatory = false),
|
||||
JDK_15(15, mandatory = false),
|
||||
JDK_16(16, mandatory = false);
|
||||
JDK_16(16, mandatory = false),
|
||||
JDK_17(17, mandatory = false);
|
||||
|
||||
fun isMandatory(): Boolean = mandatory
|
||||
|
||||
@@ -180,4 +181,4 @@ fun Project.getJdkVersionWithOverride(jdkVersion: JdkMajorVersion): JdkMajorVers
|
||||
} else {
|
||||
jdkVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,12 +9,8 @@ import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.kotlin.dsl.extra
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.gradle.process.CommandLineArgumentProvider
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
@JvmOverloads
|
||||
fun Project.configureJava9Compilation(
|
||||
@@ -59,17 +55,3 @@ private class Java9AdditionalArgumentsProvider(
|
||||
"-Xlint:-requires-transitive-automatic" // suppress automatic module transitive dependencies in kotlin.test
|
||||
)
|
||||
}
|
||||
|
||||
fun Project.disableDeprecatedJvmTargetWarning() {
|
||||
if (!kotlinBuildProperties.useFir && !kotlinBuildProperties.disableWerror) {
|
||||
val tasksWithWarnings: List<String> by rootProject.extra
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
if (!tasksWithWarnings.contains(path)) {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors = true
|
||||
freeCompilerArgs += "-Xsuppress-deprecated-jvm-target-warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,11 +37,10 @@ configurations {
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.languageVersion = "1.3"
|
||||
kotlinOptions.apiVersion = "1.3"
|
||||
kotlinOptions.languageVersion = "1.4"
|
||||
kotlinOptions.apiVersion = "1.4"
|
||||
kotlinOptions.freeCompilerArgs += listOf(
|
||||
"-Xskip-prerelease-check",
|
||||
"-Xskip-runtime-version-check",
|
||||
"-Xsuppress-version-warnings",
|
||||
"-Xuse-ir" // Needed as long as languageVersion is less than 1.5.
|
||||
)
|
||||
|
||||
@@ -82,8 +82,6 @@ fun Project.intellijCoreDep() = "kotlin.build:intellij-core:${rootProject.extra[
|
||||
|
||||
fun Project.jpsStandalone() = "kotlin.build:jps-standalone:${rootProject.extra["versions.intellijSdk"]}"
|
||||
|
||||
fun Project.nodeJSPlugin() = "kotlin.build:NodeJS:${rootProject.extra["versions.idea.NodeJS"]}"
|
||||
|
||||
fun Project.jpsBuildTest() = "com.jetbrains.intellij.idea:jps-build-test:${rootProject.extra["versions.intellijSdk"]}"
|
||||
|
||||
fun Project.kotlinxCollectionsImmutable() = "org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:${rootProject.extra["versions.kotlinx-collections-immutable"]}"
|
||||
|
||||
@@ -171,16 +171,18 @@ fun Project.configureDefaultPublishing() {
|
||||
.all { configureRepository() }
|
||||
}
|
||||
|
||||
private fun Project.getSensitiveProperty(name: String): String? {
|
||||
return project.findProperty(name) as? String ?: System.getenv(name)
|
||||
}
|
||||
|
||||
private fun Project.configureSigning() {
|
||||
configure<SigningExtension> {
|
||||
sign(extensions.getByType<PublishingExtension>().publications) // all publications
|
||||
|
||||
val signKeyId = project.findProperty("signKeyId") as? String
|
||||
val signKeyId = project.getSensitiveProperty("signKeyId")
|
||||
if (!signKeyId.isNullOrBlank()) {
|
||||
val signKeyPrivate = project.findProperty("signKeyPrivate") as? String
|
||||
?: error("Parameter `signKeyPrivate` not found")
|
||||
val signKeyPassphrase = project.findProperty("signKeyPassphrase") as? String
|
||||
?: error("Parameter `signKeyPassphrase` not found")
|
||||
val signKeyPrivate = project.getSensitiveProperty("signKeyPrivate") ?: error("Parameter `signKeyPrivate` not found")
|
||||
val signKeyPassphrase = project.getSensitiveProperty("signKeyPassphrase") ?: error("Parameter `signKeyPassphrase` not found")
|
||||
useInMemoryPgpKeys(signKeyId, signKeyPrivate, signKeyPassphrase)
|
||||
} else {
|
||||
useGpgCmd()
|
||||
|
||||
@@ -15,7 +15,10 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestFiles
|
||||
import org.jetbrains.kotlin.codegen.GenerationUtils
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
@@ -23,11 +26,11 @@ import org.jetbrains.kotlin.test.*
|
||||
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
|
||||
import org.jetbrains.kotlin.test.model.DependencyKind
|
||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||
import org.jetbrains.kotlin.test.model.ResultingArtifact
|
||||
import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerTest
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.impl.BackendKindExtractorImpl
|
||||
import org.jetbrains.kotlin.test.services.impl.TemporaryDirectoryManagerImpl
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.CodegenHelpersSourceFilesProvider
|
||||
@@ -382,6 +385,7 @@ class CodegenTestsOnAndroidGenerator private constructor(private val pathManager
|
||||
"test${testDataFile.nameWithoutExtension.replaceFirstChar(Char::uppercaseChar)}",
|
||||
emptySet()
|
||||
)
|
||||
startingArtifactFactory = { ResultingArtifact.Source() }
|
||||
}.build(testDataFile.path)
|
||||
}
|
||||
|
||||
@@ -406,7 +410,6 @@ class CodegenTestsOnAndroidGenerator private constructor(private val pathManager
|
||||
|
||||
assertions = JUnit5Assertions
|
||||
useAdditionalService<TemporaryDirectoryManager>(::TemporaryDirectoryManagerImpl)
|
||||
useAdditionalService<BackendKindExtractor>(::BackendKindExtractorImpl)
|
||||
useSourcePreprocessor(*AbstractKotlinCompilerTest.defaultPreprocessors.toTypedArray())
|
||||
useDirectives(*AbstractKotlinCompilerTest.defaultDirectiveContainers.toTypedArray())
|
||||
}
|
||||
|
||||
@@ -5,21 +5,15 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.common
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.coroutinesIntrinsicsPackageFqName
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.isTopLevelInPackage
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
val COROUTINE_SUSPENDED_NAME = Name.identifier("COROUTINE_SUSPENDED")
|
||||
|
||||
fun FunctionDescriptor.isBuiltInIntercepted(languageVersionSettings: LanguageVersionSettings): Boolean =
|
||||
!languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines) &&
|
||||
isTopLevelInPackage("intercepted", languageVersionSettings.coroutinesIntrinsicsPackageFqName().asString())
|
||||
|
||||
fun FunctionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(languageVersionSettings: LanguageVersionSettings): Boolean =
|
||||
fun FunctionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(): Boolean =
|
||||
isTopLevelInPackage(
|
||||
"suspendCoroutineUninterceptedOrReturn",
|
||||
languageVersionSettings.coroutinesIntrinsicsPackageFqName().asString()
|
||||
StandardNames.COROUTINES_INTRINSICS_PACKAGE_FQ_NAME.asString()
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
|
||||
@@ -26,7 +26,7 @@ class AccessorForConstructorDescriptor(
|
||||
containingDeclaration: DeclarationDescriptor,
|
||||
override val superCallTarget: ClassDescriptor?,
|
||||
override val accessorKind: AccessorKind
|
||||
) : AbstractAccessorForFunctionDescriptor(containingDeclaration, Name.special("<init>")),
|
||||
) : AbstractAccessorForFunctionDescriptor(containingDeclaration, SpecialNames.INIT),
|
||||
ClassConstructorDescriptor,
|
||||
AccessorForCallableDescriptor<ConstructorDescriptor> {
|
||||
|
||||
|
||||
@@ -475,7 +475,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
|
||||
List<Type> superCtorArgTypes = new ArrayList<>();
|
||||
if (superClassAsmType.equals(LAMBDA) || functionReferenceTarget != null ||
|
||||
CoroutineCodegenUtilKt.isCoroutineSuperClass(state.getLanguageVersionSettings(), superClassAsmType.getInternalName())
|
||||
CoroutineCodegenUtilKt.isCoroutineSuperClass(superClassAsmType.getInternalName())
|
||||
) {
|
||||
iv.iconst(CodegenUtilKt.getArity(funDescriptor));
|
||||
superCtorArgTypes.add(Type.INT_TYPE);
|
||||
|
||||
@@ -89,7 +89,6 @@ import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor;
|
||||
import org.jetbrains.kotlin.types.*;
|
||||
import org.jetbrains.kotlin.types.checker.ClassicTypeSystemContextImpl;
|
||||
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS;
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker;
|
||||
import org.jetbrains.kotlin.types.model.TypeParameterMarker;
|
||||
import org.jetbrains.kotlin.types.typesApproximation.CapturedTypeApproximationKt;
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
||||
@@ -1288,11 +1287,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
@NotNull
|
||||
private static CallableDescriptor unwrapOriginalReceiverOwnerForSuspendLambda(@NotNull MethodContext context) {
|
||||
FunctionDescriptor originalForDoResume =
|
||||
context.getFunctionDescriptor().getUserData(CoroutineCodegenUtilKt.INITIAL_SUSPEND_DESCRIPTOR_FOR_DO_RESUME);
|
||||
FunctionDescriptor originalForInvokeSuspend =
|
||||
context.getFunctionDescriptor().getUserData(CoroutineCodegenUtilKt.INITIAL_SUSPEND_DESCRIPTOR_FOR_INVOKE_SUSPEND);
|
||||
|
||||
if (originalForDoResume != null) {
|
||||
return originalForDoResume;
|
||||
if (originalForInvokeSuspend != null) {
|
||||
return originalForInvokeSuspend;
|
||||
}
|
||||
|
||||
if (context.getFunctionDescriptor().isSuspend()) {
|
||||
@@ -2617,26 +2616,6 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
? coroutineInstanceValueForSuspensionPoint
|
||||
: getContinuationParameterFromEnclosingSuspendFunction(resolvedCall);
|
||||
|
||||
if (coroutineInstanceValue != null && needsExperimentalCoroutinesWrapper(resolvedCall.getCandidateDescriptor())) {
|
||||
StackValue releaseContinuation = coroutineInstanceValue;
|
||||
coroutineInstanceValue = new StackValue(CoroutineCodegenUtilKt.EXPERIMENTAL_CONTINUATION_ASM_TYPE) {
|
||||
@Override
|
||||
public void putSelector(
|
||||
@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v
|
||||
) {
|
||||
releaseContinuation.put(CoroutineCodegenUtilKt.RELEASE_CONTINUATION_ASM_TYPE, v);
|
||||
invokeCoroutineMigrationMethod(
|
||||
v,
|
||||
"toExperimentalContinuation",
|
||||
Type.getMethodDescriptor(
|
||||
CoroutineCodegenUtilKt.EXPERIMENTAL_CONTINUATION_ASM_TYPE,
|
||||
CoroutineCodegenUtilKt.RELEASE_CONTINUATION_ASM_TYPE
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
tempVariables.put(continuationExpression, coroutineInstanceValue);
|
||||
}
|
||||
|
||||
@@ -2774,7 +2753,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
}
|
||||
|
||||
SuspensionPointKind suspensionPointKind =
|
||||
CoroutineCodegenUtilKt.isSuspensionPoint(resolvedCall, this, state.getLanguageVersionSettings());
|
||||
CoroutineCodegenUtilKt.isSuspensionPoint(resolvedCall, this);
|
||||
boolean maybeSuspensionPoint = suspensionPointKind != SuspensionPointKind.NEVER && !insideCallableReference();
|
||||
boolean isConstructor = resolvedCall.getResultingDescriptor() instanceof ConstructorDescriptor;
|
||||
if (!(callableMethod instanceof IntrinsicWithSpecialReceiver)) {
|
||||
@@ -2833,7 +2812,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
KotlinType unboxedInlineClass = CoroutineCodegenUtilKt.originalReturnTypeOfSuspendFunctionReturningUnboxedInlineClass(
|
||||
(FunctionDescriptor) resolvedCall.getResultingDescriptor(), typeMapper);
|
||||
if (unboxedInlineClass != null) {
|
||||
CoroutineCodegenUtilKt.generateCoroutineSuspendedCheck(v, state.getLanguageVersionSettings());
|
||||
CoroutineCodegenUtilKt.generateCoroutineSuspendedCheck(v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2948,7 +2927,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
FunctionDescriptor original =
|
||||
CoroutineCodegenUtilKt.getOriginalSuspendFunctionView(
|
||||
unwrapInitialSignatureDescriptor(DescriptorUtils.unwrapFakeOverride((FunctionDescriptor) descriptor.getOriginal())),
|
||||
bindingContext, state
|
||||
bindingContext
|
||||
);
|
||||
|
||||
FunctionDescriptor functionDescriptor =
|
||||
@@ -5243,7 +5222,7 @@ The "returned" value of try expression with no finally is either the last expres
|
||||
}
|
||||
|
||||
CodegenUtilKt.generateAsCast(
|
||||
v, rightKotlinType, boxedRightType, safeAs, state.getLanguageVersionSettings(), state.getUnifiedNullChecks()
|
||||
v, rightKotlinType, boxedRightType, safeAs, state.getUnifiedNullChecks()
|
||||
);
|
||||
|
||||
return Unit.INSTANCE;
|
||||
@@ -5297,7 +5276,7 @@ The "returned" value of try expression with no finally is either the last expres
|
||||
return null;
|
||||
}
|
||||
|
||||
CodegenUtilKt.generateIsCheck(v, rhsKotlinType, type, state.getLanguageVersionSettings().supportsFeature(LanguageFeature.ReleaseCoroutines));
|
||||
CodegenUtilKt.generateIsCheck(v, rhsKotlinType, type);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -481,8 +481,7 @@ public class FunctionCodegen {
|
||||
}
|
||||
|
||||
if (!functionDescriptor.isExternal()) {
|
||||
generateMethodBody(mv, functionDescriptor, methodContext, jvmSignature, strategy, memberCodegen, state.getJvmDefaultMode(),
|
||||
state.getLanguageVersionSettings().supportsFeature(LanguageFeature.ReleaseCoroutines));
|
||||
generateMethodBody(mv, functionDescriptor, methodContext, jvmSignature, strategy, memberCodegen, state.getJvmDefaultMode());
|
||||
}
|
||||
else if (staticInCompanionObject) {
|
||||
// native @JvmStatic foo() in companion object should delegate to the static native function moved to the outer class
|
||||
@@ -583,8 +582,7 @@ public class FunctionCodegen {
|
||||
@NotNull JvmMethodSignature signature,
|
||||
@NotNull FunctionGenerationStrategy strategy,
|
||||
@NotNull MemberCodegen<?> parentCodegen,
|
||||
@NotNull JvmDefaultMode jvmDefaultMode,
|
||||
boolean isReleaseCoroutines
|
||||
@NotNull JvmDefaultMode jvmDefaultMode
|
||||
) {
|
||||
mv.visitCode();
|
||||
|
||||
@@ -594,8 +592,7 @@ public class FunctionCodegen {
|
||||
KotlinTypeMapper typeMapper = parentCodegen.typeMapper;
|
||||
if (BuiltinSpecialBridgesUtil.shouldHaveTypeSafeBarrier(functionDescriptor, typeMapper::mapAsmMethod)) {
|
||||
generateTypeCheckBarrierIfNeeded(
|
||||
new InstructionAdapter(mv), functionDescriptor, signature.getReturnType(), null, typeMapper,
|
||||
isReleaseCoroutines);
|
||||
new InstructionAdapter(mv), functionDescriptor, signature.getReturnType(), null, typeMapper);
|
||||
}
|
||||
|
||||
Label methodEntry = null;
|
||||
@@ -1430,8 +1427,7 @@ public class FunctionCodegen {
|
||||
MemberCodegen.markLineNumberForDescriptor(owner.getThisDescriptor(), iv);
|
||||
|
||||
if (delegateTo.getArgumentTypes().length > 0 && isSpecialBridge) {
|
||||
generateTypeCheckBarrierIfNeeded(iv, descriptor, bridge.getReturnType(), delegateTo.getArgumentTypes(), typeMapper,
|
||||
state.getLanguageVersionSettings().supportsFeature(LanguageFeature.ReleaseCoroutines));
|
||||
generateTypeCheckBarrierIfNeeded(iv, descriptor, bridge.getReturnType(), delegateTo.getArgumentTypes(), typeMapper);
|
||||
}
|
||||
|
||||
iv.load(0, OBJECT_TYPE);
|
||||
@@ -1477,8 +1473,7 @@ public class FunctionCodegen {
|
||||
@NotNull FunctionDescriptor descriptor,
|
||||
@NotNull Type returnType,
|
||||
@Nullable Type[] delegateParameterTypes,
|
||||
@NotNull KotlinTypeMapper typeMapper,
|
||||
boolean isReleaseCoroutines
|
||||
@NotNull KotlinTypeMapper typeMapper
|
||||
) {
|
||||
BuiltinMethodsWithSpecialGenericSignature.TypeSafeBarrierDescription typeSafeBarrierDescription =
|
||||
BuiltinMethodsWithSpecialGenericSignature.getDefaultValueForOverriddenBuiltinFunction(descriptor);
|
||||
@@ -1512,7 +1507,7 @@ public class FunctionCodegen {
|
||||
} else {
|
||||
targetBoxedType = boxType(delegateParameterTypes[i]);
|
||||
}
|
||||
CodegenUtilKt.generateIsCheck(iv, kotlinType, targetBoxedType, isReleaseCoroutines);
|
||||
CodegenUtilKt.generateIsCheck(iv, kotlinType, targetBoxedType);
|
||||
iv.ifeq(defaultBranch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames.COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME
|
||||
import org.jetbrains.kotlin.builtins.createFunctionType
|
||||
import org.jetbrains.kotlin.codegen.coroutines.coroutinesJvmInternalPackageFqName
|
||||
import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView
|
||||
import org.jetbrains.kotlin.codegen.coroutines.isSuspendLambdaOrLocalFunction
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
||||
@@ -33,8 +32,7 @@ class JvmRuntimeTypes(
|
||||
private val generateOptimizedCallableReferenceSuperClasses: Boolean
|
||||
) {
|
||||
private val kotlinJvmInternalPackage = MutablePackageFragmentDescriptor(module, FqName("kotlin.jvm.internal"))
|
||||
private val kotlinCoroutinesJvmInternalPackage =
|
||||
MutablePackageFragmentDescriptor(module, languageVersionSettings.coroutinesJvmInternalPackageFqName())
|
||||
private val kotlinCoroutinesJvmInternalPackage = MutablePackageFragmentDescriptor(module, COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME)
|
||||
|
||||
private fun internal(className: String, packageFragment: PackageFragmentDescriptor = kotlinJvmInternalPackage): Lazy<ClassDescriptor> =
|
||||
lazy { createClass(packageFragment, className) }
|
||||
@@ -53,24 +51,16 @@ class JvmRuntimeTypes(
|
||||
private val localVariableReference: ClassDescriptor by internal("LocalVariableReference")
|
||||
private val mutableLocalVariableReference: ClassDescriptor by internal("MutableLocalVariableReference")
|
||||
|
||||
private val coroutineImpl: ClassDescriptor by internal("CoroutineImpl", kotlinCoroutinesJvmInternalPackage)
|
||||
private val continuationImpl: ClassDescriptor by coroutinesInternal("ContinuationImpl")
|
||||
private val restrictedContinuationImpl: ClassDescriptor by coroutinesInternal("RestrictedContinuationImpl")
|
||||
private val suspendLambda: ClassDescriptor by coroutinesInternal("SuspendLambda")
|
||||
private val restrictedSuspendLambda: ClassDescriptor by coroutinesInternal("RestrictedSuspendLambda")
|
||||
|
||||
private val suspendFunctionInterface: ClassDescriptor? by lazy {
|
||||
if (languageVersionSettings.isReleaseCoroutines())
|
||||
createClass(kotlinCoroutinesJvmInternalPackage, "SuspendFunction", ClassKind.INTERFACE)
|
||||
else null
|
||||
createClass(kotlinCoroutinesJvmInternalPackage, "SuspendFunction", ClassKind.INTERFACE)
|
||||
}
|
||||
|
||||
private fun createCoroutineSuperClass(className: String): ClassDescriptor {
|
||||
return if (languageVersionSettings.isReleaseCoroutines())
|
||||
createClass(kotlinCoroutinesJvmInternalPackage, className)
|
||||
else
|
||||
coroutineImpl
|
||||
}
|
||||
private fun createCoroutineSuperClass(className: String): ClassDescriptor = createClass(kotlinCoroutinesJvmInternalPackage, className)
|
||||
|
||||
private val propertyReferences: List<ClassDescriptor> by propertyClasses("PropertyReference", "")
|
||||
private val mutablePropertyReferences: List<ClassDescriptor> by propertyClasses("MutablePropertyReference", "")
|
||||
@@ -94,7 +84,7 @@ class JvmRuntimeTypes(
|
||||
fun getSupertypesForClosure(descriptor: FunctionDescriptor): Collection<KotlinType> {
|
||||
val actualFunctionDescriptor =
|
||||
if (descriptor.isSuspend)
|
||||
getOrCreateJvmSuspendFunctionView(descriptor, languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines))
|
||||
getOrCreateJvmSuspendFunctionView(descriptor)
|
||||
else
|
||||
descriptor
|
||||
|
||||
@@ -118,7 +108,7 @@ class JvmRuntimeTypes(
|
||||
if (descriptor.isSuspend) {
|
||||
return mutableListOf<KotlinType>().apply {
|
||||
if (actualFunctionDescriptor.extensionReceiverParameter?.type
|
||||
?.isRestrictsSuspensionReceiver(languageVersionSettings) == true
|
||||
?.isRestrictsSuspensionReceiver() == true
|
||||
) {
|
||||
if (descriptor.isSuspendLambdaOrLocalFunction()) {
|
||||
add(restrictedSuspendLambda.defaultType)
|
||||
|
||||
@@ -87,7 +87,7 @@ class StringConcatGenerator(val mode: JvmStringConcat, val mv: InstructionAdapte
|
||||
paramTypes.add(type)
|
||||
paramSlots += type.size
|
||||
template.append("\u0001")
|
||||
if (paramSlots >= 200) {
|
||||
if (paramSlots >= 199) {
|
||||
// Concatenate current arguments into string
|
||||
// because of `StringConcatFactory` limitation add use it as new argument for further processing:
|
||||
// "The number of parameter slots in {@code concatType} is less than or equal to 200"
|
||||
@@ -166,4 +166,4 @@ class StringConcatGenerator(val mode: JvmStringConcat, val mv: InstructionAdapte
|
||||
StringConcatGenerator(state.runtimeStringConcat, mv)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||
import org.jetbrains.kotlin.load.java.sam.JavaSingleAbstractMethodUtils;
|
||||
import org.jetbrains.kotlin.name.ClassId;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.name.SpecialNames;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.stubs.KotlinFileStub;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
@@ -298,7 +299,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
|
||||
private String getName(ClassDescriptor classDescriptor) {
|
||||
String base = peekFromStack(nameStack);
|
||||
Name descriptorName = safeIdentifier(classDescriptor.getName());
|
||||
Name descriptorName = SpecialNames.safeIdentifier(classDescriptor.getName());
|
||||
if (DescriptorUtils.isTopLevelDeclaration(classDescriptor)) {
|
||||
return base.isEmpty() ? descriptorName.asString() : base + '/' + descriptorName;
|
||||
}
|
||||
@@ -476,7 +477,6 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
SimpleFunctionDescriptor jvmSuspendFunctionView =
|
||||
CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(
|
||||
functionDescriptor,
|
||||
languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines),
|
||||
this.bindingContext
|
||||
);
|
||||
|
||||
@@ -713,54 +713,6 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
super.visitCallExpression(expression);
|
||||
checkSamCall(expression);
|
||||
checkCrossinlineCall(expression);
|
||||
recordSuspendFunctionTypeWrapperForArguments(expression);
|
||||
}
|
||||
|
||||
private void recordSuspendFunctionTypeWrapperForArguments(@NotNull KtCallExpression expression) {
|
||||
ResolvedCall<?> call = CallUtilKt.getResolvedCall(expression, bindingContext);
|
||||
if (call == null) return;
|
||||
|
||||
CallableDescriptor descriptor = call.getResultingDescriptor();
|
||||
if (!CodegenUtilKt.needsExperimentalCoroutinesWrapper(descriptor)) return;
|
||||
|
||||
List<ResolvedValueArgument> argumentsByIndex = call.getValueArgumentsByIndex();
|
||||
if (argumentsByIndex == null) return;
|
||||
|
||||
for (ValueParameterDescriptor parameter : descriptor.getValueParameters()) {
|
||||
ResolvedValueArgument resolvedValueArgument = argumentsByIndex.get(parameter.getIndex());
|
||||
if (!(resolvedValueArgument instanceof ExpressionValueArgument)) continue;
|
||||
ValueArgument valueArgument = ((ExpressionValueArgument) resolvedValueArgument).getValueArgument();
|
||||
if (valueArgument == null) continue;
|
||||
KtExpression argumentExpression = valueArgument.getArgumentExpression();
|
||||
if (argumentExpression == null) continue;
|
||||
|
||||
recordSuspendFunctionTypeWrapperForArgument(parameter, argumentExpression);
|
||||
}
|
||||
|
||||
ReceiverValue receiver = call.getExtensionReceiver();
|
||||
if (descriptor.getExtensionReceiverParameter() != null && receiver instanceof ExpressionReceiver) {
|
||||
recordSuspendFunctionTypeWrapperForArgument(
|
||||
descriptor.getExtensionReceiverParameter(),
|
||||
((ExpressionReceiver) receiver).getExpression()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void recordSuspendFunctionTypeWrapperForArgument(ParameterDescriptor parameter, KtExpression argumentExpression) {
|
||||
if (FunctionTypesKt.isSuspendFunctionTypeOrSubtype(parameter.getType())) {
|
||||
|
||||
// SuspendFunctionN type is mapped to is mapped to FunctionTypeN+1, but we also need to remove an argument for return type
|
||||
// So, it could be parameter.getType().getArguments().size() + 1 - 1
|
||||
int functionTypeArity = parameter.getType().getArguments().size();
|
||||
|
||||
Type functionType = Type.getObjectType(NUMBERED_FUNCTION_PREFIX + functionTypeArity);
|
||||
|
||||
bindingTrace.record(
|
||||
FUNCTION_TYPE_FOR_SUSPEND_WRAPPER,
|
||||
argumentExpression,
|
||||
functionType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkCrossinlineCall(@NotNull KtCallExpression expression) {
|
||||
|
||||
@@ -13,15 +13,13 @@ import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
import org.jetbrains.kotlin.codegen.context.CodegenContext
|
||||
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
|
||||
import org.jetbrains.kotlin.codegen.context.MultifileClassFacadeContext
|
||||
import org.jetbrains.kotlin.codegen.coroutines.continuationAsmType
|
||||
import org.jetbrains.kotlin.codegen.coroutines.CONTINUATION_ASM_TYPE
|
||||
import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction
|
||||
import org.jetbrains.kotlin.codegen.inline.NUMBERED_FUNCTION_PREFIX
|
||||
import org.jetbrains.kotlin.codegen.inline.ReificationArgument
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
@@ -45,8 +43,6 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.Synthetic
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor.CoroutinesCompatibilityMode
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
|
||||
@@ -70,8 +66,7 @@ internal val JAVA_LANG_DEPRECATED = Type.getType(Deprecated::class.java).descrip
|
||||
fun generateIsCheck(
|
||||
v: InstructionAdapter,
|
||||
kotlinType: KotlinType,
|
||||
asmType: Type,
|
||||
isReleaseCoroutines: Boolean
|
||||
asmType: Type
|
||||
) {
|
||||
if (TypeUtils.isNullableType(kotlinType)) {
|
||||
val nope = Label()
|
||||
@@ -82,7 +77,7 @@ fun generateIsCheck(
|
||||
|
||||
ifnull(nope)
|
||||
|
||||
TypeIntrinsics.instanceOf(this, kotlinType, asmType, isReleaseCoroutines)
|
||||
TypeIntrinsics.instanceOf(this, kotlinType, asmType)
|
||||
|
||||
goTo(end)
|
||||
|
||||
@@ -93,7 +88,7 @@ fun generateIsCheck(
|
||||
mark(end)
|
||||
}
|
||||
} else {
|
||||
TypeIntrinsics.instanceOf(v, kotlinType, asmType, isReleaseCoroutines)
|
||||
TypeIntrinsics.instanceOf(v, kotlinType, asmType)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +97,6 @@ fun generateAsCast(
|
||||
kotlinType: KotlinType,
|
||||
asmType: Type,
|
||||
isSafe: Boolean,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
unifiedNullChecks: Boolean,
|
||||
) {
|
||||
if (!isSafe) {
|
||||
@@ -112,7 +106,7 @@ fun generateAsCast(
|
||||
} else {
|
||||
with(v) {
|
||||
dup()
|
||||
TypeIntrinsics.instanceOf(v, kotlinType, asmType, languageVersionSettings.isReleaseCoroutines())
|
||||
TypeIntrinsics.instanceOf(v, kotlinType, asmType)
|
||||
val ok = Label()
|
||||
ifne(ok)
|
||||
pop()
|
||||
@@ -444,14 +438,10 @@ fun KotlinType.isInlineClassTypeWithPrimitiveEquality(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
fun CallableDescriptor.needsExperimentalCoroutinesWrapper() =
|
||||
(this as? DeserializedMemberDescriptor)?.coroutinesExperimentalCompatibilityMode == CoroutinesCompatibilityMode.NEEDS_WRAPPER
|
||||
|
||||
fun recordCallLabelForLambdaArgument(declaration: KtFunctionLiteral, bindingTrace: BindingTrace) {
|
||||
val labelName = getCallLabelForLambdaArgument(declaration, bindingTrace.bindingContext) ?: return
|
||||
val functionDescriptor = bindingTrace[BindingContext.FUNCTION, declaration] ?: return
|
||||
bindingTrace.record(CodegenBinding.CALL_LABEL_FOR_LAMBDA_ARGUMENT, functionDescriptor, labelName)
|
||||
|
||||
}
|
||||
|
||||
fun getCallLabelForLambdaArgument(declaration: KtFunctionLiteral, bindingContext: BindingContext): String? {
|
||||
@@ -647,7 +637,7 @@ private fun generateLambdaForRunSuspend(
|
||||
}
|
||||
|
||||
visitVarInsn(ALOAD, 1)
|
||||
val continuationInternalName = state.languageVersionSettings.continuationAsmType().internalName
|
||||
val continuationInternalName = CONTINUATION_ASM_TYPE.internalName
|
||||
|
||||
visitTypeInsn(
|
||||
CHECKCAST,
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
package org.jetbrains.kotlin.codegen.context
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.isBuiltInSuspendCoroutineUninterceptedOrReturn
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.codegen.OwnerKind
|
||||
import org.jetbrains.kotlin.codegen.binding.MutableClosure
|
||||
import org.jetbrains.kotlin.config.coroutinesPackageFqName
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.isTopLevelInPackage
|
||||
@@ -17,25 +17,26 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getParentResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
|
||||
class InlineLambdaContext(
|
||||
functionDescriptor: FunctionDescriptor,
|
||||
contextKind: OwnerKind,
|
||||
parentContext: CodegenContext<*>,
|
||||
closure: MutableClosure?,
|
||||
val isCrossInline: Boolean,
|
||||
private val isPropertyReference: Boolean
|
||||
functionDescriptor: FunctionDescriptor,
|
||||
contextKind: OwnerKind,
|
||||
parentContext: CodegenContext<*>,
|
||||
closure: MutableClosure?,
|
||||
val isCrossInline: Boolean,
|
||||
private val isPropertyReference: Boolean
|
||||
) : MethodContext(functionDescriptor, contextKind, parentContext, closure, false) {
|
||||
|
||||
override fun getFirstCrossInlineOrNonInlineContext(): CodegenContext<*> {
|
||||
if (isCrossInline && !isSuspendIntrinsicParameter()) return this
|
||||
|
||||
val parent = if (isPropertyReference) parentContext as? AnonymousClassContext else { parentContext as? ClosureContext } ?:
|
||||
throw AssertionError(
|
||||
"Parent of inlining lambda body should be " +
|
||||
"${if (isPropertyReference) "ClosureContext" else "AnonymousClassContext"}, but: $parentContext"
|
||||
)
|
||||
val parent = if (isPropertyReference) parentContext as? AnonymousClassContext else {
|
||||
parentContext as? ClosureContext
|
||||
} ?: throw AssertionError(
|
||||
"Parent of inlining lambda body should be " +
|
||||
"${if (isPropertyReference) "ClosureContext" else "AnonymousClassContext"}, but: $parentContext"
|
||||
)
|
||||
|
||||
val grandParent = parent.parentContext ?:
|
||||
throw AssertionError("Parent context of lambda class context should exist: $contextDescriptor")
|
||||
val grandParent =
|
||||
parent.parentContext ?: throw AssertionError("Parent context of lambda class context should exist: $contextDescriptor")
|
||||
return grandParent.firstCrossInlineOrNonInlineContext
|
||||
}
|
||||
|
||||
@@ -44,7 +45,7 @@ class InlineLambdaContext(
|
||||
if (contextDescriptor !is AnonymousFunctionDescriptor) return false
|
||||
val resolvedCall = (contextDescriptor.source.getPsi() as? KtElement).getParentResolvedCall(state.bindingContext) ?: return false
|
||||
val descriptor = resolvedCall.resultingDescriptor as? FunctionDescriptor ?: return false
|
||||
return descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(state.languageVersionSettings)
|
||||
|| descriptor.isTopLevelInPackage("suspendCoroutine", state.languageVersionSettings.coroutinesPackageFqName().asString())
|
||||
return descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn()
|
||||
|| descriptor.isTopLevelInPackage("suspendCoroutine", StandardNames.COROUTINES_PACKAGE_FQ_NAME.asString())
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.coroutines
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.isPrimitiveBoxing
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
@@ -15,10 +16,9 @@ import org.jetbrains.kotlin.utils.sure
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import java.util.*
|
||||
|
||||
private val BOXING_CLASS_INTERNAL_NAME =
|
||||
RELEASE_COROUTINES_VERSION_SETTINGS.coroutinesJvmInternalPackageFqName().child(Name.identifier("Boxing")).topLevelClassInternalName()
|
||||
StandardNames.COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.child(Name.identifier("Boxing")).topLevelClassInternalName()
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
object ChangeBoxingMethodTransformer : MethodTransformer() {
|
||||
|
||||
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.codegen.coroutines
|
||||
|
||||
import com.intellij.util.ArrayUtil
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding.CAPTURES_CROSSINLINE_LAMBDA
|
||||
@@ -17,8 +18,6 @@ import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHO
|
||||
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
||||
@@ -41,7 +40,6 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
@@ -56,7 +54,7 @@ abstract class AbstractCoroutineCodegen(
|
||||
element: KtElement,
|
||||
closureContext: ClosureContext,
|
||||
classBuilder: ClassBuilder,
|
||||
private val userDataForDoResume: Map<out CallableDescriptor.UserDataKey<*>, *>? = null
|
||||
private val userDataForInvokeSuspend: Map<out CallableDescriptor.UserDataKey<*>, *>? = null
|
||||
) : ClosureCodegen(
|
||||
outerExpressionCodegen.state,
|
||||
element, null, closureContext, null,
|
||||
@@ -67,17 +65,10 @@ abstract class AbstractCoroutineCodegen(
|
||||
protected val languageVersionSettings = outerExpressionCodegen.state.languageVersionSettings
|
||||
|
||||
protected val methodToImplement =
|
||||
if (languageVersionSettings.isReleaseCoroutines())
|
||||
createImplMethod(
|
||||
INVOKE_SUSPEND_METHOD_NAME,
|
||||
SUSPEND_CALL_RESULT_NAME to classDescriptor.module.getResult(classDescriptor.builtIns.anyType)
|
||||
)
|
||||
else
|
||||
createImplMethod(
|
||||
DO_RESUME_METHOD_NAME,
|
||||
"data" to classDescriptor.builtIns.nullableAnyType,
|
||||
"throwable" to classDescriptor.builtIns.throwable.defaultType.makeNullable()
|
||||
)
|
||||
createImplMethod(
|
||||
INVOKE_SUSPEND_METHOD_NAME,
|
||||
SUSPEND_CALL_RESULT_NAME to classDescriptor.module.getResult(classDescriptor.builtIns.anyType)
|
||||
)
|
||||
|
||||
private fun createImplMethod(name: String, vararg parameters: Pair<String, KotlinType>) =
|
||||
SimpleFunctionDescriptorImpl.create(
|
||||
@@ -94,7 +85,7 @@ abstract class AbstractCoroutineCodegen(
|
||||
builtIns.nullableAnyType,
|
||||
Modality.FINAL,
|
||||
DescriptorVisibilities.PUBLIC,
|
||||
userDataForDoResume
|
||||
userDataForInvokeSuspend
|
||||
)
|
||||
}
|
||||
|
||||
@@ -109,7 +100,7 @@ abstract class AbstractCoroutineCodegen(
|
||||
|
||||
override fun generateConstructor(): Method {
|
||||
val args = calculateConstructorParameters(typeMapper, languageVersionSettings, closure, asmType)
|
||||
val argTypes = args.map { it.fieldType }.plus(languageVersionSettings.continuationAsmType()).toTypedArray()
|
||||
val argTypes = args.map { it.fieldType }.plus(CONTINUATION_ASM_TYPE).toTypedArray()
|
||||
|
||||
val constructor = Method("<init>", Type.VOID_TYPE, argTypes)
|
||||
val mv = v.newMethod(
|
||||
@@ -124,18 +115,17 @@ abstract class AbstractCoroutineCodegen(
|
||||
iv.generateClosureFieldsInitializationFromParameters(closure, args)
|
||||
|
||||
iv.load(0, AsmTypes.OBJECT_TYPE)
|
||||
val hasArityParameter = !languageVersionSettings.isReleaseCoroutines() || passArityToSuperClass
|
||||
if (hasArityParameter) {
|
||||
iv.iconst(if (passArityToSuperClass) funDescriptor.arity else 0)
|
||||
if (passArityToSuperClass) {
|
||||
iv.iconst(funDescriptor.arity)
|
||||
}
|
||||
|
||||
iv.load(argTypes.map { it.size }.sum(), AsmTypes.OBJECT_TYPE)
|
||||
|
||||
val parameters =
|
||||
if (hasArityParameter)
|
||||
listOf(Type.INT_TYPE, languageVersionSettings.continuationAsmType())
|
||||
if (passArityToSuperClass)
|
||||
listOf(Type.INT_TYPE, CONTINUATION_ASM_TYPE)
|
||||
else
|
||||
listOf(languageVersionSettings.continuationAsmType())
|
||||
listOf(CONTINUATION_ASM_TYPE)
|
||||
|
||||
val superClassConstructorDescriptor = Type.getMethodDescriptor(
|
||||
Type.VOID_TYPE,
|
||||
@@ -148,9 +138,7 @@ abstract class AbstractCoroutineCodegen(
|
||||
FunctionCodegen.endVisit(iv, "constructor", element)
|
||||
}
|
||||
|
||||
if (languageVersionSettings.isReleaseCoroutines()) {
|
||||
v.newField(JvmDeclarationOrigin.NO_ORIGIN, AsmUtil.NO_FLAG_PACKAGE_PRIVATE, "label", "I", null, null)
|
||||
}
|
||||
v.newField(JvmDeclarationOrigin.NO_ORIGIN, AsmUtil.NO_FLAG_PACKAGE_PRIVATE, "label", "I", null, null)
|
||||
|
||||
return constructor
|
||||
}
|
||||
@@ -167,7 +155,7 @@ class CoroutineCodegenForLambda private constructor(
|
||||
private val forInline: Boolean
|
||||
) : AbstractCoroutineCodegen(
|
||||
outerExpressionCodegen, element, closureContext, classBuilder,
|
||||
userDataForDoResume = mapOf(INITIAL_SUSPEND_DESCRIPTOR_FOR_DO_RESUME to originalSuspendFunctionDescriptor)
|
||||
userDataForInvokeSuspend = mapOf(INITIAL_SUSPEND_DESCRIPTOR_FOR_INVOKE_SUSPEND to originalSuspendFunctionDescriptor)
|
||||
) {
|
||||
private val builtIns = funDescriptor.builtIns
|
||||
|
||||
@@ -215,8 +203,7 @@ class CoroutineCodegenForLambda private constructor(
|
||||
funDescriptor.typeParameters,
|
||||
funDescriptor.valueParameters,
|
||||
funDescriptor.module.getContinuationOfTypeOrAny(
|
||||
builtIns.unitType,
|
||||
state.languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)
|
||||
builtIns.unitType
|
||||
),
|
||||
funDescriptor.modality,
|
||||
DescriptorVisibilities.PUBLIC
|
||||
@@ -231,14 +218,11 @@ class CoroutineCodegenForLambda private constructor(
|
||||
"too many arguments of create to have an erased signature: $argumentsNum: $typedCreate"
|
||||
}
|
||||
return typedCreate.module.resolveClassByFqName(
|
||||
languageVersionSettings.coroutinesJvmInternalPackageFqName().child(
|
||||
if (languageVersionSettings.isReleaseCoroutines())
|
||||
Name.identifier("BaseContinuationImpl")
|
||||
else
|
||||
Name.identifier("CoroutineImpl")
|
||||
StandardNames.COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.child(
|
||||
Name.identifier("BaseContinuationImpl")
|
||||
),
|
||||
NoLookupLocation.FROM_BACKEND
|
||||
).sure { "BaseContinuationImpl or CoroutineImpl is not found" }.defaultType.memberScope
|
||||
).sure { "BaseContinuationImpl is not found" }.defaultType.memberScope
|
||||
.getContributedFunctions(typedCreate.name, NoLookupLocation.FROM_BACKEND)
|
||||
.find { it.valueParameters.size == argumentsNum }
|
||||
.sure { "erased parent of $typedCreate is not found" }
|
||||
@@ -365,7 +349,7 @@ class CoroutineCodegenForLambda private constructor(
|
||||
v.thisName,
|
||||
typeMapper.mapFunctionName(createCoroutineDescriptor, null),
|
||||
Type.getMethodDescriptor(
|
||||
languageVersionSettings.continuationAsmType(),
|
||||
CONTINUATION_ASM_TYPE,
|
||||
*createArgumentTypes.toTypedArray()
|
||||
),
|
||||
false
|
||||
@@ -373,11 +357,7 @@ class CoroutineCodegenForLambda private constructor(
|
||||
checkcast(Type.getObjectType(v.thisName))
|
||||
|
||||
// .doResume(Unit)
|
||||
if (languageVersionSettings.isReleaseCoroutines()) {
|
||||
invokeInvokeSuspendWithUnit(v.thisName)
|
||||
} else {
|
||||
invokeDoResumeWithUnit(v.thisName)
|
||||
}
|
||||
invokeInvokeSuspendWithUnit(v.thisName)
|
||||
areturn(AsmTypes.OBJECT_TYPE)
|
||||
}
|
||||
|
||||
@@ -540,15 +520,14 @@ class CoroutineCodegenForLambda private constructor(
|
||||
override fun wrapMethodVisitor(mv: MethodVisitor, access: Int, name: String, desc: String): MethodVisitor {
|
||||
val stateMachineBuilder = CoroutineTransformerMethodVisitor(
|
||||
mv, access, name, desc, null, null,
|
||||
containingClassInternalName = v.thisName,
|
||||
obtainClassBuilderForCoroutineState = { v },
|
||||
isForNamedFunction = false,
|
||||
shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = false,
|
||||
reportSuspensionPointInsideMonitor = { reportSuspensionPointInsideMonitor(element, state, it) },
|
||||
lineNumber = CodegenUtil.getLineNumberForElement(element, false) ?: 0,
|
||||
sourceFile = element.containingKtFile.name,
|
||||
shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
containingClassInternalName = v.thisName,
|
||||
isForNamedFunction = false,
|
||||
languageVersionSettings = languageVersionSettings,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = false,
|
||||
useOldSpilledVarTypeAnalysis = state.configuration.getBoolean(JVMConfigurationKeys.USE_OLD_SPILLED_VAR_TYPE_ANALYSIS),
|
||||
initialVarsCountByType = varsCountByType
|
||||
)
|
||||
@@ -625,7 +604,7 @@ class CoroutineCodegenForNamedFunction private constructor(
|
||||
private val labelFieldStackValue by lazy {
|
||||
StackValue.field(
|
||||
FieldInfo.createForHiddenField(
|
||||
computeLabelOwner(languageVersionSettings, v.thisName),
|
||||
Type.getObjectType(v.thisName),
|
||||
Type.INT_TYPE,
|
||||
COROUTINE_LABEL_FIELD_NAME
|
||||
),
|
||||
@@ -647,44 +626,25 @@ class CoroutineCodegenForNamedFunction private constructor(
|
||||
}
|
||||
|
||||
override fun generateClosureBody() {
|
||||
generateResumeImpl()
|
||||
|
||||
if (!languageVersionSettings.isReleaseCoroutines()) {
|
||||
generateGetLabelMethod()
|
||||
generateSetLabelMethod()
|
||||
}
|
||||
generateInvokeSuspend()
|
||||
|
||||
v.newField(
|
||||
JvmDeclarationOrigin.NO_ORIGIN, Opcodes.ACC_SYNTHETIC or AsmUtil.NO_FLAG_PACKAGE_PRIVATE,
|
||||
languageVersionSettings.dataFieldName(), AsmTypes.OBJECT_TYPE.descriptor, null, null
|
||||
CONTINUATION_RESULT_FIELD_NAME, AsmTypes.OBJECT_TYPE.descriptor, null, null
|
||||
)
|
||||
|
||||
if (!languageVersionSettings.isReleaseCoroutines()) {
|
||||
v.newField(
|
||||
JvmDeclarationOrigin.NO_ORIGIN, Opcodes.ACC_SYNTHETIC or AsmUtil.NO_FLAG_PACKAGE_PRIVATE,
|
||||
EXCEPTION_FIELD_NAME, AsmTypes.JAVA_THROWABLE_TYPE.descriptor, null, null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateResumeImpl() {
|
||||
private fun generateInvokeSuspend() {
|
||||
functionCodegen.generateMethod(
|
||||
OtherOrigin(element),
|
||||
methodToImplement,
|
||||
object : FunctionGenerationStrategy.CodegenBased(state) {
|
||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||
StackValue.field(
|
||||
AsmTypes.OBJECT_TYPE, Type.getObjectType(v.thisName), languageVersionSettings.dataFieldName(), false,
|
||||
AsmTypes.OBJECT_TYPE, Type.getObjectType(v.thisName), CONTINUATION_RESULT_FIELD_NAME, false,
|
||||
StackValue.LOCAL_0
|
||||
).store(StackValue.local(1, AsmTypes.OBJECT_TYPE), codegen.v)
|
||||
|
||||
if (!languageVersionSettings.isReleaseCoroutines()) {
|
||||
StackValue.field(
|
||||
AsmTypes.JAVA_THROWABLE_TYPE, Type.getObjectType(v.thisName), EXCEPTION_FIELD_NAME, false,
|
||||
StackValue.LOCAL_0
|
||||
).store(StackValue.local(2, AsmTypes.JAVA_THROWABLE_TYPE), codegen.v)
|
||||
}
|
||||
|
||||
labelFieldStackValue.store(
|
||||
StackValue.operation(Type.INT_TYPE) {
|
||||
labelFieldStackValue.put(Type.INT_TYPE, it)
|
||||
@@ -736,7 +696,7 @@ class CoroutineCodegenForNamedFunction private constructor(
|
||||
with(codegen.v) {
|
||||
// We need to box the returned inline class in resume path.
|
||||
// But first, check for COROUTINE_SUSPENDED, since the function can return it
|
||||
generateCoroutineSuspendedCheck(languageVersionSettings)
|
||||
generateCoroutineSuspendedCheck()
|
||||
// Now we box the inline class
|
||||
StackValue.coerce(AsmTypes.OBJECT_TYPE, typeMapper.mapType(inlineClassToBoxInInvokeSuspend), this)
|
||||
StackValue.boxInlineClass(inlineClassToBoxInInvokeSuspend, this, typeMapper)
|
||||
|
||||
@@ -9,15 +9,10 @@ import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
@@ -49,7 +44,6 @@ class CoroutineTransformerMethodVisitor(
|
||||
obtainClassBuilderForCoroutineState: () -> ClassBuilder,
|
||||
private val isForNamedFunction: Boolean,
|
||||
private val shouldPreserveClassInitialization: Boolean,
|
||||
private val languageVersionSettings: LanguageVersionSettings,
|
||||
// Since tail-call optimization of functions with Unit return type relies on ability of call-site to recognize them,
|
||||
// in order to ignore return value and push Unit, when we cannot ensure this ability, for example, when the function overrides function,
|
||||
// returning Any, we need to disable tail-call optimization for these functions.
|
||||
@@ -73,7 +67,6 @@ class CoroutineTransformerMethodVisitor(
|
||||
|
||||
private var continuationIndex = if (isForNamedFunction) -1 else 0
|
||||
private var dataIndex = if (isForNamedFunction) -1 else 1
|
||||
private var exceptionIndex = if (isForNamedFunction || languageVersionSettings.isReleaseCoroutines()) -1 else 2
|
||||
|
||||
override fun performTransformations(methodNode: MethodNode) {
|
||||
removeFakeContinuationConstructorCall(methodNode)
|
||||
@@ -89,9 +82,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
val suspensionPoints = collectSuspensionPoints(methodNode)
|
||||
RedundantLocalsEliminationMethodTransformer(suspensionPoints)
|
||||
.transform(containingClassInternalName, methodNode)
|
||||
if (languageVersionSettings.isReleaseCoroutines()) {
|
||||
ChangeBoxingMethodTransformer.transform(containingClassInternalName, methodNode)
|
||||
}
|
||||
ChangeBoxingMethodTransformer.transform(containingClassInternalName, methodNode)
|
||||
updateMaxStack(methodNode)
|
||||
|
||||
checkForSuspensionPointInsideMonitor(methodNode, suspensionPoints)
|
||||
@@ -105,7 +96,6 @@ class CoroutineTransformerMethodVisitor(
|
||||
}
|
||||
|
||||
val examiner = MethodNodeExaminer(
|
||||
languageVersionSettings,
|
||||
containingClassInternalName,
|
||||
methodNode,
|
||||
suspensionPoints,
|
||||
@@ -119,9 +109,6 @@ class CoroutineTransformerMethodVisitor(
|
||||
}
|
||||
|
||||
dataIndex = methodNode.maxLocals++
|
||||
if (!languageVersionSettings.isReleaseCoroutines()) {
|
||||
exceptionIndex = methodNode.maxLocals++
|
||||
}
|
||||
continuationIndex = methodNode.maxLocals++
|
||||
|
||||
prepareMethodNodePreludeForNamedFunction(methodNode)
|
||||
@@ -158,7 +145,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
insertBefore(
|
||||
actualCoroutineStart,
|
||||
insnListOf(
|
||||
*withInstructionAdapter { loadCoroutineSuspendedMarker(languageVersionSettings) }.toArray(),
|
||||
*withInstructionAdapter { loadCoroutineSuspendedMarker() }.toArray(),
|
||||
tableSwitchLabel,
|
||||
// Allow debugger to stop on enter into suspend function
|
||||
LineNumberNode(lineNumber, tableSwitchLabel),
|
||||
@@ -176,7 +163,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
)
|
||||
|
||||
insert(firstStateLabel, withInstructionAdapter {
|
||||
generateResumeWithExceptionCheck(languageVersionSettings.isReleaseCoroutines(), dataIndex, exceptionIndex)
|
||||
generateResumeWithExceptionCheck(dataIndex)
|
||||
})
|
||||
insert(last, defaultLabel)
|
||||
|
||||
@@ -192,11 +179,9 @@ class CoroutineTransformerMethodVisitor(
|
||||
dropUnboxInlineClassMarkers(methodNode, suspensionPoints)
|
||||
methodNode.removeEmptyCatchBlocks()
|
||||
|
||||
updateLvtAccordingToLiveness(methodNode, isForNamedFunction)
|
||||
updateLvtAccordingToLiveness(methodNode, isForNamedFunction, stateLabels)
|
||||
|
||||
if (languageVersionSettings.isReleaseCoroutines()) {
|
||||
writeDebugMetadata(methodNode, suspensionPointLineNumbers, spilledToVariableMapping)
|
||||
}
|
||||
writeDebugMetadata(methodNode, suspensionPointLineNumbers, spilledToVariableMapping)
|
||||
}
|
||||
|
||||
// When suspension point is inlined, it is in range of fake inliner variables.
|
||||
@@ -263,7 +248,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
methodNode.localVariables.add(
|
||||
LocalVariableNode(
|
||||
SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME,
|
||||
languageVersionSettings.continuationAsmType().descriptor,
|
||||
CONTINUATION_ASM_TYPE.descriptor,
|
||||
null,
|
||||
startLabel,
|
||||
endLabel,
|
||||
@@ -402,7 +387,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
methodNode.instructions.add(withInstructionAdapter { mark(endLabel) })
|
||||
methodNode.visitLocalVariable(
|
||||
CONTINUATION_VARIABLE_NAME,
|
||||
languageVersionSettings.continuationAsmType().descriptor,
|
||||
CONTINUATION_ASM_TYPE.descriptor,
|
||||
null,
|
||||
startLabel,
|
||||
endLabel,
|
||||
@@ -430,33 +415,17 @@ class CoroutineTransformerMethodVisitor(
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.getLabel() {
|
||||
if (isForNamedFunction && !languageVersionSettings.isReleaseCoroutines())
|
||||
invokevirtual(
|
||||
classBuilderForCoroutineState.thisName,
|
||||
"getLabel",
|
||||
Type.getMethodDescriptor(Type.INT_TYPE),
|
||||
false
|
||||
)
|
||||
else
|
||||
getfield(
|
||||
computeLabelOwner(languageVersionSettings, classBuilderForCoroutineState.thisName).internalName,
|
||||
COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor
|
||||
)
|
||||
getfield(
|
||||
Type.getObjectType(classBuilderForCoroutineState.thisName).internalName,
|
||||
COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor
|
||||
)
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.setLabel() {
|
||||
if (isForNamedFunction && !languageVersionSettings.isReleaseCoroutines())
|
||||
invokevirtual(
|
||||
classBuilderForCoroutineState.thisName,
|
||||
"setLabel",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE),
|
||||
false
|
||||
)
|
||||
else
|
||||
putfield(
|
||||
computeLabelOwner(languageVersionSettings, classBuilderForCoroutineState.thisName).internalName,
|
||||
COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor
|
||||
)
|
||||
putfield(
|
||||
Type.getObjectType(classBuilderForCoroutineState.thisName).internalName,
|
||||
COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateMaxStack(methodNode: MethodNode) {
|
||||
@@ -534,8 +503,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
needDispatchReceiver,
|
||||
internalNameForDispatchReceiver,
|
||||
containingClassInternalName,
|
||||
classBuilderForCoroutineState,
|
||||
languageVersionSettings
|
||||
classBuilderForCoroutineState
|
||||
)
|
||||
|
||||
visitVarInsn(Opcodes.ASTORE, continuationIndex)
|
||||
@@ -543,19 +511,13 @@ class CoroutineTransformerMethodVisitor(
|
||||
visitLabel(afterCoroutineStateCreated)
|
||||
|
||||
visitVarInsn(Opcodes.ALOAD, continuationIndex)
|
||||
getfield(classBuilderForCoroutineState.thisName, languageVersionSettings.dataFieldName(), AsmTypes.OBJECT_TYPE.descriptor)
|
||||
getfield(classBuilderForCoroutineState.thisName, CONTINUATION_RESULT_FIELD_NAME, AsmTypes.OBJECT_TYPE.descriptor)
|
||||
visitVarInsn(Opcodes.ASTORE, dataIndex)
|
||||
|
||||
val resultStartLabel = Label()
|
||||
visitLabel(resultStartLabel)
|
||||
|
||||
addContinuationAndResultToLvt(methodNode, afterCoroutineStateCreated, resultStartLabel)
|
||||
|
||||
if (!languageVersionSettings.isReleaseCoroutines()) {
|
||||
visitVarInsn(Opcodes.ALOAD, continuationIndex)
|
||||
getfield(classBuilderForCoroutineState.thisName, EXCEPTION_FIELD_NAME, AsmTypes.JAVA_THROWABLE_TYPE.descriptor)
|
||||
visitVarInsn(Opcodes.ASTORE, exceptionIndex)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -692,7 +654,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
// k + 1 - data
|
||||
// k + 2 - exception
|
||||
for (slot in 0 until localsCount) {
|
||||
if (slot == continuationIndex || slot == dataIndex || slot == exceptionIndex) continue
|
||||
if (slot == continuationIndex || slot == dataIndex) continue
|
||||
val value = frame.getLocal(slot)
|
||||
if (value.type == null || !livenessFrame.isAlive(slot)) continue
|
||||
|
||||
@@ -992,7 +954,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
|
||||
insert(possibleTryCatchBlockStart, withInstructionAdapter {
|
||||
nop()
|
||||
generateResumeWithExceptionCheck(languageVersionSettings.isReleaseCoroutines(), dataIndex, exceptionIndex)
|
||||
generateResumeWithExceptionCheck(dataIndex)
|
||||
|
||||
// Load continuation argument just like suspending function returns it
|
||||
load(dataIndex, AsmTypes.OBJECT_TYPE)
|
||||
@@ -1118,8 +1080,7 @@ internal fun InstructionAdapter.generateContinuationConstructorCall(
|
||||
needDispatchReceiver: Boolean,
|
||||
internalNameForDispatchReceiver: String?,
|
||||
containingClassInternalName: String,
|
||||
classBuilderForCoroutineState: ClassBuilder,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
classBuilderForCoroutineState: ClassBuilder
|
||||
) {
|
||||
anew(objectTypeForState)
|
||||
dup()
|
||||
@@ -1128,8 +1089,7 @@ internal fun InstructionAdapter.generateContinuationConstructorCall(
|
||||
getParameterTypesIndicesForCoroutineConstructor(
|
||||
methodNode.desc,
|
||||
methodNode.access,
|
||||
needDispatchReceiver, internalNameForDispatchReceiver ?: containingClassInternalName,
|
||||
languageVersionSettings
|
||||
needDispatchReceiver, internalNameForDispatchReceiver ?: containingClassInternalName
|
||||
)
|
||||
for ((type, index) in parameterTypesAndIndices) {
|
||||
load(index, type)
|
||||
@@ -1149,22 +1109,11 @@ internal fun InstructionAdapter.generateContinuationConstructorCall(
|
||||
)
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.generateResumeWithExceptionCheck(isReleaseCoroutines: Boolean, dataIndex: Int, exceptionIndex: Int) {
|
||||
private fun InstructionAdapter.generateResumeWithExceptionCheck(dataIndex: Int) {
|
||||
// Check if resumeWithException has been called
|
||||
|
||||
if (isReleaseCoroutines) {
|
||||
load(dataIndex, AsmTypes.OBJECT_TYPE)
|
||||
invokestatic("kotlin/ResultKt", "throwOnFailure", "(Ljava/lang/Object;)V", false)
|
||||
} else {
|
||||
load(exceptionIndex, AsmTypes.OBJECT_TYPE)
|
||||
dup()
|
||||
val noExceptionLabel = Label()
|
||||
ifnull(noExceptionLabel)
|
||||
athrow()
|
||||
|
||||
mark(noExceptionLabel)
|
||||
pop()
|
||||
}
|
||||
load(dataIndex, AsmTypes.OBJECT_TYPE)
|
||||
invokestatic("kotlin/ResultKt", "throwOnFailure", "(Ljava/lang/Object;)V", false)
|
||||
}
|
||||
|
||||
private fun Type.fieldNameForVar(index: Int) = descriptor.first() + "$" + index
|
||||
@@ -1235,16 +1184,15 @@ private fun getParameterTypesIndicesForCoroutineConstructor(
|
||||
desc: String,
|
||||
containingFunctionAccess: Int,
|
||||
needDispatchReceiver: Boolean,
|
||||
thisName: String,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
thisName: String
|
||||
): Collection<Pair<Type, Int>> {
|
||||
return mutableListOf<Pair<Type, Int>>().apply {
|
||||
if (needDispatchReceiver) {
|
||||
add(Type.getObjectType(thisName) to 0)
|
||||
}
|
||||
val continuationIndex =
|
||||
getAllParameterTypes(desc, !isStatic(containingFunctionAccess), thisName).dropLast(1).map(Type::getSize).sum()
|
||||
add(languageVersionSettings.continuationAsmType() to continuationIndex)
|
||||
getAllParameterTypes(desc, !isStatic(containingFunctionAccess), thisName).dropLast(1).sumOf(Type::getSize)
|
||||
add(CONTINUATION_ASM_TYPE to continuationIndex)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1269,7 +1217,7 @@ private fun MethodNode.nodeTextWithLiveness(liveness: List<VariableLivenessFrame
|
||||
* This means, that function parameters do not longer span the whole function, including `this`.
|
||||
* This might and will break some bytecode processors, including old versions of R8. See KT-24510.
|
||||
*/
|
||||
private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction: Boolean) {
|
||||
private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction: Boolean, suspensionPoints: List<LabelNode>) {
|
||||
val liveness = analyzeLiveness(method)
|
||||
|
||||
fun List<LocalVariableNode>.findRecord(insnIndex: Int, variableIndex: Int): LocalVariableNode? {
|
||||
@@ -1302,6 +1250,8 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
||||
oldLvt += record
|
||||
}
|
||||
method.localVariables.clear()
|
||||
|
||||
val oldLvtNodeToLatestNewLvtNode = mutableMapOf<LocalVariableNode, LocalVariableNode>()
|
||||
// Skip `this` for suspend lambda
|
||||
val start = if (isForNamedFunction) 0 else 1
|
||||
for (variableIndex in start until method.maxLocals) {
|
||||
@@ -1341,33 +1291,25 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
||||
val endLabel = nextLabel(insn.next)?.let { min(lvtRecord.end, it) } ?: lvtRecord.end
|
||||
// startLabel can be null in case of parameters
|
||||
@Suppress("NAME_SHADOWING") val startLabel = startLabel ?: lvtRecord.start
|
||||
val node = LocalVariableNode(lvtRecord.name, lvtRecord.desc, lvtRecord.signature, startLabel, endLabel, lvtRecord.index)
|
||||
method.localVariables.add(node)
|
||||
|
||||
// Attempt to extend existing local variable node corresponding to the record in
|
||||
// the original local variable table, if there is no back-edge
|
||||
val latest = oldLvtNodeToLatestNewLvtNode[lvtRecord]
|
||||
// if we can extend the previous range to where the local variable dies, we do not need a
|
||||
// new entry, we know we cannot extend it to the lvt.endOffset, if we could we would have
|
||||
// done so when we added it below.
|
||||
val extended = latest?.extendRecordIfPossible(method, suspensionPoints, lvtRecord.end) ?: false
|
||||
if (!extended) {
|
||||
val new = LocalVariableNode(lvtRecord.name, lvtRecord.desc, lvtRecord.signature, startLabel, endLabel, lvtRecord.index)
|
||||
oldLvtNodeToLatestNewLvtNode[lvtRecord] = new
|
||||
method.localVariables.add(new)
|
||||
// see if we can extend it all the way to the old end
|
||||
new.extendRecordIfPossible(method, suspensionPoints, lvtRecord.end)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge consequent LVT records, otherwise, atomicfu goes crazy (KT-47749)
|
||||
val toRemove = arrayListOf<LocalVariableNode>()
|
||||
val sortedLVT = method.localVariables.sortedBy { method.instructions.indexOf(it.start) }
|
||||
for (i in sortedLVT.indices) {
|
||||
var endIndex = method.instructions.indexOf(sortedLVT[i].end)
|
||||
for (j in (i + 1) until sortedLVT.size) {
|
||||
val startIndex = method.instructions.indexOf(sortedLVT[j].start)
|
||||
if (endIndex < startIndex) break
|
||||
if (endIndex != startIndex ||
|
||||
sortedLVT[i].index != sortedLVT[j].index ||
|
||||
sortedLVT[i].name != sortedLVT[j].name ||
|
||||
sortedLVT[i].desc != sortedLVT[j].desc
|
||||
) continue
|
||||
sortedLVT[i].end = sortedLVT[j].end
|
||||
endIndex = method.instructions.indexOf(sortedLVT[j].end)
|
||||
toRemove += sortedLVT[j]
|
||||
}
|
||||
}
|
||||
|
||||
method.localVariables.removeAll(toRemove)
|
||||
|
||||
for (variable in oldLvt) {
|
||||
// $continuation and $result are dead, but they are used by debugger, as well as fake inliner variables
|
||||
// For example, $continuation is used to create async stack trace
|
||||
@@ -1385,3 +1327,35 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We cannot extend a record if there is STORE instruction or a back-edge.
|
||||
* STORE instructions can signify a unspilling operation, in which case, the variable will become visible before it unspilled,
|
||||
* back-edges occur in loops.
|
||||
*
|
||||
* @return true if the range has been extended
|
||||
*/
|
||||
private fun LocalVariableNode.extendRecordIfPossible(
|
||||
method: MethodNode,
|
||||
suspensionPoints: List<LabelNode>,
|
||||
endLabel: LabelNode
|
||||
): Boolean {
|
||||
val nextSuspensionPointLabel = suspensionPoints.find { it in InsnSequence(end, endLabel) } ?: endLabel
|
||||
|
||||
var current: AbstractInsnNode? = end
|
||||
while (current != null && current != nextSuspensionPointLabel) {
|
||||
if (current is JumpInsnNode) {
|
||||
if (method.instructions.indexOf(current.label) < method.instructions.indexOf(current)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// TODO: HACK
|
||||
// TODO: Find correct label, which is OK to be used as end label.
|
||||
if (current.opcode == Opcodes.ARETURN && nextSuspensionPointLabel != endLabel) return false
|
||||
if (current.isStoreOperation() && (current as VarInsnNode).`var` == index) {
|
||||
return false
|
||||
}
|
||||
current = current.next
|
||||
}
|
||||
end = nextSuspensionPointLabel
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -88,18 +88,17 @@ class SuspendFunctionGenerationStrategy(
|
||||
return CoroutineTransformerMethodVisitor(
|
||||
mv, access, name, desc, null, null, containingClassInternalName, this::classBuilderForCoroutineState,
|
||||
isForNamedFunction = true,
|
||||
shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = originalSuspendDescriptor.returnType?.isUnit() == true &&
|
||||
originalSuspendDescriptor.overriddenDescriptors.isNotEmpty() &&
|
||||
!originalSuspendDescriptor.allOverriddenFunctionsReturnUnit(),
|
||||
reportSuspensionPointInsideMonitor = { reportSuspensionPointInsideMonitor(declaration, state, it) },
|
||||
lineNumber = CodegenUtil.getLineNumberForElement(declaration, false) ?: 0,
|
||||
sourceFile = declaration.containingKtFile.name,
|
||||
shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
needDispatchReceiver = originalSuspendDescriptor.dispatchReceiverParameter != null,
|
||||
internalNameForDispatchReceiver = (originalSuspendDescriptor.containingDeclaration as? ClassDescriptor)?.let {
|
||||
if (it.isInlineClass()) state.typeMapper.mapType(it).internalName else null
|
||||
} ?: containingClassInternalNameOrNull(),
|
||||
languageVersionSettings = languageVersionSettings,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = originalSuspendDescriptor.returnType?.isUnit() == true &&
|
||||
originalSuspendDescriptor.overriddenDescriptors.isNotEmpty() &&
|
||||
!originalSuspendDescriptor.allOverriddenFunctionsReturnUnit(),
|
||||
useOldSpilledVarTypeAnalysis = state.configuration.getBoolean(JVMConfigurationKeys.USE_OLD_SPILLED_VAR_TYPE_ANALYSIS)
|
||||
)
|
||||
}
|
||||
@@ -155,8 +154,7 @@ class SuspendFunctionGenerationStrategy(
|
||||
needDispatchReceiver,
|
||||
internalNameForDispatchReceiver,
|
||||
containingClassInternalName,
|
||||
classBuilderForCoroutineState,
|
||||
languageVersionSettings
|
||||
classBuilderForCoroutineState
|
||||
)
|
||||
addFakeContinuationConstructorCallMarker(this, false)
|
||||
pop() // Otherwise stack-transformation breaks
|
||||
|
||||
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
@@ -27,7 +26,6 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||
|
||||
internal class MethodNodeExaminer(
|
||||
val languageVersionSettings: LanguageVersionSettings,
|
||||
containingClassInternalName: String,
|
||||
val methodNode: MethodNode,
|
||||
suspensionPoints: List<SuspensionPoint>,
|
||||
@@ -116,7 +114,7 @@ internal class MethodNodeExaminer(
|
||||
val label = Label()
|
||||
methodNode.instructions.insertBefore(pop, withInstructionAdapter {
|
||||
dup()
|
||||
loadCoroutineSuspendedMarker(languageVersionSettings)
|
||||
loadCoroutineSuspendedMarker()
|
||||
ifacmpne(label)
|
||||
areturn(AsmTypes.OBJECT_TYPE)
|
||||
mark(label)
|
||||
|
||||
@@ -9,6 +9,8 @@ import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.backend.common.COROUTINE_SUSPENDED_NAME
|
||||
import org.jetbrains.kotlin.backend.common.isBuiltInSuspendCoroutineUninterceptedOrReturn
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.builtins.StandardNames.COROUTINES_INTRINSICS_PACKAGE_FQ_NAME
|
||||
import org.jetbrains.kotlin.builtins.StandardNames.COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME
|
||||
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalClassDescriptor
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
@@ -54,67 +56,27 @@ import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
const val COROUTINE_LABEL_FIELD_NAME = "label"
|
||||
const val SUSPEND_FUNCTION_CREATE_METHOD_NAME = "create"
|
||||
const val DO_RESUME_METHOD_NAME = "doResume"
|
||||
const val INVOKE_SUSPEND_METHOD_NAME = "invokeSuspend"
|
||||
const val EXCEPTION_FIELD_NAME = "exception"
|
||||
const val CONTINUATION_RESULT_FIELD_NAME = "result"
|
||||
|
||||
val RELEASE_COROUTINES_VERSION_SETTINGS = LanguageVersionSettingsImpl(LanguageVersion.KOTLIN_1_3, ApiVersion.KOTLIN_1_3)
|
||||
private const val GET_CONTEXT_METHOD_NAME = "getContext"
|
||||
|
||||
fun LanguageVersionSettings.isResumeImplMethodName(name: String) =
|
||||
if (isReleaseCoroutines())
|
||||
name == INVOKE_SUSPEND_METHOD_NAME
|
||||
else
|
||||
name == DO_RESUME_METHOD_NAME
|
||||
val DEBUG_METADATA_ANNOTATION_ASM_TYPE: Type =
|
||||
COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.child(Name.identifier("DebugMetadata")).topLevelClassAsmType()
|
||||
|
||||
fun LanguageVersionSettings.dataFieldName(): String = if (isReleaseCoroutines()) "result" else "data"
|
||||
fun coroutineContextAsmType(): Type =
|
||||
StandardNames.COROUTINES_PACKAGE_FQ_NAME.child(Name.identifier("CoroutineContext")).topLevelClassAsmType()
|
||||
|
||||
fun isResumeImplMethodNameFromAnyLanguageSettings(name: String) = name == INVOKE_SUSPEND_METHOD_NAME || name == DO_RESUME_METHOD_NAME
|
||||
|
||||
fun LanguageVersionSettings.coroutinesJvmInternalPackageFqName() =
|
||||
coroutinesPackageFqName().child(Name.identifier("jvm")).child(Name.identifier("internal"))
|
||||
|
||||
val DEBUG_METADATA_ANNOTATION_ASM_TYPE = RELEASE_COROUTINES_VERSION_SETTINGS.coroutinesJvmInternalPackageFqName()
|
||||
.child(Name.identifier("DebugMetadata")).topLevelClassAsmType()
|
||||
|
||||
fun LanguageVersionSettings.continuationAsmType() =
|
||||
continuationInterfaceFqName().topLevelClassAsmType()
|
||||
|
||||
fun continuationAsmTypes() = listOf(
|
||||
LanguageVersionSettingsImpl(LanguageVersion.KOTLIN_1_3, ApiVersion.KOTLIN_1_3).continuationAsmType(),
|
||||
LanguageVersionSettingsImpl(LanguageVersion.KOTLIN_1_2, ApiVersion.KOTLIN_1_2).continuationAsmType()
|
||||
)
|
||||
|
||||
fun LanguageVersionSettings.coroutineContextAsmType() =
|
||||
coroutinesPackageFqName().child(Name.identifier("CoroutineContext")).topLevelClassAsmType()
|
||||
|
||||
fun LanguageVersionSettings.isCoroutineSuperClass(internalName: String): Boolean {
|
||||
val coroutinesJvmInternalPackage = coroutinesJvmInternalPackageFqName()
|
||||
|
||||
return if (isReleaseCoroutines())
|
||||
coroutinesJvmInternalPackage.identifiedChild("ContinuationImpl") == internalName ||
|
||||
coroutinesJvmInternalPackage.identifiedChild("RestrictedContinuationImpl") == internalName ||
|
||||
coroutinesJvmInternalPackage.identifiedChild("SuspendLambda") == internalName ||
|
||||
coroutinesJvmInternalPackage.identifiedChild("RestrictedSuspendLambda") == internalName
|
||||
else
|
||||
coroutinesJvmInternalPackage.identifiedChild("CoroutineImpl") == internalName
|
||||
}
|
||||
fun String.isCoroutineSuperClass(): Boolean =
|
||||
COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.identifiedChild("ContinuationImpl") == this ||
|
||||
COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.identifiedChild("RestrictedContinuationImpl") == this ||
|
||||
COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.identifiedChild("SuspendLambda") == this ||
|
||||
COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.identifiedChild("RestrictedSuspendLambda") == this
|
||||
|
||||
private fun FqName.identifiedChild(name: String) = child(Name.identifier(name)).topLevelClassInternalName()
|
||||
|
||||
private fun LanguageVersionSettings.coroutinesIntrinsicsFileFacadeInternalName() =
|
||||
coroutinesIntrinsicsPackageFqName().child(Name.identifier("IntrinsicsKt")).topLevelClassAsmType()
|
||||
|
||||
private fun LanguageVersionSettings.internalCoroutineIntrinsicsOwnerInternalName() =
|
||||
coroutinesJvmInternalPackageFqName().child(Name.identifier("CoroutineIntrinsics")).topLevelClassInternalName()
|
||||
|
||||
fun computeLabelOwner(languageVersionSettings: LanguageVersionSettings, thisName: String): Type =
|
||||
if (languageVersionSettings.isReleaseCoroutines())
|
||||
Type.getObjectType(thisName)
|
||||
else
|
||||
languageVersionSettings.coroutinesJvmInternalPackageFqName().child(Name.identifier("CoroutineImpl")).topLevelClassAsmType()
|
||||
|
||||
private const val NORMALIZE_CONTINUATION_METHOD_NAME = "normalizeContinuation"
|
||||
private const val GET_CONTEXT_METHOD_NAME = "getContext"
|
||||
private val coroutinesIntrinsicsFileFacadeInternalName: Type =
|
||||
COROUTINES_INTRINSICS_PACKAGE_FQ_NAME.child(Name.identifier("IntrinsicsKt")).topLevelClassAsmType()
|
||||
|
||||
data class ResolvedCallWithRealDescriptor(val resolvedCall: ResolvedCall<*>, val fakeContinuationExpression: KtExpression)
|
||||
|
||||
@@ -122,7 +84,7 @@ data class ResolvedCallWithRealDescriptor(val resolvedCall: ResolvedCall<*>, val
|
||||
val INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION = object : CallableDescriptor.UserDataKey<FunctionDescriptor> {}
|
||||
|
||||
@JvmField
|
||||
val INITIAL_SUSPEND_DESCRIPTOR_FOR_DO_RESUME = object : CallableDescriptor.UserDataKey<FunctionDescriptor> {}
|
||||
val INITIAL_SUSPEND_DESCRIPTOR_FOR_INVOKE_SUSPEND = object : CallableDescriptor.UserDataKey<FunctionDescriptor> {}
|
||||
|
||||
val CONTINUATION_PARAMETER_NAME = Name.identifier("continuation")
|
||||
|
||||
@@ -158,9 +120,9 @@ fun ResolvedCall<*>.replaceSuspensionFunctionWithRealDescriptor(
|
||||
val newCandidateDescriptor =
|
||||
when (function) {
|
||||
is FunctionImportedFromObject ->
|
||||
getOrCreateJvmSuspendFunctionView(function.callableFromObject, isReleaseCoroutines, bindingContext).asImportedFromObject()
|
||||
getOrCreateJvmSuspendFunctionView(function.callableFromObject, bindingContext).asImportedFromObject()
|
||||
is SimpleFunctionDescriptor ->
|
||||
getOrCreateJvmSuspendFunctionView(function, isReleaseCoroutines, bindingContext)
|
||||
getOrCreateJvmSuspendFunctionView(function, bindingContext)
|
||||
else ->
|
||||
throw AssertionError("Unexpected suspend function descriptor: $function")
|
||||
}
|
||||
@@ -223,10 +185,10 @@ private fun NewResolvedCallImpl<VariableDescriptor>.asDummyOldResolvedCall(bindi
|
||||
|
||||
enum class SuspensionPointKind { NEVER, NOT_INLINE, ALWAYS }
|
||||
|
||||
fun ResolvedCall<*>.isSuspensionPoint(codegen: ExpressionCodegen, languageVersionSettings: LanguageVersionSettings): SuspensionPointKind {
|
||||
fun ResolvedCall<*>.isSuspensionPoint(codegen: ExpressionCodegen): SuspensionPointKind {
|
||||
val functionDescriptor = resultingDescriptor as? FunctionDescriptor ?: return SuspensionPointKind.NEVER
|
||||
if (!functionDescriptor.unwrapInitialDescriptorForSuspendFunction().isSuspend) return SuspensionPointKind.NEVER
|
||||
if (functionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm(languageVersionSettings)) return SuspensionPointKind.ALWAYS
|
||||
if (functionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm()) return SuspensionPointKind.ALWAYS
|
||||
if (functionDescriptor.isInline) return SuspensionPointKind.NEVER
|
||||
|
||||
val isInlineLambda = this.safeAs<VariableAsFunctionResolvedCall>()
|
||||
@@ -242,7 +204,6 @@ fun CallableDescriptor.isSuspendFunctionNotSuspensionView(): Boolean {
|
||||
|
||||
fun <D : FunctionDescriptor> getOrCreateJvmSuspendFunctionView(function: D, state: GenerationState): D = getOrCreateJvmSuspendFunctionView(
|
||||
function,
|
||||
state.languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines),
|
||||
state.bindingContext
|
||||
)
|
||||
|
||||
@@ -252,7 +213,6 @@ fun <D : FunctionDescriptor> getOrCreateJvmSuspendFunctionView(function: D, stat
|
||||
@JvmOverloads
|
||||
fun <D : FunctionDescriptor> getOrCreateJvmSuspendFunctionView(
|
||||
function: D,
|
||||
isReleaseCoroutines: Boolean,
|
||||
bindingContext: BindingContext? = null
|
||||
): D {
|
||||
assert(function.isSuspend) {
|
||||
@@ -272,7 +232,7 @@ fun <D : FunctionDescriptor> getOrCreateJvmSuspendFunctionView(
|
||||
outType = if (function.containingDeclaration.safeAs<ClassDescriptor>()?.isBuiltinFunctionalClassDescriptor == true)
|
||||
function.builtIns.nullableAnyType
|
||||
else
|
||||
function.getContinuationParameterTypeOfSuspendFunction(isReleaseCoroutines),
|
||||
function.getContinuationParameterTypeOfSuspendFunction(),
|
||||
declaresDefaultValue = false, isCrossinline = false,
|
||||
isNoinline = false, varargElementType = null,
|
||||
source = SourceElement.NO_SOURCE
|
||||
@@ -309,8 +269,7 @@ fun <D : FunctionDescriptor> D.createCustomCopy(
|
||||
return result as D
|
||||
}
|
||||
|
||||
private fun FunctionDescriptor.getContinuationParameterTypeOfSuspendFunction(isReleaseCoroutines: Boolean) =
|
||||
module.getContinuationOfTypeOrAny(returnType!!, if (this.needsExperimentalCoroutinesWrapper()) false else isReleaseCoroutines)
|
||||
private fun FunctionDescriptor.getContinuationParameterTypeOfSuspendFunction() = module.getContinuationOfTypeOrAny(returnType!!)
|
||||
|
||||
fun ModuleDescriptor.getResult(kotlinType: KotlinType) =
|
||||
module.resolveTopLevelClass(
|
||||
@@ -323,44 +282,11 @@ fun ModuleDescriptor.getResult(kotlinType: KotlinType) =
|
||||
)
|
||||
} ?: ErrorUtils.createErrorType("For Result")
|
||||
|
||||
private fun MethodNode.invokeNormalizeContinuation(languageVersionSettings: LanguageVersionSettings) {
|
||||
visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
languageVersionSettings.internalCoroutineIntrinsicsOwnerInternalName(),
|
||||
NORMALIZE_CONTINUATION_METHOD_NAME,
|
||||
Type.getMethodDescriptor(languageVersionSettings.continuationAsmType(), languageVersionSettings.continuationAsmType()),
|
||||
false
|
||||
)
|
||||
}
|
||||
fun FunctionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm() =
|
||||
getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION)?.isBuiltInSuspendCoroutineUninterceptedOrReturn() == true
|
||||
|
||||
fun FunctionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm(languageVersionSettings: LanguageVersionSettings) =
|
||||
getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION)?.isBuiltInSuspendCoroutineUninterceptedOrReturn(languageVersionSettings) == true
|
||||
|
||||
fun createMethodNodeForIntercepted(languageVersionSettings: LanguageVersionSettings): MethodNode {
|
||||
val node =
|
||||
MethodNode(
|
||||
Opcodes.API_VERSION,
|
||||
Opcodes.ACC_STATIC,
|
||||
"fake",
|
||||
Type.getMethodDescriptor(languageVersionSettings.continuationAsmType(), languageVersionSettings.continuationAsmType()),
|
||||
null, null
|
||||
)
|
||||
|
||||
node.visitVarInsn(Opcodes.ALOAD, 0)
|
||||
|
||||
node.invokeNormalizeContinuation(languageVersionSettings)
|
||||
|
||||
node.visitInsn(Opcodes.ARETURN)
|
||||
node.visitMaxs(1, 1)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
fun createMethodNodeForCoroutineContext(
|
||||
functionDescriptor: FunctionDescriptor,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
): MethodNode {
|
||||
assert(functionDescriptor.isBuiltInCoroutineContext(languageVersionSettings)) {
|
||||
fun createMethodNodeForCoroutineContext(functionDescriptor: FunctionDescriptor): MethodNode {
|
||||
assert(functionDescriptor.isBuiltInCoroutineContext()) {
|
||||
"functionDescriptor must be kotlin.coroutines.intrinsics.coroutineContext property getter"
|
||||
}
|
||||
|
||||
@@ -369,7 +295,7 @@ fun createMethodNodeForCoroutineContext(
|
||||
Opcodes.API_VERSION,
|
||||
Opcodes.ACC_STATIC,
|
||||
"fake",
|
||||
Type.getMethodDescriptor(languageVersionSettings.coroutineContextAsmType()),
|
||||
Type.getMethodDescriptor(coroutineContextAsmType()),
|
||||
null, null
|
||||
)
|
||||
|
||||
@@ -377,20 +303,20 @@ fun createMethodNodeForCoroutineContext(
|
||||
|
||||
addFakeContinuationMarker(v)
|
||||
|
||||
v.invokeGetContext(languageVersionSettings)
|
||||
v.invokeGetContext()
|
||||
|
||||
node.visitMaxs(1, 1)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
fun createMethodNodeForSuspendCoroutineUninterceptedOrReturn(languageVersionSettings: LanguageVersionSettings): MethodNode {
|
||||
fun createMethodNodeForSuspendCoroutineUninterceptedOrReturn(): MethodNode {
|
||||
val node =
|
||||
MethodNode(
|
||||
Opcodes.API_VERSION,
|
||||
Opcodes.ACC_STATIC,
|
||||
"fake",
|
||||
Type.getMethodDescriptor(OBJECT_TYPE, AsmTypes.FUNCTION1, languageVersionSettings.continuationAsmType()),
|
||||
Type.getMethodDescriptor(OBJECT_TYPE, AsmTypes.FUNCTION1, CONTINUATION_ASM_TYPE),
|
||||
null, null
|
||||
)
|
||||
|
||||
@@ -405,25 +331,22 @@ fun createMethodNodeForSuspendCoroutineUninterceptedOrReturn(languageVersionSett
|
||||
"($OBJECT_TYPE)$OBJECT_TYPE"
|
||||
)
|
||||
|
||||
if (languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)) {
|
||||
val elseLabel = Label()
|
||||
// if (result === COROUTINE_SUSPENDED) {
|
||||
dup()
|
||||
loadCoroutineSuspendedMarker(languageVersionSettings)
|
||||
ifacmpne(elseLabel)
|
||||
// DebugProbesKt.probeCoroutineSuspended(continuation)
|
||||
load(1, OBJECT_TYPE) // continuation
|
||||
checkcast(languageVersionSettings.continuationAsmType())
|
||||
invokestatic(
|
||||
languageVersionSettings.coroutinesJvmInternalPackageFqName().child(Name.identifier("DebugProbesKt"))
|
||||
.topLevelClassAsmType().internalName,
|
||||
"probeCoroutineSuspended",
|
||||
"(${languageVersionSettings.continuationAsmType()})V",
|
||||
false
|
||||
)
|
||||
// }
|
||||
mark(elseLabel)
|
||||
}
|
||||
val elseLabel = Label()
|
||||
// if (result === COROUTINE_SUSPENDED) {
|
||||
dup()
|
||||
loadCoroutineSuspendedMarker()
|
||||
ifacmpne(elseLabel)
|
||||
// DebugProbesKt.probeCoroutineSuspended(continuation)
|
||||
load(1, OBJECT_TYPE) // continuation
|
||||
checkcast(CONTINUATION_ASM_TYPE)
|
||||
invokestatic(
|
||||
COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.child(Name.identifier("DebugProbesKt")).topLevelClassAsmType().internalName,
|
||||
"probeCoroutineSuspended",
|
||||
"($CONTINUATION_ASM_TYPE)V",
|
||||
false
|
||||
)
|
||||
// }
|
||||
mark(elseLabel)
|
||||
}
|
||||
|
||||
node.visitInsn(Opcodes.ARETURN)
|
||||
@@ -433,13 +356,13 @@ fun createMethodNodeForSuspendCoroutineUninterceptedOrReturn(languageVersionSett
|
||||
}
|
||||
|
||||
|
||||
private fun InstructionAdapter.invokeGetContext(languageVersionSettings: LanguageVersionSettings) {
|
||||
private fun InstructionAdapter.invokeGetContext() {
|
||||
invokeinterface(
|
||||
languageVersionSettings.continuationAsmType().internalName,
|
||||
CONTINUATION_ASM_TYPE.internalName,
|
||||
GET_CONTEXT_METHOD_NAME,
|
||||
Type.getMethodDescriptor(languageVersionSettings.coroutineContextAsmType())
|
||||
Type.getMethodDescriptor(coroutineContextAsmType())
|
||||
)
|
||||
areturn(languageVersionSettings.coroutineContextAsmType())
|
||||
areturn(coroutineContextAsmType())
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@@ -447,15 +370,12 @@ fun <D : CallableDescriptor?> D.unwrapInitialDescriptorForSuspendFunction(): D =
|
||||
this.safeAs<SimpleFunctionDescriptor>()?.getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION) as D ?: this
|
||||
|
||||
|
||||
fun FunctionDescriptor.getOriginalSuspendFunctionView(bindingContext: BindingContext, isReleaseCoroutines: Boolean): FunctionDescriptor =
|
||||
fun FunctionDescriptor.getOriginalSuspendFunctionView(bindingContext: BindingContext): FunctionDescriptor =
|
||||
if (isSuspend)
|
||||
getOrCreateJvmSuspendFunctionView(unwrapInitialDescriptorForSuspendFunction().original, isReleaseCoroutines, bindingContext)
|
||||
getOrCreateJvmSuspendFunctionView(unwrapInitialDescriptorForSuspendFunction().original, bindingContext)
|
||||
else
|
||||
this
|
||||
|
||||
fun FunctionDescriptor.getOriginalSuspendFunctionView(bindingContext: BindingContext, state: GenerationState) =
|
||||
getOriginalSuspendFunctionView(bindingContext, state.languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines))
|
||||
|
||||
// For each suspend function, we have a corresponding JVM view function that has an extra continuation parameter,
|
||||
// and, more importantly, returns 'kotlin.Any' (so that it can return as a reference value or a special COROUTINE_SUSPENDED object).
|
||||
// This also causes boxing of primitives and inline class values.
|
||||
@@ -482,38 +402,24 @@ fun FunctionDescriptor.originalReturnTypeOfSuspendFunctionReturningUnboxedInline
|
||||
return originalReturnType
|
||||
}
|
||||
|
||||
fun InstructionAdapter.loadCoroutineSuspendedMarker(languageVersionSettings: LanguageVersionSettings) {
|
||||
fun InstructionAdapter.loadCoroutineSuspendedMarker() {
|
||||
invokestatic(
|
||||
languageVersionSettings.coroutinesIntrinsicsFileFacadeInternalName().internalName,
|
||||
coroutinesIntrinsicsFileFacadeInternalName.internalName,
|
||||
"get$COROUTINE_SUSPENDED_NAME",
|
||||
Type.getMethodDescriptor(OBJECT_TYPE),
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
fun InstructionAdapter.generateCoroutineSuspendedCheck(languageVersionSettings: LanguageVersionSettings) {
|
||||
fun InstructionAdapter.generateCoroutineSuspendedCheck() {
|
||||
dup()
|
||||
loadCoroutineSuspendedMarker(languageVersionSettings)
|
||||
loadCoroutineSuspendedMarker()
|
||||
val elseLabel = Label()
|
||||
ifacmpne(elseLabel)
|
||||
areturn(OBJECT_TYPE)
|
||||
mark(elseLabel)
|
||||
}
|
||||
|
||||
fun InstructionAdapter.invokeDoResumeWithUnit(thisName: String) {
|
||||
// .doResume(Unit, null)
|
||||
StackValue.putUnitInstance(this)
|
||||
|
||||
aconst(null)
|
||||
|
||||
invokevirtual(
|
||||
thisName,
|
||||
DO_RESUME_METHOD_NAME,
|
||||
Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE, AsmTypes.JAVA_THROWABLE_TYPE),
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
fun InstructionAdapter.invokeInvokeSuspendWithUnit(thisName: String) {
|
||||
StackValue.putUnitInstance(this)
|
||||
|
||||
@@ -537,18 +443,14 @@ fun FunctionDescriptor.isSuspendLambdaOrLocalFunction() = this.isSuspend && when
|
||||
}
|
||||
|
||||
fun FunctionDescriptor.isLocalSuspendFunctionNotSuspendLambda() = isSuspendLambdaOrLocalFunction() && this !is AnonymousFunctionDescriptor
|
||||
|
||||
@JvmField
|
||||
val EXPERIMENTAL_CONTINUATION_ASM_TYPE = StandardNames.CONTINUATION_INTERFACE_FQ_NAME_EXPERIMENTAL.topLevelClassAsmType()
|
||||
|
||||
@JvmField
|
||||
val RELEASE_CONTINUATION_ASM_TYPE = StandardNames.CONTINUATION_INTERFACE_FQ_NAME_RELEASE.topLevelClassAsmType()
|
||||
val CONTINUATION_ASM_TYPE = StandardNames.CONTINUATION_INTERFACE_FQ_NAME.topLevelClassAsmType()
|
||||
|
||||
fun FunctionDescriptor.isInvokeSuspendOfLambda(): Boolean {
|
||||
if (this !is SimpleFunctionDescriptor) return false
|
||||
if (valueParameters.size != 1 ||
|
||||
valueParameters[0].name.asString() != SUSPEND_CALL_RESULT_NAME ||
|
||||
name.asString() != "invokeSuspend"
|
||||
name.asString() != INVOKE_SUSPEND_METHOD_NAME
|
||||
) return false
|
||||
return containingDeclaration is SyntheticClassDescriptorForLambda
|
||||
}
|
||||
@@ -171,10 +171,11 @@ class UninitializedStoresProcessor(
|
||||
|
||||
assert(insn.opcode == Opcodes.INVOKESPECIAL) { "Expected opcode Opcodes.INVOKESPECIAL for <init>, but ${insn.opcode} found" }
|
||||
val paramsCountIncludingReceiver = Type.getArgumentTypes((insn as MethodInsnNode).desc).size + 1
|
||||
val newValue = peek(paramsCountIncludingReceiver) as? UninitializedNewValue ?: if (isInSpecialMethod)
|
||||
return null
|
||||
else
|
||||
error("Expected value generated with NEW")
|
||||
val newValue = peek(paramsCountIncludingReceiver) as? UninitializedNewValue
|
||||
?: if (isInSpecialMethod)
|
||||
return null
|
||||
else
|
||||
error("Expected value generated with NEW")
|
||||
|
||||
assert(peek(paramsCountIncludingReceiver - 1) is UninitializedNewValue) {
|
||||
"Next value after NEW should be one generated by DUP"
|
||||
|
||||
@@ -62,7 +62,7 @@ class AnonymousObjectTransformer(
|
||||
createClassReader().accept(object : ClassVisitor(Opcodes.API_VERSION, classBuilder.visitor) {
|
||||
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<String>) {
|
||||
classBuilder.defineClass(null, maxOf(version, state.classFileVersion), access, name, signature, superName, interfaces)
|
||||
if (languageVersionSettings.isCoroutineSuperClass(superName)) {
|
||||
if (superName.isCoroutineSuperClass()) {
|
||||
inliningContext.isContinuation = true
|
||||
}
|
||||
superClassName = superName
|
||||
@@ -320,7 +320,8 @@ class AnonymousObjectTransformer(
|
||||
inliningContext.callSiteInfo.isInlineOrInsideInline,
|
||||
inliningContext.callSiteInfo.file,
|
||||
inliningContext.callSiteInfo.lineNumber
|
||||
), null
|
||||
),
|
||||
null
|
||||
).doInline(deferringVisitor, LocalVarRemapper(parameters, 0), false, mapOf())
|
||||
reifiedTypeParametersUsages?.let(result.reifiedTypeParametersUsages::mergeAll)
|
||||
deferringVisitor.visitMaxs(-1, -1)
|
||||
|
||||
@@ -187,6 +187,9 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
val splitBy = SimpleInterval(start.info as LabelNode, extension.finallyIntervalEnd)
|
||||
processor.tryBlocksMetaInfo.splitAndRemoveCurrentIntervals(splitBy, true)
|
||||
processor.localVarsMetaInfo.splitAndRemoveCurrentIntervals(splitBy, true)
|
||||
finallyNode.localVariables.forEach {
|
||||
processor.localVarsMetaInfo.addNewInterval(LocalVarNodeWrapper(it))
|
||||
}
|
||||
}
|
||||
|
||||
curInstr = curInstr.next
|
||||
|
||||
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.isMethodInsnWith
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.removeUnusedLocalVariables
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.updateMaxStack
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
|
||||
class InplaceArgumentsMethodTransformer : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
val methodContext = parseMethodOrNull(methodNode)
|
||||
if (methodContext != null) {
|
||||
if (methodContext.calls.isEmpty()) return
|
||||
|
||||
collectStartToEnd(methodContext)
|
||||
collectLvtEntryInstructions(methodContext)
|
||||
collectSuspensionPoints(methodContext)
|
||||
|
||||
transformMethod(methodContext)
|
||||
updateLvtEntriesForMovedInstructions(methodContext)
|
||||
|
||||
methodNode.removeUnusedLocalVariables()
|
||||
methodNode.updateMaxStack()
|
||||
}
|
||||
stripMarkers(methodNode)
|
||||
}
|
||||
|
||||
private class MethodContext(
|
||||
val methodNode: MethodNode,
|
||||
val calls: List<CallContext>
|
||||
) {
|
||||
val startArgToEndArg = HashMap<AbstractInsnNode, AbstractInsnNode>()
|
||||
val lvtEntryForInstruction = HashMap<AbstractInsnNode, LocalVariableNode>()
|
||||
val varInstructionMoved = HashMap<AbstractInsnNode, CallContext>()
|
||||
val suspensionJumpLabels = HashSet<LabelNode>()
|
||||
}
|
||||
|
||||
private class CallContext(
|
||||
val callStartMarker: AbstractInsnNode,
|
||||
val callEndMarker: AbstractInsnNode,
|
||||
val args: List<ArgContext>,
|
||||
val calls: List<CallContext>,
|
||||
val endLabel: LabelNode
|
||||
)
|
||||
|
||||
private class ArgContext(
|
||||
val argStartMarker: AbstractInsnNode,
|
||||
val argEndMarker: AbstractInsnNode,
|
||||
val calls: List<CallContext>,
|
||||
val storeInsn: VarInsnNode
|
||||
) {
|
||||
val loadOpcode = storeInsn.opcode - Opcodes.ISTORE + Opcodes.ILOAD
|
||||
|
||||
val varIndex = storeInsn.`var`
|
||||
}
|
||||
|
||||
private fun parseMethodOrNull(methodNode: MethodNode): MethodContext? {
|
||||
// We assume that the method body structure follows this grammar:
|
||||
// METHOD ::= insn* (CALL insn*)*
|
||||
// CALL ::= callStartMarker insn* (ARG insn*)* (CALL insn*)* callEndMarker
|
||||
// ARG ::= argStartMarker insn* (CALL insn*)* argEndMarker storeInsn
|
||||
|
||||
val iter = methodNode.instructions.iterator()
|
||||
val calls = ArrayList<CallContext>()
|
||||
try {
|
||||
while (iter.hasNext()) {
|
||||
val insn = iter.next()
|
||||
when {
|
||||
insn.isInplaceCallStartMarker() ->
|
||||
calls.add(parseCall(methodNode, insn, iter))
|
||||
insn.isInplaceCallEndMarker() || insn.isInplaceArgumentStartMarker() || insn.isInplaceArgumentEndMarker() ->
|
||||
throw ParseErrorException()
|
||||
}
|
||||
}
|
||||
} catch (e: ParseErrorException) {
|
||||
return null
|
||||
}
|
||||
return MethodContext(methodNode, calls)
|
||||
}
|
||||
|
||||
private fun parseCall(methodNode: MethodNode, start: AbstractInsnNode, iter: ListIterator<AbstractInsnNode>): CallContext {
|
||||
// CALL ::= callStartMarker insn* (ARG insn*)* (CALL insn*)* callEndMarker
|
||||
val args = ArrayList<ArgContext>()
|
||||
val calls = ArrayList<CallContext>()
|
||||
while (iter.hasNext()) {
|
||||
val insn = iter.next()
|
||||
when {
|
||||
insn.isInplaceCallStartMarker() ->
|
||||
calls.add(parseCall(methodNode, insn, iter))
|
||||
insn.isInplaceCallEndMarker() -> {
|
||||
val previous = insn.previous
|
||||
val endLabel =
|
||||
if (previous.type == AbstractInsnNode.LABEL)
|
||||
previous as LabelNode
|
||||
else
|
||||
LabelNode(Label()).also {
|
||||
// Make sure each call with inplace arguments has an endLabel
|
||||
// (we need it to update LVT after transformation).
|
||||
methodNode.instructions.insertBefore(insn, it)
|
||||
}
|
||||
return CallContext(start, insn, args, calls, endLabel)
|
||||
}
|
||||
insn.isInplaceArgumentStartMarker() ->
|
||||
args.add(parseArg(methodNode, insn, iter))
|
||||
insn.isInplaceArgumentEndMarker() ->
|
||||
throw ParseErrorException()
|
||||
}
|
||||
}
|
||||
// Reached instruction list end, didn't find inplace-call-end marker
|
||||
throw ParseErrorException()
|
||||
}
|
||||
|
||||
private fun parseArg(methodNode: MethodNode, start: AbstractInsnNode, iter: ListIterator<AbstractInsnNode>): ArgContext {
|
||||
// ARG ::= argStartMarker insn* (CALL insn*)* argEndMarker storeInsn
|
||||
val calls = ArrayList<CallContext>()
|
||||
while (iter.hasNext()) {
|
||||
val insn = iter.next()
|
||||
when {
|
||||
insn.isInplaceCallStartMarker() ->
|
||||
calls.add(parseCall(methodNode, insn, iter))
|
||||
insn.isInplaceArgumentEndMarker() -> {
|
||||
val next = insn.next
|
||||
if (next is VarInsnNode && next.opcode in Opcodes.ISTORE..Opcodes.ASTORE) {
|
||||
iter.next()
|
||||
return ArgContext(start, insn, calls, next)
|
||||
} else {
|
||||
throw ParseErrorException()
|
||||
}
|
||||
}
|
||||
insn.isInplaceCallEndMarker() || insn.isInplaceArgumentStartMarker() ->
|
||||
throw ParseErrorException()
|
||||
}
|
||||
}
|
||||
// Reached instruction list end, didn't find inplace-argument-end marker
|
||||
throw ParseErrorException()
|
||||
}
|
||||
|
||||
private class ParseErrorException : RuntimeException() {
|
||||
override fun fillInStackTrace(): Throwable = this
|
||||
}
|
||||
|
||||
private fun collectStartToEnd(methodContext: MethodContext) {
|
||||
for (call in methodContext.calls) {
|
||||
collectStartToEnd(methodContext, call)
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectStartToEnd(methodContext: MethodContext, callContext: CallContext) {
|
||||
for (arg in callContext.args) {
|
||||
collectStartToEnd(methodContext, arg)
|
||||
}
|
||||
for (call in callContext.calls) {
|
||||
collectStartToEnd(methodContext, call)
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectStartToEnd(methodContext: MethodContext, argContext: ArgContext) {
|
||||
methodContext.startArgToEndArg[argContext.argStartMarker] = argContext.argEndMarker
|
||||
for (call in argContext.calls) {
|
||||
collectStartToEnd(methodContext, call)
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectLvtEntryInstructions(methodContext: MethodContext) {
|
||||
val insnList = methodContext.methodNode.instructions
|
||||
val insnArray = insnList.toArray()
|
||||
for (lv in methodContext.methodNode.localVariables) {
|
||||
val lvStartIndex = insnList.indexOf(lv.start)
|
||||
val lvEndIndex = insnList.indexOf(lv.end)
|
||||
for (i in lvStartIndex until lvEndIndex) {
|
||||
val insn = insnArray[i]
|
||||
if (insn.opcode in Opcodes.ILOAD..Opcodes.ALOAD || insn.opcode in Opcodes.ISTORE..Opcodes.ASTORE) {
|
||||
if ((insn as VarInsnNode).`var` == lv.index) {
|
||||
methodContext.lvtEntryForInstruction[insn] = lv
|
||||
}
|
||||
} else if (insn.opcode == Opcodes.IINC) {
|
||||
if ((insn as IincInsnNode).`var` == lv.index) {
|
||||
methodContext.lvtEntryForInstruction[insn] = lv
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectSuspensionPoints(methodContext: MethodContext) {
|
||||
val insnList = methodContext.methodNode.instructions
|
||||
var insn = insnList.first
|
||||
while (
|
||||
!insn.isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
||||
owner == "kotlin/coroutines/intrinsics/IntrinsicsKt" &&
|
||||
name == "getCOROUTINE_SUSPENDED" &&
|
||||
desc == "()Ljava/lang/Object;"
|
||||
}
|
||||
) {
|
||||
insn = insn.next ?: return
|
||||
}
|
||||
|
||||
// Find a first TABLESWITCH and record its jump destinations
|
||||
while (insn != null) {
|
||||
if (insn.opcode != Opcodes.TABLESWITCH || insn.previous.opcode != Opcodes.GETFIELD) {
|
||||
insn = insn.next
|
||||
continue
|
||||
}
|
||||
val getFiendInsn = insn.previous as FieldInsnNode
|
||||
if (getFiendInsn.name != "label" || getFiendInsn.desc != "I") {
|
||||
insn = insn.next
|
||||
continue
|
||||
}
|
||||
val tableSwitchInsn = insn as TableSwitchInsnNode
|
||||
methodContext.suspensionJumpLabels.addAll(tableSwitchInsn.labels)
|
||||
methodContext.suspensionJumpLabels.add(tableSwitchInsn.dflt)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformMethod(methodContext: MethodContext) {
|
||||
for (call in methodContext.calls) {
|
||||
transformCall(methodContext, call)
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformCall(methodContext: MethodContext, callContext: CallContext) {
|
||||
// Transform nested calls
|
||||
for (arg in callContext.args) {
|
||||
for (nestedCall in arg.calls) {
|
||||
transformCall(methodContext, nestedCall)
|
||||
}
|
||||
}
|
||||
for (call in callContext.calls) {
|
||||
transformCall(methodContext, call)
|
||||
}
|
||||
|
||||
// If an inplace argument contains a non-local jump,
|
||||
// moving such argument inside inline function body can interfere with stack normalization.
|
||||
// TODO investigate complex cases
|
||||
if (callContext.args.any { it.isUnsafeToMove(methodContext) }) {
|
||||
// Do not transform such call, just strip call and argument markers.
|
||||
val insnList = methodContext.methodNode.instructions
|
||||
for (arg in callContext.args) {
|
||||
insnList.remove(arg.argStartMarker)
|
||||
insnList.remove(arg.argEndMarker)
|
||||
}
|
||||
insnList.remove(callContext.callStartMarker)
|
||||
insnList.remove(callContext.callEndMarker)
|
||||
return
|
||||
}
|
||||
|
||||
moveInplaceArgumentsFromStoresToLoads(methodContext, callContext)
|
||||
}
|
||||
|
||||
private fun ArgContext.isUnsafeToMove(methodContext: MethodContext): Boolean {
|
||||
val argInsns = InsnSequence(this.argStartMarker, this.argEndMarker)
|
||||
val localLabels = argInsns.filterTo(HashSet()) { it is LabelNode }
|
||||
return argInsns.any { insn ->
|
||||
insn in methodContext.suspensionJumpLabels ||
|
||||
insn.opcode == Opcodes.GOTO && (insn as JumpInsnNode).label !in localLabels
|
||||
}
|
||||
}
|
||||
|
||||
private fun moveInplaceArgumentsFromStoresToLoads(methodContext: MethodContext, callContext: CallContext) {
|
||||
// Transform call
|
||||
val insnList = methodContext.methodNode.instructions
|
||||
val args = callContext.args.associateBy { it.varIndex }
|
||||
var argsProcessed = 0
|
||||
|
||||
var insn: AbstractInsnNode = callContext.callStartMarker
|
||||
while (insn != callContext.callEndMarker) {
|
||||
when {
|
||||
insn.isInplaceArgumentStartMarker() -> {
|
||||
// Skip argument body
|
||||
insn = methodContext.startArgToEndArg[insn]!!
|
||||
}
|
||||
|
||||
insn.opcode in Opcodes.ILOAD..Opcodes.ALOAD -> {
|
||||
// Load instruction
|
||||
val loadInsn = insn as VarInsnNode
|
||||
val varIndex = loadInsn.`var`
|
||||
val arg = args[varIndex]
|
||||
|
||||
if (arg == null || arg.loadOpcode != insn.opcode) {
|
||||
// Not an argument load
|
||||
insn = insn.next
|
||||
} else {
|
||||
// For each argument within this call we have
|
||||
// <inplaceArgStartMarker>
|
||||
// <argumentBody>
|
||||
// <inplaceArgEndMarker>
|
||||
// store [arg]
|
||||
// ...
|
||||
// load [arg]
|
||||
// Replace 'load [arg]' with '<argumentBody>', drop 'store [arg]' and argument markers.
|
||||
|
||||
var argInsn = arg.argStartMarker.next
|
||||
while (argInsn != arg.argEndMarker) {
|
||||
// If a LOAD/STORE/IINC instruction was moved,
|
||||
// record it so that we can update corresponding LVT entry if needed.
|
||||
// NB it's better to do so after all transformations, so that we don't recalculate node indices.
|
||||
if (argInsn.opcode in Opcodes.ILOAD..Opcodes.ALOAD ||
|
||||
argInsn.opcode in Opcodes.ISTORE..Opcodes.ASTORE ||
|
||||
argInsn.opcode == Opcodes.IINC
|
||||
) {
|
||||
methodContext.varInstructionMoved[argInsn] = callContext
|
||||
}
|
||||
|
||||
val argInsnNext = argInsn.next
|
||||
insnList.remove(argInsn)
|
||||
insnList.insertBefore(loadInsn, argInsn)
|
||||
argInsn = argInsnNext
|
||||
}
|
||||
|
||||
// Remove argument load and corresponding argument store instructions
|
||||
insnList.remove(arg.storeInsn)
|
||||
insn = loadInsn.next
|
||||
insnList.remove(loadInsn)
|
||||
|
||||
// Replace subsequent argument loads with DUP instructions of appropriate size
|
||||
while (insn.opcode == loadInsn.opcode && (insn as VarInsnNode).`var` == varIndex) {
|
||||
if (insn.opcode == Opcodes.LLOAD || insn.opcode == Opcodes.DLOAD) {
|
||||
insnList.insertBefore(insn, InsnNode(Opcodes.DUP2))
|
||||
} else {
|
||||
insnList.insertBefore(insn, InsnNode(Opcodes.DUP))
|
||||
}
|
||||
val next = insn.next
|
||||
insnList.remove(insn)
|
||||
insn = next
|
||||
}
|
||||
|
||||
// Remove argument markers
|
||||
insnList.remove(arg.argStartMarker)
|
||||
insnList.remove(arg.argEndMarker)
|
||||
|
||||
// If there are no more inplace arguments left to process, we are done
|
||||
++argsProcessed
|
||||
if (argsProcessed >= callContext.args.size)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
else ->
|
||||
insn = insn.next
|
||||
}
|
||||
}
|
||||
|
||||
// Remove call start and call end markers
|
||||
insnList.remove(callContext.callStartMarker)
|
||||
insnList.remove(callContext.callEndMarker)
|
||||
}
|
||||
|
||||
private fun updateLvtEntriesForMovedInstructions(methodContext: MethodContext) {
|
||||
val insnList = methodContext.methodNode.instructions
|
||||
for ((insn, callContext) in methodContext.varInstructionMoved.entries) {
|
||||
// Extend local variable interval to call end label if needed
|
||||
val lv = methodContext.lvtEntryForInstruction[insn] ?: continue
|
||||
val lvEndIndex = insnList.indexOf(lv.end)
|
||||
val endLabelIndex = insnList.indexOf(callContext.endLabel)
|
||||
if (endLabelIndex > lvEndIndex) {
|
||||
lv.end = callContext.endLabel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun stripMarkers(methodNode: MethodNode) {
|
||||
var insn = methodNode.instructions.first
|
||||
while (insn != null) {
|
||||
if (insn.isInplaceCallStartMarker() ||
|
||||
insn.isInplaceCallEndMarker() ||
|
||||
insn.isInplaceArgumentStartMarker() ||
|
||||
insn.isInplaceArgumentEndMarker()
|
||||
) {
|
||||
val next = insn.next
|
||||
methodNode.instructions.remove(insn)
|
||||
insn = next
|
||||
continue
|
||||
}
|
||||
insn = insn.next
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,7 @@ class DefaultLambda(info: ExtractedDefaultLambda, sourceCompiler: SourceCompiler
|
||||
// TODO: suspend lambdas are their own continuations, so the body is pre-inlined into `invokeSuspend`
|
||||
// and thus can't be detangled from the state machine. To make them inlinable, this needs to be redesigned.
|
||||
// See `SuspendLambdaLowering`.
|
||||
require(!sourceCompiler.state.languageVersionSettings.isCoroutineSuperClass(superName)) {
|
||||
require(!superName.isCoroutineSuperClass()) {
|
||||
"suspend default lambda ${lambdaClassType.internalName} cannot be inlined; use a function reference instead"
|
||||
}
|
||||
|
||||
|
||||
@@ -6,19 +6,15 @@
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.coroutines.continuationAsmType
|
||||
import org.jetbrains.kotlin.codegen.coroutines.CONTINUATION_ASM_TYPE
|
||||
import org.jetbrains.kotlin.codegen.inline.FieldRemapper.Companion.foldName
|
||||
import org.jetbrains.kotlin.codegen.inline.coroutines.CoroutineTransformer
|
||||
import org.jetbrains.kotlin.codegen.inline.coroutines.markNoinlineLambdaIfSuspend
|
||||
import org.jetbrains.kotlin.codegen.inline.coroutines.surroundInvokesWithSuspendMarkersIfNeeded
|
||||
import org.jetbrains.kotlin.codegen.optimization.ApiVersionCallsPreprocessingMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.FixStackWithLabelNormalizationMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.ControlFlowGraph
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.FastStackAnalyzer
|
||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.isCheckParameterIsNotNull
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
@@ -692,7 +688,7 @@ class MethodInliner(
|
||||
}
|
||||
|
||||
for ((index, param) in paramTypes.reversed().withIndex()) {
|
||||
if (param != languageVersionSettings.continuationAsmType() && param != OBJECT_TYPE) continue
|
||||
if (param != CONTINUATION_ASM_TYPE && param != OBJECT_TYPE) continue
|
||||
val sourceIndices = (frame.getStack(frame.stackSize - index - 1) as? Aload0BasicValue)?.indices ?: continue
|
||||
for (sourceIndex in sourceIndices) {
|
||||
val src = processingNode.instructions[sourceIndex]
|
||||
@@ -737,6 +733,7 @@ class MethodInliner(
|
||||
|
||||
private fun preprocessNodeBeforeInline(node: MethodNode, returnLabels: Map<String, Label?>) {
|
||||
try {
|
||||
InplaceArgumentsMethodTransformer().transform("fake", node)
|
||||
FixStackWithLabelNormalizationMethodTransformer().transform("fake", node)
|
||||
} catch (e: Throwable) {
|
||||
throw wrapException(e, node, "couldn't inline method call")
|
||||
@@ -747,6 +744,8 @@ class MethodInliner(
|
||||
ApiVersionCallsPreprocessingMethodTransformer(targetApiVersion).transform("fake", node)
|
||||
}
|
||||
|
||||
removeFakeVariablesInitializationIfPresent(node)
|
||||
|
||||
val frames = FastStackAnalyzer("<fake>", node, FixStackInterpreter()).analyze()
|
||||
|
||||
val localReturnsNormalizer = LocalReturnsNormalizer()
|
||||
@@ -769,6 +768,73 @@ class MethodInliner(
|
||||
localReturnsNormalizer.transform(node)
|
||||
}
|
||||
|
||||
private fun removeFakeVariablesInitializationIfPresent(node: MethodNode) {
|
||||
// Before 1.6, we generated fake variable initialization instructions
|
||||
// ICONST_0
|
||||
// ISTORE x
|
||||
// for all inline functions. Original intent was to mark inline function body for the debugger with corresponding LVT entry.
|
||||
// However, for @InlineOnly functions corresponding LVT entries were not copied (assuming that nobody is actually debugging
|
||||
// @InlineOnly functions).
|
||||
// Since 1.6, we no longer generate fake variables for @InlineOnly functions
|
||||
// Here we erase fake variable initialization for @InlineOnly functions inlined into existing bytecode (e.g., inline function
|
||||
// inside third-party library).
|
||||
// We consider a sequence of instructions 'ICONST_0; ISTORE x' a fake variable initialization if the corresponding variable 'x'
|
||||
// is not used in the bytecode (see below).
|
||||
|
||||
val insnArray = node.instructions.toArray()
|
||||
|
||||
// Very conservative variable usage check.
|
||||
// Here we look at integer variables only (this includes integral primitive types: byte, char, short, boolean).
|
||||
// Variable is considered "used" if:
|
||||
// - it's loaded with ILOAD instruction
|
||||
// - it's incremented with IINC instruction
|
||||
// - there's a local variable table entry for this variable
|
||||
val usedIntegerVar = BooleanArray(node.maxLocals)
|
||||
for (insn in insnArray) {
|
||||
if (insn.type == AbstractInsnNode.VAR_INSN && insn.opcode == Opcodes.ILOAD) {
|
||||
usedIntegerVar[(insn as VarInsnNode).`var`] = true
|
||||
} else if (insn.type == AbstractInsnNode.IINC_INSN) {
|
||||
usedIntegerVar[(insn as IincInsnNode).`var`] = true
|
||||
}
|
||||
}
|
||||
for (localVariable in node.localVariables) {
|
||||
val d0 = localVariable.desc[0]
|
||||
// byte || char || short || int || boolean
|
||||
if (d0 == 'B' || d0 == 'C' || d0 == 'S' || d0 == 'I' || d0 == 'Z') {
|
||||
usedIntegerVar[localVariable.index] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Looking for sequences of instructions:
|
||||
// p0: ICONST_0
|
||||
// p1: ISTORE x
|
||||
// p2: <label>
|
||||
// If variable 'x' is not "used" (see above), remove p0 and p1 instructions.
|
||||
var changes = false
|
||||
for (p0 in insnArray) {
|
||||
if (p0.opcode != Opcodes.ICONST_0) continue
|
||||
|
||||
val p1 = p0.next ?: break
|
||||
if (p1.opcode != Opcodes.ISTORE) continue
|
||||
|
||||
val p2 = p1.next ?: break
|
||||
if (p2.type != AbstractInsnNode.LABEL) continue
|
||||
|
||||
val varIndex = (p1 as VarInsnNode).`var`
|
||||
if (!usedIntegerVar[varIndex]) {
|
||||
changes = true
|
||||
node.instructions.remove(p0)
|
||||
node.instructions.remove(p1)
|
||||
}
|
||||
}
|
||||
|
||||
if (changes) {
|
||||
// If we removed some instructions, some TCBs could (in theory) become empty.
|
||||
// Remove empty TCBs if there are any.
|
||||
node.removeEmptyCatchBlocks()
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAnonymousClassThatMustBeRegenerated(type: Type?): Boolean {
|
||||
if (type == null || type.sort != Type.OBJECT) return false
|
||||
return inliningContext.isRegeneratedAnonymousObject(type.internalName)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
@@ -12,12 +13,12 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.*
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.TYPEOF_NON_REIFIED_TYPE_PARAMETER_WITH_RECURSIVE_BOUND
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.TYPEOF_SUSPEND_TYPE
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.model.TypeParameterMarker
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
@@ -72,6 +73,14 @@ class PsiInlineIntrinsicsSupport(
|
||||
|
||||
override fun toKotlinType(type: KotlinType): KotlinType = type
|
||||
|
||||
override fun checkAnnotatedType(type: KotlinType) {
|
||||
if (type.annotations.hasAnnotation(StandardNames.FqNames.extensionFunctionType)) {
|
||||
state.diagnostics.report(TYPEOF_EXTENSION_FUNCTION_TYPE.on(reportErrorsOn))
|
||||
} else if (type.annotations.any { it.fqName != JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION }) {
|
||||
state.diagnostics.report(TYPEOF_ANNOTATED_TYPE.on(reportErrorsOn))
|
||||
}
|
||||
}
|
||||
|
||||
override fun reportSuspendTypeUnsupported() {
|
||||
state.diagnostics.report(TYPEOF_SUSPEND_TYPE.on(reportErrorsOn))
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.KotlinLookupLocation
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
@@ -23,13 +22,11 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.isInlineClass
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.isCallableMemberCompiledToJvmDefault
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForReturnType
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
||||
import org.jetbrains.kotlin.types.expressions.LabelResolver
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
@@ -160,8 +157,7 @@ class PsiSourceCompilerForInline(
|
||||
}
|
||||
|
||||
FunctionCodegen.generateMethodBody(
|
||||
adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen, state.jvmDefaultMode,
|
||||
state.languageVersionSettings.isReleaseCoroutines()
|
||||
adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen, state.jvmDefaultMode
|
||||
)
|
||||
|
||||
if (isLambda) {
|
||||
@@ -237,7 +233,7 @@ class PsiSourceCompilerForInline(
|
||||
}
|
||||
|
||||
override fun compileInlineFunction(jvmSignature: JvmMethodSignature): SMAPAndMethodNode {
|
||||
generateInlineIntrinsic(state.languageVersionSettings, functionDescriptor, jvmSignature.asmMethod, codegen.typeSystem)?.let {
|
||||
generateInlineIntrinsic(functionDescriptor, jvmSignature.asmMethod, codegen.typeSystem)?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,6 @@ import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.intConstant
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -75,6 +73,7 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
|
||||
|
||||
fun toKotlinType(type: KT): KotlinType
|
||||
|
||||
fun checkAnnotatedType(type: KT)
|
||||
fun reportSuspendTypeUnsupported()
|
||||
fun reportNonReifiedTypeParameterWithRecursiveBoundUnsupported(typeParameterName: Name)
|
||||
}
|
||||
@@ -232,7 +231,7 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
|
||||
if (stubCheckcast !is TypeInsnNode) return false
|
||||
|
||||
val newMethodNode = MethodNode(Opcodes.API_VERSION)
|
||||
generateAsCast(InstructionAdapter(newMethodNode), kotlinType, asmType, safe, languageVersionSettings, unifiedNullChecks)
|
||||
generateAsCast(InstructionAdapter(newMethodNode), kotlinType, asmType, safe, unifiedNullChecks)
|
||||
|
||||
instructions.insert(insn, newMethodNode.instructions)
|
||||
// Keep stubCheckcast to avoid VerifyErrors on 1.8+ bytecode,
|
||||
@@ -256,7 +255,7 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
|
||||
if (stubInstanceOf !is TypeInsnNode) return false
|
||||
|
||||
val newMethodNode = MethodNode(Opcodes.API_VERSION)
|
||||
generateIsCheck(InstructionAdapter(newMethodNode), kotlinType, asmType, languageVersionSettings.isReleaseCoroutines())
|
||||
generateIsCheck(InstructionAdapter(newMethodNode), kotlinType, asmType)
|
||||
|
||||
instructions.insert(insn, newMethodNode.instructions)
|
||||
instructions.remove(stubInstanceOf)
|
||||
|
||||
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
@@ -49,18 +48,16 @@ class CoroutineTransformer(
|
||||
fun suspendLambdaWithGeneratedStateMachine(node: MethodNode): Boolean =
|
||||
!isContinuationNotLambda() && isSuspendLambda(node) && isStateMachine(node)
|
||||
|
||||
private fun isContinuationNotLambda(): Boolean = inliningContext.isContinuation &&
|
||||
if (state.languageVersionSettings.isReleaseCoroutines()) superClassName.endsWith("ContinuationImpl")
|
||||
else methods.any { it.name == "getLabel" }
|
||||
private fun isContinuationNotLambda(): Boolean = inliningContext.isContinuation && superClassName.endsWith("ContinuationImpl")
|
||||
|
||||
private fun isStateMachine(node: MethodNode): Boolean =
|
||||
node.instructions.asSequence().any { insn -> insn is LdcInsnNode && insn.cst == ILLEGAL_STATE_ERROR_MESSAGE }
|
||||
|
||||
private fun isSuspendLambda(node: MethodNode) = isResumeImpl(node)
|
||||
private fun isSuspendLambda(node: MethodNode) = isInvokeSuspend(node)
|
||||
|
||||
fun newMethod(node: MethodNode): DeferredMethodVisitor {
|
||||
return when {
|
||||
isResumeImpl(node) -> {
|
||||
isInvokeSuspend(node) -> {
|
||||
assert(!isStateMachine(node)) {
|
||||
"Inlining/transforming state-machine"
|
||||
}
|
||||
@@ -71,9 +68,8 @@ class CoroutineTransformer(
|
||||
}
|
||||
}
|
||||
|
||||
private fun isResumeImpl(node: MethodNode): Boolean =
|
||||
state.languageVersionSettings.isResumeImplMethodName(node.name.removeSuffix(FOR_INLINE_SUFFIX)) &&
|
||||
inliningContext.isContinuation
|
||||
private fun isInvokeSuspend(node: MethodNode): Boolean =
|
||||
node.name.removeSuffix(FOR_INLINE_SUFFIX) == INVOKE_SUSPEND_METHOD_NAME && inliningContext.isContinuation
|
||||
|
||||
private fun isSuspendFunctionWithFakeConstructorCall(node: MethodNode): Boolean = findFakeContinuationConstructorClassName(node) != null
|
||||
|
||||
@@ -88,16 +84,15 @@ class CoroutineTransformer(
|
||||
val sourceCompilerForInline = inliningContext.root.sourceCompilerForInline
|
||||
val stateMachineBuilder = CoroutineTransformerMethodVisitor(
|
||||
createNewMethodFrom(node, name), node.access, name, node.desc, null, null,
|
||||
containingClassInternalName = classBuilder.thisName,
|
||||
obtainClassBuilderForCoroutineState = { classBuilder },
|
||||
isForNamedFunction = false,
|
||||
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = false,
|
||||
reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) },
|
||||
// TODO: this linenumbers might not be correct and since they are used only for step-over, check them.
|
||||
lineNumber = inliningContext.callSiteInfo.lineNumber,
|
||||
sourceFile = inliningContext.callSiteInfo.file?.name ?: "",
|
||||
languageVersionSettings = state.languageVersionSettings,
|
||||
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
containingClassInternalName = classBuilder.thisName,
|
||||
isForNamedFunction = false,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = false,
|
||||
useOldSpilledVarTypeAnalysis = state.configuration.getBoolean(JVMConfigurationKeys.USE_OLD_SPILLED_VAR_TYPE_ANALYSIS)
|
||||
)
|
||||
|
||||
@@ -123,17 +118,16 @@ class CoroutineTransformer(
|
||||
val sourceCompilerForInline = inliningContext.root.sourceCompilerForInline
|
||||
val stateMachineBuilder = CoroutineTransformerMethodVisitor(
|
||||
createNewMethodFrom(node, name), node.access, name, node.desc, null, null,
|
||||
containingClassInternalName = classBuilder.thisName,
|
||||
obtainClassBuilderForCoroutineState = { (inliningContext as RegeneratedClassContext).continuationBuilders[continuationClassName]!! },
|
||||
isForNamedFunction = true,
|
||||
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = disableTailCallOptimization,
|
||||
reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) },
|
||||
lineNumber = inliningContext.callSiteInfo.lineNumber,
|
||||
sourceFile = inliningContext.callSiteInfo.file?.name ?: "",
|
||||
languageVersionSettings = state.languageVersionSettings,
|
||||
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
containingClassInternalName = classBuilder.thisName,
|
||||
isForNamedFunction = true,
|
||||
needDispatchReceiver = true,
|
||||
internalNameForDispatchReceiver = classBuilder.thisName,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = disableTailCallOptimization,
|
||||
putContinuationParameterToLvt = !state.isIrBackend,
|
||||
useOldSpilledVarTypeAnalysis = state.configuration.getBoolean(JVMConfigurationKeys.USE_OLD_SPILLED_VAR_TYPE_ANALYSIS)
|
||||
)
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.isParameterCheckedForNull
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
|
||||
|
||||
fun canInlineArgumentsInPlace(methodNode: MethodNode): Boolean {
|
||||
// Usual inline functions are inlined in the following way:
|
||||
// <evaluate argument #1>
|
||||
// <store argument to an argument variable V1>
|
||||
// ...
|
||||
// <evaluate argument #N>
|
||||
// <store argument to an argument variable VN>
|
||||
// <inline function method body with parameter variables Pi remapped to argument variables Vi>
|
||||
// If an argument #k is already stored in a local variable W, this variable W is reused.
|
||||
// When inlining arguments in-place, we instead replace corresponding variable load instructions in the inline function method body
|
||||
// with bytecode for evaluating a given argument.
|
||||
// We can do so if such transformation keeps the evaluation order intact, possibly disregarding class initialization.
|
||||
//
|
||||
// This is true for many simple @InlineOnly functions from Kotlin standard library.
|
||||
// For example, bytecode for 'inline fun println(message: Any?)' is:
|
||||
// GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
|
||||
// ALOAD 0
|
||||
// INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
|
||||
// Basic inlining for 'println("Hello, world!")' would produce (skipping labels and line numbers):
|
||||
// // evaluate arguments, storing them to local variables
|
||||
// LDC "Hello, world!"
|
||||
// ASTORE 0
|
||||
// GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
|
||||
// ALOAD 0
|
||||
// INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
|
||||
// With argument "Hello, world!" inlined in-place it would be:
|
||||
// GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
|
||||
// LDC "Hello, world!"
|
||||
// INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
|
||||
// Such inlining is possible because we consider it OK to reorder 'GETSTATIC java/lang/System.out : Ljava/io/PrintStream;' instruction
|
||||
// with any argument evaluation instructions ('LDC "Hello, world!"' in this case).
|
||||
|
||||
val tcbStartLabels = methodNode.tryCatchBlocks.mapTo(HashSet()) { it.start }
|
||||
|
||||
val methodParameterTypes = Type.getArgumentTypes(methodNode.desc)
|
||||
|
||||
val jvmArgumentTypes = ArrayList<Type>(methodParameterTypes.size + 1)
|
||||
if (methodNode.access and Opcodes.ACC_STATIC == 0) {
|
||||
// Here we don't care much about the exact 'this' type,
|
||||
// it's only important to remember that variable slot #0 holds an object reference.
|
||||
jvmArgumentTypes.add(AsmTypes.OBJECT_TYPE)
|
||||
}
|
||||
jvmArgumentTypes.addAll(methodParameterTypes)
|
||||
|
||||
val argumentVarEnd = jvmArgumentTypes.sumOf { it.size }
|
||||
var expectedArgumentVar = 0
|
||||
var lastArgIndex = 0
|
||||
|
||||
var insn = methodNode.instructions.first
|
||||
|
||||
// During arguments evaluation, make sure that all arguments are loaded in expected order
|
||||
// and there are no unexpected side effects in-between.
|
||||
while (insn != null && expectedArgumentVar < argumentVarEnd) {
|
||||
// Entering a try-catch block before all arguments are loaded breaks evaluation order.
|
||||
if (insn in tcbStartLabels)
|
||||
return false
|
||||
|
||||
// Some instructions break evaluation order.
|
||||
if (insn.isProhibitedDuringArgumentsEvaluation())
|
||||
return false
|
||||
|
||||
// Allow a limited list of 'GETSTATIC <owner> <name> <desc>' instructions.
|
||||
if (insn.opcode == Opcodes.GETSTATIC) {
|
||||
val fieldInsn = insn as FieldInsnNode
|
||||
val fieldSignature = FieldSignature(fieldInsn.owner, fieldInsn.name, fieldInsn.desc)
|
||||
if (fieldSignature !in whitelistedStaticFields)
|
||||
return false
|
||||
}
|
||||
|
||||
// Writing to or incrementing an argument variable forbids in-place argument inlining.
|
||||
if (insn.opcode in Opcodes.ISTORE..Opcodes.ASTORE && (insn as VarInsnNode).`var` < argumentVarEnd)
|
||||
return false
|
||||
if (insn.opcode == Opcodes.IINC && (insn as IincInsnNode).`var` < argumentVarEnd)
|
||||
return false
|
||||
|
||||
// Analyze variable loads.
|
||||
if (insn.opcode in Opcodes.ILOAD..Opcodes.ALOAD) {
|
||||
// Skip parameter null check: 'aload x; ldc "..."; invokestatic <check>'
|
||||
if (insn.opcode == Opcodes.ALOAD && insn.isParameterCheckedForNull()) {
|
||||
// Go directly to the instruction after 'invokestatic <check>'
|
||||
insn = insn.next.next.next
|
||||
continue
|
||||
}
|
||||
|
||||
val varInsn = insn as VarInsnNode
|
||||
val varIndex = (varInsn).`var`
|
||||
if (varIndex == expectedArgumentVar) {
|
||||
// Expected argument variable loaded.
|
||||
expectedArgumentVar += jvmArgumentTypes[lastArgIndex].size
|
||||
++lastArgIndex
|
||||
// Skip a sequence of load instructions referring to the same argument variable
|
||||
// (such sequence is present in functions like 'Array.copyOf' and can be replaced with DUP instructions).
|
||||
do {
|
||||
insn = insn.next
|
||||
} while (insn != null && insn.opcode == varInsn.opcode && (insn as VarInsnNode).`var` == varIndex)
|
||||
continue
|
||||
} else if (varIndex < argumentVarEnd) {
|
||||
// Loaded an argument variable, but not an expected one => broken evaluation order
|
||||
return false
|
||||
} else {
|
||||
// It's OK to load any non-argument variable during argument evaluation.
|
||||
insn = insn.next
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Anything else is fine.
|
||||
insn = insn.next
|
||||
}
|
||||
|
||||
// Method body is over, but not all arguments were loaded on stack.
|
||||
if (expectedArgumentVar < argumentVarEnd)
|
||||
return false
|
||||
|
||||
// After arguments evaluation make sure that argument variables are no longer accessed
|
||||
// (we are not going to store anything to those variables anyway).
|
||||
while (insn != null) {
|
||||
if (insn.opcode in Opcodes.ILOAD..Opcodes.ALOAD || insn.opcode in Opcodes.ISTORE..Opcodes.ASTORE) {
|
||||
if ((insn as VarInsnNode).`var` < argumentVarEnd)
|
||||
return false
|
||||
} else if (insn.opcode == Opcodes.IINC) {
|
||||
if ((insn as IincInsnNode).`var` < argumentVarEnd)
|
||||
return false
|
||||
}
|
||||
insn = insn.next
|
||||
}
|
||||
|
||||
// Didn't encounter anything suspicious.
|
||||
return true
|
||||
}
|
||||
|
||||
internal data class FieldSignature(
|
||||
val owner: String,
|
||||
val name: String,
|
||||
val desc: String
|
||||
)
|
||||
|
||||
private val whitelistedStaticFields: Set<FieldSignature> =
|
||||
hashSetOf(
|
||||
FieldSignature("java/lang/System", "out", "Ljava/io/PrintStream;"),
|
||||
FieldSignature("kotlin/Result", "Companion", "Lkotlin/Result\$Companion;"),
|
||||
FieldSignature("kotlin/_Assertions", "ENABLED", "Z")
|
||||
)
|
||||
|
||||
private fun AbstractInsnNode.isProhibitedDuringArgumentsEvaluation() =
|
||||
opcode in opcodeProhibitedDuringArgumentsEvaluation.indices &&
|
||||
opcodeProhibitedDuringArgumentsEvaluation[opcode]
|
||||
|
||||
private val opcodeProhibitedDuringArgumentsEvaluation = BooleanArray(256).also { a ->
|
||||
// Any kind of jump during arguments evaluation is a hazard.
|
||||
// This includes all conditional jump instructions, switch instructions, return and throw instructions.
|
||||
// Very conservative, but enough for practical cases.
|
||||
for (i in Opcodes.IFEQ..Opcodes.RETURN) a[i] = true
|
||||
a[Opcodes.IFNULL] = true
|
||||
a[Opcodes.IFNONNULL] = true
|
||||
a[Opcodes.ATHROW] = true
|
||||
|
||||
// Instruction with non-trivial side effects is a hazard.
|
||||
// NB GETSTATIC is taken care of separately.
|
||||
a[Opcodes.PUTSTATIC] = true
|
||||
a[Opcodes.PUTFIELD] = true
|
||||
a[Opcodes.INVOKEVIRTUAL] = true
|
||||
a[Opcodes.INVOKESPECIAL] = true
|
||||
a[Opcodes.INVOKESTATIC] = true
|
||||
a[Opcodes.INVOKEINTERFACE] = true
|
||||
a[Opcodes.INVOKEDYNAMIC] = true
|
||||
a[Opcodes.MONITORENTER] = true
|
||||
a[Opcodes.MONITOREXIT] = true
|
||||
|
||||
// Integer division instructions can throw exception
|
||||
a[Opcodes.IDIV] = true
|
||||
a[Opcodes.LDIV] = true
|
||||
a[Opcodes.IREM] = true
|
||||
a[Opcodes.LREM] = true
|
||||
|
||||
// CHECKCAST can throw exception
|
||||
a[Opcodes.CHECKCAST] = true
|
||||
|
||||
// Array creation can throw exception (in case of negative array size)
|
||||
a[Opcodes.NEWARRAY] = true
|
||||
a[Opcodes.ANEWARRAY] = true
|
||||
a[Opcodes.MULTIANEWARRAY] = true
|
||||
|
||||
// Array access instructions can throw exception
|
||||
for (i in Opcodes.IALOAD..Opcodes.SALOAD) a[i] = true
|
||||
for (i in Opcodes.IASTORE..Opcodes.SASTORE) a[i] = true
|
||||
}
|
||||
|
||||
|
||||
private const val MARKER_INPLACE_CALL_START = "<INPLACE-CALL-START>"
|
||||
private const val MARKER_INPLACE_ARGUMENT_START = "<INPLACE-ARGUMENT-START>"
|
||||
private const val MARKER_INPLACE_ARGUMENT_END = "<INPLACE-ARGUMENT-END>"
|
||||
private const val MARKER_INPLACE_CALL_END = "<INPLACE-CALL-END>"
|
||||
|
||||
|
||||
private fun InstructionAdapter.addMarker(name: String) {
|
||||
visitMethodInsn(Opcodes.INVOKESTATIC, INLINE_MARKER_CLASS_NAME, name, "()V", false)
|
||||
}
|
||||
|
||||
fun InstructionAdapter.addInplaceCallStartMarker() = addMarker(MARKER_INPLACE_CALL_START)
|
||||
fun InstructionAdapter.addInplaceCallEndMarker() = addMarker(MARKER_INPLACE_CALL_END)
|
||||
fun InstructionAdapter.addInplaceArgumentStartMarker() = addMarker(MARKER_INPLACE_ARGUMENT_START)
|
||||
fun InstructionAdapter.addInplaceArgumentEndMarker() = addMarker(MARKER_INPLACE_ARGUMENT_END)
|
||||
|
||||
internal fun AbstractInsnNode.isInplaceCallStartMarker() = isInlineMarker(this, MARKER_INPLACE_CALL_START)
|
||||
internal fun AbstractInsnNode.isInplaceCallEndMarker() = isInlineMarker(this, MARKER_INPLACE_CALL_END)
|
||||
internal fun AbstractInsnNode.isInplaceArgumentStartMarker() = isInlineMarker(this, MARKER_INPLACE_ARGUMENT_START)
|
||||
internal fun AbstractInsnNode.isInplaceArgumentEndMarker() = isInlineMarker(this, MARKER_INPLACE_ARGUMENT_END)
|
||||
@@ -60,7 +60,7 @@ const val INLINE_FUN_VAR_SUFFIX = "\$iv"
|
||||
internal const val FIRST_FUN_LABEL = "$$$$\$ROOT$$$$$"
|
||||
internal const val SPECIAL_TRANSFORMATION_NAME = "\$special"
|
||||
const val INLINE_TRANSFORMATION_SUFFIX = "\$inlined"
|
||||
internal const val INLINE_CALL_TRANSFORMATION_SUFFIX = "$" + INLINE_TRANSFORMATION_SUFFIX
|
||||
internal const val INLINE_CALL_TRANSFORMATION_SUFFIX = "$$INLINE_TRANSFORMATION_SUFFIX"
|
||||
internal const val INLINE_FUN_THIS_0_SUFFIX = "\$inline_fun"
|
||||
internal const val DEFAULT_LAMBDA_FAKE_CALL = "$$\$DEFAULT_LAMBDA_FAKE_CALL$$$"
|
||||
internal const val CAPTURED_FIELD_FOLD_PREFIX = "$$$"
|
||||
@@ -68,11 +68,10 @@ internal const val CAPTURED_FIELD_FOLD_PREFIX = "$$$"
|
||||
private const val NON_LOCAL_RETURN = "$$$$\$NON_LOCAL_RETURN$$$$$"
|
||||
const val CAPTURED_FIELD_PREFIX = "$"
|
||||
private const val NON_CAPTURED_FIELD_PREFIX = "$$"
|
||||
private const val INLINE_MARKER_CLASS_NAME = "kotlin/jvm/internal/InlineMarker"
|
||||
internal const val INLINE_MARKER_CLASS_NAME = "kotlin/jvm/internal/InlineMarker"
|
||||
private const val INLINE_MARKER_BEFORE_METHOD_NAME = "beforeInlineCall"
|
||||
private const val INLINE_MARKER_AFTER_METHOD_NAME = "afterInlineCall"
|
||||
private const val INLINE_MARKER_FINALLY_START = "finallyStart"
|
||||
|
||||
private const val INLINE_MARKER_FINALLY_END = "finallyEnd"
|
||||
private const val INLINE_MARKER_BEFORE_SUSPEND_ID = 0
|
||||
private const val INLINE_MARKER_AFTER_SUSPEND_ID = 1
|
||||
@@ -302,7 +301,7 @@ internal fun firstLabelInChain(node: LabelNode): LabelNode {
|
||||
internal fun areLabelsBeforeSameInsn(first: LabelNode, second: LabelNode): Boolean =
|
||||
firstLabelInChain(first) == firstLabelInChain(second)
|
||||
|
||||
internal val MethodNode?.nodeText: String
|
||||
val MethodNode?.nodeText: String
|
||||
get() {
|
||||
if (this == null) {
|
||||
return "Not generated"
|
||||
@@ -535,17 +534,15 @@ internal fun isInlineMarker(insn: AbstractInsnNode): Boolean {
|
||||
return isInlineMarker(insn, null)
|
||||
}
|
||||
|
||||
private fun isInlineMarker(insn: AbstractInsnNode, name: String?): Boolean {
|
||||
if (insn !is MethodInsnNode) {
|
||||
return false
|
||||
}
|
||||
internal fun isInlineMarker(insn: AbstractInsnNode, name: String?): Boolean {
|
||||
if (insn.opcode != Opcodes.INVOKESTATIC) return false
|
||||
|
||||
return insn.getOpcode() == Opcodes.INVOKESTATIC &&
|
||||
insn.owner == INLINE_MARKER_CLASS_NAME &&
|
||||
val methodInsn = insn as MethodInsnNode
|
||||
return methodInsn.owner == INLINE_MARKER_CLASS_NAME &&
|
||||
if (name != null)
|
||||
insn.name == name
|
||||
methodInsn.name == name
|
||||
else
|
||||
insn.name == INLINE_MARKER_BEFORE_METHOD_NAME || insn.name == INLINE_MARKER_AFTER_METHOD_NAME
|
||||
methodInsn.name == INLINE_MARKER_BEFORE_METHOD_NAME || methodInsn.name == INLINE_MARKER_AFTER_METHOD_NAME
|
||||
}
|
||||
|
||||
internal fun isBeforeInlineMarker(insn: AbstractInsnNode): Boolean {
|
||||
|
||||
@@ -5,13 +5,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.isBuiltInIntercepted
|
||||
import org.jetbrains.kotlin.backend.common.isBuiltInSuspendCoroutineUninterceptedOrReturn
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.coroutines.createMethodNodeForCoroutineContext
|
||||
import org.jetbrains.kotlin.codegen.coroutines.createMethodNodeForIntercepted
|
||||
import org.jetbrains.kotlin.codegen.coroutines.createMethodNodeForSuspendCoroutineUninterceptedOrReturn
|
||||
import org.jetbrains.kotlin.codegen.createMethodNodeForAlwaysEnabledAssert
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructors
|
||||
@@ -30,25 +28,22 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
fun generateInlineIntrinsicForIr(languageVersionSettings: LanguageVersionSettings, descriptor: FunctionDescriptor): SMAPAndMethodNode? =
|
||||
fun generateInlineIntrinsicForIr(descriptor: FunctionDescriptor): SMAPAndMethodNode? =
|
||||
when {
|
||||
// TODO: implement these as codegen intrinsics (see IrIntrinsicMethods)
|
||||
descriptor.isBuiltInIntercepted(languageVersionSettings) ->
|
||||
createMethodNodeForIntercepted(languageVersionSettings)
|
||||
descriptor.isBuiltInCoroutineContext(languageVersionSettings) ->
|
||||
createMethodNodeForCoroutineContext(descriptor, languageVersionSettings)
|
||||
descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(languageVersionSettings) ->
|
||||
createMethodNodeForSuspendCoroutineUninterceptedOrReturn(languageVersionSettings)
|
||||
descriptor.isBuiltInCoroutineContext() ->
|
||||
createMethodNodeForCoroutineContext(descriptor)
|
||||
descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn() ->
|
||||
createMethodNodeForSuspendCoroutineUninterceptedOrReturn()
|
||||
else -> null
|
||||
}?.let { SMAPAndMethodNode(it, SMAP(listOf())) }
|
||||
|
||||
internal fun generateInlineIntrinsic(
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
descriptor: FunctionDescriptor,
|
||||
asmMethod: Method,
|
||||
typeSystem: TypeSystemCommonBackendContext
|
||||
): SMAPAndMethodNode? {
|
||||
return generateInlineIntrinsicForIr(languageVersionSettings, descriptor) ?: when {
|
||||
return generateInlineIntrinsicForIr(descriptor) ?: when {
|
||||
isSpecialEnumMethod(descriptor) ->
|
||||
createSpecialEnumMethodBody(descriptor.name.asString(), descriptor.original.typeParameters.single(), typeSystem)
|
||||
TypeOfChecker.isTypeOf(descriptor) ->
|
||||
|
||||
@@ -95,6 +95,8 @@ fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateTypeOf(
|
||||
intrinsicsSupport.reportSuspendTypeUnsupported()
|
||||
}
|
||||
|
||||
intrinsicsSupport.checkAnnotatedType(type)
|
||||
|
||||
if (intrinsicsSupport.state.stableTypeOf) {
|
||||
if (intrinsicsSupport.isMutableCollectionType(type)) {
|
||||
v.invokestatic(REFLECTION, "mutableCollectionType", Type.getMethodDescriptor(K_TYPE, K_TYPE), false)
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.jetbrains.org.objectweb.asm.tree.*
|
||||
|
||||
object TypeIntrinsics {
|
||||
@JvmStatic
|
||||
fun instanceOf(v: InstructionAdapter, jetType: KotlinType, boxedAsmType: Type, isReleaseCoroutines: Boolean) {
|
||||
fun instanceOf(v: InstructionAdapter, jetType: KotlinType, boxedAsmType: Type) {
|
||||
val functionTypeArity = getFunctionTypeArity(jetType)
|
||||
if (functionTypeArity >= 0) {
|
||||
v.iconst(functionTypeArity)
|
||||
@@ -27,28 +27,26 @@ object TypeIntrinsics {
|
||||
return
|
||||
}
|
||||
|
||||
if (isReleaseCoroutines) {
|
||||
val suspendFunctionTypeArity = getSuspendFunctionTypeArity(jetType)
|
||||
if (suspendFunctionTypeArity >= 0) {
|
||||
val notSuspendLambda = Label()
|
||||
val end = Label()
|
||||
val suspendFunctionTypeArity = getSuspendFunctionTypeArity(jetType)
|
||||
if (suspendFunctionTypeArity >= 0) {
|
||||
val notSuspendLambda = Label()
|
||||
val end = Label()
|
||||
|
||||
with(v) {
|
||||
dup()
|
||||
instanceOf(AsmTypes.SUSPEND_FUNCTION_TYPE)
|
||||
ifeq(notSuspendLambda)
|
||||
iconst(suspendFunctionTypeArity + 1)
|
||||
typeIntrinsic(IS_FUNCTON_OF_ARITY_METHOD_NAME, IS_FUNCTON_OF_ARITY_DESCRIPTOR)
|
||||
goTo(end)
|
||||
with(v) {
|
||||
dup()
|
||||
instanceOf(AsmTypes.SUSPEND_FUNCTION_TYPE)
|
||||
ifeq(notSuspendLambda)
|
||||
iconst(suspendFunctionTypeArity + 1)
|
||||
typeIntrinsic(IS_FUNCTON_OF_ARITY_METHOD_NAME, IS_FUNCTON_OF_ARITY_DESCRIPTOR)
|
||||
goTo(end)
|
||||
|
||||
mark(notSuspendLambda)
|
||||
pop()
|
||||
iconst(0)
|
||||
mark(notSuspendLambda)
|
||||
pop()
|
||||
iconst(0)
|
||||
|
||||
mark(end)
|
||||
}
|
||||
return
|
||||
mark(end)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val isMutableCollectionMethodName = getIsMutableCollectionMethodName(jetType)
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
package org.jetbrains.kotlin.codegen.optimization
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicVerifier
|
||||
|
||||
class MethodVerifier(private val checkPoint: String) : MethodTransformer() {
|
||||
class MethodVerifier(private val checkPoint: String, private val generationState: GenerationState) : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
if (!generationState.shouldValidateBytecode) return
|
||||
try {
|
||||
analyze(internalClassName, methodNode, BasicVerifier())
|
||||
} catch (e: Throwable) {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.codegen.optimization
|
||||
|
||||
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor
|
||||
import org.jetbrains.kotlin.codegen.inline.InplaceArgumentsMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.PopBackwardPropagationTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.StackPeepholeOptimizationsTransformer
|
||||
@@ -40,8 +41,9 @@ class OptimizationMethodVisitor(
|
||||
UninitializedStoresMethodTransformer(generationState.constructorCallNormalizationMode)
|
||||
|
||||
val normalizationMethodTransformer = CompositeMethodTransformer(
|
||||
InplaceArgumentsMethodTransformer(),
|
||||
FixStackWithLabelNormalizationMethodTransformer(),
|
||||
MethodVerifier("AFTER mandatory stack transformations")
|
||||
MethodVerifier("AFTER mandatory stack transformations", generationState)
|
||||
)
|
||||
|
||||
val optimizationTransformer = CompositeMethodTransformer(
|
||||
@@ -55,7 +57,7 @@ class OptimizationMethodVisitor(
|
||||
DeadCodeEliminationMethodTransformer(),
|
||||
RedundantGotoMethodTransformer(),
|
||||
RedundantNopsCleanupMethodTransformer(),
|
||||
MethodVerifier("AFTER optimizations")
|
||||
MethodVerifier("AFTER optimizations", generationState)
|
||||
)
|
||||
|
||||
override fun performTransformations(methodNode: MethodNode) {
|
||||
|
||||
@@ -17,9 +17,8 @@
|
||||
package org.jetbrains.kotlin.codegen.optimization.boxing
|
||||
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.coroutines.RELEASE_COROUTINES_VERSION_SETTINGS
|
||||
import org.jetbrains.kotlin.codegen.coroutines.coroutinesJvmInternalPackageFqName
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||
@@ -216,7 +215,7 @@ fun AbstractInsnNode.isPrimitiveBoxing() =
|
||||
}
|
||||
|
||||
private val BOXING_CLASS_INTERNAL_NAME =
|
||||
RELEASE_COROUTINES_VERSION_SETTINGS.coroutinesJvmInternalPackageFqName().child(Name.identifier("Boxing")).topLevelClassInternalName()
|
||||
StandardNames.COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.child(Name.identifier("Boxing")).topLevelClassInternalName()
|
||||
|
||||
private fun isJvmPrimitiveName(name: String) = JvmPrimitiveType.values().any { it.javaKeywordName == name }
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.optimization.common
|
||||
|
||||
import org.jetbrains.kotlin.codegen.inline.insnOpcodeText
|
||||
import org.jetbrains.kotlin.codegen.inline.insnText
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
@@ -56,34 +55,9 @@ open class FastMethodAnalyzer<V : Value>(
|
||||
private val insnsArray = method.instructions.toArray()
|
||||
private val nInsns = method.instructions.size()
|
||||
|
||||
// Single Predecessor Block (SPB) is a continuous sequence of instructions { I1, ... In } such that
|
||||
// if I=insns[i] and J=insns[i+1] both belong to SPB,
|
||||
// then I is a single immediate predecessor of J in a complete method control flow graph
|
||||
// (including exception edges).
|
||||
//
|
||||
// Note that classic basic blocks are SPBs, but the opposite is not true:
|
||||
// SPBs have single entry point, but can have multiple exit points
|
||||
// (which lead to instructions not belonging to the given SPB).
|
||||
// Example:
|
||||
// aload 1
|
||||
// dup
|
||||
// ifnull LA
|
||||
// invokevirtual foo()
|
||||
// dup
|
||||
// ifnull LB
|
||||
// invokevirtual bar()
|
||||
// goto LC
|
||||
// is SPB (but not a basic block).
|
||||
//
|
||||
// For each J=insns[i+1] such that I=insns[i] belongs to the same SPB,
|
||||
// data flow transfer function
|
||||
// Execute( J, Merge( { Out(K) | K <- Pred(J) } ) )
|
||||
// is effectively
|
||||
// Execute( J, Out(I) ) )
|
||||
// so, we don't need to merge frames for such I->J edges.
|
||||
private val singlePredBlock = IntArray(nInsns)
|
||||
private val isMergeNode = BooleanArray(nInsns)
|
||||
|
||||
val frames: Array<Frame<V>?> = arrayOfNulls(nInsns)
|
||||
private val frames: Array<Frame<V>?> = arrayOfNulls(nInsns)
|
||||
|
||||
private val handlers: Array<MutableList<TryCatchBlockNode>?> = arrayOfNulls(nInsns)
|
||||
private val queued = BooleanArray(nInsns)
|
||||
@@ -97,14 +71,13 @@ open class FastMethodAnalyzer<V : Value>(
|
||||
if (nInsns == 0) return frames
|
||||
|
||||
checkAssertions()
|
||||
|
||||
computeExceptionHandlersForEachInsn(method)
|
||||
|
||||
initSinglePredBlocks()
|
||||
initMergeNodes()
|
||||
|
||||
val current = newFrame(method.maxLocals, method.maxStack)
|
||||
val handler = newFrame(method.maxLocals, method.maxStack)
|
||||
initControlFlowAnalysis(current, method, owner)
|
||||
initLocals(current)
|
||||
mergeControlFlowEdge(0, current)
|
||||
|
||||
while (top > 0) {
|
||||
val insn = queue[--top]
|
||||
@@ -117,16 +90,16 @@ open class FastMethodAnalyzer<V : Value>(
|
||||
val insnType = insnNode.type
|
||||
|
||||
if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) {
|
||||
visitNopInsn(f, insn)
|
||||
mergeControlFlowEdge(insn + 1, f)
|
||||
} else {
|
||||
current.init(f).execute(insnNode, interpreter)
|
||||
when {
|
||||
insnNode is JumpInsnNode ->
|
||||
visitJumpInsnNode(insnNode, current, insn, insnOpcode)
|
||||
insnNode is LookupSwitchInsnNode ->
|
||||
visitLookupSwitchInsnNode(insnNode, current, insn)
|
||||
insnNode is TableSwitchInsnNode ->
|
||||
visitTableSwitchInsnNode(insnNode, current, insn)
|
||||
insnType == AbstractInsnNode.JUMP_INSN ->
|
||||
visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode)
|
||||
insnType == AbstractInsnNode.LOOKUPSWITCH_INSN ->
|
||||
visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode, current)
|
||||
insnType == AbstractInsnNode.TABLESWITCH_INSN ->
|
||||
visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current)
|
||||
insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) ->
|
||||
visitOpInsn(current, insn)
|
||||
else -> {
|
||||
@@ -141,7 +114,7 @@ open class FastMethodAnalyzer<V : Value>(
|
||||
handler.init(f)
|
||||
handler.clearStack()
|
||||
handler.push(interpreter.newValue(exnType))
|
||||
mergeControlFlowEdge(insn, jump, handler)
|
||||
mergeControlFlowEdge(jump, handler)
|
||||
}
|
||||
|
||||
} catch (e: AnalyzerException) {
|
||||
@@ -155,87 +128,56 @@ open class FastMethodAnalyzer<V : Value>(
|
||||
return frames
|
||||
}
|
||||
|
||||
internal fun initLocals(current: Frame<V>) {
|
||||
current.setReturn(interpreter.newValue(Type.getReturnType(method.desc)))
|
||||
val args = Type.getArgumentTypes(method.desc)
|
||||
var local = 0
|
||||
if ((method.access and Opcodes.ACC_STATIC) == 0) {
|
||||
val ctype = Type.getObjectType(owner)
|
||||
current.setLocal(local++, interpreter.newValue(ctype))
|
||||
}
|
||||
for (arg in args) {
|
||||
current.setLocal(local++, interpreter.newValue(arg))
|
||||
if (arg.size == 2) {
|
||||
current.setLocal(local++, interpreter.newValue(null))
|
||||
}
|
||||
}
|
||||
while (local < method.maxLocals) {
|
||||
current.setLocal(local++, interpreter.newValue(null))
|
||||
}
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.indexOf() =
|
||||
method.instructions.indexOf(this)
|
||||
|
||||
private fun initSinglePredBlocks() {
|
||||
markSinglePredBlockEntries()
|
||||
markSinglePredBlockBodies()
|
||||
}
|
||||
|
||||
private fun markSinglePredBlockEntries() {
|
||||
// Method entry point is SPB entry point.
|
||||
var blockId = 0
|
||||
singlePredBlock[0] = ++blockId
|
||||
|
||||
// Every jump target is SPB entry point.
|
||||
private fun initMergeNodes() {
|
||||
for (insn in insnsArray) {
|
||||
when (insn) {
|
||||
is JumpInsnNode -> {
|
||||
val labelIndex = insn.label.indexOf()
|
||||
if (singlePredBlock[labelIndex] == 0) {
|
||||
singlePredBlock[labelIndex] = ++blockId
|
||||
when (insn.type) {
|
||||
AbstractInsnNode.JUMP_INSN -> {
|
||||
val jumpInsn = insn as JumpInsnNode
|
||||
isMergeNode[jumpInsn.label.indexOf()] = true
|
||||
}
|
||||
AbstractInsnNode.LOOKUPSWITCH_INSN -> {
|
||||
val switchInsn = insn as LookupSwitchInsnNode
|
||||
isMergeNode[switchInsn.dflt.indexOf()] = true
|
||||
for (label in switchInsn.labels) {
|
||||
isMergeNode[label.indexOf()] = true
|
||||
}
|
||||
}
|
||||
is LookupSwitchInsnNode -> {
|
||||
insn.dflt?.let { dfltLabel ->
|
||||
val dfltIndex = dfltLabel.indexOf()
|
||||
if (singlePredBlock[dfltIndex] == 0) {
|
||||
singlePredBlock[dfltIndex] = ++blockId
|
||||
}
|
||||
}
|
||||
for (label in insn.labels) {
|
||||
val labelIndex = label.indexOf()
|
||||
if (singlePredBlock[labelIndex] == 0) {
|
||||
singlePredBlock[labelIndex] = ++blockId
|
||||
}
|
||||
}
|
||||
}
|
||||
is TableSwitchInsnNode -> {
|
||||
insn.dflt?.let { dfltLabel ->
|
||||
val dfltIndex = dfltLabel.indexOf()
|
||||
if (singlePredBlock[dfltIndex] == 0) {
|
||||
singlePredBlock[dfltIndex] = ++blockId
|
||||
}
|
||||
}
|
||||
for (label in insn.labels) {
|
||||
val labelIndex = label.indexOf()
|
||||
if (singlePredBlock[labelIndex] == 0) {
|
||||
singlePredBlock[labelIndex] = ++blockId
|
||||
}
|
||||
AbstractInsnNode.TABLESWITCH_INSN -> {
|
||||
val switchInsn = insn as TableSwitchInsnNode
|
||||
isMergeNode[switchInsn.dflt.indexOf()] = true
|
||||
for (label in switchInsn.labels) {
|
||||
isMergeNode[label.indexOf()] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Every try-catch block handler entry point is SPB entry point
|
||||
for (tcb in method.tryCatchBlocks) {
|
||||
val handlerIndex = tcb.handler.indexOf()
|
||||
if (singlePredBlock[handlerIndex] == 0) {
|
||||
singlePredBlock[handlerIndex] = ++blockId
|
||||
}
|
||||
isMergeNode[tcb.handler.indexOf()] = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun markSinglePredBlockBodies() {
|
||||
var current = 0
|
||||
for ((i, insn) in insnsArray.withIndex()) {
|
||||
if (singlePredBlock[i] == 0) {
|
||||
singlePredBlock[i] = current
|
||||
} else {
|
||||
// Entered a new SPB.
|
||||
current = singlePredBlock[i]
|
||||
}
|
||||
|
||||
// GOTO, ATHROW, *RETURN instructions terminate current SPB.
|
||||
when (insn.opcode) {
|
||||
Opcodes.GOTO,
|
||||
Opcodes.ATHROW,
|
||||
in Opcodes.IRETURN..Opcodes.RETURN ->
|
||||
current = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getFrame(insn: AbstractInsnNode): Frame<V>? =
|
||||
frames[insn.indexOf()]
|
||||
@@ -246,62 +188,33 @@ open class FastMethodAnalyzer<V : Value>(
|
||||
}
|
||||
|
||||
private fun visitOpInsn(current: Frame<V>, insn: Int) {
|
||||
mergeControlFlowEdge(insn, insn + 1, current)
|
||||
mergeControlFlowEdge(insn + 1, current)
|
||||
}
|
||||
|
||||
private fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame<V>, insn: Int) {
|
||||
var jump = insnNode.dflt.indexOf()
|
||||
mergeControlFlowEdge(insn, jump, current)
|
||||
private fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame<V>) {
|
||||
mergeControlFlowEdge(insnNode.dflt.indexOf(), current)
|
||||
// In most cases order of visiting switch labels should not matter
|
||||
// The only one is a tableswitch being added in the beginning of coroutine method, these switch' labels may lead
|
||||
// in the middle of try/catch block, and FixStackAnalyzer is not ready for this (trying to restore stack before it was saved)
|
||||
// So we just fix the order of labels being traversed: the first one should be one at the method beginning
|
||||
// Using 'reversed' is because nodes are processed in LIFO order
|
||||
for (label in insnNode.labels.reversed()) {
|
||||
jump = label.indexOf()
|
||||
mergeControlFlowEdge(insn, jump, current)
|
||||
for (label in insnNode.labels.asReversed()) {
|
||||
mergeControlFlowEdge(label.indexOf(), current)
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame<V>, insn: Int) {
|
||||
var jump = insnNode.dflt.indexOf()
|
||||
mergeControlFlowEdge(insn, jump, current)
|
||||
private fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame<V>) {
|
||||
mergeControlFlowEdge(insnNode.dflt.indexOf(), current)
|
||||
for (label in insnNode.labels) {
|
||||
jump = label.indexOf()
|
||||
mergeControlFlowEdge(insn, jump, current)
|
||||
mergeControlFlowEdge(label.indexOf(), current)
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame<V>, insn: Int, insnOpcode: Int) {
|
||||
if (insnOpcode != Opcodes.GOTO) {
|
||||
mergeControlFlowEdge(insn, insn + 1, current)
|
||||
mergeControlFlowEdge(insn + 1, current)
|
||||
}
|
||||
val jump = insnNode.label.indexOf()
|
||||
mergeControlFlowEdge(insn, jump, current)
|
||||
}
|
||||
|
||||
private fun visitNopInsn(f: Frame<V>, insn: Int) {
|
||||
mergeControlFlowEdge(insn, insn + 1, f)
|
||||
}
|
||||
|
||||
private fun initControlFlowAnalysis(current: Frame<V>, m: MethodNode, owner: String) {
|
||||
current.setReturn(interpreter.newValue(Type.getReturnType(m.desc)))
|
||||
val args = Type.getArgumentTypes(m.desc)
|
||||
var local = 0
|
||||
if ((m.access and Opcodes.ACC_STATIC) == 0) {
|
||||
val ctype = Type.getObjectType(owner)
|
||||
current.setLocal(local++, interpreter.newValue(ctype))
|
||||
}
|
||||
for (arg in args) {
|
||||
current.setLocal(local++, interpreter.newValue(arg))
|
||||
if (arg.size == 2) {
|
||||
current.setLocal(local++, interpreter.newValue(null))
|
||||
}
|
||||
}
|
||||
while (local < m.maxLocals) {
|
||||
current.setLocal(local++, interpreter.newValue(null))
|
||||
}
|
||||
mergeControlFlowEdge(0, 0, current)
|
||||
mergeControlFlowEdge(insnNode.label.indexOf(), current)
|
||||
}
|
||||
|
||||
private fun computeExceptionHandlersForEachInsn(m: MethodNode) {
|
||||
@@ -320,15 +233,14 @@ open class FastMethodAnalyzer<V : Value>(
|
||||
}
|
||||
}
|
||||
|
||||
private fun mergeControlFlowEdge(src: Int, dest: Int, frame: Frame<V>) {
|
||||
private fun mergeControlFlowEdge(dest: Int, frame: Frame<V>) {
|
||||
val oldFrame = frames[dest]
|
||||
val changes = when {
|
||||
oldFrame == null -> {
|
||||
frames[dest] = newFrame(frame.locals, frame.maxStackSize).apply { init(frame) }
|
||||
true
|
||||
}
|
||||
dest == src + 1 && singlePredBlock[src] == singlePredBlock[dest] -> {
|
||||
// Forward jump within a single predecessor block, no need to merge.
|
||||
!isMergeNode[dest] -> {
|
||||
oldFrame.init(frame)
|
||||
true
|
||||
}
|
||||
@@ -340,35 +252,4 @@ open class FastMethodAnalyzer<V : Value>(
|
||||
queue[top++] = dest
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
private fun dumpBlocksInfo() {
|
||||
fun LabelNode?.labelText() =
|
||||
if (this != null) "L#${indexOf()}" else "L<null>"
|
||||
|
||||
println("===== ${method.name} ${method.signature} ======")
|
||||
for ((i, insn) in insnsArray.withIndex()) {
|
||||
val insnText = when (insn) {
|
||||
is LabelNode ->
|
||||
"L#$i"
|
||||
is JumpInsnNode ->
|
||||
"${insn.insnOpcodeText} ${insn.label.labelText()}"
|
||||
is TableSwitchInsnNode ->
|
||||
"${insn.insnOpcodeText} min=${insn.min} max=${insn.max} \n\t\t\t" +
|
||||
"[${insn.labels.joinToString { it.labelText() }}] \n\t\t\t" +
|
||||
"dflt:${insn.dflt.labelText()}"
|
||||
is LookupSwitchInsnNode ->
|
||||
"${insn.insnOpcodeText} \n\t\t\t" +
|
||||
"[${insn.keys.zip(insn.labels).joinToString { (key, label) -> "$key: ${label.labelText()}"}}] \n\t\t\t" +
|
||||
"dflt:${insn.dflt.labelText()}"
|
||||
else ->
|
||||
insn.insnText
|
||||
}
|
||||
println("$i\t${singlePredBlock[i]}\t$insnText")
|
||||
}
|
||||
for (tcb in method.tryCatchBlocks) {
|
||||
println("\tTCB start:${tcb.start.labelText()} end:${tcb.end.labelText()} handler:${tcb.handler.labelText()}")
|
||||
}
|
||||
println()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.codegen.optimization.removeNodeGetNext
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
@@ -36,7 +35,7 @@ val AbstractInsnNode.isMeaningful: Boolean
|
||||
|
||||
val AbstractInsnNode.isBranchOrCall: Boolean
|
||||
get() =
|
||||
when(this.type) {
|
||||
when (this.type) {
|
||||
AbstractInsnNode.JUMP_INSN,
|
||||
AbstractInsnNode.TABLESWITCH_INSN,
|
||||
AbstractInsnNode.LOOKUPSWITCH_INSN,
|
||||
@@ -85,13 +84,17 @@ fun MethodNode.prepareForEmitting() {
|
||||
|
||||
current = prev
|
||||
}
|
||||
updateMaxStack()
|
||||
}
|
||||
|
||||
fun MethodNode.updateMaxStack() {
|
||||
maxStack = -1
|
||||
accept(
|
||||
MaxStackFrameSizeAndLocalsCalculator(
|
||||
Opcodes.API_VERSION, access, desc,
|
||||
object : MethodVisitor(Opcodes.API_VERSION) {
|
||||
API_VERSION, access, desc,
|
||||
object : MethodVisitor(API_VERSION) {
|
||||
override fun visitMaxs(maxStack: Int, maxLocals: Int) {
|
||||
this@prepareForEmitting.maxStack = maxStack
|
||||
this@updateMaxStack.maxStack = maxStack
|
||||
}
|
||||
})
|
||||
)
|
||||
@@ -100,10 +103,10 @@ fun MethodNode.prepareForEmitting() {
|
||||
fun MethodNode.stripOptimizationMarkers() {
|
||||
var insn = instructions.first
|
||||
while (insn != null) {
|
||||
if (isOptimizationMarker(insn)) {
|
||||
insn = instructions.removeNodeGetNext(insn)
|
||||
insn = if (isOptimizationMarker(insn)) {
|
||||
instructions.removeNodeGetNext(insn)
|
||||
} else {
|
||||
insn = insn.next
|
||||
insn.next
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,7 +126,7 @@ fun MethodNode.removeUnusedLocalVariables() {
|
||||
// Arguments are always used whether or not they are in the local variable table
|
||||
// or used by instructions.
|
||||
var argumentIndex = 0
|
||||
val isStatic = (access and Opcodes.ACC_STATIC) != 0
|
||||
val isStatic = (access and ACC_STATIC) != 0
|
||||
if (!isStatic) {
|
||||
used[argumentIndex++] = true
|
||||
}
|
||||
@@ -230,8 +233,8 @@ val AbstractInsnNode.intConstant: Int?
|
||||
|
||||
fun insnListOf(vararg insns: AbstractInsnNode) = InsnList().apply { insns.forEach { add(it) } }
|
||||
|
||||
fun AbstractInsnNode.isStoreOperation(): Boolean = opcode in Opcodes.ISTORE..Opcodes.ASTORE
|
||||
fun AbstractInsnNode.isLoadOperation(): Boolean = opcode in Opcodes.ILOAD..Opcodes.ALOAD
|
||||
fun AbstractInsnNode.isStoreOperation(): Boolean = opcode in ISTORE..ASTORE
|
||||
fun AbstractInsnNode.isLoadOperation(): Boolean = opcode in ILOAD..ALOAD
|
||||
|
||||
val AbstractInsnNode?.debugText
|
||||
get() =
|
||||
|
||||
@@ -101,12 +101,12 @@ internal open class FastStackAnalyzer<V : Value>(
|
||||
}
|
||||
|
||||
when {
|
||||
insnNode is JumpInsnNode ->
|
||||
visitJumpInsnNode(insnNode, current, insn, insnOpcode)
|
||||
insnNode is LookupSwitchInsnNode ->
|
||||
visitLookupSwitchInsnNode(insnNode, current, insn)
|
||||
insnNode is TableSwitchInsnNode ->
|
||||
visitTableSwitchInsnNode(insnNode, current, insn)
|
||||
insnType == AbstractInsnNode.JUMP_INSN ->
|
||||
visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode)
|
||||
insnType == AbstractInsnNode.LOOKUPSWITCH_INSN ->
|
||||
visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode, current, insn)
|
||||
insnType == AbstractInsnNode.TABLESWITCH_INSN ->
|
||||
visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current, insn)
|
||||
insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) ->
|
||||
visitOpInsn(current, insn)
|
||||
else -> {
|
||||
@@ -174,7 +174,7 @@ internal open class FastStackAnalyzer<V : Value>(
|
||||
}
|
||||
|
||||
private fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame<V>, insn: Int, insnOpcode: Int) {
|
||||
if (insnOpcode != Opcodes.GOTO && insnOpcode != Opcodes.JSR) {
|
||||
if (insnOpcode != Opcodes.GOTO) {
|
||||
processControlFlowEdge(current, insn, insn + 1)
|
||||
}
|
||||
val jump = insnNode.label.indexOf()
|
||||
|
||||
@@ -441,7 +441,7 @@ fun MethodNode.usesLocalExceptParameterNullCheck(index: Int): Boolean =
|
||||
it is VarInsnNode && it.opcode == Opcodes.ALOAD && it.`var` == index && !it.isParameterCheckedForNull()
|
||||
}
|
||||
|
||||
internal fun AbstractInsnNode.isParameterCheckedForNull(): Boolean =
|
||||
fun AbstractInsnNode.isParameterCheckedForNull(): Boolean =
|
||||
next?.takeIf { it.opcode == Opcodes.LDC }?.next?.isCheckParameterIsNotNull() == true
|
||||
|
||||
internal fun AbstractInsnNode.isCheckParameterIsNotNull() =
|
||||
|
||||
@@ -37,7 +37,6 @@ import org.jetbrains.kotlin.psi.KtCodeFragment
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtScript
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.deprecation.CoroutineCompatibilitySupport
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.PrecomputedSuppressCache
|
||||
@@ -158,8 +157,7 @@ class GenerationState private constructor(
|
||||
CompilerDeserializationConfiguration(languageVersionSettings)
|
||||
|
||||
val deprecationProvider = DeprecationResolver(
|
||||
LockBasedStorageManager.NO_LOCKS, languageVersionSettings, CoroutineCompatibilitySupport.ENABLED,
|
||||
JavaDeprecationSettings
|
||||
LockBasedStorageManager.NO_LOCKS, languageVersionSettings, JavaDeprecationSettings
|
||||
)
|
||||
|
||||
init {
|
||||
@@ -299,6 +297,8 @@ class GenerationState private constructor(
|
||||
!configuration.getBoolean(JVMConfigurationKeys.NO_UNIFIED_NULL_CHECKS)
|
||||
val functionsWithInlineClassReturnTypesMangled: Boolean =
|
||||
languageVersionSettings.supportsFeature(LanguageFeature.MangleClassMembersReturningInlineClasses)
|
||||
val shouldValidateIr = configuration.getBoolean(JVMConfigurationKeys.VALIDATE_IR)
|
||||
val shouldValidateBytecode = configuration.getBoolean(JVMConfigurationKeys.VALIDATE_BYTECODE)
|
||||
|
||||
val rootContext: CodegenContext<*> = RootContext(this)
|
||||
|
||||
@@ -405,8 +405,8 @@ class GenerationState private constructor(
|
||||
this[KOTLIN_1_2] = oldMetadataVersion
|
||||
this[KOTLIN_1_3] = oldMetadataVersion
|
||||
this[KOTLIN_1_4] = JvmMetadataVersion(1, 4, 3)
|
||||
this[KOTLIN_1_5] = JvmMetadataVersion.INSTANCE
|
||||
this[KOTLIN_1_6] = JvmMetadataVersion(1, 6, 0)
|
||||
this[KOTLIN_1_5] = JvmMetadataVersion(1, 5, 1)
|
||||
this[KOTLIN_1_6] = JvmMetadataVersion.INSTANCE
|
||||
this[KOTLIN_1_7] = JvmMetadataVersion(1, 7, 0)
|
||||
|
||||
check(size == LanguageVersion.values().size) {
|
||||
|
||||
@@ -193,7 +193,7 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
if (descriptor.isSuspendFunctionNotSuspensionView()) {
|
||||
return mapReturnType(getOrCreateJvmSuspendFunctionView(descriptor as SimpleFunctionDescriptor, isReleaseCoroutines), sw)
|
||||
return mapReturnType(getOrCreateJvmSuspendFunctionView(descriptor as SimpleFunctionDescriptor), sw)
|
||||
}
|
||||
|
||||
if (hasVoidReturnType(descriptor)) {
|
||||
@@ -804,7 +804,7 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
if (f.isSuspendFunctionNotSuspensionView()) {
|
||||
return mapSignature(getOrCreateJvmSuspendFunctionView(f, isReleaseCoroutines), kind, skipGenericSignature)
|
||||
return mapSignature(getOrCreateJvmSuspendFunctionView(f), kind, skipGenericSignature)
|
||||
}
|
||||
|
||||
if (isDeclarationOfBigArityFunctionInvoke(f) || isDeclarationOfBigArityCreateCoroutineMethod(f)) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
|
||||
import org.jetbrains.kotlin.ideaExt.idea
|
||||
import java.io.File
|
||||
|
||||
@@ -10,27 +9,6 @@ plugins {
|
||||
val compilerModules: Array<String> by rootProject.extra
|
||||
val otherCompilerModules = compilerModules.filter { it != path }
|
||||
|
||||
val tasksWithWarnings: List<String> by rootProject.extra
|
||||
|
||||
val effectSystemEnabled: Boolean by rootProject.extra
|
||||
val newInferenceEnabled: Boolean by rootProject.extra
|
||||
|
||||
configureFreeCompilerArg(effectSystemEnabled, "-Xeffect-system")
|
||||
configureFreeCompilerArg(newInferenceEnabled, "-Xnew-inference")
|
||||
configureFreeCompilerArg(true, "-Xuse-mixed-named-arguments")
|
||||
|
||||
fun configureFreeCompilerArg(isEnabled: Boolean, compilerArgument: String) {
|
||||
if (isEnabled) {
|
||||
allprojects {
|
||||
tasks.withType<KotlinCompile<*>> {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs += listOf(compilerArgument)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val antLauncherJar by configurations.creating
|
||||
|
||||
dependencies {
|
||||
@@ -85,16 +63,6 @@ if (kotlinBuildProperties.isInJpsBuildIdeaSync) {
|
||||
idea {
|
||||
this.module.generatedSourceDirs.add(generationRoot)
|
||||
}
|
||||
} else if (!kotlinBuildProperties.useFir && !kotlinBuildProperties.disableWerror) {
|
||||
allprojects {
|
||||
tasks.withType<KotlinCompile<*>> {
|
||||
if (path !in tasksWithWarnings) {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
projectTest(parallel = true) {
|
||||
|
||||
@@ -57,10 +57,10 @@ sourceSets {
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
|
||||
kotlinOptions {
|
||||
languageVersion = "1.3"
|
||||
apiVersion = "1.3"
|
||||
languageVersion = "1.4"
|
||||
apiVersion = "1.4"
|
||||
freeCompilerArgs = freeCompilerArgs - "-progressive" + listOf(
|
||||
"-Xskip-prerelease-check", "-Xsuppress-version-warnings"
|
||||
"-Xskip-prerelease-check", "-Xsuppress-version-warnings", "-Xuse-mixed-named-arguments", "-Xnew-inference"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<idea-plugin>
|
||||
<id>org.jetbrains.kotlin</id>
|
||||
<version>1.2</version>
|
||||
|
||||
<!-- Don't add more extension points here! Logic in KotlinCoreEnvironment assumes that there is only one EP. -->
|
||||
<!-- And this file should be removed once 202 is no longer supported -->
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="com.intellij.psi.classFileDecompiler"
|
||||
interface="com.intellij.psi.compiled.ClassFileDecompilers$Decompiler"
|
||||
dynamic="true"/>
|
||||
</extensionPoints>
|
||||
</idea-plugin>
|
||||
@@ -1,10 +0,0 @@
|
||||
<idea-plugin>
|
||||
<id>org.jetbrains.kotlin</id>
|
||||
<version>1.2</version>
|
||||
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="com.intellij.psi.classFileDecompiler"
|
||||
interface="com.intellij.psi.compiled.ClassFileDecompilers$Decompiler"
|
||||
dynamic="true"/>
|
||||
</extensionPoints>
|
||||
</idea-plugin>
|
||||
@@ -369,7 +369,6 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
|
||||
)
|
||||
var extendedCompilerChecks: Boolean by FreezableVar(false)
|
||||
|
||||
@GradleOption(DefaultValues.BooleanFalseDefault::class)
|
||||
@Argument(
|
||||
value = "-Xbuiltins-from-sources",
|
||||
description = "Compile builtIns from sources"
|
||||
@@ -382,6 +381,12 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
|
||||
)
|
||||
var unrestrictedBuilderInference: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xself-upper-bound-inference",
|
||||
description = "Support inferring type arguments based on only self upper bounds of the corresponding type parameters"
|
||||
)
|
||||
var selfUpperBoundInference: Boolean by FreezableVar(false)
|
||||
|
||||
open fun configureAnalysisFlags(collector: MessageCollector, languageVersion: LanguageVersion): MutableMap<AnalysisFlag<*>, Any> {
|
||||
return HashMap<AnalysisFlag<*>, Any>().apply {
|
||||
put(AnalysisFlags.skipMetadataVersionCheck, skipMetadataVersionCheck)
|
||||
@@ -424,6 +429,10 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
|
||||
put(LanguageFeature.UnrestrictedBuilderInference, LanguageFeature.State.ENABLED)
|
||||
}
|
||||
|
||||
if (selfUpperBoundInference) {
|
||||
put(LanguageFeature.TypeInferenceOnCallsWithSelfTypes, LanguageFeature.State.ENABLED)
|
||||
}
|
||||
|
||||
if (newInference) {
|
||||
put(LanguageFeature.NewInference, LanguageFeature.State.ENABLED)
|
||||
put(LanguageFeature.SamConversionPerArgument, LanguageFeature.State.ENABLED)
|
||||
|
||||
@@ -214,6 +214,12 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
||||
)
|
||||
var useOldClassFilesReading: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xuse-fast-jar-file-system",
|
||||
description = "Use fast implementation on Jar FS. This may speed up compilation time, but currently it's an experimental mode"
|
||||
)
|
||||
var useFastJarFileSystem: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xdump-declarations-to",
|
||||
valueDescription = "<path>",
|
||||
@@ -505,6 +511,18 @@ default: `indy-with-constants` for JVM target 9 or greater, `inline` otherwise""
|
||||
)
|
||||
var serializeIr: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xvalidate-ir",
|
||||
description = "Validate IR before and after lowering"
|
||||
)
|
||||
var validateIr: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xvalidate-bytecode",
|
||||
description = "Validate generated JVM bytecode before and after optimizations"
|
||||
)
|
||||
var validateBytecode: Boolean by FreezableVar(false)
|
||||
|
||||
override fun configureAnalysisFlags(collector: MessageCollector, languageVersion: LanguageVersion): MutableMap<AnalysisFlag<*>, Any> {
|
||||
val result = super.configureAnalysisFlags(collector, languageVersion)
|
||||
result[JvmAnalysisFlags.strictMetadataVersionSemantics] = strictMetadataVersionSemantics
|
||||
|
||||
@@ -38,8 +38,11 @@ import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider
|
||||
import org.jetbrains.kotlin.incremental.js.IncrementalNextRoundChecker
|
||||
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer
|
||||
import org.jetbrains.kotlin.ir.backend.js.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.ic.buildCache
|
||||
import org.jetbrains.kotlin.ir.backend.js.ic.checkCaches
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrFactory
|
||||
import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult
|
||||
import org.jetbrains.kotlin.js.config.*
|
||||
import org.jetbrains.kotlin.library.KLIB_FILE_EXTENSION
|
||||
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
|
||||
@@ -91,7 +94,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
configuration.put(CommonConfigurationKeys.MODULE_NAME, "repl.kts")
|
||||
|
||||
val environment = KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForProduction(rootDisposable, configuration)
|
||||
val projectEnv = KotlinCoreEnvironment.ProjectEnvironment(rootDisposable, environment)
|
||||
val projectEnv = KotlinCoreEnvironment.ProjectEnvironment(rootDisposable, environment, configuration)
|
||||
projectEnv.registerExtensionsFromPlugins(configuration)
|
||||
|
||||
val scriptingEvaluators = ScriptEvaluationExtension.getInstances(projectEnv.project)
|
||||
@@ -179,18 +182,74 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
// TODO: Handle non-empty main call arguments
|
||||
val mainCallArguments = if (K2JsArgumentConstants.NO_CALL == arguments.main) null else emptyList<String>()
|
||||
|
||||
val icCaches = configureLibraries(arguments.cacheDirectories)
|
||||
|
||||
if (arguments.irBuildCache) {
|
||||
messageCollector.report(INFO, "")
|
||||
messageCollector.report(INFO, "Building cache:")
|
||||
messageCollector.report(INFO, "to: ${outputFilePath}")
|
||||
messageCollector.report(INFO, arguments.cacheDirectories ?: "")
|
||||
messageCollector.report(INFO, libraries.toString())
|
||||
|
||||
val includes = arguments.includes!!
|
||||
|
||||
// TODO: deduplicate
|
||||
val mainModule = run {
|
||||
if (sourcesFiles.isNotEmpty()) {
|
||||
messageCollector.report(ERROR, "Source files are not supported when -Xinclude is present")
|
||||
}
|
||||
MainModule.Klib(includes)
|
||||
}
|
||||
|
||||
val start = System.currentTimeMillis()
|
||||
if (buildCache(
|
||||
cachePath = outputFilePath,
|
||||
project = projectJs,
|
||||
mainModule = mainModule,
|
||||
configuration = config.configuration,
|
||||
dependencies = libraries,
|
||||
friendDependencies = friendLibraries,
|
||||
icCache = checkCaches(libraries, icCaches, skipLib = mainModule.libPath)
|
||||
)
|
||||
) {
|
||||
messageCollector.report(INFO, "IC cache building duration: ${System.currentTimeMillis() - start}ms")
|
||||
} else {
|
||||
messageCollector.report(INFO, "IC cache up-to-date check duration: ${System.currentTimeMillis() - start}ms")
|
||||
}
|
||||
return OK
|
||||
}
|
||||
|
||||
// Run analysis if main module is sources
|
||||
lateinit var sourceModule: ModulesStructure
|
||||
if (arguments.includes == null) {
|
||||
do {
|
||||
sourceModule = prepareAnalyzedSourceModule(
|
||||
projectJs,
|
||||
environmentForJS.getSourceFiles(),
|
||||
configurationJs,
|
||||
libraries,
|
||||
friendLibraries,
|
||||
AnalyzerWithCompilerReport(config.configuration),
|
||||
icUseGlobalSignatures = icCaches.isNotEmpty(),
|
||||
icUseStdlibCache = icCaches.isNotEmpty(),
|
||||
icCache = if (icCaches.isNotEmpty()) checkCaches(libraries, icCaches, skipLib = arguments.includes).data else emptyMap()
|
||||
)
|
||||
val result = sourceModule.jsFrontEndResult.jsAnalysisResult
|
||||
if (result is JsAnalysisResult.RetryWithAdditionalRoots) {
|
||||
environmentForJS.addKotlinSourceRoots(result.additionalKotlinRoots)
|
||||
}
|
||||
} while (result is JsAnalysisResult.RetryWithAdditionalRoots)
|
||||
if (!sourceModule.jsFrontEndResult.jsAnalysisResult.shouldGenerateCode)
|
||||
return OK
|
||||
}
|
||||
|
||||
if (arguments.irProduceKlibDir || arguments.irProduceKlibFile) {
|
||||
if (arguments.irProduceKlibFile) {
|
||||
require(outputFile.extension == KLIB_FILE_EXTENSION) { "Please set up .klib file as output" }
|
||||
}
|
||||
|
||||
generateKLib(
|
||||
project = config.project,
|
||||
files = sourcesFiles,
|
||||
analyzer = AnalyzerWithCompilerReport(config.configuration),
|
||||
configuration = config.configuration,
|
||||
dependencies = libraries,
|
||||
friendDependencies = friendLibraries,
|
||||
sourceModule,
|
||||
irFactory = PersistentIrFactory(), // TODO IrFactoryImpl?
|
||||
outputKlibPath = outputFile.path,
|
||||
nopack = arguments.irProduceKlibDir,
|
||||
@@ -199,32 +258,40 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
}
|
||||
|
||||
if (arguments.irProduceJs) {
|
||||
messageCollector.report(INFO,"Produce executable: $outputFilePath")
|
||||
messageCollector.report(INFO, arguments.cacheDirectories ?: "")
|
||||
|
||||
val phaseConfig = createPhaseConfig(jsPhases, arguments, messageCollector)
|
||||
|
||||
val includes = arguments.includes
|
||||
|
||||
val mainModule = if (includes != null) {
|
||||
val module = if (includes != null) {
|
||||
if (sourcesFiles.isNotEmpty()) {
|
||||
messageCollector.report(ERROR, "Source files are not supported when -Xinclude is present")
|
||||
}
|
||||
val includesPath = File(includes).canonicalPath
|
||||
val mainLibPath = libraries.find { File(it).canonicalPath == includesPath }
|
||||
?: error("No library with name $includes ($includesPath) found")
|
||||
MainModule.Klib(mainLibPath)
|
||||
val kLib = MainModule.Klib(mainLibPath)
|
||||
ModulesStructure(
|
||||
projectJs,
|
||||
kLib,
|
||||
configurationJs,
|
||||
libraries,
|
||||
friendLibraries,
|
||||
icUseGlobalSignatures = icCaches.isNotEmpty(),
|
||||
icUseStdlibCache = icCaches.isNotEmpty(),
|
||||
icCache = if (icCaches.isNotEmpty()) checkCaches(libraries, icCaches, skipLib = includes).data else emptyMap()
|
||||
)
|
||||
} else {
|
||||
MainModule.SourceFiles(sourcesFiles)
|
||||
sourceModule
|
||||
}
|
||||
|
||||
if (arguments.wasm) {
|
||||
val res = compileWasm(
|
||||
projectJs,
|
||||
mainModule,
|
||||
AnalyzerWithCompilerReport(config.configuration),
|
||||
config.configuration,
|
||||
module,
|
||||
PhaseConfig(wasmPhases),
|
||||
IrFactoryImpl,
|
||||
dependencies = libraries,
|
||||
friendDependencies = friendLibraries,
|
||||
exportedDeclarations = setOf(FqName("main"))
|
||||
)
|
||||
val outputWasmFile = outputFile.withReplacedExtensionOrNull(outputFile.extension, "wasm")!!
|
||||
@@ -235,7 +302,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
val runner = """
|
||||
const wasmBinary = read(String.raw`${outputWasmFile.absoluteFile}`, 'binary');
|
||||
const wasmModule = new WebAssembly.Module(wasmBinary);
|
||||
const wasmInstance = new WebAssembly.Instance(wasmModule, { runtime, js_code });
|
||||
wasmInstance = new WebAssembly.Instance(wasmModule, { runtime, js_code });
|
||||
wasmInstance.exports.main();
|
||||
""".trimIndent()
|
||||
|
||||
@@ -243,15 +310,12 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
return OK
|
||||
}
|
||||
|
||||
val start = System.currentTimeMillis()
|
||||
|
||||
val compiledModule = compile(
|
||||
projectJs,
|
||||
mainModule,
|
||||
AnalyzerWithCompilerReport(config.configuration),
|
||||
config.configuration,
|
||||
module,
|
||||
phaseConfig,
|
||||
if (arguments.irDceDriven) PersistentIrFactory() else IrFactoryImpl,
|
||||
dependencies = libraries,
|
||||
friendDependencies = friendLibraries,
|
||||
mainArguments = mainCallArguments,
|
||||
generateFullJs = !arguments.irDce,
|
||||
generateDceJs = arguments.irDce,
|
||||
@@ -270,8 +334,11 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
arguments.irSafeExternalBooleanDiagnostic,
|
||||
messageCollector
|
||||
),
|
||||
lowerPerModule = icCaches.isNotEmpty(),
|
||||
)
|
||||
|
||||
messageCollector.report(INFO, "Executable production duration: ${System.currentTimeMillis() - start}ms")
|
||||
|
||||
val outputs = if (arguments.irDce && !arguments.irDceDriven) compiledModule.outputsAfterDce!! else compiledModule.outputs!!
|
||||
outputFile.write(outputs)
|
||||
outputs.dependencies.forEach { (name, content) ->
|
||||
|
||||
13
compiler/cli/src/com/intellij/util/io/FileChannelUtil.java
Normal file
13
compiler/cli/src/com/intellij/util/io/FileChannelUtil.java
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.util.io;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
public final class FileChannelUtil {
|
||||
@NotNull
|
||||
static FileChannel unInterruptible(@NotNull FileChannel channel) {
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.common.extensions
|
||||
|
||||
import com.intellij.core.JavaCoreProjectEnvironment
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
|
||||
|
||||
@@ -22,6 +22,6 @@ interface ScriptEvaluationExtension {
|
||||
fun eval(
|
||||
arguments: CommonCompilerArguments,
|
||||
configuration: CompilerConfiguration,
|
||||
projectEnvironment: JavaCoreProjectEnvironment
|
||||
projectEnvironment: KotlinCoreEnvironment.ProjectEnvironment
|
||||
): ExitCode
|
||||
}
|
||||
|
||||
@@ -233,8 +233,8 @@ object JvmRuntimeVersionsConsistencyChecker {
|
||||
jars: List<KotlinLibraryFile>
|
||||
): MavenComparableVersion? {
|
||||
assert(jars.isNotEmpty()) { "'jars' must not be empty" }
|
||||
val oldestVersion = jars.minBy { it.version }!!.version
|
||||
val newestVersion = jars.maxBy { it.version }!!.version
|
||||
val oldestVersion = jars.minOf { it.version }
|
||||
val newestVersion = jars.maxOf { it.version }
|
||||
|
||||
// If the oldest version is the same as the newest version, then all jars have the same version
|
||||
if (oldestVersion == newestVersion) return oldestVersion
|
||||
@@ -250,9 +250,9 @@ object JvmRuntimeVersionsConsistencyChecker {
|
||||
// we suggest to provide an explicit dependency on version X.
|
||||
// TODO: report this depending on the content of the jars instead
|
||||
val minReflectJar =
|
||||
jars.filter { it.file.name.startsWith("kotlin-reflect") }.minBy { it.version }
|
||||
jars.filter { it.file.name.startsWith("kotlin-reflect") }.minByOrNull { it.version }
|
||||
val maxStdlibJar =
|
||||
jars.filter { it.file.name.startsWith("kotlin-runtime") || it.file.name.startsWith("kotlin-stdlib") }.maxBy { it.version }
|
||||
jars.filter { it.file.name.startsWith("kotlin-runtime") || it.file.name.startsWith("kotlin-stdlib") }.maxByOrNull { it.version }
|
||||
if (minReflectJar != null && maxStdlibJar != null && minReflectJar.version < maxStdlibJar.version) {
|
||||
messageCollector.issue(
|
||||
null,
|
||||
|
||||
@@ -89,7 +89,8 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
||||
val projectEnvironment =
|
||||
KotlinCoreEnvironment.ProjectEnvironment(
|
||||
rootDisposable,
|
||||
KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForProduction(rootDisposable, configuration)
|
||||
KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForProduction(rootDisposable, configuration),
|
||||
configuration
|
||||
)
|
||||
projectEnvironment.registerExtensionsFromPlugins(configuration)
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ import org.jetbrains.kotlin.load.java.components.JavaDeprecationSettings
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.deprecation.CoroutineCompatibilitySupport
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -84,7 +83,6 @@ class CliLightClassGenerationSupport(
|
||||
get() = DeprecationResolver(
|
||||
LockBasedStorageManager.NO_LOCKS,
|
||||
languageVersionSettings,
|
||||
CoroutineCompatibilitySupport.ENABLED,
|
||||
JavaDeprecationSettings
|
||||
)
|
||||
|
||||
|
||||
@@ -235,6 +235,7 @@ object FirKotlinToJvmBytecodeCompiler {
|
||||
if (commonSession != null) {
|
||||
sourceDependsOnDependencies(listOf(commonSession.moduleData))
|
||||
}
|
||||
friendDependencies(module.getFriendPaths())
|
||||
}
|
||||
|
||||
val commonAnalyzerFacade = commonSession?.let { FirAnalyzerFacade(it, languageVersionSettings, commonKtFiles) }
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
|
||||
import com.intellij.openapi.project.DumbUtil
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
class KotlinCoreDumbUtil : DumbUtil {
|
||||
override fun <T : Any?> filterByDumbAwarenessHonoringIgnoring(collection: Collection<T>): List<T> =
|
||||
when (collection) {
|
||||
is List<T> -> collection
|
||||
else -> ArrayList(collection)
|
||||
}
|
||||
|
||||
override fun mayUseIndices(): Boolean = false
|
||||
}
|
||||
@@ -68,6 +68,7 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_W
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
||||
import org.jetbrains.kotlin.cli.jvm.JvmRuntimeVersionsConsistencyChecker
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.jarfs.FastJarFileSystem
|
||||
import org.jetbrains.kotlin.cli.jvm.config.*
|
||||
import org.jetbrains.kotlin.cli.jvm.index.*
|
||||
import org.jetbrains.kotlin.cli.jvm.javac.JavacWrapperRegistrar
|
||||
@@ -92,11 +93,11 @@ import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
|
||||
import org.jetbrains.kotlin.resolve.ModuleAnnotationsResolver
|
||||
import org.jetbrains.kotlin.resolve.extensions.ExtraImportsProviderExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.SyntheticJavaResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.SyntheticJavaResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.CliDeclarationProviderFactoryService
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
|
||||
@@ -107,17 +108,54 @@ import java.nio.file.FileSystems
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
class KotlinCoreEnvironment private constructor(
|
||||
val projectEnvironment: JavaCoreProjectEnvironment,
|
||||
val projectEnvironment: ProjectEnvironment,
|
||||
private val initialConfiguration: CompilerConfiguration,
|
||||
configFiles: EnvironmentConfigFiles
|
||||
) {
|
||||
|
||||
class ProjectEnvironment(
|
||||
disposable: Disposable,
|
||||
applicationEnvironment: KotlinCoreApplicationEnvironment
|
||||
applicationEnvironment: KotlinCoreApplicationEnvironment,
|
||||
configuration: CompilerConfiguration
|
||||
) :
|
||||
KotlinCoreProjectEnvironment(disposable, applicationEnvironment) {
|
||||
|
||||
internal val jarFileSystem: VirtualFileSystem
|
||||
|
||||
init {
|
||||
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
|
||||
if (configuration.getBoolean(JVMConfigurationKeys.USE_FAST_JAR_FILE_SYSTEM)) {
|
||||
messageCollector?.report(
|
||||
STRONG_WARNING,
|
||||
"Using new faster version of JAR FS: it should make your build faster, but the new implementation is experimental"
|
||||
)
|
||||
}
|
||||
|
||||
jarFileSystem = when {
|
||||
configuration.getBoolean(JVMConfigurationKeys.USE_FAST_JAR_FILE_SYSTEM) || configuration.getBoolean(CommonConfigurationKeys.USE_FIR) -> {
|
||||
val fastJarFs = FastJarFileSystem.createIfUnmappingPossible()
|
||||
|
||||
if (fastJarFs == null) {
|
||||
messageCollector?.report(
|
||||
STRONG_WARNING,
|
||||
"Your JDK doesn't seem to support mapped buffer unmapping, so the slower (old) version of JAR FS will be used"
|
||||
)
|
||||
applicationEnvironment.jarFileSystem
|
||||
} else {
|
||||
|
||||
Disposer.register(disposable) {
|
||||
fastJarFs.clearHandlersCache()
|
||||
}
|
||||
|
||||
fastJarFs
|
||||
}
|
||||
}
|
||||
|
||||
else -> applicationEnvironment.jarFileSystem
|
||||
}
|
||||
}
|
||||
|
||||
private var extensionRegistered = false
|
||||
|
||||
override fun preregisterServices() {
|
||||
@@ -167,6 +205,13 @@ class KotlinCoreEnvironment private constructor(
|
||||
|
||||
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
|
||||
if (configuration.getBoolean(JVMConfigurationKeys.USE_FAST_JAR_FILE_SYSTEM)) {
|
||||
messageCollector?.report(
|
||||
STRONG_WARNING,
|
||||
"Using new faster version of JAR FS: it should make your build faster, but the new implementation is experimental"
|
||||
)
|
||||
}
|
||||
|
||||
(projectEnvironment as? ProjectEnvironment)?.registerExtensionsFromPlugins(configuration)
|
||||
// otherwise consider that project environment is properly configured before passing to the environment
|
||||
// TODO: consider some asserts to check important extension points
|
||||
@@ -222,11 +267,14 @@ class KotlinCoreEnvironment private constructor(
|
||||
this.initialRoots.addAll(initialRoots)
|
||||
|
||||
if (!configuration.getBoolean(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK) && messageCollector != null) {
|
||||
/*
|
||||
// Temporarily disable until compiler is bootstrapped to 1.6.
|
||||
JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency(
|
||||
messageCollector,
|
||||
configuration,
|
||||
initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null }
|
||||
)
|
||||
*/
|
||||
}
|
||||
|
||||
val (roots, singleJavaFileRoots) =
|
||||
@@ -391,7 +439,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
}
|
||||
|
||||
private fun findJarRoot(file: File): VirtualFile? =
|
||||
applicationEnvironment.jarFileSystem.findFileByPath("$file${URLUtil.JAR_SEPARATOR}")
|
||||
projectEnvironment.jarFileSystem.findFileByPath("$file${URLUtil.JAR_SEPARATOR}")
|
||||
|
||||
private fun getSourceRootsCheckingForDuplicates(): List<KotlinSourceRoot> {
|
||||
val uniqueSourceRoots = hashSetOf<String>()
|
||||
@@ -417,17 +465,22 @@ class KotlinCoreEnvironment private constructor(
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance(KotlinCoreEnvironment::class.java)
|
||||
|
||||
private val APPLICATION_LOCK = Object()
|
||||
@PublishedApi
|
||||
internal val APPLICATION_LOCK = Object()
|
||||
|
||||
private var ourApplicationEnvironment: KotlinCoreApplicationEnvironment? = null
|
||||
private var ourProjectCount = 0
|
||||
|
||||
inline fun <R> underApplicationLock(action: () -> R): R =
|
||||
synchronized(APPLICATION_LOCK) { action() }
|
||||
|
||||
@JvmStatic
|
||||
fun createForProduction(
|
||||
parentDisposable: Disposable, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
setupIdeaStandaloneExecution()
|
||||
val appEnv = getOrCreateApplicationEnvironmentForProduction(parentDisposable, configuration)
|
||||
val projectEnv = ProjectEnvironment(parentDisposable, appEnv)
|
||||
val projectEnv = ProjectEnvironment(parentDisposable, appEnv, configuration)
|
||||
val environment = KotlinCoreEnvironment(projectEnv, configuration, configFiles)
|
||||
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
@@ -438,7 +491,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
|
||||
@JvmStatic
|
||||
fun createForProduction(
|
||||
projectEnvironment: JavaCoreProjectEnvironment, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles
|
||||
projectEnvironment: ProjectEnvironment, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
val environment = KotlinCoreEnvironment(projectEnvironment, configuration, configFiles)
|
||||
|
||||
@@ -459,7 +512,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
val configuration = initialConfiguration.copy()
|
||||
// Tests are supposed to create a single project and dispose it right after use
|
||||
val appEnv = createApplicationEnvironment(parentDisposable, configuration, unitTestMode = true)
|
||||
val projectEnv = ProjectEnvironment(parentDisposable, appEnv)
|
||||
val projectEnv = ProjectEnvironment(parentDisposable, appEnv, configuration)
|
||||
return KotlinCoreEnvironment(projectEnv, configuration, extensionConfigs)
|
||||
}
|
||||
|
||||
@@ -474,7 +527,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
@TestOnly
|
||||
fun createProjectEnvironmentForTests(parentDisposable: Disposable, configuration: CompilerConfiguration): ProjectEnvironment {
|
||||
val appEnv = createApplicationEnvironment(parentDisposable, configuration, unitTestMode = true)
|
||||
return ProjectEnvironment(parentDisposable, appEnv)
|
||||
return ProjectEnvironment(parentDisposable, appEnv, configuration)
|
||||
}
|
||||
|
||||
// used in the daemon for jar cache cleanup
|
||||
@@ -493,7 +546,14 @@ class KotlinCoreEnvironment private constructor(
|
||||
): KotlinCoreApplicationEnvironment {
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
if (ourApplicationEnvironment == null) {
|
||||
val disposable = Disposer.newDisposable()
|
||||
val disposable = if (unitTestMode) {
|
||||
parentDisposable
|
||||
} else {
|
||||
// TODO this is a memory leak in the compiler, as this Disposable is not registered and thus is never disposed
|
||||
// but using parentDisposable as disposable in compiler, causes access to application extension points after
|
||||
// they was disposed, this needs to be fixed
|
||||
Disposer.newDisposable("Disposable for the KotlinCoreApplicationEnvironment")
|
||||
}
|
||||
ourApplicationEnvironment = createApplicationEnvironment(disposable, configuration, unitTestMode)
|
||||
ourProjectCount = 0
|
||||
Disposer.register(disposable, Disposable {
|
||||
@@ -538,11 +598,6 @@ class KotlinCoreEnvironment private constructor(
|
||||
val applicationEnvironment = KotlinCoreApplicationEnvironment.create(parentDisposable, unitTestMode)
|
||||
|
||||
registerApplicationExtensionPointsAndExtensionsFrom(configuration, "extensions/compiler.xml")
|
||||
// FIX ME WHEN BUNCH 202 REMOVED: this code is required to support compiler bundled to both 202 and 203.
|
||||
// Please, remove "com.intellij.psi.classFileDecompiler" EP registration once 202 is no longer supported by the compiler
|
||||
if (!ApplicationManager.getApplication().extensionArea.hasExtensionPoint("com.intellij.psi.classFileDecompiler")) {
|
||||
registerApplicationExtensionPointsAndExtensionsFrom(configuration, "extensions/core.xml")
|
||||
}
|
||||
|
||||
registerApplicationServicesForCLI(applicationEnvironment)
|
||||
registerApplicationServices(applicationEnvironment)
|
||||
|
||||
@@ -1,722 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
|
||||
import com.intellij.codeInsight.ExternalAnnotationsManager
|
||||
import com.intellij.codeInsight.InferredAnnotationsManager
|
||||
import com.intellij.core.CoreApplicationEnvironment
|
||||
import com.intellij.core.CoreJavaFileManager
|
||||
import com.intellij.core.JavaCoreProjectEnvironment
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.lang.java.JavaParserDefinition
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.TransactionGuard
|
||||
import com.intellij.openapi.application.TransactionGuardImpl
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.extensions.ExtensionsArea
|
||||
import com.intellij.openapi.fileTypes.PlainTextFileType
|
||||
import com.intellij.openapi.project.DumbUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.io.FileUtilRt
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.*
|
||||
import com.intellij.openapi.vfs.impl.ZipHandler
|
||||
import com.intellij.psi.PsiElementFinder
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.impl.JavaClassSupersImpl
|
||||
import com.intellij.psi.impl.PsiElementFinderImpl
|
||||
import com.intellij.psi.impl.PsiTreeChangePreprocessor
|
||||
import com.intellij.psi.impl.file.impl.JavaFileManager
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.JavaClassSupers
|
||||
import com.intellij.util.io.URLUtil
|
||||
import com.intellij.util.lang.UrlClassLoader
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
|
||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||
import org.jetbrains.kotlin.asJava.classes.FacadeCache
|
||||
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.CliModuleVisibilityManagerImpl
|
||||
import org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY
|
||||
import org.jetbrains.kotlin.cli.common.config.ContentRoot
|
||||
import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
|
||||
import org.jetbrains.kotlin.cli.common.config.kotlinSourceRoots
|
||||
import org.jetbrains.kotlin.cli.common.extensions.ScriptEvaluationExtension
|
||||
import org.jetbrains.kotlin.cli.common.extensions.ShellExtension
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_WARNING
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
||||
import org.jetbrains.kotlin.cli.jvm.JvmRuntimeVersionsConsistencyChecker
|
||||
import org.jetbrains.kotlin.cli.jvm.config.*
|
||||
import org.jetbrains.kotlin.cli.jvm.index.*
|
||||
import org.jetbrains.kotlin.cli.jvm.javac.JavacWrapperRegistrar
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleResolver
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
|
||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.extensions.*
|
||||
import org.jetbrains.kotlin.extensions.internal.CandidateInterceptor
|
||||
import org.jetbrains.kotlin.extensions.internal.TypeResolutionInterceptor
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.js.translate.extensions.JsSyntheticTranslateExtension
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache
|
||||
import org.jetbrains.kotlin.load.kotlin.MetadataFinderFactory
|
||||
import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory
|
||||
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
|
||||
import org.jetbrains.kotlin.resolve.ModuleAnnotationsResolver
|
||||
import org.jetbrains.kotlin.resolve.extensions.ExtraImportsProviderExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.SyntheticJavaResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.CliDeclarationProviderFactoryService
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializerPlugin
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import java.io.File
|
||||
import java.nio.file.FileSystems
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
class KotlinCoreEnvironment private constructor(
|
||||
val projectEnvironment: JavaCoreProjectEnvironment,
|
||||
initialConfiguration: CompilerConfiguration,
|
||||
configFiles: EnvironmentConfigFiles
|
||||
) {
|
||||
|
||||
class ProjectEnvironment(
|
||||
disposable: Disposable, applicationEnvironment: KotlinCoreApplicationEnvironment
|
||||
) :
|
||||
KotlinCoreProjectEnvironment(disposable, applicationEnvironment) {
|
||||
|
||||
private var extensionRegistered = false
|
||||
|
||||
override fun preregisterServices() {
|
||||
registerProjectExtensionPoints(project.extensionArea)
|
||||
}
|
||||
|
||||
fun registerExtensionsFromPlugins(configuration: CompilerConfiguration) {
|
||||
if (!extensionRegistered) {
|
||||
registerPluginExtensionPoints(project)
|
||||
registerExtensionsFromPlugins(project, configuration)
|
||||
extensionRegistered = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerJavaPsiFacade() {
|
||||
with(project) {
|
||||
registerService(
|
||||
CoreJavaFileManager::class.java,
|
||||
ServiceManager.getService(this, JavaFileManager::class.java) as CoreJavaFileManager
|
||||
)
|
||||
|
||||
registerKotlinLightClassSupport(project)
|
||||
|
||||
registerService(ExternalAnnotationsManager::class.java, MockExternalAnnotationsManager())
|
||||
registerService(InferredAnnotationsManager::class.java, MockInferredAnnotationsManager())
|
||||
}
|
||||
|
||||
super.registerJavaPsiFacade()
|
||||
}
|
||||
}
|
||||
|
||||
private val sourceFiles = mutableListOf<KtFile>()
|
||||
private val rootsIndex: JvmDependenciesDynamicCompoundIndex
|
||||
private val packagePartProviders = mutableListOf<JvmPackagePartProvider>()
|
||||
|
||||
private val classpathRootsResolver: ClasspathRootsResolver
|
||||
private val initialRoots = ArrayList<JavaRoot>()
|
||||
|
||||
val configuration: CompilerConfiguration = initialConfiguration.apply { setupJdkClasspathRoots(configFiles) }.copy()
|
||||
|
||||
init {
|
||||
PersistentFSConstants::class.java.getDeclaredField("ourMaxIntellisenseFileSize")
|
||||
.apply { isAccessible = true }
|
||||
.setInt(null, FileUtilRt.LARGE_FOR_CONTENT_LOADING)
|
||||
|
||||
val project = projectEnvironment.project
|
||||
|
||||
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
|
||||
(projectEnvironment as? ProjectEnvironment)?.registerExtensionsFromPlugins(configuration)
|
||||
// otherwise consider that project environment is properly configured before passing to the environment
|
||||
// TODO: consider some asserts to check important extension points
|
||||
|
||||
project.registerService(DeclarationProviderFactoryService::class.java, CliDeclarationProviderFactoryService(sourceFiles))
|
||||
|
||||
val isJvm = configFiles == EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||
project.registerService(ModuleVisibilityManager::class.java, CliModuleVisibilityManagerImpl(isJvm))
|
||||
|
||||
registerProjectServicesForCLI(projectEnvironment)
|
||||
|
||||
registerProjectServices(projectEnvironment.project)
|
||||
|
||||
for (extension in CompilerConfigurationExtension.getInstances(project)) {
|
||||
extension.updateConfiguration(configuration)
|
||||
}
|
||||
|
||||
sourceFiles += createKtFiles(project)
|
||||
|
||||
collectAdditionalSources(project)
|
||||
|
||||
sourceFiles.sortBy { it.virtualFile.path }
|
||||
|
||||
val javaFileManager = ServiceManager.getService(project, CoreJavaFileManager::class.java) as KotlinCliJavaFileManagerImpl
|
||||
|
||||
val jdkHome = configuration.get(JVMConfigurationKeys.JDK_HOME)
|
||||
val jrtFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL)
|
||||
val javaModuleFinder = CliJavaModuleFinder(
|
||||
jdkHome?.path?.let { path ->
|
||||
jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
|
||||
},
|
||||
javaFileManager
|
||||
)
|
||||
|
||||
val outputDirectory =
|
||||
configuration.get(JVMConfigurationKeys.MODULES)?.singleOrNull()?.getOutputDirectory()
|
||||
?: configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY)?.absolutePath
|
||||
|
||||
classpathRootsResolver = ClasspathRootsResolver(
|
||||
PsiManager.getInstance(project),
|
||||
messageCollector,
|
||||
configuration.getList(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES),
|
||||
this::contentRootToVirtualFile,
|
||||
javaModuleFinder,
|
||||
!configuration.getBoolean(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE),
|
||||
outputDirectory?.let(this::findLocalFile),
|
||||
javaFileManager
|
||||
)
|
||||
|
||||
val (initialRoots, javaModules) =
|
||||
classpathRootsResolver.convertClasspathRoots(configuration.getList(CLIConfigurationKeys.CONTENT_ROOTS))
|
||||
this.initialRoots.addAll(initialRoots)
|
||||
|
||||
if (!configuration.getBoolean(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK) && messageCollector != null) {
|
||||
JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency(
|
||||
messageCollector,
|
||||
configuration,
|
||||
initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null }
|
||||
)
|
||||
}
|
||||
|
||||
val (roots, singleJavaFileRoots) =
|
||||
initialRoots.partition { (file) -> file.isDirectory || file.extension != JavaFileType.DEFAULT_EXTENSION }
|
||||
|
||||
// REPL and kapt2 update classpath dynamically
|
||||
rootsIndex = JvmDependenciesDynamicCompoundIndex().apply {
|
||||
addIndex(JvmDependenciesIndexImpl(roots))
|
||||
updateClasspathFromRootsIndex(this)
|
||||
}
|
||||
|
||||
javaFileManager.initialize(
|
||||
rootsIndex,
|
||||
packagePartProviders,
|
||||
SingleJavaFileRootsIndex(singleJavaFileRoots),
|
||||
configuration.getBoolean(JVMConfigurationKeys.USE_PSI_CLASS_FILES_READING)
|
||||
)
|
||||
|
||||
project.registerService(
|
||||
JavaModuleResolver::class.java,
|
||||
CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules, javaModuleFinder.systemModules.toList(), project)
|
||||
)
|
||||
|
||||
val finderFactory = CliVirtualFileFinderFactory(rootsIndex)
|
||||
project.registerService(MetadataFinderFactory::class.java, finderFactory)
|
||||
project.registerService(VirtualFileFinderFactory::class.java, finderFactory)
|
||||
|
||||
project.putUserData(APPEND_JAVA_SOURCE_ROOTS_HANDLER_KEY, fun(roots: List<File>) {
|
||||
updateClasspath(roots.map { JavaSourceRoot(it, null) })
|
||||
})
|
||||
}
|
||||
|
||||
private fun collectAdditionalSources(project: MockProject) {
|
||||
var unprocessedSources: Collection<KtFile> = sourceFiles
|
||||
val processedSources = HashSet<KtFile>()
|
||||
val processedSourcesByExtension = HashMap<CollectAdditionalSourcesExtension, Collection<KtFile>>()
|
||||
// repeat feeding extensions with sources while new sources a being added
|
||||
var sourceCollectionIterations = 0
|
||||
while (unprocessedSources.isNotEmpty()) {
|
||||
if (sourceCollectionIterations++ > 10) { // TODO: consider using some appropriate global constant
|
||||
throw IllegalStateException("Unable to collect additional sources in reasonable number of iterations")
|
||||
}
|
||||
processedSources.addAll(unprocessedSources)
|
||||
val allNewSources = ArrayList<KtFile>()
|
||||
for (extension in CollectAdditionalSourcesExtension.getInstances(project)) {
|
||||
// do not feed the extension with the sources it returned on the previous iteration
|
||||
val sourcesToProcess = unprocessedSources - (processedSourcesByExtension[extension] ?: emptyList())
|
||||
val newSources = extension.collectAdditionalSourcesAndUpdateConfiguration(sourcesToProcess, configuration, project)
|
||||
if (newSources.isNotEmpty()) {
|
||||
allNewSources.addAll(newSources)
|
||||
processedSourcesByExtension[extension] = newSources
|
||||
}
|
||||
}
|
||||
unprocessedSources = allNewSources.filterNot { processedSources.contains(it) }.distinct()
|
||||
sourceFiles += unprocessedSources
|
||||
}
|
||||
}
|
||||
|
||||
fun addKotlinSourceRoots(rootDirs: List<File>) {
|
||||
val roots = rootDirs.map { KotlinSourceRoot(it.absolutePath, isCommon = false) }
|
||||
sourceFiles += createSourceFilesFromSourceRoots(configuration, project, roots)
|
||||
}
|
||||
|
||||
fun createPackagePartProvider(scope: GlobalSearchScope): JvmPackagePartProvider {
|
||||
return JvmPackagePartProvider(configuration.languageVersionSettings, scope).apply {
|
||||
addRoots(initialRoots, configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY))
|
||||
packagePartProviders += this
|
||||
(ModuleAnnotationsResolver.getInstance(project) as CliModuleAnnotationsResolver).addPackagePartProvider(this)
|
||||
}
|
||||
}
|
||||
|
||||
private val VirtualFile.javaFiles: List<VirtualFile>
|
||||
get() = mutableListOf<VirtualFile>().apply {
|
||||
VfsUtilCore.processFilesRecursively(this@javaFiles) { file ->
|
||||
if (file.fileType == JavaFileType.INSTANCE) {
|
||||
add(file)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private val allJavaFiles: List<File>
|
||||
get() = configuration.javaSourceRoots
|
||||
.mapNotNull(this::findLocalFile)
|
||||
.flatMap { it.javaFiles }
|
||||
.map { File(it.canonicalPath) }
|
||||
|
||||
fun registerJavac(
|
||||
javaFiles: List<File> = allJavaFiles,
|
||||
kotlinFiles: List<KtFile> = sourceFiles,
|
||||
arguments: Array<String>? = null,
|
||||
bootClasspath: List<File>? = null,
|
||||
sourcePath: List<File>? = null
|
||||
): Boolean {
|
||||
return JavacWrapperRegistrar.registerJavac(
|
||||
projectEnvironment.project, configuration, javaFiles, kotlinFiles, arguments, bootClasspath, sourcePath,
|
||||
LightClassGenerationSupport.getInstance(project), packagePartProviders
|
||||
)
|
||||
}
|
||||
|
||||
private val applicationEnvironment: CoreApplicationEnvironment
|
||||
get() = projectEnvironment.environment
|
||||
|
||||
val project: Project
|
||||
get() = projectEnvironment.project
|
||||
|
||||
internal fun countLinesOfCode(sourceFiles: List<KtFile>): Int =
|
||||
sourceFiles.sumBy { sourceFile ->
|
||||
val text = sourceFile.text
|
||||
StringUtil.getLineBreakCount(text) + (if (StringUtil.endsWithLineBreak(text)) 0 else 1)
|
||||
}
|
||||
|
||||
private fun updateClasspathFromRootsIndex(index: JvmDependenciesIndex) {
|
||||
index.indexedRoots.forEach {
|
||||
projectEnvironment.addSourcesToClasspath(it.file)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateClasspath(contentRoots: List<ContentRoot>): List<File>? {
|
||||
// TODO: add new Java modules to CliJavaModuleResolver
|
||||
val newRoots = classpathRootsResolver.convertClasspathRoots(contentRoots).roots
|
||||
|
||||
if (packagePartProviders.isEmpty()) {
|
||||
initialRoots.addAll(newRoots)
|
||||
} else {
|
||||
for (packagePartProvider in packagePartProviders) {
|
||||
packagePartProvider.addRoots(newRoots, configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY))
|
||||
}
|
||||
}
|
||||
|
||||
return rootsIndex.addNewIndexForRoots(newRoots)?.let { newIndex ->
|
||||
updateClasspathFromRootsIndex(newIndex)
|
||||
newIndex.indexedRoots.mapNotNull { (file) ->
|
||||
VfsUtilCore.virtualToIoFile(VfsUtilCore.getVirtualFileForJar(file) ?: file)
|
||||
}.toList()
|
||||
}.orEmpty()
|
||||
}
|
||||
|
||||
private fun contentRootToVirtualFile(root: JvmContentRoot): VirtualFile? =
|
||||
when (root) {
|
||||
is JvmClasspathRoot ->
|
||||
if (root.file.isFile) findJarRoot(root.file) else findExistingRoot(root, "Classpath entry")
|
||||
is JvmModulePathRoot ->
|
||||
if (root.file.isFile) findJarRoot(root.file) else findExistingRoot(root, "Java module root")
|
||||
is JavaSourceRoot ->
|
||||
findExistingRoot(root, "Java source root")
|
||||
else ->
|
||||
throw IllegalStateException("Unexpected root: $root")
|
||||
}
|
||||
|
||||
internal fun findLocalFile(path: String): VirtualFile? =
|
||||
applicationEnvironment.localFileSystem.findFileByPath(path)
|
||||
|
||||
private fun findExistingRoot(root: JvmContentRoot, rootDescription: String): VirtualFile? {
|
||||
return findLocalFile(root.file.absolutePath).also {
|
||||
if (it == null) {
|
||||
report(STRONG_WARNING, "$rootDescription points to a non-existent location: ${root.file}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findJarRoot(file: File): VirtualFile? =
|
||||
applicationEnvironment.jarFileSystem.findFileByPath("$file${URLUtil.JAR_SEPARATOR}")
|
||||
|
||||
private fun getSourceRootsCheckingForDuplicates(): List<KotlinSourceRoot> {
|
||||
val uniqueSourceRoots = hashSetOf<String>()
|
||||
val result = mutableListOf<KotlinSourceRoot>()
|
||||
|
||||
for (root in configuration.kotlinSourceRoots) {
|
||||
if (!uniqueSourceRoots.add(root.path)) {
|
||||
report(STRONG_WARNING, "Duplicate source root: ${root.path}")
|
||||
}
|
||||
result.add(root)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fun getSourceFiles(): List<KtFile> = sourceFiles
|
||||
|
||||
private fun createKtFiles(project: Project): List<KtFile> =
|
||||
createSourceFilesFromSourceRoots(configuration, project, getSourceRootsCheckingForDuplicates())
|
||||
|
||||
internal fun report(severity: CompilerMessageSeverity, message: String) = configuration.report(severity, message)
|
||||
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance(KotlinCoreEnvironment::class.java)
|
||||
|
||||
private val APPLICATION_LOCK = Object()
|
||||
private var ourApplicationEnvironment: KotlinCoreApplicationEnvironment? = null
|
||||
private var ourProjectCount = 0
|
||||
|
||||
@JvmStatic
|
||||
fun createForProduction(
|
||||
parentDisposable: Disposable, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
setupIdeaStandaloneExecution()
|
||||
val appEnv = getOrCreateApplicationEnvironmentForProduction(parentDisposable, configuration)
|
||||
val projectEnv = ProjectEnvironment(parentDisposable, appEnv)
|
||||
val environment = KotlinCoreEnvironment(projectEnv, configuration, configFiles)
|
||||
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
ourProjectCount++
|
||||
}
|
||||
return environment
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun createForProduction(
|
||||
projectEnvironment: JavaCoreProjectEnvironment, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
val environment = KotlinCoreEnvironment(projectEnvironment, configuration, configFiles)
|
||||
|
||||
if (projectEnvironment.environment == applicationEnvironment) {
|
||||
// accounting for core environment disposing
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
ourProjectCount++
|
||||
}
|
||||
}
|
||||
return environment
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
@JvmStatic
|
||||
fun createForTests(
|
||||
parentDisposable: Disposable, initialConfiguration: CompilerConfiguration, extensionConfigs: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
val configuration = initialConfiguration.copy()
|
||||
// Tests are supposed to create a single project and dispose it right after use
|
||||
val appEnv = createApplicationEnvironment(parentDisposable, configuration, unitTestMode = true)
|
||||
val projectEnv = ProjectEnvironment(parentDisposable, appEnv)
|
||||
return KotlinCoreEnvironment(projectEnv, configuration, extensionConfigs)
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
@JvmStatic
|
||||
fun createForTests(
|
||||
projectEnvironment: ProjectEnvironment, initialConfiguration: CompilerConfiguration, extensionConfigs: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
return KotlinCoreEnvironment(projectEnvironment, initialConfiguration, extensionConfigs)
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun createProjectEnvironmentForTests(parentDisposable: Disposable, configuration: CompilerConfiguration): ProjectEnvironment {
|
||||
val appEnv = createApplicationEnvironment(parentDisposable, configuration, unitTestMode = true)
|
||||
return ProjectEnvironment(parentDisposable, appEnv)
|
||||
}
|
||||
|
||||
// used in the daemon for jar cache cleanup
|
||||
val applicationEnvironment: KotlinCoreApplicationEnvironment? get() = ourApplicationEnvironment
|
||||
|
||||
fun getOrCreateApplicationEnvironmentForProduction(
|
||||
parentDisposable: Disposable, configuration: CompilerConfiguration
|
||||
): KotlinCoreApplicationEnvironment = getOrCreateApplicationEnvironment(parentDisposable, configuration, unitTestMode = false)
|
||||
|
||||
fun getOrCreateApplicationEnvironmentForTests(
|
||||
parentDisposable: Disposable, configuration: CompilerConfiguration
|
||||
): KotlinCoreApplicationEnvironment = getOrCreateApplicationEnvironment(parentDisposable, configuration, unitTestMode = true)
|
||||
|
||||
private fun getOrCreateApplicationEnvironment(
|
||||
parentDisposable: Disposable, configuration: CompilerConfiguration, unitTestMode: Boolean
|
||||
): KotlinCoreApplicationEnvironment {
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
if (ourApplicationEnvironment == null) {
|
||||
val disposable = Disposer.newDisposable()
|
||||
ourApplicationEnvironment = createApplicationEnvironment(disposable, configuration, unitTestMode)
|
||||
ourProjectCount = 0
|
||||
Disposer.register(disposable, Disposable {
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
ourApplicationEnvironment = null
|
||||
}
|
||||
})
|
||||
}
|
||||
// Disposing of the environment is unsafe in production then parallel builds are enabled, but turning it off universally
|
||||
// breaks a lot of tests, therefore it is disabled for production and enabled for tests
|
||||
if (System.getProperty(KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY).toBooleanLenient() != true) {
|
||||
// JPS may run many instances of the compiler in parallel (there's an option for compiling independent modules in parallel in IntelliJ)
|
||||
// All projects share the same ApplicationEnvironment, and when the last project is disposed, the ApplicationEnvironment is disposed as well
|
||||
Disposer.register(parentDisposable, Disposable {
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
if (--ourProjectCount <= 0) {
|
||||
disposeApplicationEnvironment()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return ourApplicationEnvironment!!
|
||||
}
|
||||
}
|
||||
|
||||
private fun disposeApplicationEnvironment() {
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
val environment = ourApplicationEnvironment ?: return
|
||||
ourApplicationEnvironment = null
|
||||
Disposer.dispose(environment.parentDisposable)
|
||||
ZipHandler.clearFileAccessorCache()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createApplicationEnvironment(
|
||||
parentDisposable: Disposable, configuration: CompilerConfiguration, unitTestMode: Boolean
|
||||
): KotlinCoreApplicationEnvironment {
|
||||
val applicationEnvironment = KotlinCoreApplicationEnvironment.create(parentDisposable, unitTestMode)
|
||||
|
||||
registerApplicationExtensionPointsAndExtensionsFrom(configuration, "extensions/compiler.xml")
|
||||
registerApplicationExtensionPointsAndExtensionsFrom(configuration, "extensions/core.xml")
|
||||
|
||||
registerApplicationServicesForCLI(applicationEnvironment)
|
||||
registerApplicationServices(applicationEnvironment)
|
||||
|
||||
return applicationEnvironment
|
||||
}
|
||||
|
||||
private fun registerApplicationExtensionPointsAndExtensionsFrom(configuration: CompilerConfiguration, configFilePath: String) {
|
||||
fun File.hasConfigFile(configFile: String): Boolean =
|
||||
if (isDirectory) File(this, "META-INF" + File.separator + configFile).exists()
|
||||
else try {
|
||||
ZipFile(this).use {
|
||||
it.getEntry("META-INF/$configFile") != null
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
false
|
||||
}
|
||||
|
||||
val pluginRoot: File =
|
||||
configuration.get(CLIConfigurationKeys.INTELLIJ_PLUGIN_ROOT)?.let(::File)
|
||||
?: PathUtil.getResourcePathForClass(this::class.java).takeIf { it.hasConfigFile(configFilePath) }
|
||||
// hack for load extensions when compiler run directly from project directory (e.g. in tests)
|
||||
?: File("compiler/cli/cli-common/resources").takeIf { it.hasConfigFile(configFilePath) }
|
||||
?: throw IllegalStateException(
|
||||
"Unable to find extension point configuration $configFilePath " +
|
||||
"(cp:\n ${(Thread.currentThread().contextClassLoader as? UrlClassLoader)?.urls?.joinToString("\n ") { it.file }})"
|
||||
)
|
||||
|
||||
CoreApplicationEnvironment.registerExtensionPointAndExtensions(
|
||||
FileSystems.getDefault().getPath(pluginRoot.path),
|
||||
configFilePath,
|
||||
ApplicationManager.getApplication().extensionArea
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Suppress("MemberVisibilityCanPrivate") // made public for CLI Android Lint
|
||||
fun registerPluginExtensionPoints(project: MockProject) {
|
||||
ExpressionCodegenExtension.registerExtensionPoint(project)
|
||||
SyntheticResolveExtension.registerExtensionPoint(project)
|
||||
SyntheticJavaResolveExtension.registerExtensionPoint(project)
|
||||
ClassBuilderInterceptorExtension.registerExtensionPoint(project)
|
||||
AnalysisHandlerExtension.registerExtensionPoint(project)
|
||||
PackageFragmentProviderExtension.registerExtensionPoint(project)
|
||||
StorageComponentContainerContributor.registerExtensionPoint(project)
|
||||
DeclarationAttributeAltererExtension.registerExtensionPoint(project)
|
||||
PreprocessedVirtualFileFactoryExtension.registerExtensionPoint(project)
|
||||
JsSyntheticTranslateExtension.registerExtensionPoint(project)
|
||||
CompilerConfigurationExtension.registerExtensionPoint(project)
|
||||
CollectAdditionalSourcesExtension.registerExtensionPoint(project)
|
||||
ExtraImportsProviderExtension.registerExtensionPoint(project)
|
||||
IrGenerationExtension.registerExtensionPoint(project)
|
||||
ScriptEvaluationExtension.registerExtensionPoint(project)
|
||||
ShellExtension.registerExtensionPoint(project)
|
||||
TypeResolutionInterceptor.registerExtensionPoint(project)
|
||||
CandidateInterceptor.registerExtensionPoint(project)
|
||||
DescriptorSerializerPlugin.registerExtensionPoint(project)
|
||||
}
|
||||
|
||||
internal fun registerExtensionsFromPlugins(project: MockProject, configuration: CompilerConfiguration) {
|
||||
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) {
|
||||
try {
|
||||
registrar.registerProjectComponents(project, configuration)
|
||||
} catch (e: AbstractMethodError) {
|
||||
val message = "The provided plugin ${registrar.javaClass.name} is not compatible with this version of compiler"
|
||||
// Since the scripting plugin is often discovered in the compiler environment, it is often taken from the incompatible
|
||||
// location, and in many cases this is not a fatal error, therefore strong warning is generated instead of exception
|
||||
if (registrar.javaClass.simpleName == "ScriptingCompilerConfigurationComponentRegistrar") {
|
||||
messageCollector?.report(STRONG_WARNING, "Default scripting plugin is disabled: $message")
|
||||
} else {
|
||||
throw IllegalStateException(message, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun registerApplicationServicesForCLI(applicationEnvironment: KotlinCoreApplicationEnvironment) {
|
||||
// ability to get text from annotations xml files
|
||||
applicationEnvironment.registerFileType(PlainTextFileType.INSTANCE, "xml")
|
||||
applicationEnvironment.registerParserDefinition(JavaParserDefinition())
|
||||
}
|
||||
|
||||
// made public for Upsource
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
@JvmStatic
|
||||
fun registerApplicationServices(applicationEnvironment: KotlinCoreApplicationEnvironment) {
|
||||
with(applicationEnvironment) {
|
||||
registerFileType(KotlinFileType.INSTANCE, "kt")
|
||||
registerFileType(KotlinFileType.INSTANCE, KotlinParserDefinition.STD_SCRIPT_SUFFIX)
|
||||
registerParserDefinition(KotlinParserDefinition())
|
||||
application.registerService(KotlinBinaryClassCache::class.java, KotlinBinaryClassCache())
|
||||
application.registerService(JavaClassSupers::class.java, JavaClassSupersImpl::class.java)
|
||||
application.registerService(TransactionGuard::class.java, TransactionGuardImpl::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun registerProjectExtensionPoints(area: ExtensionsArea) {
|
||||
CoreApplicationEnvironment.registerExtensionPoint(
|
||||
area, PsiTreeChangePreprocessor.EP.name, PsiTreeChangePreprocessor::class.java
|
||||
)
|
||||
CoreApplicationEnvironment.registerExtensionPoint(area, PsiElementFinder.EP.name, PsiElementFinder::class.java)
|
||||
|
||||
IdeaExtensionPoints.registerVersionSpecificProjectExtensionPoints(area)
|
||||
}
|
||||
|
||||
// made public for Upsource
|
||||
@JvmStatic
|
||||
@Deprecated("Use registerProjectServices(project) instead.", ReplaceWith("registerProjectServices(projectEnvironment.project)"))
|
||||
fun registerProjectServices(
|
||||
projectEnvironment: JavaCoreProjectEnvironment,
|
||||
@Suppress("UNUSED_PARAMETER") messageCollector: MessageCollector?
|
||||
) {
|
||||
registerProjectServices(projectEnvironment.project)
|
||||
}
|
||||
|
||||
// made public for Android Lint
|
||||
@JvmStatic
|
||||
fun registerProjectServices(project: MockProject) {
|
||||
with(project) {
|
||||
registerService(KotlinJavaPsiFacade::class.java, KotlinJavaPsiFacade(this))
|
||||
registerService(FacadeCache::class.java, FacadeCache(this))
|
||||
registerService(ModuleAnnotationsResolver::class.java, CliModuleAnnotationsResolver())
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerProjectServicesForCLI(@Suppress("UNUSED_PARAMETER") projectEnvironment: JavaCoreProjectEnvironment) {
|
||||
/**
|
||||
* Note that Kapt may restart code analysis process, and CLI services should be aware of that.
|
||||
* Use PsiManager.getModificationTracker() to ensure that all the data you cached is still valid.
|
||||
*/
|
||||
}
|
||||
|
||||
// made public for Android Lint
|
||||
@JvmStatic
|
||||
fun registerKotlinLightClassSupport(project: MockProject) {
|
||||
with(project) {
|
||||
val traceHolder = CliTraceHolder()
|
||||
val cliLightClassGenerationSupport = CliLightClassGenerationSupport(traceHolder, project)
|
||||
val kotlinAsJavaSupport = CliKotlinAsJavaSupport(this, traceHolder)
|
||||
registerService(LightClassGenerationSupport::class.java, cliLightClassGenerationSupport)
|
||||
registerService(CliLightClassGenerationSupport::class.java, cliLightClassGenerationSupport)
|
||||
registerService(KotlinAsJavaSupport::class.java, kotlinAsJavaSupport)
|
||||
registerService(CodeAnalyzerInitializer::class.java, traceHolder)
|
||||
@Suppress("UnstableApiUsage")
|
||||
registerService(DumbUtil::class.java, KotlinCoreDumbUtil())
|
||||
|
||||
// We don't pass Disposable because in some tests, we manually unregister these extensions, and that leads to LOG.error
|
||||
// exception from `ExtensionPointImpl.doRegisterExtension`, because the registered extension can no longer be found
|
||||
// when the project is being disposed.
|
||||
// For example, see the `unregisterExtension` call in `GenerationUtils.compileFilesUsingFrontendIR`.
|
||||
// TODO: refactor this to avoid registering unneeded extensions in the first place, and avoid using deprecated API.
|
||||
@Suppress("DEPRECATION")
|
||||
PsiElementFinder.EP.getPoint(project).registerExtension(JavaElementFinder(this))
|
||||
@Suppress("DEPRECATION")
|
||||
PsiElementFinder.EP.getPoint(project).registerExtension(PsiElementFinderImpl(this))
|
||||
}
|
||||
}
|
||||
|
||||
private fun CompilerConfiguration.setupJdkClasspathRoots(configFiles: EnvironmentConfigFiles) {
|
||||
if (getBoolean(JVMConfigurationKeys.NO_JDK)) return
|
||||
|
||||
val jvmTarget = configFiles == EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||
if (!jvmTarget) return
|
||||
|
||||
val jdkHome = get(JVMConfigurationKeys.JDK_HOME)
|
||||
val (javaRoot, classesRoots) = if (jdkHome == null) {
|
||||
val javaHome = File(System.getProperty("java.home"))
|
||||
put(JVMConfigurationKeys.JDK_HOME, javaHome)
|
||||
|
||||
javaHome to PathUtil.getJdkClassesRootsFromCurrentJre()
|
||||
} else {
|
||||
jdkHome to PathUtil.getJdkClassesRoots(jdkHome)
|
||||
}
|
||||
|
||||
if (!CoreJrtFileSystem.isModularJdk(javaRoot)) {
|
||||
if (classesRoots.isEmpty()) {
|
||||
report(ERROR, "No class roots are found in the JDK path: $javaRoot")
|
||||
} else {
|
||||
addJvmSdkRoots(classesRoots)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler.jarfs
|
||||
|
||||
class ByteArrayCharSequence(
|
||||
private val bytes: ByteArray,
|
||||
private val start: Int = 0,
|
||||
private val end: Int = bytes.size
|
||||
) : CharSequence {
|
||||
|
||||
override fun hashCode(): Int {
|
||||
error("Do not try computing hashCode ByteArrayCharSequence")
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
error("Do not try comparing ByteArrayCharSequence")
|
||||
}
|
||||
|
||||
override val length get() = end - start
|
||||
|
||||
override fun get(index: Int): Char = bytes[index + start].toChar()
|
||||
|
||||
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
|
||||
if (startIndex == 0 && endIndex == length) return this
|
||||
return ByteArrayCharSequence(bytes, start + startIndex, start + endIndex)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val chars = CharArray(length)
|
||||
|
||||
for (i in 0 until length) {
|
||||
chars[i] = bytes[i + start].toChar()
|
||||
}
|
||||
|
||||
return String(chars)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler.jarfs
|
||||
|
||||
import com.intellij.openapi.util.Couple
|
||||
import com.intellij.openapi.vfs.DeprecatedVirtualFileSystem
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.containers.ConcurrentFactoryMap
|
||||
import com.intellij.util.io.FileAccessorCache
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.RandomAccessFile
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.MappedByteBuffer
|
||||
import java.nio.channels.FileChannel
|
||||
|
||||
private typealias RandomAccessFileAndBuffer = Pair<RandomAccessFile, MappedByteBuffer>
|
||||
|
||||
class FastJarFileSystem private constructor(internal val unmapBuffer: MappedByteBuffer.() -> Unit) : DeprecatedVirtualFileSystem() {
|
||||
private val myHandlers: MutableMap<String, FastJarHandler> =
|
||||
ConcurrentFactoryMap.createMap { key: String -> FastJarHandler(this@FastJarFileSystem, key) }
|
||||
|
||||
internal val cachedOpenFileHandles: FileAccessorCache<File, RandomAccessFileAndBuffer> =
|
||||
object : FileAccessorCache<File, RandomAccessFileAndBuffer>(20, 10) {
|
||||
@Throws(IOException::class)
|
||||
override fun createAccessor(file: File): RandomAccessFileAndBuffer {
|
||||
val randomAccessFile = RandomAccessFile(file, "r")
|
||||
return Pair(randomAccessFile, randomAccessFile.channel.map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length()))
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun disposeAccessor(fileAccessor: RandomAccessFileAndBuffer) {
|
||||
fileAccessor.first.close()
|
||||
fileAccessor.second.unmapBuffer()
|
||||
}
|
||||
|
||||
override fun isEqual(val1: File, val2: File): Boolean {
|
||||
return val1 == val2 // reference equality to handle different jars for different ZipHandlers on the same path
|
||||
}
|
||||
}
|
||||
|
||||
override fun getProtocol(): String {
|
||||
return StandardFileSystems.JAR_PROTOCOL
|
||||
}
|
||||
|
||||
override fun findFileByPath(path: String): VirtualFile? {
|
||||
val pair = splitPath(path)
|
||||
return myHandlers[pair.first]!!.findFileByPath(pair.second)
|
||||
}
|
||||
|
||||
override fun refresh(asynchronous: Boolean) {}
|
||||
override fun refreshAndFindFileByPath(path: String): VirtualFile? {
|
||||
return findFileByPath(path)
|
||||
}
|
||||
|
||||
fun clearHandlersCache() {
|
||||
myHandlers.clear()
|
||||
cachedOpenFileHandles.clear()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun splitPath(path: String): Couple<String> {
|
||||
val separator = path.indexOf("!/")
|
||||
require(separator >= 0) { "Path in JarFileSystem must contain a separator: $path" }
|
||||
val localPath = path.substring(0, separator)
|
||||
val pathInJar = path.substring(separator + 2)
|
||||
return Couple.of(localPath, pathInJar)
|
||||
}
|
||||
|
||||
fun createIfUnmappingPossible(): FastJarFileSystem? {
|
||||
val cleanerCallBack = prepareCleanerCallback() ?: return null
|
||||
return FastJarFileSystem(cleanerCallBack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val IS_PRIOR_9_JRE = System.getProperty("java.specification.version", "").startsWith("1.")
|
||||
|
||||
private fun prepareCleanerCallback(): ((ByteBuffer) -> Unit)? {
|
||||
return try {
|
||||
if (IS_PRIOR_9_JRE) {
|
||||
val cleaner = Class.forName("java.nio.DirectByteBuffer").getMethod("cleaner")
|
||||
cleaner.isAccessible = true
|
||||
|
||||
val clean = Class.forName("sun.misc.Cleaner").getMethod("clean")
|
||||
clean.isAccessible = true
|
||||
|
||||
{ buffer: ByteBuffer -> clean.invoke(cleaner.invoke(buffer)) }
|
||||
} else {
|
||||
val unsafeClass = try {
|
||||
Class.forName("sun.misc.Unsafe")
|
||||
} catch (ex: Exception) {
|
||||
// jdk.internal.misc.Unsafe doesn't yet have an invokeCleaner() method,
|
||||
// but that method should be added if sun.misc.Unsafe is removed.
|
||||
Class.forName("jdk.internal.misc.Unsafe")
|
||||
}
|
||||
|
||||
val clean = unsafeClass.getMethod("invokeCleaner", ByteBuffer::class.java)
|
||||
clean.isAccessible = true
|
||||
|
||||
val theUnsafeField = unsafeClass.getDeclaredField("theUnsafe")
|
||||
theUnsafeField.isAccessible = true
|
||||
|
||||
val theUnsafe = theUnsafeField.get(null);
|
||||
|
||||
{ buffer: ByteBuffer -> clean.invoke(theUnsafe, buffer) }
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler.jarfs
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.RandomAccessFile
|
||||
import java.nio.channels.FileChannel
|
||||
|
||||
class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) {
|
||||
private val myRoot: VirtualFile?
|
||||
internal val file = File(path)
|
||||
|
||||
private val cachedManifest: ByteArray?
|
||||
|
||||
init {
|
||||
val entries: List<ZipEntryDescription>
|
||||
RandomAccessFile(file, "r").use { randomAccessFile ->
|
||||
val mappedByteBuffer = randomAccessFile.channel.map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length())
|
||||
try {
|
||||
entries = mappedByteBuffer.parseCentralDirectory()
|
||||
cachedManifest =
|
||||
entries.singleOrNull { StringUtil.equals(MANIFEST_PATH, it.relativePath) }
|
||||
?.let(mappedByteBuffer::contentsToByteArray)
|
||||
} finally {
|
||||
with(fileSystem) {
|
||||
mappedByteBuffer.unmapBuffer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
myRoot = FastJarVirtualFile(this, "", -1, parent = null, entryDescription = null)
|
||||
|
||||
// ByteArrayCharSequence should not be used instead of String
|
||||
// because the former class does not support equals/hashCode properly
|
||||
val filesByRelativePath = HashMap<String, FastJarVirtualFile>(entries.size)
|
||||
filesByRelativePath[""] = myRoot
|
||||
|
||||
for (entryDescription in entries) {
|
||||
if (!entryDescription.isDirectory) {
|
||||
createFile(entryDescription, filesByRelativePath)
|
||||
} else {
|
||||
getOrCreateDirectory(entryDescription.relativePath, filesByRelativePath)
|
||||
}
|
||||
}
|
||||
|
||||
for (node in filesByRelativePath.values) {
|
||||
node.initChildrenArrayFromList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createFile(entry: ZipEntryDescription, directories: MutableMap<String, FastJarVirtualFile>): FastJarVirtualFile {
|
||||
val (parentName, shortName) = entry.relativePath.splitPath()
|
||||
|
||||
val parentFile = getOrCreateDirectory(parentName, directories)
|
||||
if ("." == shortName) {
|
||||
return parentFile
|
||||
}
|
||||
|
||||
return FastJarVirtualFile(
|
||||
this, shortName,
|
||||
if (entry.isDirectory) -1 else entry.uncompressedSize,
|
||||
parentFile,
|
||||
entry,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getOrCreateDirectory(entryName: CharSequence, directories: MutableMap<String, FastJarVirtualFile>): FastJarVirtualFile {
|
||||
return directories.getOrPut(entryName.toString()) {
|
||||
val (parentPath, shortName) = entryName.splitPath()
|
||||
val parentFile = getOrCreateDirectory(parentPath, directories)
|
||||
|
||||
FastJarVirtualFile(this, shortName, -1, parentFile, entryDescription = null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun CharSequence.splitPath(): Pair<CharSequence, CharSequence> {
|
||||
var slashIndex = this.length - 1
|
||||
|
||||
while (slashIndex >= 0 && this[slashIndex] != '/') {
|
||||
slashIndex--
|
||||
}
|
||||
|
||||
if (slashIndex == -1) return Pair("", this)
|
||||
return Pair(subSequence(0, slashIndex), subSequence(slashIndex + 1, this.length))
|
||||
}
|
||||
|
||||
fun findFileByPath(pathInJar: String): VirtualFile? {
|
||||
return myRoot?.findFileByRelativePath(pathInJar)
|
||||
}
|
||||
|
||||
fun contentsToByteArray(zipEntryDescription: ZipEntryDescription): ByteArray {
|
||||
val relativePath = zipEntryDescription.relativePath
|
||||
if (StringUtil.equals(relativePath, MANIFEST_PATH)) return cachedManifest ?: throw FileNotFoundException("$file!/$relativePath")
|
||||
return fileSystem.cachedOpenFileHandles[file].use {
|
||||
synchronized(it) {
|
||||
it.get().second.contentsToByteArray(zipEntryDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val MANIFEST_PATH = "META-INF/MANIFEST.MF"
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler.jarfs
|
||||
|
||||
import com.intellij.openapi.util.io.BufferExposingByteArrayInputStream
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileSystem
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
internal class FastJarVirtualFile(
|
||||
private val handler: FastJarHandler,
|
||||
private val name: CharSequence,
|
||||
private val length: Int,
|
||||
private val parent: FastJarVirtualFile?,
|
||||
private val entryDescription: ZipEntryDescription?,
|
||||
) : VirtualFile() {
|
||||
|
||||
private var myChildrenArray = EMPTY_ARRAY
|
||||
private val myChildrenList: MutableList<VirtualFile> = mutableListOf()
|
||||
|
||||
init {
|
||||
parent?.myChildrenList?.add(this)
|
||||
}
|
||||
|
||||
fun initChildrenArrayFromList() {
|
||||
myChildrenArray = myChildrenList.toTypedArray()
|
||||
myChildrenList.clear()
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
return name.toString()
|
||||
}
|
||||
|
||||
override fun getNameSequence(): CharSequence {
|
||||
return name
|
||||
}
|
||||
|
||||
override fun getFileSystem(): VirtualFileSystem {
|
||||
return handler.fileSystem
|
||||
}
|
||||
|
||||
override fun getPath(): String {
|
||||
if (parent == null) {
|
||||
return FileUtil.toSystemIndependentName(handler.file.path) + "!/"
|
||||
}
|
||||
val parentPath = parent.path
|
||||
val answer = StringBuilder(parentPath.length + 1 + name.length)
|
||||
answer.append(parentPath)
|
||||
if (answer[answer.length - 1] != '/') {
|
||||
answer.append('/')
|
||||
}
|
||||
answer.append(name)
|
||||
return answer.toString()
|
||||
}
|
||||
|
||||
override fun isWritable(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isDirectory(): Boolean {
|
||||
return length < 0
|
||||
}
|
||||
|
||||
override fun isValid(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getParent(): VirtualFile? {
|
||||
return parent
|
||||
}
|
||||
|
||||
override fun getChildren(): Array<VirtualFile> {
|
||||
return myChildrenArray
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun getOutputStream(requestor: Any, newModificationStamp: Long, newTimeStamp: Long): OutputStream {
|
||||
throw UnsupportedOperationException("JarFileSystem is read-only")
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun contentsToByteArray(): ByteArray {
|
||||
if (entryDescription == null) return EMPTY_BYTE_ARRAY
|
||||
return handler.contentsToByteArray(entryDescription)
|
||||
}
|
||||
|
||||
override fun getTimeStamp(): Long = 0
|
||||
|
||||
override fun getLength(): Long = length.toLong()
|
||||
|
||||
override fun refresh(asynchronous: Boolean, recursive: Boolean, postRunnable: Runnable?) {}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun getInputStream(): InputStream {
|
||||
return BufferExposingByteArrayInputStream(contentsToByteArray())
|
||||
}
|
||||
|
||||
override fun getModificationStamp(): Long {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
private val EMPTY_BYTE_ARRAY = ByteArray(0)
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler.jarfs
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.nio.MappedByteBuffer
|
||||
import java.util.zip.Inflater
|
||||
|
||||
|
||||
class ZipEntryDescription(
|
||||
val relativePath: CharSequence,
|
||||
val compressedSize: Int,
|
||||
val uncompressedSize: Int,
|
||||
val offsetInFile: Int,
|
||||
val compressionKind: CompressionKind,
|
||||
val fileNameSize: Int,
|
||||
) {
|
||||
|
||||
enum class CompressionKind {
|
||||
PLAIN, DEFLATE
|
||||
}
|
||||
|
||||
val isDirectory get() = uncompressedSize == 0
|
||||
}
|
||||
|
||||
private const val END_OF_CENTRAL_DIR_SIZE = 22
|
||||
private const val LOCAL_FILE_HEADER_EXTRA_OFFSET = 28
|
||||
private const val LOCAL_FILE_HEADER_SIZE = LOCAL_FILE_HEADER_EXTRA_OFFSET + 2
|
||||
|
||||
fun MappedByteBuffer.contentsToByteArray(
|
||||
zipEntryDescription: ZipEntryDescription
|
||||
): ByteArray {
|
||||
order(ByteOrder.LITTLE_ENDIAN)
|
||||
val extraSize =
|
||||
getUnsignedShort(zipEntryDescription.offsetInFile + LOCAL_FILE_HEADER_EXTRA_OFFSET)
|
||||
|
||||
position(
|
||||
zipEntryDescription.offsetInFile + LOCAL_FILE_HEADER_SIZE + zipEntryDescription.fileNameSize + extraSize
|
||||
)
|
||||
val compressed = ByteArray(zipEntryDescription.compressedSize + 1)
|
||||
get(compressed, 0, zipEntryDescription.compressedSize)
|
||||
|
||||
return when (zipEntryDescription.compressionKind) {
|
||||
ZipEntryDescription.CompressionKind.DEFLATE -> {
|
||||
val inflater = Inflater(true)
|
||||
inflater.setInput(compressed, 0, zipEntryDescription.compressedSize)
|
||||
|
||||
val result = ByteArray(zipEntryDescription.uncompressedSize)
|
||||
|
||||
inflater.inflate(result)
|
||||
|
||||
result
|
||||
}
|
||||
ZipEntryDescription.CompressionKind.PLAIN -> compressed.copyOf(zipEntryDescription.compressedSize)
|
||||
}
|
||||
}
|
||||
|
||||
fun MappedByteBuffer.parseCentralDirectory(): List<ZipEntryDescription> {
|
||||
order(ByteOrder.LITTLE_ENDIAN)
|
||||
|
||||
var endOfCentralDirectoryOffset = capacity() - END_OF_CENTRAL_DIR_SIZE
|
||||
while (endOfCentralDirectoryOffset >= 0) {
|
||||
// header of "End of central directory"
|
||||
if (getInt(endOfCentralDirectoryOffset) == 0x06054b50) break
|
||||
endOfCentralDirectoryOffset--
|
||||
}
|
||||
|
||||
val entriesNumber = getUnsignedShort(endOfCentralDirectoryOffset + 10)
|
||||
val offsetOfCentralDirectory = getInt(endOfCentralDirectoryOffset + 16)
|
||||
|
||||
var currentOffset = offsetOfCentralDirectory
|
||||
|
||||
val result = mutableListOf<ZipEntryDescription>()
|
||||
for (i in 0 until entriesNumber) {
|
||||
val headerConst = getInt(currentOffset)
|
||||
require(headerConst == 0x02014b50) {
|
||||
"$i: $headerConst"
|
||||
}
|
||||
|
||||
val versionNeededToExtract =
|
||||
getShort(currentOffset + 6).toInt()
|
||||
|
||||
val compressionMethod = getShort(currentOffset + 10).toInt()
|
||||
|
||||
val compressedSize = getInt(currentOffset + 20)
|
||||
val uncompressedSize = getInt(currentOffset + 24)
|
||||
val fileNameLength = getUnsignedShort(currentOffset + 28).toInt()
|
||||
val extraLength = getUnsignedShort(currentOffset + 30).toInt()
|
||||
val fileCommentLength = getUnsignedShort(currentOffset + 32).toInt()
|
||||
|
||||
val offsetOfFileData = getInt(currentOffset + 42)
|
||||
|
||||
val bytesForName = ByteArray(fileNameLength)
|
||||
|
||||
position(currentOffset + 46)
|
||||
get(bytesForName)
|
||||
|
||||
val name =
|
||||
if (bytesForName.all { it >= 0 })
|
||||
ByteArrayCharSequence(bytesForName)
|
||||
else
|
||||
String(bytesForName, Charsets.UTF_8)
|
||||
|
||||
currentOffset += 46 + fileNameLength + extraLength + fileCommentLength
|
||||
|
||||
require(versionNeededToExtract == 10 || versionNeededToExtract == 20) {
|
||||
"Unexpected versionNeededToExtract ($versionNeededToExtract) at $name"
|
||||
}
|
||||
|
||||
val compressionKind = when (compressionMethod) {
|
||||
0 -> ZipEntryDescription.CompressionKind.PLAIN
|
||||
8 -> ZipEntryDescription.CompressionKind.DEFLATE
|
||||
else -> error("Unexpected compression method ($compressionMethod) at $name")
|
||||
}
|
||||
|
||||
result += ZipEntryDescription(
|
||||
name, compressedSize, uncompressedSize, offsetOfFileData, compressionKind,
|
||||
fileNameLength
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun ByteBuffer.getUnsignedShort(offset: Int): Int = java.lang.Short.toUnsignedInt(getShort(offset))
|
||||
@@ -254,6 +254,9 @@ fun CompilerConfiguration.configureAdvancedJvmOptions(arguments: K2JVMCompilerAr
|
||||
|
||||
put(JVMConfigurationKeys.SERIALIZE_IR, arguments.serializeIr)
|
||||
|
||||
put(JVMConfigurationKeys.VALIDATE_IR, arguments.validateIr)
|
||||
put(JVMConfigurationKeys.VALIDATE_BYTECODE, arguments.validateBytecode)
|
||||
|
||||
if (!JVMConstructorCallNormalizationMode.isSupportedValue(arguments.constructorCallNormalizationMode)) {
|
||||
messageCollector.report(
|
||||
ERROR,
|
||||
@@ -280,11 +283,16 @@ fun CompilerConfiguration.configureAdvancedJvmOptions(arguments: K2JVMCompilerAr
|
||||
put(JVMConfigurationKeys.USE_TYPE_TABLE, arguments.useTypeTable)
|
||||
put(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK, arguments.skipRuntimeVersionCheck)
|
||||
put(JVMConfigurationKeys.USE_PSI_CLASS_FILES_READING, arguments.useOldClassFilesReading)
|
||||
put(JVMConfigurationKeys.USE_FAST_JAR_FILE_SYSTEM, arguments.useFastJarFileSystem)
|
||||
|
||||
if (arguments.useOldClassFilesReading) {
|
||||
messageCollector.report(INFO, "Using the old java class files reading implementation")
|
||||
}
|
||||
|
||||
if (arguments.useFastJarFileSystem) {
|
||||
messageCollector.report(INFO, "Using fast Jar FS implementation")
|
||||
}
|
||||
|
||||
put(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE, arguments.allowKotlinPackage)
|
||||
put(JVMConfigurationKeys.USE_SINGLE_MODULE, arguments.singleModule)
|
||||
put(JVMConfigurationKeys.USE_OLD_SPILLED_VAR_TYPE_ANALYSIS, arguments.useOldSpilledVarTypeAnalysis)
|
||||
|
||||
@@ -99,6 +99,9 @@ public class JVMConfigurationKeys {
|
||||
public static final CompilerConfigurationKey<Boolean> USE_PSI_CLASS_FILES_READING =
|
||||
CompilerConfigurationKey.create("use a slower (PSI-based) class files reading implementation");
|
||||
|
||||
public static final CompilerConfigurationKey<Boolean> USE_FAST_JAR_FILE_SYSTEM =
|
||||
CompilerConfigurationKey.create("use a faster JAR filesystem implementation");
|
||||
|
||||
public static final CompilerConfigurationKey<Boolean> USE_JAVAC =
|
||||
CompilerConfigurationKey.create("use javac [experimental]");
|
||||
|
||||
@@ -154,5 +157,11 @@ public class JVMConfigurationKeys {
|
||||
CompilerConfigurationKey.create("Don't automatically include kotlin-reflect.jar into the output if the output is a jar");
|
||||
|
||||
public static final CompilerConfigurationKey<Boolean> SERIALIZE_IR =
|
||||
CompilerConfigurationKey.create("serialize IR to class metadata");
|
||||
CompilerConfigurationKey.create("Serialize IR to class metadata");
|
||||
|
||||
public static final CompilerConfigurationKey<Boolean> VALIDATE_IR =
|
||||
CompilerConfigurationKey.create("Validate IR");
|
||||
|
||||
public static final CompilerConfigurationKey<Boolean> VALIDATE_BYTECODE =
|
||||
CompilerConfigurationKey.create("Validate generated JVM bytecode");
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ enum class JvmTarget(
|
||||
JVM_14("14", Opcodes.V12 + 2),
|
||||
JVM_15("15", Opcodes.V12 + 3),
|
||||
JVM_16("16", Opcodes.V12 + 4),
|
||||
JVM_17("17", Opcodes.V12 + 5),
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.config
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
fun LanguageVersionSettings.coroutinesPackageFqName(): FqName {
|
||||
return coroutinesPackageFqName(isReleaseCoroutines())
|
||||
}
|
||||
|
||||
fun LanguageVersionSettings.isReleaseCoroutines() = supportsFeature(LanguageFeature.ReleaseCoroutines)
|
||||
|
||||
private fun coroutinesPackageFqName(isReleaseCoroutines: Boolean): FqName {
|
||||
return if (isReleaseCoroutines)
|
||||
StandardNames.COROUTINES_PACKAGE_FQ_NAME_RELEASE
|
||||
else
|
||||
StandardNames.COROUTINES_PACKAGE_FQ_NAME_EXPERIMENTAL
|
||||
}
|
||||
|
||||
fun LanguageVersionSettings.coroutinesIntrinsicsPackageFqName() =
|
||||
coroutinesPackageFqName().child(Name.identifier("intrinsics"))
|
||||
|
||||
fun LanguageVersionSettings.continuationInterfaceFqName() =
|
||||
coroutinesPackageFqName().child(Name.identifier("Continuation"))
|
||||
|
||||
fun LanguageVersionSettings.restrictsSuspensionFqName() =
|
||||
coroutinesPackageFqName().child(Name.identifier("RestrictsSuspension"))
|
||||
|
||||
fun FqName.isBuiltInCoroutineContext(languageVersionSettings: LanguageVersionSettings) =
|
||||
if (languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines))
|
||||
this == StandardNames.COROUTINES_PACKAGE_FQ_NAME_RELEASE.child(Name.identifier("coroutineContext"))
|
||||
else
|
||||
this == StandardNames.COROUTINES_PACKAGE_FQ_NAME_EXPERIMENTAL.child(Name.identifier("coroutineContext")) ||
|
||||
this == StandardNames.COROUTINES_INTRINSICS_PACKAGE_FQ_NAME_EXPERIMENTAL.child(Name.identifier("coroutineContext"))
|
||||
@@ -49,7 +49,7 @@ dependencies {
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
|
||||
kotlinOptions {
|
||||
apiVersion = "1.3"
|
||||
apiVersion = "1.4"
|
||||
freeCompilerArgs += "-Xsuppress-version-warnings"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.daemon.client.experimental
|
||||
|
||||
import io.ktor.network.sockets.Socket
|
||||
import io.ktor.network.sockets.*
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
@@ -438,6 +438,7 @@ class KotlinCompilerClient : KotlinCompilerDaemonClient {
|
||||
.thenBy(FileAgeComparator()) { it.runFile }
|
||||
val optsCopy = daemonJVMOptions.copy()
|
||||
// if required options fit into fattest running daemon - return the daemon and required options with memory params set to actual ones in the daemon
|
||||
@Suppress("DEPRECATION") // TODO: replace with maxWithOrNull as soon as minimal version of Gradle that we support has Kotlin 1.4+.
|
||||
aliveWithMetadata.maxWith(comparator)
|
||||
?.takeIf { daemonJVMOptions memorywiseFitsInto it.jvmOptions }
|
||||
?.let {
|
||||
|
||||
@@ -40,8 +40,8 @@ dependencies {
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
|
||||
kotlinOptions {
|
||||
// This module is being run from within Gradle, older versions of which only have kotlin-stdlib 1.3 in the runtime classpath.
|
||||
apiVersion = "1.3"
|
||||
// This module is being run from within Gradle, older versions of which only have older kotlin-stdlib in the runtime classpath.
|
||||
apiVersion = "1.4"
|
||||
freeCompilerArgs += "-Xsuppress-version-warnings"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,6 +359,7 @@ object KotlinCompilerClient {
|
||||
.thenBy(FileAgeComparator()) { it.runFile }
|
||||
val optsCopy = daemonJVMOptions.copy()
|
||||
// if required options fit into fattest running daemon - return the daemon and required options with memory params set to actual ones in the daemon
|
||||
@Suppress("DEPRECATION") // TODO: replace with maxWithOrNull as soon as minimal version of Gradle that we support has Kotlin 1.4+.
|
||||
return aliveWithMetadata.maxWith(comparator)?.takeIf { daemonJVMOptions memorywiseFitsInto it.jvmOptions }?.let {
|
||||
Pair(it.daemon, optsCopy.updateMemoryUpperBounds(it.jvmOptions))
|
||||
}
|
||||
@@ -377,7 +378,7 @@ object KotlinCompilerClient {
|
||||
val javaVersion = CompilerSystemProperties.JAVA_VERSION.value?.toIntOrNull()
|
||||
val javaIllegalAccessWorkaround =
|
||||
if (javaVersion != null && javaVersion >= 16)
|
||||
listOf("--illegal-access=permit")
|
||||
listOf("--add-exports", "java.base/sun.nio.ch=ALL-UNNAMED")
|
||||
else emptyList()
|
||||
val args = listOf(
|
||||
javaExecutable.absolutePath, "-cp", compilerId.compilerClasspath.joinToString(File.pathSeparator)) +
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalPathApi::class)
|
||||
@file:OptIn(ExperimentalPathApi::class, DelicateCoroutinesApi::class)
|
||||
|
||||
package org.jetbrains.kotlin.daemon.experimental.integration
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.daemon.experimental.unit
|
||||
|
||||
import io.ktor.network.sockets.aSocket
|
||||
import io.ktor.util.*
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@@ -31,6 +32,7 @@ class TestServer(val serverPort: Int = 6999) {
|
||||
private val serverSocket = aSocket(selectorMgr).tcp().bind(InetSocketAddress(serverPort))
|
||||
private val log = Logger.getLogger("TestServer")
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun awaitClient() = GlobalScope.async {
|
||||
log.info("accepting clientSocket...")
|
||||
val client = serverSocket.accept()
|
||||
@@ -61,7 +63,7 @@ class ClientSerializationTest : KotlinIntegrationTestBase() {
|
||||
}
|
||||
}
|
||||
log.info("printed")
|
||||
var client2: T? = null
|
||||
var client2: T?
|
||||
var connected = false
|
||||
runBlocking {
|
||||
val clientAwait = testServer.awaitClient()
|
||||
|
||||
@@ -21,9 +21,7 @@ import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.vfs.impl.ZipHandler
|
||||
import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem
|
||||
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
|
||||
import org.jetbrains.kotlin.build.report.BuildReporter
|
||||
import org.jetbrains.kotlin.build.report.RemoteBuildReporter
|
||||
import org.jetbrains.kotlin.build.report.RemoteReporter
|
||||
import org.jetbrains.kotlin.cli.common.CLICompiler
|
||||
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||
@@ -37,10 +35,15 @@ import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
|
||||
import org.jetbrains.kotlin.cli.js.K2JSCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.jarfs.FastJarFileSystem
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.jarfs.FastJarHandler
|
||||
import org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler
|
||||
import org.jetbrains.kotlin.config.Services
|
||||
import org.jetbrains.kotlin.daemon.common.*
|
||||
import org.jetbrains.kotlin.daemon.report.*
|
||||
import org.jetbrains.kotlin.daemon.report.CompileServicesFacadeMessageCollector
|
||||
import org.jetbrains.kotlin.daemon.report.DaemonMessageReporter
|
||||
import org.jetbrains.kotlin.daemon.report.DaemonMessageReporterPrintStreamAdapter
|
||||
import org.jetbrains.kotlin.daemon.report.getBuildReporter
|
||||
import org.jetbrains.kotlin.incremental.*
|
||||
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
|
||||
@@ -66,7 +66,8 @@ abstract class KotlinJvmReplServiceBase(
|
||||
val projectEnvironment =
|
||||
KotlinCoreEnvironment.ProjectEnvironment(
|
||||
disposable,
|
||||
KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForProduction(disposable, configuration)
|
||||
KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForProduction(disposable, configuration),
|
||||
configuration,
|
||||
)
|
||||
ReplFactoryExtension.registerExtensionPoint(projectEnvironment.project)
|
||||
projectEnvironment.registerExtensionsFromPlugins(configuration)
|
||||
@@ -223,4 +224,4 @@ fun CompilerConfiguration.configureScripting(compilerId: CompilerId) {
|
||||
error
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult
|
||||
import org.jetbrains.kotlin.cli.js.K2JSCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.jarfs.FastJarFileSystem
|
||||
import org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler
|
||||
import org.jetbrains.kotlin.config.Services
|
||||
import org.jetbrains.kotlin.daemon.CompileServiceImplBase
|
||||
|
||||
@@ -36,12 +36,7 @@ dependencies {
|
||||
includeJars("jna", rootProject = rootProject)
|
||||
}
|
||||
|
||||
Platform[202] {
|
||||
testRuntimeOnly(intellijDep()) { includeJars("intellij-deps-fastutil-8.3.1-1") }
|
||||
}
|
||||
Platform[203].orHigher {
|
||||
testRuntimeOnly(intellijDep()) { includeJars("intellij-deps-fastutil-8.4.1-4") }
|
||||
}
|
||||
testRuntimeOnly(intellijDep()) { includeJars("intellij-deps-fastutil-8.4.1-4") }
|
||||
testRuntimeOnly(toolsJar())
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("annotationOnDeclarationWithDifferentArguments.kt")
|
||||
public void testAnnotationOnDeclarationWithDifferentArguments() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/annotationOnDeclarationWithDifferentArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("annotationUsedAsAnnotationArgument.kt")
|
||||
public void testAnnotationUsedAsAnnotationArgument() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/annotationUsedAsAnnotationArgument.kt");
|
||||
@@ -64,6 +69,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/catchParameter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("classCallInLambda.kt")
|
||||
public void testClassCallInLambda() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/classCallInLambda.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("companion.kt")
|
||||
public void testCompanion() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/companion.kt");
|
||||
@@ -611,6 +621,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/arguments/namedArrayInAnnotation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedClassInAnnotationArgument.kt")
|
||||
public void testNestedClassInAnnotationArgument() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/arguments/nestedClassInAnnotationArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noParameterForName.kt")
|
||||
public void testNoParameterForName() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/arguments/noParameterForName.kt");
|
||||
@@ -2575,6 +2590,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/localClasses"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("anonymousInAnonymous.kt")
|
||||
public void testAnonymousInAnonymous() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/localClasses/anonymousInAnonymous.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitInAnonymous.kt")
|
||||
public void testImplicitInAnonymous() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/localClasses/implicitInAnonymous.kt");
|
||||
@@ -2681,6 +2701,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/overrides/protobufExt.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("sameValueParametersDifferentReceiver.kt")
|
||||
public void testSameValueParametersDifferentReceiver() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/overrides/sameValueParametersDifferentReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/overrides/simple.kt");
|
||||
@@ -3044,6 +3069,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/equalsAndIdentity.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("incorrectSmartcastToNothing.kt")
|
||||
public void testIncorrectSmartcastToNothing() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/incorrectSmartcastToNothing.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt10240.kt")
|
||||
public void testKt10240() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/kt10240.kt");
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
FILE: annotationOnDeclarationWithDifferentArguments.kt
|
||||
public final enum class SomeEnum : R|kotlin/Enum<SomeEnum>| {
|
||||
private constructor(): R|SomeEnum| {
|
||||
super<R|kotlin/Enum<SomeEnum>|>()
|
||||
}
|
||||
|
||||
public final static enum entry A: R|SomeEnum|
|
||||
public final static enum entry B: R|SomeEnum|
|
||||
public final static fun values(): R|kotlin/Array<SomeEnum>| {
|
||||
}
|
||||
|
||||
public final static fun valueOf(value: R|kotlin/String|): R|SomeEnum| {
|
||||
}
|
||||
|
||||
}
|
||||
public final annotation class MyAnnotation : R|kotlin/Annotation| {
|
||||
public constructor(intValue: R|kotlin/Int|, stringValue: R|kotlin/String|, enumValue: R|SomeEnum|, kClasses: R|kotlin/Array<out kotlin/reflect/KClass<*>>|, annotation: R|MyOtherAnnotation|): R|MyAnnotation| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final val intValue: R|kotlin/Int| = R|<local>/intValue|
|
||||
public get(): R|kotlin/Int|
|
||||
|
||||
public final val stringValue: R|kotlin/String| = R|<local>/stringValue|
|
||||
public get(): R|kotlin/String|
|
||||
|
||||
public final val enumValue: R|SomeEnum| = R|<local>/enumValue|
|
||||
public get(): R|SomeEnum|
|
||||
|
||||
public final val kClasses: R|kotlin/Array<out kotlin/reflect/KClass<*>>| = R|<local>/kClasses|
|
||||
public get(): R|kotlin/Array<out kotlin/reflect/KClass<*>>|
|
||||
|
||||
public final val annotation: R|MyOtherAnnotation| = R|<local>/annotation|
|
||||
public get(): R|MyOtherAnnotation|
|
||||
|
||||
}
|
||||
public final annotation class MyOtherAnnotation : R|kotlin/Annotation| {
|
||||
public constructor(intValue: R|kotlin/Int|, stringValue: R|kotlin/String|): R|MyOtherAnnotation| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final val intValue: R|kotlin/Int| = R|<local>/intValue|
|
||||
public get(): R|kotlin/Int|
|
||||
|
||||
public final val stringValue: R|kotlin/String| = R|<local>/stringValue|
|
||||
public get(): R|kotlin/String|
|
||||
|
||||
}
|
||||
public final const val constInt: R|kotlin/Int| = Int(10)
|
||||
public get(): R|kotlin/Int|
|
||||
public final const val constString: R|kotlin/String| = String()
|
||||
public get(): R|kotlin/String|
|
||||
@R|MyAnnotation|(intValue = Int(10), stringValue = R|/constString|, enumValue = Q|SomeEnum|.R|/SomeEnum.A|, kClasses = <implicitArrayOf>(<getClass>(Q|kotlin/String|), <getClass>(R|/constString|)), annotation = R|/MyOtherAnnotation.MyOtherAnnotation|(intValue = R|/constInt|, stringValue = String(hello))) public final fun foo(): R|kotlin/Unit| {
|
||||
}
|
||||
30
compiler/fir/analysis-tests/testData/resolve/annotationOnDeclarationWithDifferentArguments.kt
vendored
Normal file
30
compiler/fir/analysis-tests/testData/resolve/annotationOnDeclarationWithDifferentArguments.kt
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
enum class SomeEnum {
|
||||
A, B
|
||||
}
|
||||
|
||||
annotation class MyAnnotation(
|
||||
val intValue: Int,
|
||||
val stringValue: String,
|
||||
val enumValue: SomeEnum,
|
||||
val kClasses: Array<out KClass<*>>,
|
||||
val annotation: MyOtherAnnotation
|
||||
)
|
||||
annotation class MyOtherAnnotation(val intValue: Int, val stringValue: String)
|
||||
|
||||
const val constInt = 10
|
||||
const val constString = ""
|
||||
|
||||
@MyAnnotation(
|
||||
intValue = 10,
|
||||
stringValue = constString,
|
||||
enumValue = SomeEnum.A,
|
||||
kClasses = [String::class, <!ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL!>constString::class<!>],
|
||||
|
||||
annotation = MyOtherAnnotation(
|
||||
intValue = constInt,
|
||||
stringValue = "hello"
|
||||
)
|
||||
)
|
||||
fun foo() {}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user