diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt index c1127366272..8677eda294f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt @@ -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) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt index 51fa7fdcefb..afb7f1fd079 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt @@ -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"), diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt index c67413bc6cb..8a1b2f88d0d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt @@ -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) } -} \ No newline at end of file +} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index cfddfc7e624..b4a6fdc1d61 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -1386,8 +1386,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map) = - 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 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()) } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt index 33e94bdc87f..45c2b3ed379 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt @@ -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() } + ) + } + } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticData.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticData.kt index c1f3a23b3d7..806da789ffe 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticData.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticData.kt @@ -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) = - stringLiterals.getOrPut(value.value) { createKotlinStringLiteral(type, value) } + fun kotlinStringLiteral(value: String) = + stringLiterals.getOrPut(value) { createKotlinStringLiteral(value) } } /** diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt index 76df0e2eb6b..1fe8651941b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt @@ -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): 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) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SpecialCallsLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PreInlineLowering.kt similarity index 67% rename from backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SpecialCallsLowering.kt rename to backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PreInlineLowering.kt index 69ab2a5b495..e0c4477b529 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SpecialCallsLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PreInlineLowering.kt @@ -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) diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 35bc70147f2..68023f97d33 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -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" diff --git a/backend.native/tests/codegen/kclass/kclass0.kt b/backend.native/tests/codegen/kclass/kclass0.kt new file mode 100644 index 00000000000..3ba740c41fd --- /dev/null +++ b/backend.native/tests/codegen/kclass/kclass0.kt @@ -0,0 +1,76 @@ +import kotlin.reflect.KClass + +fun main(args: Array) { + 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::class) + checkReifiedClass(Int::class) + checkReifiedClass2(Int::class) + checkReifiedClass2(Int::class) + checkReifiedClass(Any::class) + checkReifiedClass2(Any::class) + checkReifiedClass2(Any::class) + checkReifiedClass(Local::class) + checkReifiedClass2(Local::class) + checkReifiedClass(RootClass::class) + checkReifiedClass2(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 checkReifiedClass(expectedClass: KClass<*>) { + assert(T::class == expectedClass) +} + +inline fun checkReifiedClass2(expectedClass: KClass<*>) { + checkReifiedClass(expectedClass) + checkReifiedClass(expectedClass) +} diff --git a/backend.native/tests/codegen/kclass/kclass1.kt b/backend.native/tests/codegen/kclass/kclass1.kt new file mode 100644 index 00000000000..75864d454cb --- /dev/null +++ b/backend.native/tests/codegen/kclass/kclass1.kt @@ -0,0 +1,53 @@ +// FILE: main.kt +fun main(args: Array) { + 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 kClass(block: KClassDsl.() -> T): T = KClassDsl().block() +} + +@MyDsl +class KClassDsl { + inline fun of() = T::class +} + +fun 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() + } + } + + 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") + } +} diff --git a/runtime/src/main/cpp/Natives.cpp b/runtime/src/main/cpp/Natives.cpp index 4cb73818fcf..d13066ee9fc 100644 --- a/runtime/src/main/cpp/Natives.cpp +++ b/runtime/src/main/cpp/Natives.cpp @@ -70,4 +70,8 @@ void Kotlin_system_exitProcess(KInt status) { konan::exit(status); } +const void* Kotlin_Any_getTypeInfo(KConstRef obj) { + return obj->type_info(); +} + } // extern "C" diff --git a/runtime/src/main/cpp/ToString.cpp b/runtime/src/main/cpp/ToString.cpp index d96cbbbe46c..f9aca84d044 100644 --- a/runtime/src/main/cpp/ToString.cpp +++ b/runtime/src/main/cpp/ToString.cpp @@ -68,18 +68,6 @@ template OBJ_GETTER(Kotlin_toStringRadix, T value, KInt radix) { extern "C" { -OBJ_GETTER(Kotlin_Any_toString, KConstRef thiz) { - char cstring[80]; - if (IsArray(thiz)) { - konan::snprintf(cstring, sizeof(cstring), "%d@%p: array of %d", - Kotlin_Any_hashCode(thiz), thiz->type_info_, thiz->array()->count_); - } else { - konan::snprintf(cstring, sizeof(cstring), "%d@%p: object", - Kotlin_Any_hashCode(thiz), thiz->type_info_); - } - RETURN_RESULT_OF(CreateStringFromCString, cstring); -} - OBJ_GETTER(Kotlin_Byte_toString, KByte value) { char cstring[8]; konan::snprintf(cstring, sizeof(cstring), "%d", value); diff --git a/runtime/src/main/cpp/TypeInfo.h b/runtime/src/main/cpp/TypeInfo.h index 536b9088d78..40d755f52d6 100644 --- a/runtime/src/main/cpp/TypeInfo.h +++ b/runtime/src/main/cpp/TypeInfo.h @@ -22,6 +22,8 @@ #include "Common.h" #include "Names.h" +struct ObjHeader; + // An element of sorted by hash in-place array representing methods. // For systems where introspection is not needed - only open methods are in // this table. @@ -56,6 +58,15 @@ struct TypeInfo { // Is negative to mark an interface. int32_t fieldsCount_; + // String for the fully qualified dot-separated name of the package containing class, + // or `null` if the class is local or anonymous. + ObjHeader* packageName_; + + // String for the qualified class name relative to the containing package + // (e.g. TopLevel.Nested1.Nested2), or simple class name if it is local, + // or `null` if the class is anonymous. + ObjHeader* relativeName_; + // vtable starts just after declared contents of the TypeInfo: // void* const vtable_[]; }; diff --git a/runtime/src/main/cpp/Types.cpp b/runtime/src/main/cpp/Types.cpp index b522d31aa66..21b1e9b36a5 100644 --- a/runtime/src/main/cpp/Types.cpp +++ b/runtime/src/main/cpp/Types.cpp @@ -50,4 +50,16 @@ void CheckInstance(const ObjHeader* obj, const TypeInfo* type_info) { ThrowClassCastException(); } +KBoolean Kotlin_TypeInfo_isInstance(KConstRef obj, KNativePtr typeInfo) { + return IsInstance(obj, reinterpret_cast(typeInfo)); +} + +OBJ_GETTER(Kotlin_TypeInfo_getPackageName, KNativePtr typeInfo) { + RETURN_OBJ(reinterpret_cast(typeInfo)->packageName_); +} + +OBJ_GETTER(Kotlin_TypeInfo_getRelativeName, KNativePtr typeInfo) { + RETURN_OBJ(reinterpret_cast(typeInfo)->relativeName_); +} + } // extern "C" diff --git a/runtime/src/main/kotlin/konan/internal/KClassImpl.kt b/runtime/src/main/kotlin/konan/internal/KClassImpl.kt new file mode 100644 index 00000000000..edd069e62da --- /dev/null +++ b/runtime/src/main/kotlin/konan/internal/KClassImpl.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package konan.internal + +import kotlin.reflect.KClass + +@ExportForCompiler +internal class KClassImpl(private val typeInfo: NativePtr) : KClass { + override val simpleName: String? + get() { + val relativeName = getRelativeName(typeInfo) + ?: return null + + return relativeName.substringAfterLast(".") + } + + override val qualifiedName: String? + get() { + val packageName = getPackageName(typeInfo) + ?: return null + + val relativeName = getRelativeName(typeInfo)!! + return if (packageName.isEmpty()) { + relativeName + } else { + "$packageName.$relativeName" + } + } + + override fun isInstance(value: Any?): Boolean = value != null && isInstance(value, this.typeInfo) + + override fun equals(other: Any?): Boolean = + other is KClassImpl<*> && this.typeInfo == other.typeInfo + + override fun hashCode(): Int = typeInfo.hashCode() +} + +@ExportForCompiler +@SymbolName("Kotlin_Any_getTypeInfo") +internal external fun getObjectTypeInfo(obj: Any): NativePtr + +@ExportForCompiler +@Intrinsic +internal external inline fun getClassTypeInfo(): NativePtr + +@SymbolName("Kotlin_TypeInfo_getPackageName") +private external fun getPackageName(typeInfo: NativePtr): String? + +@SymbolName("Kotlin_TypeInfo_getRelativeName") +private external fun getRelativeName(typeInfo: NativePtr): String? + +@SymbolName("Kotlin_TypeInfo_isInstance") +private external fun isInstance(obj: Any, typeInfo: NativePtr): Boolean diff --git a/runtime/src/main/kotlin/kotlin/Any.kt b/runtime/src/main/kotlin/kotlin/Any.kt index a3281624e3f..23e4a0a7185 100644 --- a/runtime/src/main/kotlin/kotlin/Any.kt +++ b/runtime/src/main/kotlin/kotlin/Any.kt @@ -48,8 +48,13 @@ public open class Any { /** * Returns a string representation of the object. */ - @SymbolName("Kotlin_Any_toString") - external public open fun toString(): String + public open fun toString(): String { + val kClass = this::class + val className = kClass.qualifiedName ?: kClass.simpleName ?: "" + val unsignedHashCode = this.hashCode().toLong() and 0xffffffffL + val hashCodeStr = unsignedHashCode.toString(16) + return "$className@$hashCodeStr" + } } public fun Any?.hashCode() = if (this != null) this.hashCode() else 0 \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/Throwable.kt b/runtime/src/main/kotlin/kotlin/Throwable.kt index a010eb2e656..d8f96ba1483 100644 --- a/runtime/src/main/kotlin/kotlin/Throwable.kt +++ b/runtime/src/main/kotlin/kotlin/Throwable.kt @@ -50,7 +50,8 @@ public open class Throwable(open val message: String?, open val cause: Throwable } override fun toString(): String { - val s = "Throwable" // TODO: should be class name + val kClass = this::class + val s = kClass.qualifiedName ?: kClass.simpleName ?: "Throwable" return if (message != null) s + ": " + message.toString() else s } } diff --git a/runtime/src/main/kotlin/kotlin/reflect/KClass.kt b/runtime/src/main/kotlin/kotlin/reflect/KClass.kt new file mode 100644 index 00000000000..aa92833e67a --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/reflect/KClass.kt @@ -0,0 +1,149 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.reflect + +/** + * Represents a class and provides introspection capabilities. + * Instances of this class are obtainable by the `::class` syntax. + * See the [Kotlin language documentation](http://kotlinlang.org/docs/reference/reflection.html#class-references) + * for more information. + * + * @param T the type of the class. + */ +public interface KClass : KDeclarationContainer, KAnnotatedElement, KClassifier { + /** + * The simple name of the class as it was declared in the source code, + * or `null` if the class has no name (if, for example, it is an anonymous object literal). + */ + public val simpleName: String? + + /** + * The fully qualified dot-separated name of the class, + * or `null` if the class is local or it is an anonymous object literal. + */ + public val qualifiedName: String? + +// /** +// * All functions and properties accessible in this class, including those declared in this class +// * and all of its superclasses. Does not include constructors. +// */ +// override val members: Collection> +// +// /** +// * All constructors declared in this class. +// */ +// public val constructors: Collection> +// +// /** +// * All classes declared inside this class. This includes both inner and static nested classes. +// */ +// public val nestedClasses: Collection> +// +// /** +// * The instance of the object declaration, or `null` if this class is not an object declaration. +// */ +// public val objectInstance: T? + + /** + * Returns `true` if [value] is an instance of this class on a given platform. + */ + @SinceKotlin("1.1") + public fun isInstance(value: Any?): Boolean + +// /** +// * The list of type parameters of this class. This list does *not* include type parameters of outer classes. +// */ +// @SinceKotlin("1.1") +// public val typeParameters: List +// +// /** +// * The list of immediate supertypes of this class, in the order they are listed in the source code. +// */ +// @SinceKotlin("1.1") +// public val supertypes: List +// +// /** +// * Visibility of this class, or `null` if its visibility cannot be represented in Kotlin. +// */ +// @SinceKotlin("1.1") +// public val visibility: KVisibility? +// +// /** +// * `true` if this class is `final`. +// */ +// @SinceKotlin("1.1") +// public val isFinal: Boolean +// +// /** +// * `true` if this class is `open`. +// */ +// @SinceKotlin("1.1") +// public val isOpen: Boolean +// +// /** +// * `true` if this class is `abstract`. +// */ +// @SinceKotlin("1.1") +// public val isAbstract: Boolean +// +// /** +// * `true` if this class is `sealed`. +// * See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/classes.html#sealed-classes) +// * for more information. +// */ +// @SinceKotlin("1.1") +// public val isSealed: Boolean +// +// /** +// * `true` if this class is a data class. +// * See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/data-classes.html) +// * for more information. +// */ +// @SinceKotlin("1.1") +// public val isData: Boolean +// +// /** +// * `true` if this class is an inner class. +// * See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/nested-classes.html#inner-classes) +// * for more information. +// */ +// @SinceKotlin("1.1") +// public val isInner: Boolean +// +// /** +// * `true` if this class is a companion object. +// * See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/object-declarations.html#companion-objects) +// * for more information. +// */ +// @SinceKotlin("1.1") +// public val isCompanion: Boolean + + /** + * Returns `true` if this [KClass] instance represents the same Kotlin class as the class represented by [other]. + * On JVM this means that all of the following conditions are satisfied: + * + * 1. [other] has the same (fully qualified) Kotlin class name as this instance. + * 2. [other]'s backing [Class] object is loaded with the same class loader as the [Class] object of this instance. + * 3. If the classes represent [Array], then [Class] objects of their element types are equal. + * + * For example, on JVM, [KClass] instances for a primitive type (`int`) and the corresponding wrapper type (`java.lang.Integer`) + * are considered equal, because they have the same fully qualified name "kotlin.Int". + */ + override fun equals(other: Any?): Boolean + + override fun hashCode(): Int +} diff --git a/runtime/src/main/kotlin/kotlin/reflect/KClassifier.kt b/runtime/src/main/kotlin/kotlin/reflect/KClassifier.kt new file mode 100644 index 00000000000..8cea9582578 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/reflect/KClassifier.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.reflect + +/** + * A classifier is either a class or a type parameter. + * + * @see [KClass] + * @see [KTypeParameter] + */ +@SinceKotlin("1.1") +public interface KClassifier diff --git a/runtime/src/main/kotlin/kotlin/reflect/KDeclarationContainer.kt b/runtime/src/main/kotlin/kotlin/reflect/KDeclarationContainer.kt new file mode 100644 index 00000000000..6ccdd96884b --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/reflect/KDeclarationContainer.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.reflect + +/** + * Represents an entity which may contain declarations of any other entities, + * such as a class or a package. + */ +public interface KDeclarationContainer { +// /** +// * All functions and properties accessible in this container. +// */ +// public val members: Collection> +}