Add basic support for class references
Also use class name for `Any.toString` and `Throwable.toString`.
This commit is contained in:
committed by
GitHub
parent
a6ca64871f
commit
786374ef86
+2
-2
@@ -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) {
|
||||
|
||||
+3
-3
@@ -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"),
|
||||
|
||||
+23
-1
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
-2
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
+43
-3
@@ -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() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-2
@@ -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)
|
||||
|
||||
|
||||
+36
-11
@@ -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)
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user