Merged different RTTI-related structure builders
This includes vTable, fields and associated objects
This commit is contained in:
+3
-3
@@ -257,10 +257,10 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
|
||||
KonanReflectionTypes(moduleDescriptor, KonanFqNames.internalPackageName)
|
||||
}
|
||||
|
||||
private val vtableBuilders = mutableMapOf<IrClass, ClassVtablesBuilder>()
|
||||
val layoutBuilders = mutableMapOf<IrClass, ClassLayoutBuilder>()
|
||||
|
||||
fun getVtableBuilder(classDescriptor: IrClass) = vtableBuilders.getOrPut(classDescriptor) {
|
||||
ClassVtablesBuilder(classDescriptor, this)
|
||||
fun getLayoutBuilder(irClass: IrClass) = layoutBuilders.getOrPut(irClass) {
|
||||
ClassLayoutBuilder(irClass, this)
|
||||
}
|
||||
|
||||
// We serialize untouched descriptor tree and inline IR bodies
|
||||
|
||||
+71
-7
@@ -12,11 +12,10 @@ import org.jetbrains.kotlin.backend.konan.llvm.functionName
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.localHash
|
||||
import org.jetbrains.kotlin.backend.konan.lower.bridgeTarget
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.util.isInterface
|
||||
import org.jetbrains.kotlin.ir.util.overrides
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrClassReference
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
internal class OverriddenFunctionInfo(
|
||||
val function: IrSimpleFunction,
|
||||
@@ -78,7 +77,7 @@ internal class OverriddenFunctionInfo(
|
||||
}
|
||||
}
|
||||
|
||||
internal class ClassVtablesBuilder(val irClass: IrClass, val context: Context) {
|
||||
internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) {
|
||||
private val DEBUG = 0
|
||||
|
||||
private inline fun DEBUG_OUTPUT(severity: Int, block: () -> Unit) {
|
||||
@@ -98,7 +97,7 @@ internal class ClassVtablesBuilder(val irClass: IrClass, val context: Context) {
|
||||
emptyList()
|
||||
} else {
|
||||
val superClass = irClass.getSuperClassNotAny() ?: context.ir.symbols.any.owner
|
||||
context.getVtableBuilder(superClass).vtableEntries
|
||||
context.getLayoutBuilder(superClass).vtableEntries
|
||||
}
|
||||
|
||||
val methods = irClass.sortedOverridableOrOverridingMethods
|
||||
@@ -172,6 +171,71 @@ internal class ClassVtablesBuilder(val irClass: IrClass, val context: Context) {
|
||||
// TODO: probably method table should contain all accessible methods to improve binary compatibility
|
||||
}
|
||||
|
||||
/**
|
||||
* All fields of the class instance.
|
||||
* The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix.
|
||||
*/
|
||||
val fields: List<IrField> by lazy {
|
||||
val superClass = irClass.getSuperClassNotAny() // TODO: what if Any has fields?
|
||||
val superFields = if (superClass != null) context.getLayoutBuilder(superClass).fields else emptyList()
|
||||
|
||||
superFields + getDeclaredFields()
|
||||
}
|
||||
|
||||
val associatedObjects by lazy {
|
||||
val result = mutableMapOf<IrClass, IrClass>()
|
||||
|
||||
irClass.annotations.forEach {
|
||||
val irFile = irClass.getContainingFile()
|
||||
|
||||
val annotationClass = (it.symbol.owner as? IrConstructor)?.constructedClass
|
||||
?: error(irFile, it, "unexpected annotation")
|
||||
|
||||
if (annotationClass.hasAnnotation(RuntimeNames.associatedObjectKey)) {
|
||||
val argument = it.getValueArgument(0)
|
||||
|
||||
val irClassReference = argument as? IrClassReference
|
||||
?: error(irFile, argument, "unexpected annotation argument")
|
||||
|
||||
val associatedObject = irClassReference.symbol.owner
|
||||
|
||||
if (associatedObject !is IrClass || !associatedObject.isObject) {
|
||||
error(irFile, irClassReference, "argument is not a singleton")
|
||||
}
|
||||
|
||||
if (annotationClass in result) {
|
||||
error(
|
||||
irFile,
|
||||
it,
|
||||
"duplicate value for ${annotationClass.name}, previous was ${result[annotationClass]?.name}"
|
||||
)
|
||||
}
|
||||
|
||||
result[annotationClass] = associatedObject
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/**
|
||||
* Fields declared in the class.
|
||||
*/
|
||||
private fun getDeclaredFields(): List<IrField> {
|
||||
val fields = irClass.declarations.mapNotNull {
|
||||
when (it) {
|
||||
is IrField -> it.takeIf { it.isReal }
|
||||
is IrProperty -> it.takeIf { it.isReal }?.backingField
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
if (irClass.hasAnnotation(FqName.fromSegments(listOf("kotlin", "native", "internal", "NoReorderFields"))))
|
||||
return fields
|
||||
|
||||
return fields.sortedBy { it.fqNameForIrSerialization.localHash.value }
|
||||
}
|
||||
|
||||
private val IrClass.sortedOverridableOrOverridingMethods: List<IrSimpleFunction>
|
||||
get() =
|
||||
this.simpleFunctions()
|
||||
+1
-1
@@ -722,7 +722,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
|
||||
val llvmMethod = if (!owner.isInterface) {
|
||||
// If this is a virtual method of the class - we can call via vtable.
|
||||
val index = context.getVtableBuilder(owner).vtableIndex(anyMethod ?: irFunction)
|
||||
val index = context.getLayoutBuilder(owner).vtableIndex(anyMethod ?: irFunction)
|
||||
val vtablePlace = gep(typeInfoPtr, Int32(1).llvm) // typeInfoPtr + 1
|
||||
val vtable = bitcast(kInt8PtrPtr, vtablePlace)
|
||||
val slot = gep(vtable, Int32(index).llvm)
|
||||
|
||||
+4
-52
@@ -65,7 +65,6 @@ internal class LlvmDeclarations(
|
||||
|
||||
internal class ClassLlvmDeclarations(
|
||||
val bodyType: LLVMTypeRef,
|
||||
val fields: List<IrField>, // TODO: it is not an LLVM declaration.
|
||||
val typeInfoGlobal: StaticData.Global,
|
||||
val writableTypeInfoGlobal: StaticData.Global?,
|
||||
val typeInfo: ConstPointer,
|
||||
@@ -88,53 +87,6 @@ internal class StaticFieldLlvmDeclarations(val storage: LLVMValueRef)
|
||||
|
||||
internal class UniqueLlvmDeclarations(val pointer: ConstPointer)
|
||||
|
||||
// TODO: rework getFields and getDeclaredFields.
|
||||
|
||||
/**
|
||||
* All fields of the class instance.
|
||||
* The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix.
|
||||
*/
|
||||
internal fun ContextUtils.getFields(irClass: IrClass) = context.getFields(irClass)
|
||||
|
||||
internal fun Context.getFields(irClass: IrClass): List<IrField> {
|
||||
val superClass = irClass.getSuperClassNotAny() // TODO: what if Any has fields?
|
||||
val superFields = if (superClass != null) getFields(superClass) else emptyList()
|
||||
|
||||
return superFields + getDeclaredFields(irClass)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fields declared in the class.
|
||||
*/
|
||||
private fun Context.getDeclaredFields(irClass: IrClass): List<IrField> {
|
||||
// TODO: Here's what is going on here:
|
||||
// The existence of a backing field for a property is only described in the IR,
|
||||
// but not in the PropertyDescriptor.
|
||||
//
|
||||
// We mark serialized properties with a Konan protobuf extension bit,
|
||||
// so it is present in DeserializedPropertyDescriptor.
|
||||
//
|
||||
// In this function we check the presence of the backing field
|
||||
// two ways: first we check IR, then we check the protobuf extension.
|
||||
|
||||
val fields = irClass.declarations.mapNotNull {
|
||||
when (it) {
|
||||
is IrField -> it.takeIf { it.isReal }
|
||||
is IrProperty -> it.takeIf { it.isReal }?.konanBackingField
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
// TODO: hack over missed parents in deserialized fields/property declarations.
|
||||
fields.forEach{it.parent = irClass}
|
||||
|
||||
if (irClass.hasAnnotation(FqName.fromSegments(listOf("kotlin", "native", "internal", "NoReorderFields"))))
|
||||
return fields
|
||||
|
||||
return fields.sortedBy {
|
||||
it.fqNameForIrSerialization.localHash.value
|
||||
}
|
||||
}
|
||||
|
||||
private fun ContextUtils.createClassBodyType(name: String, fields: List<IrField>): LLVMTypeRef {
|
||||
val fieldTypes = listOf(runtime.objHeaderType) + fields.map { getLLVMType(it.type) }
|
||||
// TODO: consider adding synthetic ObjHeader field to Any.
|
||||
@@ -213,7 +165,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
||||
private fun createClassDeclarations(declaration: IrClass): ClassLlvmDeclarations {
|
||||
val internalName = qualifyInternalName(declaration)
|
||||
|
||||
val fields = getFields(declaration)
|
||||
val fields = context.getLayoutBuilder(declaration).fields
|
||||
val bodyType = createClassBodyType("kclassbody:$internalName", fields)
|
||||
|
||||
val typeInfoPtr: ConstPointer
|
||||
@@ -232,7 +184,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
||||
|
||||
val typeInfoWithVtableType = structType(
|
||||
runtime.typeInfoType,
|
||||
LLVMArrayType(int8TypePtr, context.getVtableBuilder(declaration).vtableEntries.size)!!
|
||||
LLVMArrayType(int8TypePtr, context.getLayoutBuilder(declaration).vtableEntries.size)!!
|
||||
)
|
||||
|
||||
typeInfoGlobal = staticData.createGlobal(typeInfoWithVtableType, typeInfoGlobalName, isExported = false)
|
||||
@@ -290,7 +242,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
||||
it.setZeroInitializer()
|
||||
}
|
||||
|
||||
return ClassLlvmDeclarations(bodyType, fields, typeInfoGlobal, writableTypeInfoGlobal, typeInfoPtr,
|
||||
return ClassLlvmDeclarations(bodyType, typeInfoGlobal, writableTypeInfoGlobal, typeInfoPtr,
|
||||
singletonDeclarations, objCDeclarations)
|
||||
}
|
||||
|
||||
@@ -367,7 +319,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
||||
if (containingClass != null) {
|
||||
val classDeclarations = this.classes[containingClass] ?:
|
||||
error(containingClass.descriptor.toString())
|
||||
val allFields = classDeclarations.fields
|
||||
val allFields = context.getLayoutBuilder(containingClass).fields
|
||||
this.fields[declaration] = FieldLlvmDeclarations(
|
||||
allFields.indexOf(declaration) + 1, // First field is ObjHeader.
|
||||
classDeclarations.bodyType
|
||||
|
||||
+6
-43
@@ -14,7 +14,6 @@ import org.jetbrains.kotlin.backend.konan.ir.isAnonymousObject
|
||||
import org.jetbrains.kotlin.backend.konan.ir.isLocal
|
||||
import org.jetbrains.kotlin.backend.konan.isExternalObjCClassMethod
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrClassReference
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.konan.KonanAbiVersion
|
||||
@@ -47,7 +46,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
private fun checkAcyclicClass(irClass: IrClass): Boolean = when {
|
||||
irClass.symbol == context.ir.symbols.array -> false
|
||||
irClass.isArray -> true
|
||||
context.llvmDeclarations.forClass(irClass).fields.all { checkAcyclicFieldType(it.type) } -> true
|
||||
context.getLayoutBuilder(irClass).fields.all { checkAcyclicFieldType(it.type) } -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
@@ -248,7 +247,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
fun vtable(irClass: IrClass): ConstArray {
|
||||
// TODO: compile-time resolution limits binary compatibility.
|
||||
val vtableEntries = context.getVtableBuilder(irClass).vtableEntries.map {
|
||||
val vtableEntries = context.getLayoutBuilder(irClass).vtableEntries.map {
|
||||
val implementation = it.implementation
|
||||
if (implementation == null || implementation.isExternalObjCClassMethod()) {
|
||||
NullPointer(int8Type)
|
||||
@@ -261,7 +260,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
fun methodTableRecords(irClass: IrClass): List<MethodTableRecord> {
|
||||
val functionNames = mutableMapOf<Long, OverriddenFunctionInfo>()
|
||||
return context.getVtableBuilder(irClass).methodTableEntries.map {
|
||||
return context.getLayoutBuilder(irClass).methodTableEntries.map {
|
||||
val functionName = it.overriddenFunction.functionName
|
||||
val nameSignature = functionName.localHash
|
||||
val previous = functionNames.putIfAbsent(nameSignature.value, it)
|
||||
@@ -295,11 +294,11 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
NullPointer(int32Type), NullPointer(int8Type), NullPointer(kInt8Ptr))
|
||||
} else {
|
||||
data class FieldRecord(val offset: Int, val type: Int, val name: String)
|
||||
val fields = getStructElements(bodyType).drop(1).mapIndexedNotNull { index, type ->
|
||||
val fields = getStructElements(bodyType).drop(1).mapIndexed { index, type ->
|
||||
FieldRecord(
|
||||
LLVMOffsetOfElement(llvmTargetData, bodyType, index + 1).toInt(),
|
||||
mapRuntimeType(type),
|
||||
llvmDeclarations.fields[index].name.asString())
|
||||
context.getLayoutBuilder(irClass).fields[index].name.asString())
|
||||
}
|
||||
val offsetsPtr = staticData.placeGlobalConstArray("kextoff:$className", int32Type,
|
||||
fields.map { Int32(it.offset) })
|
||||
@@ -315,7 +314,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
}
|
||||
|
||||
private fun genAssociatedObjects(irClass: IrClass): ConstPointer? {
|
||||
val associatedObjects = getAssociatedObjects(irClass)
|
||||
val associatedObjects = context.getLayoutBuilder(irClass).associatedObjects
|
||||
if (associatedObjects.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
@@ -425,42 +424,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAssociatedObjects(irClass: IrClass): Map<IrClass, IrClass> {
|
||||
val result = mutableMapOf<IrClass, IrClass>()
|
||||
|
||||
irClass.annotations.forEach {
|
||||
val irFile = irClass.getContainingFile()
|
||||
|
||||
val annotationClass = (it.symbol.owner as? IrConstructor)?.constructedClass
|
||||
?: error(irFile, it, "unexpected annotation")
|
||||
|
||||
if (annotationClass.hasAnnotation(RuntimeNames.associatedObjectKey)) {
|
||||
val argument = it.getValueArgument(0)
|
||||
|
||||
val irClassReference = argument as? IrClassReference
|
||||
?: error(irFile, argument, "unexpected annotation argument")
|
||||
|
||||
val associatedObject = irClassReference.symbol.owner
|
||||
|
||||
if (associatedObject !is IrClass || !associatedObject.isObject) {
|
||||
error(irFile, irClassReference, "argument is not a singleton")
|
||||
}
|
||||
|
||||
if (annotationClass in result) {
|
||||
error(
|
||||
irFile,
|
||||
it,
|
||||
"duplicate value for ${annotationClass.name}, previous was ${result[annotationClass]?.name}"
|
||||
)
|
||||
}
|
||||
|
||||
result[annotationClass] = associatedObject
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Keep in sync with Konan_TypeFlags in TypeInfo.h.
|
||||
private const val TF_IMMUTABLE = 1
|
||||
private const val TF_ACYCLIC = 2
|
||||
|
||||
+1
-1
@@ -102,7 +102,7 @@ internal fun StaticData.createConstArrayList(array: ConstPointer, length: Int):
|
||||
// Now sort these values according to the order of fields returned by getFields()
|
||||
// to match the sorting order of the real ArrayList().
|
||||
val sorted = linkedMapOf<String, ConstValue>()
|
||||
getFields(arrayListClass).forEach {
|
||||
context.getLayoutBuilder(arrayListClass).fields.forEach {
|
||||
val fqName = it.fqNameForIrSerialization.asString()
|
||||
sorted.put(fqName, arrayListFields[fqName]!!)
|
||||
}
|
||||
|
||||
+2
-2
@@ -901,7 +901,7 @@ private fun ObjCExportCodeGenerator.vtableIndex(irFunction: IrSimpleFunction): I
|
||||
return if (irClass.isInterface) {
|
||||
null
|
||||
} else {
|
||||
context.getVtableBuilder(irClass).vtableIndex(irFunction)
|
||||
context.getLayoutBuilder(irClass).vtableIndex(irFunction)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -974,7 +974,7 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
|
||||
val vtableSize = if (irClass.kind == ClassKind.INTERFACE) {
|
||||
-1
|
||||
} else {
|
||||
context.getVtableBuilder(irClass).vtableEntries.size
|
||||
context.getLayoutBuilder(irClass).vtableEntries.size
|
||||
}
|
||||
|
||||
val vtable = if (!irClass.isInterface && !irClass.typeInfoHasVtableAttached) {
|
||||
|
||||
+1
-2
@@ -651,8 +651,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
|
||||
value
|
||||
)
|
||||
} else {
|
||||
val vTableBuilder = context.getVtableBuilder(owner)
|
||||
val vtableIndex = vTableBuilder.vtableIndex(callee)
|
||||
val vtableIndex = context.getLayoutBuilder(owner).vtableIndex(callee)
|
||||
DataFlowIR.Node.VtableCall(
|
||||
symbolTable.mapFunction(callee.target),
|
||||
receiverType,
|
||||
|
||||
+3
-3
@@ -530,9 +530,9 @@ internal object DataFlowIR {
|
||||
|
||||
type.superTypes += irClass.superTypes.map { mapClassReferenceType(it.getClass()!!) }
|
||||
if (!isAbstract) {
|
||||
val vtableBuilder = context.getVtableBuilder(irClass)
|
||||
type.vtable += vtableBuilder.vtableEntries.map { mapFunction(it.getImplementation(context)!!) }
|
||||
vtableBuilder.methodTableEntries.forEach {
|
||||
val layoutBuilder = context.getLayoutBuilder(irClass)
|
||||
type.vtable += layoutBuilder.vtableEntries.map { mapFunction(it.getImplementation(context)!!) }
|
||||
layoutBuilder.methodTableEntries.forEach {
|
||||
type.itable[it.overriddenFunction.functionName.localHash.value] = mapFunction(it.getImplementation(context)!!)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user