Compare commits

...

199 Commits

Author SHA1 Message Date
Ivan Kylchik
8e8dd98968 ~~~ Prepare for stdlib jar ~~~ 2021-04-01 16:15:43 +03:00
Ivan Kylchik
53da9b9028 ~~~ Remove ir body map ~~~ 2021-04-01 16:13:46 +03:00
Georgy Bronnikov
aef5af5298 JVM_IR: fix multifile facades deserialization 2021-04-01 15:32:35 +03:00
Georgy Bronnikov
15d93a6931 TMP: set source in deserialized declarations 2021-04-01 15:32:33 +03:00
Georgy Bronnikov
ffe8ef1d0b fixup! JVM_IR: store special annotation constructors in symbol table 2021-04-01 15:32:32 +03:00
Georgy Bronnikov
f5c0b212bf JVM_IR: deal with accessors in findFacadeClassName 2021-03-29 20:41:29 +03:00
Georgy Bronnikov
0fca4c8144 JVM_IR: handle accessors of toplevel properties in SingleClassJvmIrProvider 2021-03-29 20:41:28 +03:00
Georgy Bronnikov
56f7bcf3fe JVM_IR: store special annotation constructors in symbol table
Annotation constructors created in JvmGeneratorExtensions can end up
referenced from serialized IR, so they should be deserialized properly.
Thus a reference from symbol table is needed.
2021-03-29 20:41:26 +03:00
Georgy Bronnikov
fee7323e33 JVM_IR: deal with facade classes when computing signature 2021-03-29 20:41:24 +03:00
Georgy Bronnikov
8bc3c486a3 TMP: fake overrides fix
Symbols for fake overrides may remain unbound even after their parent
class is deserialized.
Will need to properly deal with this later.
2021-03-29 20:41:22 +03:00
Georgy Bronnikov
65684d496a IR: a slightly more accurate mangling for flexible types
Still won't work for complicated cases.
2021-03-29 20:41:20 +03:00
Georgy Bronnikov
0d9064a298 JVM_IR: run Kotlin against Kotlin tests with serialization 2021-03-29 20:41:19 +03:00
Georgy Bronnikov
aabbdf40d1 JVM_IR: set containerSource for deserialized IR 2021-03-29 20:41:17 +03:00
Georgy Bronnikov
cc3dc8d0da IR, minor: fix typo in function name 2021-03-29 20:41:15 +03:00
Georgy Bronnikov
7e837775fc TMP: store facade names in JvmIr 2021-03-29 20:41:13 +03:00
Georgy Bronnikov
bd57b54592 TMP: deserializer 2021-03-29 20:41:11 +03:00
Georgy Bronnikov
d20a75eb64 TMP: save auxTables as byte strings 2021-03-29 20:41:09 +03:00
Georgy Bronnikov
378a0e7016 TMP: start SingleClassJvmIrProvider, insert it into irProviders list 2021-03-29 20:41:07 +03:00
Georgy Bronnikov
d8189eed95 JVM_IR: read serialized IR from annotation 2021-03-29 20:41:05 +03:00
Georgy Bronnikov
e206eeb280 JVM_IR: write serialized IR to an annotation 2021-03-29 20:41:03 +03:00
Georgy Bronnikov
82dd52ca09 JVM_IR: compute public signatures for Java fields 2021-03-29 20:40:58 +03:00
Georgy Bronnikov
b86e393871 JVM_IR: call serialization for IR.
Serializer for IR is called when -Xserialize-ir flag is set.
2021-03-29 20:40:57 +03:00
Georgy Bronnikov
039ebaf8f8 JVM_IR: JvmIrSerializer 2021-03-29 20:40:54 +03:00
Ivan Kylchik
f74e9d0a9f [TESTS] 2021-03-29 20:39:26 +03:00
Ivan Kylchik
6f349c9096 Add new intercept method in CallInterceptor for proxy 2021-03-29 20:39:25 +03:00
Ivan Kylchik
c75d513b9d Add CallInterceptor interface and implement its default realization
CallInterceptor will handle calls, constructor calls, getting enum
entries and objects. It will make a decision for interpreter: this call
must be handled as intrinsic or it must be interpreted.
2021-03-29 20:39:25 +03:00
Ivan Kylchik
a7af0e3562 Edit interpreter tests to check new structure with call stack 2021-03-29 20:39:25 +03:00
Ivan Kylchik
52cb820370 Refactor entire IrInterpreter to achieve better code quality 2021-03-29 20:39:24 +03:00
Ivan Kylchik
b070b5fc1d Get correct irClass from class reference in case of reified parameter 2021-03-29 20:39:24 +03:00
Ivan Kylchik
8759fa4364 Implement more precise control flow in ir interpreter
"More precise" in terms of the number of passing tests.
In this case ControlStructures test block was used.
2021-03-29 20:39:24 +03:00
Ivan Kylchik
585bdeef38 Implement correct processing for receivers of IrFunctionReference 2021-03-29 20:39:23 +03:00
Ivan Kylchik
e4fb85d337 Fix interpretation of arguments with default value 2021-03-29 20:39:23 +03:00
Ivan Kylchik
000cad3ac2 Implement interpretation of assert method 2021-03-29 20:39:23 +03:00
Ivan Kylchik
f4a97527b0 Mark assert method with EvaluateIntrinsic annotation 2021-03-29 20:39:23 +03:00
Ivan Kylchik
e2dbb5e262 Implement spec tests runner for ir interpreter 2021-03-29 20:39:22 +03:00
Ivan Kylchik
e441168f34 Implement black box tests runner for ir interpreter 2021-03-29 20:39:22 +03:00
Ivan Kylchik
92397ea879 Rename expression value parameter to declaration 2021-03-29 20:39:22 +03:00
Alexander Udalov
ec3c1f03bc Rewrite generator for IrBuiltInsMapGenerated
Similarly to 742fef9042 with OperationsMapGenerated, use optimized
`when` over strings instead of lambdas because lambdas lead to a lot of
bytecode.

The change in `IrConst.toPrimitive` is needed because for operations
like `Byte.plus(Int)` the IrConst instance would have the IR type for
kotlin.Byte, but the actual runtime value of type Int
(java.lang.Integer), which would lead to CCE from
`interpretBinaryFunction`. Previously it didn't fail because of
unchecked cast before calling the lambda, which allowed a value of
runtime type java.lang.Integer to sneak through to the lambda parameter
and be "unboxed" to the correct type via the `(... as Number).toByte()`
conversion which backend generates.

The main benefit of this change is that it reduces the size of the
proguarded compiler jar by ~0.69%.
2021-03-29 20:39:21 +03:00
Ivan Kylchik
fb03015cbb Add test to check sourceLocation evaluation 2021-03-29 20:39:21 +03:00
Ivan Kylchik
e43fa7883e [HACK] Change report's text if evaluation were made for default arg 2021-03-29 20:39:21 +03:00
Ivan Kylchik
07ed11b8e6 Handle default arguments separately from call in visitCall method
This change allow to evaluate sourceLocation intrinsic correctly
2021-03-29 20:39:20 +03:00
Ivan Kylchik
196c2e1e57 Add new intrinsic handler for sourceLocation function 2021-03-29 20:39:20 +03:00
Ivan Kylchik
5498e1db1f Add new intrinsic function - sourceLocation 2021-03-29 20:39:20 +03:00
Ivan Kylchik
13ae1e36f1 Simplify work with stack by adding additional newFrame method 2021-03-29 20:39:19 +03:00
Ivan Kylchik
ca2fe3337a Fix tests results after fixing lines number calculation 2021-03-29 20:39:19 +03:00
Ivan Kylchik
3f24334396 [HACK] Fix line number showing in tests by adding plus 1 2021-03-29 20:39:19 +03:00
Ivan Kylchik
14f618f01b Pass irFile in interpreter method in CompileTimeCalculationLowering 2021-03-29 20:39:18 +03:00
Ivan Kylchik
4e3226ca7a Pass irFile in interpreter method in IrConstTransformer 2021-03-29 20:39:18 +03:00
Ivan Kylchik
c1fa0d26f3 Save right entry point for expression 2021-03-29 20:39:18 +03:00
Ivan Kylchik
656ecd6e8b Add use case test with matrix 2021-03-29 20:39:17 +03:00
Ivan Kylchik
e6b2ecf9f6 Add test to check correctness of array's subtype check 2021-03-29 20:39:17 +03:00
Ivan Kylchik
9d6dbc30b7 Remove typeArguments field in State interface 2021-03-29 20:39:17 +03:00
Ivan Kylchik
a2da6a9658 Mark Pair class as intrinsic for compile time calculations
This class will be handled as intrinsic because result of
native methods invocation can return Pair and there are some methods
that expect Pair as value argument
2021-03-29 20:39:16 +03:00
Ivan Kylchik
8597a20113 Simplify getIrFunctionByIrCall method in KFunctionState
Removed check of invoke symbols equality. Without this check it is
possible to store init functions in KFunctionState
2021-03-29 20:39:16 +03:00
Ivan Kylchik
f6041ab003 Change call method of KProperty1Proxy to invoke corresponding accessors 2021-03-29 20:39:16 +03:00
Ivan Kylchik
4ee4f70ddc Create some tests to check KParameter and KProperty2 interpretation 2021-03-29 20:39:16 +03:00
Ivan Kylchik
926714b04e Implement getter and setter for KPropertyProxy 2021-03-29 20:39:15 +03:00
Ivan Kylchik
68cf5f02dc Implement interpretation of KParameter interface 2021-03-29 20:39:15 +03:00
Ivan Kylchik
57a368b40d Extract all reflection state classes into separate files 2021-03-29 20:39:15 +03:00
Ivan Kylchik
2830e7b531 Move ReflectionState.kt into separate package "reflection" 2021-03-29 20:39:14 +03:00
Ivan Kylchik
f085a6a4b8 Add new test cases in classReference.kt file 2021-03-29 20:39:14 +03:00
Ivan Kylchik
390e685495 Implement interpretation for KType and KTypeParameter 2021-03-29 20:39:14 +03:00
Ivan Kylchik
17bad5eea2 Mark KType, KParameter and KTypeParameter as compile time 2021-03-29 20:39:13 +03:00
Ivan Kylchik
abe7b5aa81 Implement interpretation of KVisibility class
For now it is interpreted as Wrapper, but maybe further it will be
converted to Common
2021-03-29 20:39:13 +03:00
Ivan Kylchik
0eb557fe0b Mark KVisibility class as compile time and intrinsic for wrapper 2021-03-29 20:39:13 +03:00
Ivan Kylchik
e01326b58b Create test file to check class reference interpretation 2021-03-29 20:39:12 +03:00
Ivan Kylchik
a7ed79a8ef Implement basic interpretation of IrClassReference 2021-03-29 20:39:12 +03:00
Ivan Kylchik
f5b718a785 Mark KClass interface as compile time 2021-03-29 20:39:12 +03:00
Ivan Kylchik
9c9694bf86 Replace Lambda state with KFunctionState 2021-03-29 20:39:12 +03:00
Ivan Kylchik
5a4e3f64df Mark KFunction as compile time object 2021-03-29 20:39:11 +03:00
Ivan Kylchik
5558f4ddc9 Fix constructor call checker
Forget to check constructor for availability of compile time annotation
2021-03-29 20:39:11 +03:00
Ivan Kylchik
7c8b6a1d86 Create some tests for property reference interpretation 2021-03-29 20:39:11 +03:00
Ivan Kylchik
c664baff4b Implement basic interpretation for KProperty 2021-03-29 20:39:10 +03:00
Ivan Kylchik
aed167876d Mark KProperty as compile time 2021-03-29 20:39:10 +03:00
Ivan Kylchik
131b498928 Allow to interpret property reference 2021-03-29 20:39:10 +03:00
Ivan Kylchik
eb68a9420f Add tests for double list and double array 2021-03-29 20:39:09 +03:00
Ivan Kylchik
84f73bd388 Forbid vararg flattening in place there array was expected 2021-03-29 20:39:09 +03:00
Ivan Kylchik
bceaf43c8a Change interpretConstructor method to be able to init object single time 2021-03-29 20:39:09 +03:00
Ivan Kylchik
1873fed551 Add additional type arguments to stack from super types 2021-03-29 20:39:08 +03:00
Ivan Kylchik
533c1e672a Add outer object to stack when interpreting constructor of inner class 2021-03-29 20:39:08 +03:00
Ivan Kylchik
e12146c39b Fix interpreter error related to recreating Unit object 2021-03-29 20:39:08 +03:00
Ivan Kylchik
b0a38d47c8 Fix interpreter error related to incorrect work of get value for objects 2021-03-29 20:39:07 +03:00
Ivan Kylchik
f301ae70cb Create test data to check interpreter proxy 2021-03-29 20:39:07 +03:00
Ivan Kylchik
310e17103d Implement simple proxy for non interface structures like kotlin.Pair 2021-03-29 20:39:07 +03:00
Ivan Kylchik
17daea6c0c Implement proxy for custom interfaces 2021-03-29 20:39:07 +03:00
Ivan Kylchik
0506ab45d8 Implement LambdaProxy class 2021-03-29 20:39:06 +03:00
Ivan Kylchik
d48080340a Remove EvaluateIntrinsic annotation from replace method in Regex class
For now this method will be interpreted using lambda proxy
2021-03-29 20:39:06 +03:00
Ivan Kylchik
e34b099346 Rewrite Complex class as interface
This change allow ExceptionState to be both: State and Throwable
2021-03-29 20:39:06 +03:00
Ivan Kylchik
30aee310fd Remove method getOriginal from Complex class 2021-03-29 20:39:05 +03:00
Ivan Kylchik
9e9be0a563 Implement basic proxy for Common state
For now proxy works only for Any class methods and only for Common
state.
2021-03-29 20:39:05 +03:00
Ivan Kylchik
15d7100317 Add possibility to interpret spread operator on unsigned arrays 2021-03-29 20:39:05 +03:00
Ivan Kylchik
815a02b60a Rethrow InterpreterTimeOutError as UserException 2021-03-29 20:39:04 +03:00
Ivan Kylchik
2d1a1ca267 Fix type arguments saving for arrays 2021-03-29 20:39:04 +03:00
Ivan Kylchik
9ae85f32fa Change evaluation function for arrayOf
For now it will get only elements variable from stack instead of all
2021-03-29 20:39:04 +03:00
Ivan Kylchik
c71a7256f3 Implement method to calculate EQEQ ir builtin function 2021-03-29 20:39:03 +03:00
Ivan Kylchik
32e55520bb Extend compile time checker to check constructors body 2021-03-29 20:39:03 +03:00
Ivan Kylchik
9fa66ea6df Support isSubtypeOf check for functional type 2021-03-29 20:39:03 +03:00
Ivan Kylchik
dfdb0a10f1 Add handler for setters in getIrFunction method 2021-03-29 20:39:02 +03:00
Ivan Kylchik
754ff3365b Add simple handler for StackOverflowError in ir interpreter 2021-03-29 20:39:02 +03:00
Ivan Kylchik
d0399e22f5 Rewrite exceptions handler in ir interpreter
For now there are 2 types of exceptions:
1. UserExceptions - express user and jvm like exceptions
2. InterpreterError - thrown only if something is wrong with interpreter
2021-03-29 20:39:02 +03:00
Ivan Kylchik
653b2472d7 Support multiple nested try blocks 2021-03-29 20:39:01 +03:00
Ivan Kylchik
8e8b3f6e40 Support lateinit var interpretation 2021-03-29 20:39:01 +03:00
Ivan Kylchik
59696a2bfa Forbid to interpret non const top level properties 2021-03-29 20:39:01 +03:00
Ivan Kylchik
16389a0bae Fix compile time checker for get and set field expressions 2021-03-29 20:39:01 +03:00
Ivan Kylchik
e6a39f5447 Return Unit value from block if statements count is equal to zero 2021-03-29 20:39:00 +03:00
Ivan Kylchik
6a910f8aa9 Move out compile time function checker logic to EvaluationMode.kt 2021-03-29 20:39:00 +03:00
Ivan Kylchik
751b19ebde Add WITH_ANNOTATIONS evaluation mode
This mode means that function can be executed if it has
CompileTimeCalculation annotation. FULL mode for now will mean deep
check without annotation help
2021-03-29 20:39:00 +03:00
Ivan Kylchik
19d3596a8c Move IrCompileTimeChecker to separate checker package 2021-03-29 20:38:59 +03:00
Ivan Kylchik
abaa7f5fc4 Fix interpreting of java static members such as Boolean.TRUE 2021-03-29 20:38:59 +03:00
Ivan Kylchik
c3206ef39a [HACK] Add compile time evaluation phase to jvm lowering 2021-03-29 20:38:59 +03:00
Ivan Kylchik
c12cad78e6 [HACK] Move compile time lowering up in js lowering phases
This change was caused by several facts:
- IrInterpreter need information that isn't available in old phase
position
- Now it will be easier to move in jvm phases
2021-03-29 20:38:58 +03:00
Ivan Kylchik
4206c27885 [HACK] Create js lowering phase for compile time calculations 2021-03-29 20:38:58 +03:00
Ivan Kylchik
fc4c0aeed1 Create ir lowering for compile-time calculations 2021-03-29 20:38:58 +03:00
Ivan Kylchik
b4b634858f Add tests for type checks and casts 2021-03-29 20:38:57 +03:00
Ivan Kylchik
abd8a07e1f Remove const modifier inside complexReturn test
This is because of jvm codegen, it can't handle const modifier with non
const expression
2021-03-29 20:38:57 +03:00
Ivan Kylchik
c1523366ad Add test to check interpretation of jvm static fields 2021-03-29 20:38:57 +03:00
Ivan Kylchik
c9a92f5caf Add new test case in tryFinally.kt 2021-03-29 20:38:56 +03:00
Ivan Kylchik
953ed5e8b6 Change results of jsLong test 2021-03-29 20:38:56 +03:00
Ivan Kylchik
358e465ae2 Add tests to check interpretation inside object declaration 2021-03-29 20:38:56 +03:00
Ivan Kylchik
84d07cb614 Add jvm tests for ir compile time interpreter 2021-03-29 20:38:56 +03:00
Ivan Kylchik
803d2dd212 [HACK] Implement basic report method in JvmBackendContext 2021-03-29 20:38:55 +03:00
Ivan Kylchik
f6f1754508 Change checker to be able to evaluate expect type aliases 2021-03-29 20:38:55 +03:00
Ivan Kylchik
e3629d12fd Mark jmv emptyArray function as compile time 2021-03-29 20:38:55 +03:00
Ivan Kylchik
f90f181273 Mark jmv ArrayList, LinkedHashMap and LinkedHashSet as compile time 2021-03-29 20:38:54 +03:00
Ivan Kylchik
88245019ac Mark jmv Regex class as compile time 2021-03-29 20:38:54 +03:00
Ivan Kylchik
6a40a2ed7a Mark jvm exceptions type aliases as compile time 2021-03-29 20:38:54 +03:00
Ivan Kylchik
b37c4f191d Mark jvm StringBuilder and Appendable type aliases as compile time 2021-03-29 20:38:53 +03:00
Ivan Kylchik
ce8174321e Add possibility to add CompileTimeCalculation annotation to type alias 2021-03-29 20:38:53 +03:00
Ivan Kylchik
deac29349e Rename AbstractJsIrWithKlibTest to AbstractJsInterpreterTestCase 2021-02-26 20:44:26 +03:00
Ivan Kylchik
e1025129ee Add tests to check interpretation of String's plus extension function 2021-02-26 20:44:25 +03:00
Ivan Kylchik
bfe7e791f3 Mark String's plus extension function as compile-time 2021-02-26 20:44:25 +03:00
Ivan Kylchik
11774994c6 Add test to check interpretation of inner classes 2021-02-26 20:44:25 +03:00
Ivan Kylchik
ee124cbee5 Add test for checking interpretation of overridden extension method 2021-02-26 20:44:25 +03:00
Ivan Kylchik
d678eb0d0d Remove unused equals and hashCode methods from Primitive class
They were used only when primitive objects were put in the collection
as they were, in Primitive state wrapper
2021-02-26 20:44:25 +03:00
Ivan Kylchik
6c6244b630 Add test to check toList function interpretation 2021-02-26 20:44:25 +03:00
Ivan Kylchik
1b9aa7ffd3 Mark toMutableList extension function as intrinsic 2021-02-26 20:44:24 +03:00
Ivan Kylchik
8f587c7b17 Add test data for interpreter 2021-02-26 20:44:24 +03:00
Ivan Kylchik
2e2eb1e550 Remove an unused annotation on get method in StringBuilder class
This annotation is unused in this place since kotlin to java name
resolving is handling inside Wrapper class
2021-02-26 20:44:24 +03:00
Ivan Kylchik
538750ef77 Mark Map interface as compile time 2021-02-26 20:44:24 +03:00
Ivan Kylchik
cb4b6e0019 Mark Set interface as compile time
Also marked one of its implementation - LinkedHashSet
2021-02-26 20:44:24 +03:00
Ivan Kylchik
74c0c8588a Mark toCollection, toList and toSet methods as compile time 2021-02-26 20:44:23 +03:00
Ivan Kylchik
d94a46021b Mark some of stdlib methods that are responsible for aggregation
This number doesn't include only scan and onEach methods
2021-02-26 20:44:23 +03:00
Ivan Kylchik
60297251ce Mark Comparator interface as compile time 2021-02-26 20:44:23 +03:00
Ivan Kylchik
8eefb31a6a Add copy of compile time annotations into stdlib module
This fix gives us possibility to build compiler in one iteration
2021-02-26 20:44:23 +03:00
Ivan Kylchik
08055885be Mark some of stdlib filtering operations 2021-02-26 20:44:23 +03:00
Ivan Kylchik
48b801965b Mark joinTo and joinToString stdlib methods 2021-02-26 20:44:23 +03:00
Ivan Kylchik
9748449b4e Mark "coerce*" stdlib methods as compile time in Comparables.kt file 2021-02-26 20:44:22 +03:00
Ivan Kylchik
d14e960599 Mark stdlib methods that are responsible for operations with ranges 2021-02-26 20:44:22 +03:00
Ivan Kylchik
26d277ae7b Mark indices and lastIndex methods as compile time in all arrays 2021-02-26 20:44:22 +03:00
Ivan Kylchik
2a0cc9cc8d Mark stdlib methods that are responsible for getting elements 2021-02-26 20:44:22 +03:00
Ivan Kylchik
9f49e3a022 Mark some of stdlib CharSequence extension methods as compile time
Marked such methods as iterator, isEmpty, isNotEmpty,
indices and lastIndex
2021-02-26 20:44:22 +03:00
Ivan Kylchik
e7695c4f57 Mark isEmpty and isNotEmpty array methods as compile time 2021-02-26 20:44:21 +03:00
Ivan Kylchik
652144ce23 Mark fromClosedRange method in progression classes 2021-02-26 20:44:21 +03:00
Ivan Kylchik
ac8dbba2f5 Mark extension iterator's methods as compile time 2021-02-26 20:44:21 +03:00
Ivan Kylchik
3052c6ed57 Mark asSequence function as compile time for arrays and iterables 2021-02-26 20:44:21 +03:00
Ivan Kylchik
750f5cc5e3 Mark Sequence interface and its utils as compile time 2021-02-26 20:44:21 +03:00
Ivan Kylchik
b90fd16458 Implement basic report logic in JsIrBackendContext 2021-02-26 20:44:21 +03:00
Ivan Kylchik
2e6d9138a7 Add some diagnostic tests for CompileTime annotation 2021-02-26 20:44:20 +03:00
Ivan Kylchik
b8cc94d705 Make implicitly property compile-time
This is allowed for properties inside compile-time primary constructor
2021-02-23 15:57:19 +03:00
Ivan Kylchik
939a5e2ce0 Allow call for compile-time fake override methods 2021-02-23 15:57:18 +03:00
Ivan Kylchik
890b911c41 Disable CompileTimeCalculations feature in kotlin-gradle-plugin module
This is needed because this module uses old stdlib, there methods
are not marked with CompileTimeCalculation annotation
2021-02-23 15:57:18 +03:00
Ivan Kylchik
bc719f7e90 Implement declaration checker for compile time members
Checker works according to the following rule: if class or its primary
constructor are marked as compile time then all overridden compile
time methods also must be marked as compile time
2021-02-23 15:56:32 +03:00
Ivan Kylchik
4a710f63fc Add new language feature CompileTimeCalculations 2021-02-23 15:56:32 +03:00
Ivan Kylchik
afa20b1867 Allow const modifier on property with constexpr initializer 2021-02-23 15:56:31 +03:00
Ivan Kylchik
5431be34f1 Mark all necessary utils for correct interpretation of unsigned numbers 2021-02-23 15:56:31 +03:00
Ivan Kylchik
a0daa70a98 Mark unsigned types with CompileTimeCalculation annotation 2021-02-23 15:56:31 +03:00
Ivan Kylchik
8f2557729f Mark Char and Long classes from js stdlib as compile-time 2021-02-23 15:56:31 +03:00
Ivan Kylchik
8e09285e65 Mark toString extension method as compile time 2021-02-23 15:56:31 +03:00
Ivan Kylchik
f87d4735f7 Mark hashCode method of Enum class as intrinsic 2021-02-23 15:56:31 +03:00
Ivan Kylchik
2d1aa10bc3 Mark Regex class as compile-time 2021-02-23 15:56:31 +03:00
Ivan Kylchik
1de90a2fa2 Mark RegexOption enum class as compile-time 2021-02-23 15:56:31 +03:00
Ivan Kylchik
cd5772f769 Mark enumValues and enumValueOf functions as compile time 2021-02-23 15:56:30 +03:00
Ivan Kylchik
fec89e9829 Mark Enum class with CompileTimeCalculation annotation 2021-02-23 15:56:30 +03:00
Ivan Kylchik
98a076fa6b Mark scope functions with CompileTimeCalculation annotation 2021-02-23 15:56:30 +03:00
Ivan Kylchik
353149cd3e Mark Number class with CompileTimeCalculation annotation 2021-02-23 15:56:30 +03:00
Ivan Kylchik
9e8a78183e Mark methods in progressionUtil with CompileTimeCalculation annotation 2021-02-23 15:56:30 +03:00
Ivan Kylchik
861d96f2f3 Mark indices and lastIndex with CompileTimeCalculation annotation 2021-02-23 15:56:30 +03:00
Ivan Kylchik
fd85f4ab48 Mark indexOf and contains methods with CompileTimeCalculation annotation 2021-02-23 15:56:30 +03:00
Ivan Kylchik
834c0a3ff1 Mark arrayOfNulls as compile-time 2021-02-23 15:56:30 +03:00
Ivan Kylchik
0e58e89f21 Remove annotations from primitive classes methods and mark whole classes 2021-02-23 15:56:29 +03:00
Ivan Kylchik
35fe33d8a8 Mark arrayOf and emptyArray functions as intrinsic 2021-02-23 15:56:29 +03:00
Ivan Kylchik
d396c84d96 Mark entire Any class with CompileTimeCalculation annotation
This change suppose to be a base for further constexpr feature design.
Methods from Any class now can be evaluated in compile time if:
- entire child class is marked as compile time
- separately equals, hashCode or toString methods are marked as compile time
2021-02-23 15:56:29 +03:00
Ivan Kylchik
b39cfe3f03 Mark exception classes with CompileTimeCalculation annotation 2021-02-23 15:56:29 +03:00
Ivan Kylchik
837de8f30c Mark Function interfaces with CompileTimeCalculation annotation 2021-02-23 15:56:29 +03:00
Ivan Kylchik
511802f36c Mark isWhitespace function as compile-time 2021-02-23 15:56:29 +03:00
Ivan Kylchik
717b2314b0 Mark StringBuilder and Appendable classes as compile-time 2021-02-23 15:56:29 +03:00
Ivan Kylchik
5c9c2bea0a Mark List with CompileTimeCalculation annotation
Also marked all necessary methods from stdlib to be able to create List
2021-02-23 15:56:29 +03:00
Ivan Kylchik
e0a22eda64 Mark arrayOf symbols with CompileTimeCalculation annotation 2021-02-23 15:56:28 +03:00
Ivan Kylchik
0bb6fbdbe4 Mark arrays with CompileTimeCalculation annotation 2021-02-23 15:56:28 +03:00
Ivan Kylchik
d2197a7763 Mark all necessary functions and methods to be able to interpret "trim" 2021-02-23 15:56:28 +03:00
Ivan Kylchik
c2a8398f15 Introduce new annotation EvaluateIntrinsic
This annotation means that function or class cannot be interpreted and
must be evaluated.
2021-02-23 15:56:28 +03:00
Ivan Kylchik
2d83d619a7 Mark rangeTo methods in js primitives as compile time 2021-02-23 15:56:28 +03:00
Ivan Kylchik
171b3a94db Mark ranges and progressions with CompileTimeCalculation annotation
Also add AnnotationTarget.CLASS to compile time annotation.
It will be used to show that all methods of the class can be
calculated in compile time.
2021-02-23 15:56:28 +03:00
Ivan Kylchik
3b6113de0f Add CompileTimeCalculation annotation to Any constructor 2021-02-23 15:56:28 +03:00
Ivan Kylchik
4dfe9c0825 Allow interpretation for ir builtins operators 2021-02-23 15:56:28 +03:00
Ivan Kylchik
74436a7f73 Add CompileTimeCalculation annotation to Char class 2021-02-23 15:56:27 +03:00
Ivan Kylchik
8d6f846546 Annotate methods in js builtins that can be calculated in compile time 2021-02-23 15:56:27 +03:00
Ivan Kylchik
8b430b1a58 Add annotation "CompileTimeCalculation" to built in methods
Some functions was excluded: "rangeTo", "hashCode", "inc", "dec"
and "subSequence".
2021-02-23 15:56:27 +03:00
Ivan Kylchik
1bae1c7bde Add new annotation "CompileTimeCalculation"
It is temporary annotation used for mark built in methods.
Such methods will be executed in compile time.
2021-02-23 15:56:27 +03:00
503 changed files with 68638 additions and 2101 deletions

View File

@@ -467,6 +467,12 @@ default: `indy-with-constants` for JVM target 9 or greater, `inline` otherwise""
)
var suppressDeprecatedJvmTargetWarning: Boolean by FreezableVar(false)
@Argument(
value = "-Xserialize-ir",
description = "Save IR to metadata"
)
var serializeIr: Boolean by FreezableVar(false)
override fun configureAnalysisFlags(collector: MessageCollector): MutableMap<AnalysisFlag<*>, Any> {
val result = super.configureAnalysisFlags(collector)
result[JvmAnalysisFlags.strictMetadataVersionSemantics] = strictMetadataVersionSemantics

View File

@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.jvm.JvmGeneratorExtensions
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
import org.jetbrains.kotlin.backend.jvm.jvmPhases
import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.checkKotlinPackageUsage
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
@@ -61,6 +62,9 @@ import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
import org.jetbrains.kotlin.fir.session.FirJvmModuleInfo
import org.jetbrains.kotlin.fir.session.FirSessionFactory
import org.jetbrains.kotlin.ir.backend.jvm.jvmResolveLibraries
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmManglerDesc
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.javac.JavacWrapper
import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager
import org.jetbrains.kotlin.modules.Module
@@ -353,8 +357,9 @@ object KotlinToJVMBytecodeCompiler {
performanceManager?.notifyGenerationStarted()
performanceManager?.notifyIRTranslationStarted()
val extensions = JvmGeneratorExtensions()
val (moduleFragment, symbolTable, sourceManager, components) = firAnalyzerFacade.convertToIr(extensions)
val symbolTable = SymbolTable(JvmIdSignatureDescriptor(JvmManglerDesc()), IrFactoryImpl)
val extensions = JvmGeneratorExtensions(symbolTable)
val (moduleFragment, sourceManager, components) = firAnalyzerFacade.convertToIr(environment.project, symbolTable, extensions)
performanceManager?.notifyIRTranslationFinished()

View File

@@ -249,6 +249,8 @@ fun CompilerConfiguration.configureAdvancedJvmOptions(arguments: K2JVMCompilerAr
put(JVMConfigurationKeys.NO_RESET_JAR_TIMESTAMPS, arguments.noResetJarTimestamps)
put(JVMConfigurationKeys.NO_UNIFIED_NULL_CHECKS, arguments.noUnifiedNullChecks)
put(JVMConfigurationKeys.SERIALIZE_IR, arguments.serializeIr)
if (!JVMConstructorCallNormalizationMode.isSupportedValue(arguments.constructorCallNormalizationMode)) {
messageCollector.report(
ERROR,

View File

@@ -152,4 +152,7 @@ public class JVMConfigurationKeys {
public static final CompilerConfigurationKey<Boolean> NO_REFLECT =
CompilerConfigurationKey.create("Don't automatically include kotlin-reflect.jar into the output if the output is a jar");
public static final CompilerConfigurationKey<Boolean> SERIALIZE_IR =
CompilerConfigurationKey.create("serialize IR to class metadata");
}

View File

@@ -4320,6 +4320,40 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/constexpr")
@TestDataPath("$PROJECT_ROOT")
public class Constexpr {
@Test
public void testAllFilesPresentInConstexpr() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/constexpr"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("compileTimeMember.kt")
public void testCompileTimeMember() throws Exception {
runTest("compiler/testData/diagnostics/tests/constexpr/compileTimeMember.kt");
}
@Test
@TestMetadata("constInitializer.kt")
public void testConstInitializer() throws Exception {
runTest("compiler/testData/diagnostics/tests/constexpr/constInitializer.kt");
}
@Test
@TestMetadata("nonCompileTimeInDeclaration.kt")
public void testNonCompileTimeInDeclaration() throws Exception {
runTest("compiler/testData/diagnostics/tests/constexpr/nonCompileTimeInDeclaration.kt");
}
@Test
@TestMetadata("nonConstInitializer.kt")
public void testNonConstInitializer() throws Exception {
runTest("compiler/testData/diagnostics/tests/constexpr/nonConstInitializer.kt");
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/constructorConsistency")
@TestDataPath("$PROJECT_ROOT")

View File

@@ -5,8 +5,9 @@
package org.jetbrains.kotlin.fir.analysis
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor
import org.jetbrains.kotlin.backend.jvm.serialization.SingleClassJvmIrProvider
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.collectors.FirDiagnosticsCollector
@@ -25,6 +26,8 @@ import org.jetbrains.kotlin.fir.resolve.providers.impl.FirProviderImpl
import org.jetbrains.kotlin.fir.resolve.transformers.FirTotalResolveProcessor
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmManglerDesc
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinder
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions
import java.io.File
@@ -82,16 +85,21 @@ class FirAnalyzerFacade(
return collectedDiagnostics!!
}
fun convertToIr(extensions: GeneratorExtensions): Fir2IrResult {
fun convertToIr(project: Project, symbolTable: SymbolTable, extensions: GeneratorExtensions): Fir2IrResult {
if (scopeSession == null) runResolution()
val signaturer = JvmIdSignatureDescriptor(JvmManglerDesc())
return Fir2IrConverter.createModuleFragment(
session, scopeSession!!, firFiles!!,
languageVersionSettings, signaturer,
extensions, FirJvmKotlinMangler(session), IrFactoryImpl,
languageVersionSettings, symbolTable,
extensions, FirJvmKotlinMangler(session), symbolTable.irFactory,
FirJvmVisibilityConverter,
Fir2IrJvmSpecialAnnotationSymbolProvider()
)
) { moduleDescriptor, irBuiltIns ->
SingleClassJvmIrProvider(
moduleDescriptor, irBuiltIns, symbolTable,
IrFactoryImpl,
VirtualFileFinder.SERVICE.getInstance(project, moduleDescriptor)
)
}
}
}

View File

@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrModuleFragmentImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.linkage.IrProvider
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.name.Name
@@ -247,15 +248,15 @@ class Fir2IrConverter(
scopeSession: ScopeSession,
firFiles: List<FirFile>,
languageVersionSettings: LanguageVersionSettings,
signaturer: IdSignatureComposer,
symbolTable: SymbolTable,
generatorExtensions: GeneratorExtensions,
mangler: FirMangler,
irFactory: IrFactory,
visibilityConverter: Fir2IrVisibilityConverter,
specialSymbolProvider: Fir2IrSpecialSymbolProvider?
specialSymbolProvider: Fir2IrSpecialSymbolProvider?,
deserializedIrProviderProvider: (FirModuleDescriptor, IrBuiltIns) -> IrProvider?,
): Fir2IrResult {
val moduleDescriptor = FirModuleDescriptor(session)
val symbolTable = SymbolTable(signaturer, irFactory)
val constantValueGenerator = ConstantValueGenerator(moduleDescriptor, symbolTable)
val typeTranslator = TypeTranslator(
symbolTable,
@@ -290,7 +291,8 @@ class Fir2IrConverter(
}
val irModuleFragment = IrModuleFragmentImpl(moduleDescriptor, irBuiltIns, irFiles)
val irProviders =
generateTypicalIrProviderList(irModuleFragment.descriptor, irBuiltIns, symbolTable, extensions = generatorExtensions)
listOfNotNull(deserializedIrProviderProvider(moduleDescriptor, irBuiltIns)) +
generateTypicalIrProviderList(irModuleFragment.descriptor, irBuiltIns, symbolTable, extensions = generatorExtensions)
val externalDependenciesGenerator = ExternalDependenciesGenerator(
symbolTable,
irProviders
@@ -321,7 +323,7 @@ class Fir2IrConverter(
evaluateConstants(irModuleFragment)
return Fir2IrResult(irModuleFragment, symbolTable, sourceManager, components)
return Fir2IrResult(irModuleFragment, sourceManager, components)
}
}
}

View File

@@ -9,4 +9,4 @@ import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.psi2ir.PsiSourceManager
data class Fir2IrResult(val irModuleFragment: IrModuleFragment, val symbolTable: SymbolTable, val sourceManager: PsiSourceManager, val components: Fir2IrComponents)
data class Fir2IrResult(val irModuleFragment: IrModuleFragment, val sourceManager: PsiSourceManager, val components: Fir2IrComponents)

View File

@@ -25,9 +25,13 @@ sealed class FirMetadataSource : MetadataSource {
else -> null
}
class File(override val fir: FirFile) : FirMetadataSource(), MetadataSource.File
class File(override val fir: FirFile) : FirMetadataSource(), MetadataSource.File {
override var serializedIr: ByteArray? = null
}
class Class(override val fir: FirClass<*>) : FirMetadataSource(), MetadataSource.Class
class Class(override val fir: FirClass<*>) : FirMetadataSource(), MetadataSource.Class {
override var serializedIr: ByteArray? = null
}
class Function(override val fir: FirFunction<*>) : FirMetadataSource(), MetadataSource.Function

View File

@@ -6,28 +6,23 @@
package org.jetbrains.kotlin.fir.backend.evaluate
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.interpreter.EvaluationMode
import org.jetbrains.kotlin.ir.interpreter.IrCompileTimeChecker
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode
import org.jetbrains.kotlin.ir.interpreter.checker.IrCompileTimeChecker
import org.jetbrains.kotlin.ir.interpreter.toIrConst
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
fun evaluateConstants(irModuleFragment: IrModuleFragment) {
val irConstTransformer = IrConstTransformer(irModuleFragment.irBuiltins)
irModuleFragment.files.forEach { it.transformChildren(irConstTransformer, null) }
val interpreter = IrInterpreter(irModuleFragment.irBuiltins)
irModuleFragment.files.forEach { it.transformChildren(IrConstTransformer(interpreter, it), null) }
}
//TODO create abstract class that will be common for this and lowering
class IrConstTransformer(irBuiltIns: IrBuiltIns) : IrElementTransformerVoid() {
private val interpreter = IrInterpreter(irBuiltIns)
class IrConstTransformer(private val interpreter: IrInterpreter, private val irFile: IrFile) : IrElementTransformerVoid() {
private fun IrExpression.replaceIfError(original: IrExpression): IrExpression {
return if (this !is IrErrorExpression) this else original
@@ -35,7 +30,7 @@ class IrConstTransformer(irBuiltIns: IrBuiltIns) : IrElementTransformerVoid() {
override fun visitCall(expression: IrCall): IrExpression {
if (expression.accept(IrCompileTimeChecker(mode = EvaluationMode.ONLY_BUILTINS), null)) {
return interpreter.interpret(expression).replaceIfError(expression)
return interpreter.interpret(expression, irFile).replaceIfError(expression)
}
return expression
}
@@ -48,7 +43,7 @@ class IrConstTransformer(irBuiltIns: IrBuiltIns) : IrElementTransformerVoid() {
if (expression is IrConst<*>) return declaration
val isConst = declaration.correspondingPropertySymbol?.owner?.isConst == true
if (isConst && expression.accept(IrCompileTimeChecker(declaration, mode = EvaluationMode.ONLY_BUILTINS), null)) {
initializer.expression = interpreter.interpret(expression).replaceIfError(expression)
initializer.expression = interpreter.interpret(expression, irFile).replaceIfError(expression)
}
return declaration
@@ -94,7 +89,7 @@ class IrConstTransformer(irBuiltIns: IrBuiltIns) : IrElementTransformerVoid() {
private fun IrExpression.transformSingleArg(expectedType: IrType): IrExpression {
if (this.accept(IrCompileTimeChecker(mode = EvaluationMode.ONLY_BUILTINS), null)) {
val const = interpreter.interpret(this).replaceIfError(this)
val const = interpreter.interpret(this, irFile).replaceIfError(this)
return const.convertToConstIfPossible(expectedType)
} else if (this is IrConstructorCall) {
transformAnnotation(this)

View File

@@ -294,6 +294,8 @@ public interface Errors {
DiagnosticFactory1<PsiElement, KotlinType> TYPE_CANT_BE_USED_FOR_CONST_VAL = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<PsiElement> CONST_VAL_WITHOUT_INITIALIZER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtExpression> CONST_VAL_WITH_NON_CONST_INITIALIZER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtExpression> NON_COMPILE_TIME_EXPRESSION_IN_COMPILE_TIME_DECLARATION = DiagnosticFactory0.create(ERROR);
DiagnosticFactory2<PsiElement, KtClassOrObject, KtNamedDeclaration> COMPILE_TIME_MEMBER_NOT_IMPLEMENTED = DiagnosticFactory2.create(ERROR);
DiagnosticFactory0<KtExpression> NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION = DiagnosticFactory0.create(ERROR);

View File

@@ -966,6 +966,8 @@ public class DefaultErrorMessages {
MAP.put(TYPE_CANT_BE_USED_FOR_CONST_VAL, "Const ''val'' has type ''{0}''. Only primitives and String are allowed", RENDER_TYPE);
MAP.put(CONST_VAL_WITHOUT_INITIALIZER, "Const 'val' should have an initializer");
MAP.put(CONST_VAL_WITH_NON_CONST_INITIALIZER, "Const 'val' initializer should be a constant value");
MAP.put(NON_COMPILE_TIME_EXPRESSION_IN_COMPILE_TIME_DECLARATION, "Compile time declaration must contains only compile time expressions");
MAP.put(COMPILE_TIME_MEMBER_NOT_IMPLEMENTED, "{0} is marked as compile time and method ''{1}'' must be also compile time", RENDER_CLASS_OR_OBJECT, DECLARATION_NAME);
MAP.put(NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION, "Only 'const val' can be used in constant expressions");
MAP.put(DEFAULT_VALUE_NOT_ALLOWED_IN_OVERRIDE, "An overriding function is not allowed to specify default values for its parameters");

View File

@@ -44,6 +44,7 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
SealedInheritorInSamePackageChecker,
SealedInheritorInSameModuleChecker,
SealedInterfaceAllowedChecker,
ConstexprDeclarationChecker
)
private val DEFAULT_CALL_CHECKERS = listOf(
@@ -58,7 +59,7 @@ private val DEFAULT_CALL_CHECKERS = listOf(
NamedFunAsExpressionChecker, ContractNotAllowedCallChecker, ReifiedTypeParameterSubstitutionChecker(),
MissingDependencySupertypeChecker.ForCalls, AbstractClassInstantiationChecker, SuspendConversionCallChecker,
UnitConversionCallChecker, FunInterfaceConstructorReferenceChecker, NullableExtensionOperatorWithSafeCallChecker,
ReferencingToUnderscoreNamedParameterOfCatchBlockChecker, VarargWrongExecutionOrderChecker
ReferencingToUnderscoreNamedParameterOfCatchBlockChecker, VarargWrongExecutionOrderChecker, ConstexprCallChecker
)
private val DEFAULT_TYPE_CHECKERS = emptyList<AdditionalTypeChecker>()
private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf(

View File

@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
import org.jetbrains.kotlin.descriptors.impl.VariableDescriptorWithInitializerImpl
@@ -127,7 +128,8 @@ class VariableTypeAndInitializerResolver(
val constant = constantExpressionEvaluator.evaluateExpression(initializer, trace, initializerType)
?: return@computeInitializer null
if (constant.usesNonConstValAsConstant && variableDescriptor.isConst) {
val supportsCompileTime = languageVersionSettings.supportsFeature(LanguageFeature.CompileTimeCalculations)
if (!supportsCompileTime && constant.usesNonConstValAsConstant && variableDescriptor.isConst) {
trace.report(Errors.NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION.on(initializer))
}

View File

@@ -0,0 +1,111 @@
/*
* 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.resolve.calls.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtPrimaryConstructor
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.isAnnotationConstructor
import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors
import org.jetbrains.kotlin.resolve.scopes.receivers.AbstractReceiverValue
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.AbbreviatedType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
object ConstexprCallChecker : CallChecker {
private val compileTimeAnnotationName = FqName("kotlin.CompileTimeCalculation")
private val compileTimeTypeAliases = setOf(
"java.lang.StringBuilder", "java.lang.IllegalArgumentException", "java.util.NoSuchElementException"
)
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
if (!context.languageVersionSettings.supportsFeature(LanguageFeature.CompileTimeCalculations)) return
if (resolvedCall.call.callElement is KtAnnotationEntry || resolvedCall.candidateDescriptor.isAnnotationConstructor()) return
if (hasEnclosingIntrinsicDeclaration(context)) return
val isConst = (context.scope.ownerDescriptor as? PropertyDescriptor)?.isConst == true
val isInsideCompileTimeFun = hasEnclosingConstDeclaration(context)
if (!isConst && !isInsideCompileTimeFun) return
val isCompileTime = isCompileTime(resolvedCall.resultingDescriptor, context) || isCompileTimeTypeAlias(resolvedCall)
if (isConst && !isCompileTime) {
context.trace.report(Errors.CONST_VAL_WITH_NON_CONST_INITIALIZER.on(resolvedCall.call.calleeExpression!!))
} else if (isInsideCompileTimeFun && !isCompileTime) {
context.trace.report(Errors.NON_COMPILE_TIME_EXPRESSION_IN_COMPILE_TIME_DECLARATION.on(resolvedCall.call.calleeExpression!!))
}
}
private fun isCompileTimeTypeAlias(resolvedCall: ResolvedCall<*>): Boolean {
if (!resolvedCall.resultingDescriptor.containingDeclaration.fqNameSafe.startsWith(Name.identifier("java"))) return false
val type = (resolvedCall.dispatchReceiver as AbstractReceiverValue).type
if (type !is AbbreviatedType) return false
return type.abbreviation.constructor.declarationDescriptor?.isMarkedAsCompileTime() ?: false
}
private fun isCompileTime(descriptor: CallableDescriptor, context: CallCheckerContext): Boolean {
return when (descriptor) {
is TypeAliasConstructorDescriptor -> descriptor.typeAliasDescriptor.isMarkedAsCompileTime()
is PropertyAccessorDescriptor ->
isCompileTime(descriptor.correspondingProperty, context) && (descriptor.isMarkedAsCompileTime() || descriptor.isDefault)
is FunctionDescriptor ->
descriptor.isMarkedAsCompileTime() || descriptor.isSpecial() || descriptor.overriddenDescriptors.any { it.isMarkedAsCompileTime() }
is PropertyDescriptor ->
descriptor.isMarkedAsCompileTime() || descriptor.isConst || descriptor.hasCompileTimePrimaryConstructor(context.trace.bindingContext)
is ValueParameterDescriptor, is ReceiverParameterDescriptor, is VariableDescriptor ->
context.scope.ownerDescriptor.isMarkedAsCompileTime()
else -> false
}
}
private fun DeclarationDescriptor.isMarkedWith(annotation: FqName): Boolean {
if (this.annotations.hasAnnotation(annotation)) return true
if (this is FunctionInvokeDescriptor) return true
if (this is ClassDescriptor && this.fqNameSafe.asString() in compileTimeTypeAliases) return true
if (this is ClassDescriptor && this.isCompanionObject) return false
if (this is AnonymousFunctionDescriptor) return this.containingDeclaration.isMarkedWith(annotation)
return (this.containingDeclaration as? ClassDescriptor)?.isMarkedWith(annotation) ?: false
}
private fun DeclarationDescriptor.isMarkedAsCompileTime(): Boolean {
return this.isMarkedWith(compileTimeAnnotationName) || this.safeAs<PropertyDescriptor>()?.isConst == true
}
private fun PropertyDescriptor.hasCompileTimePrimaryConstructor(bindingContext: BindingContext): Boolean {
val property = this.findTopMostOverriddenDescriptors().first()
val ktParameter = property.source.getPsi() as? KtParameter ?: return false
val primaryConstructor = ktParameter.ownerFunction as? KtPrimaryConstructor ?: return false
val classOwner = primaryConstructor.getContainingClassOrObject()
val annotations = primaryConstructor.annotationEntries.mapNotNull { bindingContext[BindingContext.ANNOTATION, it] } +
classOwner.annotationEntries.mapNotNull { bindingContext[BindingContext.ANNOTATION, it] }
return annotations.any { it.fqName == compileTimeAnnotationName }
}
private fun hasEnclosingConstDeclaration(context: CallCheckerContext): Boolean {
return context.scope.ownerDescriptor.isMarkedAsCompileTime()
}
private fun hasEnclosingIntrinsicDeclaration(context: CallCheckerContext): Boolean {
return context.scope.ownerDescriptor.isMarkedWith(FqName("kotlin.EvaluateIntrinsic"))
}
// TODO add annotation to special functions?
private fun CallableDescriptor.isSpecial(): Boolean {
return this.name.asString().contains("SPECIAL-FUNCTION")
}
}

View File

@@ -17,6 +17,9 @@
package org.jetbrains.kotlin.resolve.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
@@ -31,7 +34,7 @@ object ConstModifierChecker : DeclarationChecker {
val constModifierPsiElement = declaration.modifierList!!.getModifier(KtTokens.CONST_KEYWORD)!!
val diagnostic = checkCanBeConst(declaration, constModifierPsiElement, descriptor).diagnostic
val diagnostic = checkCanBeConst(declaration, constModifierPsiElement, descriptor, context.languageVersionSettings).diagnostic
if (diagnostic != null) {
context.trace.report(diagnostic)
}
@@ -43,12 +46,14 @@ object ConstModifierChecker : DeclarationChecker {
private fun checkCanBeConst(
declaration: KtDeclaration,
constModifierPsiElement: PsiElement,
descriptor: VariableDescriptor
descriptor: VariableDescriptor,
languageSettings: LanguageVersionSettings = LanguageVersionSettingsImpl.DEFAULT
): ConstApplicability {
if (descriptor.isVar) {
return Errors.WRONG_MODIFIER_TARGET.on(constModifierPsiElement, KtTokens.CONST_KEYWORD, "vars").nonApplicable()
}
// TODO remove the following check after introducing constexpr modifier
val containingDeclaration = descriptor.containingDeclaration
if (containingDeclaration is ClassDescriptor && containingDeclaration.kind != ClassKind.OBJECT) {
return Errors.CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT.on(constModifierPsiElement).nonApplicable()
@@ -76,7 +81,7 @@ object ConstModifierChecker : DeclarationChecker {
return Errors.CONST_VAL_WITHOUT_INITIALIZER.on(constModifierPsiElement).nonApplicable()
}
if (descriptor.compileTimeInitializer == null) {
if (!languageSettings.supportsFeature(LanguageFeature.CompileTimeCalculations) && descriptor.compileTimeInitializer == null) {
return Errors.CONST_VAL_WITH_NON_CONST_INITIALIZER.on(declaration.initializer!!).nonApplicable()
}
@@ -89,4 +94,4 @@ sealed class ConstApplicability(val canBeConst: Boolean, val diagnostic: Diagnos
class NonApplicable(diagnostic: Diagnostic? = null) : ConstApplicability(false, diagnostic)
}
private fun Diagnostic.nonApplicable() = ConstApplicability.NonApplicable(this)
private fun Diagnostic.nonApplicable() = ConstApplicability.NonApplicable(this)

View File

@@ -0,0 +1,47 @@
/*
* 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.resolve.checkers
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClass
import org.jetbrains.kotlin.resolve.BindingContext
object ConstexprDeclarationChecker : DeclarationChecker {
private val compileTimeAnnotationName = FqName("kotlin.CompileTimeCalculation")
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (!context.languageVersionSettings.supportsFeature(LanguageFeature.CompileTimeCalculations)) return
if (!descriptor.annotations.hasAnnotation(compileTimeAnnotationName)) return
val ktClass = when (declaration) {
is KtClass -> declaration
is KtPrimaryConstructor -> declaration.getContainingClassOrObject()
else -> return
}
val overriddenDeclaration = ktClass.declarations
.filterIsInstance<KtNamedDeclaration>()
.filter { it.hasModifier(KtTokens.OVERRIDE_KEYWORD) }
for (ktDeclaration in overriddenDeclaration) {
val annotationEntries = ktDeclaration.annotationEntries
val classAnnotationEntries = ktDeclaration.containingClass()?.annotationEntries
fun List<KtAnnotationEntry>?.containsCompileTimeAnnotation(): Boolean {
this ?: return false
return this.any { context.trace.bindingContext[BindingContext.ANNOTATION, it]?.fqName == compileTimeAnnotationName }
}
if (!annotationEntries.containsCompileTimeAnnotation() && !classAnnotationEntries.containsCompileTimeAnnotation()) {
context.trace.report(Errors.COMPILE_TIME_MEMBER_NOT_IMPLEMENTED.on(ktDeclaration, ktClass, ktDeclaration))
}
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2010-2019 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.backend.common.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.ir.interpreter.checker.IrCompileTimeChecker
import org.jetbrains.kotlin.ir.interpreter.*
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.copyTypeAndValueArgumentsFrom
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
class CompileTimeCalculationLowering(val context: CommonBackendContext) : FileLoweringPass {
private val isTest = context.configuration[CommonConfigurationKeys.MODULE_NAME] == "<test-module>"
private val interpreter = IrInterpreter(context.ir.irModule)
override fun lower(irFile: IrFile) {
if (!context.configuration.languageVersionSettings.supportsFeature(LanguageFeature.CompileTimeCalculations)) return
if (irFile.fileEntry.name.contains("/kotlin/libraries/")) return
irFile.transformChildren(Transformer(irFile), null)
}
private inner class Transformer(private val irFile: IrFile) : IrElementTransformerVoid() {
private fun IrExpression.report(original: IrExpression): IrExpression {
if (this == original) return this
val isError = this is IrErrorExpression && isTest
val message = when (this) {
is IrConst<*> -> this.value.toString()
is IrErrorExpression -> this.description
else -> TODO("unsupported type ${this::class.java}")
}
context.report(original, irFile, message, isError)
return if (this !is IrErrorExpression) this else original
}
override fun visitCall(expression: IrCall): IrExpression {
expression.symbol.owner.valueParameters
.forEachIndexed { index, parameter ->
if (expression.getValueArgument(index) != null || !expression.symbol.owner.isInline) return@forEachIndexed
val default = parameter.defaultValue?.expression as? IrCall ?: return@forEachIndexed
val withNewOffsets = IrCallImpl(
expression.startOffset, expression.endOffset, default.type, default.symbol,
default.typeArgumentsCount, default.valueArgumentsCount, default.origin, default.superQualifierSymbol
)
withNewOffsets.copyTypeAndValueArgumentsFrom(default)
if (withNewOffsets.accept(IrCompileTimeChecker(), null)) {
interpreter.interpret(withNewOffsets, irFile)
.report(withNewOffsets)
.takeIf { it != withNewOffsets }
?.apply { expression.putArgument(parameter, this) }
}
}
if (expression.accept(IrCompileTimeChecker(), null)) {
return interpreter.interpret(expression, irFile).report(expression)
}
return super.visitCall(expression)
}
override fun visitField(declaration: IrField): IrStatement {
val initializer = declaration.initializer
val expression = initializer?.expression ?: return declaration
if (expression is IrConst<*>) return declaration
// must pass declaration symbol as initial containing declaration because of complex constructions such as try block or safe call
val isCompileTimeComputable = expression.accept(IrCompileTimeChecker(declaration), null)
val isConst = declaration.correspondingPropertySymbol?.owner?.isConst == true
if (isConst && !isCompileTimeComputable) {
context.report(expression, irFile, "Const property is used only with functions annotated as CompileTimeCalculation", true)
} else if (isCompileTimeComputable) {
initializer.expression = interpreter.interpret(expression, irFile).report(expression)
}
return declaration
}
//todo annotation call
}
}

View File

@@ -25,7 +25,8 @@ internal fun IrFactory.buildClass(builder: IrClassBuilder): IrClass = with(build
startOffset, endOffset, origin,
IrClassSymbolImpl(),
name, kind, visibility, modality,
isCompanion, isInner, isData, isExternal, isInline, isExpect, isFun
isCompanion, isInner, isData, isExternal, isInline, isExpect, isFun,
source
)
}

View File

@@ -16,6 +16,7 @@ dependencies {
compile(project(":compiler:ir.tree.impl"))
compile(project(":js:js.ast"))
compile(project(":js:js.frontend"))
compile(project(":compiler:cli"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
}

View File

@@ -9,6 +9,8 @@ import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.backend.common.ir.Ir
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
@@ -23,6 +25,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.DescriptorlessExternalPackageFragmentSymbol
import org.jetbrains.kotlin.ir.types.*
@@ -33,6 +36,12 @@ import org.jetbrains.kotlin.js.config.ErrorTolerancePolicy
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtCallElement
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
import org.jetbrains.kotlin.psi2ir.PsiSourceManager
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.Variance
@@ -362,6 +371,21 @@ class JsIrBackendContext(
override fun report(element: IrElement?, irFile: IrFile?, message: String, isError: Boolean) {
/*TODO*/
val psiFileEntry = irFile?.fileEntry as? PsiSourceManager.PsiFileEntry
val elementInfo = when {
psiFileEntry != null && element != null -> {
var psi = psiFileEntry.findPsiElement(element)
while (psi?.parent !is KtProperty && psi?.parent !is KtBlockExpression && psi?.parent is KtExpression) psi = psi.parent
val prefixIfDefault = (psi as? KtCallElement)?.getCallNameExpression()?.getReferencedName()
?.let { if (element is IrCall && it != element.symbol.owner.name.asString()) " (it's default argument)" else "" } ?: ""
"${psi?.text}$prefixIfDefault at ${psiFileEntry.getLineNumber(element.startOffset) + 1} line"
}
else -> ""
}
val messageCollector = configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY]!!
val severity = if (isError) CompilerMessageSeverity.EXCEPTION else CompilerMessageSeverity.INFO
val textInTheMiddle = if (isError) "will produce exception:" else "will be replaced on:"
messageCollector.report(severity, "$elementInfo $textInTheMiddle $message")
print(message)
}

View File

@@ -709,6 +709,13 @@ private val captureStackTraceInThrowablesPhase = makeBodyLoweringPhase(
description = "Capture stack trace in Throwable constructors"
)
val compileTimeEvaluationPhase = makeJsModulePhase(
::CompileTimeCalculationLowering,
name = "CompileTimeEvaluation",
//TODO change annotation to modifier
description = "Evaluate calls that are marked with @CompileTimeCalculation annotation",
).toModuleLowering()
private val cleanupLoweringPhase = makeBodyLoweringPhase(
{ CleanupLowering() },
name = "CleanupLowering",
@@ -719,6 +726,7 @@ private val loweringList = listOf<Lowering>(
scriptRemoveReceiverLowering,
validateIrBeforeLowering,
expectDeclarationsRemovingPhase,
compileTimeEvaluationPhase,
stripTypeAliasDeclarationsPhase,
jsCodeOutliningPhase,
arrayConstructorPhase,

View File

@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.common.Mapping
import org.jetbrains.kotlin.backend.common.ir.Ir
import org.jetbrains.kotlin.backend.common.lower.irThrow
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.serialization.DeclarationTable
import org.jetbrains.kotlin.backend.jvm.codegen.*
import org.jetbrains.kotlin.backend.jvm.descriptors.JvmSharedVariablesManager
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
@@ -19,9 +20,11 @@ import org.jetbrains.kotlin.backend.jvm.lower.CollectionStubComputer
import org.jetbrains.kotlin.backend.jvm.lower.JvmInnerClassesSupport
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.MemoizedInlineClassReplacements
import org.jetbrains.kotlin.backend.jvm.serialization.JvmGlobalDeclarationTable
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.diagnostics.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.builders.irBlock
@@ -29,12 +32,15 @@ import org.jetbrains.kotlin.ir.builders.irNull
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
import org.jetbrains.kotlin.psi2ir.PsiErrorBuilder
import org.jetbrains.kotlin.psi2ir.PsiSourceManager
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
@@ -144,6 +150,22 @@ class JvmBackendContext(
override fun report(element: IrElement?, irFile: IrFile?, message: String, isError: Boolean) {
/*TODO*/
val psiFileEntry = irFile?.fileEntry as? PsiSourceManager.PsiFileEntry
val elementInfo = when {
psiFileEntry != null && element != null -> {
var psi = psiFileEntry.findPsiElement(element)
while (psi?.parent !is KtProperty && psi?.parent !is KtBlockExpression && psi?.parent is KtExpression) psi = psi.parent
val prefixIfDefault = (psi as? KtCallElement)?.getCallNameExpression()?.getReferencedName()
?.let { if (element is IrCall && it != element.symbol.owner.name.asString()) " (it's default argument)" else "" } ?: ""
"${psi?.text}$prefixIfDefault at ${psiFileEntry.getLineNumber(element.startOffset) + 1} line"
}
else -> ""
}
//val messageCollector = configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY]!!
val severity = if (isError) "EXCEPTION" else "INFO"
val textInTheMiddle = if (isError) "will produce exception:" else "will be replaced on:"
//messageCollector.report(severity, "$elementInfo $textInTheMiddle $message")
psiErrorBuilder.at(element!!, irFile!!).report(Errors.NEW_INFERENCE_DIAGNOSTIC, "$severity: $elementInfo $textInTheMiddle $message")
print(message)
}
@@ -213,4 +235,6 @@ class JvmBackendContext(
override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true
}
val declarationTable = DeclarationTable(JvmGlobalDeclarationTable())
}

View File

@@ -7,11 +7,13 @@ package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.backend.common.ir.createParameterDeclarations
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureSerializer
import org.jetbrains.kotlin.codegen.SamType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmManglerIr
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
import org.jetbrains.kotlin.ir.declarations.*
@@ -21,6 +23,7 @@ import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.symbols.impl.DescriptorlessExternalPackageFragmentSymbol
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
@@ -49,9 +52,11 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations
class JvmGeneratorExtensions(private val generateFacades: Boolean = true) : GeneratorExtensions() {
class JvmGeneratorExtensions(symbolTable: SymbolTable, private val generateFacades: Boolean = true) : GeneratorExtensions() {
val classNameOverride = mutableMapOf<IrClass, JvmClassName>()
private val signaturer = IdSignatureSerializer(JvmManglerIr)
override val samConversion: SamConversion
get() = JvmSamConversion
@@ -72,7 +77,7 @@ class JvmGeneratorExtensions(private val generateFacades: Boolean = true) : Gene
else
null
override fun computeExternalDeclarationOrigin(descriptor: DeclarationDescriptor): IrDeclarationOrigin? =
override fun computeExternalDeclarationOrigin(descriptor: DeclarationDescriptor): IrDeclarationOrigin =
if (descriptor is JavaCallableMemberDescriptor || descriptor is JavaClassDescriptor)
IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB
else
@@ -171,14 +176,20 @@ class JvmGeneratorExtensions(private val generateFacades: Boolean = true) : Gene
private val enhancedNullabilityAnnotationClass =
createSpecialAnnotationClass(ENHANCED_NULLABILITY_ANNOTATION_FQ_NAME, kotlinJvmInternalPackage)
override val flexibleNullabilityAnnotationConstructor: IrConstructor? =
flexibleNullabilityAnnotationClass.constructors.single()
override val flexibleNullabilityAnnotationConstructor: IrConstructor =
flexibleNullabilityAnnotationClass.constructors.single().also {
symbolTable.storeConstructorBySignature(signaturer.composeSignatureForDeclaration(it), it)
}
override val enhancedNullabilityAnnotationConstructor: IrConstructor? =
enhancedNullabilityAnnotationClass.constructors.single()
override val enhancedNullabilityAnnotationConstructor: IrConstructor =
enhancedNullabilityAnnotationClass.constructors.single().also {
symbolTable.storeConstructorBySignature(signaturer.composeSignatureForDeclaration(it), it)
}
override val rawTypeAnnotationConstructor: IrConstructor? =
rawTypeAnnotationClass.constructors.single()
override val rawTypeAnnotationConstructor: IrConstructor =
rawTypeAnnotationClass.constructors.single().also {
symbolTable.storeConstructorBySignature(signaturer.composeSignatureForDeclaration(it), it)
}
companion object {
val FLEXIBLE_NULLABILITY_ANNOTATION_FQ_NAME =

View File

@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen
import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor
import org.jetbrains.kotlin.backend.jvm.serialization.SingleClassJvmIrProvider
import org.jetbrains.kotlin.codegen.CodegenFactory
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.JVMConfigurationKeys
@@ -26,12 +27,14 @@ import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmManglerDesc
import org.jetbrains.kotlin.ir.builders.TranslationPluginContext
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.MetadataSource
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.descriptors.IrFunctionFactory
import org.jetbrains.kotlin.ir.linkage.IrProvider
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinder
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
@@ -59,10 +62,10 @@ class JvmIrCodegenFactory(private val phaseConfig: PhaseConfig) : CodegenFactory
@JvmOverloads
fun convertToIr(state: GenerationState, files: Collection<KtFile>, ignoreErrors: Boolean = false): JvmIrBackendInput {
val extensions = JvmGeneratorExtensions()
val mangler = JvmManglerDesc(MainFunctionDetector(state.bindingContext, state.languageVersionSettings))
val psi2ir = Psi2IrTranslator(state.languageVersionSettings, Psi2IrConfiguration(ignoreErrors))
val symbolTable = SymbolTable(JvmIdSignatureDescriptor(mangler), IrFactoryImpl, JvmNameProvider)
val extensions = JvmGeneratorExtensions(symbolTable)
val messageLogger = state.configuration[IrMessageLogger.IR_MESSAGE_LOGGER] ?: IrMessageLogger.None
val psi2irContext = psi2ir.createGeneratorContext(state.module, state.bindingContext, symbolTable, extensions)
val pluginExtensions = IrGenerationExtension.getInstances(state.project)
@@ -135,7 +138,13 @@ class JvmIrCodegenFactory(private val phaseConfig: PhaseConfig) : CodegenFactory
}
irLinker.deserializeIrModuleHeader(it, kotlinLibrary)
}
val irProviders = listOf(irLinker)
val irProviders = listOf(
SingleClassJvmIrProvider(
psi2irContext.moduleDescriptor, psi2irContext.irBuiltIns, symbolTable, psi2irContext.irFactory,
VirtualFileFinder.SERVICE.getInstance(state.project, psi2irContext.moduleDescriptor)
),
irLinker
)
val irModuleFragment = psi2ir.generateModuleFragment(psi2irContext, files, irProviders, pluginExtensions, expectDescriptorToSymbol = null)
irLinker.postProcess()
@@ -183,6 +192,24 @@ class JvmIrCodegenFactory(private val phaseConfig: PhaseConfig) : CodegenFactory
/* JvmBackendContext creates new unbound symbols, have to resolve them. */
ExternalDependenciesGenerator(symbolTable, irProviders).generateUnboundSymbolsAsDependencies()
if (state.configuration.getBoolean(JVMConfigurationKeys.SERIALIZE_IR)) {
val logger = state.configuration[IrMessageLogger.IR_MESSAGE_LOGGER]
fun log(str: String) = logger?.report(IrMessageLogger.Severity.WARNING, str, location = null)
log("in serialize")
log(irModuleFragment.files.size.toString())
for (irFile in irModuleFragment.files) {
(irFile.metadata as? MetadataSource.File)?.serializedIr = serializeIrFile(context, irFile)
log("ifFile ${irFile.fileEntry.name} is serialized ${(irFile.metadata as? MetadataSource.File)?.serializedIr != null}")
for (irClass in irFile.declarations.filterIsInstance<IrClass>()) {
(irClass.metadata as? MetadataSource.Class)?.serializedIr = serializeTopLevelIrClass(context, irClass)
log("irClass ${irClass.name} is serialized ${(irClass.metadata as? MetadataSource.Class)?.serializedIr != null}")
}
}
}
state.mapInlineClass = { descriptor ->
context.typeMapper.mapType(context.referenceClass(descriptor).defaultType)
}
@@ -227,13 +254,13 @@ class JvmIrCodegenFactory(private val phaseConfig: PhaseConfig) : CodegenFactory
backendExtension: JvmBackendExtension,
notifyCodegenStart: () -> Unit
) {
val irProviders = configureBuiltInsAndgenerateIrProvidersInFrontendIRMode(irModuleFragment, symbolTable, extensions)
val irProviders = configureBuiltInsAndGenerateIrProvidersInFrontendIRMode(irModuleFragment, symbolTable, extensions)
doGenerateFilesInternal(
JvmIrBackendInput(state, irModuleFragment, symbolTable, sourceManager, phaseConfig, irProviders, extensions, backendExtension, notifyCodegenStart)
)
}
fun configureBuiltInsAndgenerateIrProvidersInFrontendIRMode(
fun configureBuiltInsAndGenerateIrProvidersInFrontendIRMode(
irModuleFragment: IrModuleFragment,
symbolTable: SymbolTable,
extensions: JvmGeneratorExtensions

View File

@@ -273,6 +273,14 @@ private val kotlinNothingValueExceptionPhase = makeIrFilePhase<CommonBackendCont
description = "Throw proper exception for calls returning value of type 'kotlin.Nothing'"
)
val compileTimeEvaluationPhase = makeIrModulePhase(
::CompileTimeCalculationLowering,
name = "CompileTimeEvaluation",
//TODO change annotation to modifier
description = "Evaluate calls that are marked with @CompileTimeCalculation annotation",
prerequisite = setOf(expectDeclarationsRemovingPhase)
)
private val jvmFilePhases = listOf(
typeAliasAnnotationMethodsPhase,
stripTypeAliasDeclarationsPhase,
@@ -387,6 +395,7 @@ val jvmPhases = NamedCompilerPhase(
lower = validateIrBeforeLowering then
processOptionalAnnotationsPhase then
expectDeclarationsRemovingPhase then
compileTimeEvaluationPhase then
scriptsToClassesPhase then
fileClassPhase then
jvmStaticInObjectPhase then

View File

@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.metadata.jvm.deserialization.BitEncoding
import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.protobuf.MessageLite
@@ -209,6 +210,11 @@ class ClassCodegen private constructor(
entry is MultifileFacadeFileEntry -> KotlinClassHeader.Kind.MULTIFILE_CLASS
else -> KotlinClassHeader.Kind.SYNTHETIC_CLASS
}
val serializedIr = when (metadata) {
is MetadataSource.Class -> metadata.serializedIr
is MetadataSource.File -> metadata.serializedIr
else -> null
}
val isMultifileClassOrPart = kind == KotlinClassHeader.Kind.MULTIFILE_CLASS || kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART
@@ -217,15 +223,15 @@ class ClassCodegen private constructor(
extraFlags = extraFlags or JvmAnnotationNames.METADATA_MULTIFILE_PARTS_INHERIT_FLAG
}
writeKotlinMetadata(visitor, state, kind, extraFlags) {
writeKotlinMetadata(visitor, state, kind, extraFlags) { av ->
if (metadata != null) {
metadataSerializer.serialize(metadata)?.let { (proto, stringTable) ->
DescriptorAsmUtil.writeAnnotationData(it, proto, stringTable)
DescriptorAsmUtil.writeAnnotationData(av, proto, stringTable)
}
}
if (entry is MultifileFacadeFileEntry) {
val arv = it.visitArray(JvmAnnotationNames.METADATA_DATA_FIELD_NAME)
val arv = av.visitArray(JvmAnnotationNames.METADATA_DATA_FIELD_NAME)
for (partFile in entry.partFiles) {
val fileClass = partFile.declarations.singleOrNull { it.isFileClass } as IrClass?
if (fileClass != null) arv.visit(null, typeMapper.mapClass(fileClass).internalName)
@@ -234,14 +240,16 @@ class ClassCodegen private constructor(
}
if (facadeClassName != null) {
it.visit(JvmAnnotationNames.METADATA_MULTIFILE_CLASS_NAME_FIELD_NAME, facadeClassName.internalName)
av.visit(JvmAnnotationNames.METADATA_MULTIFILE_CLASS_NAME_FIELD_NAME, facadeClassName.internalName)
}
if (irClass in context.classNameOverride) {
val isFileClass = isMultifileClassOrPart || kind == KotlinClassHeader.Kind.FILE_FACADE
assert(isFileClass) { "JvmPackageName is not supported for classes: ${irClass.render()}" }
it.visit(JvmAnnotationNames.METADATA_PACKAGE_NAME_FIELD_NAME, irClass.fqNameWhenAvailable!!.parent().asString())
av.visit(JvmAnnotationNames.METADATA_PACKAGE_NAME_FIELD_NAME, irClass.fqNameWhenAvailable!!.parent().asString())
}
serializedIr?.let { storeSerializedIr(av, it) }
}
}
@@ -536,3 +544,12 @@ private val Modality.flags: Int
private val DescriptorVisibility.flags: Int
get() = DescriptorAsmUtil.getVisibilityAccessFlag(this) ?: throw AssertionError("Unsupported visibility $this")
private fun storeSerializedIr(av: AnnotationVisitor, serializedIr: ByteArray) {
val serializedIrParts = BitEncoding.encodeBytes(serializedIr)
val partsVisitor = av.visitArray(JvmAnnotationNames.METADATA_SERIALIZED_IR_FIELD_NAME)
for (part in serializedIrParts) {
partsVisitor.visit(null, part)
}
partsVisitor.visitEnd()
}

View File

@@ -383,7 +383,7 @@ fun collectVisibleTypeParameters(scopeOwner: IrTypeParametersContainer): Set<IrT
// See `TypeTranslator.translateTypeAnnotations`.
private fun JvmBackendContext.makeRawTypeAnnotation() =
IrConstructorCallImpl.fromSymbolOwner(
generatorExtensions.rawTypeAnnotationConstructor!!.constructedClassType,
generatorExtensions.rawTypeAnnotationConstructor.constructedClassType,
generatorExtensions.rawTypeAnnotationConstructor.symbol
)

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.backend.jvm.serialization.JvmIrSerializer
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.util.IrMessageLogger
fun serializeIrFile(context: JvmBackendContext, irFile: IrFile): ByteArray {
return makeSerializer(context).serializeJvmIrFile(irFile).toByteArray()
}
fun serializeTopLevelIrClass(context: JvmBackendContext, irClass: IrClass): ByteArray {
assert(irClass.parent is IrFile)
return makeSerializer(context).serializeTopLevelClass(irClass).toByteArray()
}
private fun makeSerializer(context: JvmBackendContext) =
JvmIrSerializer(
context.configuration.get(IrMessageLogger.IR_MESSAGE_LOGGER) ?: IrMessageLogger.None,
context.declarationTable,
mutableMapOf(),
context.psiSourceManager,
externallyVisibleOnly = false
)

View File

@@ -1,10 +1,13 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compile(project(":compiler:ir.tree"))
compileOnly(project(":compiler:ir.tree"))
compileOnly(project(":kotlin-reflect-api"))
}
sourceSets {
@@ -12,3 +15,7 @@ sourceSets {
"test" {}
}
val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
freeCompilerArgs = listOf("-Xinline-classes")
}

View File

@@ -0,0 +1,224 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.interpreter
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.interpreter.builtins.evaluateIntrinsicAnnotation
import org.jetbrains.kotlin.ir.interpreter.builtins.interpretBinaryFunction
import org.jetbrains.kotlin.ir.interpreter.builtins.interpretTernaryFunction
import org.jetbrains.kotlin.ir.interpreter.builtins.interpretUnaryFunction
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterError
import org.jetbrains.kotlin.ir.interpreter.intrinsics.IntrinsicEvaluator
import org.jetbrains.kotlin.ir.interpreter.proxy.wrap
import org.jetbrains.kotlin.ir.interpreter.stack.CallStack
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.*
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KFunctionState
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KTypeState
import org.jetbrains.kotlin.ir.interpreter.state.reflection.ReflectionState
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import java.lang.invoke.MethodHandle
internal interface CallInterceptor {
val environment: IrInterpreterEnvironment
val irBuiltIns: IrBuiltIns
val interpreter: IrInterpreter
fun interceptProxy(irFunction: IrFunction, valueArguments: List<Variable>, expectedResultClass: Class<*> = Any::class.java): Any?
fun interceptCall(call: IrCall, irFunction: IrFunction, receiver: State?, args: List<State>)
fun interceptConstructor(constructorCall: IrFunctionAccessExpression, receiver: State, args: List<State>)
fun interceptGetObjectValue(expression: IrGetObjectValue)
fun interceptEnumEntry(enumEntry: IrEnumEntry)
fun interceptJavaStaticField(expression: IrGetField)
}
internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) : CallInterceptor {
override val environment: IrInterpreterEnvironment = interpreter.environment
private val callStack: CallStack = environment.callStack
override val irBuiltIns: IrBuiltIns = environment.irBuiltIns
private val bodyMap: Map<IdSignature, IrBody> = emptyMap()//interpreter.bodyMap
override fun interceptProxy(irFunction: IrFunction, valueArguments: List<Variable>, expectedResultClass: Class<*>): Any? {
return interpreter.withNewCallStack(irFunction) {
this@withNewCallStack.environment.callStack.addInstruction(CompoundInstruction(irFunction))
valueArguments.forEach { this@withNewCallStack.environment.callStack.addVariable(it) }
}.wrap(this@DefaultCallInterceptor, expectedResultClass)
}
override fun interceptCall(call: IrCall, irFunction: IrFunction, receiver: State?, args: List<State>) {
val isInlineOnly = irFunction.hasAnnotation(FqName("kotlin.internal.InlineOnly"))
val originalCallName = call.symbol.owner.name.asString()
when {
receiver is Wrapper && !isInlineOnly -> receiver.getMethod(irFunction).invokeMethod(irFunction, args)
irFunction.hasAnnotation(evaluateIntrinsicAnnotation) -> Wrapper.getStaticMethod(irFunction).invokeMethod(irFunction, args)
receiver is KFunctionState && originalCallName == "invoke" -> callStack.addInstruction(CompoundInstruction(irFunction))
receiver is ReflectionState -> Wrapper.getReflectionMethod(irFunction).invokeMethod(irFunction, args)
receiver is Primitive<*> -> calculateBuiltIns(irFunction, args) // check for js char, js long and get field for primitives
irFunction.body is IrSyntheticBody -> handleIntrinsicMethods(irFunction)
irFunction.body == null ->
irFunction.trySubstituteFunctionBody() ?: irFunction.tryCalculateLazyConst() ?: calculateBuiltIns(irFunction, args)
else -> callStack.addInstruction(CompoundInstruction(irFunction))
}
}
override fun interceptConstructor(constructorCall: IrFunctionAccessExpression, receiver: State, args: List<State>) {
val constructor = constructorCall.symbol.owner
val irClass = constructor.parentAsClass
when {
irClass.hasAnnotation(evaluateIntrinsicAnnotation) || irClass.fqNameWhenAvailable!!.startsWith(Name.identifier("java")) -> {
Wrapper.getConstructorMethod(constructor).invokeMethod(constructor, args)
if (constructorCall !is IrConstructorCall) (receiver as Common).superWrapperClass = callStack.popState() as Wrapper
}
irClass.defaultType.isArray() || irClass.defaultType.isPrimitiveArray() -> {
// array constructor doesn't have body so must be treated separately
callStack.addVariable(Variable(constructor.symbol, KTypeState(constructorCall.type, irBuiltIns.anyClass.owner)))
handleIntrinsicMethods(constructor)
}
else -> {
callStack.pushState(receiver)
callStack.addInstruction(CompoundInstruction(constructor))
}
}
}
override fun interceptGetObjectValue(expression: IrGetObjectValue) {
val objectClass = expression.symbol.owner
if (objectClass.hasAnnotation(evaluateIntrinsicAnnotation)) {
val result = Wrapper.getCompanionObject(objectClass)
environment.mapOfObjects[expression.symbol] = result
callStack.pushState(result)
}
}
override fun interceptEnumEntry(enumEntry: IrEnumEntry) {
val enumClass = enumEntry.symbol.owner.parentAsClass
when {
enumClass.hasAnnotation(evaluateIntrinsicAnnotation) -> {
val enumEntryName = enumEntry.name.asString().toState(environment.irBuiltIns.stringType)
val valueOfFun = enumClass.declarations.single { it.nameForIrSerialization.asString() == "valueOf" } as IrFunction
Wrapper.getEnumEntry(enumClass)!!.invokeMethod(valueOfFun, listOf(enumEntryName))
environment.mapOfEnums[enumEntry.symbol] = callStack.popState() as Complex
}
else -> {
val enumSuperCall = (enumClass.primaryConstructor?.body?.statements?.firstOrNull() as? IrEnumConstructorCall)
enumSuperCall?.apply { (0 until this.valueArgumentsCount).forEach { putValueArgument(it, null) } } // restore to null
callStack.dropSubFrame()
}
}
}
override fun interceptJavaStaticField(expression: IrGetField) {
val field = expression.symbol.owner
assert(field.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB && field.isStatic)
assert(field.initializer?.expression !is IrConst<*>)
callStack.pushState(Wrapper.getStaticGetter(field)!!.invokeWithArguments().toState(field.type))
}
private fun MethodHandle?.invokeMethod(irFunction: IrFunction, args: List<State>) {
this ?: return handleIntrinsicMethods(irFunction)
val argsForMethodInvocation = irFunction.getArgsForMethodInvocation(this@DefaultCallInterceptor, this.type(), args)
withExceptionHandler(environment) {
val result = this.invokeWithArguments(argsForMethodInvocation)
callStack.pushState(result.toState(result.getType(irFunction.returnType)))
}
}
private fun handleIntrinsicMethods(irFunction: IrFunction) {
IntrinsicEvaluator.unwindInstructions(irFunction, environment).forEach { callStack.addInstruction(it) }
}
private fun calculateBuiltIns(irFunction: IrFunction, args: List<State>) {
val methodName = when (val property = (irFunction as? IrSimpleFunction)?.correspondingPropertySymbol) {
null -> irFunction.name.asString()
else -> property.owner.name.asString()
}
val receiverType = irFunction.dispatchReceiverParameter?.type
val argsType = listOfNotNull(receiverType) + irFunction.valueParameters.map { it.type }
val argsValues = args.map { it.wrap(this, calledFromBuiltIns = methodName !in setOf("plus", IrBuiltIns.OperatorNames.EQEQ)) }
fun IrType.getOnlyName(): String {
return when {
this.originalKotlinType != null -> this.originalKotlinType.toString()
this is IrSimpleType -> (this.classifierOrFail.owner as IrDeclarationWithName).name.asString() + (if (this.hasQuestionMark) "?" else "")
else -> this.render()
}
}
// TODO replace unary, binary, ternary functions with vararg
withExceptionHandler(environment) {
val result = when (argsType.size) {
1 -> interpretUnaryFunction(methodName, argsType[0].getOnlyName(), argsValues[0])
2 -> when (methodName) {
"rangeTo" -> return calculateRangeTo(irFunction.returnType, args)
else -> interpretBinaryFunction(
methodName, argsType[0].getOnlyName(), argsType[1].getOnlyName(), argsValues[0], argsValues[1]
)
}
3 -> interpretTernaryFunction(
methodName, argsType[0].getOnlyName(), argsType[1].getOnlyName(), argsType[2].getOnlyName(),
argsValues[0], argsValues[1], argsValues[2]
)
else -> throw InterpreterError("Unsupported number of arguments for invocation as builtin functions")
}
// TODO check "result is Unit"
callStack.pushState(result.toState(result.getType(irFunction.returnType)))
}
}
private fun calculateRangeTo(type: IrType, args: List<State>) {
val constructor = type.classOrNull!!.owner.constructors.first()
val constructorCall = IrConstructorCallImpl.fromSymbolOwner(constructor.returnType, constructor.symbol)
val constructorValueParameters = constructor.valueParameters.map { it.symbol }
val primitiveValueParameters = args.map { it as Primitive<*> }
primitiveValueParameters.forEachIndexed { index, primitive ->
constructorCall.putValueArgument(index, primitive.value.toIrConst(constructorValueParameters[index].owner.type))
}
callStack.addInstruction(CompoundInstruction(constructorCall))
}
private fun Any?.getType(defaultType: IrType): IrType {
return when (this) {
is Boolean -> irBuiltIns.booleanType
is Char -> irBuiltIns.charType
is Byte -> irBuiltIns.byteType
is Short -> irBuiltIns.shortType
is Int -> irBuiltIns.intType
is Long -> irBuiltIns.longType
is String -> irBuiltIns.stringType
is Float -> irBuiltIns.floatType
is Double -> irBuiltIns.doubleType
null -> irBuiltIns.nothingNType
else -> defaultType
}
}
private fun IrFunction.trySubstituteFunctionBody(): IrElement? {
val signature = this.symbol.signature ?: return null
this.body = bodyMap[signature] ?: return null
callStack.addInstruction(CompoundInstruction(this))
return body
}
// TODO fix in FIR2IR; const val getter must have body with IrGetField node
private fun IrFunction.tryCalculateLazyConst(): IrExpression? {
if (this !is IrSimpleFunction) return null
val expression = this.correspondingPropertySymbol?.owner?.backingField?.initializer?.expression
return expression?.apply { callStack.addInstruction(CompoundInstruction(this)) }
}
}

View File

@@ -0,0 +1,426 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.interpreter
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
import org.jetbrains.kotlin.ir.interpreter.builtins.evaluateIntrinsicAnnotation
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterError
import org.jetbrains.kotlin.ir.interpreter.stack.CallStack
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.*
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
internal fun unfoldInstruction(element: IrElement?, environment: IrInterpreterEnvironment) {
val callStack = environment.callStack
when (element) {
null -> return
is IrSimpleFunction -> unfoldFunction(element, environment)
is IrConstructor -> unfoldConstructor(element, callStack)
is IrCall -> unfoldCall(element, callStack)
is IrConstructorCall -> unfoldConstructorCall(element, callStack)
is IrEnumConstructorCall -> unfoldEnumConstructorCall(element, callStack)
is IrDelegatingConstructorCall -> unfoldDelegatingConstructorCall(element, callStack)
is IrInstanceInitializerCall -> unfoldInstanceInitializerCall(element, callStack)
is IrField -> unfoldField(element, callStack)
is IrBody -> unfoldBody(element, callStack)
is IrBlock -> unfoldBlock(element, callStack)
is IrReturn -> unfoldReturn(element, callStack)
is IrSetField -> unfoldSetField(element, callStack)
is IrGetField -> callStack.addInstruction(SimpleInstruction(element))
is IrGetValue -> unfoldGetValue(element, environment)
is IrGetObjectValue -> unfoldGetObjectValue(element, environment)
is IrGetEnumValue -> unfoldGetEnumValue(element, environment)
is IrEnumEntry -> unfoldEnumEntry(element, environment)
is IrConst<*> -> callStack.addInstruction(SimpleInstruction(element))
is IrVariable -> unfoldVariable(element, callStack)
is IrSetValue -> unfoldSetValue(element, callStack)
is IrTypeOperatorCall -> unfoldTypeOperatorCall(element, callStack)
is IrBranch -> unfoldBranch(element, callStack)
is IrWhileLoop -> unfoldWhileLoop(element, callStack)
is IrDoWhileLoop -> unfoldDoWhileLoop(element, callStack)
is IrWhen -> unfoldWhen(element, callStack)
is IrBreak -> unfoldBreak(element, callStack)
is IrContinue -> unfoldContinue(element, callStack)
is IrVararg -> unfoldVararg(element, callStack)
is IrSpreadElement -> callStack.addInstruction(CompoundInstruction(element.expression))
is IrTry -> unfoldTry(element, callStack)
is IrCatch -> unfoldCatch(element, callStack)
is IrThrow -> unfoldThrow(element, callStack)
is IrStringConcatenation -> unfoldStringConcatenation(element, environment)
is IrFunctionExpression -> callStack.addInstruction(SimpleInstruction(element))
is IrFunctionReference -> unfoldFunctionReference(element, callStack)
is IrPropertyReference -> unfoldPropertyReference(element, callStack)
is IrClassReference -> unfoldClassReference(element, callStack)
is IrComposite -> unfoldComposite(element, callStack)
else -> TODO("${element.javaClass} not supported")
}
}
private fun unfoldFunction(function: IrSimpleFunction, environment: IrInterpreterEnvironment) {
if (environment.callStack.getStackCount() >= IrInterpreterEnvironment.MAX_STACK)
return StackOverflowError().handleUserException(environment)
// SimpleInstruction with function is added in IrCall
// It will serve as endpoint for all possible calls, there we drop frame and copy result to new one
function.body?.let { environment.callStack.addInstruction(CompoundInstruction(it)) }
?: throw InterpreterError("Ir function must be with body")
}
private fun unfoldConstructor(constructor: IrConstructor, callStack: CallStack) {
// SimpleInstruction with function is added in constructor call
// It will serve as endpoint for all possible constructor calls, there we drop frame and return object
callStack.addInstruction(CompoundInstruction(constructor.body!!))
}
private fun unfoldCall(call: IrCall, callStack: CallStack) {
val function = call.symbol.owner
// new sub frame is used to store value arguments, in case then they are used in default args evaluation
callStack.newSubFrame(call, listOf())
callStack.addInstruction(SimpleInstruction(call))
unfoldValueParameters(call, callStack)
// must save receivers in memory in case then they are used in default args evaluation
call.extensionReceiver?.let {
callStack.addInstruction(SimpleInstruction(function.extensionReceiverParameter!!))
callStack.addInstruction(CompoundInstruction(it))
}
call.dispatchReceiver?.let {
callStack.addInstruction(SimpleInstruction(function.dispatchReceiverParameter!!))
callStack.addInstruction(CompoundInstruction(it))
}
}
private fun unfoldConstructorCall(constructorCall: IrFunctionAccessExpression, callStack: CallStack) {
val constructor = constructorCall.symbol.owner
callStack.newSubFrame(constructorCall, listOf()) // used to store value arguments, in case then they are use as default args
// this variable is used to create object once
callStack.addVariable(Variable(constructorCall.getThisReceiver(), Common(constructor.parentAsClass)))
callStack.addInstruction(SimpleInstruction(constructorCall))
unfoldValueParameters(constructorCall, callStack)
constructorCall.dispatchReceiver?.let {
callStack.addInstruction(SimpleInstruction(constructor.dispatchReceiverParameter!!))
callStack.addInstruction(CompoundInstruction(it))
}
}
private fun unfoldEnumConstructorCall(enumConstructorCall: IrEnumConstructorCall, callStack: CallStack) {
callStack.newSubFrame(enumConstructorCall, listOf()) // used to store value arguments, in case then they are use as default args
callStack.addInstruction(SimpleInstruction(enumConstructorCall))
unfoldValueParameters(enumConstructorCall, callStack)
}
private fun unfoldDelegatingConstructorCall(delegatingConstructorCall: IrFunctionAccessExpression, callStack: CallStack) {
callStack.newSubFrame(delegatingConstructorCall, listOf()) // used to store value arguments, in case then they are use as default args
callStack.addInstruction(SimpleInstruction(delegatingConstructorCall))
unfoldValueParameters(delegatingConstructorCall, callStack)
}
private fun unfoldValueParameters(expression: IrFunctionAccessExpression, callStack: CallStack) {
fun IrValueParameter.getDefault(): IrExpressionBody? {
return defaultValue
?: (this.parent as? IrSimpleFunction)?.overriddenSymbols
?.map { it.owner.valueParameters[this.index].getDefault() }
?.firstNotNullResult { it }
}
fun getDefaultForParameterAt(index: Int): IrExpression? {
return expression.symbol.owner.valueParameters[index].getDefault()?.expression
}
val irFunction = expression.symbol.owner
val valueParametersSymbols = irFunction.valueParameters.map { it.symbol }
val valueArguments = (0 until expression.valueArgumentsCount).map { expression.getValueArgument(it) }
for (i in valueParametersSymbols.indices.reversed()) {
callStack.addInstruction(SimpleInstruction(valueParametersSymbols[i].owner))
val arg = valueArguments[i] ?: getDefaultForParameterAt(i)
when {
arg != null -> callStack.addInstruction(CompoundInstruction(arg))
else ->
// case when value parameter is vararg and it is missing
expression.getVarargType(i)?.let {
callStack.addInstruction(SimpleInstruction(IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, it)))
}
}
}
// hack for extension receiver in lambda
if (expression.valueArgumentsCount != irFunction.valueParameters.size) {
val extensionReceiver = irFunction.getExtensionReceiver() ?: return
callStack.addInstruction(SimpleInstruction(extensionReceiver.owner))
valueArguments[0]?.let { callStack.addInstruction(CompoundInstruction(it)) }
}
}
private fun unfoldInstanceInitializerCall(instanceInitializerCall: IrInstanceInitializerCall, callStack: CallStack) {
val irClass = instanceInitializerCall.classSymbol.owner
// init blocks processing
val anonymousInitializer = irClass.declarations.filterIsInstance<IrAnonymousInitializer>().filter { !it.isStatic }
anonymousInitializer.reversed().forEach { callStack.addInstruction(CompoundInstruction(it.body)) }
// properties processing
val classProperties = irClass.declarations.filterIsInstance<IrProperty>()
classProperties.filter { it.backingField?.initializer?.expression != null }.reversed()
.forEach { callStack.addInstruction(CompoundInstruction(it.backingField)) }
}
private fun unfoldField(field: IrField, callStack: CallStack) {
callStack.addInstruction(SimpleInstruction(field))
callStack.addInstruction(CompoundInstruction(field.initializer?.expression))
}
private fun unfoldBody(body: IrBody, callStack: CallStack) {
unfoldStatements(body.statements, callStack)
}
private fun unfoldBlock(block: IrBlock, callStack: CallStack) {
callStack.newSubFrame(block, listOf())
callStack.addInstruction(SimpleInstruction(block))
unfoldStatements(block.statements, callStack)
}
private fun unfoldStatements(statements: List<IrStatement>, callStack: CallStack) {
for (i in statements.indices.reversed()) {
when (val statement = statements[i]) {
is IrClass -> if (!statement.isLocal) TODO("Only local classes are supported")
is IrFunction -> if (!statement.isLocal) TODO("Only local functions are supported")
else -> callStack.addInstruction(CompoundInstruction(statement))
}
}
}
private fun unfoldReturn(expression: IrReturn, callStack: CallStack) {
callStack.addInstruction(SimpleInstruction(expression)) //2
callStack.addInstruction(CompoundInstruction(expression.value)) //1
}
private fun unfoldSetField(expression: IrSetField, callStack: CallStack) {
// receiver is null, for example, for top level fields; cannot interpret set on top level var
if (expression.receiver.let { it == null || (it.type.classifierOrNull?.owner as? IrClass)?.isObject == true }) {
error("Cannot interpret set method on top level properties")
}
callStack.addInstruction(SimpleInstruction(expression))
callStack.addInstruction(CompoundInstruction(expression.value))
}
private fun unfoldGetValue(expression: IrGetValue, environment: IrInterpreterEnvironment) {
val expectedClass = expression.type.classOrNull?.owner
// used to evaluate constants inside object
if (expectedClass != null && expectedClass.isObject && expression.symbol.owner.origin == IrDeclarationOrigin.INSTANCE_RECEIVER) {
// TODO is this correct behaviour?
return unfoldGetObjectValue(IrGetObjectValueImpl(0, 0, expectedClass.defaultType, expectedClass.symbol), environment)
}
environment.callStack.pushState(environment.callStack.getVariable(expression.symbol).state)
}
private fun unfoldGetObjectValue(expression: IrGetObjectValue, environment: IrInterpreterEnvironment) {
val callStack = environment.callStack
val objectClass = expression.symbol.owner
environment.mapOfObjects[objectClass.symbol]?.let { return callStack.pushState(it) }
callStack.newSubFrame(expression, listOf(SimpleInstruction(expression)))
when {
!objectClass.hasAnnotation(evaluateIntrinsicAnnotation) -> {
val state = Common(objectClass)
environment.mapOfObjects[objectClass.symbol] = state // must set object's state here to avoid cyclic evaluation
callStack.addVariable(Variable(objectClass.thisReceiver!!.symbol, state))
val constructor = objectClass.constructors.firstOrNull() ?: return callStack.pushState(state)
val constructorCall = IrConstructorCallImpl.fromSymbolOwner(constructor.returnType, constructor.symbol)
callStack.addInstruction(CompoundInstruction(constructorCall))
}
}
}
private fun unfoldGetEnumValue(expression: IrGetEnumValue, environment: IrInterpreterEnvironment) {
val callStack = environment.callStack
environment.mapOfEnums[expression.symbol]?.let { return callStack.pushState(it) }
callStack.addInstruction(SimpleInstruction(expression))
val enumEntry = expression.symbol.owner
val enumClass = enumEntry.symbol.owner.parentAsClass
enumClass.declarations.filterIsInstance<IrEnumEntry>().forEach {
if (enumClass.hasAnnotation(evaluateIntrinsicAnnotation)) return@forEach callStack.addInstruction(SimpleInstruction(it))
callStack.addInstruction(CompoundInstruction(it))
}
}
private fun unfoldEnumEntry(enumEntry: IrEnumEntry, environment: IrInterpreterEnvironment) {
val enumClass = enumEntry.symbol.owner.parentAsClass
val enumEntries = enumClass.declarations.filterIsInstance<IrEnumEntry>()
val enumSuperCall = (enumClass.primaryConstructor?.body?.statements?.firstOrNull() as? IrEnumConstructorCall)
if (enumEntries.isNotEmpty() && enumSuperCall != null) {
val valueArguments = listOf(
enumEntry.name.asString().toIrConst(environment.irBuiltIns.stringType),
enumEntries.indexOf(enumEntry).toIrConst(environment.irBuiltIns.intType)
)
valueArguments.forEachIndexed { index, irConst -> enumSuperCall.putValueArgument(index, irConst) }
}
val enumConstructorCall = enumEntry.initializerExpression?.expression as? IrEnumConstructorCall
?: throw InterpreterError("Initializer at enum entry ${enumEntry.fqNameWhenAvailable} is null")
val enumClassObject = Variable(enumConstructorCall.getThisReceiver(), Common(enumEntry.correspondingClass ?: enumClass))
environment.mapOfEnums[enumEntry.symbol] = enumClassObject.state as Complex
environment.callStack.newSubFrame(enumEntry, listOf(CompoundInstruction(enumConstructorCall), SimpleInstruction(enumEntry)))
environment.callStack.addVariable(enumClassObject)
}
private fun unfoldVariable(variable: IrVariable, callStack: CallStack) {
when (variable.initializer) {
null -> callStack.addVariable(Variable(variable.symbol))
else -> {
callStack.addInstruction(SimpleInstruction(variable))
callStack.addInstruction(CompoundInstruction(variable.initializer!!))
}
}
}
private fun unfoldSetValue(expression: IrSetValue, callStack: CallStack) {
callStack.addInstruction(SimpleInstruction(expression))
callStack.addInstruction(CompoundInstruction(expression.value))
}
private fun unfoldTypeOperatorCall(element: IrTypeOperatorCall, callStack: CallStack) {
callStack.addInstruction(SimpleInstruction(element))
callStack.addInstruction(CompoundInstruction(element.argument))
}
private fun unfoldBranch(branch: IrBranch, callStack: CallStack) {
callStack.addInstruction(SimpleInstruction(branch)) //2
callStack.addInstruction(CompoundInstruction(branch.condition)) //1
}
private fun unfoldWhileLoop(loop: IrWhileLoop, callStack: CallStack) {
callStack.newSubFrame(loop, listOf())
callStack.addInstruction(SimpleInstruction(loop))
callStack.addInstruction(CompoundInstruction(loop.condition))
}
private fun unfoldDoWhileLoop(loop: IrDoWhileLoop, callStack: CallStack) {
callStack.newSubFrame(loop, listOf())
callStack.addInstruction(SimpleInstruction(loop))
callStack.addInstruction(CompoundInstruction(loop.condition))
callStack.addInstruction(CompoundInstruction(loop.body!!))
}
private fun unfoldWhen(element: IrWhen, callStack: CallStack) {
// new sub frame to drop it after
callStack.newSubFrame(element, element.branches.map { CompoundInstruction(it) } + listOf(SimpleInstruction(element)))
}
private fun unfoldContinue(element: IrContinue, callStack: CallStack) {
callStack.unrollInstructionsForBreakContinue(element)
}
private fun unfoldBreak(element: IrBreak, callStack: CallStack) {
callStack.unrollInstructionsForBreakContinue(element)
}
private fun unfoldVararg(element: IrVararg, callStack: CallStack) {
callStack.addInstruction(SimpleInstruction(element))
element.elements.reversed().forEach { callStack.addInstruction(CompoundInstruction(it)) }
}
private fun unfoldTry(element: IrTry, callStack: CallStack) {
callStack.newSubFrame(element, listOf())
callStack.addInstruction(SimpleInstruction(element))
callStack.addInstruction(CompoundInstruction(element.tryResult))
}
private fun unfoldCatch(element: IrCatch, callStack: CallStack) {
val exceptionState = callStack.peekState() as? ExceptionState ?: return
if (exceptionState.isSubtypeOf(element.catchParameter.type)) {
callStack.popState()
val frameOwner = callStack.getCurrentFrameOwner() as IrTry
callStack.dropSubFrame() // drop other catch blocks
callStack.newSubFrame(element, listOf()) // new frame with IrTry instruction to interpret finally block at the end
callStack.addVariable(Variable(element.catchParameter.symbol, exceptionState))
callStack.addInstruction(SimpleInstruction(frameOwner))
callStack.addInstruction(CompoundInstruction(element.result))
}
}
private fun unfoldThrow(expression: IrThrow, callStack: CallStack) {
callStack.addInstruction(SimpleInstruction(expression))
callStack.addInstruction(CompoundInstruction(expression.value))
}
private fun unfoldStringConcatenation(expression: IrStringConcatenation, environment: IrInterpreterEnvironment) {
val callStack = environment.callStack
callStack.newSubFrame(expression, listOf())
callStack.addInstruction(SimpleInstruction(expression))
// this callback is used to check the need for an explicit toString call
val explicitToStringCheck = fun() {
when (val state = callStack.peekState()) {
is Common -> {
callStack.popState()
val toStringFun = state.getToStringFunction()
val receiver = toStringFun.dispatchReceiverParameter!!
val toStringCall = IrCallImpl.fromSymbolOwner(0, 0, environment.irBuiltIns.stringType, toStringFun.symbol)
toStringCall.dispatchReceiver = IrConstImpl.constNull(0, 0, receiver.type) // just stub receiver
callStack.newSubFrame(toStringCall, listOf(SimpleInstruction(toStringCall)))
callStack.pushState(state)
}
}
}
expression.arguments.reversed().forEach {
callStack.addInstruction(CustomInstruction(explicitToStringCheck))
callStack.addInstruction(CompoundInstruction(it))
}
}
private fun unfoldComposite(element: IrComposite, callStack: CallStack) {
when (element.origin) {
IrStatementOrigin.DESTRUCTURING_DECLARATION -> element.statements.reversed()
.forEach { callStack.addInstruction(CompoundInstruction(it)) }
null -> element.statements.reversed()
.forEach { callStack.addInstruction(CompoundInstruction(it)) } // is null for body of do while loop
else -> TODO("${element.origin} not implemented")
}
}
private fun unfoldFunctionReference(reference: IrFunctionReference, callStack: CallStack) {
val function = reference.symbol.owner
callStack.newSubFrame(reference, listOf())
callStack.addInstruction(SimpleInstruction(reference))
reference.extensionReceiver?.let {
callStack.addInstruction(SimpleInstruction(function.extensionReceiverParameter!!))
callStack.addInstruction(CompoundInstruction(it))
}
reference.dispatchReceiver?.let {
callStack.addInstruction(SimpleInstruction(function.dispatchReceiverParameter!!))
callStack.addInstruction(CompoundInstruction(it))
}
}
private fun unfoldPropertyReference(propertyReference: IrPropertyReference, callStack: CallStack) {
callStack.addInstruction(SimpleInstruction(propertyReference))
callStack.addInstruction(CompoundInstruction(propertyReference.dispatchReceiver))
}
private fun unfoldClassReference(classReference: IrClassReference, callStack: CallStack) {
callStack.addInstruction(SimpleInstruction(classReference))
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.interpreter
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.interpreter.stack.CallStack
import org.jetbrains.kotlin.ir.interpreter.state.Complex
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.isSubclassOf
internal class IrInterpreterEnvironment(val irBuiltIns: IrBuiltIns, val callStack: CallStack) {
val irExceptions = mutableListOf<IrClass>()
var mapOfEnums = mutableMapOf<IrSymbol, Complex>()
var mapOfObjects = mutableMapOf<IrSymbol, Complex>()
private constructor(environment: IrInterpreterEnvironment) : this(environment.irBuiltIns, CallStack()) {
irExceptions.addAll(environment.irExceptions)
mapOfEnums = environment.mapOfEnums
mapOfObjects = environment.mapOfObjects
}
constructor(irModule: IrModuleFragment) : this(irModule.irBuiltins, CallStack()) {
irExceptions.addAll(
irModule.files
.flatMap { it.declarations }
.filterIsInstance<IrClass>()
.filter { it.isSubclassOf(irBuiltIns.throwableClass.owner) }
)
}
fun copyWithNewCallStack(): IrInterpreterEnvironment {
return IrInterpreterEnvironment(this)
}
companion object {
const val MAX_STACK = 10_000
const val MAX_COMMANDS = 1_000_000
}
}

View File

@@ -1,85 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.interpreter
import org.jetbrains.kotlin.ir.interpreter.stack.Stack
import org.jetbrains.kotlin.ir.interpreter.state.Primitive
import org.jetbrains.kotlin.ir.interpreter.state.isSubtypeOf
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrReturnableBlock
import org.jetbrains.kotlin.ir.expressions.IrWhen
import org.jetbrains.kotlin.ir.expressions.IrWhileLoop
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.render
enum class ReturnLabel {
REGULAR, RETURN, BREAK_LOOP, BREAK_WHEN, CONTINUE, EXCEPTION
}
open class ExecutionResult(val returnLabel: ReturnLabel, private val owner: IrElement? = null) {
fun getNextLabel(irElement: IrElement, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
return when (returnLabel) {
ReturnLabel.RETURN -> when (irElement) {
is IrCall, is IrReturnableBlock, is IrSimpleFunction -> if (owner == irElement) Next else this
else -> this
}
ReturnLabel.BREAK_WHEN -> when (irElement) {
is IrWhen -> Next
else -> this
}
ReturnLabel.BREAK_LOOP -> when (irElement) {
is IrWhileLoop -> if (owner == irElement) Next else this
else -> this
}
ReturnLabel.CONTINUE -> when (irElement) {
is IrWhileLoop -> if (owner == irElement) irElement.interpret() else this
else -> this
}
ReturnLabel.EXCEPTION -> Exception
ReturnLabel.REGULAR -> Next
}
}
fun addOwnerInfo(owner: IrElement): ExecutionResult {
return ExecutionResult(returnLabel, owner)
}
}
inline fun ExecutionResult.check(toCheckLabel: ReturnLabel = ReturnLabel.REGULAR, returnBlock: (ExecutionResult) -> Unit): ExecutionResult {
if (this.returnLabel != toCheckLabel) returnBlock(this)
return this
}
/**
* This method is analog of `checkcast` jvm bytecode operation. Throw exception whenever actual type is not a subtype of expected.
*/
internal fun ExecutionResult.implicitCastIfNeeded(expectedType: IrType, actualType: IrType, stack: Stack): ExecutionResult {
if (actualType.classifierOrNull !is IrTypeParameterSymbol) return this
if (expectedType.classifierOrFail is IrTypeParameterSymbol) return this
val actualState = stack.peekReturnValue()
if (actualState is Primitive<*> && actualState.value == null) return this // this is handled as NullPointerException
if (!actualState.isSubtypeOf(expectedType)) {
val convertibleClassName = stack.popReturnValue().irClass.fqNameWhenAvailable
throw ClassCastException("$convertibleClassName cannot be cast to ${expectedType.render()}")
}
return this
}
object Next : ExecutionResult(ReturnLabel.REGULAR)
object Return : ExecutionResult(ReturnLabel.RETURN)
object BreakLoop : ExecutionResult(ReturnLabel.BREAK_LOOP)
object BreakWhen : ExecutionResult(ReturnLabel.BREAK_WHEN)
object Continue : ExecutionResult(ReturnLabel.CONTINUE)
object Exception : ExecutionResult(ReturnLabel.EXCEPTION)

View File

@@ -11,19 +11,24 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl
import org.jetbrains.kotlin.ir.interpreter.builtins.evaluateIntrinsicAnnotation
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.*
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.interpreter.proxy.Proxy
import org.jetbrains.kotlin.ir.interpreter.proxy.wrap
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KTypeState
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.buildSimpleType
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.lang.invoke.MethodType
import kotlin.math.min
internal fun IrFunction.getDispatchReceiver(): IrValueParameterSymbol? = this.dispatchReceiverParameter?.symbol
@@ -33,6 +38,8 @@ internal fun IrFunction.getReceiver(): IrSymbol? = this.getDispatchReceiver() ?:
internal fun IrFunctionAccessExpression.getBody(): IrBody? = this.symbol.owner.body
internal fun IrFunctionAccessExpression.getThisReceiver(): IrValueSymbol = this.symbol.owner.parentAsClass.thisReceiver!!.symbol
internal fun State.toIrExpression(expression: IrExpression): IrExpression {
val start = expression.startOffset
val end = expression.endOffset
@@ -44,6 +51,9 @@ internal fun State.toIrExpression(expression: IrExpression): IrExpression {
type.isPrimitiveType() || type.isString() -> this.value.toIrConst(type, start, end)
else -> expression // TODO support for arrays
}
is ExceptionState -> {
IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, "\n" + this.getFullDescription())
}
is Complex -> {
val stateType = this.irClass.defaultType
when {
@@ -55,13 +65,17 @@ internal fun State.toIrExpression(expression: IrExpression): IrExpression {
}
}
/**
* Convert object from outer world to state
*/
internal fun Any?.toState(irType: IrType): State {
return when (this) {
is Proxy -> this.state
is State -> this
is Boolean, is Char, is Byte, is Short, is Int, is Long, is String, is Float, is Double, is Array<*>, is ByteArray,
is CharArray, is ShortArray, is IntArray, is LongArray, is FloatArray, is DoubleArray, is BooleanArray -> Primitive(this, irType)
null -> Primitive(this, irType)
else -> Wrapper(this, irType.classOrNull!!.owner)
else -> irType.classOrNull?.owner?.let { Wrapper(this, it) } ?: Wrapper(this)
}
}
@@ -86,8 +100,11 @@ fun Any?.toIrConst(irType: IrType, startOffset: Int = UNDEFINED_OFFSET, endOffse
}
}
internal fun <T> IrConst<T>.toPrimitive(): Primitive<T> {
return Primitive(this.value, this.type)
@Suppress("UNCHECKED_CAST")
internal fun <T> IrConst<T>.toPrimitive(): Primitive<T> = when {
type.isByte() -> Primitive((value as Number).toByte() as T, type)
type.isShort() -> Primitive((value as Number).toShort() as T, type)
else -> Primitive(value, type)
}
fun IrAnnotationContainer?.hasAnnotation(annotation: FqName): Boolean {
@@ -124,27 +141,6 @@ internal fun getPrimitiveClass(irType: IrType, asObject: Boolean = false): Class
}
}
internal fun IrFunction.getArgsForMethodInvocation(args: List<Variable>): List<Any?> {
val argsValues = args.map {
when (val state = it.state) {
is ExceptionState -> state.getThisAsCauseForException()
is Wrapper -> state.value
is Primitive<*> -> state.value
else -> throw AssertionError("${state::class} is unsupported as argument for wrapper method invocation")
}
}.toMutableList()
// TODO if vararg isn't last parameter
// must convert vararg array into separated elements for correct invoke
if (this.valueParameters.lastOrNull()?.varargElementType != null) {
val varargValue = argsValues.last()
argsValues.removeAt(argsValues.size - 1)
argsValues.addAll(varargValue as Array<out Any?>)
}
return argsValues
}
fun IrFunction.getLastOverridden(): IrFunction {
if (this !is IrSimpleFunction) return this
@@ -168,53 +164,33 @@ internal fun List<Any?>.toPrimitiveStateArray(type: IrType): Primitive<*> {
fun IrFunctionAccessExpression.getVarargType(index: Int): IrType? {
val varargType = this.symbol.owner.valueParameters[index].varargElementType ?: return null
varargType.classOrNull?.let { return this.symbol.owner.valueParameters[index].type }
val typeParameter = varargType.classifierOrFail.owner as IrTypeParameter
return this.getTypeArgument(typeParameter.index)
}
internal fun getTypeArguments(
container: IrTypeParametersContainer, expression: IrFunctionAccessExpression, mapper: (IrTypeParameterSymbol) -> State
): List<Variable> {
fun IrType.getState(): State {
return this.classOrNull?.owner?.let { Common(it) } ?: mapper(this.classifierOrFail as IrTypeParameterSymbol)
val type = this.symbol.owner.valueParameters[index].type as? IrSimpleType ?: return null
return type.buildSimpleType {
val typeParameter = varargType.classifierOrFail.owner as IrTypeParameter
arguments = listOf(makeTypeProjection(this@getVarargType.getTypeArgument(typeParameter.index)!!, Variance.OUT_VARIANCE))
}
val typeArguments = container.typeParameters.mapIndexed { index, typeParameter ->
val typeArgument = expression.getTypeArgument(index)!!
Variable(typeParameter.symbol, typeArgument.getState())
}.toMutableList()
if (container is IrSimpleFunction) {
container.returnType.classifierOrFail.owner.safeAs<IrTypeParameter>()
?.let { typeArguments.add(Variable(it.symbol, expression.type.getState())) }
}
return typeArguments
}
internal fun State?.extractNonLocalDeclarations(): List<Variable> {
this ?: return listOf()
val state = this.takeIf { it !is Complex } ?: (this as Complex).getOriginal()
return state.fields.filter { it.symbol !is IrFieldSymbol }
}
internal fun State?.getCorrectReceiverByFunction(irFunction: IrFunction): State? {
if (this !is Complex) return this
val original: Complex? = this.getOriginal()
val other = irFunction.parentClassOrNull?.thisReceiver ?: return this
return generateSequence(original) { it.superClass }.firstOrNull { it.irClass.thisReceiver == other } ?: this
return this.fields.filter { it.symbol !is IrFieldSymbol }
}
internal fun IrFunction.getCapitalizedFileName() = this.file.name.replace(".kt", "Kt").capitalizeAsciiOnly()
internal fun IrType.isUnsigned() = this.isUByte() || this.isUShort() || this.isUInt() || this.isULong()
internal fun IrType.isUnsignedArray(): Boolean {
if (this !is IrSimpleType || classifier !is IrClassSymbol) return false
val fqName = (classifier.owner as IrDeclarationWithName).fqNameWhenAvailable?.asString()
return fqName in setOf("kotlin.UByteArray", "kotlin.UShortArray", "kotlin.UIntArray", "kotlin.ULongArray")
}
internal fun IrType.isPrimitiveArray(): Boolean {
return this.getClass()?.fqNameWhenAvailable?.toUnsafe()?.let { StandardNames.isPrimitiveArray(it) } ?: false
}
internal fun IrType.isFunction() = this.getClass()?.fqNameWhenAvailable?.asString()?.startsWith("kotlin.Function") ?: false
internal fun IrType.isKFunction() = this.getClass()?.fqNameWhenAvailable?.asString()?.startsWith("kotlin.reflect.KFunction") ?: false
internal fun IrType.isTypeParameter() = classifierOrNull is IrTypeParameterSymbol
@@ -222,6 +198,24 @@ internal fun IrType.isInterface() = classOrNull?.owner?.kind == ClassKind.INTERF
internal fun IrType.isThrowable() = this.getClass()?.fqNameWhenAvailable?.asString() == "kotlin.Throwable"
internal fun IrType.renderType(): String {
var renderedType = this.render()
do {
val index = renderedType.indexOf(" of ")
if (index == -1) break
val replaceUntilComma = renderedType.indexOf(',', index)
val replaceUntilTriangle = renderedType.indexOf('>', index)
val replaceUntil = when {
replaceUntilComma == -1 && replaceUntilTriangle == -1 -> renderedType.length
replaceUntilComma == -1 -> replaceUntilTriangle
replaceUntilTriangle == -1 -> replaceUntilComma
else -> min(replaceUntilComma, replaceUntilTriangle)
}
renderedType = renderedType.replaceRange(index, replaceUntil, "")
} while (true)
return renderedType
}
fun IrClass.internalName(): String {
val internalName = StringBuilder(this.name.asString())
generateSequence(this as? IrDeclarationParent) { (it as? IrDeclaration)?.parent }
@@ -234,3 +228,63 @@ fun IrClass.internalName(): String {
}
return internalName.toString()
}
internal inline fun withExceptionHandler(environment: IrInterpreterEnvironment, block: () -> Unit) {
try {
block()
// check if during proxy interpretation was an exception
val possibleException = environment.callStack.peekState() as? ExceptionState
if (possibleException != null) environment.callStack.dropFramesUntilTryCatch()
} catch (e: Throwable) {
e.handleUserException(environment)
}
}
internal fun Throwable.handleUserException(environment: IrInterpreterEnvironment) {
val exceptionName = this::class.java.simpleName
val irExceptionClass = environment.irExceptions.firstOrNull { it.name.asString() == exceptionName }
?: environment.irBuiltIns.throwableClass.owner
environment.callStack.pushState(ExceptionState(this, irExceptionClass, environment.callStack.getStackTrace()))
environment.callStack.dropFramesUntilTryCatch()
}
/**
* This method is analog of `checkcast` jvm bytecode operation. Throw exception whenever actual type is not a subtype of expected.
*/
internal fun IrFunction?.checkCast(environment: IrInterpreterEnvironment): Boolean {
this ?: return true
val actualType = this.returnType
if (actualType.classifierOrNull !is IrTypeParameterSymbol) return true
// TODO expectedType can be missing for functions called as proxy
val expectedType = (environment.callStack.getVariable(this.symbol).state as? KTypeState)?.irType ?: return true
if (expectedType.classifierOrFail is IrTypeParameterSymbol) return true
val actualState = environment.callStack.peekState() ?: return true
if (actualState is Primitive<*> && actualState.value == null) return true // this is handled in checkNullability
if (!actualState.isSubtypeOf(expectedType)) {
val convertibleClassName = environment.callStack.popState().irClass.fqNameWhenAvailable
ClassCastException("$convertibleClassName cannot be cast to ${expectedType.render()}").handleUserException(environment)
return false
}
return true
}
internal fun IrFunction.getArgsForMethodInvocation(
callInterceptor: CallInterceptor, methodType: MethodType, args: List<State>
): List<Any?> {
val argsValues = args
.mapIndexed { index, state -> state.wrap(callInterceptor, methodType.parameterType(index)) }
.toMutableList()
// TODO if vararg isn't last parameter
// must convert vararg array into separated elements for correct invoke
if (this.valueParameters.lastOrNull()?.varargElementType != null) {
val varargValue = argsValues.last()
argsValues.removeAt(argsValues.size - 1)
argsValues.addAll(varargValue as Array<out Any?>)
}
return argsValues
}

View File

@@ -10,28 +10,3 @@ import org.jetbrains.kotlin.name.FqName
val compileTimeAnnotation = FqName("kotlin.CompileTimeCalculation")
val evaluateIntrinsicAnnotation = FqName("kotlin.EvaluateIntrinsic")
val contractsDslAnnotation = FqName("kotlin.internal.ContractsDsl")
data class CompileTimeFunction(val methodName: String, val args: List<String>)
@Suppress("UNCHECKED_CAST")
fun <T> unaryOperation(
methodName: String, receiverType: String, function: (T) -> Any?
): Pair<CompileTimeFunction, Function1<Any?, Any?>> {
return CompileTimeFunction(methodName, listOf(receiverType)) to function as Function1<Any?, Any?>
}
@Suppress("UNCHECKED_CAST")
fun <T, E> binaryOperation(
methodName: String, receiverType: String, parameterType: String, function: (T, E) -> Any?
): Pair<CompileTimeFunction, Function2<Any?, Any?, Any?>> {
return CompileTimeFunction(methodName, listOfNotNull(receiverType, parameterType)) to function as Function2<Any?, Any?, Any?>
}
@Suppress("UNCHECKED_CAST")
fun <T, E, R> ternaryOperation(
methodName: String, receiverType: String, firstParameterType: String, secondParameterType: String, function: (T, E, R) -> Any?
): Pair<CompileTimeFunction, Function3<Any?, Any?, Any?, Any?>> {
return CompileTimeFunction(
methodName, listOfNotNull(receiverType, firstParameterType, secondParameterType)
) to function as Function3<Any?, Any?, Any?, Any?>
}

View File

@@ -0,0 +1,105 @@
/*
* 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.ir.interpreter.checker
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.interpreter.builtins.compileTimeAnnotation
import org.jetbrains.kotlin.ir.interpreter.builtins.contractsDslAnnotation
import org.jetbrains.kotlin.ir.interpreter.builtins.evaluateIntrinsicAnnotation
import org.jetbrains.kotlin.ir.interpreter.hasAnnotation
import org.jetbrains.kotlin.ir.interpreter.isPrimitiveArray
import org.jetbrains.kotlin.ir.interpreter.isUnsigned
import org.jetbrains.kotlin.ir.types.isAny
import org.jetbrains.kotlin.ir.types.isArray
import org.jetbrains.kotlin.ir.types.isPrimitiveType
import org.jetbrains.kotlin.ir.types.isString
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
enum class EvaluationMode(protected val mustCheckBody: Boolean) {
FULL(mustCheckBody = true) {
private val visited = mutableMapOf<String, Boolean>()
override fun canEvaluateFunction(function: IrFunction, expression: IrCall?): Boolean {
return true//function.isBuiltin() || isIrBuiltin() || isIntrinsic() || function.body != null
}
private fun IrFunction.isBuiltin(): Boolean {
val parent = this.parentClassOrNull ?: return false
val parentType = parent.defaultType
return parentType.let { it.isPrimitiveType() || it.isString() || it.isArray() || it.isPrimitiveArray() }
}
private fun isIrBuiltin(): Boolean {
return false
}
private fun isIntrinsic(): Boolean {
return false
}
},
WITH_ANNOTATIONS(mustCheckBody = false) {
override fun canEvaluateFunction(function: IrFunction, expression: IrCall?): Boolean {
if (function.isCompileTimeProperty()) return true
return function.isMarkedAsCompileTime() || function.origin == IrBuiltIns.BUILTIN_OPERATOR ||
(function is IrSimpleFunction && function.isOperator && function.name.asString() == "invoke") ||
(function is IrSimpleFunction && function.isFakeOverride && function.overriddenSymbols.any { canEvaluateFunction(it.owner) }) ||
function.isCompileTimeTypeAlias()
}
private fun IrDeclaration?.isCompileTimeProperty(): Boolean {
val property = (this as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: return false
if (property.isConst) return true
if (property.isMarkedAsCompileTime() || property.isCompileTimeTypeAlias()) return true
val backingField = property.backingField
val backingFieldExpression = backingField?.initializer?.expression as? IrGetValue
return backingFieldExpression?.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER
}
},
ONLY_BUILTINS(mustCheckBody = false) {
override fun canEvaluateFunction(function: IrFunction, expression: IrCall?): Boolean {
if ((function as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.isConst == true) return true
val parent = function.parentClassOrNull ?: return false
val parentType = parent.defaultType
return when {
parentType.isPrimitiveType() -> function.name.asString() !in setOf("inc", "dec", "rangeTo", "hashCode")
parentType.isString() -> function.name.asString() !in setOf("subSequence", "hashCode")
parentType.isAny() -> function.name.asString() == "toString" && expression?.dispatchReceiver !is IrGetObjectValue
parent.isObject -> parent.parentClassOrNull?.defaultType?.let { it.isPrimitiveType() || it.isUnsigned() } == true
else -> false
}
}
};
abstract fun canEvaluateFunction(function: IrFunction, expression: IrCall? = null): Boolean
fun canEvaluateBody(function: IrFunction): Boolean {
return (mustCheckBody || function.isLocal) && !function.isContract() && !function.isMarkedAsEvaluateIntrinsic()
}
protected val compileTimeTypeAliases = setOf(
"java.lang.StringBuilder", "java.lang.IllegalArgumentException", "java.util.NoSuchElementException"
)
fun IrDeclaration.isMarkedAsCompileTime() = isMarkedWith(compileTimeAnnotation)
private fun IrDeclaration.isContract() = isMarkedWith(contractsDslAnnotation)
private fun IrDeclaration.isMarkedAsEvaluateIntrinsic() = isMarkedWith(evaluateIntrinsicAnnotation)
protected fun IrDeclaration.isCompileTimeTypeAlias() = this.parentClassOrNull?.fqNameWhenAvailable?.asString() in compileTimeTypeAliases
protected fun IrDeclaration.isMarkedWith(annotation: FqName): Boolean {
if (this is IrClass && this.isCompanion) return false
if (this.hasAnnotation(annotation)) return true
return (this.parent as? IrClass)?.isMarkedWith(annotation) ?: false
}
}

View File

@@ -3,11 +3,8 @@
* 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.ir.interpreter
package org.jetbrains.kotlin.ir.interpreter.checker
import org.jetbrains.kotlin.ir.interpreter.builtins.compileTimeAnnotation
import org.jetbrains.kotlin.ir.interpreter.builtins.contractsDslAnnotation
import org.jetbrains.kotlin.ir.interpreter.builtins.evaluateIntrinsicAnnotation
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
@@ -17,55 +14,11 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.name.FqName
enum class EvaluationMode {
FULL, ONLY_BUILTINS
}
class IrCompileTimeChecker(
containingDeclaration: IrElement? = null, private val mode: EvaluationMode = EvaluationMode.FULL
containingDeclaration: IrElement? = null, private val mode: EvaluationMode = EvaluationMode.WITH_ANNOTATIONS
) : IrElementVisitor<Boolean, Nothing?> {
private val visitedStack = mutableListOf<IrElement>().apply { if (containingDeclaration != null) add(containingDeclaration) }
private val compileTimeTypeAliases = setOf(
"java.lang.StringBuilder", "java.lang.IllegalArgumentException", "java.util.NoSuchElementException"
)
private fun IrDeclaration.isContract() = isMarkedWith(contractsDslAnnotation)
private fun IrDeclaration.isMarkedAsEvaluateIntrinsic() = isMarkedWith(evaluateIntrinsicAnnotation)
private fun IrDeclaration.isMarkedAsCompileTime(expression: IrCall? = null): Boolean {
if (mode == EvaluationMode.FULL) {
return isMarkedWith(compileTimeAnnotation) ||
(this is IrSimpleFunction && this.isFakeOverride && this.overriddenSymbols.any { it.owner.isMarkedAsCompileTime() }) ||
this.parentClassOrNull?.fqNameWhenAvailable?.asString() in compileTimeTypeAliases
}
val parent = this.parentClassOrNull
val parentType = parent?.defaultType
return when {
parentType?.isPrimitiveType() == true -> (this as IrFunction).name.asString() !in setOf("inc", "dec", "rangeTo", "hashCode")
parentType?.isString() == true -> (this as IrDeclarationWithName).name.asString() !in setOf("subSequence", "hashCode")
parentType?.isAny() == true -> (this as IrFunction).name.asString() == "toString" && expression?.dispatchReceiver !is IrGetObjectValue
parent?.isObject == true -> parent.parentClassOrNull?.defaultType?.let { it.isPrimitiveType() || it.isUnsigned() } == true
else -> false
}
}
private fun IrDeclaration.isMarkedWith(annotation: FqName): Boolean {
if (this is IrClass && this.isCompanion) return false
if (this.hasAnnotation(annotation)) return true
return (this.parent as? IrClass)?.isMarkedWith(annotation) ?: false
}
private fun IrProperty?.isCompileTime(): Boolean {
if (this == null) return false
if (this.isConst) return true
if (this.isMarkedAsCompileTime()) return true
val backingField = this.backingField
val backingFieldExpression = backingField?.initializer?.expression as? IrGetValue
return backingFieldExpression?.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER
}
private fun IrElement.asVisited(block: () -> Boolean): Boolean {
visitedStack += this
val result = block()
@@ -82,26 +35,21 @@ class IrCompileTimeChecker(
private fun visitConstructor(expression: IrFunctionAccessExpression): Boolean {
return when {
expression.symbol.owner.isMarkedAsEvaluateIntrinsic() -> true
!visitValueParameters(expression, null) -> false
else -> expression.symbol.owner.isMarkedAsCompileTime()
!visitValueParameters(expression, null) || !mode.canEvaluateFunction(expression.symbol.owner) -> false
else -> if (mode.canEvaluateBody(expression.symbol.owner)) expression.symbol.owner.body?.accept(this, null) != false else true
}
}
override fun visitCall(expression: IrCall, data: Nothing?): Boolean {
if (expression.symbol.owner.isContract()) return false
val owner = expression.symbol.owner
if (!mode.canEvaluateFunction(owner, expression)) return false
val property = (expression.symbol.owner as? IrSimpleFunction)?.correspondingPropertySymbol?.owner
if (expression.symbol.owner.isMarkedAsCompileTime(expression) || property.isCompileTime()) {
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, null) ?: true
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, null) ?: true
if (!visitValueParameters(expression, null)) return false
val bodyComputable = if (expression.symbol.owner.isLocal) expression.symbol.owner.body?.accept(this, null) ?: true else true
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, null) ?: true
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, null) ?: true
if (!visitValueParameters(expression, null)) return false
val bodyComputable = owner.asVisited { if (mode.canEvaluateBody(owner)) owner.body?.accept(this, null) ?: true else true }
return dispatchReceiverComputable && extensionReceiverComputable && bodyComputable
}
return false
return dispatchReceiverComputable && extensionReceiverComputable && bodyComputable
}
override fun visitVariable(declaration: IrVariable, data: Nothing?): Boolean {
@@ -137,7 +85,7 @@ class IrCompileTimeChecker(
}
override fun visitComposite(expression: IrComposite, data: Nothing?): Boolean {
if (expression.origin == IrStatementOrigin.DESTRUCTURING_DECLARATION) {
if (expression.origin == IrStatementOrigin.DESTRUCTURING_DECLARATION || expression.origin == null) {
return visitStatements(expression.statements, data)
}
return false
@@ -167,6 +115,17 @@ class IrCompileTimeChecker(
}
override fun visitGetField(expression: IrGetField, data: Nothing?): Boolean {
// TODO fix later; used it here because java boolean resolves very strange,
// its type is flexible (so its not primitive) and there is no initializer at backing field
val fqName = expression.symbol.owner.fqNameForIrSerialization
if (fqName.toString().let { it == "java.lang.Boolean.FALSE" || it == "java.lang.Boolean.TRUE" }) {
return true
}
if (expression.receiver == null)
return expression.symbol.owner.correspondingPropertySymbol?.owner?.let { it.isConst || it.backingField?.initializer?.expression is IrConst<*> } == true
val property = expression.symbol.owner.correspondingPropertySymbol?.owner
val owner = expression.symbol.owner
if (owner.origin == IrDeclarationOrigin.PROPERTY_BACKING_FIELD && owner.correspondingPropertySymbol?.owner?.isConst == true) {
val receiverComputable = expression.receiver?.accept(this, null) ?: true
@@ -175,35 +134,65 @@ class IrCompileTimeChecker(
return true
}
}
val parent = owner.parent as IrSymbolOwner
val parent = owner.parent as IrDeclarationContainer
val getter = parent.declarations.filterIsInstance<IrProperty>().single { it == property }.getter
val isJavaPrimitiveStatic = owner.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB && owner.isStatic &&
(owner.type.isPrimitiveType() || owner.type.isStringClassType())
return visitedStack.contains(parent) || isJavaPrimitiveStatic
return getter?.let { visitedStack.contains(it) } == true || isJavaPrimitiveStatic
}
override fun visitSetField(expression: IrSetField, data: Nothing?): Boolean {
if (expression.receiver.let { it == null || (it.type.classifierOrNull?.owner as? IrClass)?.isObject == true }) {
return false
}
//todo check receiver?
val parent = expression.symbol.owner.parent as IrSymbolOwner
return visitedStack.contains(parent) && expression.value.accept(this, data)
val property = expression.symbol.owner.correspondingPropertySymbol?.owner
val parent = expression.symbol.owner.parent as IrDeclarationContainer
val setter = parent.declarations.filterIsInstance<IrProperty>().single { it == property }.setter ?: return false
return visitedStack.contains(setter) && expression.value.accept(this, data)
}
override fun visitConstructorCall(expression: IrConstructorCall, data: Nothing?): Boolean {
return visitConstructor(expression)
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: Nothing?): Boolean {
if (expression.symbol.owner.returnType.isAny()) return true
return visitConstructor(expression)
}
override fun visitEnumConstructorCall(expression: IrEnumConstructorCall, data: Nothing?): Boolean {
return visitConstructor(expression)
}
override fun visitFunctionReference(expression: IrFunctionReference, data: Nothing?): Boolean {
return expression.asVisited {
expression.symbol.owner.isMarkedAsCompileTime() && expression.symbol.owner.body?.accept(this, data) == true
override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall, data: Nothing?): Boolean {
val irClass = expression.classSymbol.owner
val classProperties = irClass.declarations.filterIsInstance<IrProperty>()
val anonymousInitializer = irClass.declarations.filterIsInstance<IrAnonymousInitializer>().filter { !it.isStatic }
return anonymousInitializer.all { init -> init.body.accept(this, data) } && classProperties.all {
val propertyInitializer = it.backingField?.initializer?.expression
if ((propertyInitializer as? IrGetValue)?.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER) return@all true
return@all (propertyInitializer?.accept(this, data) != false)
}
}
override fun visitFunctionReference(expression: IrFunctionReference, data: Nothing?): Boolean {
val owner = expression.symbol.owner
if (!mode.canEvaluateFunction(owner)) return false
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, null) ?: true
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, null) ?: true
val bodyComputable = owner.asVisited { if (mode.canEvaluateBody(owner)) owner.body?.accept(this, null) ?: true else true }
return dispatchReceiverComputable && extensionReceiverComputable && bodyComputable
}
override fun visitFunctionExpression(expression: IrFunctionExpression, data: Nothing?): Boolean {
val isLambda = expression.origin == IrStatementOrigin.LAMBDA || expression.origin == IrStatementOrigin.ANONYMOUS_FUNCTION
val isCompileTime = expression.function.isMarkedAsCompileTime()
val isCompileTime = mode.canEvaluateFunction(expression.function)
return expression.function.asVisited {
if (isLambda || isCompileTime) expression.function.body?.accept(this, data) == true else false
}
@@ -263,4 +252,18 @@ class IrCompileTimeChecker(
override fun visitThrow(expression: IrThrow, data: Nothing?): Boolean {
return expression.value.accept(this, data)
}
override fun visitPropertyReference(expression: IrPropertyReference, data: Nothing?): Boolean {
return mode.canEvaluateFunction(expression.getter!!.owner)
}
override fun visitClassReference(expression: IrClassReference, data: Nothing?): Boolean {
return with(mode) {
when (this) {
EvaluationMode.FULL -> true
EvaluationMode.WITH_ANNOTATIONS -> (expression.symbol.owner as IrClass).isMarkedAsCompileTime()
EvaluationMode.ONLY_BUILTINS -> false
}
}
}
}

View File

@@ -0,0 +1,14 @@
/*
* 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.ir.interpreter.exceptions
open class InterpreterError(override val message: String) : InterpreterException()
class InterpreterMethodNotFoundError(override val message: String) : InterpreterError(message)
class InterpreterTimeOutError : InterpreterError("Exceeded execution limit of constexpr expression")
class InterpreterEmptyReturnStackError : InterpreterError("Return values stack is empty")

View File

@@ -5,5 +5,4 @@
package org.jetbrains.kotlin.ir.interpreter.exceptions
open class InterpreterException(override val message: String) : Exception(message) {
}
abstract class InterpreterException : Exception()

View File

@@ -1,9 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.interpreter.exceptions
class InterpreterMethodNotFoundException(override val message: String) : InterpreterException(message) {
}

View File

@@ -1,10 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.interpreter.exceptions
class InterpreterTimeOutException : InterpreterException("Exceeded execution limit of constexpr expression") {
}

View File

@@ -5,25 +5,25 @@
package org.jetbrains.kotlin.ir.interpreter.intrinsics
import org.jetbrains.kotlin.ir.interpreter.ExecutionResult
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterMethodNotFoundException
import org.jetbrains.kotlin.ir.interpreter.stack.Stack
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.interpreter.Instruction
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterMethodNotFoundError
internal class IntrinsicEvaluator {
fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
internal object IntrinsicEvaluator {
fun unwindInstructions(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
return when {
EmptyArray.equalTo(irFunction) -> EmptyArray.evaluate(irFunction, stack, interpret)
ArrayOf.equalTo(irFunction) -> ArrayOf.evaluate(irFunction, stack, interpret)
ArrayOfNulls.equalTo(irFunction) -> ArrayOfNulls.evaluate(irFunction, stack, interpret)
EnumValues.equalTo(irFunction) -> EnumValues.evaluate(irFunction, stack, interpret)
EnumValueOf.equalTo(irFunction) -> EnumValueOf.evaluate(irFunction, stack, interpret)
RegexReplace.equalTo(irFunction) -> RegexReplace.evaluate(irFunction, stack, interpret)
EnumHashCode.equalTo(irFunction) -> EnumHashCode.evaluate(irFunction, stack, interpret)
JsPrimitives.equalTo(irFunction) -> JsPrimitives.evaluate(irFunction, stack, interpret)
ArrayConstructor.equalTo(irFunction) -> ArrayConstructor.evaluate(irFunction, stack, interpret)
else -> throw InterpreterMethodNotFoundException("Method ${irFunction.name} hasn't implemented")
EmptyArray.equalTo(irFunction) -> EmptyArray.unwind(irFunction, environment)
ArrayOf.equalTo(irFunction) -> ArrayOf.unwind(irFunction, environment)
ArrayOfNulls.equalTo(irFunction) -> ArrayOfNulls.unwind(irFunction, environment)
EnumValues.equalTo(irFunction) -> EnumValues.unwind(irFunction, environment)
EnumValueOf.equalTo(irFunction) -> EnumValueOf.unwind(irFunction, environment)
EnumHashCode.equalTo(irFunction) -> EnumHashCode.unwind(irFunction, environment)
JsPrimitives.equalTo(irFunction) -> JsPrimitives.unwind(irFunction, environment)
ArrayConstructor.equalTo(irFunction) -> ArrayConstructor.unwind(irFunction, environment)
SourceLocation.equalTo(irFunction) -> SourceLocation.unwind(irFunction, environment)
AssertIntrinsic.equalTo(irFunction) -> AssertIntrinsic.unwind(irFunction, environment)
else -> throw InterpreterMethodNotFoundError("Method ${irFunction.name} hasn't implemented")
}
}
}

View File

@@ -5,20 +5,36 @@
package org.jetbrains.kotlin.ir.interpreter.intrinsics
import org.jetbrains.kotlin.ir.interpreter.*
import org.jetbrains.kotlin.ir.interpreter.stack.Stack
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.interpreter.*
import org.jetbrains.kotlin.ir.interpreter.state.*
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KFunctionState
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KTypeState
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.types.impl.buildSimpleType
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.types.isArray
import org.jetbrains.kotlin.ir.types.isCharArray
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.types.Variance
internal sealed class IntrinsicBase {
abstract fun equalTo(irFunction: IrFunction): Boolean
abstract fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult
abstract fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment)
abstract fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction>
fun customEvaluateInstruction(irFunction: IrFunction, environment: IrInterpreterEnvironment): CustomInstruction {
return CustomInstruction {
evaluate(irFunction, environment)
environment.callStack.dropFrameAndCopyResult()
}
}
}
internal object EmptyArray : IntrinsicBase() {
@@ -27,10 +43,13 @@ internal object EmptyArray : IntrinsicBase() {
return fqName in setOf("kotlin.emptyArray", "kotlin.ArrayIntrinsicsKt.emptyArray")
}
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
val typeArguments = irFunction.typeParameters.map { stack.getVariable(it.symbol) }
stack.pushReturnValue(emptyArray<Any?>().toState(irFunction.returnType).apply { addTypeArguments(typeArguments) })
return Next
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
return listOf(customEvaluateInstruction(irFunction, environment))
}
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
val returnType = environment.callStack.getVariable(irFunction.symbol).state as KTypeState
environment.callStack.pushState(emptyArray<Any?>().toState(returnType.irType))
}
}
@@ -40,11 +59,14 @@ internal object ArrayOf : IntrinsicBase() {
return fqName == "kotlin.arrayOf"
}
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
val array = irFunction.getArgsForMethodInvocation(stack.getAll()).toTypedArray()
val typeArguments = irFunction.typeParameters.map { stack.getVariable(it.symbol) }
stack.pushReturnValue(array.toState(irFunction.returnType).apply { addTypeArguments(typeArguments) })
return Next
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
return listOf(customEvaluateInstruction(irFunction, environment))
}
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
val elementsSymbol = irFunction.valueParameters.single().symbol
val varargVariable = environment.callStack.getVariable(elementsSymbol).state
environment.callStack.pushState(varargVariable)
}
}
@@ -54,12 +76,19 @@ internal object ArrayOfNulls : IntrinsicBase() {
return fqName == "kotlin.arrayOfNulls"
}
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
val size = stack.getVariable(irFunction.valueParameters.first().symbol).state.asInt()
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
return listOf(customEvaluateInstruction(irFunction, environment))
}
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
val size = environment.callStack.getVariable(irFunction.valueParameters.first().symbol).state.asInt()
val array = arrayOfNulls<Any?>(size)
val typeArguments = irFunction.typeParameters.map { stack.getVariable(it.symbol) }
stack.pushReturnValue(array.toState(irFunction.returnType).apply { addTypeArguments(typeArguments) })
return Next
val typeArgument = irFunction.typeParameters.map { environment.callStack.getVariable(it.symbol) }.single().state as KTypeState
val returnType = (irFunction.returnType as IrSimpleType).buildSimpleType {
arguments = listOf(makeTypeProjection(typeArgument.irType, Variance.INVARIANT))
}
environment.callStack.pushState(array.toState(returnType))
}
}
@@ -69,16 +98,28 @@ internal object EnumValues : IntrinsicBase() {
return (fqName == "kotlin.enumValues" || fqName.endsWith(".values")) && irFunction.valueParameters.isEmpty()
}
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
val enumClass = when (irFunction.fqNameWhenAvailable.toString()) {
"kotlin.enumValues" -> stack.getVariable(irFunction.typeParameters.first().symbol).state.irClass
private fun getEnumClass(irFunction: IrFunction, environment: IrInterpreterEnvironment): IrClass {
return when (irFunction.fqNameWhenAvailable.toString()) {
"kotlin.enumValues" -> {
val kType = environment.callStack.getVariable(irFunction.typeParameters.first().symbol).state as KTypeState
kType.irType.classOrNull!!.owner
}
else -> irFunction.parent as IrClass
}
}
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
val enumClass = getEnumClass(irFunction, environment)
val enumEntries = enumClass.declarations.filterIsInstance<IrEnumEntry>()
.map { entry -> entry.interpret().check { return it }.let { stack.popReturnValue() as Common } }
stack.pushReturnValue(enumEntries.toTypedArray().toState(irFunction.returnType))
return Next
return listOf(customEvaluateInstruction(irFunction, environment)) + enumEntries.reversed().map { CompoundInstruction(it) }
}
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
val enumClass = getEnumClass(irFunction, environment)
val enumEntries = enumClass.declarations.filterIsInstance<IrEnumEntry>().map { environment.mapOfEnums[it.symbol] }
environment.callStack.pushState(enumEntries.toTypedArray().toState(irFunction.returnType))
}
}
@@ -88,39 +129,34 @@ internal object EnumValueOf : IntrinsicBase() {
return (fqName == "kotlin.enumValueOf" || fqName.endsWith(".valueOf")) && irFunction.valueParameters.size == 1
}
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
val enumClass = when (irFunction.fqNameWhenAvailable.toString()) {
"kotlin.enumValueOf" -> stack.getVariable(irFunction.typeParameters.first().symbol).state.irClass
private fun getEnumClass(irFunction: IrFunction, environment: IrInterpreterEnvironment): IrClass {
return when (irFunction.fqNameWhenAvailable.toString()) {
"kotlin.enumValueOf" -> {
val kType = environment.callStack.getVariable(irFunction.typeParameters.first().symbol).state as KTypeState
kType.irType.classOrNull!!.owner
}
else -> irFunction.parent as IrClass
}
val enumEntryName = stack.getVariable(irFunction.valueParameters.first().symbol).state.asString()
}
private fun getEnumEntryByName(irFunction: IrFunction, environment: IrInterpreterEnvironment): IrEnumEntry? {
val enumClass = getEnumClass(irFunction, environment)
val enumEntryName = environment.callStack.getVariable(irFunction.valueParameters.first().symbol).state.asString()
val enumEntry = enumClass.declarations.filterIsInstance<IrEnumEntry>().singleOrNull { it.name.asString() == enumEntryName }
enumEntry?.interpret()?.check { return it }
?: throw IllegalArgumentException("No enum constant ${enumClass.fqNameWhenAvailable}.$enumEntryName")
return Next
}
}
internal object RegexReplace : IntrinsicBase() {
override fun equalTo(irFunction: IrFunction): Boolean {
val fqName = irFunction.fqNameWhenAvailable.toString()
return fqName == "kotlin.text.Regex.replace" && irFunction.valueParameters.size == 2
}
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
val states = stack.getAll().map { it.state }
val regex = states.filterIsInstance<Wrapper>().single().value as Regex
val input = states.filterIsInstance<Primitive<*>>().single().asString()
val transform = states.filterIsInstance<Lambda>().single().irFunction
val matchResultParameter = transform.valueParameters.single()
val result = regex.replace(input) {
val itAsState = Variable(matchResultParameter.symbol, Wrapper(it, matchResultParameter.type.classOrNull!!.owner))
stack.newFrame(initPool = listOf(itAsState)) { transform.interpret() }//.check { return it }
stack.popReturnValue().asString()
if (enumEntry == null) {
IllegalArgumentException("No enum constant ${enumClass.fqNameWhenAvailable}.$enumEntryName").handleUserException(environment)
}
stack.pushReturnValue(result.toState(irFunction.returnType))
return Next
return enumEntry
}
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
val enumEntry = getEnumEntryByName(irFunction, environment) ?: return emptyList()
return listOf(customEvaluateInstruction(irFunction, environment), CompoundInstruction(enumEntry))
}
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
val enumEntry = getEnumEntryByName(irFunction, environment)!!
environment.callStack.pushState(environment.mapOfEnums[enumEntry.symbol]!!)
}
}
@@ -130,10 +166,13 @@ internal object EnumHashCode : IntrinsicBase() {
return fqName == "kotlin.Enum.hashCode"
}
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
val hashCode = stack.getAll().single().state.hashCode()
stack.pushReturnValue(hashCode.toState(irFunction.returnType))
return Next
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
return listOf(customEvaluateInstruction(irFunction, environment))
}
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
val hashCode = environment.callStack.getVariable(irFunction.dispatchReceiverParameter!!.symbol).state.hashCode()
environment.callStack.pushState(hashCode.toState(irFunction.returnType))
}
}
@@ -143,19 +182,22 @@ internal object JsPrimitives : IntrinsicBase() {
return fqName == "kotlin.Long.<init>" || fqName == "kotlin.Char.<init>"
}
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
return listOf(customEvaluateInstruction(irFunction, environment))
}
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
when (irFunction.fqNameWhenAvailable.toString()) {
"kotlin.Long.<init>" -> {
val low = stack.getVariable(irFunction.valueParameters[0].symbol).state.asInt()
val high = stack.getVariable(irFunction.valueParameters[1].symbol).state.asInt()
stack.pushReturnValue((high.toLong().shl(32) + low).toState(irFunction.returnType))
val low = environment.callStack.getVariable(irFunction.valueParameters[0].symbol).state.asInt()
val high = environment.callStack.getVariable(irFunction.valueParameters[1].symbol).state.asInt()
environment.callStack.pushState((high.toLong().shl(32) + low).toState(irFunction.returnType))
}
"kotlin.Char.<init>" -> {
val value = stack.getVariable(irFunction.valueParameters[0].symbol).state.asInt()
stack.pushReturnValue(value.toChar().toState(irFunction.returnType))
val value = environment.callStack.getVariable(irFunction.valueParameters[0].symbol).state.asInt()
environment.callStack.pushState(value.toChar().toState(irFunction.returnType))
}
}
return Next
}
}
@@ -165,28 +207,87 @@ internal object ArrayConstructor : IntrinsicBase() {
return fqName.matches("kotlin\\.(Byte|Char|Short|Int|Long|Float|Double|Boolean|)Array\\.<init>".toRegex())
}
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
if (irFunction.valueParameters.size == 1) return listOf(customEvaluateInstruction(irFunction, environment))
val instructions = mutableListOf<Instruction>(customEvaluateInstruction(irFunction, environment))
val sizeDescriptor = irFunction.valueParameters[0].symbol
val size = stack.getVariable(sizeDescriptor).state.asInt()
val arrayValue = MutableList<Any>(size) { 0 }
val size = environment.callStack.getVariable(sizeDescriptor).state.asInt()
val initDescriptor = irFunction.valueParameters[1].symbol
val initLambda = environment.callStack.getVariable(initDescriptor).state as KFunctionState
environment.callStack.loadUpValues(initLambda)
val function = initLambda.irFunction as IrSimpleFunction
val index = initLambda.irFunction.valueParameters.single()
for (i in 0 until size) {
val call = IrCallImpl.fromSymbolOwner(0, 0, function.returnType, function.symbol)
call.putValueArgument(0, IrConstImpl.int(0, 0, index.type, i))
instructions += CompoundInstruction(call)
}
return instructions
}
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
val sizeDescriptor = irFunction.valueParameters[0].symbol
val size = environment.callStack.getVariable(sizeDescriptor).state.asInt()
val arrayValue = MutableList<Any?>(size) { if (irFunction.returnType.isCharArray()) 0.toChar() else 0 }
if (irFunction.valueParameters.size == 2) {
val initDescriptor = irFunction.valueParameters[1].symbol
val initLambda = stack.getVariable(initDescriptor).state as Lambda
val index = initLambda.irFunction.valueParameters.single()
val nonLocalDeclarations = initLambda.extractNonLocalDeclarations()
for (i in 0 until size) {
val indexVar = listOf(Variable(index.symbol, i.toState(index.type)))
// TODO throw exception if label != RETURN
stack.newFrame(
asSubFrame = initLambda.irFunction.isLocal || initLambda.irFunction.isInline,
initPool = nonLocalDeclarations + indexVar
) { initLambda.irFunction.body!!.interpret() }.check(ReturnLabel.RETURN) { return it }
arrayValue[i] = stack.popReturnValue().let { (it as? Wrapper)?.value ?: (it as? Primitive<*>)?.value ?: it }
arrayValue[i] = environment.callStack.popState().let {
// TODO wrap
when (it) {
is Wrapper -> it.value
is Primitive<*> -> if (it.type.isArray() || it.type.isPrimitiveArray()) it else it.value
else -> it
}
}
}
}
stack.pushReturnValue(arrayValue.toPrimitiveStateArray(irFunction.parentAsClass.defaultType))
return Next
val type = environment.callStack.getVariable(irFunction.symbol).state as KTypeState
environment.callStack.pushState(arrayValue.toPrimitiveStateArray(type.irType))
}
}
internal object SourceLocation : IntrinsicBase() {
override fun equalTo(irFunction: IrFunction): Boolean {
val fqName = irFunction.fqNameWhenAvailable.toString()
return fqName == "kotlin.experimental.sourceLocation" || fqName == "kotlin.experimental.SourceLocationKt.sourceLocation"
}
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
return listOf(customEvaluateInstruction(irFunction, environment))
}
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
environment.callStack.pushState(environment.callStack.getFileAndPositionInfo().toState(irFunction.returnType))
}
}
internal object AssertIntrinsic : IntrinsicBase() {
override fun equalTo(irFunction: IrFunction): Boolean {
val fqName = irFunction.fqNameWhenAvailable.toString()
return fqName == "kotlin.PreconditionsKt.assert"
}
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
if (irFunction.valueParameters.size == 1) return listOf(customEvaluateInstruction(irFunction, environment))
val messageLambda = environment.callStack.getVariable(irFunction.valueParameters.last().symbol).state as KFunctionState
val function = messageLambda.irFunction as IrSimpleFunction
val call = IrCallImpl.fromSymbolOwner(0, 0, function.returnType, function.symbol)
return listOf(customEvaluateInstruction(irFunction, environment), CompoundInstruction(call))
}
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
val value = environment.callStack.getVariable(irFunction.valueParameters.first().symbol).state.asBoolean()
if (value) return
when (irFunction.valueParameters.size) {
1 -> AssertionError("Assertion failed").handleUserException(environment)
2 -> AssertionError(environment.callStack.popState().asString()).handleUserException(environment)
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* 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.ir.interpreter.proxy
import org.jetbrains.kotlin.ir.interpreter.*
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.getDispatchReceiver
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.Common
import org.jetbrains.kotlin.ir.interpreter.toState
import org.jetbrains.kotlin.ir.util.isFakeOverriddenFromAny
/**
* calledFromBuiltIns - used to avoid cyclic calls. For example:
* override fun toString(): String {
* return super.toString()
* }
*/
internal class CommonProxy private constructor(
override val state: Common, override val callInterceptor: CallInterceptor, private val calledFromBuiltIns: Boolean = false
) : Proxy {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Proxy) return false
val valueArguments = mutableListOf<Variable>()
val equalsFun = state.getEqualsFunction()
if (equalsFun.isFakeOverriddenFromAny() || calledFromBuiltIns) return this.state === other.state
equalsFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
valueArguments.add(Variable(equalsFun.valueParameters.single().symbol, other.state))
return callInterceptor.interceptProxy(equalsFun, valueArguments) as Boolean
}
override fun hashCode(): Int {
val valueArguments = mutableListOf<Variable>()
val hashCodeFun = state.getHashCodeFunction()
if (hashCodeFun.isFakeOverriddenFromAny() || calledFromBuiltIns) return System.identityHashCode(state)
hashCodeFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
return callInterceptor.interceptProxy(hashCodeFun, valueArguments) as Int
}
override fun toString(): String {
val valueArguments = mutableListOf<Variable>()
val toStringFun = state.getToStringFunction()
if (toStringFun.isFakeOverriddenFromAny() || calledFromBuiltIns) {
return "${state.irClass.internalName()}@" + hashCode().toString(16).padStart(8, '0')
}
toStringFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
return callInterceptor.interceptProxy(toStringFun, valueArguments) as String
}
companion object {
internal fun Common.asProxy(
callInterceptor: CallInterceptor, extendFrom: Class<*>? = null, calledFromBuiltIns: Boolean = false
): Any {
val commonProxy = CommonProxy(this, callInterceptor, calledFromBuiltIns)
val interfaces = when (extendFrom) {
null, Object::class.java -> arrayOf(Proxy::class.java)
else -> arrayOf(extendFrom, Proxy::class.java)
}
return java.lang.reflect.Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), interfaces)
{ /*proxy*/_, method, args ->
when {
method.declaringClass == Proxy::class.java && method.name == "getState" -> commonProxy.state
method.declaringClass == Proxy::class.java && method.name == "getCallInterceptor" -> commonProxy.callInterceptor
method.name == "equals" && method.parameterTypes.single().isObject() -> commonProxy.equals(args.single())
method.name == "hashCode" && method.parameterTypes.isEmpty() -> commonProxy.hashCode()
method.name == "toString" && method.parameterTypes.isEmpty() -> commonProxy.toString()
else -> {
val irFunction = commonProxy.state.getIrFunction(method)
?: throw AssertionError("Cannot find method $method in ${commonProxy.state}")
val valueArguments = mutableListOf<Variable>()
valueArguments += Variable(irFunction.getDispatchReceiver()!!, commonProxy.state)
valueArguments += irFunction.valueParameters
.mapIndexed { index, parameter -> Variable(parameter.symbol, args[index].toState(parameter.type)) }
callInterceptor.interceptProxy(irFunction, valueArguments, method.returnType)
}
}
}
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.ir.interpreter.proxy
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.proxy.CommonProxy.Companion.asProxy
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.ReflectionProxy.Companion.asProxy
import org.jetbrains.kotlin.ir.interpreter.state.*
import org.jetbrains.kotlin.ir.interpreter.state.reflection.ReflectionState
internal interface Proxy {
val state: State
val callInterceptor: CallInterceptor
override fun equals(other: Any?): Boolean
override fun hashCode(): Int
override fun toString(): String
}
/**
* Prepare state object to be passed in outer world
*/
internal fun State.wrap(callInterceptor: CallInterceptor, extendFrom: Class<*>? = null, calledFromBuiltIns: Boolean = false): Any? {
return when (this) {
is ExceptionState -> this
is Wrapper -> this.value
is Primitive<*> -> this.value
is Common -> this.asProxy(callInterceptor, extendFrom, calledFromBuiltIns)
is ReflectionState -> this.asProxy(callInterceptor)
else -> throw AssertionError("${this::class} is unsupported as argument for wrap function")
}
}
internal fun Class<*>.isObject(): Boolean {
return this.name == "java.lang.Object"
}

View File

@@ -0,0 +1,119 @@
/*
* 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.ir.interpreter.proxy.reflection
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KPropertyState
import org.jetbrains.kotlin.ir.types.IrType
import kotlin.reflect.*
internal abstract class AbstractKPropertyProxy(
override val state: KPropertyState, override val callInterceptor: CallInterceptor
) : ReflectionProxy, KProperty<Any?> {
protected val propertyType: IrType
get() = state.property.getter!!.returnType
override val isAbstract: Boolean
get() = state.property.modality == Modality.ABSTRACT
override val isConst: Boolean
get() = state.property.isConst
override val isFinal: Boolean
get() = state.property.modality == Modality.FINAL
override val isLateinit: Boolean
get() = state.property.isLateinit
override val isOpen: Boolean
get() = state.property.modality == Modality.OPEN
override val isSuspend: Boolean
get() = false
override val name: String
get() = state.property.name.asString()
override val annotations: List<Annotation>
get() = TODO("not implemented")
override val parameters: List<KParameter>
get() = state.getParameters(callInterceptor)
override val returnType: KType
get() = state.getReturnType(callInterceptor)
override val typeParameters: List<KTypeParameter>
get() = listOf()
override val visibility: KVisibility?
get() = state.property.visibility.toKVisibility()
override fun call(vararg args: Any?): Any? = getter.call(*args)
override fun callBy(args: Map<KParameter, Any?>): Any? = getter.callBy(args)
protected fun checkArguments(expected: Int, actual: Int) {
if (expected != actual) {
throw IllegalArgumentException("Callable expects $expected arguments, but $actual were provided.")
}
}
abstract inner class Getter(val getter: IrSimpleFunction) : KProperty.Getter<Any?> {
override val property: KProperty<Any?> = this@AbstractKPropertyProxy
override val name: String = "<get-${this@AbstractKPropertyProxy.name.capitalize()}>"
override val annotations: List<Annotation>
get() = this@AbstractKPropertyProxy.annotations
override val parameters: List<KParameter>
get() = this@AbstractKPropertyProxy.parameters
override val returnType: KType
get() = this@AbstractKPropertyProxy.returnType
override val typeParameters: List<KTypeParameter>
get() = this@AbstractKPropertyProxy.typeParameters
override val isInline: Boolean = getter.isInline
override val isExternal: Boolean = getter.isExternal
override val isOperator: Boolean = getter.isOperator
override val isInfix: Boolean = getter.isInfix
override val isSuspend: Boolean = getter.isSuspend
override val visibility: KVisibility? = getter.visibility.toKVisibility()
override val isFinal: Boolean = getter.modality == Modality.FINAL
override val isOpen: Boolean = getter.modality == Modality.OPEN
override val isAbstract: Boolean = getter.modality == Modality.ABSTRACT
}
abstract inner class Setter(val setter: IrSimpleFunction) : KMutableProperty.Setter<Any?> {
override val property: KProperty<Any?> = this@AbstractKPropertyProxy
override val name: String = "<set-${this@AbstractKPropertyProxy.name.capitalize()}>"
override val annotations: List<Annotation>
get() = this@AbstractKPropertyProxy.annotations
override val parameters: List<KParameter>
get() = this@AbstractKPropertyProxy.parameters
override val returnType: KType
get() = this@AbstractKPropertyProxy.returnType
override val typeParameters: List<KTypeParameter>
get() = this@AbstractKPropertyProxy.typeParameters
override val isInline: Boolean = setter.isInline
override val isExternal: Boolean = setter.isExternal
override val isOperator: Boolean = setter.isOperator
override val isInfix: Boolean = setter.isInfix
override val isSuspend: Boolean = setter.isSuspend
override val visibility: KVisibility? = setter.visibility.toKVisibility()
override val isFinal: Boolean = setter.modality == Modality.FINAL
override val isOpen: Boolean = setter.modality == Modality.OPEN
override val isAbstract: Boolean = setter.modality == Modality.ABSTRACT
}
override fun equals(other: Any?): Boolean {
if (other !is AbstractKPropertyProxy) return false
return state == other.state
}
override fun hashCode(): Int {
return state.hashCode()
}
override fun toString(): String {
return state.toString()
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.ir.interpreter.proxy.reflection
import kotlin.jvm.functions.FunctionN
import kotlin.reflect.KCallable
// copied from core/reflection.jvm/src/kotlin/reflect/jvm/internal/FunctionWithAllInvokes.kt
// additionally added FunctionN
internal interface FunctionWithAllInvokes :
// (0..22).forEach { n -> println("Function$n<" + (0..n).joinToString { "Any?" } + ">,") }
Function0<Any?>,
Function1<Any?, Any?>,
Function2<Any?, Any?, Any?>,
Function3<Any?, Any?, Any?, Any?>,
Function4<Any?, Any?, Any?, Any?, Any?>,
Function5<Any?, Any?, Any?, Any?, Any?, Any?>,
Function6<Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
Function7<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
Function8<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
Function9<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
Function10<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
Function11<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
Function12<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
Function13<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
Function14<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
Function15<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
Function16<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
Function17<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
Function18<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
Function19<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
Function20<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
Function21<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
Function22<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?>,
KCallable<Any?>, FunctionN<Any?>
{
// (0..22).forEach { n -> println("override fun invoke(" + (1..n).joinToString { "p$it: Any?" } + "): Any? = call(" + (1..n).joinToString { "p$it" } + ")") }
override fun invoke(): Any? = call()
override fun invoke(p1: Any?): Any? = call(p1)
override fun invoke(p1: Any?, p2: Any?): Any? = call(p1, p2)
override fun invoke(p1: Any?, p2: Any?, p3: Any?): Any? = call(p1, p2, p3)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?): Any? = call(p1, p2, p3, p4)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?): Any? = call(p1, p2, p3, p4, p5)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?): Any? = call(p1, p2, p3, p4, p5, p6)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?): Any? = call(p1, p2, p3, p4, p5, p6, p7)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?): Any? = call(p1, p2, p3, p4, p5, p6, p7, p8)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?): Any? = call(p1, p2, p3, p4, p5, p6, p7, p8, p9)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?): Any? = call(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?): Any? = call(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?): Any? = call(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?): Any? = call(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?): Any? = call(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?): Any? = call(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?, p16: Any?): Any? = call(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?, p16: Any?, p17: Any?): Any? = call(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?, p16: Any?, p17: Any?, p18: Any?): Any? = call(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?, p16: Any?, p17: Any?, p18: Any?, p19: Any?): Any? = call(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?, p16: Any?, p17: Any?, p18: Any?, p19: Any?, p20: Any?): Any? = call(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?, p16: Any?, p17: Any?, p18: Any?, p19: Any?, p20: Any?, p21: Any?): Any? = call(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21)
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?, p16: Any?, p17: Any?, p18: Any?, p19: Any?, p20: Any?, p21: Any?, p22: Any?): Any? = call(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22)
override fun invoke(vararg args: Any?): Any? = call(*args)
}

View File

@@ -0,0 +1,78 @@
/*
* 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.ir.interpreter.proxy.reflection
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.internalName
import org.jetbrains.kotlin.ir.interpreter.proxy.Proxy
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KClassState
import kotlin.reflect.*
internal class KClassProxy(
override val state: KClassState, override val callInterceptor: CallInterceptor
) : ReflectionProxy, KClass<Proxy> {
override val simpleName: String?
get() = state.classReference.name.takeIf { !it.isSpecial }?.asString()
override val qualifiedName: String?
get() = if (!state.classReference.name.isSpecial) state.classReference.internalName() else null
@Suppress("UNCHECKED_CAST")
override val constructors: Collection<KFunction<Proxy>>
get() = state.getConstructors(callInterceptor) as Collection<KFunction<Proxy>>
override val members: Collection<KCallable<*>>
get() = state.getMembers(callInterceptor)
override val nestedClasses: Collection<KClass<*>>
get() = TODO("Not yet implemented")
override val objectInstance: Proxy?
get() = TODO("Not yet implemented")
override val typeParameters: List<KTypeParameter>
get() = state.getTypeParameters(callInterceptor)
override val supertypes: List<KType>
get() = state.getSupertypes(callInterceptor)
override val sealedSubclasses: List<KClass<out Proxy>>
get() = TODO("Not yet implemented")
override val annotations: List<Annotation>
get() = TODO("Not yet implemented")
override val visibility: KVisibility?
get() = state.classReference.visibility.toKVisibility()
override val isFinal: Boolean
get() = state.classReference.modality == Modality.FINAL
override val isOpen: Boolean
get() = state.classReference.modality == Modality.OPEN
override val isAbstract: Boolean
get() = state.classReference.modality == Modality.ABSTRACT
override val isSealed: Boolean
get() = state.classReference.modality == Modality.SEALED
override val isData: Boolean
get() = state.classReference.isData
override val isInner: Boolean
get() = state.classReference.isInner
override val isCompanion: Boolean
get() = state.classReference.isCompanion
override val isFun: Boolean
get() = state.classReference.isFun
override fun isInstance(value: Any?): Boolean {
TODO("Not yet implemented")
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is KClassProxy) return false
return state == other.state
}
override fun hashCode(): Int {
return state.hashCode()
}
override fun toString(): String {
return state.toString()
}
}

View File

@@ -0,0 +1,84 @@
/*
* 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.ir.interpreter.proxy.reflection
import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KFunctionState
import org.jetbrains.kotlin.ir.interpreter.toState
import org.jetbrains.kotlin.ir.util.isSuspend
import kotlin.reflect.*
internal class KFunctionProxy(
override val state: KFunctionState, override val callInterceptor: CallInterceptor
) : ReflectionProxy, KFunction<Any?>, FunctionWithAllInvokes {
override val arity: Int = state.getArity() ?: BuiltInFunctionArity.BIG_ARITY
override val isInline: Boolean
get() = state.irFunction.isInline
override val isExternal: Boolean
get() = state.irFunction.isExternal
override val isOperator: Boolean
get() = state.irFunction is IrSimpleFunction && state.irFunction.isOperator
override val isInfix: Boolean
get() = state.irFunction is IrSimpleFunction && state.irFunction.isInfix
override val name: String
get() = state.irFunction.name.asString()
override val annotations: List<Annotation>
get() = TODO("Not yet implemented")
override val parameters: List<KParameter>
get() = state.getParameters(callInterceptor)
override val returnType: KType
get() = state.getReturnType(callInterceptor)
override val typeParameters: List<KTypeParameter>
get() = state.getTypeParameters(callInterceptor)
override fun call(vararg args: Any?): Any? {
// TODO check arity
var index = 0
val dispatchReceiver = state.irFunction.dispatchReceiverParameter?.let { Variable(it.symbol, args[index++].toState(it.type)) }
val extensionReceiver = state.irFunction.extensionReceiverParameter?.let { Variable(it.symbol, args[index++].toState(it.type)) }
val valueArguments = listOfNotNull(dispatchReceiver, extensionReceiver) +
state.irFunction.valueParameters.map { parameter -> Variable(parameter.symbol, args[index++].toState(parameter.type)) }
return callInterceptor.interceptProxy(state.irFunction, valueArguments)
}
override fun callBy(args: Map<KParameter, Any?>): Any? {
TODO("Not yet implemented")
}
override val visibility: KVisibility?
get() = state.irFunction.visibility.toKVisibility()
override val isFinal: Boolean
get() = state.irFunction is IrSimpleFunction && state.irFunction.modality == Modality.FINAL
override val isOpen: Boolean
get() = state.irFunction is IrSimpleFunction && state.irFunction.modality == Modality.OPEN
override val isAbstract: Boolean
get() = state.irFunction is IrSimpleFunction && state.irFunction.modality == Modality.ABSTRACT
override val isSuspend: Boolean
get() = state.irFunction.isSuspend
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is ReflectionProxy) return false
return state == other.state
}
override fun hashCode(): Int {
return state.hashCode()
}
override fun toString(): String {
return state.toString()
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.ir.interpreter.proxy.reflection
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KParameterState
import kotlin.reflect.KParameter
import kotlin.reflect.KType
internal class KParameterProxy(override val state: KParameterState, override val callInterceptor: CallInterceptor) : ReflectionProxy, KParameter {
override val index: Int
get() = state.index
override val name: String?
get() = if (kind == KParameter.Kind.VALUE) state.irParameter.name.asString() else null
override val type: KType
get() = state.getType(callInterceptor)
override val kind: KParameter.Kind
get() = state.kind
override val isOptional: Boolean
get() = state.irParameter.defaultValue != null
override val isVararg: Boolean
get() = state.irParameter.varargElementType != null
override val annotations: List<Annotation>
get() = TODO("Not yet implemented")
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is KParameterProxy) return false
return state == other.state
}
override fun hashCode(): Int = state.hashCode()
override fun toString(): String = state.toString()
}

View File

@@ -0,0 +1,60 @@
/*
* 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.ir.interpreter.proxy.reflection
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KPropertyState
import org.jetbrains.kotlin.ir.interpreter.toState
import org.jetbrains.kotlin.ir.util.resolveFakeOverride
import kotlin.reflect.*
internal open class KProperty0Proxy(
override val state: KPropertyState, override val callInterceptor: CallInterceptor
) : AbstractKPropertyProxy(state, callInterceptor), KProperty0<Any?> {
override val getter: KProperty0.Getter<Any?>
get() = object : Getter(state.property.getter!!), KProperty0.Getter<Any?> {
override fun invoke(): Any? = call()
override fun call(vararg args: Any?): Any? {
checkArguments(0, args.size)
val actualPropertySymbol = state.property.resolveFakeOverride()?.symbol ?: state.property.symbol
return state.dispatchReceiver!!.getState(actualPropertySymbol)!!
}
override fun callBy(args: Map<KParameter, Any?>): Any? {
TODO("Not yet implemented")
}
}
override fun get(): Any? = getter.call()
override fun getDelegate(): Any? {
TODO("Not yet implemented")
}
override fun invoke(): Any? = getter.call()
}
internal class KMutableProperty0Proxy(
override val state: KPropertyState, override val callInterceptor: CallInterceptor
) : KProperty0Proxy(state, callInterceptor), KMutableProperty0<Any?> {
override val setter: KMutableProperty0.Setter<Any?> =
object : Setter(state.property.setter!!), KMutableProperty0.Setter<Any?> {
override fun invoke(p1: Any?) = call(p1)
override fun call(vararg args: Any?) {
checkArguments(1, args.size)
state.dispatchReceiver!!.setField(Variable(state.property.symbol, args.single().toState(propertyType)))
}
override fun callBy(args: Map<KParameter, Any?>) {
TODO("Not yet implemented")
}
}
override fun set(value: Any?) = setter.call(value)
}

View File

@@ -0,0 +1,64 @@
/*
* 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.ir.interpreter.proxy.reflection
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KPropertyState
import org.jetbrains.kotlin.ir.interpreter.toState
import kotlin.reflect.*
internal open class KProperty1Proxy(
override val state: KPropertyState, override val callInterceptor: CallInterceptor
) : AbstractKPropertyProxy(state, callInterceptor), KProperty1<Any?, Any?> {
override val getter: KProperty1.Getter<Any?, Any?>
get() = object : Getter(state.property.getter!!), KProperty1.Getter<Any?, Any?> {
override fun invoke(p1: Any?): Any? = call(p1)
override fun call(vararg args: Any?): Any? {
checkArguments(1, args.size)
val receiverParameter = getter.dispatchReceiverParameter ?: getter.extensionReceiverParameter
val receiver = Variable(receiverParameter!!.symbol, args[0].toState(receiverParameter.type))
return callInterceptor.interceptProxy(getter, listOf(receiver))
}
override fun callBy(args: Map<KParameter, Any?>): Any? {
TODO("Not yet implemented")
}
}
override fun get(receiver: Any?): Any? = getter.call(receiver)
override fun getDelegate(receiver: Any?): Any? {
TODO("Not yet implemented")
}
override fun invoke(p1: Any?): Any? = getter.call(p1)
}
internal class KMutableProperty1Proxy(
override val state: KPropertyState, override val callInterceptor: CallInterceptor
) : KProperty1Proxy(state, callInterceptor), KMutableProperty1<Any?, Any?> {
override val setter: KMutableProperty1.Setter<Any?, Any?> =
object : Setter(state.property.setter!!), KMutableProperty1.Setter<Any?, Any?> {
override fun invoke(p1: Any?, p2: Any?) = call(p1, p2)
override fun call(vararg args: Any?) {
checkArguments(2, args.size)
val receiverParameter = setter.dispatchReceiverParameter ?: setter.extensionReceiverParameter
val receiver = Variable(receiverParameter!!.symbol, args[0].toState(receiverParameter.type))
val valueParameter = setter.valueParameters.single()
val value = Variable(valueParameter.symbol, args[1].toState(valueParameter.type))
callInterceptor.interceptProxy(setter, listOf(receiver, value))
}
override fun callBy(args: Map<KParameter, Any?>) {
TODO("Not yet implemented")
}
}
override fun set(receiver: Any?, value: Any?) = setter.call(receiver, value)
}

View File

@@ -0,0 +1,63 @@
/*
* 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.ir.interpreter.proxy.reflection
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KPropertyState
import org.jetbrains.kotlin.ir.interpreter.toState
import kotlin.reflect.*
internal open class KProperty2Proxy(
override val state: KPropertyState, override val callInterceptor: CallInterceptor
) : AbstractKPropertyProxy(state, callInterceptor), KProperty2<Any?, Any?, Any?> {
override val getter: KProperty2.Getter<Any?, Any?, Any?>
get() = object : Getter(state.property.getter!!), KProperty2.Getter<Any?, Any?, Any?> {
override fun invoke(p1: Any?, p2: Any?): Any? = call(p1, p2)
override fun call(vararg args: Any?): Any? {
checkArguments(2, args.size)
val dispatch = Variable(getter.dispatchReceiverParameter!!.symbol, args[0].toState(getter.dispatchReceiverParameter!!.type))
val extension = Variable(getter.extensionReceiverParameter!!.symbol, args[1].toState(getter.extensionReceiverParameter!!.type))
return callInterceptor.interceptProxy(getter, listOf(dispatch, extension))
}
override fun callBy(args: Map<KParameter, Any?>): Any? {
TODO("Not yet implemented")
}
}
override fun get(receiver1: Any?, receiver2: Any?): Any? = getter.call(receiver1, receiver2)
override fun getDelegate(receiver1: Any?, receiver2: Any?): Any? {
TODO("Not yet implemented")
}
override fun invoke(p1: Any?, p2: Any?): Any? = getter.call(p1, p2)
}
internal class KMutableProperty2Proxy(
override val state: KPropertyState, override val callInterceptor: CallInterceptor
) : KProperty2Proxy(state, callInterceptor), KMutableProperty2<Any?, Any?, Any?> {
override val setter: KMutableProperty2.Setter<Any?, Any?, Any?>
get() = object : Setter(state.property.setter!!), KMutableProperty2.Setter<Any?, Any?, Any?> {
override fun invoke(p1: Any?, p2: Any?, p3: Any?) = call(p1, p2, p3)
override fun call(vararg args: Any?) {
checkArguments(3, args.size)
val receiver1 = args[0]
val receiver2 = args[1]
val value = args[2]
TODO("Not yet implemented, receiver1 = $receiver1, receiver2 = $receiver2, value = $value")
}
override fun callBy(args: Map<KParameter, Any?>) {
TODO("Not yet implemented")
}
}
override fun set(receiver1: Any?, receiver2: Any?, value: Any?) = setter.call(receiver1, receiver2, value)
}

View File

@@ -0,0 +1,39 @@
/*
* 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.ir.interpreter.proxy.reflection
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KTypeParameterState
import org.jetbrains.kotlin.types.Variance
import kotlin.reflect.*
internal class KTypeParameterProxy(
override val state: KTypeParameterState, override val callInterceptor: CallInterceptor
) : ReflectionProxy, KTypeParameter {
override val name: String
get() = state.irTypeParameter.name.asString()
override val upperBounds: List<KType>
get() = state.getUpperBounds(callInterceptor)
override val variance: KVariance
get() = when (state.irTypeParameter.variance) {
Variance.INVARIANT -> KVariance.INVARIANT
Variance.IN_VARIANCE -> KVariance.IN
Variance.OUT_VARIANCE -> KVariance.OUT
}
override val isReified: Boolean
get() = state.irTypeParameter.isReified
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is KTypeParameterProxy) return false
return state == other.state
}
override fun hashCode(): Int = state.hashCode()
override fun toString(): String = state.toString()
}

View File

@@ -0,0 +1,35 @@
/*
* 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.ir.interpreter.proxy.reflection
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KTypeState
import org.jetbrains.kotlin.ir.types.isMarkedNullable
import kotlin.reflect.KClassifier
import kotlin.reflect.KType
import kotlin.reflect.KTypeProjection
internal class KTypeProxy(override val state: KTypeState, override val callInterceptor: CallInterceptor) : ReflectionProxy, KType {
override val classifier: KClassifier?
get() = state.getClassifier(callInterceptor)
override val arguments: List<KTypeProjection>
get() = state.getArguments(callInterceptor)
override val isMarkedNullable: Boolean
get() = state.irType.isMarkedNullable()
override val annotations: List<Annotation>
get() = TODO("Not yet implemented")
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is KTypeProxy) return false
return state == other.state
}
override fun hashCode(): Int = state.hashCode()
override fun toString(): String = state.toString()
}

View File

@@ -0,0 +1,47 @@
/*
* 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.ir.interpreter.proxy.reflection
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.proxy.Proxy
import org.jetbrains.kotlin.ir.interpreter.state.reflection.*
import kotlin.reflect.KVisibility
internal interface ReflectionProxy : Proxy {
fun DescriptorVisibility.toKVisibility(): KVisibility? {
return when (this) {
DescriptorVisibilities.PUBLIC -> KVisibility.PUBLIC
DescriptorVisibilities.PROTECTED -> KVisibility.PROTECTED
DescriptorVisibilities.INTERNAL -> KVisibility.INTERNAL
DescriptorVisibilities.PRIVATE -> KVisibility.PRIVATE
else -> null
}
}
companion object {
internal fun ReflectionState.asProxy(callInterceptor: CallInterceptor): ReflectionProxy {
return when (this) {
is KPropertyState -> when {
this.isKMutableProperty0() -> KMutableProperty0Proxy(this, callInterceptor)
this.isKProperty0() -> KProperty0Proxy(this, callInterceptor)
this.isKMutableProperty1() -> KMutableProperty1Proxy(this, callInterceptor)
this.isKProperty1() -> KProperty1Proxy(this, callInterceptor)
this.isKMutableProperty2() -> KMutableProperty2Proxy(this, callInterceptor)
this.isKProperty2() -> KProperty2Proxy(this, callInterceptor)
else -> TODO()
}
is KFunctionState -> KFunctionProxy(this, callInterceptor)
is KClassState -> KClassProxy(this, callInterceptor)
is KTypeState -> KTypeProxy(this, callInterceptor)
is KTypeParameterState -> KTypeParameterProxy(this, callInterceptor)
is KParameterState -> KParameterProxy(this, callInterceptor)
else -> TODO("not supported reference state")
}
}
}
}

View File

@@ -0,0 +1,194 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.interpreter.stack
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.interpreter.CompoundInstruction
import org.jetbrains.kotlin.ir.interpreter.Instruction
import org.jetbrains.kotlin.ir.interpreter.SimpleInstruction
import org.jetbrains.kotlin.ir.interpreter.state.State
import org.jetbrains.kotlin.ir.interpreter.state.StateWithClosure
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.fileOrNull
internal class CallStack {
private val frames = mutableListOf<Frame>()
private fun getCurrentFrame() = frames.last()
internal fun getCurrentFrameOwner() = frames.last().currentSubFrameOwner
fun newFrame(frameOwner: IrElement, instructions: List<Instruction>, irFile: IrFile? = null) {
val newFrame = SubFrame(instructions.toMutableList(), frameOwner)
frames.add(Frame(newFrame, irFile))
}
fun newFrame(frameOwner: IrFunction, instructions: List<Instruction>) {
val newFrame = SubFrame(instructions.toMutableList(), frameOwner)
frames.add(Frame(newFrame, frameOwner.fileOrNull))
}
fun newSubFrame(frameOwner: IrElement, instructions: List<Instruction>) {
val newFrame = SubFrame(instructions.toMutableList(), frameOwner)
getCurrentFrame().addSubFrame(newFrame)
}
fun dropFrame() {
frames.removeLast()
}
fun dropFrameAndCopyResult() {
val result = peekState() ?: return dropFrame()
popState()
dropFrame()
pushState(result)
}
fun dropSubFrame() {
getCurrentFrame().removeSubFrame()
}
fun returnFromFrameWithResult(irReturn: IrReturn) {
val result = popState()
var frameOwner = getCurrentFrameOwner()
while (frameOwner != irReturn.returnTargetSymbol.owner) {
when (frameOwner) {
is IrTry -> {
dropSubFrame()
pushState(result)
addInstruction(SimpleInstruction(irReturn))
addInstruction(CompoundInstruction(frameOwner.finallyExpression))
return
}
is IrCatch -> {
val tryBlock = getCurrentFrame().dropInstructions()!!.element as IrTry// last instruction in `catch` block is `try`
dropSubFrame()
pushState(result)
addInstruction(SimpleInstruction(irReturn))
addInstruction(CompoundInstruction(tryBlock.finallyExpression))
return
}
else -> {
dropSubFrame()
if (getCurrentFrame().hasNoSubFrames() && frameOwner != irReturn.returnTargetSymbol.owner) dropFrame()
frameOwner = getCurrentFrameOwner()
}
}
}
dropFrame()
// check that last frame is not a function itself; use case for proxyInterpret
if (frames.size == 0) newFrame(irReturn, emptyList()) // just stub frame
pushState(result)
}
fun unrollInstructionsForBreakContinue(breakOrContinue: IrBreakContinue) {
var frameOwner = getCurrentFrameOwner()
while (frameOwner != breakOrContinue.loop) {
when (frameOwner) {
is IrTry -> {
addInstruction(CompoundInstruction(breakOrContinue))
addInstruction(SimpleInstruction(frameOwner))
return
}
is IrCatch -> {
val tryInstruction = getCurrentFrame().dropInstructions()!! // last instruction in `catch` block is `try`
addInstruction(CompoundInstruction(breakOrContinue))
addInstruction(tryInstruction)
return
}
else -> {
getCurrentFrame().removeSubFrameWithoutDataPropagation()
frameOwner = getCurrentFrameOwner()
}
}
}
when (breakOrContinue) {
is IrBreak -> getCurrentFrame().removeSubFrameWithoutDataPropagation() // drop loop
else -> addInstruction(CompoundInstruction(breakOrContinue.loop))
}
}
fun dropFramesUntilTryCatch() {
val exception = popState()
var frameOwner = getCurrentFrameOwner()
while (frames.isNotEmpty()) {
val frame = getCurrentFrame()
while (!frame.hasNoSubFrames()) {
frameOwner = frame.currentSubFrameOwner
when (frameOwner) {
is IrTry -> {
dropSubFrame() // drop all instructions that left
newSubFrame(frameOwner, listOf())
addInstruction(SimpleInstruction(frameOwner)) // to evaluate finally at the end
frameOwner.catches.reversed().forEach { addInstruction(CompoundInstruction(it)) }
pushState(exception)
return
}
is IrCatch -> {
// in case of exception in catch, drop everything except of last `try` instruction
addInstruction(frame.dropInstructions()!!)
pushState(exception)
return
}
else -> frame.removeSubFrameWithoutDataPropagation()
}
}
dropFrame()
}
if (frames.size == 0) newFrame(frameOwner, emptyList()) // just stub frame
pushState(exception)
}
fun hasNoInstructions() = frames.isEmpty() || (frames.size == 1 && frames.first().hasNoInstructions())
fun addInstruction(instruction: Instruction) {
getCurrentFrame().addInstruction(instruction)
}
fun popInstruction(): Instruction {
return getCurrentFrame().popInstruction()
}
fun pushState(state: State) {
getCurrentFrame().pushState(state)
}
fun popState(): State = getCurrentFrame().popState()
fun peekState(): State? = getCurrentFrame().peekState()
fun addVariable(variable: Variable) {
getCurrentFrame().addVariable(variable)
}
fun getVariable(symbol: IrSymbol): Variable = getCurrentFrame().getVariable(symbol)
fun storeUpValues(state: StateWithClosure) {
// TODO save only necessary declarations
state.upValues.addAll(getCurrentFrame().getAll().toMutableList())
}
fun loadUpValues(state: StateWithClosure) {
state.upValues.forEach { addVariable(it) }
}
fun copyUpValuesFromPreviousFrame() {
frames[frames.size - 2].getAll().forEach { addVariable(it) }
}
fun getStackTrace(): List<String> {
return frames.map { it.toString() }.filter { it != Frame.NOT_DEFINED }
}
fun getFileAndPositionInfo(): String {
return frames[frames.size - 2].getFileAndPositionInfo()
}
fun getStackCount(): Int = frames.size
}

View File

@@ -1,79 +1,142 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.interpreter.stack
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.interpreter.Instruction
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterError
import org.jetbrains.kotlin.ir.interpreter.state.State
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
internal interface Frame {
fun addVar(variable: Variable)
fun addAll(variables: List<Variable>)
fun getVariable(symbol: IrSymbol): Variable?
fun getAll(): List<Variable>
fun contains(symbol: IrSymbol): Boolean
fun pushReturnValue(state: State)
fun pushReturnValue(frame: Frame) // TODO rename to getReturnValueFrom
fun peekReturnValue(): State
fun popReturnValue(): State
fun hasReturnValue(): Boolean
internal class Frame(subFrame: SubFrame, val irFile: IrFile? = null) {
private val innerStack = mutableListOf(subFrame)
private var currentInstruction: Instruction? = null
val currentSubFrameOwner: IrElement
get() = getCurrentFrame().owner
companion object {
const val NOT_DEFINED = "Not defined"
}
private fun getCurrentFrame() = innerStack.last()
fun addSubFrame(frame: SubFrame) {
innerStack.add(frame)
}
fun removeSubFrame() {
getCurrentFrame().peekState()?.let { if (innerStack.size > 1) innerStack[innerStack.size - 2].pushState(it) }
removeSubFrameWithoutDataPropagation()
}
fun removeSubFrameWithoutDataPropagation() {
innerStack.removeLast()
}
fun hasNoSubFrames() = innerStack.isEmpty()
fun hasNoInstructions() = hasNoSubFrames() || (innerStack.size == 1 && innerStack.first().isEmpty())
fun addInstruction(instruction: Instruction) {
getCurrentFrame().pushInstruction(instruction)
}
fun popInstruction(): Instruction {
return getCurrentFrame().popInstruction().apply { currentInstruction = this }
}
fun dropInstructions() = getCurrentFrame().dropInstructions()
fun pushState(state: State) {
getCurrentFrame().pushState(state)
}
fun popState(): State = getCurrentFrame().popState()
fun peekState(): State? = getCurrentFrame().peekState()
fun addVariable(variable: Variable) {
getCurrentFrame().addVariable(variable)
}
fun getVariable(symbol: IrSymbol): Variable {
return innerStack.firstNotNullResult { it.getVariable(symbol) }
?: throw InterpreterError("$symbol not found") // TODO better message
}
fun getAll(): List<Variable> = innerStack.flatMap { it.getAll() }
private fun getLineNumberForCurrentInstruction(): String {
irFile ?: return ""
val frameOwner = currentInstruction?.element
return when {
frameOwner is IrExpression || (frameOwner is IrDeclaration && frameOwner.origin == IrDeclarationOrigin.DEFINED) ->
":${irFile.fileEntry.getLineNumber(frameOwner.startOffset) + 1}"
else -> ""
}
}
fun getFileAndPositionInfo(): String {
irFile ?: return NOT_DEFINED
val lineNum = getLineNumberForCurrentInstruction()
return "${irFile.name}$lineNum"
}
override fun toString(): String {
irFile ?: return NOT_DEFINED
val fileNameCapitalized = irFile.name.replace(".kt", "Kt").capitalize()
val entryPoint = innerStack.firstOrNull { it.owner is IrFunction }?.owner as? IrFunction
val lineNum = getLineNumberForCurrentInstruction()
return "at $fileNameCapitalized.${entryPoint?.fqNameWhenAvailable ?: "<clinit>"}(${irFile.name}$lineNum)"
}
}
// TODO replace exceptions with InterpreterException
internal class InterpreterFrame(
private val pool: MutableList<Variable> = mutableListOf(),
private val typeArguments: List<Variable> = listOf()
) : Frame {
private val returnStack: MutableList<State> = mutableListOf()
internal class SubFrame(private val instructions: MutableList<Instruction>, val owner: IrElement) {
private val memory = mutableListOf<Variable>()
private val dataStack = DataStack()
override fun addVar(variable: Variable) {
pool.add(variable)
fun isEmpty() = instructions.isEmpty()
fun pushInstruction(instruction: Instruction) {
instructions.add(0, instruction)
}
override fun addAll(variables: List<Variable>) {
pool.addAll(variables)
fun popInstruction(): Instruction {
return instructions.removeFirst()
}
override fun getVariable(symbol: IrSymbol): Variable? {
return (if (symbol is IrTypeParameterSymbol) typeArguments else pool).firstOrNull { it.symbol == symbol }
fun dropInstructions() = instructions.firstOrNull()?.apply { instructions.clear() }
fun pushState(state: State) {
dataStack.push(state)
}
override fun getAll(): List<Variable> {
return pool
fun popState(): State = dataStack.pop()
fun peekState(): State? = if (!dataStack.isEmpty()) dataStack.peek() else null
fun addVariable(variable: Variable) {
memory += variable
}
override fun contains(symbol: IrSymbol): Boolean {
return (typeArguments + pool).any { it.symbol == symbol }
}
override fun pushReturnValue(state: State) {
returnStack += state
}
override fun pushReturnValue(frame: Frame) {
if (frame.hasReturnValue()) this.pushReturnValue(frame.popReturnValue())
}
override fun hasReturnValue(): Boolean {
return returnStack.isNotEmpty()
}
override fun peekReturnValue(): State {
if (returnStack.isNotEmpty()) {
return returnStack.last()
}
throw NoSuchElementException("Return values stack is empty")
}
override fun popReturnValue(): State {
if (returnStack.isNotEmpty()) {
val item = returnStack.last()
returnStack.removeAt(returnStack.size - 1)
return item
}
throw NoSuchElementException("Return values stack is empty")
}
fun getVariable(symbol: IrSymbol): Variable? = memory.firstOrNull { it.symbol == symbol }
fun getAll(): List<Variable> = memory
}
private class DataStack {
private val stack = mutableListOf<State>()
fun isEmpty() = stack.isEmpty()
fun push(state: State) {
stack.add(state)
}
fun pop(): State = stack.removeLast()
fun peek(): State = stack.last()
}

View File

@@ -1,147 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.interpreter.stack
import org.jetbrains.kotlin.ir.interpreter.ExecutionResult
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterException
import org.jetbrains.kotlin.ir.interpreter.getCapitalizedFileName
import org.jetbrains.kotlin.ir.interpreter.state.State
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.name
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.fileEntry
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
internal interface Stack {
fun newFrame(asSubFrame: Boolean = false, initPool: List<Variable> = listOf(), block: () -> ExecutionResult): ExecutionResult
fun setCurrentFrameName(irFunction: IrFunction)
fun getStackTrace(): List<String>
fun clean()
fun addVar(variable: Variable)
fun addAll(variables: List<Variable>)
fun getVariable(symbol: IrSymbol): Variable
fun getAll(): List<Variable>
fun contains(symbol: IrSymbol): Boolean
fun hasReturnValue(): Boolean
fun pushReturnValue(state: State)
fun popReturnValue(): State
fun peekReturnValue(): State
}
internal class StackImpl : Stack {
private val frameList = mutableListOf(FrameContainer()) // first frame is default, it is easier to work when last() is not null
private fun getCurrentFrame() = frameList.last()
override fun newFrame(asSubFrame: Boolean, initPool: List<Variable>, block: () -> ExecutionResult): ExecutionResult {
val typeArgumentsPool = initPool.filter { it.symbol is IrTypeParameterSymbol }
val valueArguments = initPool.filter { it.symbol !is IrTypeParameterSymbol }
val newFrame = InterpreterFrame(valueArguments.toMutableList(), typeArgumentsPool)
if (asSubFrame) getCurrentFrame().addSubFrame(newFrame) else frameList.add(FrameContainer(newFrame))
return try {
block()
} finally {
if (asSubFrame) getCurrentFrame().removeSubFrame() else removeLastFrame()
}
}
private fun removeLastFrame() {
if (frameList.size > 1 && getCurrentFrame().hasReturnValue()) frameList[frameList.lastIndex - 1].pushReturnValue(getCurrentFrame())
frameList.removeAt(frameList.lastIndex)
}
override fun setCurrentFrameName(irFunction: IrFunction) {
val fileName = irFunction.file.name
val fileNameCapitalized = irFunction.getCapitalizedFileName()
val lineNum = irFunction.fileEntry.getLineNumber(irFunction.startOffset) + 1
if (getCurrentFrame().frameEntryPoint == null)
getCurrentFrame().frameEntryPoint = "at $fileNameCapitalized.${irFunction.fqNameWhenAvailable}($fileName:$lineNum)"
}
override fun getStackTrace(): List<String> {
// TODO implement some sort of cache
return frameList.mapNotNull { it.frameEntryPoint }
}
override fun clean() {
frameList.clear()
frameList.add(FrameContainer())
}
override fun addVar(variable: Variable) {
getCurrentFrame().addVar(variable)
}
override fun addAll(variables: List<Variable>) {
getCurrentFrame().addAll(variables)
}
override fun getVariable(symbol: IrSymbol): Variable {
return getCurrentFrame().getVariable(symbol)
}
override fun getAll(): List<Variable> {
return getCurrentFrame().getAll()
}
override fun contains(symbol: IrSymbol): Boolean {
return getCurrentFrame().contains(symbol)
}
override fun hasReturnValue(): Boolean {
return getCurrentFrame().hasReturnValue()
}
override fun pushReturnValue(state: State) {
getCurrentFrame().pushReturnValue(state)
}
override fun popReturnValue(): State {
return getCurrentFrame().popReturnValue()
}
override fun peekReturnValue(): State {
return getCurrentFrame().peekReturnValue()
}
}
private class FrameContainer(current: Frame = InterpreterFrame()) {
var frameEntryPoint: String? = null
private val innerStack = mutableListOf(current)
private fun getTopFrame() = innerStack.first()
fun addSubFrame(frame: Frame) {
innerStack.add(0, frame)
}
fun removeSubFrame() {
if (getTopFrame().hasReturnValue() && innerStack.size > 1) innerStack[1].pushReturnValue(getTopFrame())
innerStack.removeAt(0)
}
fun addVar(variable: Variable) = getTopFrame().addVar(variable)
fun addAll(variables: List<Variable>) = getTopFrame().addAll(variables)
fun getAll() = innerStack.flatMap { it.getAll() }
fun getVariable(symbol: IrSymbol): Variable {
return innerStack.firstNotNullResult { it.getVariable(symbol) }
?: throw InterpreterException("$symbol not found") // TODO better message
}
fun contains(symbol: IrSymbol) = innerStack.any { it.contains(symbol) }
fun hasReturnValue() = getTopFrame().hasReturnValue()
fun pushReturnValue(container: FrameContainer) = getTopFrame().pushReturnValue(container.getTopFrame())
fun pushReturnValue(state: State) = getTopFrame().pushReturnValue(state)
fun popReturnValue() = getTopFrame().popReturnValue()
fun peekReturnValue() = getTopFrame().peekReturnValue()
override fun toString() = frameEntryPoint ?: "Not defined"
}

View File

@@ -5,8 +5,19 @@
package org.jetbrains.kotlin.ir.interpreter.stack
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
import org.jetbrains.kotlin.ir.interpreter.state.State
import org.jetbrains.kotlin.ir.symbols.IrSymbol
// TODO maybe switch to typealias and use map instead of list
internal data class Variable(val symbol: IrSymbol, var state: State)
internal data class Variable(val symbol: IrSymbol) {
lateinit var state: State
constructor(symbol: IrSymbol, state: State) : this(symbol) {
this.state = state
}
override fun toString(): String {
return "Variable(symbol=${(symbol.owner as? IrDeclarationWithName)?.name}, state=$state)"
}
}

View File

@@ -5,50 +5,46 @@
package org.jetbrains.kotlin.ir.interpreter.state
import org.jetbrains.kotlin.ir.interpreter.isInterface
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.ir.util.nameForIrSerialization
internal class Common private constructor(
override val irClass: IrClass, override val fields: MutableList<Variable>
) : Complex(irClass, fields) {
internal class Common private constructor(override val irClass: IrClass, override val fields: MutableList<Variable>) : Complex, StateWithClosure {
override val upValues: MutableList<Variable> = mutableListOf()
override var superWrapperClass: Wrapper? = null
override var outerClass: Variable? = null
constructor(irClass: IrClass) : this(irClass, mutableListOf())
fun setSuperClassRecursive() {
var thisClass: Common? = this
while (thisClass != null) {
val superClass = thisClass.irClass.superTypes.filterNot { it.isInterface() }.singleOrNull()
val superClassOwner = superClass?.classOrNull?.owner
val superClassState = superClassOwner?.let { Common(it) }
superClassState?.let { thisClass!!.setSuperClassInstance(it) }
fun copyFieldsFrom(state: Complex) {
this.fields.addAll(state.fields)
superWrapperClass = state.superWrapperClass ?: state as? Wrapper
}
if (superClass == null && thisClass.irClass.superTypes.isNotEmpty()) {
// cover the case when super type implement an interface and so doesn't have explicit any as super class
thisClass.setSuperClassInstance(Common(getAnyClassRecursive()))
}
thisClass = superClassState
// This method is used to get correct java method name
private fun getKotlinName(declaringClassName: String, methodName: String): String {
return when {
// TODO see specialBuiltinMembers.kt
//"kotlin.collections.Map.<get-entries>" -> "entrySet"
//"kotlin.collections.Map.<get-keys>" -> "keySet"
declaringClassName == "java.lang.CharSequence" && methodName == "charAt" -> "get"
//"kotlin.collections.MutableList.removeAt" -> "remove"
else -> methodName
}
}
private fun getAnyClassRecursive(): IrClass {
var owner = irClass.superTypes.first().classOrNull!!.owner
while (owner.superTypes.isNotEmpty()) owner = owner.superTypes.first().classOrNull!!.owner
return owner
}
fun getToStringFunction(): IrFunction {
return irClass.declarations.filterIsInstance<IrFunction>()
.filter { it.name.asString() == "toString" }
.first { it.valueParameters.isEmpty() }
.let { getOverridden(it as IrSimpleFunction, this) }
fun getIrFunction(method: java.lang.reflect.Method): IrFunction? {
val methodName = getKotlinName(method.declaringClass.name, method.name)
return when (val declaration = irClass.declarations.singleOrNull { it.nameForIrSerialization.asString() == methodName }) {
is IrProperty -> declaration.getter
else -> declaration as? IrFunction
}
}
override fun toString(): String {
return "Common(obj='${irClass.fqNameForIrSerialization}', super=$superClass, values=$fields)"
return "Common(obj='${irClass.fqNameForIrSerialization}', values=$fields)"
}
}

View File

@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.ir.interpreter.state
import org.jetbrains.kotlin.ir.interpreter.getCorrectReceiverByFunction
import org.jetbrains.kotlin.ir.interpreter.getLastOverridden
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.declarations.IrClass
@@ -14,79 +13,76 @@ import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.util.overrides
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.isAny
import org.jetbrains.kotlin.ir.types.isNullableAny
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
internal abstract class Complex(override val irClass: IrClass, override val fields: MutableList<Variable>) : State {
var superClass: Complex? = null
var subClass: Complex? = null
val interfaces: MutableList<Complex> = mutableListOf() // filled lazily, as needed
override val typeArguments: MutableList<Variable> = mutableListOf()
var outerClass: Variable? = null
fun setSuperClassInstance(superClass: Complex) {
if (this.irClass == superClass.irClass) {
// if superClass is just secondary constructor instance, then copy properties that isn't already present in instance
superClass.fields.forEach { if (!this.contains(it)) fields.add(it) }
this.superClass = superClass.superClass
superClass.superClass?.subClass = this
} else {
this.superClass = superClass
superClass.subClass = this
}
}
fun getOriginal(): Complex {
return subClass?.getOriginal() ?: this
}
internal interface Complex: State {
var superWrapperClass: Wrapper?
var outerClass: Variable?
fun irClassFqName(): String {
return irClass.fqNameForIrSerialization.toString()
}
private fun contains(variable: Variable) = fields.any { it.symbol == variable.symbol }
private fun getIrFunction(symbol: IrFunctionSymbol): IrFunction? {
val propertyGetters = irClass.declarations.filterIsInstance<IrProperty>().mapNotNull { it.getter }
val functions = irClass.declarations.filterIsInstance<IrFunction>()
return (propertyGetters + functions).firstOrNull {
if (it is IrSimpleFunction) it.overrides(symbol.owner as IrSimpleFunction) else it == symbol.owner
}
}
private fun getThisOrSuperReceiver(superIrClass: IrClass?): Complex? {
return when {
superIrClass == null -> this.getOriginal()
superIrClass.isInterface -> Common(superIrClass).apply {
interfaces.add(this)
this.subClass = this@Complex
private fun IrClass.getIrFunction(symbol: IrFunctionSymbol): IrFunction? {
val propertyGetters = this.declarations.filterIsInstance<IrProperty>().mapNotNull { it.getter }
val propertySetters = this.declarations.filterIsInstance<IrProperty>().mapNotNull { it.setter }
val functions = this.declarations.filterIsInstance<IrFunction>()
return (propertyGetters + propertySetters + functions).firstOrNull {
val owner = symbol.owner
when {
it is IrSimpleFunction && owner is IrSimpleFunction -> it.overrides(owner) || owner.overrides(it)
else -> it == symbol.owner
}
else -> this.superClass
}
}
protected fun getOverridden(owner: IrSimpleFunction, qualifier: State?): IrSimpleFunction {
if (!owner.isFakeOverride) return owner
if (qualifier == null || qualifier is ExceptionState || (qualifier as? Complex)?.superClass == null) {
return owner.getLastOverridden() as IrSimpleFunction
}
val overriddenOwner = owner.overriddenSymbols.single().owner
private fun getThisOrSuperReceiver(superIrClass: IrClass?): IrClass? {
return when {
overriddenOwner.body != null -> overriddenOwner
else -> getOverridden(overriddenOwner, qualifier.superClass!!)
superIrClass == null -> this.irClass
superIrClass.isInterface -> superIrClass
else -> irClass.superTypes.map { it.classOrNull?.owner }.singleOrNull { it?.isInterface == false }
}
}
private fun getOverridden(owner: IrSimpleFunction): IrSimpleFunction {
if (owner.parent == superWrapperClass?.irClass) return owner
if (!owner.isFakeOverride || owner.body != null || owner.parentAsClass.defaultType.isAny()) return owner
val overriddenOwner = owner.overriddenSymbols.let { it.singleOrNull { it.owner.body != null } ?: it.singleOrNull() }?.owner
return overriddenOwner?.let { getOverridden(it) } ?: owner.getLastOverridden() as IrSimpleFunction
}
override fun getIrFunctionByIrCall(expression: IrCall): IrFunction? {
val receiver = getThisOrSuperReceiver(expression.superQualifierSymbol?.owner) ?: return null
val irFunction = receiver.getIrFunction(expression.symbol) ?: return null
return getOverridden(irFunction as IrSimpleFunction)
}
return when (irFunction.body) {
null -> getOverridden(irFunction as IrSimpleFunction, this.getCorrectReceiverByFunction(irFunction))
else -> irFunction
}
fun getEqualsFunction(): IrSimpleFunction {
val equalsFun = irClass.declarations
.filterIsInstance<IrSimpleFunction>()
.single {
it.name == Name.identifier("equals") && it.dispatchReceiverParameter != null
&& it.valueParameters.size == 1 && it.valueParameters[0].type.isNullableAny()
}
return getOverridden(equalsFun)
}
fun getHashCodeFunction(): IrSimpleFunction {
return irClass.declarations.filterIsInstance<IrSimpleFunction>()
.filter { it.name.asString() == "hashCode" }
.first { it.valueParameters.isEmpty() }
.let { getOverridden(it) }
}
fun getToStringFunction(): IrSimpleFunction {
return irClass.declarations.filterIsInstance<IrSimpleFunction>()
.filter { it.name.asString() == "toString" }
.first { it.valueParameters.isEmpty() }
.let { getOverridden(it) }
}
}

View File

@@ -17,7 +17,15 @@ import kotlin.math.min
internal class ExceptionState private constructor(
override val irClass: IrClass, override val fields: MutableList<Variable>, stackTrace: List<String>
) : Complex(irClass, fields) {
) : Complex, StateWithClosure, Throwable() {
override val upValues: MutableList<Variable> = mutableListOf()
override var superWrapperClass: Wrapper? = null
override var outerClass: Variable? = null
override val message: String?
get() = getState(messageProperty.symbol)?.asStringOrNull()
override val cause: Throwable?
get() = getState(causeProperty.symbol)?.let { if (it is ExceptionState) it else null }
private lateinit var exceptionFqName: String
private val exceptionHierarchy = mutableListOf<String>()
@@ -35,9 +43,8 @@ internal class ExceptionState private constructor(
}
constructor(common: Common, stackTrace: List<String>) : this(common.irClass, common.fields, stackTrace) {
var wrapperSuperType: Complex? = common
while (wrapperSuperType != null && wrapperSuperType !is Wrapper) wrapperSuperType = (wrapperSuperType as Common).superClass
setUpCauseIfNeeded(wrapperSuperType as? Wrapper)
(common.superWrapperClass?.value as? Throwable)?.let { setMessage(it.message) }
setUpCauseIfNeeded(common.superWrapperClass)
}
constructor(wrapper: Wrapper, stackTrace: List<String>) : this(wrapper.value as Throwable, wrapper.irClass, stackTrace) {
@@ -47,6 +54,7 @@ internal class ExceptionState private constructor(
constructor(
exception: Throwable, irClass: IrClass, stackTrace: List<String>
) : this(irClass, evaluateFields(exception, irClass, stackTrace), stackTrace + evaluateAdditionalStackTrace(exception)) {
setCause(null) // TODO check this fact
if (irClass.name.asString() != exception::class.java.simpleName) {
// ir class wasn't found in classpath, a stub was passed => need to save java class hierarchy
this.exceptionFqName = exception::class.java.name
@@ -56,18 +64,11 @@ internal class ExceptionState private constructor(
}
}
data class ExceptionData(val state: ExceptionState) : Throwable() {
override val message: String? = state.getMessage()
override fun fillInStackTrace() = this
override fun toString(): String = state.getMessageWithName()
}
private fun setUpCauseIfNeeded(wrapper: Wrapper?) {
val cause = (wrapper?.value as? Throwable)?.cause as? ExceptionData
setCause(cause?.state)
if (getMessage() == null && cause != null) {
val causeMessage = cause.state.exceptionFqName + (cause.state.getMessage()?.let { ": $it" } ?: "")
val cause = (wrapper?.value as? Throwable)?.cause as? ExceptionState
setCause(cause)
if (message == null && cause != null) {
val causeMessage = cause.exceptionFqName + (cause.message?.let { ": $it" } ?: "")
setMessage(causeMessage)
}
}
@@ -87,23 +88,18 @@ internal class ExceptionState private constructor(
setField(Variable(causeProperty.symbol, causeValue ?: Primitive<Throwable?>(null, causeProperty.getter!!.returnType)))
}
fun getMessage(): String? = (getState(messageProperty.symbol) as Primitive<*>).value as String?
private fun getMessageWithName(): String = getMessage()?.let { "$exceptionFqName: $it" } ?: exceptionFqName
fun getCause(): ExceptionState? = getState(causeProperty.symbol)?.let { if (it is ExceptionState) it else null }
fun getFullDescription(): String {
// TODO remainder of the stack trace with "..."
val message = getMessage().let { if (it?.isNotEmpty() == true) ": $it" else "" }
val message = message.let { if (it?.isNotEmpty() == true) ": $it" else "" }
val prefix = if (stackTrace.isNotEmpty()) "\n\t" else ""
val postfix = if (stackTrace.size > 10) "\n\t..." else ""
val causeMessage = getCause()?.getFullDescription()?.replaceFirst("Exception ", "\nCaused by: ") ?: ""
val causeMessage = (cause as? ExceptionState)?.getFullDescription()?.replaceFirst("Exception ", "\nCaused by: ") ?: ""
return "Exception $exceptionFqName$message" +
stackTrace.subList(0, min(stackTrace.size, 10)).joinToString(separator = "\n\t", prefix = prefix, postfix = postfix) +
causeMessage
}
fun getThisAsCauseForException() = ExceptionData(this)
override fun toString(): String = message?.let { "$exceptionFqName: $it" } ?: exceptionFqName
companion object {
private fun IrClass.getPropertyByName(name: String): IrProperty {

View File

@@ -1,37 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.interpreter.state
import org.jetbrains.kotlin.ir.interpreter.getLastOverridden
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.util.nameForIrSerialization
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.utils.addToStdlib.cast
internal class Lambda(val irFunction: IrFunction, override val irClass: IrClass) : State {
override val fields: MutableList<Variable> = mutableListOf()
override val typeArguments: MutableList<Variable> = mutableListOf()
private val invokeSymbol = irClass.declarations
.single { it.nameForIrSerialization.asString() == "invoke" }
.cast<IrSimpleFunction>()
.getLastOverridden().symbol
override fun getIrFunctionByIrCall(expression: IrCall): IrFunction? {
return if (invokeSymbol == expression.symbol) irFunction else null
}
override fun toString(): String {
val receiver = (irFunction.dispatchReceiverParameter?.type ?: irFunction.extensionReceiverParameter?.type)?.render()
val arguments = irFunction.valueParameters.joinToString(prefix = "(", postfix = ")") { it.type.render() }
val returnType = irFunction.returnType.render()
return ("$arguments -> $returnType").let { if (receiver != null) "$receiver.$it" else it }
}
}

View File

@@ -19,9 +19,8 @@ import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isFakeOverride
import org.jetbrains.kotlin.ir.util.overrides
internal class Primitive<T>(var value: T, val type: IrType) : State {
internal class Primitive<T>(val value: T, val type: IrType) : State {
override val fields: MutableList<Variable> = mutableListOf()
override val typeArguments: MutableList<Variable> = mutableListOf()
override val irClass: IrClass = type.classOrNull!!.owner
override fun getState(symbol: IrSymbol): State {
@@ -37,26 +36,6 @@ internal class Primitive<T>(var value: T, val type: IrType) : State {
?.let { if (it.isFakeOverride) it.getLastOverridden() else it }
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Primitive<*>
if (value != other.value) return false
if (type != other.type) return false
if (fields != other.fields) return false
return true
}
override fun hashCode(): Int {
var result = value?.hashCode() ?: 0
result = 31 * result + type.hashCode()
result = 31 * result + fields.hashCode()
return result
}
override fun toString(): String {
return "Primitive(value=$value, type=${irClass.defaultType})"
}

View File

@@ -9,6 +9,8 @@ import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment
import org.jetbrains.kotlin.ir.interpreter.handleUserException
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.defaultType
@@ -16,7 +18,6 @@ import org.jetbrains.kotlin.ir.util.defaultType
internal interface State {
val fields: MutableList<Variable>
val irClass: IrClass
val typeArguments: MutableList<Variable>
fun getState(symbol: IrSymbol): State? {
return fields.firstOrNull { it.symbol == symbol }?.state
@@ -29,10 +30,6 @@ internal interface State {
}
}
fun addTypeArguments(typeArguments: List<Variable>) {
this.typeArguments.addAll(typeArguments)
}
fun getIrFunctionByIrCall(expression: IrCall): IrFunction?
}
@@ -43,15 +40,22 @@ internal fun State.asBoolean() = (this as Primitive<*>).value as Boolean
internal fun State.asString() = (this as Primitive<*>).value.toString()
internal fun State.asBooleanOrNull() = (this as? Primitive<*>)?.value as? Boolean
internal fun State.asStringOrNull() = (this as Primitive<*>).value as? String
internal fun State.isSubtypeOf(other: IrType): Boolean {
if (this is Primitive<*> && this.value == null) return other.isNullable()
if (this is ExceptionState) return this.isSubtypeOf(other.classOrNull!!.owner)
if (this is Primitive<*> && this.type.isArray() && other.isArray()) {
val thisClass = this.typeArguments.single().state.irClass.symbol
val otherArgument = (other as IrSimpleType).arguments.single()
if (otherArgument is IrStarProjection) return true
return thisClass.isSubtypeOfClass(otherArgument.typeOrNull!!.classOrNull!!)
fun IrType.arraySubtypeCheck(other: IrType): Boolean {
if (other !is IrSimpleType || this !is IrSimpleType) return false
val thisArgument = this.arguments.single().typeOrNull ?: return false
val otherArgument = other.arguments.single().typeOrNull ?: return other.arguments.single() is IrStarProjection
if (thisArgument.isArray() && otherArgument.isArray()) return thisArgument.arraySubtypeCheck(otherArgument)
if (otherArgument.classOrNull == null) return false
return thisArgument.classOrNull?.isSubtypeOfClass(otherArgument.classOrNull!!) ?: false
}
return this.type.arraySubtypeCheck(other)
}
return this.irClass.defaultType.isSubtypeOfClass(other.classOrNull!!)
@@ -60,10 +64,13 @@ internal fun State.isSubtypeOf(other: IrType): Boolean {
/**
* This method used to check if for not null parameter there was passed null argument.
*/
internal fun State.checkNullability(irType: IrType?, throwException: () -> Nothing = { throw NullPointerException() }): State {
internal fun State.checkNullability(
irType: IrType?, environment: IrInterpreterEnvironment, exceptionToThrow: () -> Throwable = { NullPointerException() }
): State? {
if (irType !is IrSimpleType) return this
if (this.isNull() && !irType.isNullable()) {
throwException()
exceptionToThrow().handleUserException(environment)
return null
}
return this
}

View File

@@ -0,0 +1,12 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.interpreter.state
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
internal interface StateWithClosure {
val upValues: MutableList<Variable>
}

View File

@@ -5,28 +5,62 @@
package org.jetbrains.kotlin.ir.interpreter.state
import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.interpreter.*
import org.jetbrains.kotlin.ir.interpreter.builtins.evaluateIntrinsicAnnotation
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
import java.lang.invoke.MethodHandle
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType
import java.util.*
import kotlin.collections.HashMap
import kotlin.collections.LinkedHashMap
internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex(irClass, mutableListOf()) {
internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex {
override val fields: MutableList<Variable> = mutableListOf()
override var superWrapperClass: Wrapper? = null
override var outerClass: Variable? = null
private val typeFqName = irClass.fqNameForIrSerialization.toUnsafe()
private val receiverClass = irClass.defaultType.getClass(true)
init {
val javaClass = value::class.java
when {
javaClass == HashMap::class.java -> {
val nodeClass = javaClass.declaredClasses.single { it.name.contains("\$Node") }
val mutableMap = irClass.superTypes.mapNotNull { it.classOrNull?.owner }.single { it.isInterface }
javaClassToIrClass += nodeClass to mutableMap.declarations.filterIsInstance<IrClass>().single()
}
javaClass == LinkedHashMap::class.java -> {
val entryClass = javaClass.declaredClasses.single { it.name.contains("\$Entry") }
val mutableMap = irClass.superTypes.mapNotNull { it.classOrNull?.owner }.single { it.isInterface }
javaClassToIrClass += entryClass to mutableMap.declarations.filterIsInstance<IrClass>().single()
}
javaClass.canonicalName == "java.util.Collections.SingletonMap" -> {
javaClassToIrClass += AbstractMap.SimpleEntry::class.java to irClass.declarations.filterIsInstance<IrClass>().single()
javaClassToIrClass += AbstractMap.SimpleImmutableEntry::class.java to irClass.declarations.filterIsInstance<IrClass>().single()
}
}
javaClassToIrClass += value::class.java to irClass
}
constructor(value: Any) : this(value, javaClassToIrClass[value::class.java]!!)
override fun getIrFunctionByIrCall(expression: IrCall): IrFunction? = null
fun getMethod(irFunction: IrFunction): MethodHandle? {
if (irFunction.getEvaluateIntrinsicValue()?.isEmpty() == true) return null // this method will handle IntrinsicEvaluator
// if function is actually a getter, then use "get${property.name.capitalize()}" as method name
@@ -51,12 +85,38 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex(
}
}
override fun toString(): String {
return value.toString()
}
companion object {
private val companionObjectValue = mapOf<String, Any>("kotlin.text.Regex\$Companion" to Regex.Companion)
private val javaClassToIrClass = mutableMapOf<Class<*>, IrClass>()
fun associateJavaClassWithIrClass(javaClass: Class<*>, irClass: IrClass) {
javaClassToIrClass += javaClass to irClass
}
fun getReflectionMethod(irFunction: IrFunction): MethodHandle {
val receiverClass = irFunction.dispatchReceiverParameter!!.type.getClass(asObject = true)
val methodType = irFunction.getMethodType()
val methodName = when (irFunction) {
is IrSimpleFunction -> {
val property = irFunction.correspondingPropertySymbol?.owner
when {
property?.getter == irFunction -> "get${property.name.asString().capitalize()}"
property?.setter == irFunction -> "set${property.name.asString().capitalize()}"
else -> irFunction.name.asString()
}
}
else -> irFunction.name.asString()
}
return MethodHandles.lookup().findVirtual(receiverClass, methodName, methodType)
}
fun getCompanionObject(irClass: IrClass): Wrapper {
val objectName = irClass.getEvaluateIntrinsicValue()!!
val objectValue = companionObjectValue[objectName] ?: throw AssertionError("Companion object $objectName cannot be interpreted")
val objectValue = companionObjectValue[objectName] ?: throw InternalError("Companion object $objectName cannot be interpreted")
return Wrapper(objectValue, irClass)
}
@@ -79,7 +139,7 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex(
fun getStaticGetter(field: IrField): MethodHandle? {
val jvmClass = field.parentAsClass.defaultType.getClass(true)
val returnType = field.type.getClass(false)
val returnType = field.type.let { it.getClass((it as IrSimpleType).hasQuestionMark) }
return MethodHandles.lookup().findStaticGetter(jvmClass, field.name.asString(), returnType)
}
@@ -113,19 +173,31 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex(
//TODO check if primitive array is possible here
return when {
notNullType.isPrimitiveType() || notNullType.isString() -> getPrimitiveClass(notNullType, asObject)!!
notNullType.isArray() -> if (asObject) Array<Any?>::class.javaObjectType else Array<Any?>::class.java
notNullType.isArray() -> {
val argumentFqName = (this as IrSimpleType).arguments.single().typeOrNull?.classOrNull?.owner?.fqNameWhenAvailable
when {
argumentFqName != null && argumentFqName.asString() != "kotlin.Any" -> argumentFqName.let { Class.forName("[L$it;") }
else -> Array<Any?>::class.java
}
}
notNullType.isNothing() -> Nothing::class.java
notNullType.isAny() -> Any::class.java
notNullType.isUnit() -> if (asObject) Void::class.javaObjectType else Void::class.javaPrimitiveType!!
notNullType.isNumber() -> Number::class.java
notNullType.isCharSequence() -> CharSequence::class.java
notNullType.isComparable() -> Comparable::class.java
notNullType.isThrowable() -> Throwable::class.java
notNullType.isIterable() -> Iterable::class.java
// TODO implement function mapping; all complexity is to map big arity to FunctionN
//notNullType.isKFunction() -> Class.forName("kotlin.reflect.KFunction")
//notNullType.isFunction() -> Class.forName("kotlin.jvm.functions.Function_TODO")
//notNullType.isSuspendFunction() || notNullType.isKSuspendFunction() -> throw AssertionError()
notNullType.isKFunction() -> Class.forName("kotlin.reflect.KFunction")
notNullType.isFunction() -> {
val arity = fqName?.removePrefix("kotlin.Function")?.toIntOrNull()
return when {
arity == null || arity >= BuiltInFunctionArity.BIG_ARITY -> Class.forName("kotlin.jvm.functions.FunctionN")
else -> Class.forName("kotlin.jvm.functions.${fqName.removePrefix("kotlin.")}")
}
}
//notNullType.isSuspendFunction() || notNullType.isKSuspendFunction() -> throw AssertionError() //TODO
fqName == "kotlin.Enum" -> Enum::class.java
fqName == "kotlin.collections.Collection" || fqName == "kotlin.collections.MutableCollection" -> Collection::class.java
@@ -136,6 +208,7 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex(
fqName == "kotlin.collections.Iterator" || fqName == "kotlin.collections.MutableIterator" -> Iterator::class.java
fqName == "kotlin.collections.Map.Entry" || fqName == "kotlin.collections.MutableMap.MutableEntry" -> Map.Entry::class.java
fqName == "kotlin.collections.ListIterator" || fqName == "kotlin.collections.MutableListIterator" -> ListIterator::class.java
fqName == "kotlin.collections.HashMap" -> HashMap::class.java
owner.hasAnnotation(evaluateIntrinsicAnnotation) -> Class.forName(owner!!.getEvaluateIntrinsicValue())
fqName == null -> Any::class.java // null if this.isTypeParameter()
@@ -184,8 +257,4 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex(
return true
}
}
override fun toString(): String {
return "Wrapper(obj='$typeFqName', value=$value)"
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.ir.interpreter.state.reflection
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.expressions.IrClassReference
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.internalName
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.*
import org.jetbrains.kotlin.ir.types.classOrNull
import kotlin.reflect.KCallable
import kotlin.reflect.KFunction
import kotlin.reflect.KType
import kotlin.reflect.KTypeParameter
internal class KClassState(val classReference: IrClass, override val irClass: IrClass) : ReflectionState() {
private var _members: Collection<KCallable<*>>? = null
private var _constructors: Collection<KFunction<*>>? = null
private var _typeParameters: List<KTypeParameter>? = null
private var _supertypes: List<KType>? = null
constructor(classReference: IrClassReference) : this(classReference.symbol.owner as IrClass, classReference.type.classOrNull!!.owner)
fun getMembers(callInterceptor: CallInterceptor): Collection<KCallable<*>> {
if (_members != null) return _members!!
_members = classReference.declarations
.filter { it !is IrClass && it !is IrConstructor }
.map {
when (it) {
is IrProperty -> {
val withExtension = it.getter?.extensionReceiverParameter != null
when {
!withExtension && !it.isVar ->
KProperty1Proxy(KPropertyState(it, callInterceptor.irBuiltIns.getKPropertyClass(false, 1).owner), callInterceptor)
!withExtension && it.isVar ->
KMutableProperty1Proxy(
KPropertyState(it, callInterceptor.irBuiltIns.getKPropertyClass(true, 1).owner), callInterceptor
)
withExtension && !it.isVar ->
KProperty2Proxy(KPropertyState(it, callInterceptor.irBuiltIns.getKPropertyClass(false, 2).owner), callInterceptor)
!withExtension && it.isVar ->
KMutableProperty2Proxy(
KPropertyState(it, callInterceptor.irBuiltIns.getKPropertyClass(true, 2).owner), callInterceptor
)
else -> TODO()
}
}
is IrFunction -> KFunctionProxy(KFunctionState(it, callInterceptor.irBuiltIns.functionFactory), callInterceptor)
else -> TODO()
}
}
return _members!!
}
fun getConstructors(callInterceptor: CallInterceptor): Collection<KFunction<*>> {
if (_constructors != null) return _constructors!!
_constructors = classReference.declarations
.filterIsInstance<IrConstructor>()
.map { KFunctionProxy(KFunctionState(it, callInterceptor.irBuiltIns.functionFactory), callInterceptor) }
return _constructors!!
}
fun getTypeParameters(callInterceptor: CallInterceptor): List<KTypeParameter> {
if (_typeParameters != null) return _typeParameters!!
val kTypeParameterIrClass = irClass.getIrClassOfReflectionFromList("typeParameters")
_typeParameters = classReference.typeParameters.map { KTypeParameterProxy(KTypeParameterState(it, kTypeParameterIrClass), callInterceptor) }
return _typeParameters!!
}
fun getSupertypes(callInterceptor: CallInterceptor): List<KType> {
if (_supertypes != null) return _supertypes!!
val kTypeIrClass = irClass.getIrClassOfReflectionFromList("supertypes")
_supertypes = (classReference.superTypes.map { it } + callInterceptor.irBuiltIns.anyType).toSet()
.map { KTypeProxy(KTypeState(it, kTypeIrClass), callInterceptor) }
return _supertypes!!
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as KClassState
if (classReference != other.classReference) return false
return true
}
override fun hashCode(): Int {
return classReference.hashCode()
}
override fun toString(): String {
return "class ${classReference.internalName()}"
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.ir.interpreter.state.reflection
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.descriptors.IrAbstractFunctionFactory
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KParameterProxy
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeParameterProxy
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeProxy
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.StateWithClosure
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.name.Name
import kotlin.reflect.KParameter
import kotlin.reflect.KType
import kotlin.reflect.KTypeParameter
internal class KFunctionState(val irFunction: IrFunction, override val irClass: IrClass) : ReflectionState(), StateWithClosure {
override val upValues: MutableList<Variable> = mutableListOf()
override val fields: MutableList<Variable> = mutableListOf()
private var _parameters: List<KParameter>? = null
private var _returnType: KType? = null
private var _typeParameters: List<KTypeParameter>? = null
constructor(functionReference: IrFunctionReference) : this(functionReference.symbol.owner, functionReference.type.classOrNull!!.owner)
constructor(irFunction: IrFunction, functionFactory: IrAbstractFunctionFactory) :
this(irFunction, functionFactory.kFunctionN(irFunction.valueParameters.size))
fun getParameters(callInterceptor: CallInterceptor): List<KParameter> {
if (_parameters != null) return _parameters!!
val kParameterIrClass = irClass.getIrClassOfReflectionFromList("parameters")
var index = 0
val instanceParameter = irFunction.dispatchReceiverParameter
?.let { KParameterProxy(KParameterState(kParameterIrClass, it, index++, KParameter.Kind.INSTANCE), callInterceptor) }
val extensionParameter = irFunction.extensionReceiverParameter
?.let { KParameterProxy(KParameterState(kParameterIrClass, it, index++, KParameter.Kind.EXTENSION_RECEIVER), callInterceptor) }
_parameters = listOfNotNull(instanceParameter, extensionParameter) +
irFunction.valueParameters.map { KParameterProxy(KParameterState(kParameterIrClass, it, index++), callInterceptor) }
return _parameters!!
}
fun getReturnType(callInterceptor: CallInterceptor): KType {
if (_returnType != null) return _returnType!!
val kTypeIrClass = irClass.getIrClassOfReflection("returnType")
_returnType = KTypeProxy(KTypeState(irFunction.returnType, kTypeIrClass), callInterceptor)
return _returnType!!
}
fun getTypeParameters(callInterceptor: CallInterceptor): List<KTypeParameter> {
if (_typeParameters != null) return _typeParameters!!
val kTypeParametersIrClass = irClass.getIrClassOfReflectionFromList("typeParameters")
_typeParameters = irClass.typeParameters.map { KTypeParameterProxy(KTypeParameterState(it, kTypeParametersIrClass), callInterceptor) }
return _typeParameters!!
}
fun getArity(): Int? {
return irClass.name.asString().removePrefix("Function").removePrefix("KFunction").toIntOrNull()
}
override fun getIrFunctionByIrCall(expression: IrCall): IrFunction? {
return if (expression.symbol.owner.name.asString() == "invoke") irFunction else null
}
private fun isLambda(): Boolean = irFunction.name.let { it == Name.special("<anonymous>") || it == Name.special("<no name provided>") }
override fun toString(): String {
return if (isLambda()) renderLambda(irFunction) else renderFunction(irFunction)
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.ir.interpreter.state.reflection
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeProxy
import kotlin.reflect.KParameter
import kotlin.reflect.KType
internal class KParameterState(
override val irClass: IrClass, val irParameter: IrValueParameter, val index: Int, val kind: KParameter.Kind = KParameter.Kind.VALUE
) : ReflectionState() {
private var _type: KType? = null
fun getType(callInterceptor: CallInterceptor): KType {
if (_type != null) return _type!!
val kTypeIrClass = irClass.getIrClassOfReflection("type")
_type = KTypeProxy(KTypeState(irParameter.type, kTypeIrClass), callInterceptor)
return _type!!
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as KParameterState
if (irParameter != other.irParameter) return false
return true
}
override fun hashCode(): Int {
return irParameter.hashCode()
}
override fun toString(): String {
return buildString {
when (kind) {
KParameter.Kind.EXTENSION_RECEIVER -> append("extension receiver parameter")
KParameter.Kind.INSTANCE -> append("instance parameter")
KParameter.Kind.VALUE -> append("parameter #$index ${irParameter.name}")
}
append(" of ")
when (val parent = irParameter.parent) {
is IrSimpleFunction -> parent.correspondingPropertySymbol?.owner?.let { append(renderProperty(it)) }
?: append(renderFunction(parent))
is IrFunction -> append(renderFunction(parent))
is IrProperty -> append(renderProperty(parent))
else -> TODO()
}
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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.ir.interpreter.state.reflection
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.expressions.IrPropertyReference
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KParameterProxy
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeProxy
import org.jetbrains.kotlin.ir.interpreter.state.State
import org.jetbrains.kotlin.ir.types.classOrNull
import kotlin.reflect.KParameter
import kotlin.reflect.KType
internal class KPropertyState(
val property: IrProperty, override val irClass: IrClass, val dispatchReceiver: State? = null
) : ReflectionState() {
constructor(propertyReference: IrPropertyReference, dispatchReceiver: State?)
: this(propertyReference.symbol.owner, propertyReference.type.classOrNull!!.owner, dispatchReceiver)
private var _parameters: List<KParameter>? = null
private var _returnType: KType? = null
fun getParameters(callInterceptor: CallInterceptor): List<KParameter> {
if (_parameters != null) return _parameters!!
val kParameterIrClass = irClass.getIrClassOfReflectionFromList("parameters")
var index = 0
val instanceParameter = property.getter?.dispatchReceiverParameter?.takeIf { dispatchReceiver == null }
?.let { KParameterProxy(KParameterState(kParameterIrClass, it, index++, KParameter.Kind.INSTANCE), callInterceptor) }
val extensionParameter = property.getter?.extensionReceiverParameter
?.let { KParameterProxy(KParameterState(kParameterIrClass, it, index++, KParameter.Kind.EXTENSION_RECEIVER), callInterceptor) }
_parameters = listOfNotNull(instanceParameter, extensionParameter)
return _parameters!!
}
fun getReturnType(callInterceptor: CallInterceptor): KType {
if (_returnType != null) return _returnType!!
val kTypeIrClass = irClass.getIrClassOfReflection("returnType")
_returnType = KTypeProxy(KTypeState(property.getter!!.returnType, kTypeIrClass), callInterceptor)
return _returnType!!
}
fun isKProperty0(): Boolean = irClass.name.asString() == "KProperty0"
fun isKProperty1(): Boolean = irClass.name.asString() == "KProperty1"
fun isKProperty2(): Boolean = irClass.name.asString() == "KProperty2"
fun isKMutableProperty0(): Boolean = irClass.name.asString() == "KMutableProperty0"
fun isKMutableProperty1(): Boolean = irClass.name.asString() == "KMutableProperty1"
fun isKMutableProperty2(): Boolean = irClass.name.asString() == "KMutableProperty2"
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as KPropertyState
if (property != other.property) return false
if (dispatchReceiver != other.dispatchReceiver) return false
return true
}
override fun hashCode(): Int {
var result = property.hashCode()
result = 31 * result + (dispatchReceiver?.hashCode() ?: 0)
return result
}
override fun toString(): String {
return renderProperty(property)
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.ir.interpreter.state.reflection
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeProxy
import kotlin.reflect.KType
internal class KTypeParameterState(val irTypeParameter: IrTypeParameter, override val irClass: IrClass) : ReflectionState() {
private var _upperBounds: List<KType>? = null
fun getUpperBounds(callInterceptor: CallInterceptor): List<KType> {
if (_upperBounds != null) return _upperBounds!!
val kTypeIrClass = irClass.getIrClassOfReflectionFromList("upperBounds")
_upperBounds = irTypeParameter.superTypes.map { KTypeProxy(KTypeState(it, kTypeIrClass), callInterceptor) }
return _upperBounds!!
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as KTypeParameterState
if (irTypeParameter != other.irTypeParameter) return false
return true
}
override fun hashCode(): Int {
return irTypeParameter.hashCode()
}
override fun toString(): String {
return irTypeParameter.name.asString()
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.ir.interpreter.state.reflection
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KClassProxy
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeParameterProxy
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeProxy
import org.jetbrains.kotlin.ir.interpreter.renderType
import org.jetbrains.kotlin.ir.interpreter.state.Wrapper
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.types.Variance
import kotlin.reflect.KClassifier
import kotlin.reflect.KTypeProjection
internal class KTypeState(val irType: IrType, override val irClass: IrClass) : ReflectionState() {
private var _classifier: KClassifier? = null
private var _arguments: List<KTypeProjection>? = null
fun getClassifier(callInterceptor: CallInterceptor): KClassifier? {
if (_classifier != null) return _classifier!!
_classifier = when (val classifier = irType.classifierOrFail.owner) {
is IrClass -> KClassProxy(KClassState(classifier, callInterceptor.irBuiltIns.kClassClass.owner), callInterceptor)
is IrTypeParameter -> {
val kTypeParameterIrClass = callInterceptor.irBuiltIns.kClassClass.owner.getIrClassOfReflectionFromList("typeParameters")
KTypeParameterProxy(KTypeParameterState(classifier, kTypeParameterIrClass), callInterceptor)
}
else -> TODO()
}
return _classifier!!
}
fun getArguments(callInterceptor: CallInterceptor): List<KTypeProjection> {
if (_arguments != null) return _arguments!!
Wrapper.associateJavaClassWithIrClass(KTypeProjection::class.java, irClass.getIrClassOfReflectionFromList("arguments"))
_arguments = (irType as IrSimpleType).arguments
.map {
when (it.getVariance()) {
Variance.INVARIANT -> KTypeProjection.invariant(KTypeProxy(KTypeState(it.typeOrNull!!, irClass), callInterceptor))
Variance.IN_VARIANCE -> KTypeProjection.contravariant(KTypeProxy(KTypeState(it.typeOrNull!!, irClass), callInterceptor))
Variance.OUT_VARIANCE -> KTypeProjection.covariant(KTypeProxy(KTypeState(it.typeOrNull!!, irClass), callInterceptor))
null -> KTypeProjection.STAR
}
}
return _arguments!!
}
private fun IrTypeArgument.getVariance(): Variance? {
return when (this) {
is IrSimpleType -> Variance.INVARIANT
is IrTypeProjection -> this.variance
is IrStarProjection -> null
else -> TODO()
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as KTypeState
if (irType != other.irType) return false
return true
}
override fun hashCode(): Int {
return irType.hashCode()
}
override fun toString(): String {
return irType.renderType()
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.ir.interpreter.state.reflection
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.interpreter.renderType
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.State
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.typeOrNull
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.nameForIrSerialization
import org.jetbrains.kotlin.ir.util.parentClassOrNull
internal abstract class ReflectionState : State {
override val fields: MutableList<Variable> = mutableListOf()
override fun getIrFunctionByIrCall(expression: IrCall): IrFunction? = null
protected fun IrClass.getIrClassOfReflectionFromList(name: String): IrClass {
val property = this.declarations.single { it.nameForIrSerialization.asString() == name } as IrProperty
val list = property.getter!!.returnType as IrSimpleType
return list.arguments.single().typeOrNull!!.classOrNull!!.owner
}
protected fun IrClass.getIrClassOfReflection(name: String): IrClass {
val property = this.declarations.single { it.nameForIrSerialization.asString() == name } as IrProperty
val type = property.getter!!.returnType as IrSimpleType
return type.classOrNull!!.owner
}
private fun renderReceivers(dispatchReceiver: IrType?, extensionReceiver: IrType?): String {
return buildString {
if (dispatchReceiver != null) {
append(dispatchReceiver.renderType())
append(".")
}
val addParentheses = dispatchReceiver != null && extensionReceiver != null
if (addParentheses) append("(")
if (extensionReceiver != null) {
append(extensionReceiver.renderType())
append(".")
}
if (addParentheses) append(")")
}
}
protected fun renderLambda(irFunction: IrFunction): String {
val receiver = (irFunction.dispatchReceiverParameter?.type ?: irFunction.extensionReceiverParameter?.type)?.renderType()
val arguments = irFunction.valueParameters.joinToString(prefix = "(", postfix = ")") { it.type.renderType() }
val returnType = irFunction.returnType.renderType()
return ("$arguments -> $returnType").let { if (receiver != null) "$receiver.$it" else it }
}
protected fun renderFunction(irFunction: IrFunction): String {
val dispatchReceiver = irFunction.parentClassOrNull?.defaultType // = instanceReceiverParameter
val extensionReceiver = irFunction.extensionReceiverParameter?.type
val receivers = if (irFunction is IrConstructor) "" else renderReceivers(dispatchReceiver, extensionReceiver)
val arguments = irFunction.valueParameters.joinToString(prefix = "(", postfix = ")") { it.type.renderType() }
val returnType = irFunction.returnType.renderType()
return "fun $receivers${irFunction.name}$arguments: $returnType"
}
protected fun renderProperty(property: IrProperty): String {
val prefix = if (property.isVar) "var" else "val"
val receivers = renderReceivers(property.getter?.dispatchReceiverParameter?.type, property.getter?.extensionReceiverParameter?.type)
val returnType = property.getter!!.returnType.renderType()
return "$prefix $receivers${property.name}: $returnType"
}
}

View File

@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.ir.builders.declarations
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.ir.declarations.IrClass
class IrClassBuilder : IrDeclarationBuilder() {
@@ -20,6 +21,7 @@ class IrClassBuilder : IrDeclarationBuilder() {
var isInline: Boolean = false
var isExpect: Boolean = false
var isFun: Boolean = false
var source: SourceElement = SourceElement.NO_SOURCE
fun updateFrom(from: IrClass) {
super.updateFrom(from)
@@ -33,5 +35,6 @@ class IrClassBuilder : IrDeclarationBuilder() {
isInline = from.isInline
isExpect = from.isExpect
isFun = from.isFun
source = from.source
}
}

View File

@@ -11,8 +11,12 @@ import org.jetbrains.kotlin.name.Name
interface MetadataSource {
val name: Name?
interface File : MetadataSource
interface Class : MetadataSource
interface File : MetadataSource {
var serializedIr: ByteArray?
}
interface Class : MetadataSource {
var serializedIr: ByteArray?
}
interface Function : MetadataSource
interface Property : MetadataSource {
val isConst: Boolean
@@ -26,9 +30,13 @@ sealed class DescriptorMetadataSource : MetadataSource {
override val name: Name?
get() = descriptor?.name
class File(val descriptors: List<DeclarationDescriptor>) : DescriptorMetadataSource(), MetadataSource.File
class File(val descriptors: List<DeclarationDescriptor>) : DescriptorMetadataSource(), MetadataSource.File {
override var serializedIr: ByteArray? = null
}
class Class(override val descriptor: ClassDescriptor) : DescriptorMetadataSource(), MetadataSource.Class
class Class(override val descriptor: ClassDescriptor) : DescriptorMetadataSource(), MetadataSource.Class {
override var serializedIr: ByteArray? = null
}
class Function(override val descriptor: FunctionDescriptor) : DescriptorMetadataSource(), MetadataSource.Function

View File

@@ -33,7 +33,8 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
class IrExternalPackageFragmentImpl(
override val symbol: IrExternalPackageFragmentSymbol,
override val fqName: FqName
override val fqName: FqName,
private val _containerSource: DeserializedContainerSource? = null
) : IrExternalPackageFragment() {
override val startOffset: Int
get() = UNDEFINED_OFFSET
@@ -53,7 +54,8 @@ class IrExternalPackageFragmentImpl(
@OptIn(ObsoleteDescriptorBasedAPI::class)
override val containerSource: DeserializedContainerSource?
get() = (symbol.descriptor as? DeserializedMemberDescriptor)?.containerSource
get() = _containerSource ?:
(symbol.descriptor as? DeserializedMemberDescriptor)?.containerSource
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitExternalPackageFragment(this, data)

View File

@@ -594,3 +594,9 @@ fun IrExpression?.isPure(anyVariable: Boolean, checkFields: Boolean = true): Boo
return false
}
val IrDeclarationParent.isFacadeClass: Boolean
get() = this is IrClass &&
(origin == IrDeclarationOrigin.JVM_MULTIFILE_CLASS ||
origin == IrDeclarationOrigin.FILE_CLASS ||
origin == IrDeclarationOrigin.SYNTHETIC_FILE_CLASS)

View File

@@ -52,6 +52,8 @@ interface ReferenceSymbolTable {
fun referenceTypeParameterFromLinker(sig: IdSignature): IrTypeParameterSymbol
fun referenceTypeAliasFromLinker(sig: IdSignature): IrTypeAliasSymbol
fun storeConstructorBySignature(sig: IdSignature, declaration: IrConstructor)
fun enterScope(owner: IrSymbol)
fun enterScope(owner: IrDeclaration)
@@ -74,6 +76,7 @@ class SymbolTable(
abstract fun get(d: D): S?
abstract fun set(s: S)
abstract fun get(sig: IdSignature): S?
abstract fun set(sig: IdSignature, s: S)
inline fun declare(d: D, createSymbol: () -> S, createOwner: (S) -> B): B {
synchronized(this) {
@@ -214,6 +217,10 @@ class SymbolTable(
}
override fun get(sig: IdSignature): S? = idSigToSymbol[sig]
override fun set(sig: IdSignature, s: S) {
idSigToSymbol[sig] = s
}
}
private inner class EnumEntrySymbolTable : FlatSymbolTable<ClassDescriptor, IrEnumEntry, IrEnumEntrySymbol>() {
@@ -247,6 +254,10 @@ class SymbolTable(
}
}
operator fun set(sig: IdSignature, s: S) {
idSigToSymbol[sig] = s
}
fun getLocal(d: D) = descriptorToSymbol[d]
@OptIn(ObsoleteDescriptorBasedAPI::class)
@@ -293,6 +304,12 @@ class SymbolTable(
return scope[sig]
}
override fun set(sig: IdSignature, s: S) {
val scope = currentScope ?: throw AssertionError("No active scope")
scope[sig] = s
}
inline fun declareLocal(d: D, createSymbol: () -> S, createOwner: (S) -> B): B {
val scope = currentScope ?: throw AssertionError("No active scope")
val symbol = scope.getLocal(d) ?: createSymbol().also { scope[d] = it }
@@ -525,6 +542,10 @@ class SymbolTable(
else IrConstructorSymbolImpl()
}
override fun storeConstructorBySignature(sig: IdSignature, declaration: IrConstructor) {
if (sig.isPublic) constructorSymbolTable.set(sig, declaration.symbol)
}
val unboundConstructors: Set<IrConstructorSymbol> get() = constructorSymbolTable.unboundSymbols
private fun createEnumEntrySymbol(descriptor: ClassDescriptor): IrEnumEntrySymbol {

View File

@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideClassFilter
import org.jetbrains.kotlin.backend.common.serialization.encodings.*
import org.jetbrains.kotlin.backend.common.serialization.proto.IrDeclaration.DeclaratorCase.*
import org.jetbrains.kotlin.backend.common.serialization.proto.IrType.KindCase.*
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
@@ -58,7 +59,7 @@ class IrDeclarationDeserializer(
private val symbolTable: SymbolTable,
private val irFactory: IrFactory,
private val fileReader: IrLibraryFile,
file: IrFile,
packageFragment: IrPackageFragment,
private val allowErrorNodes: Boolean,
private val deserializeInlineFunctions: Boolean,
private var deserializeBodies: Boolean,
@@ -69,6 +70,8 @@ class IrDeclarationDeserializer(
private val bodyDeserializer = IrBodyDeserializer(builtIns, allowErrorNodes, irFactory, fileReader, this)
private val containerSource = (packageFragment as? IrExternalPackageFragment)?.containerSource
private fun deserializeName(index: Int): Name {
val name = fileReader.deserializeString(index)
return Name.guessByFirstCharacter(name)
@@ -154,7 +157,7 @@ class IrDeclarationDeserializer(
}
}
private var currentParent: IrDeclarationParent = file
private var currentParent: IrDeclarationParent = packageFragment
private inline fun <T : IrDeclarationParent> T.usingParent(block: T.() -> Unit): T =
this.apply {
@@ -272,7 +275,7 @@ class IrDeclarationDeserializer(
}
}
private fun deserializeIrClass(proto: ProtoClass): IrClass =
fun deserializeIrClass(proto: ProtoClass, source: SourceElement = SourceElement.NO_SOURCE): IrClass =
withDeserializedIrDeclarationBase(proto.base) { symbol, signature, startOffset, endOffset, origin, fcode ->
val flags = ClassFlags.decode(fcode)
@@ -291,6 +294,7 @@ class IrDeclarationDeserializer(
flags.isInline,
flags.isExpect,
flags.isFun,
source,
)
}.usingParent {
typeParameters = deserializeTypeParameters(proto.typeParameterList, true)
@@ -490,7 +494,8 @@ class IrDeclarationDeserializer(
flags.isOperator,
flags.isInfix,
flags.isExpect,
flags.isFakeOverride
flags.isFakeOverride,
containerSource
)
}.apply {
overriddenSymbols = proto.overriddenList.map { deserializeIrSymbolAndRemap(it) as IrSimpleFunctionSymbol }
@@ -617,7 +622,8 @@ class IrDeclarationDeserializer(
flags.isDelegated,
flags.isExternal,
flags.isExpect,
flags.isFakeOverride
flags.isFakeOverride,
containerSource
)
}.apply {
if (proto.hasGetter()) {

View File

@@ -146,12 +146,12 @@ class IrLibraryFileFromKlib(private val klib: IrLibrary, private val fileIndex:
override fun body(index: Int): ByteArray = klib.body(index, fileIndex)
}
internal fun IrLibraryFile.deserializeString(index: Int): String = String(string(index))
fun IrLibraryFile.deserializeString(index: Int): String = String(string(index))
internal fun IrLibraryFile.deserializeFqName(fqn: List<Int>): String =
fqn.joinToString(".", transform = ::deserializeString)
internal fun IrLibraryFile.createFile(moduleDescriptor: ModuleDescriptor, fileProto: ProtoFile): IrFile {
fun IrLibraryFile.createFile(moduleDescriptor: ModuleDescriptor, fileProto: ProtoFile): IrFile {
val fileName = fileProto.fileEntry.name
val fileEntry = NaiveSourceBasedFileEntryImpl(fileName, fileProto.fileEntry.lineStartOffsetsList.toIntArray())
val fqName = FqName(deserializeFqName(fileProto.fqNameList))

View File

@@ -119,17 +119,17 @@ open class IrFileSerializer(
// The same type can be used multiple times in a file
// so use this index to store type data only once.
private val protoTypeMap = mutableMapOf<IrTypeKey, Int>()
private val protoTypeArray = arrayListOf<ProtoType>()
protected val protoTypeArray = arrayListOf<ProtoType>()
private val protoStringMap = mutableMapOf<String, Int>()
private val protoStringArray = arrayListOf<String>()
protected val protoStringArray = arrayListOf<String>()
// The same signature could be used multiple times in a file
// so use this index to store signature only once.
private val protoIdSignatureMap = mutableMapOf<IdSignature, Int>()
private val protoIdSignatureArray = arrayListOf<ProtoIdSignature>()
protected val protoIdSignatureArray = arrayListOf<ProtoIdSignature>()
private val protoBodyArray = mutableListOf<XStatementOrExpression>()
protected val protoBodyArray = mutableListOf<XStatementOrExpression>()
sealed class XStatementOrExpression {
abstract fun toByteArray(): ByteArray
@@ -178,7 +178,7 @@ open class IrFileSerializer(
protoStringArray.size - 1
}
private fun serializeName(name: Name): Int = serializeString(name.toString())
protected fun serializeName(name: Name): Int = serializeString(name.toString())
/* ------- IdSignature ------------------------------------------------------ */
@@ -232,7 +232,7 @@ open class IrFileSerializer(
return proto.build()
}
private fun protoIdSignature(declaration: IrDeclaration): Int {
protected fun protoIdSignature(declaration: IrDeclaration): Int {
val idSig = declarationTable.signatureByDeclaration(declaration)
return protoIdSignature(idSig)
}
@@ -283,7 +283,7 @@ open class IrFileSerializer(
TODO("Unexpected symbol kind: $symbol")
}
fun serializeIrSymbol(symbol: IrSymbol): Long {
open fun serializeIrSymbol(symbol: IrSymbol): Long {
val declaration = symbol.owner as? IrDeclaration ?: error("Expected IrDeclaration: ${symbol.owner.render()}")
val symbolKind = protoSymbolKind(symbol)
@@ -294,10 +294,10 @@ open class IrFileSerializer(
/* ------- IrTypes ---------------------------------------------------------- */
private fun serializeAnnotations(annotations: List<IrConstructorCall>) =
protected fun serializeAnnotations(annotations: List<IrConstructorCall>) =
annotations.map { serializeConstructorCall(it) }
private fun serializeFqName(fqName: String): List<Int> = fqName.split(".").map(::serializeString)
protected fun serializeFqName(fqName: String): List<Int> = fqName.split(".").map(::serializeString)
private fun serializeIrStarProjection() = BinaryTypeProjection.STAR_CODE
@@ -1130,7 +1130,7 @@ open class IrFileSerializer(
return proto.build()
}
private fun serializeIrClass(clazz: IrClass): ProtoClass {
protected fun serializeIrClass(clazz: IrClass): ProtoClass {
val proto = ProtoClass.newBuilder()
.setBase(serializeIrDeclarationBase(clazz, ClassFlags.encode(clazz)))
.setName(serializeName(clazz.name))
@@ -1185,7 +1185,7 @@ open class IrFileSerializer(
return proto.build()
}
private fun serializeDeclaration(declaration: IrDeclaration): ProtoDeclaration {
protected fun serializeDeclaration(declaration: IrDeclaration): ProtoDeclaration {
val proto = ProtoDeclaration.newBuilder()
when (declaration) {

View File

@@ -199,13 +199,7 @@ abstract class DescriptorMangleComputer(protected val builder: StringBuilder, pr
is DynamicType -> tBuilder.appendSignature(MangleConstant.DYNAMIC_MARK)
is FlexibleType -> {
// TODO: is that correct way to mangle flexible type?
with(MangleConstant.FLEXIBLE_TYPE) {
tBuilder.appendSignature(prefix)
mangleType(tBuilder, type.lowerBound)
tBuilder.appendSignature(separator)
mangleType(tBuilder, type.upperBound)
tBuilder.appendSignature(suffix)
}
mangleType(tBuilder, type.upperBound)
}
else -> error("Unexpected type $wtype")
}

View File

@@ -12,9 +12,7 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.isVararg
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.types.Variance
@@ -257,7 +255,8 @@ abstract class IrMangleComputer(protected val builder: StringBuilder, private va
isRealExpect = isRealExpect or declaration.isExpect
val container = declaration.correspondingPropertySymbol?.owner ?: declaration
val isStatic = declaration.dispatchReceiverParameter == null && container.parent !is IrPackageFragment
val isStatic = declaration.dispatchReceiverParameter == null &&
(container.parent !is IrPackageFragment && !container.parent.isFacadeClass)
declaration.mangleFunction(false, isStatic, container)
}

View File

@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.ir.overrides.isOverridableFunction
import org.jetbrains.kotlin.ir.overrides.isOverridableProperty
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.KotlinMangler
import org.jetbrains.kotlin.ir.util.isFacadeClass
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
@@ -49,7 +50,9 @@ open class IdSignatureSerializer(val mangler: KotlinMangler.IrMangler) : IdSigna
private fun collectFqNames(declaration: IrDeclarationWithName) {
declaration.parent.acceptVoid(this)
classFqnSegments.add(declaration.name.asString())
if (declaration !is IrClass || !declaration.isFacadeClass) {
classFqnSegments.add(declaration.name.asString())
}
}
override fun visitElement(element: IrElement) = error("Unexpected element ${element.render()}")
@@ -95,6 +98,10 @@ open class IdSignatureSerializer(val mangler: KotlinMangler.IrMangler) : IdSigna
override fun visitEnumEntry(declaration: IrEnumEntry) {
collectFqNames(declaration)
}
override fun visitField(declaration: IrField) {
collectFqNames(declaration)
}
}
private val publicSignatureBuilder = PublicIdSigBuilder()

View File

@@ -11,6 +11,7 @@ dependencies {
compile(project(":core:metadata.jvm"))
implementation(project(":core:deserialization.common.jvm"))
compile(project(":compiler:frontend.java"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core", rootProject = rootProject) }
}
sourceSets {

View File

@@ -0,0 +1,84 @@
syntax = "proto2";
package org.jetbrains.kotlin.backend.jvm.serialization.proto;
import "compiler/ir/serialization.common/src/KotlinIr.proto";
option java_outer_classname = "JvmIr";
option optimize_for = LITE_RUNTIME;
/* Stored in JVM .class annotations */
//message UniqIdInfo {
// required fixed64 id = 1;
// repeated int32 toplevel_fq_name = 2;
//}
//message SignatureTable {
// repeated common.serialization.proto.IrSignature signature = 1;
//}
//
//message TypeTable {
// repeated common.serialization.proto.IrType type = 1;
//}
//message UniqIdTable {
// repeated UniqIdInfo infos = 1;
//}
//message StringTable {
// repeated string string = 1;
//}
message XStatementOrExpression {
oneof kind {
common.serialization.proto.IrStatement statement = 1;
common.serialization.proto.IrExpression expression = 2;
}
}
//message StatementsAndExpressionsTable {
// repeated XStatementOrExpression statement_or_expression = 1;
//}
message FacadeClassInfo {
required int32 signature = 1; /* signature index for a file-level declaration */
required int32 facade_class_name = 2; /* string index for file class name */
}
//
//message JvmExternalPackage {
// repeated int32 fq_name = 1;
// required common.serialization.proto.IrDeclarationContainer declaration_container = 2;
//}
//message ExternalRefs {
// repeated JvmExternalPackage package = 1;
// repeated ExternalReference reference = 2;
//}
message AuxTables {
repeated bytes type = 1;
repeated bytes signature = 2;
repeated bytes string = 3;
repeated bytes body = 4;
repeated FacadeClassInfo facade_class_info = 5;
// required TypeTable type_table = 2;
// required SignatureTable signature_table = 1;
// required StringTable string_table = 3;
// required StatementsAndExpressionsTable statements_and_expressions_table = 4;
// required ExternalRefs external_refs = 5;
// required UniqIdTable uniq_id_table = 6;
}
message JvmIrFile {
repeated common.serialization.proto.IrDeclaration declaration = 1;
repeated common.serialization.proto.IrConstructorCall annotation = 2;
required int32 facade_fq_name = 3;
required AuxTables aux_tables = 4;
}
message JvmIrClass {
required common.serialization.proto.IrClass ir_class = 1;
required AuxTables aux_tables = 2;
}

View File

@@ -0,0 +1,12 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.jvm.serialization
import org.jetbrains.kotlin.backend.common.serialization.GlobalDeclarationTable
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureSerializer
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmManglerIr
class JvmGlobalDeclarationTable : GlobalDeclarationTable(IdSignatureSerializer(JvmManglerIr), JvmManglerIr)

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.jvm.serialization
import org.jetbrains.kotlin.backend.common.serialization.DeclarationTable
import org.jetbrains.kotlin.backend.common.serialization.IrFileSerializer
import org.jetbrains.kotlin.backend.jvm.serialization.proto.JvmIr
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.fileClasses.JvmSimpleFileClassInfo
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.ir.util.NaiveSourceBasedFileEntryImpl
import org.jetbrains.kotlin.ir.util.isFacadeClass
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.protobuf.ByteString
import org.jetbrains.kotlin.psi2ir.PsiSourceManager
class JvmIrSerializer(
messageLogger: IrMessageLogger,
declarationTable: DeclarationTable,
expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>,
private val psiSourceManager: PsiSourceManager,
externallyVisibleOnly: Boolean = true,
skipExpects: Boolean = false,
) : IrFileSerializer(messageLogger, declarationTable, expectDescriptorToSymbol, externallyVisibleOnly, skipExpects) {
// Usage protocol: construct an instance, call only one of `serializeIrFile()` and `serializeTopLevelClass()` only once.
private class FacadeClassInfo(val signature: Int, val facadeName: Int)
private val facadeInfoArray = mutableListOf<FacadeClassInfo>()
fun serializeJvmIrFile(irFile: IrFile): JvmIr.JvmIrFile {
val proto = JvmIr.JvmIrFile.newBuilder()
irFile.declarations.filter { it !is IrClass }.forEach { declaration ->
proto.addDeclaration(serializeDeclaration(declaration))
}
proto.addAllAnnotation(serializeAnnotations(irFile.annotations))
val facadeFqName = irFile.facadeFqName()
proto.addAllFacadeFqName(serializeFqName(facadeFqName.toString()))
// TODO -- serialize referencesToTopLevelMap
proto.auxTables = serializeAuxTables()
return proto.build()
}
fun serializeTopLevelClass(irClass: IrClass): JvmIr.JvmIrClass {
val proto = JvmIr.JvmIrClass.newBuilder()
proto.irClass = serializeIrClass(irClass)
proto.auxTables = serializeAuxTables()
return proto.build()
}
override fun serializeIrSymbol(symbol: IrSymbol): Long {
val declaration = symbol.owner as? IrDeclaration ?: error("Expected IrDeclaration: ${symbol.owner.render()}")
val parent = declaration.parent
if ((parent is IrPackageFragment || parent.isFacadeClass) && declaration is IrMemberWithContainerSource && declaration !is IrClass) {
val facadeClassName = declaration.facadeName() ?: return super.serializeIrSymbol(symbol)
val facadeId = serializeName(facadeClassName)
val signatureId = protoIdSignature(declaration)
facadeInfoArray.add(FacadeClassInfo(signatureId, facadeId))
}
return super.serializeIrSymbol(symbol)
}
private fun serializeAuxTables(): JvmIr.AuxTables {
val proto = JvmIr.AuxTables.newBuilder()
protoTypeArray.forEach { proto.addType(it.toByteString()) }
protoIdSignatureArray.forEach { proto.addSignature(it.toByteString()) }
protoStringArray.forEach { proto.addString(ByteString.copyFromUtf8(it)) }
protoBodyArray.forEach { proto.addBody(ByteString.copyFrom(it.toByteArray())) }
facadeInfoArray.forEach {
proto.addFacadeClassInfo(JvmIr.FacadeClassInfo.newBuilder().run {
signature = it.signature
facadeClassName = it.facadeName
build()
})
}
return proto.build()
}
fun IrFile.facadeFqName(): FqName {
val fileClassInfo = when (val fileEntry = fileEntry) {
is PsiSourceManager.PsiFileEntry -> {
val ktFile = psiSourceManager.getKtFile(fileEntry)
?: throw AssertionError("Unexpected file entry: $fileEntry")
JvmFileClassUtil.getFileClassInfoNoResolve(ktFile)
}
is NaiveSourceBasedFileEntryImpl -> {
JvmSimpleFileClassInfo(PackagePartClassUtils.getPackagePartFqName(fqName, fileEntry.name), false)
}
else -> error("unknown kind of file entry: $fileEntry")
}
return fileClassInfo.fileClassFqName
}
fun IrMemberWithContainerSource.facadeName(): Name? {
val parent = parent
if (this is IrClass) return null
return when (parent) {
is IrFile -> parent.facadeFqName().shortName()
is IrClass -> {
assert(parent.isFacadeClass)
parent.name
}
is IrExternalPackageFragment -> {
val source = containerSource as? JvmPackagePartSource ?: return null
val facadeName = source.facadeClassName ?: source.className
facadeName.fqNameForTopLevelClassMaybeWithDollars.shortName()
}
else -> error("Unknown IrPackageFragment kind: $parent")
}
}
}

View File

@@ -0,0 +1,268 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.jvm.serialization
import org.jetbrains.kotlin.backend.common.ir.createParameterDeclarations
import org.jetbrains.kotlin.backend.common.overrides.DefaultFakeOverrideClassFilter
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
import org.jetbrains.kotlin.backend.common.overrides.FileLocalAwareLinker
import org.jetbrains.kotlin.backend.common.serialization.*
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureSerializer
import org.jetbrains.kotlin.backend.jvm.serialization.proto.JvmIr
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmManglerDesc
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmManglerIr
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.linkage.IrProvider
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.impl.*
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.load.kotlin.*
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.protobuf.ByteString
import org.jetbrains.kotlin.resolve.descriptorUtil.propertyIfAccessor
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
/* Reads serialized IR from annotations in classfiles */
class SingleClassJvmIrProvider(
val moduleDescriptor: ModuleDescriptor,
val irBuiltIns: IrBuiltIns,
val symbolTable: SymbolTable,
val irFactory: IrFactory,
val fileFinder: VirtualFileFinder,
) : IrProvider, FileLocalAwareLinker {
val descriptorFinder =
DescriptorByIdSignatureFinder(moduleDescriptor, JvmManglerDesc(), DescriptorByIdSignatureFinder.LookupMode.MODULE_WITH_DEPENDENCIES)
val packageFragments = mutableMapOf<FqName, IrExternalPackageFragment>()
val facadeClassMap = mutableMapOf<IdSignature, Name>()
override fun getDeclaration(symbol: IrSymbol): IrDeclaration? {
val fileClassInfo = findFacadeClassInfo(symbol)
if (fileClassInfo != null)
loadIrFileFromFacadeClass(fileClassInfo.first, fileClassInfo.second)
else
symbol.signature?.let { loadToplevelClassBySignature(it) }
return if (symbol.isBound) (symbol.owner as IrDeclaration) else null
}
private fun loadIrFileFromFacadeClass(fileClassName: FqName, facadeName: FqName) {
val vfile = fileFinder.findVirtualFileWithHeader(ClassId.topLevel(fileClassName)) ?: return
val binaryClass = KotlinBinaryClassCache.getKotlinBinaryClassOrClassFileContent(vfile)?.toKotlinJvmBinaryClass() ?: return
val classHeader = binaryClass.classHeader
if (classHeader.serializedIr == null || classHeader.serializedIr!!.isEmpty()) return
val containerSource = getJvmPackagePartSource(binaryClass)
val irProto = JvmIr.JvmIrFile.parseFrom(classHeader.serializedIr)
val packageFragment = getPackageFragment(facadeName.parent(), containerSource)
val declarationDeserializer = getDeclarationDeserializer(packageFragment, irProto.auxTables)
val facadeClass = irFactory.buildClass {
name = facadeName.shortName()
origin = IrDeclarationOrigin.SYNTHETIC_FILE_CLASS
source = containerSource
}.apply {
parent = packageFragment
createParameterDeclarations()
// TODO: annotations
}
for (declProto in irProto.declarationList) {
val declaration = declarationDeserializer.deserializeDeclaration(declProto)
facadeClass.addMember(declaration)
declaration.parent = facadeClass
if (declaration is IrProperty) {
declaration.getter?.parent = facadeClass
declaration.setter?.parent = facadeClass
declaration.backingField?.parent = facadeClass
}
}
}
private fun loadToplevelClassBySignature(signature: IdSignature) {
if (signature !is IdSignature.PublicSignature) return
val classId = ClassId.topLevel(signature.packageFqName().child(Name.identifier(signature.firstNameSegment)))
val toplevelDescriptor = moduleDescriptor.findClassAcrossModuleDependencies(classId) ?: return
/* TODO: should not be needed after we deal with fake overrides. */
if (symbolTable.referenceClass(toplevelDescriptor).isBound) return
val source = toplevelDescriptor.source as? KotlinJvmBinarySourceElement ?: return
val classHeader = source.binaryClass.classHeader
if (classHeader.serializedIr == null || classHeader.serializedIr!!.isEmpty()) return
val irProto = JvmIr.JvmIrClass.parseFrom(classHeader.serializedIr)
val packageFragment = getPackageFragment(signature.packageFqName(), source)
val declarationDeserializer = getDeclarationDeserializer(packageFragment, irProto.auxTables)
declarationDeserializer.deserializeIrClass(irProto.irClass, source)
}
private fun getDeclarationDeserializer(packageFragment: IrPackageFragment, auxTables: JvmIr.AuxTables): IrDeclarationDeserializer {
val libraryFile = IrLibraryFileFromAnnotation(
auxTables.typeList,
auxTables.signatureList,
auxTables.stringList,
auxTables.bodyList
)
val symbolDeserializer = IrSymbolDeserializer(
symbolTable,
libraryFile,
/* TODO */ actuals = emptyList(),
enqueueLocalTopLevelDeclaration = {}, // just link to it in symbolTable
handleExpectActualMapping = { _, _ -> TODO() },
deserializePublicSymbol = ::referencePublicSymbol
)
populateFacadeClassMap(auxTables, libraryFile, symbolDeserializer)
return IrDeclarationDeserializer(
irBuiltIns,
symbolTable,
irFactory,
libraryFile,
packageFragment,
allowErrorNodes = false,
deserializeInlineFunctions = true,
deserializeBodies = true,
symbolDeserializer = symbolDeserializer,
platformFakeOverrideClassFilter = DefaultFakeOverrideClassFilter,
fakeOverrideBuilder = FakeOverrideBuilder(
linker = this,
symbolTable,
IdSignatureSerializer(JvmManglerIr),
irBuiltIns
)
)
}
fun id(x: Any?): Any? = x
class IrLibraryFileFromAnnotation(
val types: List<ByteString>,
val signatures: List<ByteString>,
val strings: List<ByteString>,
val bodies: List<ByteString>,
) : IrLibraryFile() {
override fun irDeclaration(index: Int): ByteArray {
error("This method is never supposed to be called")
}
override fun type(index: Int): ByteArray = types[index].toByteArray()
override fun signature(index: Int): ByteArray = signatures[index].toByteArray()
override fun string(index: Int): ByteArray = strings[index].toByteArray()
override fun body(index: Int): ByteArray = bodies[index].toByteArray()
}
private fun getPackageFragment(fqName: FqName, containerSource: DeserializedContainerSource?): IrExternalPackageFragment =
IrExternalPackageFragmentImpl(
IrExternalPackageFragmentSymbolImpl(object : PackageFragmentDescriptorImpl(moduleDescriptor, fqName) {
override fun getMemberScope() = MemberScope.Empty
}),
fqName,
containerSource
)
private fun referencePublicSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol {
with(symbolTable) {
val descriptor = descriptorFinder.findDescriptorBySignature(idSig)
return if (descriptor != null) {
when (symbolKind) {
BinarySymbolData.SymbolKind.CLASS_SYMBOL -> referenceClass(descriptor as ClassDescriptor)
BinarySymbolData.SymbolKind.CONSTRUCTOR_SYMBOL -> referenceConstructor(descriptor as ClassConstructorDescriptor)
BinarySymbolData.SymbolKind.ENUM_ENTRY_SYMBOL -> referenceEnumEntry(descriptor as ClassDescriptor)
BinarySymbolData.SymbolKind.STANDALONE_FIELD_SYMBOL -> referenceField(descriptor as PropertyDescriptor)
BinarySymbolData.SymbolKind.FUNCTION_SYMBOL -> referenceSimpleFunction(descriptor as FunctionDescriptor)
BinarySymbolData.SymbolKind.TYPEALIAS_SYMBOL -> referenceTypeAlias(descriptor as TypeAliasDescriptor)
BinarySymbolData.SymbolKind.PROPERTY_SYMBOL -> referenceProperty(descriptor as PropertyDescriptor)
else -> error("Unexpected classifier symbol kind: $symbolKind for signature $idSig")
}
} else {
when (symbolKind) {
BinarySymbolData.SymbolKind.CLASS_SYMBOL -> referenceClassFromLinker(idSig)
BinarySymbolData.SymbolKind.CONSTRUCTOR_SYMBOL -> referenceConstructorFromLinker(idSig)
BinarySymbolData.SymbolKind.ENUM_ENTRY_SYMBOL -> referenceEnumEntryFromLinker(idSig)
BinarySymbolData.SymbolKind.STANDALONE_FIELD_SYMBOL -> referenceFieldFromLinker(idSig)
BinarySymbolData.SymbolKind.FUNCTION_SYMBOL -> referenceSimpleFunctionFromLinker(idSig)
BinarySymbolData.SymbolKind.TYPEALIAS_SYMBOL -> referenceTypeAliasFromLinker(idSig)
BinarySymbolData.SymbolKind.PROPERTY_SYMBOL -> referencePropertyFromLinker(idSig)
else -> error("Unexpected classifier symbol kind: $symbolKind for signature $idSig")
}
}
}
}
private fun populateFacadeClassMap(auxTables: JvmIr.AuxTables, libraryFile: IrLibraryFile, symbolDeserializer: IrSymbolDeserializer) {
for (protoFacadeClassInfo in auxTables.facadeClassInfoList) {
val signature = symbolDeserializer.deserializeIdSignature(protoFacadeClassInfo.signature)
val name = Name.identifier(libraryFile.deserializeString(protoFacadeClassInfo.facadeClassName))
facadeClassMap[signature] = name
}
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
private fun findFacadeClassInfo(symbol: IrSymbol): Pair<FqName, FqName>? {
if (symbol.hasDescriptor) {
var descriptor = symbol.descriptor
if (descriptor is CallableMemberDescriptor) descriptor = descriptor.propertyIfAccessor
while (descriptor.containingDeclaration !is PackageFragmentDescriptor) {
descriptor = descriptor.containingDeclaration!!
}
if (descriptor is ClassDescriptor) return null
val source = (descriptor as? DescriptorWithContainerSource)?.containerSource as? JvmPackagePartSource ?: return null
val facadeName = source.facadeClassName ?: source.className
val fileClassName = source.className
return fileClassName.fqNameForTopLevelClassMaybeWithDollars to facadeName.fqNameForTopLevelClassMaybeWithDollars
} else {
error("No descriptor in symbol")
}
}
// Copied from KotlinDeserializedJvmSymbolsProvider.
private fun getJvmPackagePartSource(binaryClass: KotlinJvmBinaryClass): JvmPackagePartSource {
val header = binaryClass.classHeader
val data = header.data ?: header.incompatibleData ?: error("Should not happen")
val strings = header.strings ?: error("Should not happen")
val (nameResolver, packageProto) = JvmProtoBufUtil.readPackageDataFrom(data, strings)
return JvmPackagePartSource(
binaryClass, packageProto, nameResolver,
binaryClass.incompatibility, binaryClass.isPreReleaseInvisible
)
}
private val KotlinJvmBinaryClass.incompatibility: IncompatibleVersionErrorData<JvmMetadataVersion>?
get() {
// TODO: skipMetadataVersionCheck
if (classHeader.metadataVersion.isCompatible()) return null
return IncompatibleVersionErrorData(classHeader.metadataVersion, JvmMetadataVersion.INSTANCE, location, classId)
}
private val KotlinJvmBinaryClass.isPreReleaseInvisible: Boolean
get() = classHeader.isPreRelease
override fun tryReferencingPropertyByLocalSignature(parent: IrDeclaration, idSignature: IdSignature): IrPropertySymbol? = null
override fun tryReferencingSimpleFunctionByLocalSignature(parent: IrDeclaration, idSignature: IdSignature): IrSimpleFunctionSymbol? =
null
}

View File

@@ -20,7 +20,10 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.load.java.lazy.descriptors.isJavaField
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
abstract class AbstractJvmManglerIr : IrBasedKotlinManglerImpl() {
@@ -30,6 +33,11 @@ abstract class AbstractJvmManglerIr : IrBasedKotlinManglerImpl() {
private class JvmIrExportChecker : IrExportCheckerVisitor() {
override fun IrDeclaration.isPlatformSpecificExported() = false
override fun visitField(declaration: IrField, data: Nothing?): Boolean {
if (declaration.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB) return true
return super.visitField(declaration, data)
}
}
private class JvmIrManglerComputer(builder: StringBuilder, mode: MangleMode) : IrMangleComputer(builder, mode) {

View File

@@ -15,6 +15,7 @@ enum class TargetBackend(
JVM_IR(true, JVM),
JVM_MULTI_MODULE_IR_AGAINST_OLD(true, JVM_IR),
JVM_MULTI_MODULE_OLD_AGAINST_IR(false, JVM),
JVM_IR_SERIALIZE(true, JVM_IR),
JS(false),
JS_IR(true, JS),
JS_IR_ES6(true, JS_IR),

View File

@@ -107,6 +107,7 @@ where advanced options include:
problems with parentheses in identifiers on certain platforms
-Xscript-resolver-environment=<key=value[,]>
Script resolver environment in key-value pairs (the value could be quoted and escaped)
-Xserialize-ir Save IR to metadata
-Xsingle-module Combine modules for source files and binary dependencies into a single module
-Xskip-runtime-version-check Allow Kotlin runtime libraries of incompatible versions in the classpath
-Xstrict-java-nullability-assertions
@@ -194,4 +195,3 @@ where advanced options include:
Advanced options are non-standard and may be changed or removed without any notice.
OK

View File

@@ -0,0 +1,18 @@
// !LANGUAGE: +CompileTimeCalculations
@CompileTimeCalculation
abstract class ConstExprClass {
abstract fun getSomeValue(): Int
}
@CompileTimeCalculation
class A(val a: Int): ConstExprClass() {
override fun getSomeValue() = a
}
class B @CompileTimeCalculation constructor(val b: Int): ConstExprClass() {
override fun getSomeValue() = b
}
const val a = A(1).getSomeValue()
const val b = B(2).getSomeValue()

View File

@@ -0,0 +1,18 @@
// !LANGUAGE: +CompileTimeCalculations
@CompileTimeCalculation
abstract class ConstExprClass {
abstract fun getSomeValue(): Int
}
@CompileTimeCalculation
class A(val a: Int): ConstExprClass() {
override fun getSomeValue() = a
}
class B @CompileTimeCalculation constructor(val b: Int): ConstExprClass() {
<!COMPILE_TIME_MEMBER_NOT_IMPLEMENTED!>override fun getSomeValue() = b<!>
}
const val a = A(1).getSomeValue()
const val b = B(2).getSomeValue()

Some files were not shown because too many files have changed in this diff Show More