Bridges (#240)
* - bridges building - bridges support in vtable and methods table * FunctionDescriptor.signature fix + some assertions * tests * refactoring * refactoring * removed debug output * refactoring * review fixes * implemented bridge construction for parameters * tests * refactoring * removed custom box/unbox * added bridge for extension receiver
This commit is contained in:
+69
-8
@@ -3,25 +3,24 @@ package org.jetbrains.kotlin.backend.konan
|
||||
import llvm.LLVMDumpModule
|
||||
import llvm.LLVMModuleRef
|
||||
import org.jetbrains.kotlin.backend.jvm.descriptors.initialize
|
||||
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.deepPrint
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||
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.backend.konan.llvm.*
|
||||
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.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
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 {
|
||||
internal class SpecialDescriptorsFactory(val context: Context) {
|
||||
private val outerThisDescriptors = mutableMapOf<ClassDescriptor, PropertyDescriptor>()
|
||||
private val bridgesDescriptors = mutableMapOf<FunctionDescriptor, FunctionDescriptor>()
|
||||
|
||||
fun getOuterThisFieldDescriptor(innerClassDescriptor: ClassDescriptor): PropertyDescriptor =
|
||||
if (!innerClassDescriptor.isInner) throw AssertionError("Class is not inner: $innerClassDescriptor")
|
||||
@@ -34,13 +33,75 @@ internal class SpecialDescriptorsFactory {
|
||||
false, "this$0".synthesizedName, CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE,
|
||||
false, false, false, false, false, false).initialize(outerClassDescriptor.defaultType, dispatchReceiverParameter = receiver)
|
||||
}
|
||||
|
||||
fun getBridgeDescriptor(descriptor: FunctionDescriptor): FunctionDescriptor {
|
||||
return bridgesDescriptors.getOrPut(descriptor) {
|
||||
SimpleFunctionDescriptorImpl.create(
|
||||
descriptor.containingDeclaration,
|
||||
Annotations.EMPTY,
|
||||
("<bridge-to>" + descriptor.functionName).synthesizedName,
|
||||
CallableMemberDescriptor.Kind.DECLARATION,
|
||||
SourceElement.NO_SOURCE).apply {
|
||||
initializeBridgeDescriptor(this, descriptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializeBridgeDescriptor(bridgeDescriptor: SimpleFunctionDescriptorImpl, descriptor: FunctionDescriptor) {
|
||||
val bridgeDirections = descriptor.bridgeDirections
|
||||
if (bridgeDirections.allNotNeeded())
|
||||
throw AssertionError("Function $descriptor is not needed in a bridge")
|
||||
|
||||
val returnType = when (bridgeDirections[0]) {
|
||||
BridgeDirection.TO_VALUE_TYPE -> descriptor.returnType!!
|
||||
BridgeDirection.NOT_NEEDED -> descriptor.returnType
|
||||
BridgeDirection.FROM_VALUE_TYPE -> context.builtIns.anyType
|
||||
}
|
||||
|
||||
val extensionReceiverType = when (bridgeDirections[1]) {
|
||||
BridgeDirection.TO_VALUE_TYPE -> descriptor.extensionReceiverParameter!!.type
|
||||
BridgeDirection.NOT_NEEDED -> descriptor.extensionReceiverParameter?.type
|
||||
BridgeDirection.FROM_VALUE_TYPE -> context.builtIns.anyType
|
||||
}
|
||||
|
||||
bridgeDescriptor.initialize(
|
||||
extensionReceiverType,
|
||||
descriptor.dispatchReceiverParameter,
|
||||
descriptor.typeParameters,
|
||||
descriptor.valueParameters.mapIndexed { index, valueParameterDescriptor ->
|
||||
when (bridgeDirections[index + 2]) {
|
||||
BridgeDirection.TO_VALUE_TYPE -> valueParameterDescriptor
|
||||
BridgeDirection.NOT_NEEDED -> valueParameterDescriptor
|
||||
BridgeDirection.FROM_VALUE_TYPE -> ValueParameterDescriptorImpl(
|
||||
valueParameterDescriptor.containingDeclaration,
|
||||
null,
|
||||
index,
|
||||
Annotations.EMPTY,
|
||||
valueParameterDescriptor.name,
|
||||
context.builtIns.anyType,
|
||||
valueParameterDescriptor.declaresDefaultValue(),
|
||||
valueParameterDescriptor.isCrossinline,
|
||||
valueParameterDescriptor.isNoinline,
|
||||
valueParameterDescriptor.varargElementType,
|
||||
SourceElement.NO_SOURCE)
|
||||
}
|
||||
},
|
||||
returnType,
|
||||
descriptor.modality,
|
||||
Visibilities.PRIVATE)
|
||||
}
|
||||
}
|
||||
|
||||
internal final class Context(val config: KonanConfig) : KonanBackendContext() {
|
||||
|
||||
var moduleDescriptor: ModuleDescriptor? = null
|
||||
|
||||
val specialDescriptorsFactory = SpecialDescriptorsFactory()
|
||||
val specialDescriptorsFactory = SpecialDescriptorsFactory(this)
|
||||
private val vtableBuilders = mutableMapOf<ClassDescriptor, ClassVtablesBuilder>()
|
||||
|
||||
fun getVtableBuilder(classDescriptor: ClassDescriptor) = vtableBuilders.getOrPut(classDescriptor) {
|
||||
ClassVtablesBuilder(classDescriptor, this)
|
||||
}
|
||||
|
||||
// TODO: make lateinit?
|
||||
var irModule: IrModuleFragment? = null
|
||||
|
||||
+3
@@ -55,6 +55,9 @@ internal class KonanLower(val context: Context) {
|
||||
phaser.phase(KonanPhase.LOWER_CALLABLES) {
|
||||
CallableReferenceLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.BRIDGES_BUILDING) {
|
||||
BridgesBuilding(context).lower(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.AUTOBOX) {
|
||||
Autoboxing(context).lower(irFile)
|
||||
}
|
||||
|
||||
+1
@@ -24,6 +24,7 @@ enum class KonanPhase(val description: String,
|
||||
/* ... ... */ LOWER_INNER_CLASSES("Inner classes lowering"),
|
||||
/* ... ... */ LOWER_STRING_CONCAT("String concatenation lowering"),
|
||||
/* ... ... */ LOWER_INITIALIZERS("Initializers lowering"),
|
||||
/* ... ... */ BRIDGES_BUILDING("Bridges building"),
|
||||
/* ... */ BITCODE("LLVM BitCode Generation"),
|
||||
/* ... ... */ RTTI("RTTI Generation"),
|
||||
/* ... ... */ CODEGEN("Code Generation"),
|
||||
|
||||
+4
-2
@@ -1,7 +1,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
/**
|
||||
@@ -69,4 +69,6 @@ tailrec fun KotlinType.notNullableIsRepresentedAs(valueType: ValueType): Boolean
|
||||
// See comment in [isRepresentedAs].
|
||||
val firstSupertype = this.constructor.supertypes.firstOrNull() ?: return false
|
||||
return firstSupertype.notNullableIsRepresentedAs(valueType)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun KotlinType.isValueType() = ValueType.values().any { this.isRepresentedAs(it) }
|
||||
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package org.jetbrains.kotlin.backend.konan.descriptors
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.functionName
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.localHash
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
|
||||
internal class OverriddenFunctionDescriptor(val descriptor: FunctionDescriptor, overriddenDescriptor: FunctionDescriptor) {
|
||||
val overriddenDescriptor = overriddenDescriptor.original
|
||||
|
||||
val needBridge: Boolean
|
||||
get() {
|
||||
if (descriptor.modality == Modality.ABSTRACT) return false
|
||||
return descriptor.needBridgeTo(overriddenDescriptor)
|
||||
|| descriptor.target.needBridgeTo(overriddenDescriptor)
|
||||
}
|
||||
|
||||
fun trivialOverride() = descriptor.original == overriddenDescriptor
|
||||
|
||||
override fun toString(): String {
|
||||
return "(descriptor=$descriptor, overriddenDescriptor=$overriddenDescriptor)"
|
||||
}
|
||||
}
|
||||
|
||||
internal class ClassVtablesBuilder(val classDescriptor: ClassDescriptor, val context: Context) {
|
||||
val vtableEntries: List<OverriddenFunctionDescriptor> by lazy {
|
||||
|
||||
assert(!classDescriptor.isInterface)
|
||||
|
||||
val superVtableEntries = if (KotlinBuiltIns.isSpecialClassWithNoSupertypes(classDescriptor)) {
|
||||
emptyList()
|
||||
} else {
|
||||
context.getVtableBuilder(classDescriptor.getSuperClassOrAny()).vtableEntries
|
||||
}
|
||||
|
||||
val methods = classDescriptor.contributedMethods
|
||||
val newVtableSlots = mutableListOf<OverriddenFunctionDescriptor>()
|
||||
|
||||
val inheritedVtableSlots = superVtableEntries.map { superMethod ->
|
||||
val overridingMethod = methods.singleOrNull { OverridingUtil.overrides(it, superMethod.descriptor) }
|
||||
if (overridingMethod == null) {
|
||||
superMethod
|
||||
} else {
|
||||
if (!superMethod.trivialOverride())
|
||||
newVtableSlots.add(OverriddenFunctionDescriptor(overridingMethod, superMethod.descriptor))
|
||||
newVtableSlots.add(OverriddenFunctionDescriptor(overridingMethod, overridingMethod))
|
||||
OverriddenFunctionDescriptor(overridingMethod, superMethod.overriddenDescriptor)
|
||||
}
|
||||
}
|
||||
|
||||
methods.filterNot { method -> inheritedVtableSlots.any { it.descriptor == method } } // Find newly defined methods.
|
||||
.mapTo(newVtableSlots) { OverriddenFunctionDescriptor(it, it) }
|
||||
|
||||
val list = inheritedVtableSlots + newVtableSlots.filter { it.descriptor.isOverridable }.sortedBy {
|
||||
it.overriddenDescriptor.functionName.localHash.value
|
||||
}
|
||||
list
|
||||
}
|
||||
|
||||
fun vtableIndex(function: FunctionDescriptor): Int {
|
||||
val target = function.target
|
||||
val index = vtableEntries.indexOfFirst { it.overriddenDescriptor == target }
|
||||
if (index < 0) throw Error(function.toString() + " not in vtable of " + this.toString())
|
||||
return index
|
||||
}
|
||||
|
||||
private val ClassDescriptor.contributedMethodsWithOverridden: List<OverriddenFunctionDescriptor>
|
||||
get() {
|
||||
return contributedMethods.flatMap { method ->
|
||||
method.allOverriddenDescriptors.map { OverriddenFunctionDescriptor(method, it) }
|
||||
}.distinctBy {
|
||||
Triple(it.overriddenDescriptor.functionName, it.descriptor, it.needBridge)
|
||||
}.sortedBy {
|
||||
it.overriddenDescriptor.functionName.localHash.value
|
||||
}
|
||||
}
|
||||
|
||||
val methodTableEntries: List<OverriddenFunctionDescriptor> by lazy {
|
||||
assert(!classDescriptor.isAbstract())
|
||||
|
||||
classDescriptor.contributedMethodsWithOverridden.filter {
|
||||
// We check that either method is open, or one of declarations it overrides
|
||||
// is open.
|
||||
it.overriddenDescriptor.isOverridable || DescriptorUtils.getAllOverriddenDeclarations(it.overriddenDescriptor).any { it.isOverridable }
|
||||
}
|
||||
// TODO: probably method table should contain all accessible methods to improve binary compatibility
|
||||
}
|
||||
|
||||
}
|
||||
+91
-48
@@ -3,27 +3,23 @@ package org.jetbrains.kotlin.backend.konan.descriptors
|
||||
import org.jetbrains.kotlin.backend.konan.KonanBuiltIns
|
||||
import org.jetbrains.kotlin.backend.konan.ValueType
|
||||
import org.jetbrains.kotlin.backend.konan.isRepresentedAs
|
||||
import org.jetbrains.kotlin.backend.konan.isValueType
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.functionName
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.localHash
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
|
||||
import org.jetbrains.kotlin.builtins.getFunctionalClassKind
|
||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||
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.PropertyGetterDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeSubstitution
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
@@ -46,14 +42,14 @@ internal val ClassDescriptor.implementedInterfaces: List<ClassDescriptor>
|
||||
*
|
||||
* TODO: this method is actually a part of resolve and probably duplicates another one
|
||||
*/
|
||||
internal fun FunctionDescriptor.resolveFakeOverride(): FunctionDescriptor {
|
||||
internal fun <T : CallableMemberDescriptor> T.resolveFakeOverride(): T {
|
||||
if (this.kind.isReal) {
|
||||
return this
|
||||
} else {
|
||||
val overridden = OverridingUtil.getOverriddenDeclarations(this)
|
||||
val filtered = OverridingUtil.filterOutOverridden(overridden)
|
||||
// TODO: is it correct to take first?
|
||||
return filtered.first { it.modality != Modality.ABSTRACT } as FunctionDescriptor
|
||||
return filtered.first { it.modality != Modality.ABSTRACT } as T
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +153,7 @@ internal val KotlinType.isKFunctionType: Boolean
|
||||
internal val FunctionDescriptor.isFunctionInvoke: Boolean
|
||||
get() {
|
||||
val dispatchReceiver = dispatchReceiverParameter ?: return false
|
||||
assert (!dispatchReceiver.type.isKFunctionType)
|
||||
assert(!dispatchReceiver.type.isKFunctionType)
|
||||
|
||||
return dispatchReceiver.type.isFunctionType &&
|
||||
this.isOperator && this.name == OperatorNameConventions.INVOKE
|
||||
@@ -165,8 +161,19 @@ internal val FunctionDescriptor.isFunctionInvoke: Boolean
|
||||
|
||||
internal fun ClassDescriptor.isUnit() = this.defaultType.isUnit()
|
||||
|
||||
internal val ClassDescriptor.contributedMethods: List<FunctionDescriptor>
|
||||
internal val <T : CallableMemberDescriptor> T.allOverriddenDescriptors: List<T>
|
||||
get() {
|
||||
val result = mutableListOf<T>()
|
||||
fun traverse(descriptor: T) {
|
||||
result.add(descriptor)
|
||||
descriptor.overriddenDescriptors.forEach { traverse(it as T) }
|
||||
}
|
||||
traverse(this)
|
||||
return result
|
||||
}
|
||||
|
||||
internal val ClassDescriptor.contributedMethods: List<FunctionDescriptor>
|
||||
get () {
|
||||
val contributedDescriptors = unsubstitutedMemberScope.getContributedDescriptors()
|
||||
// (includes declarations from supers)
|
||||
|
||||
@@ -177,49 +184,85 @@ internal val ClassDescriptor.contributedMethods: List<FunctionDescriptor>
|
||||
val setters = properties.mapNotNull { it.setter }
|
||||
|
||||
val allMethods = (functions + getters + setters).sortedBy {
|
||||
// TODO: use local hash instead, but it needs major refactoring.
|
||||
it.functionName.hashCode()
|
||||
it.functionName.localHash.value
|
||||
}
|
||||
|
||||
return allMethods
|
||||
}
|
||||
|
||||
fun ClassDescriptor.isAbstract() = this.modality == Modality.SEALED || this.modality == Modality.ABSTRACT
|
||||
|| this.kind == ClassKind.ENUM_CLASS
|
||||
|
||||
// TODO: optimize
|
||||
val ClassDescriptor.vtableEntries: List<FunctionDescriptor>
|
||||
get() {
|
||||
assert(!this.isInterface)
|
||||
|
||||
val superVtableEntries = if (KotlinBuiltIns.isSpecialClassWithNoSupertypes(this)) {
|
||||
emptyList()
|
||||
} else {
|
||||
this.getSuperClassOrAny().vtableEntries
|
||||
}
|
||||
|
||||
val methods = this.contributedMethods
|
||||
|
||||
val inheritedVtableSlots = superVtableEntries.map { superMethod ->
|
||||
methods.singleOrNull { OverridingUtil.overrides(it, superMethod) } ?: superMethod
|
||||
}
|
||||
|
||||
return inheritedVtableSlots + (methods - inheritedVtableSlots).filter { it.isOverridable }
|
||||
internal fun FunctionDescriptor.hasValueTypeAt(index: Int): Boolean {
|
||||
when (index) {
|
||||
0 -> return returnType.let { it != null && it.isValueType() }
|
||||
1 -> return extensionReceiverParameter.let { it != null && it.type.isValueType() }
|
||||
else -> return this.valueParameters[index - 2].type.isValueType()
|
||||
}
|
||||
|
||||
fun ClassDescriptor.vtableIndex(function: FunctionDescriptor): Int {
|
||||
this.vtableEntries.forEachIndexed { index, functionDescriptor ->
|
||||
if (functionDescriptor == function.original) return index
|
||||
}
|
||||
throw Error(function.toString() + " not in vtable of " + this.toString())
|
||||
}
|
||||
|
||||
val ClassDescriptor.methodTableEntries: List<FunctionDescriptor>
|
||||
internal fun FunctionDescriptor.hasReferenceAt(index: Int): Boolean {
|
||||
when (index) {
|
||||
0 -> return returnType.let { it != null && !it.isValueType() }
|
||||
1 -> return extensionReceiverParameter.let { it != null && !it.type.isValueType() }
|
||||
else -> return !this.valueParameters[index - 2].type.isValueType()
|
||||
}
|
||||
}
|
||||
|
||||
private fun FunctionDescriptor.overridesFunWithReferenceAt(index: Int)
|
||||
= allOverriddenDescriptors.any { it.original.hasReferenceAt(index) }
|
||||
|
||||
private fun FunctionDescriptor.overridesFunWithValueTypeAt(index: Int)
|
||||
= allOverriddenDescriptors.any { it.original.hasValueTypeAt(index) }
|
||||
|
||||
private fun FunctionDescriptor.needBridgeToAt(target: FunctionDescriptor, index: Int)
|
||||
= hasValueTypeAt(index) xor target.hasValueTypeAt(index)
|
||||
|
||||
internal fun FunctionDescriptor.needBridgeTo(target: FunctionDescriptor)
|
||||
= (0..this.valueParameters.size + 1).any { needBridgeToAt(target, it) }
|
||||
|
||||
internal val FunctionDescriptor.target: FunctionDescriptor
|
||||
get() = (if (modality == Modality.ABSTRACT) this else resolveFakeOverride()).original
|
||||
|
||||
internal enum class BridgeDirection {
|
||||
NOT_NEEDED,
|
||||
FROM_VALUE_TYPE,
|
||||
TO_VALUE_TYPE
|
||||
}
|
||||
|
||||
private fun FunctionDescriptor.bridgeDirectionAt(index: Int) : BridgeDirection {
|
||||
if (kind.isReal) {
|
||||
if (hasValueTypeAt(index) && overridesFunWithReferenceAt(index))
|
||||
return BridgeDirection.FROM_VALUE_TYPE
|
||||
return BridgeDirection.NOT_NEEDED
|
||||
}
|
||||
|
||||
val target = this.target
|
||||
return when {
|
||||
hasValueTypeAt(index) && target.hasValueTypeAt(index) && overridesFunWithReferenceAt(index) -> BridgeDirection.FROM_VALUE_TYPE
|
||||
hasValueTypeAt(index) && target.hasReferenceAt(index) && overridesFunWithValueTypeAt(index) -> BridgeDirection.TO_VALUE_TYPE
|
||||
else -> BridgeDirection.NOT_NEEDED
|
||||
}
|
||||
}
|
||||
|
||||
private fun bridgesEqual(first: Array<BridgeDirection>, second: Array<BridgeDirection>)
|
||||
= first.indices.none { first[it] != second[it] }
|
||||
|
||||
internal fun Array<BridgeDirection>.allNotNeeded() = this.all { it == BridgeDirection.NOT_NEEDED }
|
||||
|
||||
internal val FunctionDescriptor.bridgeDirections: Array<BridgeDirection>
|
||||
get() {
|
||||
assert(!this.isAbstract())
|
||||
return this.contributedMethods.filter {
|
||||
// We check that either method is open, or one of declarations it overrides
|
||||
// is open.
|
||||
it.isOverridable || DescriptorUtils.getAllOverriddenDeclarations(it).any { it.isOverridable }
|
||||
val ourDirections = Array<BridgeDirection>(this.valueParameters.size + 2, { BridgeDirection.NOT_NEEDED })
|
||||
if (modality == Modality.ABSTRACT)
|
||||
return ourDirections
|
||||
for (index in ourDirections.indices)
|
||||
ourDirections[index] = this.bridgeDirectionAt(index)
|
||||
|
||||
if (!kind.isReal && bridgesEqual(ourDirections, this.target.bridgeDirections)) {
|
||||
// Bridge is inherited from supers
|
||||
for (index in ourDirections.indices)
|
||||
ourDirections[index] = BridgeDirection.NOT_NEEDED
|
||||
}
|
||||
// TODO: probably method table should contain all accessible methods to improve binary compatibility
|
||||
}
|
||||
|
||||
return ourDirections
|
||||
}
|
||||
|
||||
+12
-5
@@ -3,6 +3,8 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
||||
import llvm.LLVMTypeRef
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.allValueParameters
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isUnit
|
||||
import org.jetbrains.kotlin.backend.konan.isValueType
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -81,15 +83,20 @@ private fun typeToHashString(type: KotlinType): String {
|
||||
private val FunctionDescriptor.signature: String
|
||||
get() {
|
||||
val extensionReceiverPart = this.extensionReceiverParameter?.let { "${it.type}." } ?: ""
|
||||
val actualDescriptor = this.findOriginalTopMostOverriddenDescriptors().firstOrNull() ?: this
|
||||
|
||||
val argsPart = actualDescriptor.valueParameters.map {
|
||||
val argsPart = this.valueParameters.map {
|
||||
typeToHashString(it.type)
|
||||
}.joinToString(";")
|
||||
|
||||
// TODO: add return type
|
||||
// (it is not simple because return type can be changed when overriding)
|
||||
return "$extensionReceiverPart($argsPart)"
|
||||
// Just distinguish value types and references - it's needed for calling virtual methods through bridges.
|
||||
val returnTypePart =
|
||||
when {
|
||||
returnType.let { it != null && it.isValueType() } -> "ValueType"
|
||||
returnType.let { it != null && !KotlinBuiltIns.isUnitOrNullableUnit(it) } -> "Reference"
|
||||
else -> ""
|
||||
}
|
||||
|
||||
return "$extensionReceiverPart($argsPart)$returnTypePart"
|
||||
}
|
||||
|
||||
// TODO: rename to indicate that it has signature included
|
||||
|
||||
+14
-15
@@ -151,16 +151,6 @@ internal interface ContextUtils : RuntimeAware {
|
||||
return base64Encode(globalHashBytes)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this string to the sequence of bytes to be used for hashing/storing to binary/etc.
|
||||
*
|
||||
* TODO: share this implementation
|
||||
*/
|
||||
private fun stringAsBytes(str: String) = str.toByteArray(Charsets.UTF_8)
|
||||
|
||||
val String.localHash: LocalHash
|
||||
get() = LocalHash(localHash(stringAsBytes(this)))
|
||||
|
||||
val String.globalHash: ConstValue
|
||||
get() = memScoped {
|
||||
val hashBytes = this@globalHash.globalHashBytes
|
||||
@@ -170,13 +160,22 @@ internal interface ContextUtils : RuntimeAware {
|
||||
val FqName.globalHash: ConstValue
|
||||
get() = this.toString().globalHash
|
||||
|
||||
val Name.localHash: LocalHash
|
||||
get() = this.toString().localHash
|
||||
|
||||
val FqName.localHash: LocalHash
|
||||
get() = this.toString().localHash
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this string to the sequence of bytes to be used for hashing/storing to binary/etc.
|
||||
*/
|
||||
internal fun stringAsBytes(str: String) = str.toByteArray(Charsets.UTF_8)
|
||||
|
||||
internal val String.localHash: LocalHash
|
||||
get() = LocalHash(localHash(stringAsBytes(this)))
|
||||
|
||||
internal val Name.localHash: LocalHash
|
||||
get() = this.toString().localHash
|
||||
|
||||
internal val FqName.localHash: LocalHash
|
||||
get() = this.toString().localHash
|
||||
|
||||
internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
|
||||
private fun importFunction(name: String, otherModule: LLVMModuleRef): LLVMValueRef {
|
||||
|
||||
+3
-3
@@ -1654,7 +1654,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
"konan.internal.areEqualByValue" -> {
|
||||
val arg0 = args[0]
|
||||
val arg1 = args[1]
|
||||
assert (arg0.type == arg1.type)
|
||||
assert (arg0.type == arg1.type, { "Types are different: '${llvmtype2string(arg0.type)}' and '${llvmtype2string(arg1.type)}'" })
|
||||
|
||||
return when (LLVMGetTypeKind(arg0.type)) {
|
||||
LLVMTypeKind.LLVMFloatTypeKind, LLVMTypeKind.LLVMDoubleTypeKind ->
|
||||
@@ -1749,7 +1749,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
fun callDirect(descriptor: FunctionDescriptor, args: List<LLVMValueRef>,
|
||||
resultLifetime: Lifetime): LLVMValueRef {
|
||||
val realDescriptor = descriptor.resolveFakeOverride().original
|
||||
val realDescriptor = descriptor.target
|
||||
val llvmFunction = codegen.functionLlvmValue(realDescriptor)
|
||||
return call(descriptor, llvmFunction, args, resultLifetime)
|
||||
}
|
||||
@@ -1766,7 +1766,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
val owner = descriptor.containingDeclaration as ClassDescriptor
|
||||
val llvmMethod = if (!owner.isInterface) {
|
||||
// If this is a virtual method of the class - we can call via vtable.
|
||||
val index = owner.vtableIndex(descriptor)
|
||||
val index = context.getVtableBuilder(owner).vtableIndex(descriptor)
|
||||
|
||||
val vtablePlace = codegen.gep(typeInfoPtr, Int32(1).llvm) // typeInfoPtr + 1
|
||||
val vtable = codegen.bitcast(kInt8PtrPtr, vtablePlace)
|
||||
|
||||
+1
-1
@@ -232,7 +232,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
||||
|
||||
val typeInfoWithVtableType = structType(
|
||||
runtime.typeInfoType,
|
||||
LLVMArrayType(int8TypePtr, descriptor.vtableEntries.size)!!
|
||||
LLVMArrayType(int8TypePtr, context.getVtableBuilder(descriptor).vtableEntries.size)!!
|
||||
)
|
||||
|
||||
typeInfoGlobal = staticData.createGlobal(typeInfoWithVtableType, typeInfoGlobalName, isExported = false)
|
||||
|
||||
+20
-7
@@ -121,15 +121,21 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
val fieldsPtr = staticData.placeGlobalConstArray("kfields:$className",
|
||||
runtime.fieldTableRecordType, fields)
|
||||
|
||||
val methods = if (!classDesc.isAbstract()) {
|
||||
classDesc.methodTableEntries.map {
|
||||
val nameSignature = it.functionName.localHash
|
||||
val methods = if (classDesc.isAbstract()) {
|
||||
emptyList()
|
||||
} else {
|
||||
val functionNames = mutableMapOf<Long, OverriddenFunctionDescriptor>()
|
||||
context.getVtableBuilder(classDesc).methodTableEntries.map {
|
||||
val functionName = it.overriddenDescriptor.functionName
|
||||
val nameSignature = functionName.localHash
|
||||
val previous = functionNames.putIfAbsent(nameSignature.value, it)
|
||||
if (previous != null)
|
||||
throw AssertionError("Duplicate method table entry: functionName = '$functionName', hash = '${nameSignature.value}', entry1 = $previous, entry2 = $it")
|
||||
|
||||
// TODO: compile-time resolution limits binary compatibility
|
||||
val methodEntryPoint = it.resolveFakeOverride().original.entryPointAddress
|
||||
val methodEntryPoint = it.implementation.entryPointAddress
|
||||
MethodTableRecord(nameSignature, methodEntryPoint)
|
||||
}.sortedBy { it.nameSignature.value }
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
|
||||
val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className",
|
||||
@@ -148,7 +154,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
typeInfo
|
||||
} else {
|
||||
// TODO: compile-time resolution limits binary compatibility
|
||||
val vtableEntries = classDesc.vtableEntries.map { it.resolveFakeOverride().original.entryPointAddress }
|
||||
val vtableEntries = context.getVtableBuilder(classDesc).vtableEntries.map { it.implementation.entryPointAddress }
|
||||
val vtable = ConstArray(int8TypePtr, vtableEntries)
|
||||
Struct(typeInfo, vtable)
|
||||
}
|
||||
@@ -159,4 +165,11 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
exportTypeInfoIfRequired(classDesc, classDesc.llvmTypeInfoPtr)
|
||||
}
|
||||
|
||||
internal val OverriddenFunctionDescriptor.implementation: FunctionDescriptor
|
||||
get() {
|
||||
val target = descriptor.target
|
||||
if (!needBridge) return target
|
||||
val bridgeOwner = if (descriptor.bridgeDirections.allNotNeeded()) target else descriptor
|
||||
return context.specialDescriptorsFactory.getBridgeDescriptor(bridgeOwner)
|
||||
}
|
||||
}
|
||||
|
||||
+34
-5
@@ -4,15 +4,12 @@ import org.jetbrains.kotlin.backend.common.AbstractValueUsageTransformer
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.ValueType
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalClass
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||
import org.jetbrains.kotlin.backend.konan.ir.isNullConst
|
||||
import org.jetbrains.kotlin.backend.konan.notNullableIsRepresentedAs
|
||||
import org.jetbrains.kotlin.backend.konan.isRepresentedAs
|
||||
import org.jetbrains.kotlin.backend.konan.util.atMostOne
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
@@ -94,6 +91,38 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
val irCall = super.visitCall(expression) as IrCall
|
||||
|
||||
val descriptor = irCall.descriptor as? FunctionDescriptor ?: return irCall
|
||||
if (descriptor.modality == Modality.ABSTRACT || (irCall.superQualifier == null && descriptor.isOverridable))
|
||||
return irCall // A virtual call. box/unbox will be in the corresponding bridge.
|
||||
|
||||
val target = descriptor.target
|
||||
|
||||
if (!descriptor.original.needBridgeTo(target)) return irCall
|
||||
|
||||
val overriddenCall = IrCallImpl(irCall.startOffset, irCall.endOffset,
|
||||
target, remapTypeArguments(irCall, target)).apply {
|
||||
dispatchReceiver = irCall.dispatchReceiver
|
||||
extensionReceiver = irCall.extensionReceiver
|
||||
mapValueParameters { irCall.getValueArgument(it)!! }
|
||||
}
|
||||
|
||||
return super.visitCall(overriddenCall)
|
||||
}
|
||||
|
||||
private fun remapTypeArguments(oldExpression: IrMemberAccessExpression, newCallee: CallableDescriptor): Map<TypeParameterDescriptor, KotlinType>? {
|
||||
val oldCallee = oldExpression.descriptor
|
||||
|
||||
return if (oldCallee.typeParameters.isEmpty())
|
||||
null
|
||||
else oldCallee.typeParameters.associateBy(
|
||||
{ newCallee.typeParameters[it.index] },
|
||||
{ oldExpression.getTypeArgument(it)!! }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the [ValueType] given type represented in generated code as,
|
||||
* or `null` if represented as object reference.
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package org.jetbrains.kotlin.backend.konan.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
|
||||
internal class BridgesBuilding(val context: Context) : FileLoweringPass {
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
|
||||
override fun visitClass(declaration: IrClass): IrStatement {
|
||||
val irClass = super.visitClass(declaration) as IrClass
|
||||
|
||||
val functions = mutableSetOf<FunctionDescriptor?>()
|
||||
irClass.declarations.forEach {
|
||||
when (it) {
|
||||
is IrFunction -> functions.add(it.descriptor)
|
||||
is IrProperty -> {
|
||||
functions.add(it.getter?.descriptor)
|
||||
functions.add(it.setter?.descriptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
irClass.descriptor.contributedMethods.forEach { functions.add(it) }
|
||||
|
||||
functions.forEach { buildBridge(it, irClass) }
|
||||
|
||||
return irClass
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
private object DECLARATION_ORIGIN_BRIDGE_METHOD :
|
||||
IrDeclarationOriginImpl("BRIDGE_METHOD")
|
||||
|
||||
private fun buildBridge(descriptor: FunctionDescriptor?, irClass: IrClass) {
|
||||
if (descriptor == null) return
|
||||
if (descriptor.bridgeDirections.allNotNeeded())
|
||||
return
|
||||
|
||||
val bridgeDescriptor = context.specialDescriptorsFactory.getBridgeDescriptor(descriptor)
|
||||
|
||||
val delegatingCall = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, descriptor.target,
|
||||
superQualifier = descriptor.target.containingDeclaration as ClassDescriptor /* Call non-virtually */).apply {
|
||||
val dispatchReceiverParameter = bridgeDescriptor.dispatchReceiverParameter
|
||||
if (dispatchReceiverParameter != null)
|
||||
dispatchReceiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, dispatchReceiverParameter)
|
||||
val extensionReceiverParameter = bridgeDescriptor.extensionReceiverParameter
|
||||
if (extensionReceiverParameter != null)
|
||||
extensionReceiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, extensionReceiverParameter)
|
||||
bridgeDescriptor.valueParameters.forEach {
|
||||
this.putValueArgument(it.index, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, it))
|
||||
}
|
||||
}
|
||||
|
||||
val bridgeBody = if (bridgeDescriptor.returnType.let { it != null && !KotlinBuiltIns.isUnitOrNullableUnit(it) })
|
||||
IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, bridgeDescriptor, delegatingCall)
|
||||
else
|
||||
delegatingCall
|
||||
irClass.declarations.add(IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, DECLARATION_ORIGIN_BRIDGE_METHOD,
|
||||
bridgeDescriptor, IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf(bridgeBody))))
|
||||
}
|
||||
}
|
||||
-1
@@ -4,7 +4,6 @@ import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrInstanceInitializerCall
|
||||
|
||||
@@ -441,6 +441,76 @@ task initializers_correctOrder2(type: RunKonanTest) {
|
||||
source = "codegen/initializers/correctOrder2.kt"
|
||||
}
|
||||
|
||||
task bridges_test0(type: RunKonanTest) {
|
||||
goldValue = "42\n42\n"
|
||||
source = "codegen/bridges/test0.kt"
|
||||
}
|
||||
|
||||
task bridges_test1(type: RunKonanTest) {
|
||||
goldValue = "1042\n"
|
||||
source = "codegen/bridges/test1.kt"
|
||||
}
|
||||
|
||||
task bridges_test2(type: RunKonanTest) {
|
||||
goldValue = "42\n42\n"
|
||||
source = "codegen/bridges/test2.kt"
|
||||
}
|
||||
|
||||
task bridges_test3(type: RunKonanTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/bridges/test3.kt"
|
||||
}
|
||||
|
||||
task bridges_test4(type: RunKonanTest) {
|
||||
goldValue = "42\n42\n"
|
||||
source = "codegen/bridges/test4.kt"
|
||||
}
|
||||
|
||||
task bridges_test5(type: RunKonanTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/bridges/test5.kt"
|
||||
}
|
||||
|
||||
task bridges_test6(type: RunKonanTest) {
|
||||
goldValue = "42\n42\n42\n42\n42\n"
|
||||
source = "codegen/bridges/test6.kt"
|
||||
}
|
||||
|
||||
task bridges_test7(type: RunKonanTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/bridges/test7.kt"
|
||||
}
|
||||
|
||||
task bridges_test8(type: RunKonanTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/bridges/test8.kt"
|
||||
}
|
||||
|
||||
task bridges_test9(type: RunKonanTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/bridges/test9.kt"
|
||||
}
|
||||
|
||||
task bridges_test10(type: RunKonanTest) {
|
||||
goldValue = "42\n42\n"
|
||||
source = "codegen/bridges/test10.kt"
|
||||
}
|
||||
|
||||
task bridges_test11(type: RunKonanTest) {
|
||||
goldValue = "42\n42\n42\n"
|
||||
source = "codegen/bridges/test11.kt"
|
||||
}
|
||||
|
||||
task bridges_test12(type: RunKonanTest) {
|
||||
goldValue = "B: 42\nC: 42\n"
|
||||
source = "codegen/bridges/test12.kt"
|
||||
}
|
||||
|
||||
task bridges_test13(type: RunKonanTest) {
|
||||
goldValue = "42\n42\n"
|
||||
source = "codegen/bridges/test13.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 @@
|
||||
// vtable call
|
||||
open class A {
|
||||
open fun foo(): Any = "A"
|
||||
}
|
||||
|
||||
open class C : A() {
|
||||
override fun foo(): Int = 42
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val c = C()
|
||||
val a: A = c
|
||||
println(c.foo().toString())
|
||||
println(a.foo().toString())
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// interface call, bridge overridden
|
||||
interface Z1 {
|
||||
fun foo(x: Int) : Any
|
||||
}
|
||||
|
||||
open class A : Z1 {
|
||||
override fun foo(x: Int) : Int = 5
|
||||
}
|
||||
|
||||
open class B : A() {
|
||||
override fun foo(x: Int) : Int = 42
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val z1: A = B()
|
||||
println((z1.foo(1) + 1000).toString())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
open class A<T> {
|
||||
open fun foo(x: T) {
|
||||
println(x.toString())
|
||||
}
|
||||
}
|
||||
|
||||
interface I {
|
||||
fun foo(x: Int)
|
||||
}
|
||||
|
||||
class B : A<Int>(), I {
|
||||
var z: Int = 5
|
||||
override fun foo(x: Int) {
|
||||
z = x
|
||||
}
|
||||
}
|
||||
|
||||
fun zzz(a: A<Int>) {
|
||||
a.foo(42)
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val b = B()
|
||||
zzz(b)
|
||||
val a = A<Int>()
|
||||
zzz(a)
|
||||
println(b.z)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
interface I {
|
||||
fun foo(x: Int)
|
||||
}
|
||||
|
||||
abstract class A<T> {
|
||||
abstract fun foo(x: T)
|
||||
}
|
||||
|
||||
class B : A<Int>(), I {
|
||||
override fun foo(x: Int) = println(x)
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val b = B()
|
||||
val a: A<Int> = b
|
||||
val c: I = b
|
||||
b.foo(42)
|
||||
a.foo(42)
|
||||
c.foo(42)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
abstract class A<in T> {
|
||||
abstract fun foo(x: T)
|
||||
}
|
||||
|
||||
class B : A<Int>() {
|
||||
override fun foo(x: Int) {
|
||||
println("B: $x")
|
||||
}
|
||||
}
|
||||
|
||||
class C : A<Any>() {
|
||||
override fun foo(x: Any) {
|
||||
println("C: $x")
|
||||
}
|
||||
}
|
||||
|
||||
fun foo(arg: A<Int>) {
|
||||
arg.foo(42)
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
foo(B())
|
||||
foo(C())
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
open class A<T> {
|
||||
open fun T.foo() {
|
||||
println(this.toString())
|
||||
}
|
||||
|
||||
fun bar(x: T) {
|
||||
x.foo()
|
||||
}
|
||||
}
|
||||
|
||||
open class B: A<Int>() {
|
||||
override fun Int.foo() {
|
||||
println(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args : Array<String>) {
|
||||
val b = B()
|
||||
val a = A<Int>()
|
||||
b.bar(42)
|
||||
a.bar(42)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// vtable call, bridge inherited
|
||||
open class A {
|
||||
open fun foo(): Any = "A"
|
||||
}
|
||||
|
||||
open class C : A() {
|
||||
override fun foo(): Int = 42
|
||||
}
|
||||
|
||||
open class D: C()
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val c = D()
|
||||
val a: A = c
|
||||
println(c.foo().toString())
|
||||
println(a.foo().toString())
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// non-generic interface, generic impl, non-virtual call + interface call
|
||||
open class A<T> {
|
||||
var size: T = 56 as T
|
||||
}
|
||||
|
||||
interface C {
|
||||
var size: Int
|
||||
}
|
||||
|
||||
class B : C, A<Int>()
|
||||
|
||||
fun box(): String {
|
||||
val b = B()
|
||||
if (b.size != 56) return "fail 1"
|
||||
|
||||
b.size = 55
|
||||
if (b.size != 55) return "fail 2"
|
||||
|
||||
val c: C = b
|
||||
if (c.size != 55) return "fail 3"
|
||||
|
||||
c.size = 57
|
||||
if (c.size != 57) return "fail 4"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(box())
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// vtable call + interface call
|
||||
interface Z {
|
||||
fun foo(): Any
|
||||
}
|
||||
|
||||
interface Y {
|
||||
fun foo(): Int
|
||||
}
|
||||
|
||||
open class A {
|
||||
fun foo(): Int = 42
|
||||
}
|
||||
|
||||
open class B: A(), Z, Y
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val z: Z = B()
|
||||
val y: Y = z as Y
|
||||
println(z.foo().toString())
|
||||
println(y.foo().toString())
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// non-generic interface, generic impl, vtable call + interface call
|
||||
open class A<T> {
|
||||
open var size: T = 56 as T
|
||||
}
|
||||
|
||||
interface C {
|
||||
var size: Int
|
||||
}
|
||||
|
||||
open class B : C, A<Int>()
|
||||
|
||||
fun box(): String {
|
||||
val b = B()
|
||||
if (b.size != 56) return "fail 1"
|
||||
|
||||
b.size = 55
|
||||
if (b.size != 55) return "fail 2"
|
||||
|
||||
val c: C = b
|
||||
if (c.size != 55) return "fail 3"
|
||||
|
||||
c.size = 57
|
||||
if (c.size != 57) return "fail 4"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(box())
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// vtable call + interface call
|
||||
interface Z {
|
||||
fun foo(): Any
|
||||
}
|
||||
|
||||
interface Y {
|
||||
fun foo(): Int
|
||||
}
|
||||
|
||||
open class A {
|
||||
open fun foo(): Any = "A"
|
||||
}
|
||||
|
||||
open class C : A() {
|
||||
override fun foo(): Int = 42
|
||||
}
|
||||
|
||||
open class D: C(), Y, Z
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val d = D()
|
||||
val y: Y = d
|
||||
val z: Z = d
|
||||
val c: C = d
|
||||
val a: A = d
|
||||
println(d.foo().toString())
|
||||
println(y.foo().toString())
|
||||
println(z.foo().toString())
|
||||
println(c.foo().toString())
|
||||
println(a.foo().toString())
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// generic interface, non-generic impl, vtable call + interface call
|
||||
open class A {
|
||||
open var size: Int = 56
|
||||
}
|
||||
|
||||
interface C<T> {
|
||||
var size: T
|
||||
}
|
||||
|
||||
open class B : C<Int>, A()
|
||||
|
||||
fun box(): String {
|
||||
val b = B()
|
||||
if (b.size != 56) return "fail 1"
|
||||
|
||||
b.size = 55
|
||||
if (b.size != 55) return "fail 2"
|
||||
|
||||
val c: C<Int> = b
|
||||
if (c.size != 55) return "fail 3"
|
||||
|
||||
c.size = 57
|
||||
if (c.size != 57) return "fail 4"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(box())
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// generic interface, non-generic impl, non-virtual call + interface call
|
||||
open class A {
|
||||
var size: Int = 56
|
||||
}
|
||||
|
||||
interface C<T> {
|
||||
var size: T
|
||||
}
|
||||
|
||||
class B : C<Int>, A()
|
||||
|
||||
fun box(): String {
|
||||
val b = B()
|
||||
if (b.size != 56) return "fail 1"
|
||||
|
||||
b.size = 55
|
||||
if (b.size != 55) return "fail 2"
|
||||
|
||||
val c: C<Int> = b
|
||||
if (c.size != 55) return "fail 3"
|
||||
|
||||
c.size = 57
|
||||
if (c.size != 57) return "fail 4"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(box())
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// abstract class vtable call
|
||||
abstract class A {
|
||||
abstract fun foo(): String
|
||||
}
|
||||
|
||||
abstract class B : A()
|
||||
|
||||
class Z : B() {
|
||||
override fun foo() = "Z"
|
||||
}
|
||||
|
||||
|
||||
fun box(): String {
|
||||
val z = Z()
|
||||
val b: B = z
|
||||
val a: A = z
|
||||
return when {
|
||||
z.foo() != "Z" -> "Fail #1"
|
||||
b.foo() != "Z" -> "Fail #2"
|
||||
a.foo() != "Z" -> "Fail #3"
|
||||
else -> "OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(box())
|
||||
}
|
||||
Reference in New Issue
Block a user