Consider a context with uninitialized this, e.g.:
fun foo() {
val x = "..."
class Local(y: String) : Base(L@{ x + y })
}
Lambda 'L' is an argument of a super class constructor call.
Here 'this@Local' is not initialized yet. Thus local variables captured
in 'Local' can't be used. Instead, they should be captured by lambda 'L'
itself.
Note that lambda 'L' sees both 'x' and 'y' as local variables that
should be captured.
When in context with uninitialized this (generating arguments for super
type constructor or delegating constructor call), and a variable in
question is not found in the current context, use enclosing local lookup
to determine whether a local variable should be captured by a closure.
Enclosing class for closure is a class whose instance is captured by
closure as an outer 'this', and stored in a field 'this$0'.
Usually enclosing class for closure is an immediate outer class,
including classes for nested closures. For example:
class C {
fun foo() {}
val example1 = L1@ { foo() }
// Enclosing class for lambda 'L1' is 'C'
val example2 = L2a@ { L2b@ { foo() } }
// Enclosing class for nested lambda 'L2b'
// is a closure class for outer lambda 'L2a'
}
However, if the closure is created in a super type constructor call for
the outer class, corresponding instance is considered "uninitialized",
and can't be used as a proper class instance, and can't be referenced:
corresponding code is rejected by front-end.
class Outer {
fun foo() {}
inner class Inner : Base(L3@ { foo() })
// Enclosing class for lambda 'L3' is 'Outer',
// because 'Inner' is uninitialized in super type constructor call.
}
In CodegenAnnotatingVisitor, we maintain a stack of currently
uninitialized classes, and chose enclosing class for closure
as an inner-most surrounding class with initialized instance.
When generating code for this or outer class instance, we skip
contexts corresponding to classes with uninitialized instances.
This fixes a number of bytecode verification errors caused by incorrect
enclosing class for closure.
#KT-4174 Fixed Target versions 1.2.20
#KT-13454 Fixed Target versions 1.2.20
#KT-14148 Fixed Target versions 1.2.20
Fix generating multifile facade with all members private (in bytecode)
leading to delegate not being generated for corresponding light class
#KT-20966 Fixed
From Kotlin's point of view, everything in annotation classes is
non-abstract. A class inheriting from an annotation has a non-abstract
fake override for each property of the annotation class constructor. But
because members of annotation classes themselves were considered as
abstract in the bridge-generating code (see
DescriptorBasedFunctionHandle.isAbstract), there was a situation where a
concrete fake override has only one declaration among overridden
descriptors and it was abstract. This situation is invalid (a concrete
fake override must have exactly one concrete super-declaration),
therefore an exception was thrown.
The fix is to avoid considering annotation class members abstract for
the purposes of bridge generation. It's reasonably safe because no
bridges should be ever generated for annotation subclasses anyway,
because annotations can only have members with simple return types
(final and non-generic).
Note that in KT-19928, the problem is reproducible because of an
incorrect "inexact analysis" in light classes where "Target" is resolved
to an annotation class kotlin.annotation.Target. This behavior of the
analysis in light classes seems to do no harm otherwise, so it's not a
goal of this commit to change anything in that regard
#KT-19928 Fixed
When a local function or class A creates an instance of a local class B
capturing an outer variable 'x', it should use ref for 'x', but not the
value of 'x'.
When a local function is captured, corresponding field accesses are
later transformed by the inliner. It doesn't have enough information to
restore the original semantics completely, so it has to rely on field
names. Local functions can be overloaded or can have names matching
local variable names, in both cases we generated fields with the same
name for captured values.
Now, we use the same '$<local-class-number>' suffix for field names for
local functions as it is present in the corresponding local class name.
This allows to distinguish captured local functions from captured local
variables and between different overloads of a function with the same
name.
#KT-19827 Fixed
#KT-18639 Fixed
Given a singleton class 'S' with possibly uninitialized static instance
(enum entry, interface companion object).
Such singleton can be referenced by name, or as an explicit or implicit
'this'.
For a given singleton class 'S' we
either use 'this@S' from context (local or captured),
or 'S' as a static instance.
Local or captured 'this@S' should be used if:
- we are in the constructor for 'S',
and corresponding instance is initialized
by super or delegating constructor call;
- we are in any other member of 'S' or any of its inner classes.
Otherwise, a static instance should be used.
If an expression with type annotated with @EnhancedNullability is used
as a function expression body, or property initializer, or variable
initializer, and corresponding type can not contain null,
generate nullability assertions for this expression.
It might differ from the JVM package FQ name if the JvmPackageName
annotation is used. This will be useful for faster indexing in the IDE
and for reflection
Constant expressions are inlined if they do not depend on non-inlineable
vals.
Java constants are always inlined.
Kotlin constants are inlined in LV 1.1+.
Codegen generates static backing fields for object properties.
They are initialized in class constructor but some of them are final static
and such access is prohibited in specification but it's allowed in
java bytecode <= 1.8. Such access in 1.9 bytecode cause
"IllegalAccessError: Update to static final field Object.INSTANCE
attempted from a different method (<init>) than the initializer method <clinit>"
Added additional hidden field in interface companion to pass out
companion instance from <clinit>.
#KT-15894 Fixed
Three modes:
- 'disable' (default): normalize constructor calls in coroutines only
(required because uninitialized objects can't be stored in fields),
don't insert additional code for forced class initialization;
- 'enable': normalize constructor calls,
don't insert additional code for forced class initialization;
- 'preserve-class-initialization': normalize constructor calls,
insert additional code for forced class initialization.
Stack should be spilled before inline function call and restored after
call only if one of the following conditions is met:
- inline function is a suspend function
- inline function has try-catch blocks
- inline function has loops (backward jumps)
Note that there're quite some "simple" inline functions in Kotlin stdlib
besides run/let/with/apply. For example, many string operations are
implemented as inline wrappers over Java method calls.
Singleton instance is "initialized" by delegating constructor call,
which is superclass constructor call in case of singletons (because
singletons can't have more than one constructor).
Singleton constructor is effectively split into two stages:
- before a super constructor call;
- after a super constructor call.
Before super constructor call, singleton instance can't be used directly
(see KT-20662), because neither 'this' nor static instance is
initialized yet. However, it can be used in closures, in which case a
static instance should be used (escaping uninitialized this is
prohibited by JVM). Actually using this static instance before it is
initialized (e.g., invoking a method that uses this singleton) will
cause a correct ExceptionInInitializerError.
After a super constructor call, static instance of a singleton may be
not initialized yet (in case of enum entries and interface companion
objects). However, we already have an initialized 'this', which we
should use for singleton references.
#KT-20651 Fixed
Instead of a separate analysis pass to determine variable types at the
point of null checks, use current data flow information and transform
possibly nullable values to definitely non-null values using a special
intrinsic.
This allows to perform a single data flow analysis pass per RNCE
transformation pass (instead of two passes).
Jump out from expression (e.g., break or continue expression in call
arguments) requires stack normalization, which inserts POP instructions.
POPping an uninitialized value is similar to ASTORE, except that it
doesn't store a value to a local variable. Such POP instructions should
be removed during postprocessing of the uninitialized stores.