Add basic support for class references

Also use class name for `Any.toString` and `Throwable.toString`.
This commit is contained in:
SvyatoslavScherbina
2017-09-26 10:59:45 +03:00
committed by GitHub
parent a6ca64871f
commit 786374ef86
21 changed files with 573 additions and 41 deletions
@@ -44,8 +44,8 @@ internal class KonanLower(val context: Context) {
irModule.files.forEach(TestProcessor(context)::lower)
}
phaser.phase(KonanPhase.LOWER_SPECIAL_CALLS) {
irModule.files.forEach(SpecialCallsLowering(context)::lower)
phaser.phase(KonanPhase.LOWER_BEFORE_INLINE) {
irModule.files.forEach(PreInlineLowering(context)::lower)
}
phaser.phase(KonanPhase.LOWER_INLINE_CONSTRUCTORS) {
@@ -29,9 +29,9 @@ enum class KonanPhase(val description: String,
/* */ BACKEND("All backend"),
/* ... */ LOWER("IR Lowering"),
/* ... ... */ TEST_PROCESSOR("Unit test processor"),
/* ... ... */ LOWER_SPECIAL_CALLS("Special calls processing before inlining"),
/* ... ... */ LOWER_INLINE_CONSTRUCTORS("Inline constructors transformation", LOWER_SPECIAL_CALLS),
/* ... ... */ LOWER_INLINE("Functions inlining", LOWER_INLINE_CONSTRUCTORS, LOWER_SPECIAL_CALLS),
/* ... ... */ LOWER_BEFORE_INLINE("Special operations processing before inlining"),
/* ... ... */ LOWER_INLINE_CONSTRUCTORS("Inline constructors transformation", LOWER_BEFORE_INLINE),
/* ... ... */ LOWER_INLINE("Functions inlining", LOWER_INLINE_CONSTRUCTORS, LOWER_BEFORE_INLINE),
/* ... ... ... */ DESERIALIZER("Deserialize inline bodies"),
/* ... ... */ LOWER_INTEROP_PART1("Interop lowering, part 1", LOWER_INLINE),
/* ... ... */ LOWER_FOR_LOOPS("For loops lowering"),
@@ -28,10 +28,15 @@ import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
import kotlin.properties.Delegates
@@ -89,6 +94,12 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
it to symbolTable.referenceClass(context.getInternalClass("${it.classFqName.shortName()}Box"))
}
val valueClassToBox = ValueType.values().associate {
val valueClassId = ClassId.topLevel(it.classFqName.toSafe())
val valueClassDescriptor = context.builtIns.builtInsModule.findClassAcrossModuleDependencies(valueClassId)!!
valueClassDescriptor to boxClasses[it]!!
}
val unboxFunctions = ValueType.values().mapNotNull {
val unboxFunctionName = "unbox${it.classFqName.shortName()}"
context.getInternalFunctions(unboxFunctionName).atMostOne()?.let { descriptor ->
@@ -170,6 +181,17 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
val kLocalDelegatedPropertyImpl = symbolTable.referenceClass(context.reflectionTypes.kLocalDelegatedPropertyImpl)
val kLocalDelegatedMutablePropertyImpl = symbolTable.referenceClass(context.reflectionTypes.kLocalDelegatedMutablePropertyImpl)
val getClassTypeInfo = internalFunction("getClassTypeInfo")
val getObjectTypeInfo = internalFunction("getObjectTypeInfo")
val kClassImpl = internalClass("KClassImpl")
val kClassImplConstructor by lazy { kClassImpl.constructors.single() }
private fun internalFunction(name: String): IrSimpleFunctionSymbol =
symbolTable.referenceSimpleFunction(context.getInternalFunctions(name).single())
private fun internalClass(name: String): IrClassSymbol =
symbolTable.referenceClass(context.getInternalClass(name))
private fun getKonanTestClass(className: String) = symbolTable.referenceClass(
builtInsPackage("konan", "test").getContributedClassifier(
Name.identifier(className), NoLookupLocation.FROM_BACKEND
@@ -216,4 +238,4 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
kind.runtimeKindName, NoLookupLocation.FROM_BACKEND
) as ClassDescriptor)
}
}
}
@@ -1386,8 +1386,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
//-------------------------------------------------------------------------//
private fun evaluateStringConst(value: IrConst<String>) =
context.llvm.staticData.kotlinStringLiteral(
context.builtIns.stringType, value).llvm
context.llvm.staticData.kotlinStringLiteral(value.value).llvm
private fun evaluateConst(value: IrConst<*>): LLVMValueRef {
context.log{"evaluateConst : ${ir2string(value)}"}
@@ -2007,6 +2006,22 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
interop.readBits -> genReadBits(args)
interop.writeBits -> genWriteBits(args)
context.ir.symbols.getClassTypeInfo.descriptor -> {
val typeArgument = callee.getTypeArgumentOrDefault(descriptor.typeParameters.single())
val typeArgumentClass = TypeUtils.getClassDescriptor(typeArgument)
if (typeArgumentClass == null) {
// E.g. for `T::class` in a body of an inline function itself.
functionGenerationContext.unreachable()
kNullInt8Ptr
} else {
val classDescriptor = context.ir.symbols.valueClassToBox[typeArgumentClass]?.descriptor
?: typeArgumentClass
val typeInfo = codegen.typeInfoValue(classDescriptor)
LLVMConstBitCast(typeInfo, kInt8Ptr)!!
}
}
else -> TODO(callee.descriptor.original.toString())
}
}
@@ -23,9 +23,11 @@ import org.jetbrains.kotlin.backend.konan.isExternalObjCClassMethod
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
internal class RTTIGenerator(override val context: Context) : ContextUtils {
@@ -45,7 +47,9 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
val methods: ConstValue,
val methodsCount: Int,
val fields: ConstValue,
val fieldsCount: Int) :
val fieldsCount: Int,
val packageName: String?,
val relativeName: String?) :
Struct(
runtime.typeInfoType,
@@ -64,9 +68,18 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
Int32(methodsCount),
fields,
Int32(fieldsCount)
Int32(fieldsCount),
kotlinStringLiteral(packageName),
kotlinStringLiteral(relativeName)
)
private fun kotlinStringLiteral(string: String?): ConstPointer = if (string == null) {
NullPointer(runtime.objHeaderType)
} else {
staticData.kotlinStringLiteral(string)
}
private fun exportTypeInfoIfRequired(classDesc: ClassDescriptor, typeInfoGlobal: LLVMValueRef?) {
val annot = classDesc.annotations.findAnnotation(FqName("konan.ExportTypeInfo"))
if (annot != null) {
@@ -159,12 +172,17 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className",
runtime.methodTableRecordType, methods)
val reflectionInfo = getReflectionInfo(classDesc)
val typeInfo = TypeInfo(name, size,
superType,
objOffsetsPtr, objOffsets.size,
interfacesPtr, interfaces.size,
methodsPtr, methods.size,
fieldsPtr, if (classDesc.isInterface) -1 else fields.size)
fieldsPtr, if (classDesc.isInterface) -1 else fields.size,
reflectionInfo.packageName,
reflectionInfo.relativeName
)
val typeInfoGlobal = llvmDeclarations.typeInfoGlobal
@@ -201,4 +219,26 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
}
return context.specialDeclarationsFactory.getBridgeDescriptor(OverriddenFunctionDescriptor(bridgeOwner, overriddenDescriptor))
}
data class ReflectionInfo(val packageName: String?, val relativeName: String?)
private fun getReflectionInfo(descriptor: ClassDescriptor): ReflectionInfo {
// Use data from value class in type info for box class:
val descriptorForReflection = context.ir.symbols.valueClassToBox.entries
.firstOrNull { it.value.descriptor == descriptor }
?.key ?: descriptor
return if (DescriptorUtils.isAnonymousObject(descriptorForReflection)) {
ReflectionInfo(packageName = null, relativeName = null)
} else if (DescriptorUtils.isLocal(descriptorForReflection)) {
ReflectionInfo(packageName = null, relativeName = descriptorForReflection.name.asString())
} else {
ReflectionInfo(
packageName = descriptorForReflection.findPackage().fqName.asString(),
relativeName = descriptorForReflection.parentsWithSelf
.takeWhile { it is ClassDescriptor }.toList().reversed()
.joinToString(".") { it.name.asString() }
)
}
}
}
@@ -157,8 +157,8 @@ internal class StaticData(override val context: Context): ContextUtils {
fun cStringLiteral(value: String) =
cStringLiterals.getOrPut(value) { placeCStringLiteral(value) }
fun kotlinStringLiteral(type: KotlinType, value: IrConst<String>) =
stringLiterals.getOrPut(value.value) { createKotlinStringLiteral(type, value) }
fun kotlinStringLiteral(value: String) =
stringLiterals.getOrPut(value) { createKotlinStringLiteral(value) }
}
/**
@@ -42,8 +42,9 @@ private fun StaticData.arrayHeader(typeInfo: ConstPointer, length: Int): Struct
return Struct(runtime.arrayHeaderType, typeInfo, Int32(containerOffsetNegative), Int32(length))
}
internal fun StaticData.createKotlinStringLiteral(type: KotlinType, irConst: IrConst<String>): ConstPointer {
val value = irConst.value
internal fun StaticData.createKotlinStringLiteral(value: String): ConstPointer {
val type = context.builtIns.stringType
val name = "kstr:" + value.globalHashBase64
val elements = value.toCharArray().map(::Char16)
@@ -1,33 +1,58 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer
import org.jetbrains.kotlin.backend.common.lower.at
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrSpreadElement
import org.jetbrains.kotlin.ir.expressions.IrVararg
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.types.typeUtil.isUnit
/**
* This pass runs before inlining and performs the following additional transformations over some calls:
* This pass runs before inlining and performs the following additional transformations over some operations:
* - Assertion call removal.
* - Convert immutableBinaryBlobOf() arguments to special IrConst.
* - Convert `obj::class` and `Class::class` to calls.
*/
internal class SpecialCallsLowering(val context: Context) : FileLoweringPass {
internal class PreInlineLowering(val context: Context) : FileLoweringPass {
private val asserts = context.ir.symbols.asserts
private val symbols get() = context.ir.symbols
private val asserts = symbols.asserts
private val enableAssertions = context.config.configuration.getBoolean(KonanConfigKeys.ENABLE_ASSERTIONS)
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
irFile.transformChildrenVoid(object : IrBuildingTransformer(context) {
override fun visitClassReference(expression: IrClassReference): IrExpression {
expression.transformChildrenVoid()
builder.at(expression)
val typeArgument = expression.descriptor.defaultType
return builder.irCall(symbols.kClassImplConstructor, listOf(typeArgument)).apply {
putValueArgument(0, builder.irCall(symbols.getClassTypeInfo, listOf(typeArgument)))
}
}
override fun visitGetClass(expression: IrGetClass): IrExpression {
expression.transformChildrenVoid()
builder.at(expression)
val typeArgument = expression.type.arguments.single().type
return builder.irCall(symbols.kClassImplConstructor, listOf(typeArgument)).apply {
val typeInfo = builder.irCall(symbols.getObjectTypeInfo).apply {
putValueArgument(0, expression.argument)
}
putValueArgument(0, typeInfo)
}
}
override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid(this)
+9
View File
@@ -928,6 +928,15 @@ task lateinit_inBaseClass(type: RunKonanTest) {
source = "codegen/lateinit/inBaseClass.kt"
}
task kclass0(type: RunKonanTest) {
source = "codegen/kclass/kclass0.kt"
}
task kclass1(type: RunKonanTest) {
goldValue = "OK :D\n"
source = "codegen/kclass/kclass1.kt"
}
task coroutines_simple(type: RunKonanTest) {
disabled = (project.testTarget == 'wasm32') // llvm: 'WebAssembly hasn't implemented computed gotos'
goldValue = "42\n"
@@ -0,0 +1,76 @@
import kotlin.reflect.KClass
fun main(args: Array<String>) {
checkClass(Any::class, "kotlin.Any", "Any", Any(), null)
checkClass(Int::class, "kotlin.Int", "Int", 42, "17")
checkClass(String::class, "kotlin.String", "String", "17", 42)
checkClass(RootClass::class, "RootClass", "RootClass", RootClass(), Any())
checkClass(RootClass.Nested::class, "RootClass.Nested", "Nested", RootClass.Nested(), Any())
class Local {
val captured = args
inner class Inner
}
checkClass(Local::class, null, "Local", Local(), Any())
checkClass(Local.Inner::class, null, "Inner", Local().Inner(), Any())
val obj = object : Any() {
val captured = args
inner class Inner
val innerKClass = Inner::class
}
checkClass(obj::class, null, null, obj, Any())
checkClass(obj.innerKClass, null, "Inner", obj.Inner(), Any())
// Interfaces:
checkClass(Comparable::class, "kotlin.Comparable", "Comparable", 42, Any())
checkClass(Interface::class, "Interface", "Interface", object : Interface {}, Any())
checkInstanceClass(Any(), Any::class)
checkInstanceClass(42, Int::class)
assert(42::class == Int::class)
checkReifiedClass<Int>(Int::class)
checkReifiedClass<Int?>(Int::class)
checkReifiedClass2<Int>(Int::class)
checkReifiedClass2<Int?>(Int::class)
checkReifiedClass<Any>(Any::class)
checkReifiedClass2<Any>(Any::class)
checkReifiedClass2<Any?>(Any::class)
checkReifiedClass<Local>(Local::class)
checkReifiedClass2<Local>(Local::class)
checkReifiedClass<RootClass>(RootClass::class)
checkReifiedClass2<RootClass>(RootClass::class)
}
class RootClass {
class Nested
}
interface Interface
fun checkClass(
clazz: KClass<*>,
expectedQualifiedName: String?, expectedSimpleName: String?,
expectedInstance: Any, expectedNotInstance: Any?
) {
assert(clazz.qualifiedName == expectedQualifiedName)
assert(clazz.simpleName == expectedSimpleName)
assert(clazz.isInstance(expectedInstance))
if (expectedNotInstance != null) assert(!clazz.isInstance(expectedNotInstance))
}
fun checkInstanceClass(instance: Any, clazz: KClass<*>) {
assert(instance::class == clazz)
}
inline fun <reified T> checkReifiedClass(expectedClass: KClass<*>) {
assert(T::class == expectedClass)
}
inline fun <reified T> checkReifiedClass2(expectedClass: KClass<*>) {
checkReifiedClass<T>(expectedClass)
checkReifiedClass<T?>(expectedClass)
}
@@ -0,0 +1,53 @@
// FILE: main.kt
fun main(args: Array<String>) {
com.github.salomonbrys.kmffkn.App(testQualified = true)
}
// FILE: app.kt
// Taken from:
// https://github.com/SalomonBrys/kmffkn/blob/master/shared/main/kotlin/com/github/salomonbrys/kmffkn/app.kt
package com.github.salomonbrys.kmffkn
@DslMarker
annotation class MyDsl
@MyDsl
class DslMain {
fun <T: Any> kClass(block: KClassDsl.() -> T): T = KClassDsl().block()
}
@MyDsl
class KClassDsl {
inline fun <reified T: Any> of() = T::class
}
fun <T: Any> dsl(block: DslMain.() -> T): T = DslMain().block()
class Test
class App(testQualified: Boolean) {
@Volatile // This could be noop in Kotlin Native, or the equivalent of volatile in C.
var type = dsl {
kClass {
//kClass { } // This should error if uncommented because of `@DslMarker`.
of<Test>()
}
}
init {
assert(type.simpleName == "Test")
if (testQualified)
assert(type.qualifiedName == "com.github.salomonbrys.kmffkn.Test") // This is not really necessary, but always better :).
assert(String::class == String::class)
assert(String::class != Int::class)
assert(Test()::class == Test()::class)
assert(Test()::class == Test::class)
println("OK :D")
}
}