mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-20 08:31:28 +00:00
Compare commits
775 Commits
rr/faster_
...
jupiter/ma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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-1728"
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1288,8 +1288,8 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
||||
fun nextLabel(node: AbstractInsnNode?): LabelNode? {
|
||||
var current = node
|
||||
while (current != null) {
|
||||
if (current is LabelNode) return current
|
||||
current = current.next
|
||||
if (current is LabelNode) return current as LabelNode
|
||||
current = current!!.next
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -1302,6 +1302,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 +1343,41 @@ 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 recordToExtend: LocalVariableNode? = oldLvtNodeToLatestNewLvtNode[lvtRecord]
|
||||
var recordExtended = false
|
||||
if (recordToExtend != null) {
|
||||
var hasBackEdgeOrStore = false
|
||||
var current: AbstractInsnNode? = recordToExtend.end
|
||||
while (current != null && current != endLabel) {
|
||||
if (current is JumpInsnNode) {
|
||||
if (method.instructions.indexOf((current as JumpInsnNode).label) < method.instructions.indexOf(current)) {
|
||||
hasBackEdgeOrStore = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (current!!.isStoreOperation() && (current as VarInsnNode).`var` == recordToExtend.index) {
|
||||
hasBackEdgeOrStore = true
|
||||
break
|
||||
}
|
||||
current = current!!.next
|
||||
}
|
||||
if (!hasBackEdgeOrStore) {
|
||||
recordToExtend.end = endLabel
|
||||
recordExtended = true
|
||||
}
|
||||
}
|
||||
if (!recordExtended) {
|
||||
val node = LocalVariableNode(lvtRecord.name, lvtRecord.desc, lvtRecord.signature, startLabel, endLabel, lvtRecord.index)
|
||||
method.localVariables.add(node)
|
||||
oldLvtNodeToLatestNewLvtNode[lvtRecord] = node
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
@@ -73,8 +73,8 @@ import kotlin.math.max
|
||||
* - restore constructor arguments
|
||||
*/
|
||||
class UninitializedStoresProcessor(
|
||||
private val methodNode: MethodNode,
|
||||
private val shouldPreserveClassInitialization: Boolean
|
||||
private val methodNode: MethodNode,
|
||||
private val shouldPreserveClassInitialization: Boolean
|
||||
) {
|
||||
// <init> method is "special", because it will invoke <init> from this class or from a base class for #0
|
||||
//
|
||||
@@ -87,10 +87,10 @@ class UninitializedStoresProcessor(
|
||||
fun run() {
|
||||
val interpreter = UninitializedNewValueMarkerInterpreter(methodNode.instructions)
|
||||
|
||||
val frames = CustomFramesMethodAnalyzer(
|
||||
"fake", methodNode, interpreter,
|
||||
this::UninitializedNewValueFrame
|
||||
).analyze()
|
||||
if (methodNode.instructions.toArray().none { it.opcode == Opcodes.NEW })
|
||||
return
|
||||
|
||||
val frames = CustomFramesMethodAnalyzer("fake", methodNode, interpreter, this::UninitializedNewValueFrame).analyze()
|
||||
|
||||
interpreter.analyzePopInstructions(frames)
|
||||
|
||||
@@ -115,12 +115,12 @@ class UninitializedStoresProcessor(
|
||||
// POP
|
||||
val typeNameForClass = newInsn.desc.replace('/', '.')
|
||||
insertBefore(newInsn, LdcInsnNode(typeNameForClass))
|
||||
insertBefore(newInsn, MethodInsnNode(
|
||||
Opcodes.INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false
|
||||
))
|
||||
insertBefore(
|
||||
newInsn,
|
||||
MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false)
|
||||
)
|
||||
set(newInsn, InsnNode(Opcodes.POP))
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
remove(newInsn)
|
||||
}
|
||||
}
|
||||
@@ -138,10 +138,7 @@ class UninitializedStoresProcessor(
|
||||
}
|
||||
methodNode.maxLocals = max(methodNode.maxLocals, nextVarIndex)
|
||||
|
||||
methodNode.instructions.insertBefore(insn, insnListOf(
|
||||
TypeInsnNode(Opcodes.NEW, newInsn.desc),
|
||||
InsnNode(Opcodes.DUP)
|
||||
))
|
||||
methodNode.instructions.insertBefore(insn, insnListOf(TypeInsnNode(Opcodes.NEW, newInsn.desc), InsnNode(Opcodes.DUP)))
|
||||
|
||||
for (type in storedTypes.reversed()) {
|
||||
nextVarIndex -= type.size
|
||||
@@ -174,11 +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"
|
||||
@@ -188,8 +185,8 @@ class UninitializedStoresProcessor(
|
||||
}
|
||||
|
||||
private class UninitializedNewValue(
|
||||
val newInsn: TypeInsnNode,
|
||||
val internalName: String
|
||||
val newInsn: TypeInsnNode,
|
||||
val internalName: String
|
||||
) : StrictBasicValue(Type.getObjectType(internalName)) {
|
||||
override fun toString() = "UninitializedNewValue(internalName='$internalName')"
|
||||
}
|
||||
@@ -236,7 +233,8 @@ class UninitializedStoresProcessor(
|
||||
|
||||
private fun checkUninitializedObjectCopy(newInsn: TypeInsnNode, usageInsn: AbstractInsnNode) {
|
||||
when (usageInsn.opcode) {
|
||||
Opcodes.DUP, Opcodes.ASTORE, Opcodes.ALOAD -> {}
|
||||
Opcodes.DUP, Opcodes.ASTORE, Opcodes.ALOAD -> {
|
||||
}
|
||||
else -> error("Unexpected copy instruction for ${newInsn.debugText}: ${usageInsn.debugText}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,12 +13,8 @@ import org.jetbrains.kotlin.codegen.inline.coroutines.markNoinlineLambdaIfSuspen
|
||||
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.fixStack.peek
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.isCheckParameterIsNotNull
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
@@ -36,8 +32,6 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import org.jetbrains.org.objectweb.asm.commons.LocalVariablesSorter
|
||||
import org.jetbrains.org.objectweb.asm.commons.MethodRemapper
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicInterpreter
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||
import org.jetbrains.org.objectweb.asm.util.Printer
|
||||
import java.util.*
|
||||
@@ -57,8 +51,10 @@ class MethodInliner(
|
||||
) {
|
||||
private val languageVersionSettings = inliningContext.state.languageVersionSettings
|
||||
private val invokeCalls = ArrayList<InvokeCall>()
|
||||
|
||||
//keeps order
|
||||
private val transformations = ArrayList<TransformationInfo>()
|
||||
|
||||
//current state
|
||||
private val currentTypeMapping = HashMap<String, String?>()
|
||||
private val result = InlineResult.create()
|
||||
@@ -273,7 +269,8 @@ class MethodInliner(
|
||||
|
||||
val varRemapper = LocalVarRemapper(lambdaParameters, valueParamShift)
|
||||
//TODO add skipped this and receiver
|
||||
val lambdaResult = inliner.doInline(localVariablesSorter, varRemapper, true, info.returnLabels, invokeCall.finallyDepthShift)
|
||||
val lambdaResult =
|
||||
inliner.doInline(localVariablesSorter, varRemapper, true, info.returnLabels, invokeCall.finallyDepthShift)
|
||||
result.mergeWithNotChangeInfo(lambdaResult)
|
||||
result.reifiedTypeParametersUsages.mergeAll(lambdaResult.reifiedTypeParametersUsages)
|
||||
result.reifiedTypeParametersUsages.mergeAll(info.reifiedTypeParametersUsages)
|
||||
@@ -303,7 +300,7 @@ class MethodInliner(
|
||||
} else capturedParamDesc
|
||||
visitFieldInsn(
|
||||
Opcodes.GETSTATIC, realDesc.containingLambdaName,
|
||||
FieldRemapper.foldName(realDesc.fieldName), realDesc.type.descriptor
|
||||
foldName(realDesc.fieldName), realDesc.type.descriptor
|
||||
)
|
||||
}
|
||||
super.visitMethodInsn(opcode, info.newClassName, name, info.newConstructorDescriptor, itf)
|
||||
@@ -441,7 +438,7 @@ class MethodInliner(
|
||||
else -> ""
|
||||
}
|
||||
|
||||
val varName = if (!varSuffix.isEmpty() && name == AsmUtil.THIS) AsmUtil.INLINE_DECLARATION_SITE_THIS else name
|
||||
val varName = if (varSuffix.isNotEmpty() && name == AsmUtil.THIS) AsmUtil.INLINE_DECLARATION_SITE_THIS else name
|
||||
super.visitLocalVariable(varName + varSuffix, desc, signature, start, end, getNewIndex(index))
|
||||
}
|
||||
}
|
||||
@@ -533,33 +530,37 @@ class MethodInliner(
|
||||
cur.opcode == Opcodes.GETSTATIC -> {
|
||||
val fieldInsnNode = cur as FieldInsnNode?
|
||||
val className = fieldInsnNode!!.owner
|
||||
if (isAnonymousSingletonLoad(className, fieldInsnNode.name)) {
|
||||
recordTransformation(
|
||||
AnonymousObjectTransformationInfo(
|
||||
className, awaitClassReification, isAlreadyRegenerated(className), true,
|
||||
inliningContext.nameGenerator
|
||||
when {
|
||||
isAnonymousSingletonLoad(className, fieldInsnNode.name) -> {
|
||||
recordTransformation(
|
||||
AnonymousObjectTransformationInfo(
|
||||
className, awaitClassReification, isAlreadyRegenerated(className), true,
|
||||
inliningContext.nameGenerator
|
||||
)
|
||||
)
|
||||
)
|
||||
awaitClassReification = false
|
||||
} else if (isWhenMappingAccess(className, fieldInsnNode.name)) {
|
||||
recordTransformation(
|
||||
WhenMappingTransformationInfo(
|
||||
className, inliningContext.nameGenerator, isAlreadyRegenerated(className), fieldInsnNode
|
||||
awaitClassReification = false
|
||||
}
|
||||
isWhenMappingAccess(className, fieldInsnNode.name) -> {
|
||||
recordTransformation(
|
||||
WhenMappingTransformationInfo(
|
||||
className, inliningContext.nameGenerator, isAlreadyRegenerated(className), fieldInsnNode
|
||||
)
|
||||
)
|
||||
)
|
||||
} else if (fieldInsnNode.isCheckAssertionsStatus()) {
|
||||
fieldInsnNode.owner = inlineCallSiteInfo.ownerClassName
|
||||
when {
|
||||
// In inline function itself:
|
||||
inliningContext.parent == null -> inliningContext
|
||||
// In method of regenerated object - field should already exist:
|
||||
inliningContext.parent is RegeneratedClassContext -> inliningContext.parent
|
||||
// In lambda inlined into the root function:
|
||||
inliningContext.parent.parent == null -> inliningContext.parent
|
||||
// In lambda inlined into a method of a regenerated object:
|
||||
else -> inliningContext.parent.parent as? RegeneratedClassContext
|
||||
?: throw AssertionError("couldn't find class for \$assertionsDisabled (context = $inliningContext)")
|
||||
}.generateAssertField = true
|
||||
}
|
||||
fieldInsnNode.isCheckAssertionsStatus() -> {
|
||||
fieldInsnNode.owner = inlineCallSiteInfo.ownerClassName
|
||||
when {
|
||||
// In inline function itself:
|
||||
inliningContext.parent == null -> inliningContext
|
||||
// In method of regenerated object - field should already exist:
|
||||
inliningContext.parent is RegeneratedClassContext -> inliningContext.parent
|
||||
// In lambda inlined into the root function:
|
||||
inliningContext.parent.parent == null -> inliningContext.parent
|
||||
// In lambda inlined into a method of a regenerated object:
|
||||
else -> inliningContext.parent.parent as? RegeneratedClassContext
|
||||
?: throw AssertionError("couldn't find class for \$assertionsDisabled (context = $inliningContext)")
|
||||
}.generateAssertField = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -588,7 +589,7 @@ class MethodInliner(
|
||||
assert(lambdaInfo.lambdaClassType.internalName == nodeRemapper.originalLambdaInternalName) {
|
||||
"Wrong bytecode template for contract template: ${lambdaInfo.lambdaClassType.internalName} != ${nodeRemapper.originalLambdaInternalName}"
|
||||
}
|
||||
fieldInsn.name = FieldRemapper.foldName(fieldInsn.name)
|
||||
fieldInsn.name = foldName(fieldInsn.name)
|
||||
fieldInsn.opcode = Opcodes.PUTSTATIC
|
||||
toDelete.addAll(stackTransformations)
|
||||
}
|
||||
@@ -732,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")
|
||||
@@ -742,7 +744,9 @@ class MethodInliner(
|
||||
ApiVersionCallsPreprocessingMethodTransformer(targetApiVersion).transform("fake", node)
|
||||
}
|
||||
|
||||
val frames = analyzeMethodNodeWithInterpreter(node, BasicInterpreter())
|
||||
removeFakeVariablesInitializationIfPresent(node)
|
||||
|
||||
val frames = FastStackAnalyzer("<fake>", node, FixStackInterpreter()).analyze()
|
||||
|
||||
val localReturnsNormalizer = LocalReturnsNormalizer()
|
||||
|
||||
@@ -764,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)
|
||||
@@ -831,9 +902,9 @@ class MethodInliner(
|
||||
if (inliningContext.isInliningLambda && inliningContext.lambdaInfo is IrExpressionLambda && !inliningContext.parent!!.isInliningLambda) {
|
||||
val capturedVars = inliningContext.lambdaInfo.capturedVars
|
||||
var offset = parameters.realParametersSizeOnStack
|
||||
val map = capturedVars.map {
|
||||
val map = capturedVars.associate {
|
||||
offset to it.also { offset += it.type.size }
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
var cur: AbstractInsnNode? = node.instructions.first
|
||||
while (cur != null) {
|
||||
@@ -878,6 +949,7 @@ class MethodInliner(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("SameParameterValue")
|
||||
private fun wrapException(originalException: Throwable, node: MethodNode, errorSuffix: String): RuntimeException {
|
||||
return if (originalException is InlineException) {
|
||||
InlineException("$errorPrefix: $errorSuffix", originalException)
|
||||
@@ -890,7 +962,7 @@ class MethodInliner(
|
||||
private class LocalReturn(
|
||||
private val returnInsn: AbstractInsnNode,
|
||||
private val insertBeforeInsn: AbstractInsnNode,
|
||||
private val frame: Frame<BasicValue>
|
||||
private val frame: Frame<FixStackValue>
|
||||
) {
|
||||
|
||||
fun transform(insnList: InsnList, returnVariableIndex: Int) {
|
||||
@@ -901,22 +973,19 @@ class MethodInliner(
|
||||
if (expectedStackSize == actualStackSize) return
|
||||
|
||||
var stackSize = actualStackSize
|
||||
val topValue = frame.getStack(stackSize - 1)
|
||||
if (isReturnWithValue) {
|
||||
val storeOpcode = Opcodes.ISTORE + returnInsn.opcode - Opcodes.IRETURN
|
||||
insnList.insertBefore(insertBeforeInsn, VarInsnNode(storeOpcode, returnVariableIndex))
|
||||
insnList.insertBefore(insertBeforeInsn, VarInsnNode(topValue.storeOpcode, returnVariableIndex))
|
||||
stackSize--
|
||||
}
|
||||
|
||||
while (stackSize > 0) {
|
||||
val stackElementSize = frame.getStack(stackSize - 1).size
|
||||
val popOpcode = if (stackElementSize == 1) Opcodes.POP else Opcodes.POP2
|
||||
insnList.insertBefore(insertBeforeInsn, InsnNode(popOpcode))
|
||||
insnList.insertBefore(insertBeforeInsn, InsnNode(frame.getStack(stackSize - 1).popOpcode))
|
||||
stackSize--
|
||||
}
|
||||
|
||||
if (isReturnWithValue) {
|
||||
val loadOpcode = Opcodes.ILOAD + returnInsn.opcode - Opcodes.IRETURN
|
||||
insnList.insertBefore(insertBeforeInsn, VarInsnNode(loadOpcode, returnVariableIndex))
|
||||
insnList.insertBefore(insertBeforeInsn, VarInsnNode(topValue.loadOpcode, returnVariableIndex))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -926,10 +995,10 @@ class MethodInliner(
|
||||
private var returnVariableSize = 0
|
||||
private var returnOpcode = -1
|
||||
|
||||
internal fun addLocalReturnToTransform(
|
||||
fun addLocalReturnToTransform(
|
||||
returnInsn: AbstractInsnNode,
|
||||
insertBeforeInsn: AbstractInsnNode,
|
||||
sourceValueFrame: Frame<BasicValue>
|
||||
sourceValueFrame: Frame<FixStackValue>
|
||||
) {
|
||||
assert(isReturnOpcode(returnInsn.opcode)) { "return instruction expected" }
|
||||
assert(returnOpcode < 0 || returnOpcode == returnInsn.opcode) { "Return op should be " + Printer.OPCODES[returnOpcode] + ", got " + Printer.OPCODES[returnInsn.opcode] }
|
||||
@@ -938,11 +1007,7 @@ class MethodInliner(
|
||||
localReturns.add(LocalReturn(returnInsn, insertBeforeInsn, sourceValueFrame))
|
||||
|
||||
if (returnInsn.opcode != Opcodes.RETURN) {
|
||||
returnVariableSize = if (returnInsn.opcode == Opcodes.LRETURN || returnInsn.opcode == Opcodes.DRETURN) {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
}
|
||||
returnVariableSize = if (returnInsn.opcode == Opcodes.LRETURN || returnInsn.opcode == Opcodes.DRETURN) 2 else 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -23,13 +23,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
|
||||
|
||||
@@ -75,6 +75,7 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
|
||||
|
||||
fun toKotlinType(type: KT): KotlinType
|
||||
|
||||
fun checkAnnotatedType(type: KT)
|
||||
fun reportSuspendTypeUnsupported()
|
||||
fun reportNonReifiedTypeParameterWithRecursiveBoundUnsupported(typeParameterName: Name)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
@@ -73,24 +75,29 @@ class OptimizationMethodVisitor(
|
||||
|
||||
companion object {
|
||||
private const val MEMORY_LIMIT_BY_METHOD_MB = 50
|
||||
private const val TRY_CATCH_BLOCKS_LIMIT = 512
|
||||
private const val TRY_CATCH_BLOCKS_SOFT_LIMIT = 16
|
||||
|
||||
fun canBeOptimized(node: MethodNode): Boolean {
|
||||
if (node.tryCatchBlocks.size > TRY_CATCH_BLOCKS_LIMIT)
|
||||
return false
|
||||
|
||||
val totalFramesSizeMb = node.instructions.size() * (node.maxLocals + node.maxStack) / (1024 * 1024)
|
||||
return totalFramesSizeMb < MEMORY_LIMIT_BY_METHOD_MB
|
||||
if (node.tryCatchBlocks.size > TRY_CATCH_BLOCKS_SOFT_LIMIT) {
|
||||
if (getTotalFramesWeight(getTotalTcbSize(node), node) > MEMORY_LIMIT_BY_METHOD_MB)
|
||||
return false
|
||||
}
|
||||
return getTotalFramesWeight(node.instructions.size(), node) < MEMORY_LIMIT_BY_METHOD_MB
|
||||
}
|
||||
|
||||
fun canBeOptimizedUsingSourceInterpreter(node: MethodNode): Boolean {
|
||||
if (node.tryCatchBlocks.size > TRY_CATCH_BLOCKS_LIMIT)
|
||||
return false
|
||||
|
||||
val frameSize = node.maxLocals + node.maxStack
|
||||
val methodSize = node.instructions.size().toLong()
|
||||
val totalFramesSizeMb = methodSize * methodSize * frameSize / (1024 * 1024)
|
||||
return totalFramesSizeMb < MEMORY_LIMIT_BY_METHOD_MB
|
||||
val methodSize = node.instructions.size()
|
||||
if (node.tryCatchBlocks.size > TRY_CATCH_BLOCKS_SOFT_LIMIT) {
|
||||
if (getTotalFramesWeight(getTotalTcbSize(node) * methodSize, node) > MEMORY_LIMIT_BY_METHOD_MB)
|
||||
return false
|
||||
}
|
||||
return getTotalFramesWeight(methodSize * methodSize, node) < MEMORY_LIMIT_BY_METHOD_MB
|
||||
}
|
||||
|
||||
private fun getTotalFramesWeight(size: Int, node: MethodNode) =
|
||||
size.toLong() * (node.maxLocals + node.maxStack) / (1024 * 1024)
|
||||
|
||||
private fun getTotalTcbSize(node: MethodNode) =
|
||||
node.tryCatchBlocks.sumOf { node.instructions.indexOf(it.end) - node.instructions.indexOf(it.start) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ class TaintedBoxedValue(private val boxedBasicValue: CleanBoxedValue) : BoxedBas
|
||||
|
||||
|
||||
class BoxedValueDescriptor(
|
||||
private val boxedType: Type,
|
||||
boxedType: Type,
|
||||
val boxingInsn: AbstractInsnNode,
|
||||
val progressionIterator: ProgressionIteratorBasicValue?,
|
||||
val generationState: GenerationState
|
||||
|
||||
@@ -38,6 +38,10 @@ import java.util.*
|
||||
class RedundantBoxingMethodTransformer(private val generationState: GenerationState) : MethodTransformer() {
|
||||
|
||||
override fun transform(internalClassName: String, node: MethodNode) {
|
||||
val insns = node.instructions.toArray()
|
||||
if (insns.none { it.isBoxing(generationState) || it.isMethodInsnWith(Opcodes.INVOKEINTERFACE) { name == "next" } })
|
||||
return
|
||||
|
||||
val interpreter = RedundantBoxingInterpreter(node.instructions, generationState)
|
||||
val frames = analyze(internalClassName, node, interpreter)
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -45,7 +44,7 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Value
|
||||
|
||||
/**
|
||||
* @see org.jetbrains.kotlin.codegen.optimization.fixStack.HackedFixStackMethodAnalyzerBase
|
||||
* @see org.jetbrains.kotlin.codegen.optimization.fixStack.FastStackAnalyzer
|
||||
*/
|
||||
@Suppress("DuplicatedCode")
|
||||
open class FastMethodAnalyzer<V : Value>(
|
||||
@@ -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() =
|
||||
|
||||
@@ -46,7 +46,7 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Value
|
||||
* @see org.jetbrains.kotlin.codegen.optimization.common.FastMethodAnalyzer
|
||||
*/
|
||||
@Suppress("DuplicatedCode")
|
||||
internal open class HackedFixStackMethodAnalyzerBase<V : Value>(
|
||||
internal open class FastStackAnalyzer<V : Value>(
|
||||
private val owner: String,
|
||||
val method: MethodNode,
|
||||
protected val interpreter: Interpreter<V>
|
||||
@@ -61,8 +61,6 @@ internal open class HackedFixStackMethodAnalyzerBase<V : Value>(
|
||||
private val queue = IntArray(nInsns)
|
||||
private var top = 0
|
||||
|
||||
private val singlePredBlock = IntArray(nInsns)
|
||||
|
||||
protected open fun newFrame(nLocals: Int, nStack: Int): Frame<V> = Frame(nLocals, nStack)
|
||||
|
||||
protected open fun visitControlFlowEdge(insn: Int, successor: Int): Boolean = true
|
||||
@@ -79,8 +77,6 @@ internal open class HackedFixStackMethodAnalyzerBase<V : Value>(
|
||||
|
||||
computeExceptionEdges()
|
||||
|
||||
initSinglePredBlocks()
|
||||
|
||||
val current = newFrame(method.maxLocals, method.maxStack)
|
||||
val handler = newFrame(method.maxLocals, method.maxStack)
|
||||
initControlFlowAnalysis(current, method, owner)
|
||||
@@ -98,15 +94,19 @@ internal open class HackedFixStackMethodAnalyzerBase<V : Value>(
|
||||
if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) {
|
||||
visitNopInsn(f, insn)
|
||||
} else {
|
||||
current.init(f).execute(insnNode, interpreter)
|
||||
current.init(f)
|
||||
if (insnOpcode != Opcodes.RETURN) {
|
||||
// Don't care about possibly incompatible return type
|
||||
current.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, insn)
|
||||
insnType == AbstractInsnNode.TABLESWITCH_INSN ->
|
||||
visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current, insn)
|
||||
insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) ->
|
||||
visitOpInsn(current, insn)
|
||||
else -> {
|
||||
@@ -136,85 +136,6 @@ internal open class HackedFixStackMethodAnalyzerBase<V : Value>(
|
||||
return frames
|
||||
}
|
||||
|
||||
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.
|
||||
for (insn in insnsArray) {
|
||||
when (insn) {
|
||||
is JumpInsnNode -> {
|
||||
val labelIndex = insn.label.indexOf()
|
||||
if (singlePredBlock[labelIndex] == 0) {
|
||||
singlePredBlock[labelIndex] = ++blockId
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.indexOf() = method.instructions.indexOf(this)
|
||||
|
||||
fun getFrame(insn: AbstractInsnNode): Frame<V>? =
|
||||
@@ -253,7 +174,7 @@ internal open class HackedFixStackMethodAnalyzerBase<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()
|
||||
@@ -90,7 +90,7 @@ internal class FixStackAnalyzer(
|
||||
private val analyzer = InternalAnalyzer(owner)
|
||||
|
||||
private inner class InternalAnalyzer(owner: String) :
|
||||
HackedFixStackMethodAnalyzerBase<FixStackValue>(owner, method, FixStackInterpreter()) {
|
||||
FastStackAnalyzer<FixStackValue>(owner, method, FixStackInterpreter()) {
|
||||
|
||||
val spilledStacks = hashMapOf<AbstractInsnNode, List<FixStackValue>>()
|
||||
var maxExtraStackSize = 0; private set
|
||||
|
||||
@@ -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() =
|
||||
|
||||
@@ -299,6 +299,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 +407,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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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() {}
|
||||
26
compiler/fir/analysis-tests/testData/resolve/arguments/nestedClassInAnnotationArgument.fir.txt
vendored
Normal file
26
compiler/fir/analysis-tests/testData/resolve/arguments/nestedClassInAnnotationArgument.fir.txt
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
FILE: nestedClassInAnnotationArgument.kt
|
||||
public final annotation class Ann : R|kotlin/Annotation| {
|
||||
public constructor(kClass: R|kotlin/reflect/KClass<*>|): R|Ann| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final val kClass: R|kotlin/reflect/KClass<*>| = R|<local>/kClass|
|
||||
public get(): R|kotlin/reflect/KClass<*>|
|
||||
|
||||
}
|
||||
public final class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
@R|Ann|(<getClass>(Q|A.EmptyList|)) public final fun foo(): R|kotlin/Unit| {
|
||||
}
|
||||
|
||||
public final object EmptyList : R|kotlin/Any| {
|
||||
private constructor(): R|A.EmptyList| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
11
compiler/fir/analysis-tests/testData/resolve/arguments/nestedClassInAnnotationArgument.kt
vendored
Normal file
11
compiler/fir/analysis-tests/testData/resolve/arguments/nestedClassInAnnotationArgument.kt
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// WITH_STDLIB
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
annotation class Ann(val kClass: KClass<*>)
|
||||
|
||||
class A {
|
||||
@Ann(EmptyList::class)
|
||||
fun foo() {}
|
||||
|
||||
object EmptyList
|
||||
}
|
||||
@@ -7,7 +7,7 @@ FILE: typeAliasWithNotNullBound.kt
|
||||
}
|
||||
public final typealias MyAlias = R|Inv<out kotlin/CharSequence>|
|
||||
public final fun foo(p: R|MyAlias|): R|kotlin/Unit| {
|
||||
R|/bar|<R|kotlin/CharSequence|>(R|<local>/p|).R|kotlin/CharSequence.length|
|
||||
R|/bar|<R|CapturedType(out kotlin/CharSequence)|>(R|<local>/p|).R|kotlin/CharSequence.length|
|
||||
}
|
||||
public final fun <T : R|kotlin/Any|> bar(x: R|Inv<T>|): R|T| {
|
||||
^bar R|kotlin/TODO|()
|
||||
|
||||
@@ -13,12 +13,12 @@ FILE: cast.kt
|
||||
}
|
||||
|
||||
public get(): R|() -> kotlin/Unit|
|
||||
public final val h: R|(kotlin/String) -> kotlin/Boolean| = fun <anonymous>(_: R|kotlin/String|): R|kotlin/Boolean| <inline=Unknown> {
|
||||
public final val h: R|(kotlin/String) -> kotlin/Boolean| = fun <anonymous>(<unused var>: R|kotlin/String|): R|kotlin/Boolean| <inline=Unknown> {
|
||||
^ Boolean(false)
|
||||
}
|
||||
|
||||
public get(): R|(kotlin/String) -> kotlin/Boolean|
|
||||
public final val hError: R|(ERROR CLASS: No type for parameter) -> kotlin/Boolean| = fun <anonymous>(_: <ERROR TYPE REF: No type for parameter>): R|kotlin/Boolean| <inline=Unknown> {
|
||||
public final val hError: R|(ERROR CLASS: No type for parameter) -> kotlin/Boolean| = fun <anonymous>(<unused var>: <ERROR TYPE REF: No type for parameter>): R|kotlin/Boolean| <inline=Unknown> {
|
||||
^ Boolean(true)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// !DUMP_CFG
|
||||
inline fun foo(vararg x: Any) {}
|
||||
<!NOTHING_TO_INLINE!>inline<!> fun foo(vararg x: Any) {}
|
||||
|
||||
fun test(a: Any, b: Any, c: Any) {
|
||||
foo(a, { "" }, b)
|
||||
|
||||
59
compiler/fir/analysis-tests/testData/resolve/classCallInLambda.dot
vendored
Normal file
59
compiler/fir/analysis-tests/testData/resolve/classCallInLambda.dot
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
digraph classCallInLambda_kt {
|
||||
graph [nodesep=3]
|
||||
node [shape=box penwidth=2]
|
||||
edge [penwidth=2]
|
||||
|
||||
subgraph cluster_0 {
|
||||
color=red
|
||||
0 [label="Enter function test" style="filled" fillcolor=red];
|
||||
subgraph cluster_1 {
|
||||
color=blue
|
||||
1 [label="Enter block"];
|
||||
2 [label="Access variable R|<local>/x|"];
|
||||
3 [label="Postponed enter to lambda"];
|
||||
subgraph cluster_2 {
|
||||
color=blue
|
||||
11 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
subgraph cluster_3 {
|
||||
color=blue
|
||||
12 [label="Enter block"];
|
||||
13 [label="Access variable R|<local>/it|"];
|
||||
14 [label="::class call"];
|
||||
15 [label="Exit block"];
|
||||
}
|
||||
16 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
}
|
||||
4 [label="Call arguments union" style="filled" fillcolor=yellow];
|
||||
5 [label="Postponed exit from lambda"];
|
||||
6 [label="Function call: R|<local>/x|.R|kotlin/let|<R|kotlin/String|, R|kotlin/reflect/KClass<out kotlin/String>|>(...)"];
|
||||
7 [label="Jump: ^test R|<local>/x|.R|kotlin/let|<R|kotlin/String|, R|kotlin/reflect/KClass<out kotlin/String>|>(<L> = let@fun <anonymous>(it: R|kotlin/String|): R|kotlin/reflect/KClass<out kotlin/String>| <inline=Inline, kind=EXACTLY_ONCE> {
|
||||
^ <getClass>(R|<local>/it|)
|
||||
}
|
||||
)"];
|
||||
8 [label="Stub" style="filled" fillcolor=gray];
|
||||
9 [label="Exit block" style="filled" fillcolor=gray];
|
||||
}
|
||||
10 [label="Exit function test" style="filled" fillcolor=red];
|
||||
}
|
||||
0 -> {1};
|
||||
1 -> {2};
|
||||
2 -> {3};
|
||||
3 -> {11};
|
||||
3 -> {5} [color=red];
|
||||
3 -> {11} [style=dashed];
|
||||
4 -> {6} [color=red];
|
||||
5 -> {6} [color=green];
|
||||
6 -> {7};
|
||||
7 -> {10};
|
||||
7 -> {8} [style=dotted];
|
||||
8 -> {9} [style=dotted];
|
||||
9 -> {10} [style=dotted];
|
||||
11 -> {12};
|
||||
12 -> {13};
|
||||
13 -> {14};
|
||||
14 -> {15};
|
||||
15 -> {16};
|
||||
16 -> {4} [color=red];
|
||||
16 -> {5} [color=green];
|
||||
|
||||
}
|
||||
7
compiler/fir/analysis-tests/testData/resolve/classCallInLambda.fir.txt
vendored
Normal file
7
compiler/fir/analysis-tests/testData/resolve/classCallInLambda.fir.txt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
FILE: classCallInLambda.kt
|
||||
public final fun test(x: R|kotlin/String|): R|kotlin/reflect/KClass<*>| {
|
||||
^test R|<local>/x|.R|kotlin/let|<R|kotlin/String|, R|kotlin/reflect/KClass<out kotlin/String>|>(<L> = let@fun <anonymous>(it: R|kotlin/String|): R|kotlin/reflect/KClass<out kotlin/String>| <inline=Inline, kind=EXACTLY_ONCE> {
|
||||
^ <getClass>(R|<local>/it|)
|
||||
}
|
||||
)
|
||||
}
|
||||
7
compiler/fir/analysis-tests/testData/resolve/classCallInLambda.kt
vendored
Normal file
7
compiler/fir/analysis-tests/testData/resolve/classCallInLambda.kt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// WITH_STDLIB
|
||||
// DUMP_CFG
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
fun test(x: String): KClass<*> {
|
||||
return x.let { it::class }
|
||||
}
|
||||
@@ -23,6 +23,16 @@ FILE: const.kt
|
||||
public get(): R|kotlin/Int|
|
||||
public final const val l: R|kotlin/Int| = R|/k|
|
||||
public get(): R|kotlin/Int|
|
||||
public final const val m: R|kotlin/String| = String(123).R|kotlin/Any.toString|()
|
||||
public get(): R|kotlin/String|
|
||||
public final const val n: R|kotlin/Int| = String(456).R|kotlin/String.length|
|
||||
public get(): R|kotlin/Int|
|
||||
public final val o: R|kotlin/String| = String(789)
|
||||
public get(): R|kotlin/String|
|
||||
public final const val p: R|kotlin/String| = R|/o|.R|kotlin/Any.toString|()
|
||||
public get(): R|kotlin/String|
|
||||
public final const val q: R|kotlin/Int| = R|/o|.R|kotlin/String.length|
|
||||
public get(): R|kotlin/Int|
|
||||
public final class ForConst : R|kotlin/Any| {
|
||||
public constructor(): R|ForConst| {
|
||||
super<R|kotlin/Any|>()
|
||||
|
||||
@@ -18,6 +18,11 @@ const val i = <!CONST_VAL_WITH_NON_CONST_INITIALIZER!>ForConst.one() + "one"<!>
|
||||
const val j = <!CONST_VAL_WITH_NON_CONST_INITIALIZER!>4 * ForConst.two()<!>
|
||||
val k = 3 - ForConst.two()
|
||||
const val l = <!CONST_VAL_WITH_NON_CONST_INITIALIZER!>k<!>
|
||||
const val m = "123".toString()
|
||||
const val n = "456".length
|
||||
val o = "789"
|
||||
const val p = <!CONST_VAL_WITH_NON_CONST_INITIALIZER!>o.toString()<!>
|
||||
const val q = <!CONST_VAL_WITH_NON_CONST_INITIALIZER!>o.length<!>
|
||||
|
||||
class ForConst{
|
||||
companion object {
|
||||
|
||||
@@ -38,5 +38,5 @@ object F {
|
||||
}
|
||||
|
||||
fun foo() {
|
||||
const val a = "2"
|
||||
<!WRONG_MODIFIER_TARGET!>const<!> val a = "2"
|
||||
}
|
||||
|
||||
@@ -8,17 +8,17 @@ fun test() {}
|
||||
|
||||
fun test(z: Int, c: Char) {}
|
||||
|
||||
<!REDECLARATION!>open class A {
|
||||
open class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>A<!> {
|
||||
open fun rest(s: String) {}
|
||||
|
||||
open val u = 20
|
||||
}<!>
|
||||
}
|
||||
|
||||
<!REDECLARATION!>class A {
|
||||
class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>A<!> {
|
||||
|
||||
}<!>
|
||||
}
|
||||
|
||||
<!REDECLARATION!>class B : <!FINAL_SUPERTYPE, SUPERTYPE_NOT_INITIALIZED!>A<!> {
|
||||
class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>B<!> : <!FINAL_SUPERTYPE, SUPERTYPE_NOT_INITIALIZED!>A<!> {
|
||||
<!CONFLICTING_OVERLOADS!><!NOTHING_TO_OVERRIDE!>override<!> fun rest(s: String)<!> {}
|
||||
|
||||
<!CONFLICTING_OVERLOADS!>fun rest(s: String)<!> {}
|
||||
@@ -26,17 +26,17 @@ fun test(z: Int, c: Char) {}
|
||||
fun rest(l: Long) {}
|
||||
|
||||
<!NOTHING_TO_OVERRIDE!>override<!> val u = 310
|
||||
}<!>
|
||||
}
|
||||
|
||||
<!REDECLARATION!>interface B<!>
|
||||
interface <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>B<!>
|
||||
|
||||
<!REDECLARATION!>enum class B<!>
|
||||
enum class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>B<!>
|
||||
|
||||
<!REDECLARATION!>val u = 10<!>
|
||||
<!REDECLARATION!>val u = 20<!>
|
||||
val <!REDECLARATION!>u<!> = 10
|
||||
val <!REDECLARATION!>u<!> = 20
|
||||
|
||||
<!REDECLARATION!>typealias TA = A<!>
|
||||
<!REDECLARATION!>typealias TA = B<!>
|
||||
typealias <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>TA<!> = A
|
||||
typealias <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>TA<!> = B
|
||||
|
||||
typealias BA = A
|
||||
|
||||
@@ -52,7 +52,7 @@ fun lol(a: Array<Boolean>) {}
|
||||
|
||||
class M {
|
||||
companion <!REDECLARATION!>object<!> {}
|
||||
<!REDECLARATION!>val Companion = object : Any {}<!>
|
||||
val <!REDECLARATION!>Companion<!> = object : Any {}
|
||||
}
|
||||
|
||||
fun B.foo() {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!INCOMPATIBLE_MODIFIERS!>private<!> <!INCOMPATIBLE_MODIFIERS!>public<!> class B
|
||||
<!INCOMPATIBLE_MODIFIERS!>protected<!> <!INCOMPATIBLE_MODIFIERS!>internal<!> class C
|
||||
<!WRONG_MODIFIER_CONTAINING_DECLARATION!>protected<!> <!INCOMPATIBLE_MODIFIERS!>internal<!> class C
|
||||
|
||||
<!INCOMPATIBLE_MODIFIERS!>final<!> <!INCOMPATIBLE_MODIFIERS!>abstract<!> class D
|
||||
<!INCOMPATIBLE_MODIFIERS!>final<!> <!INCOMPATIBLE_MODIFIERS!>open<!> class E
|
||||
@@ -20,7 +20,7 @@ abstract class K {
|
||||
<!INCOMPATIBLE_MODIFIERS!>private<!> <!INCOMPATIBLE_MODIFIERS!>abstract<!> val i2: Int
|
||||
}
|
||||
|
||||
private open <!INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER!>class L<!> : K()
|
||||
private open <!INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER_ERROR!>class L<!> : K()
|
||||
private abstract class M : K()
|
||||
|
||||
class X {
|
||||
|
||||
@@ -6,11 +6,11 @@ infix fun Int.good(x: Int) {}
|
||||
|
||||
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun baz(x: Int, y: Int) {}<!>
|
||||
|
||||
infix class A
|
||||
<!WRONG_MODIFIER_TARGET!>infix<!> class A
|
||||
|
||||
infix typealias B = A
|
||||
<!WRONG_MODIFIER_TARGET!>infix<!> typealias B = A
|
||||
|
||||
infix val x = 1
|
||||
<!WRONG_MODIFIER_TARGET!>infix<!> val x = 1
|
||||
|
||||
class C {
|
||||
infix fun good(x: Int) {}
|
||||
|
||||
@@ -37,7 +37,7 @@ class F(var a: Int, b: Int, closure: () -> Unit, instance: F?) {
|
||||
val a = 10
|
||||
<!INSTANCE_ACCESS_BEFORE_SUPER_CALL!>this<!>
|
||||
test(<!INSTANCE_ACCESS_BEFORE_SUPER_CALL!>this<!>)
|
||||
<!INSTANCE_ACCESS_BEFORE_SUPER_CALL!>this<!>.<!UNRESOLVED_REFERENCE!>a<!> = 20
|
||||
<!INSTANCE_ACCESS_BEFORE_SUPER_CALL!>this<!>.a = 20
|
||||
}, <!INSTANCE_ACCESS_BEFORE_SUPER_CALL!>this<!>) {
|
||||
this.a = 30
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ FILE: notASupertype.kt
|
||||
}
|
||||
|
||||
public final fun g(): R|kotlin/Unit| {
|
||||
this@R|/B|.super<R|kotlin/String|>.<Unresolved name: f>#()
|
||||
this@R|/B|.super<<ERROR TYPE REF: Not a super type>>.<Unresolved name: f>#()
|
||||
this@R|/B|.super<R|A|>.R|/A.f|()
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ open class A {
|
||||
|
||||
class B : <!SUPERTYPE_NOT_INITIALIZED!>A<!> {
|
||||
fun g() {
|
||||
<!NOT_A_SUPERTYPE!>super<String><!>.<!UNRESOLVED_REFERENCE!>f<!>()
|
||||
super<<!NOT_A_SUPERTYPE!>String<!>>.f()
|
||||
super<A>.f()
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user