Inner classes (#207)
* Inner classes lowering * cmd line fix * tests * * reverted adding parameter to inner classes constructors * variables are identified by their descriptors * review fixes
This commit is contained in:
+33
-11
@@ -1,24 +1,46 @@
|
||||
package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.ir.Ir
|
||||
import org.jetbrains.kotlin.backend.konan.ir.ModuleIndex
|
||||
import org.jetbrains.kotlin.backend.konan.KonanBackendContext
|
||||
import org.jetbrains.kotlin.backend.konan.KonanPhase
|
||||
import org.jetbrains.kotlin.backend.konan.KonanConfig
|
||||
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import llvm.LLVMDumpModule
|
||||
import llvm.LLVMModuleRef
|
||||
import org.jetbrains.kotlin.backend.jvm.descriptors.initialize
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.deepPrint
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
||||
import org.jetbrains.kotlin.backend.konan.ir.Ir
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.Llvm
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.LlvmDeclarations
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.verifyModule
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.util.DumpIrTreeVisitor
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||
import java.lang.System.out
|
||||
|
||||
internal class SpecialDescriptorsFactory {
|
||||
private val outerThisDescriptors = mutableMapOf<ClassDescriptor, PropertyDescriptor>()
|
||||
|
||||
fun getOuterThisFieldDescriptor(innerClassDescriptor: ClassDescriptor): PropertyDescriptor =
|
||||
if (!innerClassDescriptor.isInner) throw AssertionError("Class is not inner: $innerClassDescriptor")
|
||||
else outerThisDescriptors.getOrPut(innerClassDescriptor) {
|
||||
val outerClassDescriptor = DescriptorUtils.getContainingClass(innerClassDescriptor) ?:
|
||||
throw AssertionError("No containing class for inner class $innerClassDescriptor")
|
||||
|
||||
val receiver = ReceiverParameterDescriptorImpl(innerClassDescriptor, ImplicitClassReceiver(innerClassDescriptor))
|
||||
PropertyDescriptorImpl.create(innerClassDescriptor, Annotations.EMPTY, Modality.FINAL, Visibilities.PRIVATE,
|
||||
false, "this$0".synthesizedName, CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE,
|
||||
false, false, false, false, false, false).initialize(outerClassDescriptor.defaultType, dispatchReceiverParameter = receiver)
|
||||
}
|
||||
}
|
||||
|
||||
internal final class Context(val config: KonanConfig) : KonanBackendContext() {
|
||||
|
||||
var moduleDescriptor: ModuleDescriptor? = null
|
||||
|
||||
val specialDescriptorsFactory = SpecialDescriptorsFactory()
|
||||
|
||||
// TODO: make lateinit?
|
||||
var irModule: IrModuleFragment? = null
|
||||
set(module: IrModuleFragment?) {
|
||||
|
||||
+3
@@ -20,6 +20,9 @@ internal class KonanLower(val context: Context) {
|
||||
phaser.phase(KonanPhase.LOWER_ENUMS) {
|
||||
EnumClassLowering(context).run(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_INNER_CLASSES) {
|
||||
InnerClassLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_DEFAULT_PARAMETER_EXTENT) {
|
||||
DefaultParameterStubGenerator(context).runOnFilePostfix(irFile)
|
||||
DefaultParameterInjector(context).runOnFilePostfix(irFile)
|
||||
|
||||
+1
@@ -19,6 +19,7 @@ enum class KonanPhase(val description: String,
|
||||
/* ... ... */ LOWER_INLINE("Functions inlining"),
|
||||
/* ... ... */ AUTOBOX("Autoboxing of primitive types"),
|
||||
/* ... ... */ LOWER_ENUMS("Enum classes lowering"),
|
||||
/* ... ... */ LOWER_INNER_CLASSES("Inner classes lowering"),
|
||||
/* ... */ BITCODE("LLVM BitCode Generation"),
|
||||
/* ... ... */ RTTI("RTTI Generation"),
|
||||
/* ... ... */ CODEGEN("Code Generation"),
|
||||
|
||||
+11
-9
@@ -134,16 +134,18 @@ internal fun KotlinType.isUnboundCallableReference(): Boolean {
|
||||
|
||||
internal val CallableDescriptor.allValueParameters: List<ParameterDescriptor>
|
||||
get() {
|
||||
val constructorReceiver = if (this is ConstructorDescriptor) {
|
||||
this.constructedClass.thisAsReceiverParameter
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val receivers = mutableListOf<ParameterDescriptor>()
|
||||
|
||||
val receivers = listOf(
|
||||
constructorReceiver,
|
||||
this.dispatchReceiverParameter,
|
||||
this.extensionReceiverParameter).filterNotNull()
|
||||
if (this is ConstructorDescriptor)
|
||||
receivers.add(this.constructedClass.thisAsReceiverParameter)
|
||||
|
||||
val dispatchReceiverParameter = this.dispatchReceiverParameter
|
||||
if (dispatchReceiverParameter != null)
|
||||
receivers.add(dispatchReceiverParameter)
|
||||
|
||||
val extensionReceiverParameter = this.extensionReceiverParameter
|
||||
if (extensionReceiverParameter != null)
|
||||
receivers.add(extensionReceiverParameter)
|
||||
|
||||
return receivers + this.valueParameters
|
||||
}
|
||||
|
||||
+13
-11
@@ -27,8 +27,13 @@ internal class VariableManager(val codegen: CodeGenerator) {
|
||||
override fun toString() = "value of ${value} from ${name}"
|
||||
}
|
||||
|
||||
data class ScopedVariable private constructor(val descriptor: ValueDescriptor, val context: CodeContext)
|
||||
{
|
||||
constructor(scoped: Pair<ValueDescriptor, CodeContext>) : this(scoped.first, scoped.second)
|
||||
}
|
||||
|
||||
val variables: ArrayList<Record> = arrayListOf()
|
||||
val contextVariablesToIndex: HashMap<Pair<Name, CodeContext>, Int> = hashMapOf()
|
||||
val contextVariablesToIndex: HashMap<ScopedVariable, Int> = hashMapOf()
|
||||
|
||||
// Clears inner state of variable manager.
|
||||
fun clear() {
|
||||
@@ -52,11 +57,11 @@ internal class VariableManager(val codegen: CodeGenerator) {
|
||||
fun createMutable(scoped: Pair<VariableDescriptor, CodeContext>,
|
||||
isVar: Boolean, value: LLVMValueRef? = null) : Int {
|
||||
val descriptor = scoped.first
|
||||
val scopedVariable = scoped.first.name to scoped.second
|
||||
val scopedVariable = ScopedVariable(scoped)
|
||||
assert(!contextVariablesToIndex.contains(scopedVariable))
|
||||
val index = variables.size
|
||||
val type = codegen.getLLVMType(descriptor.type)
|
||||
val slot = codegen.alloca(type, descriptor.name.asString())
|
||||
val slot = codegen.alloca(type, scopedVariable.descriptor.name.asString())
|
||||
if (value != null)
|
||||
codegen.storeAnyLocal(value, slot)
|
||||
variables.add(SlotRecord(slot, codegen.isObjectType(type), isVar))
|
||||
@@ -85,21 +90,18 @@ internal class VariableManager(val codegen: CodeGenerator) {
|
||||
}
|
||||
|
||||
fun createImmutable(scoped: Pair<ValueDescriptor, CodeContext>, value: LLVMValueRef) : Int {
|
||||
val descriptor = scoped.first
|
||||
val scopedVariable = scoped.first.name to scoped.second
|
||||
val scopedVariable = ScopedVariable(scoped)
|
||||
if(contextVariablesToIndex.containsKey(scopedVariable))
|
||||
throw Error("${scopedVariable.descriptor} is already defined")
|
||||
assert(!contextVariablesToIndex.containsKey(scopedVariable))
|
||||
val index = variables.size
|
||||
variables.add(ValueRecord(value, descriptor.name))
|
||||
variables.add(ValueRecord(value, scopedVariable.descriptor.name))
|
||||
contextVariablesToIndex[scopedVariable] = index
|
||||
return index
|
||||
}
|
||||
|
||||
fun indexOf(scoped: Pair<ValueDescriptor, CodeContext>) : Int {
|
||||
return indexOfNamed(scoped.first.name to scoped.second)
|
||||
}
|
||||
|
||||
private fun indexOfNamed(scoped: Pair<Name, CodeContext>) : Int {
|
||||
return contextVariablesToIndex.getOrElse(scoped) { -1 }
|
||||
return contextVariablesToIndex.getOrElse(ScopedVariable(scoped)) { -1 }
|
||||
}
|
||||
|
||||
fun addressOf(index: Int): LLVMValueRef {
|
||||
|
||||
+15
-6
@@ -75,6 +75,18 @@ class DefaultParameterStubGenerator internal constructor(val context: Context):
|
||||
val params = mutableListOf<VariableDescriptor>()
|
||||
val variables = mutableMapOf<VariableDescriptor, VariableDescriptor>()
|
||||
|
||||
val newExtensionReceiver =
|
||||
if (description.function.extensionReceiverParameter == null) {
|
||||
null
|
||||
} else {
|
||||
IrGetValueImpl(
|
||||
startOffset = irFunction.startOffset,
|
||||
endOffset = irFunction.endOffset,
|
||||
descriptor = description.function.extensionReceiverParameter!!,
|
||||
origin = null
|
||||
)
|
||||
}
|
||||
|
||||
for (valueParameter in functionDescriptor.valueParameters) {
|
||||
val parameterDescriptor = description.function.valueParameters[valueParameter.index]
|
||||
if (valueParameter.hasDefaultValue()) {
|
||||
@@ -92,6 +104,8 @@ class DefaultParameterStubGenerator internal constructor(val context: Context):
|
||||
/* Use previously calculated values in next expression. */
|
||||
exprBody.expression.transformChildrenVoid(object:IrElementTransformerVoid() {
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
if (expression.descriptor == functionDescriptor.extensionReceiverParameter)
|
||||
return newExtensionReceiver!!
|
||||
if (!variables.containsKey(expression.descriptor))
|
||||
return expression
|
||||
return irGet(variables[expression.descriptor] as VariableDescriptor)
|
||||
@@ -114,12 +128,7 @@ class DefaultParameterStubGenerator internal constructor(val context: Context):
|
||||
dispatchReceiver = irThis()
|
||||
}
|
||||
if (functionDescriptor.extensionReceiverParameter != null) {
|
||||
extensionReceiver = IrGetValueImpl(
|
||||
startOffset = irFunction.startOffset,
|
||||
endOffset = irFunction.endOffset,
|
||||
descriptor = functionDescriptor.extensionReceiverParameter!!,
|
||||
origin = null
|
||||
)
|
||||
extensionReceiver = newExtensionReceiver
|
||||
}
|
||||
params.forEachIndexed { i, variable ->
|
||||
putValueArgument(i, irGet(variable))
|
||||
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package org.jetbrains.kotlin.backend.konan.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
import org.jetbrains.kotlin.ir.util.transformFlat
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
|
||||
|
||||
internal class InnerClassLowering(val context: Context) : ClassLoweringPass {
|
||||
override fun lower(irClass: IrClass) {
|
||||
InnerClassTransformer(irClass).lowerInnerClass()
|
||||
}
|
||||
|
||||
private inner class InnerClassTransformer(val irClass: IrClass) {
|
||||
val classDescriptor = irClass.descriptor
|
||||
|
||||
lateinit var outerThisFieldDescriptor: PropertyDescriptor
|
||||
|
||||
fun lowerInnerClass() {
|
||||
if (!irClass.descriptor.isInner) return
|
||||
|
||||
createOuterThisField()
|
||||
lowerOuterThisReferences()
|
||||
lowerConstructors()
|
||||
}
|
||||
|
||||
object DECLARATION_ORIGIN_FIELD_FOR_OUTER_THIS :
|
||||
IrDeclarationOriginImpl("FIELD_FOR_OUTER_THIS")
|
||||
|
||||
private fun createOuterThisField() {
|
||||
outerThisFieldDescriptor = context.specialDescriptorsFactory.getOuterThisFieldDescriptor(classDescriptor)
|
||||
|
||||
irClass.declarations.add(IrFieldImpl(
|
||||
irClass.startOffset, irClass.endOffset,
|
||||
DECLARATION_ORIGIN_FIELD_FOR_OUTER_THIS,
|
||||
outerThisFieldDescriptor
|
||||
))
|
||||
}
|
||||
|
||||
private fun lowerConstructors() {
|
||||
irClass.declarations.transformFlat { irMember ->
|
||||
if (irMember is IrConstructor)
|
||||
lowerConstructor(irMember).singletonList()
|
||||
else
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun lowerConstructor(irConstructor: IrConstructor): IrConstructor {
|
||||
val descriptor = irConstructor.descriptor
|
||||
val startOffset = irConstructor.startOffset
|
||||
val endOffset = irConstructor.endOffset
|
||||
|
||||
val dispatchReceiver = descriptor.dispatchReceiverParameter!!
|
||||
|
||||
val blockBody = irConstructor.body as? IrBlockBody ?: throw AssertionError("Unexpected constructor body: ${irConstructor.body}")
|
||||
|
||||
val instanceInitializerIndex = blockBody.statements.indexOfFirst { it is IrInstanceInitializerCall }
|
||||
if (instanceInitializerIndex >= 0) {
|
||||
// Initializing constructor: initialize 'this.this$0' with '$outer'.
|
||||
blockBody.statements.add(
|
||||
instanceInitializerIndex,
|
||||
IrSetFieldImpl(
|
||||
startOffset, endOffset, outerThisFieldDescriptor,
|
||||
IrGetValueImpl(startOffset, endOffset, classDescriptor.thisAsReceiverParameter),
|
||||
IrGetValueImpl(startOffset, endOffset, dispatchReceiver)
|
||||
)
|
||||
)
|
||||
}
|
||||
else {
|
||||
// Delegating constructor: invoke old constructor with dispatch receiver '$outer'.
|
||||
val delegatingConstructorCall = (blockBody.statements.find { it is IrDelegatingConstructorCall } ?:
|
||||
throw AssertionError("Delegating constructor call expected: ${irConstructor.dump()}")
|
||||
) as IrDelegatingConstructorCall
|
||||
delegatingConstructorCall.dispatchReceiver = IrGetValueImpl(
|
||||
delegatingConstructorCall.startOffset, delegatingConstructorCall.endOffset, dispatchReceiver
|
||||
)
|
||||
}
|
||||
|
||||
return irConstructor
|
||||
}
|
||||
|
||||
private fun lowerOuterThisReferences() {
|
||||
irClass.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
val implicitThisClass = expression.descriptor.getClassDescriptorForImplicitThis() ?:
|
||||
return expression
|
||||
|
||||
if (implicitThisClass == classDescriptor) return expression
|
||||
|
||||
val startOffset = expression.startOffset
|
||||
val endOffset = expression.endOffset
|
||||
val origin = expression.origin
|
||||
|
||||
var irThis: IrExpression = IrGetValueImpl(startOffset, endOffset, classDescriptor.thisAsReceiverParameter, origin)
|
||||
var innerClass = classDescriptor
|
||||
|
||||
while (innerClass != implicitThisClass) {
|
||||
if (!innerClass.isInner) {
|
||||
// Captured 'this' unrelated to inner classes nesting hierarchy, leave it as is -
|
||||
// should be transformed by closures conversion.
|
||||
return expression
|
||||
}
|
||||
|
||||
val outerThisField = context.specialDescriptorsFactory.getOuterThisFieldDescriptor(innerClass)
|
||||
irThis = IrGetFieldImpl(startOffset, endOffset, outerThisField, irThis, origin)
|
||||
|
||||
val outer = classDescriptor.containingDeclaration
|
||||
innerClass = outer as? ClassDescriptor ?:
|
||||
throw AssertionError("Unexpected containing declaration for inner class $innerClass: $outer")
|
||||
}
|
||||
|
||||
return irThis
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun ValueDescriptor.getClassDescriptorForImplicitThis(): ClassDescriptor? {
|
||||
if (this is ReceiverParameterDescriptor) {
|
||||
val receiverValue = value
|
||||
if (receiverValue is ImplicitClassReceiver) {
|
||||
return receiverValue.classDescriptor
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,6 +342,26 @@ task enum_companionObject(type: RunKonanTest) {
|
||||
source = "codegen/enum/companionObject.kt"
|
||||
}
|
||||
|
||||
task innerClass_simple(type: RunKonanTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/innerClass/simple.kt"
|
||||
}
|
||||
|
||||
task innerClass_getOuterVal(type: RunKonanTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/innerClass/getOuterVal.kt"
|
||||
}
|
||||
|
||||
task innerClass_generic(type: RunKonanTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/innerClass/generic.kt"
|
||||
}
|
||||
|
||||
task innerClass_qualifiedThis(type: RunKonanTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/innerClass/qualifiedThis.kt"
|
||||
}
|
||||
|
||||
task array0(type: RunKonanTest) {
|
||||
goldValue = "5\n6\n7\n8\n9\n10\n11\n12\n13\n"
|
||||
source = "runtime/collections/array0.kt"
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
class Outer {
|
||||
inner class Inner<T>(val t: T) {
|
||||
fun box() = t
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
if (Outer().Inner("OK").box() != "OK") return "Fail"
|
||||
val x: Outer.Inner<String> = Outer().Inner("OK")
|
||||
return x.box()
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(box())
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
class Outer(val s: String) {
|
||||
inner class Inner {
|
||||
fun box() = s
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = Outer("OK").Inner().box()
|
||||
|
||||
fun main(args: Array<String>)
|
||||
{
|
||||
println(box())
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
open class ABase
|
||||
{
|
||||
open fun zzz() = "a_base"
|
||||
}
|
||||
|
||||
open class BBase
|
||||
{
|
||||
open fun zzz() = "b_base"
|
||||
}
|
||||
|
||||
class D() {
|
||||
val z = "d"
|
||||
}
|
||||
|
||||
class A: ABase() { // implicit label @A
|
||||
val z = "a"
|
||||
override fun zzz() = "a"
|
||||
inner class B: BBase() { // implicit label @B
|
||||
val z = "b"
|
||||
override fun zzz() = "b"
|
||||
fun D.foo() : String { // implicit label @foo
|
||||
if(this@A.z != "a") return "Fail1"
|
||||
if(this@B.z != "b") return "Fail2"
|
||||
|
||||
if(super@A.zzz() != "a_base") return "Fail3"
|
||||
if(super<BBase>.zzz() != "b_base") return "Fail4"
|
||||
if(super@B.zzz() != "b_base") return "Fail5"
|
||||
if(this@A.zzz() != "a") return "Fail6"
|
||||
if(this@B.zzz() != "b") return "Fail7"
|
||||
|
||||
if(this.z != "d") return "Fail8"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
fun bar(d: D): String {
|
||||
return d.foo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = A().B().bar(D())
|
||||
|
||||
fun main(args : Array<String>) {
|
||||
println(box())
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
fun box() = "OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = Outer().Inner().box()
|
||||
|
||||
fun main(args: Array<String>)
|
||||
{
|
||||
println(box())
|
||||
}
|
||||
+1
-1
@@ -56,7 +56,7 @@ INTEROP_JAR="${KONAN_HOME}/konan/lib/Runtime.jar"
|
||||
NATIVE_LIB="${KONAN_HOME}/konan/nativelib"
|
||||
KONAN_CLASSPATH="$KOTLIN_JAR:$INTEROP_JAR:$KONAN_JAR"
|
||||
KONAN_COMPILER=org.jetbrains.kotlin.cli.bc.K2NativeKt
|
||||
|
||||
JAVA_OPTS=-ea
|
||||
|
||||
#
|
||||
# KONAN BACKEND INVOCATION
|
||||
|
||||
Reference in New Issue
Block a user