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:
Igor Chevdar
2017-01-31 15:36:21 +05:00
committed by GitHub
parent eeea4fc101
commit 4911f78200
13 changed files with 323 additions and 38 deletions
@@ -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?) {
@@ -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)
@@ -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"),
@@ -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
}
@@ -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 {
@@ -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))
@@ -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
}
}
}
+20
View File
@@ -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
View File
@@ -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