mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-28 15:51:42 +00:00
Compare commits
759 Commits
rr/perf/no
...
as41/maste
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de25359a90 | ||
|
|
202bbdf8dd | ||
|
|
8a4521864e | ||
|
|
cce5a9a1ca | ||
|
|
1ea72ec378 | ||
|
|
74e4a817cb | ||
|
|
0d6e309372 | ||
|
|
4f8ad6bdcb | ||
|
|
c023a02884 | ||
|
|
e3fb74b656 | ||
|
|
a4b9e8fdc6 | ||
|
|
0db02926f5 | ||
|
|
77c20066a8 | ||
|
|
80afe42d17 | ||
|
|
986b13c3a1 | ||
|
|
175fe163af | ||
|
|
7fff8f82e2 | ||
|
|
db1210fc67 | ||
|
|
0b2c9ff77a | ||
|
|
822c14814b | ||
|
|
148f49d54a | ||
|
|
3c8ef5749f | ||
|
|
35b1bb05c8 | ||
|
|
a18bfad53a | ||
|
|
1ed4324613 | ||
|
|
0e908b720d | ||
|
|
3fa9ea9bc0 | ||
|
|
ea60b83f16 | ||
|
|
cd0e218a07 | ||
|
|
f247ea7c27 | ||
|
|
99a1ef0460 | ||
|
|
426f164e02 | ||
|
|
01f3e4b083 | ||
|
|
402dfd5da7 | ||
|
|
e27bd04ba2 | ||
|
|
d3260bca27 | ||
|
|
d27ad99daa | ||
|
|
a5e46568ee | ||
|
|
ef63d6f84c | ||
|
|
d4d8495840 | ||
|
|
65db6bb2a5 | ||
|
|
9d8eb65a5e | ||
|
|
ba43ee8410 | ||
|
|
b0445496bb | ||
|
|
af13ae1ef2 | ||
|
|
ebae6332aa | ||
|
|
f2d0d8b422 | ||
|
|
dae2acca88 | ||
|
|
9fa4ff750b | ||
|
|
15d85bdc05 | ||
|
|
0c18cb054d | ||
|
|
eceeacdf61 | ||
|
|
9cc13c8324 | ||
|
|
8acdb39bdd | ||
|
|
1b120c189e | ||
|
|
ed55e84afa | ||
|
|
87f0f123a8 | ||
|
|
bd194686df | ||
|
|
6cb0190fad | ||
|
|
5eae262264 | ||
|
|
a401374ed4 | ||
|
|
f9c2c846f7 | ||
|
|
71da941c8b | ||
|
|
8f80cf5664 | ||
|
|
890da492fb | ||
|
|
bc4d7e0020 | ||
|
|
e9e850ad8f | ||
|
|
19219c37b6 | ||
|
|
7a22827af4 | ||
|
|
4a4dce1766 | ||
|
|
6507656496 | ||
|
|
291afd8448 | ||
|
|
55a4c40970 | ||
|
|
723b275d99 | ||
|
|
409e90e7de | ||
|
|
080565e482 | ||
|
|
ec746e17c9 | ||
|
|
b0b7cf4042 | ||
|
|
7ba1371466 | ||
|
|
755b846877 | ||
|
|
9f793f1486 | ||
|
|
cb1c3f87f0 | ||
|
|
14a41d91f6 | ||
|
|
a3d6c42810 | ||
|
|
233cf13bdc | ||
|
|
93f39d5df9 | ||
|
|
be4d3783a4 | ||
|
|
4d484dd971 | ||
|
|
164b4dd439 | ||
|
|
f8fdb0dc7e | ||
|
|
2ca751a9fc | ||
|
|
695d383ed1 | ||
|
|
f128e5222a | ||
|
|
5efbe6ae15 | ||
|
|
16f175612e | ||
|
|
343af60cb4 | ||
|
|
f005091dfb | ||
|
|
afd544cbab | ||
|
|
e2c3455445 | ||
|
|
d19f9ee0c5 | ||
|
|
b4ba00ca36 | ||
|
|
7b68de38e1 | ||
|
|
6a24becd1d | ||
|
|
bdd53ee9cd | ||
|
|
562788ceb9 | ||
|
|
79afc4f72b | ||
|
|
4ae6665b94 | ||
|
|
0ffa0b2bd7 | ||
|
|
cb345a4c19 | ||
|
|
8c422fbfc7 | ||
|
|
9ea69b4b3c | ||
|
|
a239604c24 | ||
|
|
ecb48b7ed9 | ||
|
|
563981808d | ||
|
|
f8b423046e | ||
|
|
05d160b130 | ||
|
|
72dd2ef448 | ||
|
|
b40709649d | ||
|
|
718f0240a1 | ||
|
|
a476d1dbc4 | ||
|
|
9431fc4693 | ||
|
|
83e17cbf09 | ||
|
|
b68715441f | ||
|
|
44f16eac2e | ||
|
|
e41b5fc1c6 | ||
|
|
c29e434d33 | ||
|
|
6eb21031b2 | ||
|
|
d6cb469e66 | ||
|
|
08438dade2 | ||
|
|
94a4aacd9e | ||
|
|
048559c053 | ||
|
|
f7ed3139ab | ||
|
|
069adebf01 | ||
|
|
30037682f8 | ||
|
|
aba97033f0 | ||
|
|
f6a739bbc5 | ||
|
|
55eb75d237 | ||
|
|
1bd63bb07f | ||
|
|
e05eeea6cd | ||
|
|
4568e438a9 | ||
|
|
7cc4cc0cec | ||
|
|
77608c8785 | ||
|
|
3b9000cc0c | ||
|
|
7ab7ca5ff0 | ||
|
|
4d859a7212 | ||
|
|
a421f74ef0 | ||
|
|
1d589e3f56 | ||
|
|
02bd8cbd6b | ||
|
|
7c8c5b057f | ||
|
|
8d05253369 | ||
|
|
561c6747a6 | ||
|
|
2ef46b586d | ||
|
|
246c68b0a9 | ||
|
|
bfac0355bf | ||
|
|
45d60baeb0 | ||
|
|
329f0227ec | ||
|
|
098469eb85 | ||
|
|
741ebeb7b8 | ||
|
|
4cf83d9526 | ||
|
|
8e8171547b | ||
|
|
111b2945e1 | ||
|
|
5b927d798c | ||
|
|
69a2697598 | ||
|
|
a4239afcb3 | ||
|
|
ded996bdf7 | ||
|
|
6868f53f46 | ||
|
|
5efbbdea57 | ||
|
|
498c40548b | ||
|
|
9aaba8c759 | ||
|
|
e2f0b76e2f | ||
|
|
25787eb869 | ||
|
|
3272b8fc2c | ||
|
|
cea879cbd5 | ||
|
|
8f3392d635 | ||
|
|
b9fab1123d | ||
|
|
2a36a8acac | ||
|
|
7d3b28d75b | ||
|
|
f073e34926 | ||
|
|
fd07d5f301 | ||
|
|
8726d3dc4e | ||
|
|
17a9d93a4f | ||
|
|
7014c17a57 | ||
|
|
8704946498 | ||
|
|
7d7835f0f4 | ||
|
|
af1e815f47 | ||
|
|
7fb5acc718 | ||
|
|
2c12d26d28 | ||
|
|
43bbfa78d1 | ||
|
|
16bdfa4fee | ||
|
|
4a511c7721 | ||
|
|
7ec8716d65 | ||
|
|
569b6eaff2 | ||
|
|
f922e454f9 | ||
|
|
599f520fd8 | ||
|
|
a5203428a4 | ||
|
|
eaeaf3c8a3 | ||
|
|
47e6805186 | ||
|
|
684dfb959c | ||
|
|
03358c61d4 | ||
|
|
01de789c76 | ||
|
|
080d8fa127 | ||
|
|
73f4b61022 | ||
|
|
ea6a8ce5cd | ||
|
|
bbe00b2fdc | ||
|
|
0ab9b3639b | ||
|
|
366ed7d4ca | ||
|
|
6ed453dfbc | ||
|
|
a112f8acb6 | ||
|
|
5e56a52f94 | ||
|
|
4dd159d4ec | ||
|
|
46e3649bd0 | ||
|
|
06abbe468b | ||
|
|
024385cbbd | ||
|
|
ee1ea15684 | ||
|
|
2828aa9ce8 | ||
|
|
789efc7c3a | ||
|
|
63429c088f | ||
|
|
20dedd53ff | ||
|
|
756f7076f6 | ||
|
|
ea71a57fac | ||
|
|
4e732b7ba2 | ||
|
|
8d51b027ed | ||
|
|
a256e75909 | ||
|
|
cfb8763b33 | ||
|
|
2e67ccbd6a | ||
|
|
0b3ae50f31 | ||
|
|
faaf40dbe5 | ||
|
|
99b2a4745a | ||
|
|
8afbb4542b | ||
|
|
aa5a5c954f | ||
|
|
b34fe77760 | ||
|
|
5e7c82ea01 | ||
|
|
7ea71a17f0 | ||
|
|
a41b746774 | ||
|
|
df173c8c93 | ||
|
|
1d32cab11d | ||
|
|
72813cefbe | ||
|
|
d612c130e8 | ||
|
|
bb9e8a8a50 | ||
|
|
1b841adfff | ||
|
|
9fd211cfcd | ||
|
|
59c2bc25d5 | ||
|
|
39869f009e | ||
|
|
2e39c1164d | ||
|
|
7d9fe55072 | ||
|
|
f21d1e1c1b | ||
|
|
7d7b9262e7 | ||
|
|
5db6a0b563 | ||
|
|
40a4cea530 | ||
|
|
35460fed19 | ||
|
|
86e434195e | ||
|
|
5f3e296f19 | ||
|
|
0fc43b1f57 | ||
|
|
6f241c6dcd | ||
|
|
422a327fca | ||
|
|
f6dabafe34 | ||
|
|
c919f6172e | ||
|
|
b4ce736814 | ||
|
|
fec3e4faf1 | ||
|
|
1973060e8c | ||
|
|
6d37776267 | ||
|
|
60fd505a9c | ||
|
|
41cdb61ef3 | ||
|
|
a1c3d2b709 | ||
|
|
34d3fe26e6 | ||
|
|
ff06e60a06 | ||
|
|
86d52d4123 | ||
|
|
1982f80bfb | ||
|
|
2812ed0a02 | ||
|
|
f76b57d260 | ||
|
|
a604404bff | ||
|
|
82ce2e7b7c | ||
|
|
e9ef6dbc28 | ||
|
|
e1418a5540 | ||
|
|
e7869bd9d4 | ||
|
|
a0c7bece93 | ||
|
|
23414496c4 | ||
|
|
1c32c62a87 | ||
|
|
8c524769b1 | ||
|
|
865ddac07a | ||
|
|
f243b8946f | ||
|
|
d47981f07b | ||
|
|
a270ee094c | ||
|
|
94509bdb4e | ||
|
|
ffdab473e2 | ||
|
|
b6cdcc8d50 | ||
|
|
19f1a3de1a | ||
|
|
85760770a8 | ||
|
|
cd24745f1f | ||
|
|
048e7a7075 | ||
|
|
0ed9356cd2 | ||
|
|
8dcbc391fe | ||
|
|
97039926f0 | ||
|
|
a431e20b45 | ||
|
|
36c9d04cd3 | ||
|
|
9c2918bdd6 | ||
|
|
104eeb9ffd | ||
|
|
e6165ed785 | ||
|
|
8eca3c8aa8 | ||
|
|
c7a53e4435 | ||
|
|
351d706e0d | ||
|
|
15ead4cbe6 | ||
|
|
2eb354f211 | ||
|
|
a8623b9b28 | ||
|
|
7f41dc4828 | ||
|
|
7b55a49ddd | ||
|
|
dc0ef996b7 | ||
|
|
e60a32e8d3 | ||
|
|
de790a3e3b | ||
|
|
1f1790d60e | ||
|
|
55f384cb04 | ||
|
|
d25f9dee62 | ||
|
|
e72f7d9a1d | ||
|
|
6c1efbe812 | ||
|
|
25b1059f74 | ||
|
|
c9f2876368 | ||
|
|
cef23a284f | ||
|
|
8e8bb9bf08 | ||
|
|
487a698e49 | ||
|
|
07b1bd9d99 | ||
|
|
4b47eb359a | ||
|
|
7c7f87e729 | ||
|
|
2d842d061e | ||
|
|
a631c24e8c | ||
|
|
4944fc78b2 | ||
|
|
cacf5f5a4a | ||
|
|
934b040152 | ||
|
|
8a15edf5e6 | ||
|
|
6b3c390fa7 | ||
|
|
90e6d5bea7 | ||
|
|
724101cdd2 | ||
|
|
bf66929b5b | ||
|
|
66da676b9e | ||
|
|
79fd24f734 | ||
|
|
bf5810a349 | ||
|
|
85e5f8a736 | ||
|
|
118c4b96cc | ||
|
|
45260cee1e | ||
|
|
9f31931bb8 | ||
|
|
2ce1625989 | ||
|
|
a2edb9b547 | ||
|
|
36a29ce2dd | ||
|
|
f26a3150ba | ||
|
|
0827d2e8a3 | ||
|
|
b5245e2df6 | ||
|
|
dbd10380ee | ||
|
|
d115a1ff9c | ||
|
|
4520e02bae | ||
|
|
c805274d2e | ||
|
|
86cd039526 | ||
|
|
bd66e87440 | ||
|
|
af2dce0549 | ||
|
|
097b93cb2a | ||
|
|
d6011103e2 | ||
|
|
cadc5ad587 | ||
|
|
0bed055fca | ||
|
|
d7f52e8b67 | ||
|
|
3643b7b0ca | ||
|
|
052d06be3f | ||
|
|
bf6dffdf38 | ||
|
|
880658135f | ||
|
|
dcebcc27b4 | ||
|
|
c78fd4aaca | ||
|
|
10462c31d2 | ||
|
|
e4a9679ec2 | ||
|
|
eab12a32ac | ||
|
|
262c9e6858 | ||
|
|
86827dfc92 | ||
|
|
6d7c03b6fd | ||
|
|
0076f1ddd5 | ||
|
|
bb329c0415 | ||
|
|
64bcd32661 | ||
|
|
fbddc6921f | ||
|
|
dec8eb7899 | ||
|
|
18953c4717 | ||
|
|
265d066878 | ||
|
|
ff509bdcd3 | ||
|
|
b93868c30b | ||
|
|
49690fc620 | ||
|
|
5bb0085f7e | ||
|
|
46b426ef77 | ||
|
|
9fa1614903 | ||
|
|
4dbd4a56d5 | ||
|
|
500acf412f | ||
|
|
2fd6921806 | ||
|
|
086241659e | ||
|
|
50162265da | ||
|
|
fe047f9b47 | ||
|
|
fe65045615 | ||
|
|
a231b21c69 | ||
|
|
adf6831d7f | ||
|
|
90bcf8bf88 | ||
|
|
fd25a5b3c8 | ||
|
|
8c412adfaa | ||
|
|
be85b36025 | ||
|
|
9ede8445e7 | ||
|
|
18bd80eb29 | ||
|
|
69fa067df9 | ||
|
|
5c58a9dfd1 | ||
|
|
a1c173733c | ||
|
|
9b7a95b05c | ||
|
|
d93e5d3dc0 | ||
|
|
f93749ae6a | ||
|
|
8d364a8a1a | ||
|
|
cfe168021a | ||
|
|
b39a65305d | ||
|
|
8a9610d3af | ||
|
|
00ec50ff73 | ||
|
|
20793a65f4 | ||
|
|
0c71c681e5 | ||
|
|
5a53c6ebf1 | ||
|
|
ed82465c3c | ||
|
|
fb8640b3b6 | ||
|
|
9d1c405b3c | ||
|
|
066b6d830f | ||
|
|
b00b6b0614 | ||
|
|
91c24605f2 | ||
|
|
dea902551d | ||
|
|
46ac241e2f | ||
|
|
f489ac1d03 | ||
|
|
a9d0c16fef | ||
|
|
8a3e63aaf6 | ||
|
|
3e05f7c2ab | ||
|
|
0bf63c3000 | ||
|
|
3a0b0770d7 | ||
|
|
9e85e9a036 | ||
|
|
45c97a2c5e | ||
|
|
bf890a1540 | ||
|
|
4244f05307 | ||
|
|
d0f34624bd | ||
|
|
e6791ea4c3 | ||
|
|
69965eaa5c | ||
|
|
2044ece335 | ||
|
|
60aa47553d | ||
|
|
c699a5b54c | ||
|
|
8fc403db94 | ||
|
|
0afd73b95c | ||
|
|
85a5e5241f | ||
|
|
18070303d5 | ||
|
|
797058f781 | ||
|
|
f6a13df388 | ||
|
|
7c0af78b08 | ||
|
|
64f6ed586b | ||
|
|
08f31758b8 | ||
|
|
19d1532dc7 | ||
|
|
8b8059acdd | ||
|
|
21d6d50241 | ||
|
|
4973d3c359 | ||
|
|
0bd2643788 | ||
|
|
075870fee9 | ||
|
|
fc433dece8 | ||
|
|
c8bad15ba9 | ||
|
|
b756e18962 | ||
|
|
ef698a5747 | ||
|
|
d86b54a2f1 | ||
|
|
4638a97bbc | ||
|
|
6ee4b5e393 | ||
|
|
bfb5efd490 | ||
|
|
581f83421a | ||
|
|
777995a3a0 | ||
|
|
905d0c1016 | ||
|
|
7abd0831a1 | ||
|
|
43b106fc72 | ||
|
|
81d01a8f8e | ||
|
|
bf3e739edf | ||
|
|
8d3e41ae60 | ||
|
|
f7a69d4aa7 | ||
|
|
9b6b25f069 | ||
|
|
b8ae13b3d9 | ||
|
|
a2f55e8b7c | ||
|
|
38b6b73745 | ||
|
|
36f3ff7d20 | ||
|
|
c1b57e8681 | ||
|
|
f81be526bc | ||
|
|
c9f721848a | ||
|
|
fa152593ef | ||
|
|
242d0aa26e | ||
|
|
3e8b0055ab | ||
|
|
eb338e2e91 | ||
|
|
e3ed8870dd | ||
|
|
8d00ec1933 | ||
|
|
74b189fa54 | ||
|
|
5212054af6 | ||
|
|
72aba2fce2 | ||
|
|
53983a99e5 | ||
|
|
06dbbe2452 | ||
|
|
44f6d490b6 | ||
|
|
e613bc406e | ||
|
|
8f8ee1ea83 | ||
|
|
037ea224a4 | ||
|
|
a97e9f641a | ||
|
|
494a4460c1 | ||
|
|
40c852e818 | ||
|
|
cf4ad1908b | ||
|
|
c604ef1255 | ||
|
|
cfe6fee1ca | ||
|
|
eda95af999 | ||
|
|
cb3e08b345 | ||
|
|
ec9608b00d | ||
|
|
59c35fe35c | ||
|
|
2932c67db7 | ||
|
|
6b53a98399 | ||
|
|
b233a2a83a | ||
|
|
13abfda00b | ||
|
|
5cd9e99caa | ||
|
|
bac68816d2 | ||
|
|
381d505430 | ||
|
|
4e8b1634e6 | ||
|
|
d937b66f30 | ||
|
|
e168cb739c | ||
|
|
d84acb8ca2 | ||
|
|
62bd16ddf9 | ||
|
|
70a3877546 | ||
|
|
f161276949 | ||
|
|
e376a04b5a | ||
|
|
d6adab785a | ||
|
|
bfa88267ec | ||
|
|
bae550665e | ||
|
|
381374d339 | ||
|
|
d8abf60b3d | ||
|
|
560c2b62ef | ||
|
|
347f69b1fd | ||
|
|
feac19f46e | ||
|
|
1fdd1bcbf6 | ||
|
|
4e61758188 | ||
|
|
d21a7f54e4 | ||
|
|
8f75f72835 | ||
|
|
7ec82ba279 | ||
|
|
ba5fe6cda9 | ||
|
|
22243c756b | ||
|
|
f7ee9b42d7 | ||
|
|
f064634eb0 | ||
|
|
60515aad77 | ||
|
|
549797c719 | ||
|
|
b201ff0168 | ||
|
|
0a704cf52c | ||
|
|
61f213c80c | ||
|
|
0bb3a4ccb7 | ||
|
|
8434806f56 | ||
|
|
70237ecf31 | ||
|
|
5f64c6b9f1 | ||
|
|
9d862aaa1d | ||
|
|
5ba9d09278 | ||
|
|
2509dfb287 | ||
|
|
f39165bb58 | ||
|
|
eb838b0fa0 | ||
|
|
03fcbfad1a | ||
|
|
69827fc5af | ||
|
|
b79b3507f2 | ||
|
|
a55dec28b9 | ||
|
|
7644b4baec | ||
|
|
44dfae53f0 | ||
|
|
e6c855111e | ||
|
|
444ecc0981 | ||
|
|
bc32688497 | ||
|
|
6f2972ee8b | ||
|
|
adba0a03a6 | ||
|
|
ed6aceca87 | ||
|
|
4a7854a948 | ||
|
|
bf5c7aea4d | ||
|
|
2f79b4c412 | ||
|
|
4455532210 | ||
|
|
7818baff1e | ||
|
|
64fcbbc89f | ||
|
|
5bca9c488e | ||
|
|
bca8e96362 | ||
|
|
ac1769a474 | ||
|
|
dbc564e74e | ||
|
|
b92194229c | ||
|
|
f625444be2 | ||
|
|
76c34a07b2 | ||
|
|
25e1fb8502 | ||
|
|
d17a18f96d | ||
|
|
a04aeeb4e1 | ||
|
|
e70242f6f1 | ||
|
|
756326cefe | ||
|
|
00df562a2b | ||
|
|
55dc308688 | ||
|
|
1ae7f693c5 | ||
|
|
2cee82a348 | ||
|
|
59f027e3e9 | ||
|
|
de69962e9d | ||
|
|
17df7ade59 | ||
|
|
6c124ccd0c | ||
|
|
bb2e58b6a4 | ||
|
|
b05a1bb1a2 | ||
|
|
12f596c620 | ||
|
|
c68de4639f | ||
|
|
58ef59074d | ||
|
|
3f09bb40c0 | ||
|
|
784d3a9005 | ||
|
|
03f2c6d38a | ||
|
|
ab15a88ce4 | ||
|
|
7840adde12 | ||
|
|
262548fd5b | ||
|
|
a7d514a0a9 | ||
|
|
9e807ebb89 | ||
|
|
545fdb96d5 | ||
|
|
7a70f8d29e | ||
|
|
257c4c5604 | ||
|
|
a84780fefb | ||
|
|
4436142f00 | ||
|
|
50fc9d3692 | ||
|
|
2382629209 | ||
|
|
a2dc2fe5bb | ||
|
|
8849a66c00 | ||
|
|
9574ac83e4 | ||
|
|
574b917061 | ||
|
|
3dd79ffea6 | ||
|
|
9d98c90881 | ||
|
|
343e519f7f | ||
|
|
e340dacb9a | ||
|
|
d49c198a7f | ||
|
|
96da6d35c5 | ||
|
|
8d686226c7 | ||
|
|
96802dde04 | ||
|
|
3de12f9b09 | ||
|
|
9a8d520b85 | ||
|
|
3e3387fb69 | ||
|
|
006232dfb2 | ||
|
|
206a646195 | ||
|
|
594a854b4e | ||
|
|
846db641be | ||
|
|
5c12b3df95 | ||
|
|
0b00015424 | ||
|
|
f3d4fa715e | ||
|
|
74353d8bc9 | ||
|
|
799253256c | ||
|
|
f764baad82 | ||
|
|
04a1027b39 | ||
|
|
c0a57ae55c | ||
|
|
6d6ed1e755 | ||
|
|
b61b4e4a97 | ||
|
|
bfad00e8c9 | ||
|
|
22c4d30a89 | ||
|
|
dae7a23998 | ||
|
|
f3145454f2 | ||
|
|
379c6944a2 | ||
|
|
eaa16714f6 | ||
|
|
c9adf22697 | ||
|
|
104352b313 | ||
|
|
6e3d3831c2 | ||
|
|
4076bf40a9 | ||
|
|
2a1e014d51 | ||
|
|
c736a1e5b0 | ||
|
|
d9160a26e9 | ||
|
|
cbba52e43c | ||
|
|
270972ca1c | ||
|
|
3a46b5a45a | ||
|
|
d94c212a60 | ||
|
|
3a31150df4 | ||
|
|
d32aca87d1 | ||
|
|
80cd26c9df | ||
|
|
c112d37ac1 | ||
|
|
7d426226f9 | ||
|
|
f363134fbc | ||
|
|
d92e4d28f5 | ||
|
|
4b032a14af | ||
|
|
255ad47406 | ||
|
|
20bbcd5d5a | ||
|
|
3c9207a2cd | ||
|
|
985623eac5 | ||
|
|
bed7b23d6c | ||
|
|
d089bbe14a | ||
|
|
79984b6e04 | ||
|
|
a6812e5d6a | ||
|
|
4f747072f4 | ||
|
|
22f5d3b134 | ||
|
|
0b33e9430b | ||
|
|
b808d5f381 | ||
|
|
ee141ae57b | ||
|
|
8d77ec83c6 | ||
|
|
f88722ae76 | ||
|
|
a83b0bef96 | ||
|
|
b89878a509 | ||
|
|
7a947e8008 | ||
|
|
0d3301b695 | ||
|
|
2ff5b253ae | ||
|
|
3903814c9c | ||
|
|
12db3d6e83 | ||
|
|
874fa5b998 | ||
|
|
165b62da63 | ||
|
|
933440dc63 | ||
|
|
2d0445ebaa | ||
|
|
fceaf65729 | ||
|
|
8bdc4d34f7 | ||
|
|
f702da5c51 | ||
|
|
d3d162f9c8 | ||
|
|
cce9646c7d | ||
|
|
372f378edc | ||
|
|
4f26ac9a04 | ||
|
|
38fd9c3d1a | ||
|
|
6fcdab23cc | ||
|
|
88efac00b3 | ||
|
|
081d6c1dff | ||
|
|
994897cee9 | ||
|
|
d825428718 | ||
|
|
dc57d307f3 | ||
|
|
8df635a4e0 | ||
|
|
b05546dd64 | ||
|
|
371757309e | ||
|
|
4e07f8180b | ||
|
|
c4407d6d63 | ||
|
|
c1080989b6 | ||
|
|
329af65996 | ||
|
|
93992d1d75 | ||
|
|
ca4deec10e | ||
|
|
dc30bf5d09 | ||
|
|
0544369503 | ||
|
|
f60c8eac71 | ||
|
|
67a259a903 | ||
|
|
24c8a659ee | ||
|
|
c165b8d55c | ||
|
|
16e7a42aea | ||
|
|
e625bb375f | ||
|
|
de4ebe4395 | ||
|
|
fcf8a91a38 | ||
|
|
cf70c83ab7 | ||
|
|
2f82c5b6af | ||
|
|
dc9f64fc9d | ||
|
|
b5bd3604b1 | ||
|
|
9beb36ca2b | ||
|
|
499a02ebe3 | ||
|
|
8b4660031f | ||
|
|
7b079a5f1c | ||
|
|
2c5e42a765 | ||
|
|
16b4342d92 | ||
|
|
73e91af792 | ||
|
|
c51c537504 | ||
|
|
7222a732c1 | ||
|
|
86b28b95ca | ||
|
|
5feadd56ef | ||
|
|
1fe7ef6521 | ||
|
|
143d8d1520 | ||
|
|
d37c412f76 | ||
|
|
bf21e1282a | ||
|
|
dd46d5ca5a | ||
|
|
1a0b59da49 | ||
|
|
0d71758112 | ||
|
|
eff5f3a242 | ||
|
|
425ce8c3ab | ||
|
|
d5141674a3 | ||
|
|
89d706972c | ||
|
|
98d3855fd8 | ||
|
|
879fe9b493 | ||
|
|
1a1bcefb2a | ||
|
|
16b7232b4f | ||
|
|
8d22429abc | ||
|
|
1513429613 | ||
|
|
942c8aaafc | ||
|
|
839e4ecff2 | ||
|
|
5287ffb4a5 | ||
|
|
8e3a62e5fd | ||
|
|
0df5d5158d | ||
|
|
12217ef2ab | ||
|
|
02c17378b1 | ||
|
|
b2be1a53cf | ||
|
|
1ba4d7d99b | ||
|
|
ba993ba0fe |
6
.bunch
6
.bunch
@@ -1,7 +1,7 @@
|
||||
193
|
||||
201
|
||||
202_201
|
||||
192
|
||||
191_192
|
||||
as35_191_192
|
||||
as36_192
|
||||
as40
|
||||
as40
|
||||
as41_201
|
||||
3
.idea/dictionaries/dmitriy_dolovov.xml
generated
3
.idea/dictionaries/dmitriy_dolovov.xml
generated
@@ -1,11 +1,14 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="dmitriy.dolovov">
|
||||
<words>
|
||||
<w>commonizable</w>
|
||||
<w>commonization</w>
|
||||
<w>commonize</w>
|
||||
<w>commonized</w>
|
||||
<w>commonizer</w>
|
||||
<w>commonizers</w>
|
||||
<w>commonizes</w>
|
||||
<w>jetbrains</w>
|
||||
<w>konan</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
|
||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -66,7 +66,7 @@
|
||||
<component name="ProjectResources">
|
||||
<default-html-doctype>http://www.w3.org/1999/xhtml</default-html-doctype>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
<component name="PsiViewerSettings">
|
||||
|
||||
@@ -32,8 +32,8 @@ import org.jetbrains.kotlin.fir.java.FirJavaModuleBasedSession
|
||||
import org.jetbrains.kotlin.fir.java.FirLibrarySession
|
||||
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.firProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.impl.FirProviderImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.FirTotalResolveTransformer
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirProviderImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.FirTotalResolveProcessor
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
@@ -66,10 +66,10 @@ private val JDK_PATH = File("${System.getProperty("java.home")!!}/lib/rt.jar")
|
||||
private val RUNTIME_JAR = File(System.getProperty("kotlin.runtime.path") ?: "dist/kotlinc/lib/kotlin-runtime.jar")
|
||||
|
||||
private val LANGUAGE_FEATURE_SETTINGS =
|
||||
LanguageVersionSettingsImpl(
|
||||
LanguageVersion.KOTLIN_1_3, ApiVersion.KOTLIN_1_3,
|
||||
specificFeatures = mapOf(LanguageFeature.NewInference to LanguageFeature.State.ENABLED)
|
||||
)
|
||||
LanguageVersionSettingsImpl(
|
||||
LanguageVersion.KOTLIN_1_3, ApiVersion.KOTLIN_1_3,
|
||||
specificFeatures = mapOf(LanguageFeature.NewInference to LanguageFeature.State.ENABLED)
|
||||
)
|
||||
|
||||
private fun newConfiguration(useNewInference: Boolean): CompilerConfiguration {
|
||||
val configuration = CompilerConfiguration()
|
||||
@@ -81,10 +81,10 @@ private fun newConfiguration(useNewInference: Boolean): CompilerConfiguration {
|
||||
|
||||
val newInferenceState = if (useNewInference) LanguageFeature.State.ENABLED else LanguageFeature.State.DISABLED
|
||||
configuration.languageVersionSettings = LanguageVersionSettingsImpl(
|
||||
LanguageVersion.KOTLIN_1_3, ApiVersion.KOTLIN_1_3,
|
||||
specificFeatures = mapOf(
|
||||
LanguageFeature.NewInference to newInferenceState
|
||||
)
|
||||
LanguageVersion.KOTLIN_1_3, ApiVersion.KOTLIN_1_3,
|
||||
specificFeatures = mapOf(
|
||||
LanguageFeature.NewInference to newInferenceState
|
||||
)
|
||||
)
|
||||
return configuration
|
||||
}
|
||||
@@ -105,21 +105,21 @@ abstract class AbstractSimpleFileBenchmark {
|
||||
fun setUp() {
|
||||
if (isIR && !useNewInference) error("Invalid configuration")
|
||||
env = KotlinCoreEnvironment.createForTests(
|
||||
myDisposable,
|
||||
newConfiguration(useNewInference),
|
||||
EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||
myDisposable,
|
||||
newConfiguration(useNewInference),
|
||||
EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||
)
|
||||
|
||||
if (isIR) {
|
||||
Extensions.getArea(env.project)
|
||||
.getExtensionPoint(PsiElementFinder.EP_NAME)
|
||||
.unregisterExtension(JavaElementFinder::class.java)
|
||||
.getExtensionPoint(PsiElementFinder.EP_NAME)
|
||||
.unregisterExtension(JavaElementFinder::class.java)
|
||||
}
|
||||
|
||||
file = createFile(
|
||||
"test.kt",
|
||||
buildText(),
|
||||
env.project
|
||||
"test.kt",
|
||||
buildText(),
|
||||
env.project
|
||||
)
|
||||
}
|
||||
|
||||
@@ -134,22 +134,22 @@ abstract class AbstractSimpleFileBenchmark {
|
||||
private fun analyzeGreenFileFrontend(bh: Blackhole) {
|
||||
val tracker = ExceptionTracker()
|
||||
val storageManager: StorageManager =
|
||||
LockBasedStorageManager.createWithExceptionHandling("benchmarks", tracker)
|
||||
LockBasedStorageManager.createWithExceptionHandling("benchmarks", tracker)
|
||||
|
||||
val context = SimpleGlobalContext(storageManager, tracker)
|
||||
val module =
|
||||
ModuleDescriptorImpl(
|
||||
Name.special("<benchmark>"), storageManager,
|
||||
JvmBuiltIns(storageManager, JvmBuiltIns.Kind.FROM_DEPENDENCIES)
|
||||
)
|
||||
ModuleDescriptorImpl(
|
||||
Name.special("<benchmark>"), storageManager,
|
||||
JvmBuiltIns(storageManager, JvmBuiltIns.Kind.FROM_DEPENDENCIES)
|
||||
)
|
||||
val moduleContext = context.withProject(env.project).withModule(module)
|
||||
|
||||
val result = TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
|
||||
moduleContext.project,
|
||||
listOf(file),
|
||||
NoScopeRecordCliBindingTrace(),
|
||||
env.configuration,
|
||||
{ scope -> JvmPackagePartProvider(LANGUAGE_FEATURE_SETTINGS, scope) }
|
||||
moduleContext.project,
|
||||
listOf(file),
|
||||
NoScopeRecordCliBindingTrace(),
|
||||
env.configuration,
|
||||
{ scope -> JvmPackagePartProvider(LANGUAGE_FEATURE_SETTINGS, scope) }
|
||||
)
|
||||
|
||||
assert(result.bindingContext.diagnostics.none { it.severity == Severity.ERROR })
|
||||
@@ -159,17 +159,15 @@ abstract class AbstractSimpleFileBenchmark {
|
||||
|
||||
private fun analyzeGreenFileIr(bh: Blackhole) {
|
||||
val scope = GlobalSearchScope.filesScope(env.project, listOf(file.virtualFile))
|
||||
.uniteWith(TopDownAnalyzerFacadeForJVM.AllJavaSourcesInProjectScope(env.project))
|
||||
.uniteWith(TopDownAnalyzerFacadeForJVM.AllJavaSourcesInProjectScope(env.project))
|
||||
val session = createSession(env, scope)
|
||||
val firProvider = session.firProvider as FirProviderImpl
|
||||
val builder = RawFirBuilder(session, firProvider.kotlinScopeProvider, stubMode = false)
|
||||
|
||||
val totalTransformer = FirTotalResolveTransformer()
|
||||
val totalTransformer = FirTotalResolveProcessor(session)
|
||||
val firFile = builder.buildFirFile(file).also(firProvider::recordFile)
|
||||
|
||||
for (transformer in totalTransformer.transformers) {
|
||||
transformer.transformFile(firFile, null)
|
||||
}
|
||||
totalTransformer.process(listOf(firFile))
|
||||
|
||||
bh.consume(firFile.hashCode())
|
||||
}
|
||||
@@ -178,37 +176,37 @@ abstract class AbstractSimpleFileBenchmark {
|
||||
}
|
||||
|
||||
fun createSession(
|
||||
environment: KotlinCoreEnvironment,
|
||||
sourceScope: GlobalSearchScope,
|
||||
librariesScope: GlobalSearchScope = GlobalSearchScope.notScope(sourceScope)
|
||||
environment: KotlinCoreEnvironment,
|
||||
sourceScope: GlobalSearchScope,
|
||||
librariesScope: GlobalSearchScope = GlobalSearchScope.notScope(sourceScope)
|
||||
): FirSession {
|
||||
val moduleInfo = FirTestModuleInfo()
|
||||
val project = environment.project
|
||||
val provider = FirProjectSessionProvider(project)
|
||||
return FirJavaModuleBasedSession(moduleInfo, provider, sourceScope).also {
|
||||
return FirJavaModuleBasedSession.create(moduleInfo, provider, sourceScope).also {
|
||||
createSessionForDependencies(provider, moduleInfo, librariesScope, environment)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSessionForDependencies(
|
||||
provider: FirProjectSessionProvider,
|
||||
moduleInfo: FirTestModuleInfo,
|
||||
librariesScope: GlobalSearchScope,
|
||||
environment: KotlinCoreEnvironment
|
||||
provider: FirProjectSessionProvider,
|
||||
moduleInfo: FirTestModuleInfo,
|
||||
librariesScope: GlobalSearchScope,
|
||||
environment: KotlinCoreEnvironment
|
||||
) {
|
||||
val dependenciesInfo = FirTestModuleInfo()
|
||||
moduleInfo.dependencies.add(dependenciesInfo)
|
||||
FirLibrarySession.create(
|
||||
dependenciesInfo, provider, librariesScope, environment.project,
|
||||
environment.createPackagePartProvider(librariesScope)
|
||||
dependenciesInfo, provider, librariesScope, environment.project,
|
||||
environment.createPackagePartProvider(librariesScope)
|
||||
)
|
||||
}
|
||||
|
||||
class FirTestModuleInfo(
|
||||
override val name: Name = Name.identifier("TestModule"),
|
||||
val dependencies: MutableList<ModuleInfo> = mutableListOf(),
|
||||
override val platform: TargetPlatform = JvmPlatforms.unspecifiedJvmPlatform,
|
||||
override val analyzerServices: PlatformDependentAnalyzerServices = JvmPlatformAnalyzerServices
|
||||
override val name: Name = Name.identifier("TestModule"),
|
||||
val dependencies: MutableList<ModuleInfo> = mutableListOf(),
|
||||
override val platform: TargetPlatform = JvmPlatforms.unspecifiedJvmPlatform,
|
||||
override val analyzerServices: PlatformDependentAnalyzerServices = JvmPlatformAnalyzerServices
|
||||
) : ModuleInfo {
|
||||
override fun dependencies(): List<ModuleInfo> = dependencies
|
||||
}
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.incremental
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.incremental.storage
|
||||
|
||||
import com.intellij.util.io.DataExternalizer
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
||||
import com.intellij.util.io.KeyDescriptor
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.utils.Printer
|
||||
import java.io.File
|
||||
|
||||
abstract class BasicMap<K : Comparable<K>, V>(
|
||||
internal val storageFile: File,
|
||||
keyDescriptor: KeyDescriptor<K>,
|
||||
valueExternalizer: DataExternalizer<V>
|
||||
) {
|
||||
protected val storage = LazyStorage(storageFile, keyDescriptor, valueExternalizer)
|
||||
|
||||
fun clean() {
|
||||
storage.clean()
|
||||
}
|
||||
|
||||
fun flush(memoryCachesOnly: Boolean) {
|
||||
storage.flush(memoryCachesOnly)
|
||||
}
|
||||
|
||||
fun close() {
|
||||
storage.close()
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun dump(): String {
|
||||
return with(StringBuilder()) {
|
||||
with(Printer(this)) {
|
||||
println(this@BasicMap::class.java.simpleName)
|
||||
pushIndent()
|
||||
|
||||
for (key in storage.keys.sorted()) {
|
||||
println("${dumpKey(key)} -> ${dumpValue(storage[key]!!)}")
|
||||
}
|
||||
|
||||
popIndent()
|
||||
}
|
||||
|
||||
this
|
||||
}.toString()
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
protected abstract fun dumpKey(key: K): String
|
||||
|
||||
@TestOnly
|
||||
protected abstract fun dumpValue(value: V): String
|
||||
}
|
||||
|
||||
abstract class BasicStringMap<V>(
|
||||
storageFile: File,
|
||||
keyDescriptor: KeyDescriptor<String>,
|
||||
valueExternalizer: DataExternalizer<V>
|
||||
) : BasicMap<String, V>(storageFile, keyDescriptor, valueExternalizer) {
|
||||
constructor(
|
||||
storageFile: File,
|
||||
valueExternalizer: DataExternalizer<V>
|
||||
) : this(storageFile, EnumeratorStringDescriptor.INSTANCE, valueExternalizer)
|
||||
|
||||
override fun dumpKey(key: String): String = key
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.incremental.storage
|
||||
|
||||
import org.jetbrains.kotlin.incremental.dumpCollection
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import java.io.File
|
||||
|
||||
internal open class ClassOneToManyMap(
|
||||
storageFile: File
|
||||
) : BasicStringMap<Collection<String>>(storageFile, StringCollectionExternalizer) {
|
||||
override fun dumpValue(value: Collection<String>): String = value.dumpCollection()
|
||||
|
||||
fun add(key: FqName, value: FqName) {
|
||||
storage.append(key.asString(), value.asString())
|
||||
}
|
||||
|
||||
operator fun get(key: FqName): Collection<FqName> =
|
||||
storage[key.asString()]?.map(::FqName) ?: setOf()
|
||||
|
||||
operator fun set(key: FqName, values: Collection<FqName>) {
|
||||
if (values.isEmpty()) {
|
||||
remove(key)
|
||||
return
|
||||
}
|
||||
|
||||
storage[key.asString()] = values.map(FqName::asString)
|
||||
}
|
||||
|
||||
fun remove(key: FqName) {
|
||||
storage.remove(key.asString())
|
||||
}
|
||||
|
||||
fun removeValues(key: FqName, removed: Set<FqName>) {
|
||||
val notRemoved = this[key].filter { it !in removed }
|
||||
this[key] = notRemoved
|
||||
}
|
||||
}
|
||||
|
||||
internal class SubtypesMap(storageFile: File) : ClassOneToManyMap(storageFile)
|
||||
internal class SupertypesMap(storageFile: File) : ClassOneToManyMap(storageFile)
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.incremental.storage
|
||||
|
||||
import com.intellij.util.io.DataExternalizer
|
||||
import com.intellij.util.io.IOUtil
|
||||
import com.intellij.util.io.KeyDescriptor
|
||||
import com.intellij.util.io.PersistentHashMap
|
||||
import java.io.DataOutput
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
/**
|
||||
* It's lazy in a sense that PersistentHashMap is created only on write
|
||||
*/
|
||||
class LazyStorage<K, V>(
|
||||
private val storageFile: File,
|
||||
private val keyDescriptor: KeyDescriptor<K>,
|
||||
private val valueExternalizer: DataExternalizer<V>
|
||||
) {
|
||||
@Volatile
|
||||
private var storage: PersistentHashMap<K, V>? = null
|
||||
|
||||
@Synchronized
|
||||
private fun getStorageIfExists(): PersistentHashMap<K, V>? {
|
||||
if (storage != null) return storage
|
||||
|
||||
if (storageFile.exists()) {
|
||||
storage = createMap()
|
||||
return storage
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun getStorageOrCreateNew(): PersistentHashMap<K, V> {
|
||||
if (storage == null) {
|
||||
storage = createMap()
|
||||
}
|
||||
|
||||
return storage!!
|
||||
}
|
||||
|
||||
val keys: Collection<K>
|
||||
get() = getStorageIfExists()?.allKeysWithExistingMapping ?: listOf()
|
||||
|
||||
operator fun contains(key: K): Boolean =
|
||||
getStorageIfExists()?.containsMapping(key) ?: false
|
||||
|
||||
operator fun get(key: K): V? =
|
||||
getStorageIfExists()?.get(key)
|
||||
|
||||
operator fun set(key: K, value: V) {
|
||||
getStorageOrCreateNew().put(key, value)
|
||||
}
|
||||
|
||||
fun remove(key: K) {
|
||||
getStorageIfExists()?.remove(key)
|
||||
}
|
||||
|
||||
fun append(key: K, value: String) {
|
||||
append(key) { out -> IOUtil.writeUTF(out, value) }
|
||||
}
|
||||
|
||||
fun append(key: K, value: Int) {
|
||||
append(key) { out -> out.writeInt(value) }
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun clean() {
|
||||
try {
|
||||
storage?.close()
|
||||
}
|
||||
catch (ignored: Throwable) {
|
||||
}
|
||||
|
||||
PersistentHashMap.deleteFilesStartingWith(storageFile)
|
||||
storage = null
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun flush(memoryCachesOnly: Boolean) {
|
||||
val existingStorage = storage ?: return
|
||||
|
||||
if (memoryCachesOnly) {
|
||||
if (existingStorage.isDirty) {
|
||||
existingStorage.dropMemoryCaches()
|
||||
}
|
||||
}
|
||||
else {
|
||||
existingStorage.force()
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun close() {
|
||||
storage?.close()
|
||||
}
|
||||
|
||||
private fun createMap(): PersistentHashMap<K, V> =
|
||||
PersistentHashMap(storageFile, keyDescriptor, valueExternalizer)
|
||||
|
||||
private fun append(key: K, append: (DataOutput)->Unit) {
|
||||
getStorageOrCreateNew().appendData(key, append)
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.incremental.storage
|
||||
|
||||
import java.io.File
|
||||
|
||||
internal class LookupMap(storage: File) : BasicMap<LookupSymbolKey, Collection<Int>>(storage, LookupSymbolKeyDescriptor, IntCollectionExternalizer) {
|
||||
override fun dumpKey(key: LookupSymbolKey): String = key.toString()
|
||||
|
||||
override fun dumpValue(value: Collection<Int>): String = value.toString()
|
||||
|
||||
fun add(name: String, scope: String, fileId: Int) {
|
||||
storage.append(LookupSymbolKey(name, scope), fileId)
|
||||
}
|
||||
|
||||
operator fun get(key: LookupSymbolKey): Collection<Int>? = storage[key]
|
||||
|
||||
operator fun set(key: LookupSymbolKey, fileIds: Set<Int>) {
|
||||
storage[key] = fileIds
|
||||
}
|
||||
|
||||
fun remove(key: LookupSymbolKey) {
|
||||
storage.remove(key)
|
||||
}
|
||||
|
||||
val keys: Collection<LookupSymbolKey>
|
||||
get() = storage.keys
|
||||
}
|
||||
@@ -1,62 +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.incremental.storage
|
||||
|
||||
import org.jetbrains.kotlin.incremental.dumpCollection
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import java.io.File
|
||||
|
||||
internal class SourceToJvmNameMap(
|
||||
storageFile: File,
|
||||
pathConverter: FileToPathConverter
|
||||
) : AbstractSourceToOutputMap<JvmClassName>(JvmClassNameTransformer, storageFile, pathConverter)
|
||||
|
||||
internal class SourceToFqNameMap(
|
||||
storageFile: File,
|
||||
pathConverter: FileToPathConverter
|
||||
) : AbstractSourceToOutputMap<FqName>(FqNameTransformer, storageFile, pathConverter)
|
||||
|
||||
internal abstract class AbstractSourceToOutputMap<Name>(
|
||||
private val nameTransformer: NameTransformer<Name>,
|
||||
storageFile: File,
|
||||
private val pathConverter: FileToPathConverter
|
||||
) : BasicStringMap<Collection<String>>(storageFile, PathStringDescriptor, StringCollectionExternalizer) {
|
||||
fun clearOutputsForSource(sourceFile: File) {
|
||||
remove(pathConverter.toPath(sourceFile))
|
||||
}
|
||||
|
||||
fun add(sourceFile: File, className: Name) {
|
||||
storage.append(pathConverter.toPath(sourceFile), nameTransformer.asString(className))
|
||||
}
|
||||
|
||||
fun contains(sourceFile: File): Boolean =
|
||||
pathConverter.toPath(sourceFile) in storage
|
||||
|
||||
operator fun get(sourceFile: File): Collection<Name> =
|
||||
storage[pathConverter.toPath(sourceFile)].orEmpty().map(nameTransformer::asName)
|
||||
|
||||
fun getFqNames(sourceFile: File): Collection<FqName> =
|
||||
storage[pathConverter.toPath(sourceFile)].orEmpty().map(nameTransformer::asFqName)
|
||||
|
||||
override fun dumpValue(value: Collection<String>) =
|
||||
value.dumpCollection()
|
||||
|
||||
private fun remove(path: String) {
|
||||
storage.remove(path)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import org.gradle.crypto.checksum.Checksum
|
||||
import org.gradle.plugins.ide.idea.model.IdeaModel
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import proguard.gradle.ProGuardTask
|
||||
@@ -42,6 +43,8 @@ plugins {
|
||||
idea
|
||||
id("jps-compatible")
|
||||
id("org.jetbrains.gradle.plugin.idea-ext")
|
||||
id("org.gradle.crypto.checksum") version "1.2.0"
|
||||
signing
|
||||
}
|
||||
|
||||
pill {
|
||||
@@ -163,7 +166,7 @@ extra["versions.junit"] = "4.12"
|
||||
extra["versions.javaslang"] = "2.0.6"
|
||||
extra["versions.ant"] = "1.8.2"
|
||||
extra["versions.android"] = "2.3.1"
|
||||
val coroutinesVersion = if (Platform[192].orHigher()) "1.2.1" else "1.1.1"
|
||||
val coroutinesVersion = if (Platform[192].orHigher()) "1.3.7" else "1.1.1"
|
||||
extra["versions.kotlinx-coroutines-core"] = coroutinesVersion
|
||||
extra["versions.kotlinx-coroutines-jdk8"] = coroutinesVersion
|
||||
extra["versions.json"] = "20160807"
|
||||
@@ -184,7 +187,7 @@ 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.4-M3-dev-15453"
|
||||
extra["versions.kotlin-native"] = "1.4-M3-dev-15627"
|
||||
}
|
||||
|
||||
val intellijUltimateEnabled by extra(project.kotlinBuildProperties.intellijUltimateEnabled)
|
||||
@@ -276,6 +279,28 @@ extra["compilerModules"] = arrayOf(
|
||||
":compiler:fir:analysis-tests"
|
||||
)
|
||||
|
||||
extra["compilerModulesForJps"] = listOf(
|
||||
":core:type-system",
|
||||
":kotlin-build-common",
|
||||
":kotlin-util-io",
|
||||
":kotlin-util-klib",
|
||||
":kotlin-util-klib-metadata",
|
||||
":compiler:cli-common",
|
||||
":kotlin-compiler-runner",
|
||||
":daemon-common",
|
||||
":daemon-common-new",
|
||||
":core:descriptors",
|
||||
":core:descriptors.jvm",
|
||||
":idea:idea-jps-common",
|
||||
":kotlin-preloader",
|
||||
":compiler:util",
|
||||
":compiler:config",
|
||||
":compiler:config.jvm",
|
||||
":js:js.config",
|
||||
":core:util.runtime",
|
||||
":compiler:compiler.version"
|
||||
)
|
||||
|
||||
val coreLibProjects = listOfNotNull(
|
||||
":kotlin-stdlib",
|
||||
":kotlin-stdlib-common",
|
||||
@@ -595,7 +620,7 @@ tasks {
|
||||
register("scriptingTest") {
|
||||
dependsOn("dist")
|
||||
dependsOn(":kotlin-script-util:test")
|
||||
dependsOn(":kotlin-scripting-compiler:test")
|
||||
dependsOn(":kotlin-scripting-compiler-embeddable:test")
|
||||
dependsOn(":kotlin-scripting-common:test")
|
||||
dependsOn(":kotlin-scripting-jvm:test")
|
||||
dependsOn(":kotlin-scripting-jvm-host-test:test")
|
||||
@@ -613,6 +638,10 @@ tasks {
|
||||
register("compilerTest") {
|
||||
dependsOn("jvmCompilerTest")
|
||||
dependsOn("jsCompilerTest")
|
||||
dependsOn("miscCompilerTest")
|
||||
}
|
||||
|
||||
register("miscCompilerTest") {
|
||||
dependsOn("wasmCompilerTest")
|
||||
dependsOn("nativeCompilerTest")
|
||||
dependsOn("firCompilerTest")
|
||||
@@ -745,6 +774,37 @@ tasks {
|
||||
)
|
||||
}
|
||||
|
||||
register("publishIdeArtifacts") {
|
||||
idePluginDependency {
|
||||
dependsOn(
|
||||
":prepare:ide-plugin-dependencies:android-extensions-compiler-plugin-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:allopen-compiler-plugin-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:allopen-compiler-plugin-tests-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:incremental-compilation-impl-tests-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:kotlin-build-common-tests-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:kotlin-compiler-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:kotlin-dist-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:kotlin-gradle-statistics-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:kotlinx-serialization-compiler-plugin-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:noarg-compiler-plugin-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:sam-with-receiver-compiler-plugin-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:compiler-components-for-jps:publish",
|
||||
":kotlin-script-runtime:publish",
|
||||
":kotlin-script-util:publish",
|
||||
":kotlin-scripting-common:publish",
|
||||
":kotlin-scripting-jvm:publish",
|
||||
":kotlin-scripting-compiler:publish",
|
||||
":kotlin-scripting-compiler-impl:publish",
|
||||
":kotlin-android-extensions-runtime:publish",
|
||||
":kotlin-stdlib-common:publish",
|
||||
":kotlin-stdlib:publish",
|
||||
":kotlin-stdlib-jdk7:publish",
|
||||
":kotlin-stdlib-jdk8:publish",
|
||||
":kotlin-reflect:publish",
|
||||
":kotlin-main-kts:publish"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
register("test") {
|
||||
doLast {
|
||||
@@ -772,15 +832,15 @@ fun CopySpec.setExecutablePermissions() {
|
||||
|
||||
val zipCompiler by task<Zip> {
|
||||
dependsOn(dist)
|
||||
destinationDir = file(distDir)
|
||||
archiveName = "kotlin-compiler-$kotlinVersion.zip"
|
||||
destinationDirectory.set(file(distDir))
|
||||
archiveFileName.set("kotlin-compiler-$kotlinVersion.zip")
|
||||
|
||||
from(distKotlinHomeDir)
|
||||
into("kotlinc")
|
||||
setExecutablePermissions()
|
||||
|
||||
doLast {
|
||||
logger.lifecycle("Compiler artifacts packed to $archivePath")
|
||||
logger.lifecycle("Compiler artifacts packed to ${archiveFile.get().asFile.absolutePath}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -830,6 +890,33 @@ val zipPlugin by task<Zip> {
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.secureZipTask(zipTask: TaskProvider<Zip>): RegisteringDomainObjectDelegateProviderWithAction<out TaskContainer, Task> {
|
||||
val checkSumTask = tasks.register("${zipTask.name}Checksum", Checksum::class) {
|
||||
dependsOn(zipTask)
|
||||
val compilerFile = zipTask.get().outputs.files.singleFile
|
||||
files = files(compilerFile)
|
||||
outputDir = compilerFile.parentFile
|
||||
algorithm = Checksum.Algorithm.SHA256
|
||||
}
|
||||
|
||||
val signTask = tasks.register("${zipTask.name}Sign", Sign::class) {
|
||||
description = "Signs the archive produced by the '" + zipTask.name + "' task."
|
||||
sign(zipTask.get())
|
||||
}
|
||||
|
||||
return tasks.registering {
|
||||
dependsOn(checkSumTask)
|
||||
dependsOn(signTask)
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
useGpgCmd()
|
||||
}
|
||||
|
||||
val zipCompilerWithSignature by secureZipTask(zipCompiler)
|
||||
val zipPluginWithSignature by secureZipTask(zipPlugin)
|
||||
|
||||
configure<IdeaModel> {
|
||||
module {
|
||||
excludeDirs = files(
|
||||
|
||||
@@ -5,8 +5,7 @@ buildscript {
|
||||
val cacheRedirectorEnabled = findProperty("cacheRedirectorEnabled")?.toString()?.toBoolean() == true
|
||||
|
||||
extra["defaultSnapshotVersion"] = kotlinBuildProperties.defaultSnapshotVersion
|
||||
BootstrapOption.BintrayBootstrap("1.4.0-dev-1818", cacheRedirectorEnabled).applyToProject(project)
|
||||
// kotlinBootstrapFrom(BootstrapOption.BintrayBootstrap(kotlinBuildProperties.kotlinBootstrapVersion!!, cacheRedirectorEnabled))
|
||||
kotlinBootstrapFrom(BootstrapOption.BintrayBootstrap(kotlinBuildProperties.kotlinBootstrapVersion!!, cacheRedirectorEnabled))
|
||||
|
||||
repositories {
|
||||
if (cacheRedirectorEnabled) {
|
||||
@@ -73,7 +72,6 @@ rootProject.apply {
|
||||
val isTeamcityBuild = kotlinBuildProperties.isTeamcityBuild
|
||||
val intellijUltimateEnabled by extra(kotlinBuildProperties.intellijUltimateEnabled)
|
||||
val intellijSeparateSdks by extra(project.getBooleanProperty("intellijSeparateSdks") ?: false)
|
||||
val verifyDependencyOutput by extra( getBooleanProperty("kotlin.build.dependency.output.verification") ?: isTeamcityBuild)
|
||||
|
||||
extra["intellijReleaseType"] = when {
|
||||
extra["versions.intellijSdk"]?.toString()?.contains("-EAP-") == true -> "snapshots"
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
org.gradle.jvmargs=-Duser.country=US -Dkotlin.daemon.jvm.options=-Xmx1600m -Dfile.encoding=UTF-8
|
||||
|
||||
cacheRedirectorEnabled=true
|
||||
|
||||
#buildSrc.kotlin.repo=https://jcenter.bintray.com
|
||||
#buildSrc.kotlin.version=1.1.50
|
||||
|
||||
intellijUltimateEnabled=false
|
||||
@@ -1,8 +0,0 @@
|
||||
org.gradle.jvmargs=-Duser.country=US -Dkotlin.daemon.jvm.options=-Xmx1600m -Dfile.encoding=UTF-8
|
||||
|
||||
cacheRedirectorEnabled=true
|
||||
|
||||
#buildSrc.kotlin.repo=https://jcenter.bintray.com
|
||||
#buildSrc.kotlin.version=1.1.50
|
||||
|
||||
intellijUltimateEnabled=false
|
||||
@@ -1,8 +0,0 @@
|
||||
org.gradle.jvmargs=-Duser.country=US -Dkotlin.daemon.jvm.options=-Xmx1600m -Dfile.encoding=UTF-8
|
||||
|
||||
cacheRedirectorEnabled=true
|
||||
|
||||
#buildSrc.kotlin.repo=https://jcenter.bintray.com
|
||||
#buildSrc.kotlin.version=1.1.50
|
||||
|
||||
intellijUltimateEnabled=false
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
import java.io.Closeable
|
||||
import java.io.FileWriter
|
||||
import java.io.OutputStreamWriter
|
||||
import java.net.URI
|
||||
import java.text.SimpleDateFormat
|
||||
@@ -10,13 +9,13 @@ import java.util.*
|
||||
import javax.xml.stream.XMLOutputFactory
|
||||
|
||||
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
|
||||
import org.jetbrains.kotlin.gradle.tasks.CleanDataTask
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
|
||||
plugins {
|
||||
base
|
||||
}
|
||||
|
||||
val verifyDependencyOutput: Boolean by rootProject.extra
|
||||
val intellijUltimateEnabled: Boolean by rootProject.extra
|
||||
val intellijReleaseType: String by rootProject.extra
|
||||
val intellijVersion = rootProject.extra["versions.intellijSdk"] as String
|
||||
@@ -25,7 +24,7 @@ val androidStudioRelease = rootProject.findProperty("versions.androidStudioRelea
|
||||
val androidStudioBuild = rootProject.findProperty("versions.androidStudioBuild") as String?
|
||||
val intellijSeparateSdks: Boolean by rootProject.extra
|
||||
val installIntellijCommunity = !intellijUltimateEnabled || intellijSeparateSdks
|
||||
val installIntellijUltimate = intellijUltimateEnabled
|
||||
val installIntellijUltimate = intellijUltimateEnabled && androidStudioRelease == null
|
||||
|
||||
val intellijVersionDelimiterIndex = intellijVersion.indexOfAny(charArrayOf('.', '-'))
|
||||
if (intellijVersionDelimiterIndex == -1) {
|
||||
@@ -34,7 +33,6 @@ if (intellijVersionDelimiterIndex == -1) {
|
||||
|
||||
val platformBaseVersion = intellijVersion.substring(0, intellijVersionDelimiterIndex)
|
||||
|
||||
logger.info("verifyDependencyOutput: $verifyDependencyOutput")
|
||||
logger.info("intellijUltimateEnabled: $intellijUltimateEnabled")
|
||||
logger.info("intellijVersion: $intellijVersion")
|
||||
logger.info("androidStudioRelease: $androidStudioRelease")
|
||||
@@ -145,21 +143,7 @@ dependencies {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val cleanupIntellijCore = tasks.register<CleanDataTask>("cleanupIntellijCore") {
|
||||
cleanableStoreProvider = provider { CleanableStore[repoDir.resolve("intellij-core").absolutePath] }
|
||||
}
|
||||
|
||||
val cleanupIntellijAnnotation = tasks.register<CleanDataTask>("cleanupIntellijAnnotation") {
|
||||
cleanableStoreProvider = provider { CleanableStore[repoDir.resolve(intellijRuntimeAnnotations).absolutePath] }
|
||||
}
|
||||
|
||||
val cleanupDependencies = tasks.register("cleanupDependencies") {
|
||||
dependsOn(cleanupIntellijCore)
|
||||
dependsOn(cleanupIntellijAnnotation)
|
||||
}
|
||||
|
||||
val makeIntellijCore = buildIvyRepositoryTaskAndRegisterCleanupTask(intellijCore, customDepsOrg, customDepsRepoDir)
|
||||
val makeIntellijCore = buildIvyRepositoryTask(intellijCore, customDepsOrg, customDepsRepoDir)
|
||||
|
||||
val makeIntellijAnnotations by tasks.registering(Copy::class) {
|
||||
dependsOn(makeIntellijCore)
|
||||
@@ -167,12 +151,17 @@ val makeIntellijAnnotations by tasks.registering(Copy::class) {
|
||||
val intellijCoreRepo = CleanableStore[repoDir.resolve("intellij-core").absolutePath][intellijVersion].use()
|
||||
from(intellijCoreRepo.resolve("artifacts/annotations.jar"))
|
||||
|
||||
val targetDir = CleanableStore[repoDir.resolve(intellijRuntimeAnnotations).absolutePath][intellijVersion].use()
|
||||
val annotationsStore = CleanableStore[repoDir.resolve(intellijRuntimeAnnotations).absolutePath]
|
||||
val targetDir = annotationsStore[intellijVersion].use()
|
||||
into(targetDir)
|
||||
|
||||
val ivyFile = File(targetDir, "$intellijRuntimeAnnotations.ivy.xml")
|
||||
outputs.files(ivyFile)
|
||||
|
||||
doFirst {
|
||||
annotationsStore.cleanStore()
|
||||
}
|
||||
|
||||
doLast {
|
||||
writeIvyXml(
|
||||
customDepsOrg,
|
||||
@@ -191,7 +180,9 @@ val mergeSources by tasks.creating(Jar::class.java) {
|
||||
dependsOn(sources)
|
||||
isPreserveFileTimestamps = false
|
||||
isReproducibleFileOrder = true
|
||||
from(provider { sources.map(::zipTree) })
|
||||
if (!kotlinBuildProperties.isTeamcityBuild) {
|
||||
from(provider { sources.map(::zipTree) })
|
||||
}
|
||||
destinationDirectory.set(File(repoDir, sources.name))
|
||||
archiveBaseName.set("intellij")
|
||||
archiveClassifier.set("sources")
|
||||
@@ -201,7 +192,7 @@ val mergeSources by tasks.creating(Jar::class.java) {
|
||||
val sourcesFile = mergeSources.outputs.files.singleFile
|
||||
|
||||
val makeIde = if (androidStudioBuild != null) {
|
||||
buildIvyRepositoryTaskAndRegisterCleanupTask(
|
||||
buildIvyRepositoryTask(
|
||||
androidStudio,
|
||||
customDepsOrg,
|
||||
customDepsRepoDir,
|
||||
@@ -212,9 +203,9 @@ val makeIde = if (androidStudioBuild != null) {
|
||||
)
|
||||
} else {
|
||||
val task = if (installIntellijUltimate) {
|
||||
buildIvyRepositoryTaskAndRegisterCleanupTask(intellijUltimate, customDepsOrg, customDepsRepoDir, null, sourcesFile)
|
||||
buildIvyRepositoryTask(intellijUltimate, customDepsOrg, customDepsRepoDir, null, sourcesFile)
|
||||
} else {
|
||||
buildIvyRepositoryTaskAndRegisterCleanupTask(intellij, customDepsOrg, customDepsRepoDir, null, sourcesFile)
|
||||
buildIvyRepositoryTask(intellij, customDepsOrg, customDepsRepoDir, null, sourcesFile)
|
||||
}
|
||||
|
||||
task.configure {
|
||||
@@ -224,7 +215,7 @@ val makeIde = if (androidStudioBuild != null) {
|
||||
task
|
||||
}
|
||||
|
||||
val buildJpsStandalone = buildIvyRepositoryTaskAndRegisterCleanupTask(jpsStandalone, customDepsOrg, customDepsRepoDir, null, sourcesFile)
|
||||
val buildJpsStandalone = buildIvyRepositoryTask(jpsStandalone, customDepsOrg, customDepsRepoDir, null, sourcesFile)
|
||||
|
||||
tasks.named("build") {
|
||||
dependsOn(
|
||||
@@ -238,24 +229,15 @@ tasks.named("build") {
|
||||
|
||||
if (installIntellijUltimate) {
|
||||
val buildNodeJsPlugin =
|
||||
buildIvyRepositoryTaskAndRegisterCleanupTask(nodeJSPlugin, customDepsOrg, customDepsRepoDir, ::skipToplevelDirectory, sourcesFile)
|
||||
buildIvyRepositoryTask(nodeJSPlugin, customDepsOrg, customDepsRepoDir, ::skipToplevelDirectory, sourcesFile)
|
||||
tasks.named("build") { dependsOn(buildNodeJsPlugin) }
|
||||
}
|
||||
|
||||
tasks.named("build") { dependsOn(cleanupDependencies) }
|
||||
|
||||
// Task to delete legacy repo locations
|
||||
tasks.register<Delete>("cleanLegacy") {
|
||||
delete("$projectDir/android-dx")
|
||||
delete("$projectDir/intellij-sdk")
|
||||
}
|
||||
|
||||
tasks.named<Delete>("clean") {
|
||||
//TODO specify repos to clean? Use CleanDataTask
|
||||
delete(customDepsRepoDir)
|
||||
}
|
||||
|
||||
fun buildIvyRepositoryTaskAndRegisterCleanupTask(
|
||||
fun buildIvyRepositoryTask(
|
||||
configuration: Configuration,
|
||||
organization: String,
|
||||
repoDirectory: File,
|
||||
@@ -268,29 +250,29 @@ fun buildIvyRepositoryTaskAndRegisterCleanupTask(
|
||||
fun ResolvedArtifact.moduleDirectory(): File =
|
||||
storeDirectory()[moduleVersion.id.version].use()
|
||||
|
||||
val buildIvyRepositoryTask = tasks.register("buildIvyRepositoryFor${configuration.name.capitalize()}") {
|
||||
return tasks.register("buildIvyRepositoryFor${configuration.name.capitalize()}") {
|
||||
dependsOn(configuration)
|
||||
inputs.files(configuration)
|
||||
|
||||
if (verifyDependencyOutput) {
|
||||
outputs.dir(
|
||||
provider {
|
||||
configuration.resolvedConfiguration.resolvedArtifacts.single().moduleDirectory()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
outputs.upToDateWhen {
|
||||
configuration.resolvedConfiguration.resolvedArtifacts.single()
|
||||
.moduleDirectory()
|
||||
.exists()
|
||||
}
|
||||
outputs.upToDateWhen {
|
||||
configuration.resolvedConfiguration.resolvedArtifacts.single()
|
||||
.moduleDirectory()
|
||||
.exists()
|
||||
}
|
||||
|
||||
doFirst {
|
||||
configuration.resolvedConfiguration.resolvedArtifacts.single().run {
|
||||
val moduleDirectory = moduleDirectory()
|
||||
val artifactsDirectory = File(moduleDirectory(), "artifacts")
|
||||
val artifact = configuration.resolvedConfiguration.resolvedArtifacts.single()
|
||||
val moduleDirectory = artifact.moduleDirectory()
|
||||
|
||||
artifact.storeDirectory().cleanStore()
|
||||
|
||||
if (moduleDirectory.exists()) {
|
||||
logger.info("Path ${moduleDirectory.absolutePath} already exists, skipping unpacking.")
|
||||
return@doFirst
|
||||
}
|
||||
|
||||
with(artifact) {
|
||||
val artifactsDirectory = File(moduleDirectory, "artifacts")
|
||||
logger.info("Unpacking ${file.name} into ${artifactsDirectory.absolutePath}")
|
||||
copy {
|
||||
val fileTree = when (extension) {
|
||||
@@ -347,20 +329,10 @@ fun buildIvyRepositoryTaskAndRegisterCleanupTask(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val cleanupIvyRepositoryTask = tasks.register<CleanDataTask>("cleanupIvyRepositoryFor${configuration.name.capitalize()}") {
|
||||
cleanableStoreProvider = provider {
|
||||
configuration.resolvedConfiguration.resolvedArtifacts.single().storeDirectory()
|
||||
}
|
||||
}
|
||||
|
||||
cleanupDependencies {
|
||||
dependsOn(cleanupIvyRepositoryTask)
|
||||
}
|
||||
|
||||
return buildIvyRepositoryTask
|
||||
}
|
||||
|
||||
fun CleanableStore.cleanStore() = cleanDir(Instant.now().minus(Duration.ofDays(30)))
|
||||
|
||||
fun writeIvyXml(
|
||||
organization: String,
|
||||
moduleName: String,
|
||||
|
||||
@@ -27,8 +27,6 @@ buildscript {
|
||||
def buildProperties = BuildPropertiesKt.getKotlinBuildPropertiesForSettings(settings)
|
||||
def projectVersions = file("../gradle/versions.properties").text
|
||||
|
||||
BuildCacheKt.setupBuildCache(settings)
|
||||
|
||||
include "prepare-deps"
|
||||
|
||||
def target_AppCode_Clion = buildProperties.includeCidrPlugins && !projectVersions.contains("versions.androidStudioRelease")
|
||||
|
||||
@@ -26,7 +26,7 @@ fun CompatibilityPredicate.or(other: CompatibilityPredicate): CompatibilityPredi
|
||||
}
|
||||
|
||||
enum class Platform : CompatibilityPredicate {
|
||||
P183, P191, P192, P193, P201;
|
||||
P183, P191, P192, P193, P201, P202;
|
||||
|
||||
val version: Int = name.drop(1).toInt()
|
||||
|
||||
@@ -47,10 +47,12 @@ enum class Ide(val platform: Platform) : CompatibilityPredicate {
|
||||
IJ192(Platform.P192),
|
||||
IJ193(Platform.P193),
|
||||
IJ201(Platform.P201),
|
||||
IJ202(Platform.P202),
|
||||
|
||||
AS35(Platform.P183),
|
||||
AS36(Platform.P192),
|
||||
AS40(Platform.P193);
|
||||
AS40(Platform.P193),
|
||||
AS41(Platform.P201);
|
||||
|
||||
val kind = Kind.values().first { it.shortName == name.take(2) }
|
||||
val version = name.dropWhile { !it.isDigit() }.toInt()
|
||||
|
||||
@@ -9,10 +9,12 @@ import org.gradle.api.artifacts.ConfigurationContainer
|
||||
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
||||
import org.gradle.api.file.DuplicatesStrategy
|
||||
import org.gradle.api.plugins.BasePluginConvention
|
||||
import org.gradle.api.plugins.JavaPlugin
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.api.tasks.Upload
|
||||
import org.gradle.api.tasks.javadoc.Javadoc
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
|
||||
@@ -155,6 +157,81 @@ fun Project.publish(body: Upload.() -> Unit = {}): Upload {
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.idePluginDependency(block: () -> Unit) {
|
||||
val shouldActivate = rootProject.findProperty("publish.ide.plugin.dependencies")?.toString()?.toBoolean() == true
|
||||
if (shouldActivate) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.publishProjectJars(projects: List<String>, libraryDependencies: List<String> = emptyList()) {
|
||||
apply<JavaPlugin>()
|
||||
|
||||
val fatJarContents by configurations.creating
|
||||
|
||||
dependencies {
|
||||
for (projectName in projects) {
|
||||
fatJarContents(project(projectName)) { isTransitive = false }
|
||||
}
|
||||
|
||||
for (libraryDependency in libraryDependencies) {
|
||||
fatJarContents(libraryDependency)
|
||||
}
|
||||
}
|
||||
|
||||
publish()
|
||||
|
||||
val jar: Jar by tasks
|
||||
|
||||
jar.apply {
|
||||
dependsOn(fatJarContents)
|
||||
|
||||
from {
|
||||
fatJarContents.map(::zipTree)
|
||||
}
|
||||
}
|
||||
|
||||
sourcesJar {
|
||||
from {
|
||||
projects.map {
|
||||
project(it).mainSourceSet.allSource
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
javadocJar()
|
||||
}
|
||||
|
||||
fun Project.publishTestJar(projectName: String) {
|
||||
apply<JavaPlugin>()
|
||||
|
||||
val fatJarContents by configurations.creating
|
||||
|
||||
dependencies {
|
||||
fatJarContents(project(projectName, configuration = "tests-jar")) { isTransitive = false }
|
||||
}
|
||||
|
||||
publish()
|
||||
|
||||
val jar: Jar by tasks
|
||||
|
||||
jar.apply {
|
||||
dependsOn(fatJarContents)
|
||||
|
||||
from {
|
||||
fatJarContents.map(::zipTree)
|
||||
}
|
||||
}
|
||||
|
||||
sourcesJar {
|
||||
from {
|
||||
project(projectName).testSourceSet.allSource
|
||||
}
|
||||
}
|
||||
|
||||
javadocJar()
|
||||
}
|
||||
|
||||
fun ConfigurationContainer.getOrCreate(name: String): Configuration = findByName(name) ?: create(name)
|
||||
|
||||
fun Jar.setupPublicJar(baseName: String, classifier: String = "") {
|
||||
|
||||
@@ -20,7 +20,6 @@ val packagesToRelocate =
|
||||
"org.picocontainer",
|
||||
"org.jline",
|
||||
"org.fusesource",
|
||||
"kotlinx.coroutines",
|
||||
"net.jpountz",
|
||||
"one.util.streamex",
|
||||
"kotlinx.collections.immutable"
|
||||
|
||||
@@ -106,6 +106,7 @@ fun Project.projectTest(
|
||||
environment("PROJECT_BUILD_DIR", buildDir)
|
||||
systemProperty("jps.kotlin.home", rootProject.extra["distKotlinHomeDir"]!!)
|
||||
systemProperty("kotlin.ni", if (rootProject.hasProperty("newInferenceTests")) "true" else "false")
|
||||
systemProperty("org.jetbrains.kotlin.skip.muted.tests", if (rootProject.hasProperty("skipMutedTests")) "true" else "false")
|
||||
|
||||
var subProjectTempRoot: Path? = null
|
||||
doFirst {
|
||||
|
||||
@@ -100,9 +100,11 @@ class CodegenTestsOnAndroidGenerator private constructor(private val pathManager
|
||||
File("./gradlew.bat").copyTo(File(projectRoot, "gradlew.bat"));
|
||||
val file = File(target, "gradle-wrapper.properties")
|
||||
file.readLines().map {
|
||||
if (it.startsWith("distributionUrl"))
|
||||
"distributionUrl=https\\://services.gradle.org/distributions/gradle-$GRADLE_VERSION-bin.zip"
|
||||
else it
|
||||
when {
|
||||
it.startsWith("distributionUrl") -> "distributionUrl=https\\://services.gradle.org/distributions/gradle-$GRADLE_VERSION-bin.zip"
|
||||
it.startsWith("distributionSha256Sum") -> "distributionSha256Sum=$GRADLE_SHA_256"
|
||||
else -> it
|
||||
}
|
||||
}.let { lines ->
|
||||
FileWriter(file).use { fw ->
|
||||
lines.forEach { line ->
|
||||
@@ -315,7 +317,8 @@ class CodegenTestsOnAndroidGenerator private constructor(private val pathManager
|
||||
CodegenTestCase.createTestFilesFromFile(file, expectedText, "kotlin.coroutines", false, TargetBackend.JVM)
|
||||
|
||||
companion object {
|
||||
const val GRADLE_VERSION = "5.6.4"
|
||||
const val GRADLE_VERSION = "5.6.4" // update GRADLE_SHA_256 on change
|
||||
const val GRADLE_SHA_256 = "1f3067073041bc44554d0efe5d402a33bc3d3c93cc39ab684f308586d732a80d"
|
||||
const val testClassPackage = "org.jetbrains.kotlin.android.tests"
|
||||
const val testClassName = "CodegenTestCaseOnAndroid"
|
||||
const val baseTestClassPackage = "org.jetbrains.kotlin.android.tests"
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.overriddenTreeUniqueAsSequence
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import org.jetbrains.kotlin.utils.mapToIndex
|
||||
|
||||
@@ -35,10 +37,10 @@ abstract class ArgumentGenerator {
|
||||
* @see kotlin.reflect.jvm.internal.KCallableImpl.callBy
|
||||
*/
|
||||
open fun generate(
|
||||
valueArgumentsByIndex: List<ResolvedValueArgument>,
|
||||
actualArgs: List<ResolvedValueArgument>,
|
||||
// may be null for a constructor of an object literal
|
||||
calleeDescriptor: CallableDescriptor?
|
||||
valueArgumentsByIndex: List<ResolvedValueArgument>,
|
||||
actualArgs: List<ResolvedValueArgument>,
|
||||
// may be null for a constructor of an object literal
|
||||
calleeDescriptor: CallableDescriptor?
|
||||
): DefaultCallArgs {
|
||||
assert(valueArgumentsByIndex.size == actualArgs.size) {
|
||||
"Value arguments collection should have same size, but ${valueArgumentsByIndex.size} != ${actualArgs.size}"
|
||||
@@ -50,9 +52,9 @@ abstract class ArgumentGenerator {
|
||||
ArgumentAndDeclIndex(it, arg2Index[it]!!)
|
||||
}.toMutableList()
|
||||
|
||||
valueArgumentsByIndex.withIndex().forEach {
|
||||
if (it.value is DefaultValueArgument) {
|
||||
actualArgsWithDeclIndex.add(it.index, ArgumentAndDeclIndex(it.value, it.index))
|
||||
for ((index, value) in valueArgumentsByIndex.withIndex()) {
|
||||
if (value is DefaultValueArgument) {
|
||||
actualArgsWithDeclIndex.add(index, ArgumentAndDeclIndex(value, index))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,8 +72,7 @@ abstract class ArgumentGenerator {
|
||||
is DefaultValueArgument -> {
|
||||
if (calleeDescriptor?.defaultValueFromJava(declIndex) == true) {
|
||||
generateDefaultJava(declIndex, argument)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
defaultArgs.mark(declIndex)
|
||||
generateDefault(declIndex, argument)
|
||||
}
|
||||
@@ -116,11 +117,42 @@ abstract class ArgumentGenerator {
|
||||
}
|
||||
|
||||
private fun CallableDescriptor.defaultValueFromJava(index: Int): Boolean = DFS.ifAny(
|
||||
listOf(this),
|
||||
{ current -> current.original.overriddenDescriptors.map { it.original } },
|
||||
{ descriptor ->
|
||||
descriptor.original.overriddenDescriptors.isEmpty() &&
|
||||
descriptor is JavaCallableMemberDescriptor &&
|
||||
descriptor.valueParameters[index].declaresDefaultValue()
|
||||
}
|
||||
listOf(this),
|
||||
{ current -> current.original.overriddenDescriptors.map { it.original } },
|
||||
{ descriptor ->
|
||||
descriptor.original.overriddenDescriptors.isEmpty() &&
|
||||
descriptor is JavaCallableMemberDescriptor &&
|
||||
descriptor.valueParameters[index].declaresDefaultValue()
|
||||
}
|
||||
)
|
||||
|
||||
fun shouldInvokeDefaultArgumentsStub(resolvedCall: ResolvedCall<*>): Boolean {
|
||||
val descriptor = resolvedCall.resultingDescriptor
|
||||
val valueArgumentsByIndex = resolvedCall.valueArgumentsByIndex ?: return false
|
||||
for (index in valueArgumentsByIndex.indices) {
|
||||
val resolvedValueArgument = valueArgumentsByIndex[index]
|
||||
if (resolvedValueArgument is DefaultValueArgument && !descriptor.defaultValueFromJava(index)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun getFunctionWithDefaultArguments(functionDescriptor: FunctionDescriptor): FunctionDescriptor {
|
||||
if (functionDescriptor.containingDeclaration !is ClassDescriptor) return functionDescriptor
|
||||
if (functionDescriptor.overriddenDescriptors.isEmpty()) return functionDescriptor
|
||||
|
||||
// We are calling a function with some arguments mapped as defaults.
|
||||
// Multiple override-equivalent functions from different supertypes with (potentially different) default values
|
||||
// can't be overridden by any function in a subtype.
|
||||
// Also, a function overriding some other function can't introduce default parameter values.
|
||||
// Thus, among all overridden functions should be one (and only one) function
|
||||
// that doesn't override anything and has parameters with default values.
|
||||
return functionDescriptor.overriddenTreeUniqueAsSequence(true)
|
||||
.firstOrNull { function ->
|
||||
function.kind == CallableMemberDescriptor.Kind.DECLARATION &&
|
||||
function.overriddenDescriptors.isEmpty() &&
|
||||
function.valueParameters.any { valueParameter -> valueParameter.hasDefaultValue() }
|
||||
}
|
||||
?: functionDescriptor
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
@@ -20,7 +19,7 @@ import org.jetbrains.org.objectweb.asm.util.Printer
|
||||
class CallableMethod(
|
||||
override val owner: Type,
|
||||
private val defaultImplOwner: Type?,
|
||||
computeDefaultMethodDesc: () -> String,
|
||||
computeDefaultMethod: () -> Method,
|
||||
private val signature: JvmMethodSignature,
|
||||
val invokeOpcode: Int,
|
||||
override val dispatchReceiverType: Type?,
|
||||
@@ -32,7 +31,10 @@ class CallableMethod(
|
||||
val isInterfaceMethod: Boolean,
|
||||
private val isDefaultMethodInInterface: Boolean
|
||||
) : Callable {
|
||||
private val defaultMethodDesc: String by lazy(LazyThreadSafetyMode.PUBLICATION, computeDefaultMethodDesc)
|
||||
private val defaultImplMethod: Method by lazy(LazyThreadSafetyMode.PUBLICATION, computeDefaultMethod)
|
||||
|
||||
private val defaultImplMethodName: String get() = defaultImplMethod.name
|
||||
private val defaultMethodDesc: String get() = defaultImplMethod.descriptor
|
||||
|
||||
fun getValueParameters(): List<JvmMethodParameterSignature> =
|
||||
signature.valueParameters
|
||||
@@ -68,10 +70,14 @@ class CallableMethod(
|
||||
} else {
|
||||
v.visitMethodInsn(
|
||||
INVOKESTATIC, defaultImplOwner.internalName,
|
||||
method.name + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, defaultMethodDesc, isDefaultMethodInInterface
|
||||
defaultImplMethodName, defaultMethodDesc, isDefaultMethodInInterface
|
||||
)
|
||||
|
||||
StackValue.coerce(Type.getReturnType(defaultMethodDesc), Type.getReturnType(signature.asmMethod.descriptor), v)
|
||||
StackValue.coerce(
|
||||
Type.getReturnType(defaultMethodDesc),
|
||||
Type.getReturnType(signature.asmMethod.descriptor),
|
||||
v
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@ import static org.jetbrains.kotlin.codegen.AsmUtil.*;
|
||||
import static org.jetbrains.kotlin.codegen.CallableReferenceUtilKt.*;
|
||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isConst;
|
||||
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.CLOSURE;
|
||||
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.initDefaultSourceMappingIfNeeded;
|
||||
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION;
|
||||
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
|
||||
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
|
||||
@@ -170,7 +169,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
superInterfaceAsmTypes
|
||||
);
|
||||
|
||||
initDefaultSourceMappingIfNeeded(context, this, state);
|
||||
initDefaultSourceMappingIfNeeded();
|
||||
|
||||
v.visitSource(element.getContainingFile().getName(), null);
|
||||
}
|
||||
@@ -260,7 +259,9 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
Method method = v.getSerializationBindings().get(METHOD_FOR_FUNCTION, frontendFunDescriptor);
|
||||
assert method != null : "No method for " + frontendFunDescriptor;
|
||||
|
||||
FunctionDescriptor freeLambdaDescriptor = FakeDescriptorsForReferencesKt.createFreeFakeLambdaDescriptor(frontendFunDescriptor);
|
||||
FunctionDescriptor freeLambdaDescriptor = FakeDescriptorsForReferencesKt.createFreeFakeLambdaDescriptor(
|
||||
frontendFunDescriptor, state.getTypeApproximator()
|
||||
);
|
||||
v.getSerializationBindings().put(METHOD_FOR_FUNCTION, freeLambdaDescriptor, method);
|
||||
|
||||
DescriptorSerializer serializer =
|
||||
@@ -407,7 +408,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
if (generateBody) {
|
||||
mv.visitCode();
|
||||
InstructionAdapter iv = new InstructionAdapter(mv);
|
||||
CallableReferenceUtilKt.generateCallableReferenceSignature(iv, descriptor, state);
|
||||
CallableReferenceUtilKt.generateFunctionReferenceSignature(iv, descriptor, state);
|
||||
iv.areturn(JAVA_STRING_TYPE);
|
||||
FunctionCodegen.endVisit(iv, "function reference getSignature", element);
|
||||
}
|
||||
@@ -465,7 +466,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
assert functionReferenceTarget != null : "No function reference target: " + funDescriptor;
|
||||
generateCallableReferenceDeclarationContainerClass(iv, functionReferenceTarget, state);
|
||||
iv.aconst(functionReferenceTarget.getName().asString());
|
||||
CallableReferenceUtilKt.generateCallableReferenceSignature(iv, functionReferenceTarget, state);
|
||||
CallableReferenceUtilKt.generateFunctionReferenceSignature(iv, functionReferenceTarget, state);
|
||||
int flags =
|
||||
getCallableReferenceTopLevelFlag(functionReferenceTarget) +
|
||||
(calculateFunctionReferenceFlags(functionReferenceCall, funDescriptor) << 1);
|
||||
|
||||
@@ -219,6 +219,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
return parentCodegen;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public KotlinTypeMapper getTypeMapper() {
|
||||
return typeMapper;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ObjectLiteralResult generateObjectLiteral(@NotNull KtObjectLiteralExpression literal) {
|
||||
KtObjectDeclaration objectDeclaration = literal.getObjectDeclaration();
|
||||
@@ -2561,7 +2566,19 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
return intrinsic.toCallable(fd, superCall, resolvedCall, this);
|
||||
}
|
||||
|
||||
return typeMapper.mapToCallableMethod(SamCodegenUtil.resolveSamAdapter(fd), superCall, null, resolvedCall);
|
||||
fd = SamCodegenUtil.resolveSamAdapter(fd);
|
||||
|
||||
if (ArgumentGeneratorKt.shouldInvokeDefaultArgumentsStub(resolvedCall)) {
|
||||
// When we invoke a function with some arguments mapped as defaults,
|
||||
// we later reroute this call to an overridden function in a base class that processes the default arguments.
|
||||
// If the base class is generic, this overridden function can have a different Kotlin signature
|
||||
// (see KT-38681 and related issues).
|
||||
// Here we replace a function with a corresponding overridden function,
|
||||
// and the rest is figured out by argument generation and type mapper.
|
||||
fd = ArgumentGeneratorKt.getFunctionWithDefaultArguments(fd);
|
||||
}
|
||||
|
||||
return typeMapper.mapToCallableMethod(fd, superCall, null, resolvedCall);
|
||||
}
|
||||
|
||||
public void invokeMethodWithArguments(
|
||||
|
||||
@@ -1038,10 +1038,7 @@ public class FunctionCodegen {
|
||||
// or all return types are supertypes of inline class (and can't be inline classes).
|
||||
|
||||
for (DescriptorBasedFunctionHandleForJvm handle : bridge.getOriginalFunctions()) {
|
||||
KotlinType returnType = handle.getDescriptor().getReturnType();
|
||||
if (returnType != null) {
|
||||
return returnType;
|
||||
}
|
||||
return state.getTypeMapper().getReturnValueType(handle.getDescriptor());
|
||||
}
|
||||
|
||||
if (state.getClassBuilderMode().mightBeIncorrectCode) {
|
||||
@@ -1450,8 +1447,8 @@ public class FunctionCodegen {
|
||||
}
|
||||
}
|
||||
|
||||
KotlinType returnType = descriptor.getReturnType();
|
||||
StackValue.coerce(delegateTo.getReturnType(), returnType, bridge.getReturnType(), bridgeReturnType, iv);
|
||||
KotlinType returnValueType = state.getTypeMapper().getReturnValueType(descriptor);
|
||||
StackValue.coerce(delegateTo.getReturnType(), returnValueType, bridge.getReturnType(), bridgeReturnType, iv);
|
||||
iv.areturn(bridge.getReturnType());
|
||||
|
||||
endVisit(mv, "bridge method", origin);
|
||||
@@ -1619,7 +1616,7 @@ public class FunctionCodegen {
|
||||
)
|
||||
);
|
||||
|
||||
stackValue.put(delegateMethod.getReturnType(), delegatedTo.getReturnType(), iv);
|
||||
stackValue.put(delegateMethod.getReturnType(), delegateFunction.getReturnType(), iv);
|
||||
|
||||
iv.areturn(delegateMethod.getReturnType());
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -68,7 +68,6 @@ import static org.jetbrains.kotlin.codegen.CodegenUtilKt.isNonGenericToArray;
|
||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
|
||||
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.enumEntryNeedSubclass;
|
||||
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.getDelegatedLocalVariableMetadata;
|
||||
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.initDefaultSourceMappingIfNeeded;
|
||||
import static org.jetbrains.kotlin.load.java.JvmAbi.*;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContext.INDEXED_LVALUE_GET;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContext.INDEXED_LVALUE_SET;
|
||||
@@ -228,7 +227,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
|
||||
v.visitSource(myClass.getContainingKtFile().getName(), null);
|
||||
|
||||
initDefaultSourceMappingIfNeeded(context, this, state);
|
||||
initDefaultSourceMappingIfNeeded();
|
||||
|
||||
writeEnclosingMethod();
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.backend.common.bridges.findImplementationFromInterfa
|
||||
import org.jetbrains.kotlin.backend.common.bridges.firstSuperMethodFromKotlin
|
||||
import org.jetbrains.kotlin.codegen.context.ClassContext
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.codegen.state.JvmMethodExceptionTypes
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtPureClassOrObject
|
||||
@@ -167,7 +168,7 @@ class InterfaceImplBodyCodegen(
|
||||
name: String,
|
||||
desc: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
exceptions: JvmMethodExceptionTypes
|
||||
): MethodVisitor {
|
||||
if (shouldCount) {
|
||||
isAnythingGenerated = true
|
||||
|
||||
@@ -13,9 +13,9 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
|
||||
import org.jetbrains.kotlin.codegen.context.*;
|
||||
import org.jetbrains.kotlin.codegen.inline.DefaultSourceMapper;
|
||||
import org.jetbrains.kotlin.codegen.inline.NameGenerator;
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeParametersUsages;
|
||||
import org.jetbrains.kotlin.codegen.inline.SourceMapper;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||
import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt;
|
||||
@@ -88,7 +88,8 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
||||
private NameGenerator inlineNameGenerator;
|
||||
private boolean jvmAssertFieldGenerated;
|
||||
|
||||
private DefaultSourceMapper sourceMapper;
|
||||
private boolean alwaysWriteSourceMap;
|
||||
private SourceMapper sourceMapper;
|
||||
|
||||
public MemberCodegen(
|
||||
@NotNull GenerationState state,
|
||||
@@ -182,8 +183,8 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
||||
|
||||
writeInnerClasses();
|
||||
|
||||
if (sourceMapper != null) {
|
||||
v.visitSMAP(sourceMapper, !state.getLanguageVersionSettings().supportsFeature(LanguageFeature.CorrectSourceMappingSyntax));
|
||||
if (alwaysWriteSourceMap || (sourceMapper != null && !sourceMapper.isTrivial())) {
|
||||
v.visitSMAP(getOrCreateSourceMapper(), !state.getLanguageVersionSettings().supportsFeature(LanguageFeature.CorrectSourceMappingSyntax));
|
||||
}
|
||||
|
||||
v.done();
|
||||
@@ -467,7 +468,7 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
||||
Name.special("<clinit>"), SYNTHESIZED, KotlinSourceElementKt.toSourceElement(element));
|
||||
clInit.initialize(null, null, Collections.emptyList(), Collections.emptyList(),
|
||||
DescriptorUtilsKt.getModule(descriptor).getBuiltIns().getUnitType(),
|
||||
null, Visibilities.PRIVATE);
|
||||
Modality.FINAL, Visibilities.PRIVATE);
|
||||
return clInit;
|
||||
}
|
||||
|
||||
@@ -679,7 +680,7 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
||||
}
|
||||
|
||||
iv.aconst(property.getName().asString());
|
||||
CallableReferenceUtilKt.generateCallableReferenceSignature(iv, property, state);
|
||||
CallableReferenceUtilKt.generatePropertyReferenceSignature(iv, property, state);
|
||||
superCtorArgTypes.add(JAVA_STRING_TYPE);
|
||||
superCtorArgTypes.add(JAVA_STRING_TYPE);
|
||||
|
||||
@@ -722,14 +723,27 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DefaultSourceMapper getOrCreateSourceMapper() {
|
||||
public SourceMapper getOrCreateSourceMapper() {
|
||||
if (sourceMapper == null) {
|
||||
// note: this is used in InlineCodegen and the element is always physical (KtElement) there
|
||||
sourceMapper = new DefaultSourceMapper(SourceInfo.Companion.createInfo((KtElement)element, getClassName()));
|
||||
sourceMapper = new SourceMapper(SourceInfo.Companion.createInfo((KtElement)element, getClassName()));
|
||||
}
|
||||
return sourceMapper;
|
||||
}
|
||||
|
||||
protected void initDefaultSourceMappingIfNeeded() {
|
||||
if (state.isInlineDisabled()) return;
|
||||
|
||||
CodegenContext parentContext = context.getParentContext();
|
||||
while (parentContext != null) {
|
||||
if (parentContext.isInlineMethodContext()) {
|
||||
alwaysWriteSourceMap = true;
|
||||
return;
|
||||
}
|
||||
parentContext = parentContext.getParentContext();
|
||||
}
|
||||
}
|
||||
|
||||
protected void generateConstInstance(@NotNull Type thisAsmType, @NotNull Type fieldAsmType) {
|
||||
v.newField(
|
||||
JvmDeclarationOriginKt.OtherOriginFromPure(element), ACC_STATIC | ACC_FINAL | ACC_PUBLIC, JvmAbi.INSTANCE_FIELD,
|
||||
|
||||
@@ -53,7 +53,8 @@ internal class ObjectSuperCallArgumentGenerator(
|
||||
valueArgumentsByIndex: List<ResolvedValueArgument>,
|
||||
actualArgs: List<ResolvedValueArgument>,
|
||||
calleeDescriptor: CallableDescriptor?
|
||||
): DefaultCallArgs = super.generate(valueArgumentsByIndex, valueArgumentsByIndex, calleeDescriptor)
|
||||
): DefaultCallArgs =
|
||||
super.generate(valueArgumentsByIndex, valueArgumentsByIndex, calleeDescriptor)
|
||||
|
||||
public override fun generateExpression(i: Int, argument: ExpressionValueArgument) {
|
||||
generateSuperCallArgument(i)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.codegen.state.JvmMethodExceptionTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
@@ -42,7 +43,7 @@ class OriginCollectingClassBuilderFactory(private val builderMode: ClassBuilderM
|
||||
name: String,
|
||||
desc: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
exceptions: JvmMethodExceptionTypes
|
||||
): MethodVisitor {
|
||||
val methodNode = super.newMethod(origin, access, name, desc, signature, exceptions) as MethodNode
|
||||
origins[methodNode] = origin
|
||||
|
||||
@@ -13,12 +13,13 @@ enum class OwnerKind {
|
||||
PACKAGE,
|
||||
IMPLEMENTATION,
|
||||
DEFAULT_IMPLS,
|
||||
ERASED_INLINE_CLASS;
|
||||
ERASED_INLINE_CLASS,
|
||||
PROPERTY_REFERENCE_SIGNATURE;
|
||||
|
||||
companion object {
|
||||
fun getMemberOwnerKind(descriptor: DeclarationDescriptor): OwnerKind = when (descriptor) {
|
||||
is PackageFragmentDescriptor -> OwnerKind.PACKAGE
|
||||
is ClassDescriptor -> OwnerKind.IMPLEMENTATION
|
||||
is PackageFragmentDescriptor -> PACKAGE
|
||||
is ClassDescriptor -> IMPLEMENTATION
|
||||
else -> throw AssertionError("Unexpected declaration container: $this")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||
import org.jetbrains.kotlin.config.LanguageFeature;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtilKt;
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
@@ -52,6 +51,7 @@ import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvmInterface;
|
||||
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
|
||||
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.*;
|
||||
import static org.jetbrains.kotlin.diagnostics.Errors.EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND;
|
||||
import static org.jetbrains.kotlin.fileClasses.JvmFileClassUtilKt.isTopLevelInJvmMultifileClass;
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject;
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isInterface;
|
||||
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.K_PROPERTY_TYPE;
|
||||
@@ -135,10 +135,10 @@ public class PropertyCodegen {
|
||||
|
||||
boolean isDefaultGetterAndSetter = isDefaultAccessor(getter) && isDefaultAccessor(setter);
|
||||
|
||||
if (isAccessorNeeded(declaration, descriptor, getter, isDefaultGetterAndSetter)) {
|
||||
if (isAccessorNeeded(descriptor, getter, isDefaultGetterAndSetter)) {
|
||||
generateGetter(descriptor, getter);
|
||||
}
|
||||
if (isAccessorNeeded(declaration, descriptor, setter, isDefaultGetterAndSetter)) {
|
||||
if (isAccessorNeeded(descriptor, setter, isDefaultGetterAndSetter)) {
|
||||
generateSetter(descriptor, setter);
|
||||
}
|
||||
}
|
||||
@@ -167,17 +167,40 @@ public class PropertyCodegen {
|
||||
generateSyntheticMethodIfNeeded(descriptor, isBackingFieldOwner);
|
||||
}
|
||||
|
||||
private boolean isAccessorNeeded(
|
||||
@NotNull PropertyDescriptor descriptor,
|
||||
@Nullable KtPropertyAccessor accessor,
|
||||
boolean isDefaultGetterAndSetter
|
||||
) {
|
||||
return isAccessorNeeded(descriptor, accessor, isDefaultGetterAndSetter, kind);
|
||||
}
|
||||
|
||||
public static boolean isReferenceablePropertyWithGetter(@NotNull PropertyDescriptor descriptor) {
|
||||
PsiElement psiElement = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
|
||||
KtDeclaration ktDeclaration = psiElement instanceof KtDeclaration ? (KtDeclaration) psiElement : null;
|
||||
if (ktDeclaration instanceof KtProperty) {
|
||||
KtProperty ktProperty = (KtProperty) ktDeclaration;
|
||||
boolean isDefaultGetterAndSetter =
|
||||
isDefaultAccessor(ktProperty.getGetter()) && isDefaultAccessor(ktProperty.getSetter());
|
||||
return isAccessorNeeded(descriptor, ktProperty.getGetter(), isDefaultGetterAndSetter, OwnerKind.IMPLEMENTATION);
|
||||
} else if (ktDeclaration instanceof KtParameter) {
|
||||
return isAccessorNeeded(descriptor, null, true, OwnerKind.IMPLEMENTATION);
|
||||
} else {
|
||||
return isAccessorNeeded(descriptor, null, false, OwnerKind.IMPLEMENTATION);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if it's necessary to generate an accessor to the property, i.e. if this property can be referenced via getter/setter
|
||||
* for any reason
|
||||
*
|
||||
* @see JvmCodegenUtil#couldUseDirectAccessToProperty
|
||||
*/
|
||||
private boolean isAccessorNeeded(
|
||||
@NotNull KtProperty declaration,
|
||||
private static boolean isAccessorNeeded(
|
||||
@NotNull PropertyDescriptor descriptor,
|
||||
@Nullable KtPropertyAccessor accessor,
|
||||
boolean isDefaultGetterAndSetter
|
||||
boolean isDefaultGetterAndSetter,
|
||||
OwnerKind kind
|
||||
) {
|
||||
if (isConstOrHasJvmFieldAnnotation(descriptor)) return false;
|
||||
|
||||
@@ -187,7 +210,8 @@ public class PropertyCodegen {
|
||||
if (kind == OwnerKind.DEFAULT_IMPLS && isDefaultAccessor) return false;
|
||||
|
||||
// Delegated or extension properties can only be referenced via accessors
|
||||
if (declaration.hasDelegate() || declaration.getReceiverTypeReference() != null) return true;
|
||||
//noinspection deprecation
|
||||
if (descriptor.isDelegated() || descriptor.getExtensionReceiverParameter() != null) return true;
|
||||
|
||||
// Companion object properties should have accessors for non-private properties because these properties can be referenced
|
||||
// via getter/setter. But these accessors getter/setter are not required for private properties that have a default getter
|
||||
@@ -201,7 +225,7 @@ public class PropertyCodegen {
|
||||
}
|
||||
|
||||
// Non-const properties from multifile classes have accessors regardless of visibility
|
||||
if (isTopLevelPropertyInMultifileClass(declaration, descriptor)) return true;
|
||||
if (isTopLevelInJvmMultifileClass(descriptor)) return true;
|
||||
|
||||
// Private class properties have accessors only in cases when those accessors are non-trivial
|
||||
if (Visibilities.isPrivate(descriptor.getVisibility())) {
|
||||
@@ -215,15 +239,12 @@ public class PropertyCodegen {
|
||||
return !isDefaultAccessor;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
// Non-public API (private and internal) primary vals of inline classes don't have getter
|
||||
if (InlineClassesUtilsKt.isUnderlyingPropertyOfInlineClass(descriptor) && !descriptor.getVisibility().isPublicAPI()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isTopLevelPropertyInMultifileClass(
|
||||
@NotNull KtProperty declaration,
|
||||
@NotNull PropertyDescriptor descriptor
|
||||
) {
|
||||
return descriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor &&
|
||||
JvmFileClassUtilKt.isInsideJvmMultifileClassFile(declaration);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean areAccessorsNeededForPrimaryConstructorProperty(
|
||||
@@ -401,6 +422,14 @@ public class PropertyCodegen {
|
||||
}
|
||||
modifiers |= getVisibilityForBackingField(propertyDescriptor, isDelegate);
|
||||
|
||||
// If val is initialized in EXACTLY_ONCE closure, other class from the same package initializes it
|
||||
// so, its visibility should be package private and not final
|
||||
if (!propertyDescriptor.isVar() &&
|
||||
bindingContext.get(BindingContext.FIELD_CAPTURED_IN_EXACLY_ONCE_CLOSURE, propertyDescriptor) != null
|
||||
) {
|
||||
modifiers &= ~(ACC_PRIVATE | ACC_FINAL);
|
||||
}
|
||||
|
||||
if (AsmUtil.isPropertyWithBackingFieldCopyInOuterClass(propertyDescriptor)) {
|
||||
ImplementationBodyCodegen parentBodyCodegen = (ImplementationBodyCodegen) memberCodegen.getParentCodegen();
|
||||
parentBodyCodegen.addCompanionObjectPropertyToCopy(propertyDescriptor, defaultValue);
|
||||
|
||||
@@ -118,7 +118,7 @@ class PropertyReferenceCodegen(
|
||||
aconst(target.name.asString())
|
||||
}
|
||||
generateMethod("property reference getSignature", ACC_PUBLIC, method("getSignature", JAVA_STRING_TYPE)) {
|
||||
generateCallableReferenceSignature(this, target, state)
|
||||
generatePropertyReferenceSignature(this, target, state)
|
||||
}
|
||||
generateMethod("property reference getOwner", ACC_PUBLIC, method("getOwner", K_DECLARATION_CONTAINER_TYPE)) {
|
||||
generateCallableReferenceDeclarationContainer(this, target, state)
|
||||
@@ -148,7 +148,7 @@ class PropertyReferenceCodegen(
|
||||
if (isOptimizedPropertyReferenceSupertype(superAsmType)) {
|
||||
generateCallableReferenceDeclarationContainerClass(this, target, state)
|
||||
aconst(target.name.asString())
|
||||
generateCallableReferenceSignature(this, target, state)
|
||||
generatePropertyReferenceSignature(this, target, state)
|
||||
aconst(getCallableReferenceTopLevelFlag(target))
|
||||
superCtorArgTypes.add(JAVA_CLASS_TYPE)
|
||||
superCtorArgTypes.add(JAVA_STRING_TYPE)
|
||||
|
||||
@@ -27,22 +27,41 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
|
||||
import org.jetbrains.kotlin.load.java.sam.JavaSingleAbstractMethodUtils;
|
||||
import org.jetbrains.kotlin.resolve.sam.SamConversionResolverImplKt;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.KotlinTypeKt;
|
||||
import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
|
||||
|
||||
public class SamType {
|
||||
@Nullable
|
||||
public static SamType createByValueParameter(@NotNull ValueParameterDescriptor valueParameter) {
|
||||
KotlinType singleArgumentType;
|
||||
KotlinType originalSingleArgumentType;
|
||||
KotlinType varargElementType = valueParameter.getVarargElementType();
|
||||
if (varargElementType != null) {
|
||||
singleArgumentType = varargElementType;
|
||||
originalSingleArgumentType = valueParameter.getOriginal().getVarargElementType();
|
||||
assert originalSingleArgumentType != null :
|
||||
"Value parameter and original value parameter have inconsistent varargs: " +
|
||||
valueParameter + "; " + valueParameter.getOriginal();
|
||||
} else {
|
||||
singleArgumentType = valueParameter.getType();
|
||||
originalSingleArgumentType = valueParameter.getOriginal().getType();
|
||||
}
|
||||
|
||||
if (KotlinTypeKt.isError(singleArgumentType) || KotlinTypeKt.isError(originalSingleArgumentType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
KotlinType originalTypeToUse =
|
||||
// This can be true in case when the value parameter is in the method of a generic type with out-projection.
|
||||
// We approximate Inv<Captured#1> to Nothing, while Inv itself can be a SAM interface safe to call here
|
||||
// (see testData genericSamProjectedOut.kt for details)
|
||||
KotlinBuiltIns.isNothing(valueParameter.getType())
|
||||
KotlinBuiltIns.isNothing(singleArgumentType)
|
||||
// In such a case we can't have a proper supertype since wildcards are not allowed there,
|
||||
// so we use Nothing arguments instead that leads to a raw type used for a SAM wrapper.
|
||||
// See org.jetbrains.kotlin.codegen.state.KotlinTypeMapper#writeGenericType to understand how
|
||||
// raw types and Nothing arguments relate.
|
||||
? TypeUtilsKt.replaceArgumentsWithNothing(valueParameter.getOriginal().getType())
|
||||
: valueParameter.getType();
|
||||
? TypeUtilsKt.replaceArgumentsWithNothing(originalSingleArgumentType)
|
||||
: singleArgumentType;
|
||||
|
||||
return create(TypeMapperUtilsKt.removeExternalProjections(originalTypeToUse));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.containers.LinkedMultiMap
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.kotlin.codegen.state.JvmMethodExceptionTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ConflictingJvmDeclarationsData
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MemberKind
|
||||
@@ -63,7 +64,7 @@ abstract class SignatureCollectingClassBuilderFactory(
|
||||
return super.newField(origin, access, name, desc, signature, value)
|
||||
}
|
||||
|
||||
override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor {
|
||||
override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: JvmMethodExceptionTypes): MethodVisitor {
|
||||
signatures.putValue(RawSignature(name, desc, MemberKind.METHOD), origin)
|
||||
if (!shouldGenerate(origin)) {
|
||||
return AbstractClassBuilder.EMPTY_METHOD_VISITOR
|
||||
|
||||
@@ -42,10 +42,7 @@ import org.jetbrains.kotlin.resolve.BindingTrace;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*;
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.NewResolvedCallImpl;
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.NewVariableAsFunctionResolvedCallImpl;
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
|
||||
@@ -799,12 +796,16 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
CallableDescriptor descriptor = call.getResultingDescriptor();
|
||||
if (!(descriptor instanceof FunctionDescriptor)) return;
|
||||
|
||||
recordSamValueForNewInference(call);
|
||||
recordSamValuesForNewInference(call);
|
||||
recordSamConstructorIfNeeded(expression, call);
|
||||
recordSamValuesForOldInference(call, descriptor);
|
||||
}
|
||||
|
||||
private void recordSamValuesForOldInference(ResolvedCall<?> call, CallableDescriptor descriptor) {
|
||||
FunctionDescriptor original = SamCodegenUtil.getOriginalIfSamAdapter((FunctionDescriptor) descriptor);
|
||||
if (original == null) return;
|
||||
|
||||
// TODO we can just record SAM_VALUE on relevant value arguments as we do in recordSamValuesForNewInference
|
||||
List<ValueParameterDescriptor> valueParametersWithSAMConversion = new SmartList<>();
|
||||
for (ValueParameterDescriptor valueParameter : original.getValueParameters()) {
|
||||
ValueParameterDescriptor adaptedParameter = descriptor.getValueParameters().get(valueParameter.getIndex());
|
||||
@@ -814,30 +815,43 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
writeSamValueForValueParameters(valueParametersWithSAMConversion, call.getValueArgumentsByIndex());
|
||||
}
|
||||
|
||||
private void recordSamValueForNewInference(@NotNull ResolvedCall<?> call) {
|
||||
NewResolvedCallImpl<?> newResolvedCall = null;
|
||||
if (call instanceof NewVariableAsFunctionResolvedCallImpl) {
|
||||
newResolvedCall = ((NewVariableAsFunctionResolvedCallImpl) call).getFunctionCall();
|
||||
}
|
||||
else if(call instanceof NewResolvedCallImpl) {
|
||||
newResolvedCall = (NewResolvedCallImpl<?>) call;
|
||||
}
|
||||
private void recordSamValuesForNewInference(@NotNull ResolvedCall<?> call) {
|
||||
NewResolvedCallImpl<?> newResolvedCall = getNewResolvedCallForCallWithPossibleSamConversions(call);
|
||||
if (newResolvedCall == null) return;
|
||||
|
||||
List<ValueParameterDescriptor> valueParametersWithSAMConversion = new SmartList<>();
|
||||
Map<ValueParameterDescriptor, ResolvedValueArgument> arguments = newResolvedCall.getValueArguments();
|
||||
for (ValueParameterDescriptor valueParameter : arguments.keySet()) {
|
||||
SamType samType = SamType.createByValueParameter(valueParameter);
|
||||
if (samType == null) continue;
|
||||
|
||||
ResolvedValueArgument argument = arguments.get(valueParameter);
|
||||
|
||||
if (!(argument instanceof ExpressionValueArgument)) continue;
|
||||
ValueArgument valueArgument = ((ExpressionValueArgument) argument).getValueArgument();
|
||||
|
||||
if (valueArgument == null || newResolvedCall.getExpectedTypeForSamConvertedArgument(valueArgument) == null) continue;
|
||||
|
||||
valueParametersWithSAMConversion.add(valueParameter);
|
||||
if (argument instanceof ExpressionValueArgument) {
|
||||
ValueArgument valueArgument = ((ExpressionValueArgument) argument).getValueArgument();
|
||||
if (valueArgument != null && newResolvedCall.getExpectedTypeForSamConvertedArgument(valueArgument) != null) {
|
||||
recordSamTypeOnArgumentExpression(samType, valueArgument);
|
||||
}
|
||||
} else if (argument instanceof VarargValueArgument) {
|
||||
VarargValueArgument varargValueArgument = (VarargValueArgument) argument;
|
||||
for (ValueArgument valueArgument : varargValueArgument.getArguments()) {
|
||||
if (valueArgument != null && newResolvedCall.getExpectedTypeForSamConvertedArgument(valueArgument) != null) {
|
||||
recordSamTypeOnArgumentExpression(samType, valueArgument);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
writeSamValueForValueParameters(valueParametersWithSAMConversion, newResolvedCall.getValueArgumentsByIndex());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static NewResolvedCallImpl<?> getNewResolvedCallForCallWithPossibleSamConversions(@NotNull ResolvedCall<?> call) {
|
||||
if (call instanceof NewVariableAsFunctionResolvedCallImpl) {
|
||||
return ((NewVariableAsFunctionResolvedCallImpl) call).getFunctionCall();
|
||||
}
|
||||
else if (call instanceof NewResolvedCallImpl) {
|
||||
return (NewResolvedCallImpl<?>) call;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeSamValueForValueParameters(
|
||||
@@ -854,13 +868,17 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
assert resolvedValueArgument instanceof ExpressionValueArgument : resolvedValueArgument;
|
||||
ValueArgument valueArgument = ((ExpressionValueArgument) resolvedValueArgument).getValueArgument();
|
||||
assert valueArgument != null;
|
||||
KtExpression argumentExpression = valueArgument.getArgumentExpression();
|
||||
assert argumentExpression != null : valueArgument.asElement().getText();
|
||||
|
||||
bindingTrace.record(CodegenBinding.SAM_VALUE, argumentExpression, samType);
|
||||
recordSamTypeOnArgumentExpression(samType, valueArgument);
|
||||
}
|
||||
}
|
||||
|
||||
private void recordSamTypeOnArgumentExpression(SamType samType, ValueArgument valueArgument) {
|
||||
KtExpression argumentExpression = valueArgument.getArgumentExpression();
|
||||
assert argumentExpression != null : valueArgument.asElement().getText();
|
||||
|
||||
bindingTrace.record(CodegenBinding.SAM_VALUE, argumentExpression, samType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSuperTypeCallEntry(@NotNull KtSuperTypeCallEntry call) {
|
||||
// Closures in super type constructor calls for anonymous objects are created in outer context
|
||||
@@ -942,7 +960,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
if (!(operationDescriptor instanceof FunctionDescriptor)) return;
|
||||
|
||||
ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(expression, bindingContext);
|
||||
if (resolvedCall != null) recordSamValueForNewInference(resolvedCall);
|
||||
if (resolvedCall != null) recordSamValuesForNewInference(resolvedCall);
|
||||
|
||||
FunctionDescriptor original = SamCodegenUtil.getOriginalIfSamAdapter((FunctionDescriptor) operationDescriptor);
|
||||
if (original == null) return;
|
||||
@@ -967,7 +985,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
if (!(operationDescriptor instanceof FunctionDescriptor)) return;
|
||||
|
||||
ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(expression, bindingContext);
|
||||
if (resolvedCall != null) recordSamValueForNewInference(resolvedCall);
|
||||
if (resolvedCall != null) recordSamValuesForNewInference(resolvedCall);
|
||||
|
||||
boolean isSetter = operationDescriptor.getName().asString().equals("set");
|
||||
FunctionDescriptor original = SamCodegenUtil.getOriginalIfSamAdapter((FunctionDescriptor) operationDescriptor);
|
||||
|
||||
@@ -168,11 +168,15 @@ private fun isTopLevelCallableReference(descriptor: CallableDescriptor): Boolean
|
||||
internal fun getCallableReferenceTopLevelFlag(descriptor: CallableDescriptor): Int =
|
||||
if (isTopLevelCallableReference(descriptor)) 1 else 0
|
||||
|
||||
internal fun generateCallableReferenceSignature(iv: InstructionAdapter, callable: CallableDescriptor, state: GenerationState) {
|
||||
internal fun generateFunctionReferenceSignature(iv: InstructionAdapter, callable: CallableDescriptor, state: GenerationState) {
|
||||
iv.aconst(getSignatureString(callable, state))
|
||||
}
|
||||
|
||||
private fun getSignatureString(callable: CallableDescriptor, state: GenerationState): String {
|
||||
internal fun generatePropertyReferenceSignature(iv: InstructionAdapter, callable: CallableDescriptor, state: GenerationState) {
|
||||
iv.aconst(getSignatureString(callable, state, isPropertySignature = true))
|
||||
}
|
||||
|
||||
private fun getSignatureString(callable: CallableDescriptor, state: GenerationState, isPropertySignature: Boolean = false): String {
|
||||
if (callable is LocalVariableDescriptor) {
|
||||
val asmType = state.bindingContext.get(CodegenBinding.DELEGATED_PROPERTY_METADATA_OWNER, callable)
|
||||
?: throw AssertionError("No delegated property metadata owner for $callable")
|
||||
@@ -198,10 +202,13 @@ private fun getSignatureString(callable: CallableDescriptor, state: GenerationSt
|
||||
else -> error("Unsupported callable reference: $callable")
|
||||
}
|
||||
val declaration = DescriptorUtils.unwrapFakeOverride(accessor).original
|
||||
val method =
|
||||
if (callable.containingDeclaration.isInlineClass() && !declaration.isGetterOfUnderlyingPropertyOfInlineClass())
|
||||
val method = when {
|
||||
callable.containingDeclaration.isInlineClass() && !declaration.isGetterOfUnderlyingPropertyOfInlineClass() ->
|
||||
state.typeMapper.mapSignatureForInlineErasedClassSkipGeneric(declaration).asmMethod
|
||||
else
|
||||
isPropertySignature ->
|
||||
state.typeMapper.mapPropertyReferenceSignature(declaration)
|
||||
else ->
|
||||
state.typeMapper.mapAsmMethod(declaration)
|
||||
}
|
||||
return method.name + method.descriptor
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.
|
||||
* 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.codegen
|
||||
|
||||
@@ -60,6 +60,7 @@ 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.commons.Method
|
||||
import java.util.*
|
||||
|
||||
fun generateIsCheck(
|
||||
v: InstructionAdapter,
|
||||
|
||||
@@ -727,7 +727,9 @@ class CoroutineCodegenForNamedFunction private constructor(
|
||||
writeKotlinMetadata(v, state, KotlinClassHeader.Kind.SYNTHETIC_CLASS, 0) { av ->
|
||||
val serializer = DescriptorSerializer.createForLambda(JvmSerializerExtension(v.serializationBindings, state))
|
||||
val functionProto =
|
||||
serializer.functionProto(createFreeFakeLambdaDescriptor(suspendFunctionJvmView))?.build() ?: return@writeKotlinMetadata
|
||||
serializer.functionProto(
|
||||
createFreeFakeLambdaDescriptor(suspendFunctionJvmView, state.typeApproximator)
|
||||
)?.build() ?: return@writeKotlinMetadata
|
||||
AsmUtil.writeAnnotationData(av, serializer, functionProto)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,23 +5,16 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.coroutines
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.isUnitInstance
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
@@ -30,7 +23,6 @@ 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.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.*
|
||||
import kotlin.math.max
|
||||
|
||||
private const val COROUTINES_DEBUG_METADATA_VERSION = 1
|
||||
@@ -92,14 +84,14 @@ class CoroutineTransformerMethodVisitor(
|
||||
)
|
||||
|
||||
FixStackMethodTransformer().transform(containingClassInternalName, methodNode)
|
||||
RedundantLocalsEliminationMethodTransformer(languageVersionSettings).transform(containingClassInternalName, methodNode)
|
||||
val suspensionPoints = collectSuspensionPoints(methodNode)
|
||||
RedundantLocalsEliminationMethodTransformer(suspensionPoints)
|
||||
.transform(containingClassInternalName, methodNode)
|
||||
if (languageVersionSettings.isReleaseCoroutines()) {
|
||||
ChangeBoxingMethodTransformer.transform(containingClassInternalName, methodNode)
|
||||
}
|
||||
updateMaxStack(methodNode)
|
||||
|
||||
val suspensionPoints = collectSuspensionPoints(methodNode)
|
||||
|
||||
checkForSuspensionPointInsideMonitor(methodNode, suspensionPoints)
|
||||
|
||||
// First instruction in the method node may change in case of named function
|
||||
@@ -114,6 +106,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
languageVersionSettings,
|
||||
containingClassInternalName,
|
||||
methodNode,
|
||||
suspensionPoints,
|
||||
disableTailCallOptimizationForFunctionReturningUnit
|
||||
)
|
||||
if (examiner.allSuspensionPointsAreTailCalls(suspensionPoints)) {
|
||||
@@ -577,11 +570,9 @@ class CoroutineTransformerMethodVisitor(
|
||||
return false
|
||||
}
|
||||
|
||||
val starts = methodNode.instructions.asSequence().filter {
|
||||
isBeforeSuspendMarker(it) &&
|
||||
cfg.getPredecessorsIndices(it).isNotEmpty() // Ignore unreachable start markers
|
||||
}.toList()
|
||||
return starts.mapNotNull { start ->
|
||||
return methodNode.instructions.asSequence().filter {
|
||||
isBeforeSuspendMarker(it)
|
||||
}.mapNotNull { start ->
|
||||
val ends = mutableSetOf<AbstractInsnNode>()
|
||||
if (collectSuspensionPointEnds(start, mutableSetOf(), ends)) return@mapNotNull null
|
||||
// Ignore suspension points, if the suspension call begin is alive and suspension call end is dead
|
||||
@@ -590,7 +581,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
// this is an exit point for the corresponding coroutine.
|
||||
val end = ends.find { isAfterSuspendMarker(it) } ?: return@mapNotNull null
|
||||
SuspensionPoint(start.previous, end)
|
||||
}
|
||||
}.toList()
|
||||
}
|
||||
|
||||
private fun dropSuspensionMarkers(methodNode: MethodNode) {
|
||||
@@ -898,197 +889,6 @@ class CoroutineTransformerMethodVisitor(
|
||||
private data class SpilledVariableDescriptor(val fieldName: String, val variableName: String)
|
||||
}
|
||||
|
||||
// TODO Use this in variable liveness analysis
|
||||
private class MethodNodeExaminer(
|
||||
val languageVersionSettings: LanguageVersionSettings,
|
||||
val containingClassInternalName: String,
|
||||
val methodNode: MethodNode,
|
||||
disableTailCallOptimizationForFunctionReturningUnit: Boolean
|
||||
) {
|
||||
private val sourceFrames: Array<Frame<SourceValue>?> =
|
||||
MethodTransformer.analyze(containingClassInternalName, methodNode, IgnoringCopyOperationSourceInterpreter())
|
||||
private val controlFlowGraph = ControlFlowGraph.build(methodNode)
|
||||
|
||||
private val safeUnitInstances = mutableSetOf<AbstractInsnNode>()
|
||||
private val popsBeforeSafeUnitInstances = mutableSetOf<AbstractInsnNode>()
|
||||
private val areturnsAfterSafeUnitInstances = mutableSetOf<AbstractInsnNode>()
|
||||
private val meaningfulSuccessorsCache = hashMapOf<AbstractInsnNode, List<AbstractInsnNode>>()
|
||||
private val meaningfulPredecessorsCache = hashMapOf<AbstractInsnNode, List<AbstractInsnNode>>()
|
||||
|
||||
init {
|
||||
if (!disableTailCallOptimizationForFunctionReturningUnit) {
|
||||
// retrieve all POP insns
|
||||
val pops = methodNode.instructions.asSequence().filter { it.opcode == Opcodes.POP }
|
||||
// for each of them check that all successors are PUSH Unit
|
||||
val popsBeforeUnitInstances = pops.map { it to it.meaningfulSuccessors() }
|
||||
.filter { (_, succs) -> succs.all { it.isUnitInstance() } }
|
||||
.map { it.first }.toList()
|
||||
for (pop in popsBeforeUnitInstances) {
|
||||
val units = pop.meaningfulSuccessors()
|
||||
val allUnitsAreSafe = units.all { unit ->
|
||||
// check no other predecessor exists
|
||||
unit.meaningfulPredecessors().all { it in popsBeforeUnitInstances } &&
|
||||
// check they have only returns among successors
|
||||
unit.meaningfulSuccessors().all { it.opcode == Opcodes.ARETURN }
|
||||
}
|
||||
if (!allUnitsAreSafe) continue
|
||||
// save them all to the properties
|
||||
popsBeforeSafeUnitInstances += pop
|
||||
safeUnitInstances += units
|
||||
units.flatMapTo(areturnsAfterSafeUnitInstances) { it.meaningfulSuccessors() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.index() = methodNode.instructions.indexOf(this)
|
||||
|
||||
// GETSTATIC kotlin/Unit.INSTANCE is considered safe iff
|
||||
// it is part of POP, PUSH Unit, ARETURN sequence.
|
||||
private fun AbstractInsnNode.isSafeUnitInstance(): Boolean = this in safeUnitInstances
|
||||
|
||||
private fun AbstractInsnNode.isPopBeforeSafeUnitInstance(): Boolean = this in popsBeforeSafeUnitInstances
|
||||
private fun AbstractInsnNode.isAreturnAfterSafeUnitInstance(): Boolean = this in areturnsAfterSafeUnitInstances
|
||||
|
||||
private fun AbstractInsnNode.meaningfulSuccessors(): List<AbstractInsnNode> = meaningfulSuccessorsCache.getOrPut(this) {
|
||||
meaningfulSuccessorsOrPredecessors(true)
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.meaningfulPredecessors(): List<AbstractInsnNode> = meaningfulPredecessorsCache.getOrPut(this) {
|
||||
meaningfulSuccessorsOrPredecessors(false)
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.meaningfulSuccessorsOrPredecessors(isSuccessors: Boolean): List<AbstractInsnNode> {
|
||||
fun AbstractInsnNode.isMeaningful() = isMeaningful && opcode != Opcodes.NOP && opcode != Opcodes.GOTO && this !is LineNumberNode
|
||||
|
||||
fun AbstractInsnNode.getIndices() =
|
||||
if (isSuccessors) controlFlowGraph.getSuccessorsIndices(this)
|
||||
else controlFlowGraph.getPredecessorsIndices(this)
|
||||
|
||||
val visited = arrayListOf<AbstractInsnNode>()
|
||||
fun dfs(insn: AbstractInsnNode) {
|
||||
if (insn in visited) return
|
||||
visited += insn
|
||||
if (!insn.isMeaningful()) {
|
||||
for (succIndex in insn.getIndices()) {
|
||||
dfs(methodNode.instructions[succIndex])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (succIndex in getIndices()) {
|
||||
dfs(methodNode.instructions[succIndex])
|
||||
}
|
||||
return visited.filter { it.isMeaningful() }
|
||||
}
|
||||
|
||||
fun replacePopsBeforeSafeUnitInstancesWithCoroutineSuspendedChecks() {
|
||||
val basicAnalyser = Analyzer(BasicInterpreter())
|
||||
basicAnalyser.analyze(containingClassInternalName, methodNode)
|
||||
val typedFrames = basicAnalyser.frames
|
||||
|
||||
val isReferenceMap = popsBeforeSafeUnitInstances
|
||||
.map { it to (!isUnreachable(it.index(), sourceFrames) && typedFrames[it.index()]?.top()?.isReference == true) }
|
||||
.toMap()
|
||||
|
||||
for (pop in popsBeforeSafeUnitInstances) {
|
||||
if (isReferenceMap[pop] == true) {
|
||||
val label = Label()
|
||||
methodNode.instructions.insertBefore(pop, withInstructionAdapter {
|
||||
dup()
|
||||
loadCoroutineSuspendedMarker(languageVersionSettings)
|
||||
ifacmpne(label)
|
||||
areturn(AsmTypes.OBJECT_TYPE)
|
||||
mark(label)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun allSuspensionPointsAreTailCalls(suspensionPoints: List<SuspensionPoint>): Boolean {
|
||||
val safelyReachableReturns = findSafelyReachableReturns()
|
||||
|
||||
val instructions = methodNode.instructions
|
||||
return suspensionPoints.all { suspensionPoint ->
|
||||
val beginIndex = instructions.indexOf(suspensionPoint.suspensionCallBegin)
|
||||
val endIndex = instructions.indexOf(suspensionPoint.suspensionCallEnd)
|
||||
|
||||
if (isUnreachable(endIndex, sourceFrames)) return@all true
|
||||
|
||||
val insideTryBlock = methodNode.tryCatchBlocks.any { block ->
|
||||
val tryBlockStartIndex = instructions.indexOf(block.start)
|
||||
val tryBlockEndIndex = instructions.indexOf(block.end)
|
||||
|
||||
beginIndex in tryBlockStartIndex..tryBlockEndIndex
|
||||
}
|
||||
if (insideTryBlock) return@all false
|
||||
|
||||
safelyReachableReturns[endIndex + 1]?.all { returnIndex ->
|
||||
sourceFrames[returnIndex]?.top().sure {
|
||||
"There must be some value on stack to return"
|
||||
}.insns.any { sourceInsn ->
|
||||
sourceInsn?.let(instructions::indexOf) in beginIndex..endIndex
|
||||
}
|
||||
} ?: false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Let's call an instruction safe if its execution is always invisible: stack modifications, branching, variable insns (invisible in debug)
|
||||
*
|
||||
* For some instruction `insn` define the result as following:
|
||||
* - if there is a path leading to the non-safe instruction then result is `null`
|
||||
* - Otherwise result contains all the reachable ARETURN indices
|
||||
*
|
||||
* @return indices of safely reachable returns for each instruction in the method node
|
||||
*/
|
||||
private fun findSafelyReachableReturns(): Array<Set<Int>?> {
|
||||
val insns = methodNode.instructions
|
||||
val reachableReturnsIndices = Array<Set<Int>?>(insns.size()) init@{ index ->
|
||||
val insn = insns[index]
|
||||
|
||||
if (insn.opcode == Opcodes.ARETURN && !insn.isAreturnAfterSafeUnitInstance()) {
|
||||
if (isUnreachable(index, sourceFrames)) return@init null
|
||||
return@init setOf(index)
|
||||
}
|
||||
|
||||
// Since POP, PUSH Unit, ARETURN behaves like normal return in terms of tail-call optimization, set return index to POP
|
||||
if (insn.isPopBeforeSafeUnitInstance()) {
|
||||
return@init setOf(index)
|
||||
}
|
||||
|
||||
if (!insn.isMeaningful || insn.opcode in SAFE_OPCODES || insn.isInvisibleInDebugVarInsn(methodNode) || isInlineMarker(insn)
|
||||
|| insn.isSafeUnitInstance() || insn.isAreturnAfterSafeUnitInstance()
|
||||
) {
|
||||
setOf<Int>()
|
||||
} else null
|
||||
}
|
||||
|
||||
var changed: Boolean
|
||||
do {
|
||||
changed = false
|
||||
for (index in 0 until insns.size()) {
|
||||
if (insns[index].opcode == Opcodes.ARETURN) continue
|
||||
|
||||
@Suppress("RemoveExplicitTypeArguments")
|
||||
val newResult =
|
||||
controlFlowGraph
|
||||
.getSuccessorsIndices(index).plus(index)
|
||||
.map(reachableReturnsIndices::get)
|
||||
.fold<Set<Int>?, Set<Int>?>(mutableSetOf<Int>()) { acc, successorsResult ->
|
||||
if (acc != null && successorsResult != null) acc + successorsResult else null
|
||||
}
|
||||
|
||||
if (newResult != reachableReturnsIndices[index]) {
|
||||
reachableReturnsIndices[index] = newResult
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
} while (changed)
|
||||
|
||||
return reachableReturnsIndices
|
||||
}
|
||||
}
|
||||
|
||||
internal fun InstructionAdapter.generateContinuationConstructorCall(
|
||||
objectTypeForState: Type?,
|
||||
methodNode: MethodNode,
|
||||
@@ -1169,15 +969,25 @@ private fun Type.normalize() =
|
||||
* ICONST_1
|
||||
* INVOKESTATIC InlineMarker.mark()
|
||||
*/
|
||||
private class SuspensionPoint(
|
||||
internal class SuspensionPoint(
|
||||
// ICONST_0
|
||||
val suspensionCallBegin: AbstractInsnNode,
|
||||
// INVOKESTATIC InlineMarker.mark()
|
||||
val suspensionCallEnd: AbstractInsnNode
|
||||
) {
|
||||
lateinit var tryCatchBlocksContinuationLabel: LabelNode
|
||||
|
||||
operator fun contains(insn: AbstractInsnNode): Boolean {
|
||||
for (i in InsnSequence(suspensionCallBegin, suspensionCallEnd.next)) {
|
||||
if (i == insn) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
internal operator fun List<SuspensionPoint>.contains(insn: AbstractInsnNode): Boolean =
|
||||
any { insn in it }
|
||||
|
||||
internal fun getLastParameterIndex(desc: String, access: Int) =
|
||||
Type.getArgumentTypes(desc).dropLast(1).map { it.size }.sum() + (if (!isStatic(access)) 1 else 0)
|
||||
|
||||
@@ -1208,25 +1018,6 @@ private fun getAllParameterTypes(desc: String, hasDispatchReceiver: Boolean, thi
|
||||
listOfNotNull(if (!hasDispatchReceiver) null else Type.getObjectType(thisName)).toTypedArray() +
|
||||
Type.getArgumentTypes(desc)
|
||||
|
||||
internal class IgnoringCopyOperationSourceInterpreter : SourceInterpreter(Opcodes.API_VERSION) {
|
||||
override fun copyOperation(insn: AbstractInsnNode?, value: SourceValue?) = value
|
||||
}
|
||||
|
||||
// Check whether this instruction is unreachable, i.e. there is no path leading to this instruction
|
||||
internal fun <T : Value> isUnreachable(index: Int, sourceFrames: Array<out Frame<out T>?>): Boolean =
|
||||
sourceFrames.size <= index || sourceFrames[index] == null
|
||||
|
||||
private fun AbstractInsnNode?.isInvisibleInDebugVarInsn(methodNode: MethodNode): Boolean {
|
||||
val insns = methodNode.instructions
|
||||
val index = insns.indexOf(this)
|
||||
return (this is VarInsnNode && methodNode.localVariables.none {
|
||||
it.index == `var` && index in it.start.let(insns::indexOf)..it.end.let(insns::indexOf)
|
||||
})
|
||||
}
|
||||
|
||||
private val SAFE_OPCODES =
|
||||
((Opcodes.DUP..Opcodes.DUP2_X2) + Opcodes.NOP + Opcodes.POP + Opcodes.POP2 + (Opcodes.IFEQ..Opcodes.GOTO)).toSet()
|
||||
|
||||
internal fun replaceFakeContinuationsWithRealOnes(methodNode: MethodNode, continuationIndex: Int) {
|
||||
val fakeContinuations = methodNode.instructions.asSequence().filter(::isFakeContinuationMarker).toList()
|
||||
for (fakeContinuation in fakeContinuations) {
|
||||
|
||||
@@ -5,93 +5,133 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.coroutines
|
||||
|
||||
import org.jetbrains.kotlin.codegen.inline.nodeText
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.isUnitInstance
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.ControlFlowGraph
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.MethodAnalyzer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.removeAll
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
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.tree.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.SourceInterpreter
|
||||
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
|
||||
|
||||
private class PossibleSpilledValue(val source: AbstractInsnNode, type: Type?) : BasicValue(type) {
|
||||
val usages = mutableSetOf<AbstractInsnNode>()
|
||||
|
||||
override fun toString(): String = when {
|
||||
source.opcode == Opcodes.ALOAD -> "" + (source as VarInsnNode).`var`
|
||||
source.isUnitInstance() -> "U"
|
||||
else -> error("unreachable")
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is PossibleSpilledValue && source == other.source
|
||||
|
||||
override fun hashCode(): Int = super.hashCode() xor source.hashCode()
|
||||
}
|
||||
|
||||
private object NonSpillableValue : BasicValue(AsmTypes.OBJECT_TYPE) {
|
||||
override fun equals(other: Any?): Boolean = other is NonSpillableValue
|
||||
|
||||
override fun toString(): String = "N"
|
||||
}
|
||||
|
||||
private object ConstructedValue : BasicValue(AsmTypes.OBJECT_TYPE) {
|
||||
override fun equals(other: Any?): Boolean = other is ConstructedValue
|
||||
|
||||
override fun toString(): String = "C"
|
||||
}
|
||||
|
||||
fun BasicValue?.nonspillable(): BasicValue? = if (this?.type?.sort == Type.OBJECT) NonSpillableValue else this
|
||||
|
||||
private class RedundantSpillingInterpreter : BasicInterpreter(Opcodes.API_VERSION) {
|
||||
val possibleSpilledValues = mutableSetOf<PossibleSpilledValue>()
|
||||
|
||||
override fun newOperation(insn: AbstractInsnNode): BasicValue? {
|
||||
if (insn.opcode == Opcodes.NEW) return ConstructedValue
|
||||
val basicValue = super.newOperation(insn)
|
||||
return if (insn.isUnitInstance())
|
||||
// Unit instances come from inlining suspend functions returning Unit.
|
||||
// They can be spilled before they are eventually popped.
|
||||
// Track them.
|
||||
PossibleSpilledValue(insn, basicValue.type).also { possibleSpilledValues += it }
|
||||
else basicValue.nonspillable()
|
||||
}
|
||||
|
||||
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? =
|
||||
when (value) {
|
||||
is ConstructedValue -> value
|
||||
is PossibleSpilledValue -> {
|
||||
value.usages += insn
|
||||
if (insn.opcode == Opcodes.ALOAD || insn.opcode == Opcodes.ASTORE) value
|
||||
else value.nonspillable()
|
||||
}
|
||||
else -> value?.nonspillable()
|
||||
}
|
||||
|
||||
override fun naryOperation(insn: AbstractInsnNode, values: MutableList<out BasicValue?>): BasicValue? {
|
||||
for (value in values.filterIsInstance<PossibleSpilledValue>()) {
|
||||
value.usages += insn
|
||||
}
|
||||
return super.naryOperation(insn, values)?.nonspillable()
|
||||
}
|
||||
|
||||
override fun merge(v: BasicValue?, w: BasicValue?): BasicValue? =
|
||||
if (v is PossibleSpilledValue && w is PossibleSpilledValue && v.source == w.source) v
|
||||
else v?.nonspillable()
|
||||
}
|
||||
|
||||
// Inliner emits a lot of locals during inlining.
|
||||
// Remove all of them since these locals are
|
||||
// 1) going to be spilled into continuation object
|
||||
// 2) breaking tail-call elimination
|
||||
class RedundantLocalsEliminationMethodTransformer(private val languageVersionSettings: LanguageVersionSettings) : MethodTransformer() {
|
||||
lateinit var internalClassName: String
|
||||
internal class RedundantLocalsEliminationMethodTransformer(private val suspensionPoints: List<SuspensionPoint>) : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
this.internalClassName = internalClassName
|
||||
do {
|
||||
var changed = false
|
||||
changed = simpleRemove(methodNode) || changed
|
||||
changed = removeWithReplacement(methodNode) || changed
|
||||
changed = removeAloadCheckcastContinuationAstore(methodNode, languageVersionSettings) || changed
|
||||
} while (changed)
|
||||
}
|
||||
val interpreter = RedundantSpillingInterpreter()
|
||||
val frames = MethodAnalyzer<BasicValue>(internalClassName, methodNode, interpreter).analyze()
|
||||
|
||||
// Replace
|
||||
// GETSTATIC kotlin/Unit.INSTANCE
|
||||
// ASTORE N
|
||||
// ...
|
||||
// ALOAD N
|
||||
// with
|
||||
// ...
|
||||
// GETSTATIC kotlin/Unit.INSTANCE
|
||||
// or
|
||||
// ACONST_NULL
|
||||
// ASTORE N
|
||||
// ...
|
||||
// ALOAD N
|
||||
// with
|
||||
// ...
|
||||
// ACONST_NULL
|
||||
// or
|
||||
// ALOAD K
|
||||
// ASTORE N
|
||||
// ...
|
||||
// ALOAD N
|
||||
// with
|
||||
// ...
|
||||
// ALOAD K
|
||||
//
|
||||
// But do not remove several at a time, since the same local (for example, ALOAD 0) might be loaded and stored multiple times in
|
||||
// sequence, like
|
||||
// ALOAD 0
|
||||
// ASTORE 1
|
||||
// ALOAD 1
|
||||
// ASTORE 2
|
||||
// ALOAD 3
|
||||
// Here, it is unsafe to replace ALOAD 3 with ALOAD 1, and then already removed ALOAD 1 with ALOAD 0.
|
||||
private fun removeWithReplacement(
|
||||
methodNode: MethodNode
|
||||
): Boolean {
|
||||
val cfg = ControlFlowGraph.build(methodNode)
|
||||
val insns = findSafeAstorePredecessors(methodNode, cfg, ignoreLocalVariableTable = false) {
|
||||
it.isUnitInstance() || it.opcode == Opcodes.ACONST_NULL || it.opcode == Opcodes.ALOAD
|
||||
val toDelete = mutableSetOf<AbstractInsnNode>()
|
||||
for (spilledValue in interpreter.possibleSpilledValues.filter { it.usages.isNotEmpty() }) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val aloads = spilledValue.usages.filter { it.opcode == Opcodes.ALOAD } as List<VarInsnNode>
|
||||
|
||||
if (aloads.isEmpty()) continue
|
||||
|
||||
val slot = aloads.first().`var`
|
||||
|
||||
if (aloads.any { it.`var` != slot }) continue
|
||||
for (aload in aloads) {
|
||||
methodNode.instructions.set(aload, spilledValue.source.clone())
|
||||
}
|
||||
|
||||
toDelete.addAll(spilledValue.usages.filter { it.opcode == Opcodes.ASTORE })
|
||||
toDelete.add(spilledValue.source)
|
||||
}
|
||||
for ((pred, astore) in insns) {
|
||||
val aload = findSingleLoadFromAstore(astore, cfg, methodNode) ?: continue
|
||||
|
||||
methodNode.instructions.removeAll(listOf(pred, astore))
|
||||
methodNode.instructions.set(aload, pred.clone())
|
||||
return true
|
||||
for (pop in methodNode.instructions.asSequence().filter { it.opcode == Opcodes.POP }) {
|
||||
val value = (frames[methodNode.instructions.indexOf(pop)]?.top() as? PossibleSpilledValue) ?: continue
|
||||
if (value.usages.isEmpty() && value.source !in suspensionPoints) {
|
||||
toDelete.add(pop)
|
||||
toDelete.add(value.source)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun findSingleLoadFromAstore(
|
||||
astore: AbstractInsnNode,
|
||||
cfg: ControlFlowGraph,
|
||||
methodNode: MethodNode
|
||||
): AbstractInsnNode? {
|
||||
val aload = methodNode.instructions.asSequence()
|
||||
.singleOrNull { it.opcode == Opcodes.ALOAD && it.localIndex() == astore.localIndex() } ?: return null
|
||||
val succ = findImmediateSuccessors(astore, cfg, methodNode).singleOrNull() ?: return null
|
||||
return if (aload == succ) aload else null
|
||||
// Remove unreachable instructions to simplify further analyses
|
||||
for (index in frames.indices) {
|
||||
if (frames[index] == null) {
|
||||
val insn = methodNode.instructions[index]
|
||||
if (insn !is LabelNode) {
|
||||
toDelete.add(insn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
methodNode.instructions.removeAll(toDelete)
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.clone() = when (this) {
|
||||
@@ -101,145 +141,41 @@ class RedundantLocalsEliminationMethodTransformer(private val languageVersionSet
|
||||
is TypeInsnNode -> TypeInsnNode(opcode, desc)
|
||||
else -> error("clone of $this is not implemented yet")
|
||||
}
|
||||
}
|
||||
|
||||
// Remove
|
||||
// ALOAD N
|
||||
// POP
|
||||
// or
|
||||
// ACONST_NULL
|
||||
// POP
|
||||
// or
|
||||
// GETSTATIC kotlin/Unit.INSTANCE
|
||||
// POP
|
||||
private fun simpleRemove(methodNode: MethodNode): Boolean {
|
||||
val insns =
|
||||
findPopPredecessors(methodNode) { it.isUnitInstance() || it.opcode == Opcodes.ACONST_NULL || it.opcode == Opcodes.ALOAD }
|
||||
for ((pred, pop) in insns) {
|
||||
methodNode.instructions.insertBefore(pred, InsnNode(Opcodes.NOP))
|
||||
methodNode.instructions.removeAll(listOf(pred, pop))
|
||||
}
|
||||
return insns.isNotEmpty()
|
||||
}
|
||||
|
||||
private fun findPopPredecessors(
|
||||
methodNode: MethodNode,
|
||||
predicate: (AbstractInsnNode) -> Boolean
|
||||
): Map<AbstractInsnNode, AbstractInsnNode> {
|
||||
val insns = methodNode.instructions.asSequence().filter { predicate(it) }.toList()
|
||||
|
||||
val cfg = ControlFlowGraph.build(methodNode)
|
||||
|
||||
val res = hashMapOf<AbstractInsnNode, AbstractInsnNode>()
|
||||
// Handy debugging routing
|
||||
@Suppress("unused")
|
||||
fun MethodNode.nodeTextWithFrames(frames: Array<*>): String {
|
||||
var insns = nodeText.split("\n")
|
||||
val first = insns.indexOfLast { it.trim().startsWith("@") } + 1
|
||||
var last = insns.indexOfFirst { it.trim().startsWith("LOCALVARIABLE") }
|
||||
if (last < 0) last = insns.size
|
||||
val prefix = insns.subList(0, first).joinToString(separator = "\n")
|
||||
val postfix = insns.subList(last, insns.size).joinToString(separator = "\n")
|
||||
insns = insns.subList(first, last)
|
||||
if (insns.any { it.contains("TABLESWITCH") }) {
|
||||
var insideTableSwitch = false
|
||||
var buffer = ""
|
||||
val res = arrayListOf<String>()
|
||||
for (insn in insns) {
|
||||
val succ = findImmediateSuccessors(insn, cfg, methodNode).singleOrNull() ?: continue
|
||||
if (succ.opcode != Opcodes.POP) continue
|
||||
if (insn.opcode == Opcodes.ALOAD && methodNode.localVariables.firstOrNull { it.index == insn.localIndex() } != null) continue
|
||||
val sources = findSourceInstructions(internalClassName, methodNode, listOf(succ), ignoreCopy = false).values.flatten()
|
||||
if (sources.size != 1) continue
|
||||
res[insn] = succ
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Replace
|
||||
// ALOAD K
|
||||
// CHECKCAST Continuation
|
||||
// ASTORE N
|
||||
// ...
|
||||
// ALOAD N
|
||||
// with
|
||||
// ...
|
||||
// ALOAD K
|
||||
// CHECKCAST Continuation
|
||||
private fun removeAloadCheckcastContinuationAstore(methodNode: MethodNode, languageVersionSettings: LanguageVersionSettings): Boolean {
|
||||
// Here we ignore the duplicates of continuation in local variable table,
|
||||
// Since it increases performance greatly.
|
||||
val cfg = ControlFlowGraph.build(methodNode)
|
||||
val insns = findSafeAstorePredecessors(methodNode, cfg, ignoreLocalVariableTable = true) {
|
||||
it.opcode == Opcodes.CHECKCAST &&
|
||||
(it as TypeInsnNode).desc == languageVersionSettings.continuationAsmType().internalName &&
|
||||
it.previous?.opcode == Opcodes.ALOAD
|
||||
}
|
||||
|
||||
var changed = false
|
||||
|
||||
for ((checkcast, astore) in insns) {
|
||||
val aloadk = checkcast.previous
|
||||
val aloadn = findSingleLoadFromAstore(astore, cfg, methodNode) ?: continue
|
||||
|
||||
methodNode.instructions.removeAll(listOf(aloadk, checkcast, astore))
|
||||
methodNode.instructions.insertBefore(aloadn, aloadk.clone())
|
||||
methodNode.instructions.set(aloadn, checkcast.clone())
|
||||
changed = true
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun findSafeAstorePredecessors(
|
||||
methodNode: MethodNode,
|
||||
cfg: ControlFlowGraph,
|
||||
ignoreLocalVariableTable: Boolean,
|
||||
predicate: (AbstractInsnNode) -> Boolean
|
||||
): Map<AbstractInsnNode, AbstractInsnNode> {
|
||||
val insns = methodNode.instructions.asSequence().filter { predicate(it) }.toList()
|
||||
val res = hashMapOf<AbstractInsnNode, AbstractInsnNode>()
|
||||
|
||||
for (insn in insns) {
|
||||
val succ = findImmediateSuccessors(insn, cfg, methodNode).singleOrNull() ?: continue
|
||||
if (succ.opcode != Opcodes.ASTORE) continue
|
||||
if (methodNode.instructions.asSequence().count {
|
||||
it.opcode == Opcodes.ASTORE && it.localIndex() == succ.localIndex()
|
||||
} != 1) continue
|
||||
if (!ignoreLocalVariableTable && methodNode.localVariables.firstOrNull { it.index == succ.localIndex() } != null) continue
|
||||
val sources = findSourceInstructions(internalClassName, methodNode, listOf(succ), ignoreCopy = false).values.flatten()
|
||||
if (sources.size > 1) continue
|
||||
res[insn] = succ
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// Find all meaningful successors of insn
|
||||
private fun findImmediateSuccessors(
|
||||
insn: AbstractInsnNode,
|
||||
cfg: ControlFlowGraph,
|
||||
methodNode: MethodNode
|
||||
): Collection<AbstractInsnNode> {
|
||||
val visited = hashSetOf<AbstractInsnNode>()
|
||||
|
||||
fun dfs(current: AbstractInsnNode): Collection<AbstractInsnNode> {
|
||||
if (!visited.add(current)) return emptySet()
|
||||
|
||||
return cfg.getSuccessorsIndices(current).flatMap {
|
||||
val succ = methodNode.instructions[it]
|
||||
if (!succ.isMeaningful || succ is JumpInsnNode || succ.opcode == Opcodes.NOP) dfs(succ)
|
||||
else setOf(succ)
|
||||
if (insn.contains("TABLESWITCH")) {
|
||||
insideTableSwitch = true
|
||||
}
|
||||
if (insideTableSwitch) {
|
||||
buffer += insn
|
||||
if (insn.contains("default")) {
|
||||
insideTableSwitch = false
|
||||
res += buffer
|
||||
buffer = ""
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
res += insn
|
||||
}
|
||||
}
|
||||
|
||||
return dfs(insn)
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.localIndex(): Int {
|
||||
assert(this is VarInsnNode)
|
||||
return (this as VarInsnNode).`var`
|
||||
}
|
||||
}
|
||||
|
||||
private fun findSourceInstructions(
|
||||
internalClassName: String,
|
||||
methodNode: MethodNode,
|
||||
insns: Collection<AbstractInsnNode>,
|
||||
ignoreCopy: Boolean
|
||||
): Map<AbstractInsnNode, Collection<AbstractInsnNode>> {
|
||||
val frames = MethodTransformer.analyze(
|
||||
internalClassName,
|
||||
methodNode,
|
||||
if (ignoreCopy) IgnoringCopyOperationSourceInterpreter() else SourceInterpreter()
|
||||
)
|
||||
return insns.keysToMap {
|
||||
val index = methodNode.instructions.indexOf(it)
|
||||
if (isUnreachable(index, frames)) return@keysToMap emptySet<AbstractInsnNode>()
|
||||
frames[index].getStack(0).insns
|
||||
insns = res
|
||||
}
|
||||
return prefix + "\n" + insns.withIndex().joinToString(separator = "\n") { (index, insn) ->
|
||||
if (index >= frames.size) "N/A\t$insn" else "${frames[index]}\t$insn"
|
||||
} + "\n" + postfix
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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.codegen.coroutines
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue
|
||||
|
||||
typealias SourceFrames = Array<Frame<SourceValue>?>
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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.codegen.coroutines
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue
|
||||
|
||||
typealias SourceFrames = Array<Frame<SourceValue>>
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* 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.codegen.coroutines
|
||||
|
||||
import org.jetbrains.kotlin.codegen.inline.isInlineMarker
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.isUnitInstance
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.ControlFlowGraph
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.LineNumberNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
|
||||
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
|
||||
|
||||
// TODO Use this in variable liveness analysis
|
||||
internal class MethodNodeExaminer(
|
||||
val languageVersionSettings: LanguageVersionSettings,
|
||||
containingClassInternalName: String,
|
||||
val methodNode: MethodNode,
|
||||
suspensionPoints: List<SuspensionPoint>,
|
||||
disableTailCallOptimizationForFunctionReturningUnit: Boolean
|
||||
) {
|
||||
private val frames: Array<Frame<BasicValue>?> =
|
||||
MethodTransformer.analyze(containingClassInternalName, methodNode, TcoInterpreter(suspensionPoints))
|
||||
private val controlFlowGraph = ControlFlowGraph.build(methodNode)
|
||||
|
||||
private val safeUnitInstances = mutableSetOf<AbstractInsnNode>()
|
||||
private val popsBeforeSafeUnitInstances = mutableSetOf<AbstractInsnNode>()
|
||||
private val areturnsAfterSafeUnitInstances = mutableSetOf<AbstractInsnNode>()
|
||||
private val meaningfulSuccessorsCache = hashMapOf<AbstractInsnNode, List<AbstractInsnNode>>()
|
||||
private val meaningfulPredecessorsCache = hashMapOf<AbstractInsnNode, List<AbstractInsnNode>>()
|
||||
|
||||
init {
|
||||
if (!disableTailCallOptimizationForFunctionReturningUnit) {
|
||||
// retrieve all POP insns
|
||||
val pops = methodNode.instructions.asSequence().filter { it.opcode == Opcodes.POP }
|
||||
// for each of them check that all successors are PUSH Unit
|
||||
val popsBeforeUnitInstances = pops.map { it to it.meaningfulSuccessors() }
|
||||
.filter { (_, succs) -> succs.all { it.isUnitInstance() } }
|
||||
.map { it.first }.toList()
|
||||
for (pop in popsBeforeUnitInstances) {
|
||||
val units = pop.meaningfulSuccessors()
|
||||
val allUnitsAreSafe = units.all { unit ->
|
||||
// check no other predecessor exists
|
||||
unit.meaningfulPredecessors().all { it in popsBeforeUnitInstances } &&
|
||||
// check they have only returns among successors
|
||||
unit.meaningfulSuccessors().all { it.opcode == Opcodes.ARETURN }
|
||||
}
|
||||
if (!allUnitsAreSafe) continue
|
||||
// save them all to the properties
|
||||
popsBeforeSafeUnitInstances += pop
|
||||
safeUnitInstances += units
|
||||
units.flatMapTo(areturnsAfterSafeUnitInstances) { it.meaningfulSuccessors() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GETSTATIC kotlin/Unit.INSTANCE is considered safe iff
|
||||
// it is part of POP, PUSH Unit, ARETURN sequence.
|
||||
private fun AbstractInsnNode.isSafeUnitInstance(): Boolean = this in safeUnitInstances
|
||||
|
||||
private fun AbstractInsnNode.isPopBeforeSafeUnitInstance(): Boolean = this in popsBeforeSafeUnitInstances
|
||||
private fun AbstractInsnNode.isAreturnAfterSafeUnitInstance(): Boolean = this in areturnsAfterSafeUnitInstances
|
||||
|
||||
private fun AbstractInsnNode.meaningfulSuccessors(): List<AbstractInsnNode> = meaningfulSuccessorsCache.getOrPut(this) {
|
||||
meaningfulSuccessorsOrPredecessors(true)
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.meaningfulPredecessors(): List<AbstractInsnNode> = meaningfulPredecessorsCache.getOrPut(this) {
|
||||
meaningfulSuccessorsOrPredecessors(false)
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.meaningfulSuccessorsOrPredecessors(isSuccessors: Boolean): List<AbstractInsnNode> {
|
||||
fun AbstractInsnNode.isMeaningful() = isMeaningful && opcode != Opcodes.NOP && opcode != Opcodes.GOTO && this !is LineNumberNode
|
||||
|
||||
fun AbstractInsnNode.getIndices() =
|
||||
if (isSuccessors) controlFlowGraph.getSuccessorsIndices(this)
|
||||
else controlFlowGraph.getPredecessorsIndices(this)
|
||||
|
||||
val visited = mutableSetOf<AbstractInsnNode>()
|
||||
fun dfs(insn: AbstractInsnNode) {
|
||||
if (insn in visited) return
|
||||
visited += insn
|
||||
if (!insn.isMeaningful()) {
|
||||
for (succIndex in insn.getIndices()) {
|
||||
dfs(methodNode.instructions[succIndex])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (succIndex in getIndices()) {
|
||||
dfs(methodNode.instructions[succIndex])
|
||||
}
|
||||
return visited.filter { it.isMeaningful() }
|
||||
}
|
||||
|
||||
fun replacePopsBeforeSafeUnitInstancesWithCoroutineSuspendedChecks() {
|
||||
val isReferenceMap =
|
||||
popsBeforeSafeUnitInstances.associateWith { (frames[methodNode.instructions.indexOf(it)]?.top()?.isReference == true) }
|
||||
|
||||
for (pop in popsBeforeSafeUnitInstances) {
|
||||
if (isReferenceMap[pop] == true) {
|
||||
val label = Label()
|
||||
methodNode.instructions.insertBefore(pop, withInstructionAdapter {
|
||||
dup()
|
||||
loadCoroutineSuspendedMarker(languageVersionSettings)
|
||||
ifacmpne(label)
|
||||
areturn(AsmTypes.OBJECT_TYPE)
|
||||
mark(label)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun allSuspensionPointsAreTailCalls(suspensionPoints: List<SuspensionPoint>): Boolean {
|
||||
val safelyReachableReturns = findSafelyReachableReturns()
|
||||
|
||||
val instructions = methodNode.instructions
|
||||
return suspensionPoints.all { suspensionPoint ->
|
||||
val beginIndex = instructions.indexOf(suspensionPoint.suspensionCallBegin)
|
||||
val endIndex = instructions.indexOf(suspensionPoint.suspensionCallEnd)
|
||||
|
||||
val insideTryBlock = methodNode.tryCatchBlocks.any { block ->
|
||||
val tryBlockStartIndex = instructions.indexOf(block.start)
|
||||
val tryBlockEndIndex = instructions.indexOf(block.end)
|
||||
|
||||
beginIndex in tryBlockStartIndex until tryBlockEndIndex
|
||||
}
|
||||
if (insideTryBlock) return@all false
|
||||
|
||||
safelyReachableReturns[endIndex + 1]?.all { returnIndex ->
|
||||
frames[returnIndex]?.top().sure {
|
||||
"There must be some value on stack to return"
|
||||
} is FromSuspensionPointValue
|
||||
} ?: false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Let's call an instruction safe if its execution is always invisible: stack modifications, branching, variable insns (invisible in debug)
|
||||
*
|
||||
* For some instruction `insn` define the result as following:
|
||||
* - if there is a path leading to the non-safe instruction then result is `null`
|
||||
* - Otherwise result contains all the reachable ARETURN indices
|
||||
*
|
||||
* @return indices of safely reachable returns for each instruction in the method node
|
||||
*/
|
||||
private fun findSafelyReachableReturns(): Array<Set<Int>?> {
|
||||
val insns = methodNode.instructions
|
||||
val reachableReturnsIndices = Array(insns.size()) init@{ index ->
|
||||
val insn = insns[index]
|
||||
|
||||
if (insn.opcode == Opcodes.ARETURN && !insn.isAreturnAfterSafeUnitInstance()) {
|
||||
return@init setOf(index)
|
||||
}
|
||||
|
||||
// Since POP, PUSH Unit, ARETURN behaves like normal return in terms of tail-call optimization, set return index to POP
|
||||
if (insn.isPopBeforeSafeUnitInstance()) {
|
||||
return@init setOf(index)
|
||||
}
|
||||
|
||||
if (!insn.isMeaningful || insn.opcode in SAFE_OPCODES || insn.isInvisibleInDebugVarInsn(methodNode) || isInlineMarker(insn)
|
||||
|| insn.isSafeUnitInstance() || insn.isAreturnAfterSafeUnitInstance()
|
||||
) {
|
||||
setOf()
|
||||
} else null
|
||||
}
|
||||
|
||||
var changed: Boolean
|
||||
do {
|
||||
changed = false
|
||||
for (index in 0 until insns.size()) {
|
||||
if (insns[index].opcode == Opcodes.ARETURN) continue
|
||||
|
||||
@Suppress("RemoveExplicitTypeArguments")
|
||||
val newResult =
|
||||
controlFlowGraph
|
||||
.getSuccessorsIndices(index).plus(index)
|
||||
.map(reachableReturnsIndices::get)
|
||||
.fold<Set<Int>?, Set<Int>?>(mutableSetOf<Int>()) { acc, successorsResult ->
|
||||
if (acc != null && successorsResult != null) acc + successorsResult else null
|
||||
}
|
||||
|
||||
if (newResult != reachableReturnsIndices[index]) {
|
||||
reachableReturnsIndices[index] = newResult
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
} while (changed)
|
||||
|
||||
return reachableReturnsIndices
|
||||
}
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode?.isInvisibleInDebugVarInsn(methodNode: MethodNode): Boolean {
|
||||
val insns = methodNode.instructions
|
||||
val index = insns.indexOf(this)
|
||||
return (this is VarInsnNode && methodNode.localVariables.none {
|
||||
it.index == `var` && index in it.start.let(insns::indexOf)..it.end.let(insns::indexOf)
|
||||
})
|
||||
}
|
||||
|
||||
private val SAFE_OPCODES =
|
||||
((Opcodes.DUP..Opcodes.DUP2_X2) + Opcodes.NOP + Opcodes.POP + Opcodes.POP2 + (Opcodes.IFEQ..Opcodes.GOTO)).toSet()
|
||||
|
||||
private object FromSuspensionPointValue : BasicValue(AsmTypes.OBJECT_TYPE) {
|
||||
override fun equals(other: Any?): Boolean = other is FromSuspensionPointValue
|
||||
}
|
||||
|
||||
private fun BasicValue?.toFromSuspensionPoint(): BasicValue? = if (this?.type?.sort == Type.OBJECT) FromSuspensionPointValue else this
|
||||
|
||||
private class TcoInterpreter(private val suspensionPoints: List<SuspensionPoint>) : BasicInterpreter(Opcodes.API_VERSION) {
|
||||
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? {
|
||||
return super.copyOperation(insn, value).convert(insn)
|
||||
}
|
||||
|
||||
private fun BasicValue?.convert(insn: AbstractInsnNode): BasicValue? = if (insn in suspensionPoints) toFromSuspensionPoint() else this
|
||||
|
||||
override fun naryOperation(insn: AbstractInsnNode, values: MutableList<out BasicValue?>?): BasicValue? {
|
||||
return super.naryOperation(insn, values).convert(insn)
|
||||
}
|
||||
|
||||
override fun ternaryOperation(insn: AbstractInsnNode, value1: BasicValue?, value2: BasicValue?, value3: BasicValue?): BasicValue? {
|
||||
return super.ternaryOperation(insn, value1, value2, value3).convert(insn)
|
||||
}
|
||||
|
||||
override fun merge(value1: BasicValue?, value2: BasicValue?): BasicValue {
|
||||
return if (value1 is FromSuspensionPointValue || value2 is FromSuspensionPointValue) FromSuspensionPointValue
|
||||
else super.merge(value1, value2)
|
||||
}
|
||||
|
||||
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? {
|
||||
return super.unaryOperation(insn, value).convert(insn)
|
||||
}
|
||||
|
||||
override fun binaryOperation(insn: AbstractInsnNode, value1: BasicValue?, value2: BasicValue?): BasicValue? {
|
||||
return super.binaryOperation(insn, value1, value2).convert(insn)
|
||||
}
|
||||
|
||||
override fun newOperation(insn: AbstractInsnNode): BasicValue? {
|
||||
return super.newOperation(insn).convert(insn)
|
||||
}
|
||||
}
|
||||
@@ -17,22 +17,20 @@
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.*
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Given a function descriptor, creates another function descriptor with type parameters copied from outer context(s).
|
||||
* This is needed because once we're serializing this to a proto, there's no place to store information about external type parameters.
|
||||
*/
|
||||
fun createFreeFakeLambdaDescriptor(descriptor: FunctionDescriptor): FunctionDescriptor {
|
||||
return createFreeDescriptor(descriptor)
|
||||
fun createFreeFakeLambdaDescriptor(descriptor: FunctionDescriptor, typeApproximator: TypeApproximator?): FunctionDescriptor {
|
||||
return createFreeDescriptor(descriptor, typeApproximator)
|
||||
}
|
||||
|
||||
private fun <D : CallableMemberDescriptor> createFreeDescriptor(descriptor: D): D {
|
||||
private fun <D : CallableMemberDescriptor> createFreeDescriptor(descriptor: D, typeApproximator: TypeApproximator?): D {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val builder = descriptor.newCopyBuilder() as CallableMemberDescriptor.CopyBuilder<D>
|
||||
|
||||
@@ -43,14 +41,82 @@ private fun <D : CallableMemberDescriptor> createFreeDescriptor(descriptor: D):
|
||||
while (container != null) {
|
||||
if (container is ClassDescriptor) {
|
||||
typeParameters.addAll(container.declaredTypeParameters)
|
||||
}
|
||||
else if (container is CallableDescriptor && container !is ConstructorDescriptor) {
|
||||
} else if (container is CallableDescriptor && container !is ConstructorDescriptor) {
|
||||
typeParameters.addAll(container.typeParameters)
|
||||
}
|
||||
container = container.containingDeclaration
|
||||
}
|
||||
|
||||
return if (typeParameters.isEmpty()) descriptor else builder.build()!!
|
||||
val approximated = typeApproximator?.approximate(descriptor, builder) ?: false
|
||||
|
||||
return if (typeParameters.isEmpty() && !approximated) descriptor else builder.build()!!
|
||||
}
|
||||
|
||||
private fun TypeApproximator.approximate(descriptor: CallableMemberDescriptor, builder: CallableMemberDescriptor.CopyBuilder<*>): Boolean {
|
||||
var approximated = false
|
||||
|
||||
val returnType = descriptor.returnType
|
||||
if (returnType != null) {
|
||||
// unwrap to avoid instances of DeferredType
|
||||
val approximatedType = approximate(returnType.unwrap(), toSuper = true)
|
||||
if (approximatedType != null) {
|
||||
builder.setReturnType(approximatedType)
|
||||
approximated = true
|
||||
}
|
||||
}
|
||||
|
||||
if (builder !is FunctionDescriptor.CopyBuilder<*>) return approximated
|
||||
|
||||
val extensionReceiverParameter = descriptor.extensionReceiverParameter
|
||||
if (extensionReceiverParameter != null) {
|
||||
val approximatedExtensionReceiver = approximate(extensionReceiverParameter.type.unwrap(), toSuper = false)
|
||||
if (approximatedExtensionReceiver != null) {
|
||||
builder.setExtensionReceiverParameter(
|
||||
extensionReceiverParameter.substituteTopLevelType(approximatedExtensionReceiver)
|
||||
)
|
||||
approximated = true
|
||||
}
|
||||
}
|
||||
|
||||
var valueParameterApproximated = false
|
||||
val newParameters = descriptor.valueParameters.map {
|
||||
val approximatedType = approximate(it.type.unwrap(), toSuper = false)
|
||||
if (approximatedType != null) {
|
||||
valueParameterApproximated = true
|
||||
// invoking constructor explicitly as substitution on value parameters is not supported
|
||||
ValueParameterDescriptorImpl(
|
||||
it.containingDeclaration, it.original, it.index, it.annotations,
|
||||
it.name, outType = approximatedType, it.declaresDefaultValue(),
|
||||
it.isCrossinline, it.isNoinline, it.varargElementType, it.source
|
||||
)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
if (valueParameterApproximated) {
|
||||
builder.setValueParameters(newParameters)
|
||||
approximated = true
|
||||
}
|
||||
|
||||
return approximated
|
||||
}
|
||||
|
||||
private fun ReceiverParameterDescriptor.substituteTopLevelType(newType: KotlinType): ReceiverParameterDescriptor? {
|
||||
val wrappedSubstitution = object : TypeSubstitution() {
|
||||
override fun get(key: KotlinType): TypeProjection? = null
|
||||
override fun prepareTopLevelType(topLevelType: KotlinType, position: Variance): KotlinType = newType
|
||||
}
|
||||
|
||||
return substitute(TypeSubstitutor.create(wrappedSubstitution))
|
||||
}
|
||||
|
||||
private fun TypeApproximator.approximate(type: UnwrappedType, toSuper: Boolean): KotlinType? {
|
||||
if (type.arguments.isEmpty() && type.constructor.isDenotable) return null
|
||||
return if (toSuper)
|
||||
approximateToSuperType(type, TypeApproximatorConfiguration.PublicDeclaration)
|
||||
else
|
||||
approximateToSubType(type, TypeApproximatorConfiguration.PublicDeclaration)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,26 +124,31 @@ private fun <D : CallableMemberDescriptor> createFreeDescriptor(descriptor: D):
|
||||
* when using reflection on that local variable at runtime.
|
||||
* Only members used by [DescriptorSerializer.propertyProto] are implemented correctly in this property descriptor.
|
||||
*/
|
||||
fun createFreeFakeLocalPropertyDescriptor(descriptor: LocalVariableDescriptor): PropertyDescriptor {
|
||||
fun createFreeFakeLocalPropertyDescriptor(descriptor: LocalVariableDescriptor, typeApproximator: TypeApproximator?): PropertyDescriptor {
|
||||
val property = PropertyDescriptorImpl.create(
|
||||
descriptor.containingDeclaration, descriptor.annotations, Modality.FINAL, descriptor.visibility, descriptor.isVar,
|
||||
descriptor.name, CallableMemberDescriptor.Kind.DECLARATION, descriptor.source, false, descriptor.isConst,
|
||||
false, false, false, @Suppress("DEPRECATION") descriptor.isDelegated
|
||||
descriptor.containingDeclaration, descriptor.annotations, Modality.FINAL, descriptor.visibility, descriptor.isVar,
|
||||
descriptor.name, CallableMemberDescriptor.Kind.DECLARATION, descriptor.source, false, descriptor.isConst,
|
||||
false, false, false, @Suppress("DEPRECATION") descriptor.isDelegated
|
||||
)
|
||||
property.setType(
|
||||
descriptor.type, descriptor.typeParameters,
|
||||
descriptor.dispatchReceiverParameter, descriptor.extensionReceiverParameter
|
||||
)
|
||||
property.setType(descriptor.type, descriptor.typeParameters, descriptor.dispatchReceiverParameter, descriptor.extensionReceiverParameter)
|
||||
|
||||
property.initialize(
|
||||
descriptor.getter?.run {
|
||||
PropertyGetterDescriptorImpl(property, annotations, modality, visibility, true, isExternal, isInline, kind, null, source).apply {
|
||||
descriptor.getter?.run {
|
||||
PropertyGetterDescriptorImpl(property, annotations, modality, visibility, true, isExternal, isInline, kind, null, source)
|
||||
.apply {
|
||||
initialize(this@run.returnType)
|
||||
}
|
||||
},
|
||||
descriptor.setter?.run {
|
||||
PropertySetterDescriptorImpl(property, annotations, modality, visibility, true, isExternal, isInline, kind, null, source).apply {
|
||||
},
|
||||
descriptor.setter?.run {
|
||||
PropertySetterDescriptorImpl(property, annotations, modality, visibility, true, isExternal, isInline, kind, null, source)
|
||||
.apply {
|
||||
initialize(this@run.valueParameters.single())
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return createFreeDescriptor(property)
|
||||
return createFreeDescriptor(property, typeApproximator)
|
||||
}
|
||||
|
||||
@@ -42,8 +42,7 @@ class AnonymousObjectTransformer(
|
||||
private val fieldNames = hashMapOf<String, MutableList<String>>()
|
||||
|
||||
private var constructor: MethodNode? = null
|
||||
private var sourceInfo: String? = null
|
||||
private var debugInfo: String? = null
|
||||
private lateinit var sourceMap: SMAP
|
||||
private lateinit var sourceMapper: SourceMapper
|
||||
private val languageVersionSettings = inliningContext.state.languageVersionSettings
|
||||
|
||||
@@ -56,6 +55,8 @@ class AnonymousObjectTransformer(
|
||||
val methodsToTransform = ArrayList<MethodNode>()
|
||||
val metadataReader = ReadKotlinClassHeaderAnnotationVisitor()
|
||||
lateinit var superClassName: String
|
||||
var sourceInfo: String? = null
|
||||
var debugInfo: String? = null
|
||||
|
||||
createClassReader().accept(object : ClassVisitor(Opcodes.API_VERSION, classBuilder.visitor) {
|
||||
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<String>) {
|
||||
@@ -113,22 +114,12 @@ class AnonymousObjectTransformer(
|
||||
override fun visitEnd() {}
|
||||
}, ClassReader.SKIP_FRAMES)
|
||||
|
||||
if (!inliningContext.isInliningLambda) {
|
||||
sourceMapper = if (debugInfo != null && !debugInfo!!.isEmpty()) {
|
||||
SourceMapper.createFromSmap(SMAPParser.parse(debugInfo!!))
|
||||
} else {
|
||||
//seems we can't do any clever mapping cause we don't know any about original class name
|
||||
IdenticalSourceMapper
|
||||
}
|
||||
if (sourceInfo != null && !GENERATE_SMAP) {
|
||||
classBuilder.visitSource(sourceInfo!!, debugInfo)
|
||||
}
|
||||
} else {
|
||||
if (sourceInfo != null) {
|
||||
classBuilder.visitSource(sourceInfo!!, debugInfo)
|
||||
}
|
||||
sourceMapper = IdenticalSourceMapper
|
||||
}
|
||||
// When regenerating objects in inline lambdas, keep the old SMAP and don't remap the line numbers to
|
||||
// save time. The result is effectively the same anyway.
|
||||
val debugInfoToParse = if (inliningContext.isInliningLambda) null else debugInfo
|
||||
val (firstLine, lastLine) = (methodsToTransform + listOfNotNull(constructor)).lineNumberRange()
|
||||
sourceMap = SMAPParser.parseOrCreateDefault(debugInfoToParse, sourceInfo, oldObjectType.internalName, firstLine, lastLine)
|
||||
sourceMapper = SourceMapper(sourceMap.fileMappings.firstOrNull { it.name == sourceInfo }?.toSourceInfo())
|
||||
|
||||
val allCapturedParamBuilder = ParametersBuilder.newBuilder()
|
||||
val constructorParamBuilder = ParametersBuilder.newBuilder()
|
||||
@@ -183,7 +174,11 @@ class AnonymousObjectTransformer(
|
||||
}
|
||||
}
|
||||
|
||||
classBuilder.visitSMAP(sourceMapper, !state.languageVersionSettings.supportsFeature(LanguageFeature.CorrectSourceMappingSyntax))
|
||||
if (GENERATE_SMAP && !inliningContext.isInliningLambda) {
|
||||
classBuilder.visitSMAP(sourceMapper, !state.languageVersionSettings.supportsFeature(LanguageFeature.CorrectSourceMappingSyntax))
|
||||
} else if (sourceInfo != null) {
|
||||
classBuilder.visitSource(sourceInfo!!, debugInfo)
|
||||
}
|
||||
|
||||
val visitor = classBuilder.visitor
|
||||
innerClassNodes.forEach { node ->
|
||||
@@ -296,7 +291,7 @@ class AnonymousObjectTransformer(
|
||||
remapper,
|
||||
isSameModule,
|
||||
"Transformer for " + transformationInfo.oldClassName,
|
||||
sourceMapper,
|
||||
SourceMapCopier(sourceMapper, sourceMap),
|
||||
InlineCallSiteInfo(
|
||||
transformationInfo.oldClassName,
|
||||
sourceNode.name,
|
||||
|
||||
@@ -30,14 +30,14 @@ import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.GENERATE_
|
||||
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.getLoadStoreArgSize;
|
||||
|
||||
public class InlineAdapter extends InstructionAdapter {
|
||||
private final SourceMapper sourceMapper;
|
||||
private final SourceMapCopier sourceMapper;
|
||||
private final List<CatchBlock> blocks = new ArrayList<>();
|
||||
|
||||
private boolean isLambdaInlining = false;
|
||||
private int nextLocalIndex = 0;
|
||||
private int nextLocalIndexBeforeInline = -1;
|
||||
|
||||
public InlineAdapter(@NotNull MethodVisitor mv, int localsSize, @NotNull SourceMapper sourceMapper) {
|
||||
public InlineAdapter(@NotNull MethodVisitor mv, int localsSize, @NotNull SourceMapCopier sourceMapper) {
|
||||
super(Opcodes.API_VERSION, mv);
|
||||
this.nextLocalIndex = localsSize;
|
||||
this.sourceMapper = sourceMapper;
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructors
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
@@ -24,7 +25,9 @@ import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||
import org.jetbrains.kotlin.resolve.inline.isInlineOnly
|
||||
import org.jetbrains.kotlin.resolve.isInlineClassType
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForReturnType
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -72,7 +75,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
var activeLambda: LambdaInfo? = null
|
||||
protected set
|
||||
|
||||
private val defaultSourceMapper = sourceCompiler.lazySourceMapper
|
||||
private val sourceMapper = sourceCompiler.lazySourceMapper
|
||||
|
||||
protected var delayedHiddenWriting: Function0<Unit>? = null
|
||||
|
||||
@@ -208,7 +211,6 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
|
||||
protected fun inlineCall(nodeAndSmap: SMAPAndMethodNode, inlineDefaultLambda: Boolean, isCallOfFunctionInCorrespondingDefaultDispatch: Boolean): InlineResult {
|
||||
assert(delayedHiddenWriting == null) { "'putHiddenParamsIntoLocals' should be called after 'processAndPutHiddenParameters(true)'" }
|
||||
if (!isCallOfFunctionInCorrespondingDefaultDispatch) defaultSourceMapper.callSiteMarker = CallSiteMarker(codegen.lastLineNumber)
|
||||
val node = nodeAndSmap.node
|
||||
if (inlineDefaultLambda) {
|
||||
for (lambda in extractDefaultLambdas(node)) {
|
||||
@@ -230,11 +232,13 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
sourceCompiler, sourceCompiler.inlineCallSiteInfo, reifiedTypeInliner, typeParameterMappings
|
||||
)
|
||||
|
||||
val sourceInfo = sourceMapper.sourceInfo!!
|
||||
val callSite = SourcePosition(codegen.lastLineNumber, sourceInfo.source, sourceInfo.pathOrCleanFQN)
|
||||
val inliner = MethodInliner(
|
||||
node, parameters, info, FieldRemapper(null, null, parameters), isSameModule,
|
||||
"Method inlining " + sourceCompiler.callElementText,
|
||||
NestedSourceMapper(defaultSourceMapper, nodeAndSmap.classSMAP), info.callSiteInfo,
|
||||
if (functionDescriptor.isInlineOnly()) InlineOnlySmapSkipper(codegen) else null,
|
||||
SourceMapCopier(sourceMapper, nodeAndSmap.classSMAP, callSite.takeIf { !isCallOfFunctionInCorrespondingDefaultDispatch }),
|
||||
info.callSiteInfo, if (functionDescriptor.isInlineOnly()) InlineOnlySmapSkipper(codegen) else null,
|
||||
!isInlinedToInlineFunInKotlinRuntime()
|
||||
) //with captured
|
||||
|
||||
@@ -269,9 +273,6 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
if (shouldSpillStack) {
|
||||
addInlineMarker(codegen.v, false)
|
||||
}
|
||||
|
||||
if (!isCallOfFunctionInCorrespondingDefaultDispatch) defaultSourceMapper.callSiteMarker = null
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -616,7 +617,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
callableDescriptor.name.asString() == "arrayOf" -> IntrinsicArrayConstructors.generateArrayOfBody(asmMethod)
|
||||
else -> throw UnsupportedOperationException("Not an array intrinsic: $callableDescriptor")
|
||||
}
|
||||
return SMAPAndMethodNode(body, SMAP(listOf(FileMapping.SKIP)))
|
||||
return SMAPAndMethodNode(body, SMAP(listOf()))
|
||||
}
|
||||
|
||||
assert(callableDescriptor is DescriptorWithContainerSource) { "Not a deserialized function or proper: $callableDescriptor" }
|
||||
@@ -631,8 +632,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
?: throw IllegalStateException("Couldn't find declaration file for $containerId")
|
||||
}
|
||||
|
||||
val methodNode =
|
||||
getMethodNode(bytes, asmMethod.name, asmMethod.descriptor, AsmUtil.asmTypeByClassId(containerId)) ?: return null
|
||||
val methodNode = getMethodNodeInner(containerId, bytes, asmMethod, callableDescriptor) ?: return null
|
||||
|
||||
// KLUDGE: Inline suspend function built with compiler version less than 1.1.4/1.2-M1 did not contain proper
|
||||
// before/after suspension point marks, so we detect those functions here and insert the corresponding marks
|
||||
@@ -643,6 +643,28 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
return methodNode
|
||||
}
|
||||
|
||||
private fun getMethodNodeInner(
|
||||
containerId: ClassId,
|
||||
bytes: ByteArray,
|
||||
asmMethod: Method,
|
||||
callableDescriptor: CallableMemberDescriptor
|
||||
): SMAPAndMethodNode? {
|
||||
val classType = AsmUtil.asmTypeByClassId(containerId)
|
||||
var methodNode = getMethodNode(bytes, asmMethod.name, asmMethod.descriptor, classType)
|
||||
if (methodNode == null && requiresFunctionNameManglingForReturnType(callableDescriptor)) {
|
||||
val nameWithoutManglingSuffix = asmMethod.name.stripManglingSuffixOrNull()
|
||||
if (nameWithoutManglingSuffix != null) {
|
||||
methodNode = getMethodNode(bytes, nameWithoutManglingSuffix, asmMethod.descriptor, classType)
|
||||
}
|
||||
}
|
||||
return methodNode
|
||||
}
|
||||
|
||||
private fun String.stripManglingSuffixOrNull(): String? {
|
||||
val dashIndex = indexOf('-')
|
||||
return if (dashIndex < 0) null else substring(0, dashIndex)
|
||||
}
|
||||
|
||||
private fun isBuiltInArrayIntrinsic(callableDescriptor: CallableMemberDescriptor): Boolean {
|
||||
if (callableDescriptor is FictitiousArrayConstructor) return true
|
||||
val name = callableDescriptor.name.asString()
|
||||
|
||||
@@ -42,7 +42,7 @@ class InlineCodegenForDefaultBody(
|
||||
val nodeAndSmap = InlineCodegen.createInlineMethodNode(
|
||||
function, methodOwner, jvmSignature, callDefault, null, codegen.typeSystem, state, sourceCompilerForInline
|
||||
)
|
||||
val childSourceMapper = NestedSourceMapper(sourceMapper, nodeAndSmap.classSMAP, sameFile = true)
|
||||
val childSourceMapper = SourceMapCopier(sourceMapper, nodeAndSmap.classSMAP)
|
||||
|
||||
val node = nodeAndSmap.node
|
||||
val transformedMethod = MethodNode(
|
||||
|
||||
@@ -54,7 +54,7 @@ class MethodInliner(
|
||||
private val nodeRemapper: FieldRemapper,
|
||||
private val isSameModule: Boolean,
|
||||
private val errorPrefix: String,
|
||||
private val sourceMapper: SourceMapper,
|
||||
private val sourceMapper: SourceMapCopier,
|
||||
private val inlineCallSiteInfo: InlineCallSiteInfo,
|
||||
private val inlineOnlySmapSkipper: InlineOnlySmapSkipper?, //non null only for root
|
||||
private val shouldPreprocessApiVersionCalls: Boolean = false
|
||||
@@ -278,7 +278,7 @@ class MethodInliner(
|
||||
visitInsn(Opcodes.NOP)
|
||||
}
|
||||
|
||||
inlineOnlySmapSkipper?.onInlineLambdaStart(remappingMethodAdapter, info)
|
||||
inlineOnlySmapSkipper?.onInlineLambdaStart(remappingMethodAdapter, info, sourceMapper.parent)
|
||||
addInlineMarker(this, true)
|
||||
val lambdaParameters = info.addAllParameters(nodeRemapper)
|
||||
|
||||
@@ -288,20 +288,14 @@ class MethodInliner(
|
||||
)
|
||||
|
||||
setLambdaInlining(true)
|
||||
val lambdaSMAP = info.node.classSMAP
|
||||
|
||||
val childSourceMapper =
|
||||
if (inliningContext.classRegeneration && !inliningContext.isInliningLambda)
|
||||
NestedSourceMapper(sourceMapper, lambdaSMAP)
|
||||
else
|
||||
NestedSourceMapper(sourceMapper.parent!!, lambdaSMAP, sameFile = info !is DefaultLambda)
|
||||
|
||||
val callSite = sourceMapper.callSite.takeIf { info is DefaultLambda }
|
||||
val inliner = MethodInliner(
|
||||
info.node.node, lambdaParameters, inliningContext.subInlineLambda(info),
|
||||
newCapturedRemapper,
|
||||
if (info is DefaultLambda) isSameModule else true /*cause all nested objects in same module as lambda*/,
|
||||
"Lambda inlining " + info.lambdaClassType.internalName,
|
||||
childSourceMapper, inlineCallSiteInfo, null
|
||||
SourceMapCopier(sourceMapper.parent, info.node.classSMAP, callSite), inlineCallSiteInfo, null
|
||||
)
|
||||
|
||||
val varRemapper = LocalVarRemapper(lambdaParameters, valueParamShift)
|
||||
|
||||
@@ -47,7 +47,7 @@ class PsiInlineIntrinsicsSupport(private val state: GenerationState) : ReifiedTy
|
||||
v.aconst(descriptor.arity)
|
||||
generateCallableReferenceDeclarationContainerClass(v, descriptor, state)
|
||||
v.aconst(descriptor.name.asString())
|
||||
generateCallableReferenceSignature(v, descriptor, state)
|
||||
generateFunctionReferenceSignature(v, descriptor, state)
|
||||
v.aconst(getCallableReferenceTopLevelFlag(descriptor))
|
||||
v.invokespecial(
|
||||
FUNCTION_REFERENCE_IMPL.internalName, "<init>",
|
||||
|
||||
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import gnu.trove.TIntIntHashMap
|
||||
import org.jetbrains.kotlin.codegen.SourceInfo
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import java.util.*
|
||||
import kotlin.math.max
|
||||
@@ -17,185 +16,100 @@ const val KOTLIN_DEBUG_STRATA_NAME = "KotlinDebug"
|
||||
|
||||
object SMAPBuilder {
|
||||
fun build(fileMappings: List<FileMapping>, backwardsCompatibleSyntax: Boolean): String? {
|
||||
val realMappings = fileMappings.filter { it.lineMappings.isNotEmpty() && it != FileMapping.SKIP }
|
||||
if (realMappings.isEmpty()) {
|
||||
if (fileMappings.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
val defaultStrata = generateDefaultStrata(realMappings)
|
||||
val debugStrata = generateDebugStrata(realMappings)
|
||||
|
||||
val debugMappings = linkedMapOf<Pair<String, String>, FileMapping>()
|
||||
for (fileMapping in fileMappings) {
|
||||
for ((_, dest, range, callSite) in fileMapping.lineMappings) {
|
||||
callSite?.let { (line, file, path) ->
|
||||
debugMappings.getOrPut(file to path) { FileMapping(file, path) }.mapNewInterval(line, dest, range)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Old versions of kotlinc and the IDEA plugin have incorrect implementations of SMAPParser:
|
||||
// 1. they require *E between strata, which is not correct syntax according to JSR-045;
|
||||
// 2. in KotlinDebug, they use `1#2,3:4` to mean "map lines 4..6 to line 1 of #2", when in reality (and in
|
||||
// the non-debug stratum) this maps lines 4..6 to lines 1..3. The correct syntax is `1#2:4,3`.
|
||||
val defaultStrata = fileMappings.toSMAP(KOTLIN_STRATA_NAME, mapToFirstLine = false)
|
||||
val debugStrata = debugMappings.values.toSMAP(KOTLIN_DEBUG_STRATA_NAME, mapToFirstLine = !backwardsCompatibleSyntax)
|
||||
if (backwardsCompatibleSyntax && defaultStrata.isNotEmpty() && debugStrata.isNotEmpty()) {
|
||||
// Old versions of kotlinc might fail if there is no END between defaultStrata and debugStrata.
|
||||
// This is not actually correct syntax according to JSR-045.
|
||||
return "SMAP\n${fileMappings[0].name}\n$KOTLIN_STRATA_NAME\n$defaultStrata${SMAP.END}\n$debugStrata${SMAP.END}\n"
|
||||
}
|
||||
return "SMAP\n${fileMappings[0].name}\n$KOTLIN_STRATA_NAME\n$defaultStrata$debugStrata${SMAP.END}\n"
|
||||
}
|
||||
|
||||
private fun generateDefaultStrata(realMappings: List<FileMapping>): String {
|
||||
val fileData = realMappings.mapIndexed { id, file -> file.toSMAPFile(id + 1) }.joinToString("")
|
||||
val lineData = realMappings.mapIndexed { id, file -> file.toSMAPMapping(id + 1) }.joinToString("")
|
||||
return "${SMAP.STRATA_SECTION} $KOTLIN_STRATA_NAME\n${SMAP.FILE_SECTION}\n$fileData${SMAP.LINE_SECTION}\n$lineData"
|
||||
}
|
||||
private fun Collection<FileMapping>.toSMAP(stratumName: String, mapToFirstLine: Boolean): String = if (isEmpty()) "" else
|
||||
"${SMAP.STRATA_SECTION} $stratumName\n" +
|
||||
"${SMAP.FILE_SECTION}\n${mapIndexed { id, file -> file.toSMAPFile(id + 1) }.joinToString("")}" +
|
||||
"${SMAP.LINE_SECTION}\n${mapIndexed { id, file -> file.toSMAPMapping(id + 1, mapToFirstLine) }.joinToString("")}"
|
||||
|
||||
private fun generateDebugStrata(realMappings: List<FileMapping>): String {
|
||||
val combinedMapping = FileMapping(realMappings[0].name, realMappings[0].path)
|
||||
for (fileMapping in realMappings) {
|
||||
for ((_, dest, range, callSiteMarker) in fileMapping.lineMappings) {
|
||||
callSiteMarker?.let { combinedMapping.mapNewInterval(it.lineNumber, dest, range) }
|
||||
}
|
||||
}
|
||||
|
||||
if (combinedMapping.lineMappings.isEmpty()) return ""
|
||||
val fileData = combinedMapping.toSMAPFile(1)
|
||||
// TODO: this generates entries like `1#2,3:4` which means "map lines 4..6 to lines 1..3 of file #2".
|
||||
// What we want is `1#2:4,3`, i.e. "map lines 4..6 to line 1 of #2", but currently IDEA cannot handle that.
|
||||
val lineData = combinedMapping.toSMAPMapping(1)
|
||||
return "${SMAP.STRATA_SECTION} $KOTLIN_DEBUG_STRATA_NAME\n${SMAP.FILE_SECTION}\n$fileData${SMAP.LINE_SECTION}\n$lineData"
|
||||
}
|
||||
|
||||
private fun RangeMapping.toSMAP(fileId: Int): String =
|
||||
if (range == 1) "$source#$fileId:$dest\n" else "$source#$fileId,$range:$dest\n"
|
||||
private fun RangeMapping.toSMAP(fileId: Int, oneLine: Boolean): String =
|
||||
if (range == 1) "$source#$fileId:$dest\n" else if (oneLine) "$source#$fileId:$dest,$range\n" else "$source#$fileId,$range:$dest\n"
|
||||
|
||||
private fun FileMapping.toSMAPFile(id: Int): String =
|
||||
"+ $id $name\n$path\n"
|
||||
|
||||
private fun FileMapping.toSMAPMapping(id: Int): String =
|
||||
lineMappings.joinToString("") { it.toSMAP(id) }
|
||||
private fun FileMapping.toSMAPMapping(id: Int, mapToFirstLine: Boolean): String =
|
||||
lineMappings.joinToString("") { it.toSMAP(id, mapToFirstLine) }
|
||||
}
|
||||
|
||||
class NestedSourceMapper(
|
||||
override val parent: SourceMapper, private val smap: SMAP, private val sameFile: Boolean = false
|
||||
) : DefaultSourceMapper(smap.sourceInfo) {
|
||||
|
||||
class SourceMapCopier(val parent: SourceMapper, private val smap: SMAP, val callSite: SourcePosition? = null) {
|
||||
private val visitedLines = TIntIntHashMap()
|
||||
|
||||
private var lastVisitedRange: RangeMapping? = null
|
||||
|
||||
override fun mapLineNumber(lineNumber: Int): Int {
|
||||
if (lineNumber in JvmAbi.SYNTHETIC_MARKER_LINE_NUMBERS) {
|
||||
return lineNumber
|
||||
}
|
||||
|
||||
if (sameFile && lineNumber <= smap.sourceInfo.linesInFile) {
|
||||
// assuming the parent source mapper is for the same file, this line number does not need remapping
|
||||
return lineNumber
|
||||
}
|
||||
|
||||
fun mapLineNumber(lineNumber: Int): Int {
|
||||
val mappedLineNumber = visitedLines.get(lineNumber)
|
||||
if (mappedLineNumber > 0) {
|
||||
return mappedLineNumber
|
||||
}
|
||||
|
||||
val range = lastVisitedRange?.takeIf { lineNumber in it }
|
||||
?: smap.findRange(lineNumber)
|
||||
?: error("Can't find range to map line $lineNumber in ${sourceInfo.source}: ${sourceInfo.pathOrCleanFQN}")
|
||||
val sourceLineNumber = range.mapDestToSource(lineNumber)
|
||||
val newLineNumber = if (sameFile)
|
||||
parent.mapLineNumber(sourceLineNumber, range.parent.name, range.parent.path, range.callSiteMarker)
|
||||
else
|
||||
parent.mapLineNumber(sourceLineNumber, range.parent.name, range.parent.path)
|
||||
if (newLineNumber > 0) {
|
||||
visitedLines.put(lineNumber, newLineNumber)
|
||||
}
|
||||
val range = lastVisitedRange?.takeIf { lineNumber in it } ?: smap.findRange(lineNumber) ?: return -1
|
||||
lastVisitedRange = range
|
||||
val newLineNumber = parent.mapLineNumber(range.mapDestToSource(lineNumber), callSite ?: range.callSite)
|
||||
|
||||
visitedLines.put(lineNumber, newLineNumber)
|
||||
return newLineNumber
|
||||
}
|
||||
}
|
||||
|
||||
interface SourceMapper {
|
||||
val resultMappings: List<FileMapping>
|
||||
val parent: SourceMapper?
|
||||
get() = null
|
||||
data class SourcePosition(val line: Int, val file: String, val path: String)
|
||||
|
||||
fun mapLineNumber(lineNumber: Int): Int
|
||||
|
||||
fun mapLineNumber(source: Int, sourceName: String, sourcePath: String): Int =
|
||||
mapLineNumber(source, sourceName, sourcePath, null)
|
||||
|
||||
fun mapLineNumber(source: Int, sourceName: String, sourcePath: String, callSiteMarker: CallSiteMarker?): Int
|
||||
|
||||
companion object {
|
||||
fun createFromSmap(smap: SMAP): SourceMapper {
|
||||
return DefaultSourceMapper(smap.sourceInfo, smap.fileMappings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object IdenticalSourceMapper : SourceMapper {
|
||||
override val resultMappings: List<FileMapping>
|
||||
get() = emptyList()
|
||||
|
||||
override val parent: SourceMapper?
|
||||
get() = null
|
||||
|
||||
override fun mapLineNumber(lineNumber: Int) = lineNumber
|
||||
|
||||
override fun mapLineNumber(source: Int, sourceName: String, sourcePath: String, callSiteMarker: CallSiteMarker?): Int =
|
||||
throw UnsupportedOperationException(
|
||||
"IdenticalSourceMapper#mapLineNumber($source, $sourceName, $sourcePath)\n"
|
||||
+ "This mapper should not encounter a line number out of range of the current file.\n"
|
||||
+ "This indicates that SMAP generation is missed somewhere."
|
||||
)
|
||||
}
|
||||
|
||||
data class CallSiteMarker(val lineNumber: Int)
|
||||
|
||||
open class DefaultSourceMapper(val sourceInfo: SourceInfo) : SourceMapper {
|
||||
private var maxUsedValue: Int = sourceInfo.linesInFile
|
||||
class SourceMapper(val sourceInfo: SourceInfo?) {
|
||||
private var maxUsedValue: Int = sourceInfo?.linesInFile ?: 0
|
||||
private var fileMappings: LinkedHashMap<Pair<String, String>, FileMapping> = linkedMapOf()
|
||||
|
||||
var callSiteMarker: CallSiteMarker? = null
|
||||
|
||||
override val resultMappings: List<FileMapping>
|
||||
val resultMappings: List<FileMapping>
|
||||
get() = fileMappings.values.toList()
|
||||
|
||||
init {
|
||||
// Explicitly map the file to itself.
|
||||
getOrRegisterNewSource(sourceInfo.source, sourceInfo.pathOrCleanFQN).mapNewInterval(1, 1, sourceInfo.linesInFile)
|
||||
}
|
||||
|
||||
constructor(sourceInfo: SourceInfo, fileMappings: List<FileMapping>) : this(sourceInfo) {
|
||||
// The first mapping is already created in the `init` block above.
|
||||
fileMappings.asSequence().drop(1)
|
||||
.forEach { fileMapping ->
|
||||
val newFileMapping = getOrRegisterNewSource(fileMapping.name, fileMapping.path)
|
||||
fileMapping.lineMappings.forEach {
|
||||
newFileMapping.mapNewInterval(it.source, it.dest, it.range)
|
||||
maxUsedValue = max(it.maxDest, maxUsedValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOrRegisterNewSource(name: String, path: String): FileMapping {
|
||||
return fileMappings.getOrPut(name to path) { FileMapping(name, path) }
|
||||
}
|
||||
|
||||
//TODO maybe add assertion that linenumber contained in fileMappings
|
||||
override fun mapLineNumber(lineNumber: Int): Int = lineNumber
|
||||
|
||||
override fun mapLineNumber(source: Int, sourceName: String, sourcePath: String): Int =
|
||||
mapLineNumber(source, sourceName, sourcePath, callSiteMarker)
|
||||
|
||||
override fun mapLineNumber(source: Int, sourceName: String, sourcePath: String, callSiteMarker: CallSiteMarker?): Int {
|
||||
if (source < 0) {
|
||||
//no source information, so just skip this linenumber
|
||||
return -1
|
||||
sourceInfo?.let {
|
||||
// Explicitly map the file to itself -- we'll probably need a lot of lines from it, so this will produce fewer ranges.
|
||||
getOrRegisterNewSource(it.source, it.pathOrCleanFQN).mapNewInterval(1, 1, it.linesInFile)
|
||||
}
|
||||
val fileMapping = getOrRegisterNewSource(sourceName, sourcePath)
|
||||
val mappedLineIndex = fileMapping.mapNewLineNumber(source, maxUsedValue, callSiteMarker)
|
||||
}
|
||||
|
||||
val isTrivial: Boolean
|
||||
get() = maxUsedValue == 0 || maxUsedValue == sourceInfo?.linesInFile
|
||||
|
||||
private fun getOrRegisterNewSource(name: String, path: String): FileMapping =
|
||||
fileMappings.getOrPut(name to path) { FileMapping(name, path) }
|
||||
|
||||
fun mapLineNumber(inlineSource: SourcePosition, inlineCallSite: SourcePosition?): Int {
|
||||
val fileMapping = getOrRegisterNewSource(inlineSource.file, inlineSource.path)
|
||||
val mappedLineIndex = fileMapping.mapNewLineNumber(inlineSource.line, maxUsedValue, inlineCallSite)
|
||||
maxUsedValue = max(maxUsedValue, mappedLineIndex)
|
||||
return mappedLineIndex
|
||||
}
|
||||
|
||||
fun mapSyntheticLineNumber(id: Int): Int {
|
||||
return mapLineNumber(SourcePosition(id, "fake.kt", "kotlin/jvm/internal/FakeKt"), null)
|
||||
}
|
||||
}
|
||||
|
||||
class SMAP(val fileMappings: List<FileMapping>) {
|
||||
val sourceInfo: SourceInfo = run {
|
||||
assert(fileMappings.isNotEmpty()) { "File Mappings shouldn't be empty" }
|
||||
val defaultFile = fileMappings.first()
|
||||
val defaultRange = defaultFile.lineMappings.first()
|
||||
SourceInfo(defaultFile.name, defaultFile.path, defaultRange.source + defaultRange.range - 1)
|
||||
}
|
||||
|
||||
// assuming disjoint line mappings (otherwise binary search can't be used anyway)
|
||||
private val intervals = fileMappings.flatMap { it.lineMappings }.sortedBy { it.dest }
|
||||
|
||||
@@ -217,54 +131,40 @@ data class SMAPAndMethodNode(val node: MethodNode, val classSMAP: SMAP)
|
||||
class FileMapping(val name: String, val path: String) {
|
||||
val lineMappings = arrayListOf<RangeMapping>()
|
||||
|
||||
fun mapNewLineNumber(source: Int, currentIndex: Int, callSiteMarker: CallSiteMarker?): Int {
|
||||
var mapping = lineMappings.lastOrNull()
|
||||
if (mapping != null && mapping.callSiteMarker == callSiteMarker &&
|
||||
(source - mapping.source) in 0 until mapping.range + (if (mapping.maxDest == currentIndex) 10 else 0)
|
||||
) {
|
||||
// Save some space in the SMAP by reusing (or extending if it's the last one) the existing range.
|
||||
mapping.range = max(mapping.range, source - mapping.source + 1)
|
||||
} else {
|
||||
mapping = mapNewInterval(source, currentIndex + 1, 1, callSiteMarker)
|
||||
}
|
||||
fun toSourceInfo(): SourceInfo =
|
||||
SourceInfo(name, path, lineMappings.fold(0) { result, mapping -> max(result, mapping.source + mapping.range - 1) })
|
||||
|
||||
fun mapNewLineNumber(source: Int, currentIndex: Int, callSite: SourcePosition?): Int {
|
||||
// Save some space in the SMAP by reusing (or extending if it's the last one) the existing range.
|
||||
// TODO some *other* range may already cover `source`; probably too slow to check them all though.
|
||||
// Maybe keep the list ordered by `source` and use binary search to locate the closest range on the left?
|
||||
val mapping = lineMappings.lastOrNull()?.takeIf { it.canReuseFor(source, currentIndex, callSite) }
|
||||
?: lineMappings.firstOrNull()?.takeIf { it.canReuseFor(source, currentIndex, callSite) }
|
||||
?: mapNewInterval(source, currentIndex + 1, 1, callSite)
|
||||
mapping.range = max(mapping.range, source - mapping.source + 1)
|
||||
return mapping.mapSourceToDest(source)
|
||||
}
|
||||
|
||||
fun mapNewInterval(source: Int, dest: Int, range: Int, callSiteMarker: CallSiteMarker? = null): RangeMapping =
|
||||
RangeMapping(source, dest, range, callSiteMarker, parent = this).also { lineMappings.add(it) }
|
||||
private fun RangeMapping.canReuseFor(newSource: Int, globalMaxDest: Int, newCallSite: SourcePosition?): Boolean =
|
||||
callSite == newCallSite && (newSource - source) in 0 until range + (if (globalMaxDest in this) 10 else 0)
|
||||
|
||||
companion object {
|
||||
val SKIP = FileMapping("no-source-info", "no-source-info").apply {
|
||||
mapNewInterval(-1, -1, 1)
|
||||
}
|
||||
}
|
||||
fun mapNewInterval(source: Int, dest: Int, range: Int, callSite: SourcePosition? = null): RangeMapping =
|
||||
RangeMapping(source, dest, range, callSite, parent = this).also { lineMappings.add(it) }
|
||||
}
|
||||
|
||||
data class RangeMapping(
|
||||
val source: Int, val dest: Int, var range: Int, val callSiteMarker: CallSiteMarker?,
|
||||
val parent: FileMapping
|
||||
) {
|
||||
private val skip = source == -1 && dest == -1
|
||||
data class RangeMapping(val source: Int, val dest: Int, var range: Int, val callSite: SourcePosition?, val parent: FileMapping) {
|
||||
operator fun contains(destLine: Int): Boolean =
|
||||
dest <= destLine && destLine < dest + range
|
||||
|
||||
val maxDest: Int
|
||||
get() = dest + range - 1
|
||||
fun hasMappingForSource(sourceLine: Int): Boolean =
|
||||
source <= sourceLine && sourceLine < source + range
|
||||
|
||||
operator fun contains(destLine: Int): Boolean {
|
||||
return skip || (dest <= destLine && destLine < dest + range)
|
||||
}
|
||||
fun mapDestToSource(destLine: Int): SourcePosition =
|
||||
SourcePosition(source + (destLine - dest), parent.name, parent.path)
|
||||
|
||||
fun hasMappingForSource(sourceLine: Int): Boolean {
|
||||
return skip || (source <= sourceLine && sourceLine < source + range)
|
||||
}
|
||||
|
||||
fun mapDestToSource(destLine: Int): Int {
|
||||
return if (skip) -1 else source + (destLine - dest)
|
||||
}
|
||||
|
||||
fun mapSourceToDest(sourceLine: Int): Int {
|
||||
return if (skip) -1 else dest + (sourceLine - source)
|
||||
}
|
||||
fun mapSourceToDest(sourceLine: Int): Int =
|
||||
dest + (sourceLine - source)
|
||||
}
|
||||
|
||||
val RangeMapping.toRange: IntRange
|
||||
get() = this.dest..this.maxDest
|
||||
get() = dest until dest + range
|
||||
|
||||
@@ -21,60 +21,64 @@ object SMAPParser {
|
||||
@JvmStatic
|
||||
fun parseOrCreateDefault(mappingInfo: String?, source: String?, path: String, methodStartLine: Int, methodEndLine: Int): SMAP {
|
||||
if (mappingInfo != null && mappingInfo.isNotEmpty()) {
|
||||
return parse(mappingInfo)
|
||||
parseOrNull(mappingInfo)?.let { return it }
|
||||
}
|
||||
if (source == null || source.isEmpty() || methodStartLine > methodEndLine) {
|
||||
return SMAP(listOf())
|
||||
}
|
||||
val mapping = FileMapping(source, path).apply {
|
||||
mapNewInterval(methodStartLine, methodStartLine, methodEndLine - methodStartLine + 1)
|
||||
}
|
||||
|
||||
val mapping =
|
||||
if (source == null || source.isEmpty() || methodStartLine > methodEndLine)
|
||||
FileMapping.SKIP
|
||||
else
|
||||
FileMapping(source, path).apply {
|
||||
if (methodStartLine <= methodEndLine) {
|
||||
//one to one
|
||||
mapNewInterval(methodStartLine, methodStartLine, methodEndLine - methodStartLine + 1)
|
||||
}
|
||||
}
|
||||
|
||||
return SMAP(listOf(mapping))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parse(mappingInfo: String): SMAP {
|
||||
fun parseOrNull(mappingInfo: String): SMAP? =
|
||||
parseStratum(mappingInfo, KOTLIN_STRATA_NAME, parseStratum(mappingInfo, KOTLIN_DEBUG_STRATA_NAME, null))
|
||||
|
||||
private fun parseStratum(mappingInfo: String, stratum: String, callSites: SMAP?): SMAP? {
|
||||
val fileMappings = linkedMapOf<Int, FileMapping>()
|
||||
val iterator = mappingInfo.lineSequence().dropWhile { it != "${SMAP.STRATA_SECTION} $stratum" }.drop(1).iterator()
|
||||
// JSR-045 allows the line section to come before the file section, but we don't generate SMAPs like this.
|
||||
if (!iterator.hasNext() || iterator.next() != SMAP.FILE_SECTION) return null
|
||||
|
||||
// Assuming we want the first stratum (which should be "Kotlin" for Kotlin classes, though you never know).
|
||||
// Also, JSR-045 allows the line section to come before the file section, but we don't generate SMAPs like this.
|
||||
val iterator = mappingInfo.lineSequence().dropWhile { it.trim() != SMAP.FILE_SECTION }.drop(1).iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val fileDeclaration = iterator.next().trim()
|
||||
if (fileDeclaration == SMAP.LINE_SECTION) break
|
||||
|
||||
if (!fileDeclaration.startsWith('+')) {
|
||||
throw AssertionError("File declaration should be in extended form, but: $fileDeclaration in $mappingInfo")
|
||||
for (line in iterator) {
|
||||
when {
|
||||
line == SMAP.LINE_SECTION -> break
|
||||
line == SMAP.FILE_SECTION || line == SMAP.END || line.startsWith(SMAP.STRATA_SECTION) -> return null
|
||||
}
|
||||
|
||||
val indexAndFileInternalName = fileDeclaration.substringAfter("+ ").trim()
|
||||
val indexAndFileInternalName = if (line.startsWith("+ ")) line.substring(2) else line
|
||||
val fileIndex = indexAndFileInternalName.substringBefore(' ').toInt()
|
||||
val fileName = indexAndFileInternalName.substringAfter(' ')
|
||||
val path = iterator.next().trim()
|
||||
val path = if (line.startsWith("+ ")) iterator.next() else fileName
|
||||
fileMappings[fileIndex] = FileMapping(fileName, path)
|
||||
}
|
||||
|
||||
for (lineMapping in iterator) {
|
||||
// The stratum is terminated either by *E or another stratum.
|
||||
if (lineMapping.trim().startsWith("*")) break
|
||||
/*only simple mapping now*/
|
||||
val targetSplit = lineMapping.indexOf(':')
|
||||
val originalPart = lineMapping.substring(0, targetSplit)
|
||||
val rangeSeparator = originalPart.indexOf(',').let { if (it < 0) targetSplit else it }
|
||||
for (line in iterator) {
|
||||
when {
|
||||
line == SMAP.LINE_SECTION || line == SMAP.FILE_SECTION -> return null
|
||||
line == SMAP.END || line.startsWith(SMAP.STRATA_SECTION) -> break
|
||||
}
|
||||
|
||||
val fileSeparator = lineMapping.indexOf('#')
|
||||
val originalIndex = originalPart.substring(0, fileSeparator).toInt()
|
||||
val range = if (rangeSeparator == targetSplit) 1 else originalPart.substring(rangeSeparator + 1, targetSplit).toInt()
|
||||
// <source>#<file>,<sourceRange>:<dest>,<destMultiplier>
|
||||
val fileSeparator = line.indexOf('#')
|
||||
if (fileSeparator < 0) return null
|
||||
val destSeparator = line.indexOf(':', fileSeparator)
|
||||
if (destSeparator < 0) return null
|
||||
val sourceRangeSeparator = line.indexOf(',').let { if (it !in fileSeparator..destSeparator) destSeparator else it }
|
||||
val destMultiplierSeparator = line.indexOf(',', destSeparator).let { if (it < 0) line.length else it }
|
||||
|
||||
val fileIndex = lineMapping.substring(fileSeparator + 1, rangeSeparator).toInt()
|
||||
val targetIndex = lineMapping.substring(targetSplit + 1).toInt()
|
||||
fileMappings[fileIndex]!!.mapNewInterval(originalIndex, targetIndex, range)
|
||||
val file = fileMappings[line.substring(fileSeparator + 1, sourceRangeSeparator).toInt()] ?: return null
|
||||
val source = line.substring(0, fileSeparator).toInt()
|
||||
val dest = line.substring(destSeparator + 1, destMultiplierSeparator).toInt()
|
||||
val range = when {
|
||||
// These two fields have a different meaning, but for compatibility we treat them the same. See `SMAPBuilder`.
|
||||
destMultiplierSeparator != line.length -> line.substring(destMultiplierSeparator + 1).toInt()
|
||||
sourceRangeSeparator != destSeparator -> line.substring(sourceRangeSeparator + 1, destSeparator).toInt()
|
||||
else -> 1
|
||||
}
|
||||
// Here we assume that each range in `Kotlin` is entirely within at most one range in `KotlinDebug`.
|
||||
file.mapNewInterval(source, dest, range, callSites?.findRange(dest)?.let { it.mapDestToSource(it.dest) })
|
||||
}
|
||||
|
||||
return SMAP(fileMappings.values.toList())
|
||||
|
||||
@@ -46,7 +46,7 @@ interface SourceCompilerForInline {
|
||||
|
||||
val inlineCallSiteInfo: InlineCallSiteInfo
|
||||
|
||||
val lazySourceMapper: DefaultSourceMapper
|
||||
val lazySourceMapper: SourceMapper
|
||||
|
||||
fun generateLambdaBody(lambdaInfo: ExpressionLambda): SMAPAndMethodNode
|
||||
|
||||
@@ -212,18 +212,7 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid
|
||||
codegen.propagateChildReifiedTypeParametersUsages(parentCodegen.reifiedTypeParametersUsages)
|
||||
}
|
||||
|
||||
return createSMAPWithDefaultMapping(expression, parentCodegen.orCreateSourceMapper.resultMappings)
|
||||
}
|
||||
|
||||
|
||||
private fun createSMAPWithDefaultMapping(
|
||||
declaration: KtExpression,
|
||||
mappings: List<FileMapping>
|
||||
): SMAP {
|
||||
val containingFile = declaration.containingFile
|
||||
CodegenUtil.getLineNumberForElement(containingFile, true) ?: error("Couldn't extract line count in $containingFile")
|
||||
|
||||
return SMAP(mappings)
|
||||
return SMAP(parentCodegen.orCreateSourceMapper.resultMappings)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@@ -310,7 +299,7 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid
|
||||
methodContext, callableDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
|
||||
inliningFunction as KtNamedFunction?, parentCodegen, asmMethod
|
||||
)
|
||||
createSMAPWithDefaultMapping(inliningFunction, parentCodegen.orCreateSourceMapper.resultMappings)
|
||||
SMAP(parentCodegen.orCreateSourceMapper.resultMappings)
|
||||
} else {
|
||||
generateMethodBody(maxCalcAdapter, callableDescriptor, methodContext, inliningFunction!!, jvmSignature, null)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.codegen.inline.coroutines
|
||||
|
||||
import com.intellij.util.ArrayUtil
|
||||
import jdk.internal.org.objectweb.asm.Type
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.coroutines.*
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
@@ -14,12 +15,14 @@ import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
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.tree.analysis.SourceInterpreter
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue
|
||||
|
||||
const val FOR_INLINE_SUFFIX = "\$\$forInline"
|
||||
|
||||
@@ -187,8 +190,7 @@ fun markNoinlineLambdaIfSuspend(mv: MethodVisitor, info: FunctionalArgument?) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Frame<SourceValue>.getSource(offset: Int): AbstractInsnNode? =
|
||||
getStack(stackSize - offset - 1)?.insns?.singleOrNull()
|
||||
private fun Frame<BasicValue>.getSource(offset: Int): AbstractInsnNode? = (getStack(stackSize - offset - 1) as? PossibleLambdaLoad)?.insn
|
||||
|
||||
fun surroundInvokesWithSuspendMarkersIfNeeded(node: MethodNode) {
|
||||
val markers = node.instructions.asSequence().filter {
|
||||
@@ -196,14 +198,14 @@ fun surroundInvokesWithSuspendMarkersIfNeeded(node: MethodNode) {
|
||||
}.toList()
|
||||
if (markers.isEmpty()) return
|
||||
|
||||
val sourceFrames = MethodTransformer.analyze("fake", node, SourceInterpreter())
|
||||
val sourceFrames = MethodTransformer.analyze("fake", node, CapturedLambdaInterpreter())
|
||||
val loads = markers.map { marker ->
|
||||
val arity = (marker.next as MethodInsnNode).owner.removePrefix(NUMBERED_FUNCTION_PREFIX).toInt()
|
||||
var receiver = sourceFrames[node.instructions.indexOf(marker) + 1].getSource(arity)
|
||||
// Navigate the ALOAD+GETFIELD+... chain to the first instruction. We need to insert a stack
|
||||
// spilling marker before it starts.
|
||||
while (receiver?.opcode == Opcodes.GETFIELD) {
|
||||
receiver = sourceFrames[node.instructions.indexOf(receiver)].getSource(0)
|
||||
receiver = receiver.previous
|
||||
}
|
||||
receiver
|
||||
}
|
||||
@@ -244,3 +246,39 @@ fun FieldInsnNode.isSuspendLambdaCapturedByOuterObjectOrLambda(inliningContext:
|
||||
}
|
||||
return isCapturedSuspendLambda(container, name, inliningContext.state.bindingContext)
|
||||
}
|
||||
|
||||
// Interpreter, that keeps track of captured functional arguments
|
||||
private class PossibleLambdaLoad(val insn: AbstractInsnNode) : BasicValue(AsmTypes.OBJECT_TYPE)
|
||||
|
||||
private class CapturedLambdaInterpreter : BasicInterpreter(Opcodes.API_VERSION) {
|
||||
override fun newOperation(insn: AbstractInsnNode): BasicValue? {
|
||||
if (insn.opcode == Opcodes.GETSTATIC) {
|
||||
insn.fieldLoad()?.let { return it }
|
||||
}
|
||||
|
||||
return super.newOperation(insn)
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.fieldLoad(): PossibleLambdaLoad? {
|
||||
if (this !is FieldInsnNode) return null
|
||||
if (desc.startsWith('L') && Type.getType(desc).internalName.isNumberedFunctionInternalName()) {
|
||||
if ((opcode == Opcodes.GETSTATIC && name.startsWith(CAPTURED_FIELD_FOLD_PREFIX + CAPTURED_FIELD_PREFIX)) ||
|
||||
(opcode == Opcodes.GETFIELD && isCapturedFieldName(name))
|
||||
) return PossibleLambdaLoad(this)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? =
|
||||
if (insn.opcode == Opcodes.ALOAD) PossibleLambdaLoad(insn) else super.copyOperation(insn, value)
|
||||
|
||||
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? {
|
||||
if (insn.opcode == Opcodes.GETFIELD) {
|
||||
insn.fieldLoad()?.let { return it }
|
||||
}
|
||||
return super.unaryOperation(insn, value)
|
||||
}
|
||||
|
||||
override fun merge(v: BasicValue?, w: BasicValue?): BasicValue? =
|
||||
if (v is PossibleLambdaLoad && w is PossibleLambdaLoad && v.insn == w.insn) v else super.merge(v, w)
|
||||
}
|
||||
@@ -92,12 +92,8 @@ internal fun getMethodNode(
|
||||
val cr = ClassReader(classData)
|
||||
var node: MethodNode? = null
|
||||
val debugInfo = arrayOfNulls<String>(2)
|
||||
val lines = IntArray(2)
|
||||
lines[0] = Integer.MAX_VALUE
|
||||
lines[1] = Integer.MIN_VALUE
|
||||
|
||||
cr.accept(object : ClassVisitor(Opcodes.API_VERSION) {
|
||||
|
||||
override fun visitSource(source: String?, debug: String?) {
|
||||
super.visitSource(source, debug)
|
||||
debugInfo[0] = source
|
||||
@@ -123,16 +119,8 @@ internal fun getMethodNode(
|
||||
node?.let { existing ->
|
||||
throw AssertionError("Can't find proper '$name' method for inline: ambiguity between '${existing.name + existing.desc}' and '${name + desc}'")
|
||||
}
|
||||
|
||||
return object : MethodNode(Opcodes.API_VERSION, access, name, desc, signature, exceptions) {
|
||||
override fun visitLineNumber(line: Int, start: Label) {
|
||||
super.visitLineNumber(line, start)
|
||||
lines[0] = min(lines[0], line)
|
||||
lines[1] = max(lines[1], line)
|
||||
}
|
||||
}.also {
|
||||
node = it
|
||||
}
|
||||
node = MethodNode(Opcodes.API_VERSION, access, name, desc, signature, exceptions)
|
||||
return node!!
|
||||
}
|
||||
}, ClassReader.SKIP_FRAMES or if (GENERATE_SMAP) 0 else ClassReader.SKIP_DEBUG)
|
||||
|
||||
@@ -140,10 +128,25 @@ internal fun getMethodNode(
|
||||
return null
|
||||
}
|
||||
|
||||
val smap = SMAPParser.parseOrCreateDefault(debugInfo[1], debugInfo[0], classType.internalName, lines[0], lines[1])
|
||||
val (first, last) = listOfNotNull(node).lineNumberRange()
|
||||
val smap = SMAPParser.parseOrCreateDefault(debugInfo[1], debugInfo[0], classType.internalName, first, last)
|
||||
return SMAPAndMethodNode(node!!, smap)
|
||||
}
|
||||
|
||||
internal fun Collection<MethodNode>.lineNumberRange(): Pair<Int, Int> {
|
||||
var minLine = Int.MAX_VALUE
|
||||
var maxLine = Int.MIN_VALUE
|
||||
for (node in this) {
|
||||
for (insn in node.instructions.asSequence()) {
|
||||
if (insn is LineNumberNode) {
|
||||
minLine = min(minLine, insn.line)
|
||||
maxLine = max(maxLine, insn.line)
|
||||
}
|
||||
}
|
||||
}
|
||||
return minLine to maxLine
|
||||
}
|
||||
|
||||
internal fun findVirtualFile(state: GenerationState, classId: ClassId): VirtualFile? {
|
||||
return VirtualFileFinder.getInstance(state.project, state.module).findVirtualFileWithHeader(classId)
|
||||
}
|
||||
@@ -202,11 +205,12 @@ private fun getInlineName(
|
||||
}
|
||||
|
||||
internal fun isInvokeOnLambda(owner: String, name: String): Boolean {
|
||||
return OperatorNameConventions.INVOKE.asString() == name &&
|
||||
owner.startsWith(NUMBERED_FUNCTION_PREFIX) &&
|
||||
owner.substring(NUMBERED_FUNCTION_PREFIX.length).isInteger()
|
||||
return OperatorNameConventions.INVOKE.asString() == name && owner.isNumberedFunctionInternalName()
|
||||
}
|
||||
|
||||
internal fun String.isNumberedFunctionInternalName(): Boolean =
|
||||
startsWith(NUMBERED_FUNCTION_PREFIX) && substring(NUMBERED_FUNCTION_PREFIX.length).isInteger()
|
||||
|
||||
internal fun isAnonymousConstructorCall(internalName: String, methodName: String): Boolean =
|
||||
isConstructor(methodName) && isAnonymousClass(internalName)
|
||||
|
||||
@@ -538,7 +542,11 @@ internal fun isThis0(name: String): Boolean = AsmUtil.CAPTURED_THIS_FIELD == nam
|
||||
class InlineOnlySmapSkipper(codegen: BaseExpressionCodegen) {
|
||||
private val callLineNumber = codegen.lastLineNumber
|
||||
|
||||
fun onInlineLambdaStart(mv: MethodVisitor, info: LambdaInfo) {
|
||||
companion object {
|
||||
const val LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER = 1
|
||||
}
|
||||
|
||||
fun onInlineLambdaStart(mv: MethodVisitor, info: LambdaInfo, smap: SourceMapper) {
|
||||
val firstLine = info.node.node.instructions.asSequence().mapNotNull { it as? LineNumberNode }.firstOrNull()?.line ?: -1
|
||||
if (callLineNumber >= 0 && firstLine == callLineNumber) {
|
||||
// We want the debugger to be able to break both on the inline call itself, plus on each
|
||||
@@ -549,7 +557,7 @@ class InlineOnlySmapSkipper(codegen: BaseExpressionCodegen) {
|
||||
// number that is remapped by the SMAP to a line that does not exist.
|
||||
val label = Label()
|
||||
mv.visitLabel(label)
|
||||
mv.visitLineNumber(JvmAbi.LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER, label)
|
||||
mv.visitLineNumber(smap.mapSyntheticLineNumber(LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER), label)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,22 +570,6 @@ class InlineOnlySmapSkipper(codegen: BaseExpressionCodegen) {
|
||||
}
|
||||
}
|
||||
|
||||
fun initDefaultSourceMappingIfNeeded(
|
||||
context: CodegenContext<*>, codegen: MemberCodegen<*>, state: GenerationState
|
||||
) {
|
||||
if (state.isInlineDisabled) return
|
||||
|
||||
var parentContext: CodegenContext<*>? = context.parentContext
|
||||
while (parentContext != null) {
|
||||
if (parentContext.isInlineMethodContext) {
|
||||
//just init default one to one mapping
|
||||
codegen.orCreateSourceMapper
|
||||
break
|
||||
}
|
||||
parentContext = parentContext.parentContext
|
||||
}
|
||||
}
|
||||
|
||||
fun MethodNode.preprocessSuspendMarkers(forInline: Boolean, keepFakeContinuation: Boolean = true) {
|
||||
if (instructions.first == null) return
|
||||
if (!keepFakeContinuation) {
|
||||
|
||||
@@ -51,10 +51,11 @@ private constructor(
|
||||
private val INT_PROGRESSION_ITERATOR_VALUE = ProgressionIteratorBasicValue("Int", Type.INT_TYPE)
|
||||
private val LONG_PROGRESSION_ITERATOR_VALUE = ProgressionIteratorBasicValue("Long", Type.LONG_TYPE)
|
||||
|
||||
private val UINT_PROGRESSION_ITERATOR_VALUE =
|
||||
ProgressionIteratorBasicValue("UInt", Type.INT_TYPE, Type.getObjectType("kotlin/UInt"))
|
||||
private val ULONG_PROGRESSION_ITERATOR_VALUE =
|
||||
ProgressionIteratorBasicValue("ULong", Type.LONG_TYPE, Type.getObjectType("kotlin/ULong"))
|
||||
// TODO functions returning inline classes are mangled now, should figure out how to work with UInt/ULong iterators here
|
||||
// private val UINT_PROGRESSION_ITERATOR_VALUE =
|
||||
// ProgressionIteratorBasicValue("UInt", Type.INT_TYPE, Type.getObjectType("kotlin/UInt"))
|
||||
// private val ULONG_PROGRESSION_ITERATOR_VALUE =
|
||||
// ProgressionIteratorBasicValue("ULong", Type.LONG_TYPE, Type.getObjectType("kotlin/ULong"))
|
||||
|
||||
private val PROGRESSION_CLASS_NAME_TO_ITERATOR_VALUE: Map<String, ProgressionIteratorBasicValue> =
|
||||
hashMapOf(
|
||||
@@ -64,10 +65,10 @@ private constructor(
|
||||
INT_PROGRESSION_FQN to INT_PROGRESSION_ITERATOR_VALUE,
|
||||
LONG_RANGE_FQN to LONG_PROGRESSION_ITERATOR_VALUE,
|
||||
LONG_PROGRESSION_FQN to LONG_PROGRESSION_ITERATOR_VALUE,
|
||||
UINT_RANGE_FQN to UINT_PROGRESSION_ITERATOR_VALUE,
|
||||
UINT_PROGRESSION_FQN to UINT_PROGRESSION_ITERATOR_VALUE,
|
||||
ULONG_RANGE_FQN to ULONG_PROGRESSION_ITERATOR_VALUE,
|
||||
ULONG_PROGRESSION_FQN to ULONG_PROGRESSION_ITERATOR_VALUE
|
||||
// UINT_RANGE_FQN to UINT_PROGRESSION_ITERATOR_VALUE,
|
||||
// UINT_PROGRESSION_FQN to UINT_PROGRESSION_ITERATOR_VALUE,
|
||||
// ULONG_RANGE_FQN to ULONG_PROGRESSION_ITERATOR_VALUE,
|
||||
// ULONG_PROGRESSION_FQN to ULONG_PROGRESSION_ITERATOR_VALUE
|
||||
)
|
||||
|
||||
fun byProgressionClassType(progressionClassType: Type): ProgressionIteratorBasicValue? =
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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.codegen.optimization.common
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||
|
||||
typealias TypeAnnotatedFrames = Array<Frame<BasicValue>?>
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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.codegen.optimization.common
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||
|
||||
typealias TypeAnnotatedFrames = Array<Frame<BasicValue>>
|
||||
@@ -60,7 +60,7 @@ fun analyzeLiveness(node: MethodNode): List<VariableLivenessFrame> {
|
||||
|
||||
private fun analyzeVisibleByDebuggerVariables(
|
||||
node: MethodNode,
|
||||
typeAnnotatedFrames: Array<Frame<BasicValue>?>
|
||||
typeAnnotatedFrames: TypeAnnotatedFrames
|
||||
): Array<BitSet> {
|
||||
val res = Array(node.instructions.size()) { BitSet(node.maxLocals) }
|
||||
for (local in node.localVariables) {
|
||||
|
||||
@@ -34,16 +34,13 @@ abstract class AbstractForInProgressionLoopGenerator(
|
||||
) : AbstractForInProgressionOrRangeLoopGenerator(codegen, forExpression) {
|
||||
|
||||
protected var incrementVar: Int = -1
|
||||
protected val asmLoopRangeType: Type
|
||||
protected val rangeKotlinType = bindingContext.getType(forExpression.loopRange!!)!!
|
||||
protected val asmLoopRangeType: Type = codegen.asmType(rangeKotlinType)
|
||||
private val rangeElementKotlinType = getRangeOrProgressionElementType(rangeKotlinType)
|
||||
?: throw AssertionError("Unexpected loop range type: $rangeKotlinType")
|
||||
private val incrementKotlinType: KotlinType
|
||||
protected val incrementType: Type
|
||||
|
||||
init {
|
||||
asmLoopRangeType = codegen.asmType(rangeKotlinType)
|
||||
|
||||
val incrementProp = rangeKotlinType.memberScope.getContributedVariables(Name.identifier("step"), NoLookupLocation.FROM_BACKEND)
|
||||
assert(incrementProp.size == 1) { rangeKotlinType.toString() + " " + incrementProp.size }
|
||||
incrementKotlinType = incrementProp.single().type
|
||||
|
||||
@@ -17,8 +17,12 @@
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.OwnerKind
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
@@ -28,6 +32,8 @@ abstract class AbstractForInProgressionOrRangeLoopGenerator(codegen: ExpressionC
|
||||
|
||||
private var loopParameter: StackValue? = null
|
||||
|
||||
protected val rangeKotlinType = bindingContext.getType(forExpression.loopRange!!)!!
|
||||
|
||||
init {
|
||||
assert(
|
||||
asmElementType.sort == Type.INT ||
|
||||
@@ -36,7 +42,7 @@ abstract class AbstractForInProgressionOrRangeLoopGenerator(codegen: ExpressionC
|
||||
asmElementType.sort == Type.CHAR ||
|
||||
asmElementType.sort == Type.LONG
|
||||
) {
|
||||
"Unexpected range element type: " + asmElementType
|
||||
"Unexpected range element type: $asmElementType"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +54,7 @@ abstract class AbstractForInProgressionOrRangeLoopGenerator(codegen: ExpressionC
|
||||
|
||||
protected fun checkPostCondition(loopExit: Label) {
|
||||
assert(endVar != -1) {
|
||||
"endVar must be allocated, endVar = " + endVar
|
||||
"endVar must be allocated, endVar = $endVar"
|
||||
}
|
||||
loopParameter().put(asmElementType, elementType, v)
|
||||
v.load(endVar, asmElementType)
|
||||
@@ -64,4 +70,14 @@ abstract class AbstractForInProgressionOrRangeLoopGenerator(codegen: ExpressionC
|
||||
|
||||
protected fun loopParameter(): StackValue =
|
||||
loopParameter ?: StackValue.local(loopParameterVar, loopParameterType).also { loopParameter = it }
|
||||
|
||||
protected fun KotlinType.getPropertyGetterName(propertyName: String): String {
|
||||
// In case of unsigned ranges, getter methods for corresponding range/progression properties would be mangled.
|
||||
val propertyDescriptor = memberScope.getContributedVariables(Name.identifier(propertyName), NoLookupLocation.FROM_BACKEND)
|
||||
.singleOrNull()
|
||||
?: throw AssertionError("No '$propertyName' in member scope of type $this")
|
||||
val getter = propertyDescriptor.getter
|
||||
?: throw AssertionError("Property has no getter: $propertyDescriptor")
|
||||
return codegen.typeMapper.mapFunctionName(getter, OwnerKind.IMPLEMENTATION)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,12 @@ class ForInProgressionExpressionLoopGenerator(
|
||||
v.dup()
|
||||
v.dup()
|
||||
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, "getFirst", asmElementType, loopParameterType, loopParameterVar)
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, "getLast", asmElementType, asmElementType, endVar)
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, "getStep", incrementType, incrementType, incrementVar)
|
||||
val firstName = rangeKotlinType.getPropertyGetterName("first")
|
||||
val lastName = rangeKotlinType.getPropertyGetterName("last")
|
||||
val stepName = rangeKotlinType.getPropertyGetterName("step")
|
||||
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, firstName, asmElementType, loopParameterType, loopParameterVar)
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, lastName, asmElementType, asmElementType, endVar)
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, stepName, incrementType, incrementType, incrementVar)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,13 @@
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.OwnerKind
|
||||
import org.jetbrains.kotlin.codegen.range.comparison.ComparisonGenerator
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
class ForInRangeInstanceLoopGenerator(
|
||||
codegen: ExpressionCodegen,
|
||||
@@ -30,18 +34,21 @@ class ForInRangeInstanceLoopGenerator(
|
||||
) : AbstractForInRangeLoopGenerator(codegen, forExpression, if (reversed) -1 else 1, comparisonGenerator) {
|
||||
|
||||
override fun storeRangeStartAndEnd() {
|
||||
val loopRangeType = codegen.bindingContext.getType(rangeExpression)!!
|
||||
val asmLoopRangeType = codegen.asmType(loopRangeType)
|
||||
codegen.gen(rangeExpression, asmLoopRangeType, loopRangeType)
|
||||
val asmLoopRangeType = codegen.asmType(rangeKotlinType)
|
||||
codegen.gen(rangeExpression, asmLoopRangeType, rangeKotlinType)
|
||||
v.dup()
|
||||
|
||||
val firstName = rangeKotlinType.getPropertyGetterName("first")
|
||||
val lastName = rangeKotlinType.getPropertyGetterName("last")
|
||||
|
||||
// ranges inherit first and last from corresponding progressions
|
||||
if (reversed) {
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, "getLast", asmElementType, loopParameterType, loopParameterVar)
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, "getFirst", asmElementType, asmElementType, endVar)
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, lastName, asmElementType, loopParameterType, loopParameterVar)
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, firstName, asmElementType, asmElementType, endVar)
|
||||
} else {
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, "getFirst", asmElementType, loopParameterType, loopParameterVar)
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, "getLast", asmElementType, asmElementType, endVar)
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, firstName, asmElementType, loopParameterType, loopParameterVar)
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, lastName, asmElementType, asmElementType, endVar)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPrivateApi
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.nonSourceAnnotations
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmDefaultAnnotation
|
||||
import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForParameterTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForReturnType
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer.Companion.writeVersionRequirement
|
||||
import org.jetbrains.kotlin.serialization.SerializerExtension
|
||||
@@ -54,8 +56,10 @@ class JvmSerializerExtension @JvmOverloads constructor(
|
||||
private val languageVersionSettings = state.languageVersionSettings
|
||||
private val isParamAssertionsDisabled = state.isParamAssertionsDisabled
|
||||
private val unifiedNullChecks = state.unifiedNullChecks
|
||||
private val functionsWithInlineClassReturnTypesMangled = state.functionsWithInlineClassReturnTypesMangled
|
||||
override val metadataVersion = state.metadataVersion
|
||||
private val jvmDefaultMode = state.jvmDefaultMode
|
||||
private val approximator = state.typeApproximator
|
||||
|
||||
override fun shouldUseTypeTable(): Boolean = useTypeTable
|
||||
override fun shouldSerializeFunction(descriptor: FunctionDescriptor): Boolean {
|
||||
@@ -75,17 +79,17 @@ class JvmSerializerExtension @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
override fun serializeClass(
|
||||
descriptor: ClassDescriptor,
|
||||
proto: ProtoBuf.Class.Builder,
|
||||
versionRequirementTable: MutableVersionRequirementTable,
|
||||
childSerializer: DescriptorSerializer
|
||||
descriptor: ClassDescriptor,
|
||||
proto: ProtoBuf.Class.Builder,
|
||||
versionRequirementTable: MutableVersionRequirementTable,
|
||||
childSerializer: DescriptorSerializer
|
||||
) {
|
||||
if (moduleName != JvmProtoBufUtil.DEFAULT_MODULE_NAME) {
|
||||
proto.setExtension(JvmProtoBuf.classModuleName, stringTable.getStringIndex(moduleName))
|
||||
}
|
||||
//TODO: support local delegated properties in new defaults scheme
|
||||
val containerAsmType =
|
||||
if (DescriptorUtils.isInterface(descriptor)) typeMapper.mapDefaultImpls(descriptor) else typeMapper.mapClass(descriptor)
|
||||
if (isInterface(descriptor)) typeMapper.mapDefaultImpls(descriptor) else typeMapper.mapClass(descriptor)
|
||||
writeLocalProperties(proto, containerAsmType, JvmProtoBuf.classLocalVariable)
|
||||
writeVersionRequirementForJvmDefaultIfNeeded(descriptor, proto, versionRequirementTable)
|
||||
|
||||
@@ -135,7 +139,7 @@ class JvmSerializerExtension @JvmOverloads constructor(
|
||||
val localVariables = CodegenBinding.getLocalDelegatedProperties(codegenBinding, classAsmType) ?: return
|
||||
|
||||
for (localVariable in localVariables) {
|
||||
val propertyDescriptor = createFreeFakeLocalPropertyDescriptor(localVariable)
|
||||
val propertyDescriptor = createFreeFakeLocalPropertyDescriptor(localVariable, approximator)
|
||||
val serializer = DescriptorSerializer.createForLambda(this)
|
||||
proto.addExtension(extension, serializer.propertyProto(propertyDescriptor)?.build() ?: continue)
|
||||
}
|
||||
@@ -198,6 +202,13 @@ class JvmSerializerExtension @JvmOverloads constructor(
|
||||
if (descriptor.needsInlineParameterNullCheckRequirement()) {
|
||||
versionRequirementTable?.writeInlineParameterNullCheckRequirement(proto::addVersionRequirement)
|
||||
}
|
||||
|
||||
if (requiresFunctionNameManglingForReturnType(descriptor) &&
|
||||
!DescriptorUtils.hasJvmNameAnnotation(descriptor) &&
|
||||
!requiresFunctionNameManglingForParameterTypes(descriptor)
|
||||
) {
|
||||
versionRequirementTable?.writeFunctionNameManglingForReturnTypeRequirement(proto::addVersionRequirement)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableVersionRequirementTable.writeInlineParameterNullCheckRequirement(add: (Int) -> Unit) {
|
||||
@@ -208,6 +219,12 @@ class JvmSerializerExtension @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableVersionRequirementTable.writeFunctionNameManglingForReturnTypeRequirement(add: (Int) -> Unit) {
|
||||
if (functionsWithInlineClassReturnTypesMangled) {
|
||||
add(writeVersionRequirement(1, 4, 0, ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION, this))
|
||||
}
|
||||
}
|
||||
|
||||
private fun FunctionDescriptor.needsInlineParameterNullCheckRequirement(): Boolean =
|
||||
isInline && !isSuspend && !isParamAssertionsDisabled &&
|
||||
!Visibilities.isPrivate(visibility) &&
|
||||
@@ -253,6 +270,10 @@ class JvmSerializerExtension @JvmOverloads constructor(
|
||||
if (getter?.needsInlineParameterNullCheckRequirement() == true || setter?.needsInlineParameterNullCheckRequirement() == true) {
|
||||
versionRequirementTable?.writeInlineParameterNullCheckRequirement(proto::addVersionRequirement)
|
||||
}
|
||||
|
||||
if (!DescriptorUtils.hasJvmNameAnnotation(descriptor) && requiresFunctionNameManglingForReturnType(descriptor)) {
|
||||
versionRequirementTable?.writeFunctionNameManglingForReturnTypeRequirement(proto::addVersionRequirement)
|
||||
}
|
||||
}
|
||||
|
||||
private fun PropertyDescriptor.isJvmFieldPropertyInInterfaceCompanion(): Boolean {
|
||||
@@ -262,7 +283,7 @@ class JvmSerializerExtension @JvmOverloads constructor(
|
||||
if (!DescriptorUtils.isCompanionObject(container)) return false
|
||||
|
||||
val grandParent = (container as ClassDescriptor).containingDeclaration
|
||||
return DescriptorUtils.isInterface(grandParent) || DescriptorUtils.isAnnotationClass(grandParent)
|
||||
return isInterface(grandParent) || DescriptorUtils.isAnnotationClass(grandParent)
|
||||
}
|
||||
|
||||
override fun serializeErrorType(type: KotlinType, builder: ProtoBuf.Type.Builder) {
|
||||
|
||||
@@ -46,6 +46,7 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind.*
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeApproximator
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import java.io.File
|
||||
@@ -251,6 +252,8 @@ class GenerationState private constructor(
|
||||
val isInlineDisabled: Boolean = configuration.getBoolean(CommonConfigurationKeys.DISABLE_INLINE)
|
||||
val useTypeTableInSerializer: Boolean = configuration.getBoolean(JVMConfigurationKeys.USE_TYPE_TABLE)
|
||||
val unifiedNullChecks: Boolean = languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_1_4
|
||||
val functionsWithInlineClassReturnTypesMangled: Boolean =
|
||||
languageVersionSettings.supportsFeature(LanguageFeature.MangleClassMembersReturningInlineClasses)
|
||||
|
||||
val rootContext: CodegenContext<*> = RootContext(this)
|
||||
|
||||
@@ -283,6 +286,12 @@ class GenerationState private constructor(
|
||||
lateinit var irBasedMapAsmMethod: (FunctionDescriptor) -> Method
|
||||
var mapInlineClass: (ClassDescriptor) -> Type = { descriptor -> typeMapper.mapType(descriptor.defaultType) }
|
||||
|
||||
val typeApproximator: TypeApproximator? =
|
||||
if (languageVersionSettings.supportsFeature(LanguageFeature.NewInference))
|
||||
TypeApproximator(module.builtIns)
|
||||
else
|
||||
null
|
||||
|
||||
init {
|
||||
this.interceptedBuilderFactory = builderFactory
|
||||
.wrapWith(
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* 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.codegen.state
|
||||
|
||||
typealias JvmMethodExceptionTypes = Array<out String>?
|
||||
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* 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.codegen.state
|
||||
|
||||
typealias JvmMethodExceptionTypes = Array<out String?>?
|
||||
@@ -364,39 +364,29 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
||||
// and, more importantly, returns 'kotlin.Any' (so that it can return as a reference value or a special COROUTINE_SUSPENDED object).
|
||||
// This also causes boxing of primitives and inline class values.
|
||||
// If we have a function returning an inline class value that is mapped to a reference type, we want to avoid boxing.
|
||||
// However, we have to do that consistently both on declaration site and on call site in case of covariant overrides.
|
||||
// However, we have to do that consistently both on declaration site and on call site.
|
||||
|
||||
if (!functionDescriptor.isSuspend) return null
|
||||
|
||||
val originalSuspendFunction = functionDescriptor.unwrapInitialDescriptorForSuspendFunction()
|
||||
val originalReturnType = originalSuspendFunction.returnType!!
|
||||
|
||||
if (!originalReturnType.isInlineClassType()) return null
|
||||
|
||||
if (!originalReturnType.isInlineClassTypeSafeToKeepUnboxedOnSuspendFunReturn()) {
|
||||
return originalSuspendFunction.module.builtIns.nullableAnyType
|
||||
// Force boxing for primitives
|
||||
if (AsmUtil.isPrimitive(mapType(originalReturnType))) {
|
||||
return functionDescriptor.builtIns.nullableAnyType
|
||||
}
|
||||
|
||||
// Lambdas, callable references, and function literals implicitly override corresponding generic method from a base class.
|
||||
// NB we don't have suspend function literals so far, but we support it in back-end, just in case.
|
||||
if (isFunctionLiteral(originalSuspendFunction) || isFunctionExpression(originalSuspendFunction)) {
|
||||
return originalSuspendFunction.module.builtIns.nullableAnyType
|
||||
}
|
||||
|
||||
// NB JVM view of a Kotlin function overrides JVM views of corresponding overridden functions
|
||||
val originalOverridden = getAllOverriddenDeclarations(functionDescriptor).map {
|
||||
it.unwrapInitialDescriptorForSuspendFunction().original
|
||||
}
|
||||
if (originalOverridden.any { !it.returnType!!.isInlineClassTypeSafeToKeepUnboxedOnSuspendFunReturn() }) {
|
||||
return originalSuspendFunction.module.builtIns.nullableAnyType
|
||||
// Force boxing for nullable inline class types with nullable underlying type
|
||||
if (originalReturnType.isMarkedNullable && originalReturnType.isNullableUnderlyingType()) {
|
||||
return functionDescriptor.builtIns.nullableAnyType
|
||||
}
|
||||
|
||||
// Don't box other inline classes
|
||||
return originalReturnType
|
||||
}
|
||||
|
||||
private fun KotlinType.isInlineClassTypeSafeToKeepUnboxedOnSuspendFunReturn(): Boolean =
|
||||
isInlineClassType() && !AsmUtil.isPrimitive(mapType(this)) && (!isMarkedNullable || !isNullableUnderlyingType())
|
||||
|
||||
|
||||
@JvmOverloads
|
||||
fun mapToCallableMethod(
|
||||
descriptor: FunctionDescriptor,
|
||||
@@ -410,7 +400,7 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
||||
val owner = mapOwner(descriptor)
|
||||
val originalDescriptor = descriptor.original
|
||||
return CallableMethod(
|
||||
owner, owner, { mapDefaultMethod(originalDescriptor, OwnerKind.IMPLEMENTATION).descriptor }, method, INVOKESPECIAL,
|
||||
owner, owner, { mapDefaultMethod(originalDescriptor, OwnerKind.IMPLEMENTATION) }, method, INVOKESPECIAL,
|
||||
null, null, null, null, null, originalDescriptor.returnType, isInterfaceMethod = false, isDefaultMethodInInterface = false
|
||||
)
|
||||
}
|
||||
@@ -571,7 +561,7 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
||||
|
||||
return CallableMethod(
|
||||
owner, ownerForDefaultImpl,
|
||||
{ mapDefaultMethod(baseMethodDescriptor, getKindForDefaultImplCall(baseMethodDescriptor)).descriptor },
|
||||
{ mapDefaultMethod(baseMethodDescriptor, getKindForDefaultImplCall(baseMethodDescriptor)) },
|
||||
signature, invokeOpcode, thisClass, dispatchReceiverKotlinType, receiverParameterType, extensionReceiverKotlinType,
|
||||
calleeType, returnKotlinType,
|
||||
if (jvmTarget >= JvmTarget.JVM_1_8) isInterfaceMember else invokeOpcode == INVOKEINTERFACE,
|
||||
@@ -629,6 +619,9 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private val shouldMangleByReturnType =
|
||||
languageVersionSettings.supportsFeature(LanguageFeature.MangleClassMembersReturningInlineClasses)
|
||||
|
||||
private fun mangleMemberNameIfRequired(
|
||||
name: String,
|
||||
descriptor: CallableMemberDescriptor,
|
||||
@@ -663,11 +656,16 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
val suffix = getInlineClassSignatureManglingSuffix(descriptor)
|
||||
if (suffix != null) {
|
||||
newName += suffix
|
||||
} else if (kind === OwnerKind.ERASED_INLINE_CLASS) {
|
||||
newName += JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS
|
||||
// Skip inline class mangling for property reference signatures,
|
||||
// so that we don't have to repeat the same logic in reflection
|
||||
// in case of properties without getter methods.
|
||||
if (kind !== OwnerKind.PROPERTY_REFERENCE_SIGNATURE || descriptor.isPropertyWithGetterSignaturePresent()) {
|
||||
val suffix = getManglingSuffixBasedOnKotlinSignature(descriptor, shouldMangleByReturnType)
|
||||
if (suffix != null) {
|
||||
newName += suffix
|
||||
} else if (kind === OwnerKind.ERASED_INLINE_CLASS) {
|
||||
newName += JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS
|
||||
}
|
||||
}
|
||||
|
||||
newName = sanitizeNameIfNeeded(newName, languageVersionSettings)
|
||||
@@ -688,6 +686,16 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
||||
} else newName
|
||||
}
|
||||
|
||||
|
||||
private fun CallableMemberDescriptor.isPropertyWithGetterSignaturePresent(): Boolean {
|
||||
val propertyDescriptor = when (this) {
|
||||
is PropertyDescriptor -> this
|
||||
is PropertyAccessorDescriptor -> correspondingProperty
|
||||
else -> return false
|
||||
}
|
||||
return PropertyCodegen.isReferenceablePropertyWithGetter(propertyDescriptor)
|
||||
}
|
||||
|
||||
private fun getModuleName(descriptor: CallableMemberDescriptor): String {
|
||||
return getJvmModuleNameForDeserializedDescriptor(descriptor) ?: moduleName
|
||||
}
|
||||
@@ -696,6 +704,10 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
||||
return mapSignature(descriptor).asmMethod
|
||||
}
|
||||
|
||||
fun mapPropertyReferenceSignature(descriptor: FunctionDescriptor): Method {
|
||||
return mapSignature(descriptor, OwnerKind.PROPERTY_REFERENCE_SIGNATURE, true).asmMethod
|
||||
}
|
||||
|
||||
fun mapAsmMethod(descriptor: FunctionDescriptor, kind: OwnerKind): Method {
|
||||
return mapSignature(descriptor, kind, true).asmMethod
|
||||
}
|
||||
@@ -966,12 +978,12 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
||||
// implicitly override generic 'invoke' from a corresponding base class.
|
||||
if ((isFunctionExpression(descriptor) || isFunctionLiteral(descriptor)) && returnType.isInlineClassType()) return true
|
||||
|
||||
return isJvmPrimitiveOrInlineClass(returnType) &&
|
||||
getAllOverriddenDescriptors(descriptor).any { !isJvmPrimitiveOrInlineClass(it.returnType!!) }
|
||||
return isJvmPrimitive(returnType) &&
|
||||
getAllOverriddenDescriptors(descriptor).any { !isJvmPrimitive(it.returnType!!) }
|
||||
}
|
||||
|
||||
private fun isJvmPrimitiveOrInlineClass(kotlinType: KotlinType) =
|
||||
KotlinBuiltIns.isPrimitiveType(kotlinType) || kotlinType.isInlineClassType()
|
||||
private fun isJvmPrimitive(kotlinType: KotlinType) =
|
||||
KotlinBuiltIns.isPrimitiveType(kotlinType)
|
||||
|
||||
private fun isBoxMethodForInlineClass(descriptor: FunctionDescriptor): Boolean {
|
||||
val containingDeclaration = descriptor.containingDeclaration
|
||||
|
||||
@@ -83,7 +83,7 @@ class SignatureDumpingBuilderFactory(
|
||||
super.defineClass(origin, version, access, name, signature, superName, interfaces)
|
||||
}
|
||||
|
||||
override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor {
|
||||
override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: JvmMethodExceptionTypes): MethodVisitor {
|
||||
signatures += RawSignature(name, desc, MemberKind.METHOD) to origin.descriptor?.let {
|
||||
if (it is CallableDescriptor) it.unwrapInitialDescriptorForSuspendFunction() else it
|
||||
}
|
||||
|
||||
@@ -5,33 +5,51 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.state
|
||||
|
||||
import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.resolve.jvm.*
|
||||
import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForParameterTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForReturnType
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
|
||||
import java.security.MessageDigest
|
||||
import java.util.*
|
||||
|
||||
fun getInlineClassSignatureManglingSuffix(descriptor: CallableMemberDescriptor): String? {
|
||||
fun getManglingSuffixBasedOnKotlinSignature(
|
||||
descriptor: CallableMemberDescriptor,
|
||||
shouldMangleByReturnType: Boolean
|
||||
): String? {
|
||||
if (descriptor !is FunctionDescriptor) return null
|
||||
if (descriptor is ConstructorDescriptor) return null
|
||||
if (InlineClassDescriptorResolver.isSynthesizedBoxOrUnboxMethod(descriptor)) return null
|
||||
|
||||
val actualValueParameterTypes = listOfNotNull(descriptor.extensionReceiverParameter?.type) + descriptor.valueParameters.map { it.type }
|
||||
// Don't mangle functions with '@JvmName' annotation.
|
||||
// Some stdlib functions ('Result.success', 'Result.failure') are annotated with '@JvmName' as a workaround for forward compatibility.
|
||||
if (DescriptorUtils.hasJvmNameAnnotation(descriptor)) return null
|
||||
|
||||
return getInlineClassSignatureManglingSuffix(actualValueParameterTypes)
|
||||
// If a function accepts inline class parameters, mangle its name.
|
||||
if (requiresFunctionNameManglingForParameterTypes(descriptor)) {
|
||||
return "-" + md5base64(collectSignatureForMangling(descriptor))
|
||||
}
|
||||
|
||||
// If a class member function returns inline class value, mangle its name.
|
||||
// NB here function can be a suspend function JVM view with return type replaced with 'Any',
|
||||
// should unwrap it and take original return type instead.
|
||||
if (shouldMangleByReturnType) {
|
||||
val unwrappedDescriptor = descriptor.unwrapInitialDescriptorForSuspendFunction()
|
||||
if (requiresFunctionNameManglingForReturnType(unwrappedDescriptor)) {
|
||||
return "-" + md5base64(":" + getSignatureElementForMangling(unwrappedDescriptor.returnType!!))
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getInlineClassSignatureManglingSuffix(valueParameterTypes: List<KotlinType>) =
|
||||
if (requiresFunctionNameMangling(valueParameterTypes))
|
||||
"-" + md5base64(collectSignatureForMangling(valueParameterTypes))
|
||||
else
|
||||
null
|
||||
|
||||
private fun collectSignatureForMangling(types: List<KotlinType>) =
|
||||
types.joinToString { getSignatureElementForMangling(it) }
|
||||
private fun collectSignatureForMangling(descriptor: CallableMemberDescriptor): String {
|
||||
val types = listOfNotNull(descriptor.extensionReceiverParameter?.type) + descriptor.valueParameters.map { it.type }
|
||||
return types.joinToString { getSignatureElementForMangling(it) }
|
||||
}
|
||||
|
||||
private fun getSignatureElementForMangling(type: KotlinType): String = buildString {
|
||||
val descriptor = type.constructor.declarationDescriptor ?: return ""
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
* 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.serialization.builtins
|
||||
@@ -97,10 +86,10 @@ class BuiltInsSerializer(dependOnOldBuiltIns: Boolean) : MetadataSerializer(Buil
|
||||
fqName ->
|
||||
val packageView = module.getPackage(fqName)
|
||||
PackageSerializer(
|
||||
packageView.memberScope.getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS) + createCloneable(module),
|
||||
packageView.fragments.flatMap { fragment -> DescriptorUtils.getAllDescriptors(fragment.getMemberScope()) },
|
||||
packageView.fqName,
|
||||
File(destDir, BuiltInSerializerProtocol.getBuiltInsFilePath(packageView.fqName))
|
||||
packageView.memberScope.getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS) + createCloneable(module),
|
||||
packageView.fragments.flatMap { fragment -> DescriptorUtils.getAllDescriptors(fragment.getMemberScope()) },
|
||||
packageView.fqName,
|
||||
File(destDir, BuiltInSerializerProtocol.getBuiltInsFilePath(packageView.fqName))
|
||||
).run()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,19 +38,12 @@ import kotlin.system.exitProcess
|
||||
|
||||
abstract class CLITool<A : CommonToolArguments> {
|
||||
fun exec(errStream: PrintStream, vararg args: String): ExitCode {
|
||||
val rendererType = System.getProperty(MessageRenderer.PROPERTY_KEY)
|
||||
|
||||
val messageRenderer = when (rendererType) {
|
||||
MessageRenderer.XML.name -> MessageRenderer.XML
|
||||
MessageRenderer.GRADLE_STYLE.name -> MessageRenderer.GRADLE_STYLE
|
||||
MessageRenderer.WITHOUT_PATHS.name -> MessageRenderer.WITHOUT_PATHS
|
||||
MessageRenderer.PLAIN_FULL_PATHS.name -> MessageRenderer.PLAIN_FULL_PATHS
|
||||
else -> MessageRenderer.PLAIN_RELATIVE_PATHS
|
||||
}
|
||||
|
||||
return exec(errStream, Services.EMPTY, messageRenderer, args)
|
||||
return exec(errStream, Services.EMPTY, defaultMessageRenderer(), args)
|
||||
}
|
||||
|
||||
fun exec(errStream: PrintStream, messageRenderer: MessageRenderer, vararg args: String): ExitCode
|
||||
= exec(errStream, Services.EMPTY, messageRenderer, args)
|
||||
|
||||
protected fun exec(
|
||||
errStream: PrintStream,
|
||||
services: Services,
|
||||
@@ -192,6 +185,16 @@ abstract class CLITool<A : CommonToolArguments> {
|
||||
abstract fun executableScriptFileName(): String
|
||||
|
||||
companion object {
|
||||
|
||||
private fun defaultMessageRenderer(): MessageRenderer =
|
||||
when (System.getProperty(MessageRenderer.PROPERTY_KEY)) {
|
||||
MessageRenderer.XML.name -> MessageRenderer.XML
|
||||
MessageRenderer.GRADLE_STYLE.name -> MessageRenderer.GRADLE_STYLE
|
||||
MessageRenderer.WITHOUT_PATHS.name -> MessageRenderer.WITHOUT_PATHS
|
||||
MessageRenderer.PLAIN_FULL_PATHS.name -> MessageRenderer.PLAIN_FULL_PATHS
|
||||
else -> MessageRenderer.PLAIN_RELATIVE_PATHS
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful main for derived command line tools
|
||||
*/
|
||||
@@ -215,8 +218,13 @@ abstract class CLITool<A : CommonToolArguments> {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun doMainNoExit(compiler: CLITool<*>, args: Array<String>): ExitCode = try {
|
||||
compiler.exec(System.err, *args)
|
||||
@JvmOverloads
|
||||
fun doMainNoExit(
|
||||
compiler: CLITool<*>,
|
||||
args: Array<String>,
|
||||
messageRenderer: MessageRenderer = defaultMessageRenderer()
|
||||
): ExitCode = try {
|
||||
compiler.exec(System.err, messageRenderer, *args)
|
||||
} catch (e: CompileEnvironmentException) {
|
||||
System.err.println(e.message)
|
||||
ExitCode.INTERNAL_ERROR
|
||||
|
||||
@@ -550,8 +550,6 @@ class KotlinCoreEnvironment private constructor(
|
||||
CollectAdditionalSourcesExtension.registerExtensionPoint(project)
|
||||
ExtraImportsProviderExtension.registerExtensionPoint(project)
|
||||
IrGenerationExtension.registerExtensionPoint(project)
|
||||
@Suppress("DEPRECATION_ERROR")
|
||||
org.jetbrains.kotlin.backend.common.extensions.PureIrGenerationExtension.registerExtensionPoint(project)
|
||||
ScriptEvaluationExtension.registerExtensionPoint(project)
|
||||
ShellExtension.registerExtensionPoint(project)
|
||||
TypeResolutionInterceptor.registerExtensionPoint(project)
|
||||
|
||||
@@ -45,24 +45,29 @@ import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
|
||||
import org.jetbrains.kotlin.cli.common.output.writeAll
|
||||
import org.jetbrains.kotlin.cli.common.toLogger
|
||||
import org.jetbrains.kotlin.cli.jvm.config.*
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
||||
import org.jetbrains.kotlin.codegen.CodegenFactory
|
||||
import org.jetbrains.kotlin.codegen.DefaultCodegenFactory
|
||||
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationStateEventCallback
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.backend.Fir2IrConverter
|
||||
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmBackendClassResolver
|
||||
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmClassCodegen
|
||||
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmKotlinMangler
|
||||
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
|
||||
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
|
||||
import org.jetbrains.kotlin.fir.extensions.extensionsService
|
||||
import org.jetbrains.kotlin.fir.extensions.BunchOfRegisteredExtensions
|
||||
import org.jetbrains.kotlin.fir.extensions.extensionService
|
||||
import org.jetbrains.kotlin.fir.extensions.registerExtensions
|
||||
import org.jetbrains.kotlin.fir.java.FirJavaModuleBasedSession
|
||||
import org.jetbrains.kotlin.fir.java.FirLibrarySession
|
||||
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.firProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.impl.FirProviderImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.FirTotalResolveTransformer
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirProviderImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.FirTotalResolveProcessor
|
||||
import org.jetbrains.kotlin.idea.MainFunctionDetector
|
||||
import org.jetbrains.kotlin.ir.backend.jvm.jvmResolveLibraries
|
||||
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmManglerDesc
|
||||
@@ -319,7 +324,7 @@ object KotlinToJVMBytecodeCompiler {
|
||||
}
|
||||
|
||||
val moduleInfo = FirJvmModuleInfo(module.getModuleName())
|
||||
val session: FirSession = FirJavaModuleBasedSession(moduleInfo, provider, scope).also {
|
||||
val session: FirSession = FirJavaModuleBasedSession.create(moduleInfo, provider, scope).also {
|
||||
val dependenciesInfo = FirJvmModuleInfo(Name.special("<dependencies>"))
|
||||
moduleInfo.dependencies.add(dependenciesInfo)
|
||||
val librariesScope = ProjectScope.getLibrariesScope(project)
|
||||
@@ -327,18 +332,18 @@ object KotlinToJVMBytecodeCompiler {
|
||||
dependenciesInfo, provider, librariesScope,
|
||||
project, environment.createPackagePartProvider(librariesScope)
|
||||
)
|
||||
it.extensionsService.registerExtensions(FirExtensionRegistrar.RegisteredExtensions.EMPTY)
|
||||
it.extensionService.registerExtensions(BunchOfRegisteredExtensions.empty())
|
||||
}
|
||||
val firProvider = (session.firProvider as FirProviderImpl)
|
||||
val builder = RawFirBuilder(session, firProvider.kotlinScopeProvider, stubMode = false)
|
||||
val resolveTransformer = FirTotalResolveTransformer()
|
||||
val resolveTransformer = FirTotalResolveProcessor(session)
|
||||
val firFiles = ktFiles.map {
|
||||
val firFile = builder.buildFirFile(it)
|
||||
firProvider.recordFile(firFile)
|
||||
firFile
|
||||
}.also {
|
||||
try {
|
||||
resolveTransformer.processFiles(it)
|
||||
resolveTransformer.process(it)
|
||||
} catch (e: Exception) {
|
||||
throw e
|
||||
}
|
||||
@@ -346,11 +351,12 @@ object KotlinToJVMBytecodeCompiler {
|
||||
|
||||
val signaturer = IdSignatureDescriptor(JvmManglerDesc())
|
||||
|
||||
val (moduleFragment, symbolTable, sourceManager) =
|
||||
val (moduleFragment, symbolTable, sourceManager, components) =
|
||||
Fir2IrConverter.createModuleFragment(
|
||||
session, resolveTransformer.scopeSession, firFiles,
|
||||
moduleConfiguration.languageVersionSettings, signaturer = signaturer,
|
||||
generatorExtensions = JvmGeneratorExtensions()
|
||||
generatorExtensions = JvmGeneratorExtensions(),
|
||||
mangler = FirJvmKotlinMangler(session)
|
||||
)
|
||||
val dummyBindingContext = NoScopeRecordCliBindingTrace().bindingContext
|
||||
|
||||
@@ -367,6 +373,8 @@ object KotlinToJVMBytecodeCompiler {
|
||||
createOutputFilesFlushingCallbackIfPossible(moduleConfiguration)
|
||||
).isIrBackend(
|
||||
true
|
||||
).jvmBackendClassResolver(
|
||||
FirJvmBackendClassResolver(components)
|
||||
).build()
|
||||
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.intellij.core.CoreApplicationEnvironment
|
||||
import com.intellij.openapi.extensions.ExtensionsArea
|
||||
import java.io.File
|
||||
|
||||
// BUNCH: 193
|
||||
// FIX ME WHEN BUNCH 193 REMOVED
|
||||
fun registerExtensionPointAndExtensionsEx(pluginFile: File, fileName: String, area: ExtensionsArea) {
|
||||
@Suppress("MissingRecentApi")
|
||||
CoreApplicationEnvironment.registerExtensionPointAndExtensions(pluginFile, fileName, area)
|
||||
|
||||
@@ -10,7 +10,7 @@ import com.intellij.openapi.extensions.ExtensionsArea
|
||||
import java.io.File
|
||||
import java.nio.file.FileSystems
|
||||
|
||||
// BUNCH: 193
|
||||
// FIX ME WHEN BUNCH 193 REMOVED
|
||||
fun registerExtensionPointAndExtensionsEx(pluginFile: File, fileName: String, area: ExtensionsArea) {
|
||||
val pluginRoot = FileSystems.getDefault().getPath(pluginFile.path)
|
||||
@Suppress("MissingRecentApi")
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.metadata
|
||||
|
||||
import org.jetbrains.kotlin.analyzer.*
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.analyzer.common.CommonDependenciesContainer
|
||||
import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices
|
||||
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMonolithicSerializer
|
||||
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.konan.util.KlibMetadataFactories
|
||||
import org.jetbrains.kotlin.library.*
|
||||
import org.jetbrains.kotlin.library.impl.BuiltInsPlatform
|
||||
import org.jetbrains.kotlin.library.impl.buildKoltinLibrary
|
||||
import org.jetbrains.kotlin.library.impl.buildKotlinLibrary
|
||||
import org.jetbrains.kotlin.library.metadata.NativeTypeTransformer
|
||||
import org.jetbrains.kotlin.library.metadata.NullFlexibleTypeDeserializer
|
||||
import org.jetbrains.kotlin.library.metadata.parseModuleHeader
|
||||
@@ -83,7 +83,7 @@ internal class K2MetadataKlibSerializer(private val metadataVersion: BuiltInsBin
|
||||
irVersion = null
|
||||
)
|
||||
|
||||
buildKoltinLibrary(
|
||||
buildKotlinLibrary(
|
||||
emptyList(),
|
||||
serializedMetadata,
|
||||
null,
|
||||
@@ -91,6 +91,7 @@ internal class K2MetadataKlibSerializer(private val metadataVersion: BuiltInsBin
|
||||
destDir.absolutePath,
|
||||
configuration[CommonConfigurationKeys.MODULE_NAME]!!,
|
||||
nopack = true,
|
||||
perFile = false,
|
||||
manifestProperties = null,
|
||||
dataFlowGraph = null,
|
||||
builtInsPlatform = BuiltInsPlatform.COMMON
|
||||
@@ -244,4 +245,4 @@ private val MetadataFactories =
|
||||
{ DefaultBuiltIns.Instance },
|
||||
NullFlexibleTypeDeserializer,
|
||||
NativeTypeTransformer()
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.metadata
|
||||
|
||||
@@ -24,15 +24,9 @@ dependencies {
|
||||
runtime(project(":kotlin-reflect"))
|
||||
|
||||
embedded(project(":daemon-common")) { isTransitive = false }
|
||||
embedded(project(":daemon-common-new")) { isTransitive = false }
|
||||
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) {
|
||||
isTransitive = false
|
||||
}
|
||||
compile(commonDep("io.ktor", "ktor-network")) {
|
||||
ktorExcludesForDaemon.forEach { (group, module) ->
|
||||
exclude(group = group, module = module)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -76,8 +76,7 @@ class CompilerApiTest : KotlinIntegrationTestBase() {
|
||||
|
||||
val compilerClassPath = listOf(
|
||||
File(compilerLibDir, "kotlin-compiler.jar"),
|
||||
File(compilerLibDir, "kotlin-daemon.jar"),
|
||||
File(compilerLibDir, "ktor-network-1.0.1.jar")
|
||||
File(compilerLibDir, "kotlin-daemon.jar")
|
||||
)
|
||||
val scriptRuntimeClassPath = listOf(
|
||||
File(compilerLibDir, "kotlin-runtime.jar"),
|
||||
|
||||
@@ -97,9 +97,8 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
)
|
||||
|
||||
val daemonClientClassPath = listOf(
|
||||
File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-daemon-client-new.jar"),
|
||||
File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-compiler.jar"),
|
||||
File(KotlinIntegrationTestBase.getCompilerLib(), "ktor-network-1.0.1.jar")
|
||||
File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-daemon-client.jar"),
|
||||
File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-compiler.jar")
|
||||
)
|
||||
|
||||
val compilerId by lazy(LazyThreadSafetyMode.NONE) { CompilerId.makeCompilerId(compilerClassPath) }
|
||||
|
||||
4
compiler/fir/analysis-tests/testData/loadCompiledKotlin/class/FunInterface.txt
vendored
Normal file
4
compiler/fir/analysis-tests/testData/loadCompiledKotlin/class/FunInterface.txt
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
public abstract interface F : R|kotlin/Any| {
|
||||
public abstract fun R|kotlin/String|.f(x: R|kotlin/Int|): R|kotlin/Unit|
|
||||
|
||||
}
|
||||
@@ -27,10 +27,10 @@ FILE: lambdaInLambda.kt
|
||||
R|/buildString|(<L> = buildString@fun R|StringBuilder|.<anonymous>(): R|kotlin/Unit| {
|
||||
this@R|special/anonymous|.R|/insert|<R|StringBuilder|, R|KDocTemplate|>(R|/KDocTemplate.KDocTemplate|(), <L> = insert@fun R|KDocTemplate|.<anonymous>(): R|kotlin/Unit| {
|
||||
this@R|special/anonymous|.R|/KDocTemplate.definition|(<L> = definition@fun R|StringBuilder|.<anonymous>(): R|kotlin/Unit| {
|
||||
^ R|<local>/ordinal|?.R|kotlin/let|<R|kotlin/Int|, R|kotlin/Unit|>(<L> = let@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Unit| <kind=EXACTLY_ONCE> {
|
||||
R|<local>/ordinal|?.{ $subj$.R|kotlin/let|<R|kotlin/Int|, R|kotlin/Unit|>(<L> = let@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Unit| <kind=EXACTLY_ONCE> {
|
||||
^@let Unit
|
||||
}
|
||||
)
|
||||
) }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ FILE: main.kt
|
||||
}
|
||||
public final fun test(): R|kotlin/Unit| {
|
||||
lval processor: R|AdapterProcessor<ft<PsiMethod, PsiMethod?>!, ft<PsiClass, PsiClass?>!>| = R|/AdapterProcessor.AdapterProcessor|<R|ft<PsiMethod, PsiMethod?>!|, R|ft<PsiClass, PsiClass?>!|>(R|/Function|<R|ft<PsiMethod, PsiMethod?>!|, R|PsiClass?|>(<L> = Function@fun <anonymous>(method: R|PsiMethod?|): R|PsiClass?| {
|
||||
^ R|<local>/method|?.R|/PsiMethod.containingClass|
|
||||
^ R|<local>/method|?.{ $subj$.R|/PsiMethod.containingClass| }
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ FILE: bareTypes.kt
|
||||
public abstract interface MutableString : R|MutableA<kotlin/String>| {
|
||||
}
|
||||
public final fun test(a: R|A<kotlin/String>|): R|kotlin/Unit| {
|
||||
(R|<local>/a| as? R|MutableA<kotlin/String>|)?.R|FakeOverride</MutableA.add: R|kotlin/Unit|>|(String())
|
||||
(R|<local>/a| as? R|MutableA<kotlin/String>|)?.{ $subj$.R|FakeOverride</MutableA.add: R|kotlin/Unit|>|(String()) }
|
||||
(R|<local>/a| as R|MutableA<kotlin/String>|).R|FakeOverride</MutableA.add: R|kotlin/Unit|>|(String())
|
||||
}
|
||||
public final fun test2(a: R|A<kotlin/String>|): R|kotlin/Unit| {
|
||||
|
||||
9
compiler/fir/analysis-tests/testData/resolve/callResolution/extensionInvokeAfterSafeCall.kt
vendored
Normal file
9
compiler/fir/analysis-tests/testData/resolve/callResolution/extensionInvokeAfterSafeCall.kt
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
interface A
|
||||
|
||||
fun test_1(a: A?, convert: A.() -> String) {
|
||||
val s = a?.convert()
|
||||
}
|
||||
|
||||
fun test_2(a: A, convert: A.() -> String) {
|
||||
val s = a.convert()
|
||||
}
|
||||
9
compiler/fir/analysis-tests/testData/resolve/callResolution/extensionInvokeAfterSafeCall.txt
vendored
Normal file
9
compiler/fir/analysis-tests/testData/resolve/callResolution/extensionInvokeAfterSafeCall.txt
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
FILE: extensionInvokeAfterSafeCall.kt
|
||||
public abstract interface A : R|kotlin/Any| {
|
||||
}
|
||||
public final fun test_1(a: R|A?|, convert: R|A.() -> kotlin/String|): R|kotlin/Unit| {
|
||||
lval s: R|kotlin/String?| = R|<local>/a|?.{ R|<local>/convert|.R|FakeOverride<kotlin/Function1.invoke: R|kotlin/String|>|($subj$) }
|
||||
}
|
||||
public final fun test_2(a: R|A|, convert: R|A.() -> kotlin/String|): R|kotlin/Unit| {
|
||||
lval s: R|kotlin/String| = R|<local>/convert|.R|FakeOverride<kotlin/Function1.invoke: R|kotlin/String|>|(R|<local>/a|)
|
||||
}
|
||||
7
compiler/fir/analysis-tests/testData/resolve/callResolution/safeCallOnTypeAlias.kt
vendored
Normal file
7
compiler/fir/analysis-tests/testData/resolve/callResolution/safeCallOnTypeAlias.kt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
typealias MyTypeAlias = (() -> String?)?
|
||||
fun foo(x: MyTypeAlias) {
|
||||
|
||||
x?.let { y -> y()?.let { result -> bar(result) } }
|
||||
}
|
||||
|
||||
fun bar(x: String) = x
|
||||
14
compiler/fir/analysis-tests/testData/resolve/callResolution/safeCallOnTypeAlias.txt
vendored
Normal file
14
compiler/fir/analysis-tests/testData/resolve/callResolution/safeCallOnTypeAlias.txt
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
FILE: safeCallOnTypeAlias.kt
|
||||
public final typealias MyTypeAlias = R|() -> kotlin/String?|
|
||||
public final fun foo(x: R|MyTypeAlias|): R|kotlin/Unit| {
|
||||
R|<local>/x|?.{ $subj$.R|kotlin/let|<R|() -> kotlin/String?|, R|kotlin/String|>(<L> = let@fun <anonymous>(y: R|() -> kotlin/String?|): R|kotlin/String| <kind=EXACTLY_ONCE> {
|
||||
^ R|<local>/y|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/String?|>|()?.{ $subj$.R|kotlin/let|<R|kotlin/String|, R|kotlin/String|>(<L> = let@fun <anonymous>(result: R|kotlin/String|): R|kotlin/String| <kind=EXACTLY_ONCE> {
|
||||
^ R|/bar|(R|<local>/result|)
|
||||
}
|
||||
) }
|
||||
}
|
||||
) }
|
||||
}
|
||||
public final fun bar(x: R|kotlin/String|): R|kotlin/String| {
|
||||
^bar R|<local>/x|
|
||||
}
|
||||
@@ -62,7 +62,7 @@ digraph initBlockAndInPlaceLambda_kt {
|
||||
}
|
||||
26 [label="Call arguments union" style="filled" fillcolor=yellow];
|
||||
24 [label="Postponed exit from lambda"];
|
||||
25 [label="Function call: R|<local>/a|.R|/A.b|?.R|kotlin/let|<R|B|, R|C|>(...)"];
|
||||
25 [label="Function call: $subj$.R|kotlin/let|<R|B|, R|C|>(...)"];
|
||||
22 [label="Exit safe call"];
|
||||
27 [label="Variable declaration: lval c: R|C?|"];
|
||||
28 [label="Exit block"];
|
||||
|
||||
@@ -12,10 +12,10 @@ FILE: initBlockAndInPlaceLambda.kt
|
||||
}
|
||||
|
||||
init {
|
||||
lval c: R|C?| = R|<local>/a|.R|/A.b|?.R|kotlin/let|<R|B|, R|C|>(<L> = let@fun <anonymous>(it: R|B|): R|C| <kind=EXACTLY_ONCE> {
|
||||
lval c: R|C?| = R|<local>/a|.R|/A.b|?.{ $subj$.R|kotlin/let|<R|B|, R|C|>(<L> = let@fun <anonymous>(it: R|B|): R|C| <kind=EXACTLY_ONCE> {
|
||||
^ R|/C.C|(R|<local>/a|, R|<local>/it|)
|
||||
}
|
||||
)
|
||||
) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ FILE: localClassesWithImplicit.kt
|
||||
}
|
||||
|
||||
lval x: R|anonymous| = object : R|kotlin/Any| {
|
||||
private constructor(): R|anonymous| {
|
||||
private[local] constructor(): R|anonymous| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
|
||||
@@ -62,10 +62,10 @@ digraph safeCalls_kt {
|
||||
16 [label="Enter function test_1" style="filled" fillcolor=red];
|
||||
18 [label="Access variable R|<local>/x|"];
|
||||
19 [label="Enter safe call"];
|
||||
21 [label="Function call: R|<local>/x|?.R|/A.foo|()"];
|
||||
21 [label="Function call: $subj$.R|/A.foo|()"];
|
||||
20 [label="Exit safe call"];
|
||||
22 [label="Enter safe call"];
|
||||
24 [label="Function call: R|<local>/x|?.R|/A.foo|()?.R|/A.bar|()"];
|
||||
24 [label="Function call: $subj$.R|/A.bar|()"];
|
||||
23 [label="Exit safe call"];
|
||||
17 [label="Exit function test_1" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user