Support inline classes (#1840)

This commit is contained in:
SvyatoslavScherbina
2018-08-10 09:57:15 +03:00
committed by GitHub
parent 699d530e87
commit cd4f116f78
49 changed files with 1486 additions and 896 deletions
@@ -20,6 +20,9 @@ import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.full.companionObjectInstance
typealias NativePtr = Long
internal typealias NonNullNativePtr = NativePtr
@PublishedApi internal fun NonNullNativePtr.toNativePtr() = this
internal fun NativePtr.toNonNull(): NonNullNativePtr = this
val nativeNullPtr: NativePtr = 0L
// TODO: the functions below should eventually be intrinsified
@@ -24,8 +24,8 @@ package kotlinx.cinterop
*
* TODO: the behavior of [equals], [hashCode] and [toString] differs on Native and JVM backends.
*/
abstract class NativePointed(rawPtr: NativePtr) {
var rawPtr = rawPtr
open class NativePointed internal constructor(rawPtr: NonNullNativePtr) {
var rawPtr = rawPtr.toNativePtr()
internal set
}
@@ -40,7 +40,7 @@ val NativePointed?.rawPtr: NativePtr
*/
inline fun <reified T : NativePointed> interpretPointed(ptr: NativePtr): T = interpretNullablePointed<T>(ptr)!!
private class OpaqueNativePointed(rawPtr: NativePtr) : NativePointed(rawPtr)
private class OpaqueNativePointed(rawPtr: NativePtr) : NativePointed(rawPtr.toNonNull())
fun interpretOpaquePointed(ptr: NativePtr): NativePointed = interpretPointed<OpaqueNativePointed>(ptr)
fun interpretNullableOpaquePointed(ptr: NativePtr): NativePointed? = interpretNullablePointed<OpaqueNativePointed>(ptr)
@@ -53,7 +53,7 @@ inline fun <reified T : NativePointed> NativePointed.reinterpret(): T = interpre
/**
* C data or code.
*/
abstract class CPointed(rawPtr: NativePtr) : NativePointed(rawPtr)
abstract class CPointed(rawPtr: NativePtr) : NativePointed(rawPtr.toNonNull())
/**
* Represents a reference to (possibly empty) sequence of C values.
@@ -130,7 +130,11 @@ abstract class CValue<T : CVariable> : CValues<T>()
/**
* C pointer.
*/
class CPointer<T : CPointed> internal constructor(val rawValue: NativePtr) : CValuesRef<T>() {
class CPointer<T : CPointed> internal constructor(@PublishedApi internal val value: NonNullNativePtr) : CValuesRef<T>() {
// TODO: replace by [value].
@Suppress("NOTHING_TO_INLINE")
inline val rawValue: NativePtr get() = value.toNativePtr()
override fun equals(other: Any?): Boolean {
if (this === other) {
@@ -18,8 +18,13 @@ package kotlinx.cinterop
import konan.internal.getNativeNullPtr
import konan.internal.Intrinsic
import konan.internal.reinterpret
typealias NativePtr = konan.internal.NativePtr
internal typealias NonNullNativePtr = konan.internal.NonNullNativePtr
@Suppress("NOTHING_TO_INLINE")
internal inline fun NativePtr.toNonNull() = this.reinterpret<NativePtr, NonNullNativePtr>()
inline val nativeNullPtr: NativePtr
get() = getNativeNullPtr()
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.cli.bc
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.Argument
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.config.LanguageFeature
class K2NativeCompilerArguments : CommonCompilerArguments() {
// First go the options interesting to the general public.
@@ -147,5 +149,9 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
description = "Paths to friend modules"
)
var friendModules: String? = null
override fun configureLanguageFeatures(collector: MessageCollector) = super.configureLanguageFeatures(collector).also {
it[LanguageFeature.InlineClasses] = LanguageFeature.State.ENABLED // TODO: remove after updating to 1.3.
}
}
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2018 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 org.jetbrains.kotlin.backend.konan
sealed class BinaryType<out T> {
class Primitive(val type: PrimitiveBinaryType) : BinaryType<Nothing>()
class Reference<T>(val types: Sequence<T>, val nullable: Boolean) : BinaryType<T>()
}
fun BinaryType<*>.primitiveBinaryTypeOrNull(): PrimitiveBinaryType? = when (this) {
is BinaryType.Primitive -> this.type
is BinaryType.Reference -> null
}
enum class PrimitiveBinaryType {
BOOLEAN, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, POINTER
}
@@ -16,51 +16,180 @@
package org.jetbrains.kotlin.backend.konan
import kotlinx.cinterop.toByte
import llvm.*
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.irasdescriptors.fqNameSafe
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.getPropertyGetter
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
internal fun KonanSymbols.getTypeConversion(actualType: KotlinType, expectedType: KotlinType) =
getTypeConversion(actualType.correspondingValueType, expectedType.correspondingValueType)
internal fun KonanSymbols.getTypeConversion(actualType: IrType, expectedType: IrType): IrSimpleFunctionSymbol? =
getTypeConversionImpl(actualType.getInlinedClass(), expectedType.getInlinedClass())
internal fun KonanSymbols.getTypeConversion(actualType: IrType, expectedType: IrType) =
getTypeConversion(actualType.correspondingValueType, expectedType.correspondingValueType)
internal fun KonanSymbols.getTypeConversion(actualType: KotlinType, expectedType: KotlinType): IrSimpleFunctionSymbol? {
// TODO: rework all usages and remove this method.
val actualInlinedClass = actualType.getInlinedClass()?.let { context.ir.get(it) }
val expectedInlinedClass = expectedType.getInlinedClass()?.let { context.ir.get(it) }
internal fun KonanSymbols.getTypeConversion(
actualValueType: ValueType?,
expectedValueType: ValueType?
return getTypeConversionImpl(actualInlinedClass, expectedInlinedClass)
}
private fun KonanSymbols.getTypeConversionImpl(
actualInlinedClass: IrClass?,
expectedInlinedClass: IrClass?
): IrSimpleFunctionSymbol? {
if (actualInlinedClass == expectedInlinedClass) return null
return when {
actualValueType == expectedValueType -> null
actualInlinedClass == null && expectedInlinedClass == null -> null
actualInlinedClass != null && expectedInlinedClass == null -> context.getBoxFunction(actualInlinedClass)
actualInlinedClass == null && expectedInlinedClass != null -> context.getUnboxFunction(expectedInlinedClass)
else -> error("actual type is ${actualInlinedClass?.fqNameSafe}, expected ${expectedInlinedClass?.fqNameSafe}")
}?.symbol
}
actualValueType == null && expectedValueType != null -> {
// This may happen in the following cases:
// 1. `actualType` is `Nothing`;
// 2. `actualType` is incompatible.
internal val Context.getBoxFunction: (IrClass) -> IrSimpleFunction by Context.lazyMapMember { inlinedClass ->
assert(inlinedClass.isUsedAsBoxClass())
this.getUnboxFunction(expectedValueType)
}
val symbols = ir.symbols
actualValueType != null && expectedValueType == null -> {
this.boxFunctions[actualValueType]!!
}
val isNullable = inlinedClass.inlinedClassIsNullable()
val unboxedType = inlinedClass.defaultOrNullableType(isNullable)
val boxedType = symbols.any.owner.defaultOrNullableType(isNullable)
else -> throw IllegalArgumentException("actual type is $actualValueType, expected $expectedValueType")
val parameterType = unboxedType
val returnType = boxedType
val descriptor = SimpleFunctionDescriptorImpl.create(
inlinedClass.descriptor,
Annotations.EMPTY,
Name.special("<box>"),
CallableMemberDescriptor.Kind.DECLARATION,
SourceElement.NO_SOURCE
)
val parameter = ValueParameterDescriptorImpl(
descriptor,
null,
0,
Annotations.EMPTY,
Name.identifier("value"),
parameterType.toKotlinType(),
false,
false,
false,
null,
SourceElement.NO_SOURCE
)
descriptor.initialize(
null,
null,
emptyList(),
listOf(parameter),
returnType.toKotlinType(),
Modality.FINAL,
Visibilities.PUBLIC
)
val startOffset = inlinedClass.startOffset
val endOffset = inlinedClass.endOffset
IrFunctionImpl(startOffset, endOffset, IrDeclarationOrigin.DEFINED, descriptor).apply {
this.returnType = returnType
this.valueParameters.add(IrValueParameterImpl(
startOffset,
endOffset,
IrDeclarationOrigin.DEFINED,
parameter,
parameterType,
null
))
this.parent = inlinedClass
}
}
internal fun KonanSymbols.getUnboxFunction(valueType: ValueType): IrSimpleFunctionSymbol =
this.unboxFunctions[valueType]
?: this.boxClasses[valueType]!!.getPropertyGetter("value")!! as IrSimpleFunctionSymbol
internal val Context.getUnboxFunction: (IrClass) -> IrSimpleFunction by Context.lazyMapMember { inlinedClass ->
assert(inlinedClass.isUsedAsBoxClass())
val symbols = ir.symbols
val isNullable = inlinedClass.inlinedClassIsNullable()
val unboxedType = inlinedClass.defaultOrNullableType(isNullable)
val boxedType = symbols.any.owner.defaultOrNullableType(isNullable)
val parameterType = boxedType
val returnType = unboxedType
val descriptor = SimpleFunctionDescriptorImpl.create(
inlinedClass.descriptor,
Annotations.EMPTY,
Name.special("<unbox>"),
CallableMemberDescriptor.Kind.DECLARATION,
SourceElement.NO_SOURCE
)
val parameter = ValueParameterDescriptorImpl(
descriptor,
null,
0,
Annotations.EMPTY,
Name.identifier("value"),
parameterType.toKotlinType(),
false,
false,
false,
null,
SourceElement.NO_SOURCE
)
descriptor.initialize(
null,
null,
emptyList(),
listOf(parameter),
returnType.toKotlinType(),
Modality.FINAL,
Visibilities.PUBLIC
)
val startOffset = inlinedClass.startOffset
val endOffset = inlinedClass.endOffset
IrFunctionImpl(startOffset, endOffset, IrDeclarationOrigin.DEFINED, descriptor).apply {
this.returnType = returnType
this.valueParameters.add(IrValueParameterImpl(
startOffset,
endOffset,
IrDeclarationOrigin.DEFINED,
parameter,
parameterType,
null
))
this.parent = inlinedClass
}
}
/**
* Initialize static boxing.
@@ -68,13 +197,11 @@ internal fun KonanSymbols.getUnboxFunction(valueType: ValueType): IrSimpleFuncti
*/
internal fun initializeCachedBoxes(context: Context) {
if (context.config.produce.isNativeBinary) {
val cachedTypes = listOf(ValueType.BOOLEAN, ValueType.BYTE, ValueType.CHAR,
ValueType.SHORT, ValueType.INT, ValueType.LONG)
cachedTypes.forEach { valueType ->
val cacheName = "${valueType.name}_CACHE"
val rangeStart = "${valueType.name}_RANGE_FROM"
val rangeEnd = "${valueType.name}_RANGE_TO"
valueType.initCache(context, cacheName, rangeStart, rangeEnd)
BoxCache.values().forEach { cache ->
val cacheName = "${cache.name}_CACHE"
val rangeStart = "${cache.name}_RANGE_FROM"
val rangeEnd = "${cache.name}_RANGE_TO"
initCache(cache, context, cacheName, rangeStart, rangeEnd)
}
}
}
@@ -82,48 +209,56 @@ internal fun initializeCachedBoxes(context: Context) {
/**
* Adds global that refers to the cache.
*/
private fun ValueType.initCache(context: Context, cacheName: String,
rangeStartName: String, rangeEndName: String) {
val kotlinType = context.ir.symbols.boxClasses[this]!!.owner
val (start, end) = context.config.target.getBoxCacheRange(this)
private fun initCache(cache: BoxCache, context: Context, cacheName: String,
rangeStartName: String, rangeEndName: String) {
val kotlinType = context.irBuiltIns.getKotlinClass(cache)
val staticData = context.llvm.staticData
val llvmType = staticData.getLLVMType(kotlinType.defaultType)
val (start, end) = context.config.target.getBoxCacheRange(cache)
// Constancy of these globals allows LLVM's constant propagation and DCE
// to remove fast path of boxing function in case of empty range.
context.llvm.staticData.placeGlobal(rangeStartName, createConstant(start), true)
staticData.placeGlobal(rangeStartName, createConstant(llvmType, start), true)
.setConstant(true)
context.llvm.staticData.placeGlobal(rangeEndName, createConstant(end), true)
staticData.placeGlobal(rangeEndName, createConstant(llvmType, end), true)
.setConstant(true)
val staticData = context.llvm.staticData
val values = (start..end).map { staticData.createInitializer(kotlinType, createConstant(it)) }
val llvmBoxType = structType(context.llvm.runtime.objHeaderType, this.llvmType)
val values = (start..end).map { staticData.createInitializer(kotlinType, createConstant(llvmType, it)) }
val llvmBoxType = structType(context.llvm.runtime.objHeaderType, llvmType)
staticData.placeGlobalConstArray(cacheName, llvmBoxType, values, true).llvm
}
private fun ValueType.createConstant(value: Int) =
constValue(when (this) {
ValueType.BOOLEAN -> LLVMConstInt(LLVMInt1Type(), (value > 0).toByte().toLong(), 1)!!
ValueType.BYTE -> LLVMConstInt(LLVMInt8Type(), value.toByte().toLong(), 1)!!
ValueType.CHAR -> LLVMConstInt(LLVMInt16Type(), value.toChar().toLong(), 0)!!
ValueType.SHORT -> LLVMConstInt(LLVMInt16Type(), value.toShort().toLong(), 1)!!
ValueType.INT -> LLVMConstInt(LLVMInt32Type(), value.toLong(), 1)!!
ValueType.LONG -> LLVMConstInt(LLVMInt64Type(), value.toLong(), 1)!!
else -> error("Cannot box value of type $this")
})
private fun createConstant(llvmType: LLVMTypeRef, value: Int): ConstValue =
constValue(LLVMConstInt(llvmType, value.toLong(), 1)!!)
// When start is greater than end then `inRange` check is always false
// and can be eliminated by LLVM.
private val emptyRange = 1 to 0
// Memory usage is around 20kb.
private val defaultCacheRanges = mapOf(
ValueType.BOOLEAN to (0 to 1),
ValueType.BYTE to (-128 to 127),
ValueType.SHORT to (-128 to 127),
ValueType.CHAR to (0 to 255),
ValueType.INT to (-128 to 127),
ValueType.LONG to (-128 to 127)
)
private val BoxCache.defaultRange get() = when (this) {
BoxCache.BOOLEAN -> (0 to 1)
BoxCache.BYTE -> (-128 to 127)
BoxCache.SHORT -> (-128 to 127)
BoxCache.CHAR -> (0 to 255)
BoxCache.INT -> (-128 to 127)
BoxCache.LONG -> (-128 to 127)
}
fun KonanTarget.getBoxCacheRange(valueType: ValueType): Pair<Int, Int> = when (this) {
private fun KonanTarget.getBoxCacheRange(cache: BoxCache): Pair<Int, Int> = when (this) {
is KonanTarget.ZEPHYR -> emptyRange
else -> defaultCacheRanges[valueType]!!
else -> cache.defaultRange
}
internal fun IrBuiltIns.getKotlinClass(cache: BoxCache): IrClass = when (cache) {
BoxCache.BOOLEAN -> booleanClass
BoxCache.BYTE -> byteClass
BoxCache.SHORT -> shortClass
BoxCache.CHAR -> charClass
BoxCache.INT -> intClass
BoxCache.LONG -> longClass
}.owner
enum class BoxCache {
BOOLEAN, BYTE, SHORT, CHAR, INT, LONG
}
@@ -116,12 +116,6 @@ private val cnameAnnotation = FqName("konan.internal.CName")
private fun org.jetbrains.kotlin.types.KotlinType.isGeneric() =
constructor.declarationDescriptor is TypeParameterDescriptor
private val ClassDescriptor.isString
get() = fqNameSafe.asString() == "kotlin.String"
private val ClassDescriptor.isValueType
get() = this.defaultType.correspondingValueType != null
private fun isExportedFunction(descriptor: FunctionDescriptor): Boolean {
if (!descriptor.isEffectivelyPublicApi || !descriptor.kind.isReal || descriptor.isExpect)
@@ -143,6 +137,8 @@ private fun isExportedClass(descriptor: ClassDescriptor): Boolean {
// Do not export types with type parameters.
// TODO: is it correct?
if (!descriptor.declaredTypeParameters.isEmpty()) return false
// Do not export inline classes for now. TODO: add proper support.
if (descriptor.isInlined()) return false
return true
}
@@ -372,7 +368,7 @@ private class ExportedElement(val kind: ElementKind,
private fun translateArgument(name: String, clazz: ClassDescriptor, direction: Direction,
builder: StringBuilder): String {
return when {
clazz.isString ->
owner.isMappedToString(clazz) ->
if (direction == Direction.C_TO_KOTLIN) {
builder.append(" KObjHolder ${name}_holder;\n")
"CreateStringFromCString($name, ${name}_holder.slot())"
@@ -387,7 +383,7 @@ private class ExportedElement(val kind: ElementKind,
"((${owner.translateType(clazz)}){ .pinned = CreateStablePointer(${name})})"
}
else -> {
assert(clazz.isValueType) {
assert(!clazz.defaultType.binaryTypeIsReference()) {
println(clazz.toString())
}
name
@@ -733,7 +729,7 @@ internal class CAdapterGenerator(
val set = mutableSetOf<ClassDescriptor>()
defineUsedTypesImpl(scope, set)
set.forEach {
if (isMappedToReference(it)) {
if (isMappedToReference(it) && !it.isInlined()) {
output("typedef struct {", indent)
output("${prefix}_KNativePtr pinned;", indent + 1)
output("} ${translateType(it)};", indent)
@@ -884,26 +880,32 @@ internal class CAdapterGenerator(
"<set-?>" to "set"
)
private val primitiveTypeMapping = mapOf(
ValueType.BOOLEAN to "${prefix}_KBoolean",
ValueType.BYTE to "${prefix}_KByte",
ValueType.SHORT to "${prefix}_KShort",
ValueType.INT to "${prefix}_KInt",
ValueType.LONG to "${prefix}_KLong",
ValueType.FLOAT to "${prefix}_KFloat",
ValueType.DOUBLE to "${prefix}_KDouble",
ValueType.CHAR to "${prefix}_KChar",
ValueType.C_POINTER to "void*",
ValueType.NATIVE_PTR to "void*",
ValueType.NATIVE_POINTED to "void*"
)
private val primitiveTypeMapping = KonanPrimitiveType.values().associate {
it to when (it) {
KonanPrimitiveType.BOOLEAN -> "${prefix}_KBoolean"
KonanPrimitiveType.CHAR -> "${prefix}_KChar"
KonanPrimitiveType.BYTE -> "${prefix}_KByte"
KonanPrimitiveType.SHORT -> "${prefix}_KShort"
KonanPrimitiveType.INT -> "${prefix}_KInt"
KonanPrimitiveType.LONG -> "${prefix}_KLong"
KonanPrimitiveType.FLOAT -> "${prefix}_KFloat"
KonanPrimitiveType.DOUBLE -> "${prefix}_KDouble"
KonanPrimitiveType.NON_NULL_NATIVE_PTR -> "void*"
}
}
internal fun isMappedToString(descriptor: ClassDescriptor) =
descriptor.fqNameSafe.asString() == "kotlin.String"
internal fun isMappedToString(descriptor: ClassDescriptor): Boolean =
isMappedToString(descriptor.defaultType.computeBinaryType())
private fun isMappedToString(binaryType: BinaryType<ClassDescriptor>): Boolean =
when (binaryType) {
is BinaryType.Primitive -> false
is BinaryType.Reference -> binaryType.types.first() == context.builtIns.string
}
internal fun isMappedToReference(descriptor: ClassDescriptor) =
!descriptor.isUnit() && !isMappedToString(descriptor) &&
!primitiveTypeMapping.contains(descriptor.defaultType.correspondingValueType)
!isMappedToVoid(descriptor) && !isMappedToString(descriptor) &&
descriptor.defaultType.binaryTypeIsReference()
internal fun isMappedToVoid(descriptor: ClassDescriptor): Boolean {
return descriptor.isUnit()
@@ -917,17 +919,29 @@ internal class CAdapterGenerator(
}
}
private fun translateTypeFull(clazz: ClassDescriptor): Pair<String, String> {
val fqName = clazz.fqNameSafe.asString()
val valueType = clazz.defaultType.correspondingValueType
return when {
clazz.isUnit() -> "void" to "void"
fqName == "kotlin.String" -> "const char*" to "KObjHeader*"
valueType != null && primitiveTypeMapping.contains(valueType) -> primitiveTypeMapping[valueType]!! to primitiveTypeMapping[valueType]!!
else -> "${prefix}_kref_${translateTypeFqName(clazz.fqNameSafe.asString())}" to "KObjHeader*"
}
private fun translateTypeFull(clazz: ClassDescriptor): Pair<String, String> = if (isMappedToVoid(clazz)) {
"void" to "void"
} else {
translateNonVoidTypeFull(clazz.defaultType)
}
private fun translateNonVoidTypeFull(type: KotlinType): Pair<String, String> = type.unwrapToPrimitiveOrReference(
eachInlinedClass = { _, _ ->
// unsigned types to be handled.
},
ifPrimitive = { primitiveType, _ ->
primitiveTypeMapping[primitiveType]!!.let { it to it }
},
ifReference = {
val clazz = (it.computeBinaryType() as BinaryType.Reference).types.first()
if (clazz == context.builtIns.string) {
"const char*" to "KObjHeader*"
} else {
"${prefix}_kref_${translateTypeFqName(clazz.fqNameSafe.asString())}" to "KObjHeader*"
}
}
)
fun translateType(clazz: ClassDescriptor): String = translateTypeFull(clazz).first
fun translateTypeBridge(clazz: ClassDescriptor): String = translateTypeFull(clazz).second
@@ -59,6 +59,7 @@ import org.jetbrains.kotlin.serialization.deserialization.getName
import org.jetbrains.kotlin.types.KotlinType
import java.lang.System.out
import kotlin.LazyThreadSafetyMode.PUBLICATION
import kotlin.reflect.KProperty
internal class SpecialDeclarationsFactory(val context: Context) {
private val enumSpecialDeclarationsFactory by lazy { EnumSpecialDeclarationsFactory(context) }
@@ -140,14 +141,20 @@ internal class SpecialDeclarationsFactory(val context: Context) {
BridgeDirection.FROM_VALUE_TYPE -> context.irBuiltIns.anyNType
}
val extensionReceiverType = when (bridgeDirections.array[1]) {
val dispatchReceiver = when (bridgeDirections.array[1]) {
BridgeDirection.TO_VALUE_TYPE -> function.dispatchReceiverParameter!!
BridgeDirection.NOT_NEEDED -> function.dispatchReceiverParameter
BridgeDirection.FROM_VALUE_TYPE -> context.irBuiltIns.anyClass.owner.thisReceiver!!
}
val extensionReceiverType = when (bridgeDirections.array[2]) {
BridgeDirection.TO_VALUE_TYPE -> function.extensionReceiverParameter!!.type
BridgeDirection.NOT_NEEDED -> function.extensionReceiverParameter?.type
BridgeDirection.FROM_VALUE_TYPE -> context.irBuiltIns.anyNType
}
val valueParameterTypes = function.valueParameters.mapIndexed { index, valueParameter ->
when (bridgeDirections.array[index + 2]) {
when (bridgeDirections.array[index + 3]) {
BridgeDirection.TO_VALUE_TYPE -> valueParameter.type
BridgeDirection.NOT_NEEDED -> valueParameter.type
BridgeDirection.FROM_VALUE_TYPE -> context.irBuiltIns.anyNType
@@ -157,6 +164,7 @@ internal class SpecialDeclarationsFactory(val context: Context) {
function,
bridgeDirections,
returnType,
dispatchReceiver,
extensionReceiverType,
valueParameterTypes
)
@@ -171,7 +179,17 @@ internal class SpecialDeclarationsFactory(val context: Context) {
this.parent = function.parent
}
bridge.createDispatchReceiverParameter()
bridge.dispatchReceiverParameter = dispatchReceiver?.let {
IrValueParameterImpl(
it.startOffset,
it.endOffset,
IrDeclarationOrigin.DEFINED,
it.descriptor,
it.type,
null
)
}
extensionReceiverType?.let {
val extensionReceiverParameter = function.extensionReceiverParameter!!
bridge.extensionReceiverParameter = IrValueParameterImpl(
@@ -207,6 +225,7 @@ internal class SpecialDeclarationsFactory(val context: Context) {
function: IrFunction,
bridgeDirections: BridgeDirections,
returnType: IrType,
dispatchReceiverParameter: IrValueParameter?,
extensionReceiverType: IrType?,
valueParameterTypes: List<IrType>
): SimpleFunctionDescriptor {
@@ -235,7 +254,7 @@ internal class SpecialDeclarationsFactory(val context: Context) {
}
bridgeDescriptor.initialize(
/* receiverParameterType = */ extensionReceiverType?.toKotlinType(),
/* dispatchReceiverParameter = */ descriptor.dispatchReceiverParameter,
/* dispatchReceiverParameter = */ dispatchReceiverParameter?.descriptor as ReceiverParameterDescriptor?,
/* typeParameters = */ descriptor.typeParameters,
/* unsubstitutedValueParameters = */ valueParameters,
/* unsubstitutedReturnType = */ returnType.toKotlinType(),
@@ -262,6 +281,28 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
}
val specialDeclarationsFactory = SpecialDeclarationsFactory(this)
class LazyMember<T>(val initializer: Context.() -> T) {
operator fun getValue(thisRef: Context, property: KProperty<*>): T = thisRef.getValue(this)
}
companion object {
fun <T> lazyMember(initializer: Context.() -> T) = LazyMember<T>(initializer)
fun <K, V> lazyMapMember(initializer: Context.(K) -> V): LazyMember<(K) -> V> = lazyMember {
val storage = mutableMapOf<K, V>()
val result: (K) -> V = {
storage.getOrPut(it, { initializer(it) })
}
result
}
}
private val lazyValues = mutableMapOf<LazyMember<*>, Any?>()
fun <T> getValue(member: LazyMember<T>): T =
@Suppress("UNCHECKED_CAST") (lazyValues.getOrPut(member, { member.initializer(this) }) as T)
override val reflectionTypes: ReflectionTypes by lazy(PUBLICATION) {
ReflectionTypes(moduleDescriptor, FqName("konan.internal"))
}
@@ -0,0 +1,298 @@
/*
* Copyright 2010-2018 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 org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.konan.irasdescriptors.containsNull
import org.jetbrains.kotlin.backend.konan.irasdescriptors.fqNameSafe
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.types.typeUtil.makeNullable
fun IrType.getInlinedClass(): IrClass? = IrTypeInlineClassesSupport.getInlinedClass(this)
fun IrType.isInlined(): Boolean = IrTypeInlineClassesSupport.isInlined(this)
fun IrClass.isInlined(): Boolean = IrTypeInlineClassesSupport.isInlined(this)
fun KotlinType.getInlinedClass(): ClassDescriptor? = KotlinTypeInlineClassesSupport.getInlinedClass(this)
fun ClassDescriptor.isInlined(): Boolean = KotlinTypeInlineClassesSupport.isInlined(this)
fun KotlinType.unwrapInlinedClasses() = KotlinTypeInlineClassesSupport.unwrappingInlinedClasses(this).last()
internal inline fun <R> KotlinType.unwrapToPrimitiveOrReference(
eachInlinedClass: (inlinedClass: ClassDescriptor, nullable: Boolean) -> Unit,
ifPrimitive: (primitiveType: KonanPrimitiveType, nullable: Boolean) -> R,
ifReference: (type: KotlinType) -> R
): R = KotlinTypeInlineClassesSupport.unwrapToPrimitiveOrReference(this, eachInlinedClass, ifPrimitive, ifReference)
// TODO: consider renaming to `isReference`.
fun KotlinType.binaryTypeIsReference(): Boolean = this.computePrimitiveBinaryTypeOrNull() == null
fun IrType.binaryTypeIsReference(): Boolean = this.computePrimitiveBinaryTypeOrNull() == null
fun KotlinType.computePrimitiveBinaryTypeOrNull(): PrimitiveBinaryType? =
this.computeBinaryType().primitiveBinaryTypeOrNull()
fun KotlinType.computeBinaryType(): BinaryType<ClassDescriptor> = KotlinTypeInlineClassesSupport.computeBinaryType(this)
fun IrType.computePrimitiveBinaryTypeOrNull(): PrimitiveBinaryType? =
this.computeBinaryType().primitiveBinaryTypeOrNull()
fun IrType.computeBinaryType(): BinaryType<IrClass> = IrTypeInlineClassesSupport.computeBinaryType(this)
fun IrClass.inlinedClassIsNullable(): Boolean = this.defaultType.makeNullable().getInlinedClass() == this // TODO: optimize
fun IrClass.isUsedAsBoxClass(): Boolean = IrTypeInlineClassesSupport.isUsedAsBoxClass(this)
/**
* Most "underlying" user-visible non-reference type.
* It is visible as inlined to compiler for simplicity, and wraps internal [ValueClass].
*/
enum class KonanPrimitiveType(val classId: ClassId) {
BOOLEAN(PrimitiveType.BOOLEAN),
CHAR(PrimitiveType.CHAR),
BYTE(PrimitiveType.BYTE),
SHORT(PrimitiveType.SHORT),
INT(PrimitiveType.INT),
LONG(PrimitiveType.LONG),
FLOAT(PrimitiveType.FLOAT),
DOUBLE(PrimitiveType.DOUBLE),
NON_NULL_NATIVE_PTR(ClassId.topLevel(KonanBuiltIns.FqNames.nonNullNativePtr.toSafe()))
;
constructor(primitiveType: PrimitiveType) : this(ClassId.topLevel(primitiveType.typeFqName))
val fqName: FqNameUnsafe get() = this.classId.asSingleFqName().toUnsafe()
companion object {
val byFqName = KonanPrimitiveType.values().associateBy { it.fqName }
}
}
internal abstract class InlineClassesSupport<Class : Any, Type : Any> {
protected abstract fun isNullable(type: Type): Boolean
protected abstract fun makeNullable(type: Type): Type
protected abstract fun erase(type: Type): Class
protected abstract fun computeFullErasure(type: Type): Sequence<Class>
protected abstract fun getFqName(clazz: Class): FqNameUnsafe
protected abstract fun hasInlineModifier(clazz: Class): Boolean
protected abstract fun getNativePointedSuperclass(clazz: Class): Class?
protected abstract fun getInlinedClassUnderlyingType(clazz: Class): Type
@JvmName("classIsInlined")
fun isInlined(clazz: Class): Boolean = getInlinedClass(clazz) != null
fun isInlined(type: Type): Boolean = getInlinedClass(type) != null
fun isUsedAsBoxClass(clazz: Class) = getInlinedClass(clazz) == clazz // To handle NativePointed subclasses.
fun getInlinedClass(type: Type): Class? =
getInlinedClass(erase(type), isNullable(type))
private fun getInlinedClass(erased: Class, isNullable: Boolean): Class? {
val inlinedClass = getInlinedClass(erased) ?: return null
return if (!isNullable || representationIsNonNullReferenceOrPointer(inlinedClass)) {
inlinedClass
} else {
null
}
}
tailrec fun representationIsNonNullReferenceOrPointer(clazz: Class): Boolean {
val valueClass = ValueClass.fqNameToValueClass[getFqName(clazz)]
if (valueClass != null) {
return valueClass == ValueClass.NON_NULL_POINTER
}
val inlinedClass = getInlinedClass(clazz) ?: return true
val underlyingType = getInlinedClassUnderlyingType(inlinedClass)
return if (isNullable(underlyingType)) {
false
} else {
representationIsNonNullReferenceOrPointer(erase(underlyingType))
}
}
@JvmName("classGetInlinedClass")
private fun getInlinedClass(clazz: Class): Class? =
if (hasInlineModifier(clazz) || getFqName(clazz) in implicitInlineClasses) {
clazz
} else {
getNativePointedSuperclass(clazz)
}
fun unwrapInlinedClass(type: Type): Type? {
val inlinedClass = getInlinedClass(type) ?: return null
val underlyingType = getInlinedClassUnderlyingType(inlinedClass)
return if (isNullable(type)) makeNullable(underlyingType) else underlyingType
}
inline fun <R> unwrapToPrimitiveOrReference(
type: Type,
eachInlinedClass: (inlinedClass: Class, nullable: Boolean) -> Unit,
ifPrimitive: (primitiveType: KonanPrimitiveType, nullable: Boolean) -> R,
ifReference: (type: Type) -> R
): R {
var currentType: Type = type
while (true) {
val inlinedClass = getInlinedClass(currentType)
if (inlinedClass == null) {
return ifReference(currentType)
}
KonanPrimitiveType.byFqName[getFqName(inlinedClass)]?.let { primitiveType ->
return ifPrimitive(primitiveType, isNullable(type))
}
eachInlinedClass(inlinedClass, isNullable(type))
currentType = unwrapInlinedClass(currentType)!!
}
}
// TODO: optimize.
fun unwrappingInlinedClasses(type: Type): Sequence<Type> = generateSequence(type, { unwrapInlinedClass(it) })
// TODO: optimize.
fun computeBinaryType(type: Type): BinaryType<Class> {
val erased = erase(type)
val valueClass = ValueClass.fqNameToValueClass[getFqName(erased)]
if (valueClass != null) {
if (isNullable(type) && valueClass != ValueClass.NON_NULL_POINTER) {
error("${valueClass.fqName} can't be nullable")
}
return valueClass.binaryType
}
val inlinedClass = getInlinedClass(erased, isNullable(type)) ?: return createReferenceBinaryType(type)
val underlyingBinaryType = computeBinaryType(getInlinedClassUnderlyingType(inlinedClass))
return if (isNullable(type) && underlyingBinaryType is BinaryType.Reference) {
BinaryType.Reference(underlyingBinaryType.types, true)
} else {
underlyingBinaryType
}
}
private fun createReferenceBinaryType(type: Type): BinaryType.Reference<Class> =
BinaryType.Reference(computeFullErasure(type), true)
}
private val implicitInlineClasses =
(KonanPrimitiveType.values().map { it.fqName } +
KonanBuiltIns.FqNames.nativePtr +
InteropBuiltIns.FqNames.cPointer).toSet()
private enum class ValueClass(val fqName: FqNameUnsafe, val binaryType: BinaryType.Primitive) {
BOOLEAN("konan.internal.BooleanValue", PrimitiveBinaryType.BOOLEAN),
BYTE("konan.internal.ByteValue", PrimitiveBinaryType.BYTE),
SHORT("konan.internal.ShortValue", PrimitiveBinaryType.SHORT),
INT("konan.internal.IntValue", PrimitiveBinaryType.INT),
LONG("konan.internal.LongValue", PrimitiveBinaryType.LONG),
FLOAT("konan.internal.FloatValue", PrimitiveBinaryType.FLOAT),
DOUBLE("konan.internal.DoubleValue", PrimitiveBinaryType.DOUBLE),
NON_NULL_POINTER("konan.internal.NotNullPointerValue", PrimitiveBinaryType.POINTER)
;
constructor(fqName: String, primitiveBinaryType: PrimitiveBinaryType) :
this(FqNameUnsafe(fqName), primitiveBinaryType)
constructor(fqName: FqNameUnsafe, primitiveBinaryType: PrimitiveBinaryType) :
this(fqName, BinaryType.Primitive(primitiveBinaryType))
companion object {
val fqNameToValueClass = ValueClass.values().associateBy { it.fqName }
}
}
private fun IrClass.getAllSuperClassifiers(): List<IrClass> = listOf(this) + this.superTypes.flatMap { (it.classifierOrFail.owner as IrClass).getAllSuperClassifiers() }
internal object KotlinTypeInlineClassesSupport : InlineClassesSupport<ClassDescriptor, KotlinType>() {
override fun isNullable(type: KotlinType): Boolean = type.isNullable()
override fun makeNullable(type: KotlinType): KotlinType = type.makeNullable()
override tailrec fun erase(type: KotlinType): ClassDescriptor {
val descriptor = type.constructor.declarationDescriptor
return if (descriptor is ClassDescriptor) {
descriptor
} else {
erase(type.constructor.supertypes.first())
}
}
override fun computeFullErasure(type: KotlinType): Sequence<ClassDescriptor> {
val classifier = type.constructor.declarationDescriptor
return if (classifier is ClassDescriptor) sequenceOf(classifier)
else type.constructor.supertypes.asSequence().flatMap { computeFullErasure(it) }
}
override fun getFqName(clazz: ClassDescriptor): FqNameUnsafe = clazz.fqNameUnsafe
override fun hasInlineModifier(clazz: ClassDescriptor): Boolean = clazz.isInline
override fun getNativePointedSuperclass(clazz: ClassDescriptor): ClassDescriptor? = clazz.getAllSuperClassifiers()
.firstOrNull { it.fqNameUnsafe == InteropBuiltIns.FqNames.nativePointed } as ClassDescriptor?
override fun getInlinedClassUnderlyingType(clazz: ClassDescriptor): KotlinType =
clazz.unsubstitutedPrimaryConstructor!!.valueParameters.single().type
}
private object IrTypeInlineClassesSupport : InlineClassesSupport<IrClass, IrType>() {
override fun isNullable(type: IrType): Boolean = type.containsNull()
override fun makeNullable(type: IrType): IrType = type.makeNullable()
override tailrec fun erase(type: IrType): IrClass {
val classifier = type.classifierOrFail
return when (classifier) {
is IrClassSymbol -> classifier.owner
is IrTypeParameterSymbol -> erase(classifier.owner.superTypes.first())
else -> error(classifier)
}
}
override fun computeFullErasure(type: IrType): Sequence<IrClass> {
val classifier = type.classifierOrFail
return when (classifier) {
is IrClassSymbol -> sequenceOf(classifier.owner)
is IrTypeParameterSymbol -> classifier.owner.superTypes.asSequence().flatMap { computeFullErasure(it) }
else -> error(classifier)
}
}
override fun getFqName(clazz: IrClass): FqNameUnsafe = clazz.fqNameSafe.toUnsafe()
override fun hasInlineModifier(clazz: IrClass): Boolean = clazz.descriptor.isInline
override fun getNativePointedSuperclass(clazz: IrClass): IrClass? = clazz.getAllSuperClassifiers()
.firstOrNull { it.fqNameSafe.toUnsafe() == InteropBuiltIns.FqNames.nativePointed }
override fun getInlinedClassUnderlyingType(clazz: IrClass): IrType =
clazz.constructors.first { it.isPrimary }.valueParameters.single().type
}
@@ -73,9 +73,7 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
val getPointerSize = packageScope.getContributedFunctions("getPointerSize").single()
val nullableInteropValueTypes = listOf(ValueType.C_POINTER, ValueType.NATIVE_POINTED)
private val nativePointed = packageScope.getContributedClass(nativePointedName)
val nativePointed = packageScope.getContributedClass(nativePointedName)
val cPointer = this.packageScope.getContributedClass(cPointerName)
@@ -124,7 +124,7 @@ internal class KonanLower(val context: Context, val parentPhaser: PhaseManager)
}
phaser.phase(KonanPhase.LOWER_DEFAULT_PARAMETER_EXTENT) {
DefaultArgumentStubGenerator(context).runOnFilePostfix(irFile)
DefaultParameterInjector(context).runOnFilePostfix(irFile)
KonanDefaultParameterInjector(context).runOnFilePostfix(irFile)
}
phaser.phase(KonanPhase.LOWER_BUILTIN_OPERATORS) {
BuiltinOperatorLowering(context).lower(irFile)
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.ImportPath
import org.jetbrains.kotlin.resolve.MultiTargetPlatform
@@ -49,6 +50,7 @@ class KonanBuiltIns(storageManager: StorageManager) : KotlinBuiltIns(storageMana
val packageName = FqName("konan.internal")
val nativePtr = packageName.child(Name.identifier(nativePtrName)).toUnsafe()
val nonNullNativePtr = FqNameUnsafe("konan.internal.NonNullNativePtr")
val throws = FqName("konan.Throws")
}
@@ -1,171 +0,0 @@
/*
* 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 org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.types.KotlinType
/**
* Value type is a kind of Kotlin types represented as plain value in generated code, not as an object reference.
* Such types may require autoboxing.
*
* The value type nearly corresponds to `[classFqName]` or `[classFqName]?` Kotlin type (depending on [isNullable]).
*
* @property classFqName name of the base value type class
* @property isNullable whether `null` is included into this value type
*/
enum class ValueType(val classFqName: FqNameUnsafe, val isNullable: Boolean = false) {
BOOLEAN(KotlinBuiltIns.FQ_NAMES._boolean),
CHAR(KotlinBuiltIns.FQ_NAMES._char),
BYTE(KotlinBuiltIns.FQ_NAMES._byte),
SHORT(KotlinBuiltIns.FQ_NAMES._short),
INT(KotlinBuiltIns.FQ_NAMES._int),
LONG(KotlinBuiltIns.FQ_NAMES._long),
FLOAT(KotlinBuiltIns.FQ_NAMES._float),
DOUBLE(KotlinBuiltIns.FQ_NAMES._double),
NATIVE_PTR(KonanBuiltIns.FqNames.nativePtr),
NATIVE_POINTED(InteropBuiltIns.FqNames.nativePointed, isNullable = true),
C_POINTER(InteropBuiltIns.FqNames.cPointer, isNullable = true)
}
private fun KotlinType.isConstructedFromGivenClass(fqName: FqNameUnsafe) =
KotlinBuiltIns.isConstructedFromGivenClass(this, fqName)
/**
* @return `true` if this type must be represented as given value type in generated code.
*/
tailrec fun KotlinType.isRepresentedAs(valueType: ValueType): Boolean {
if (this.isMarkedNullable && !valueType.isNullable) {
return false
}
if (this.isConstructedFromGivenClass(valueType.classFqName)) {
return true
}
// Supertypes should be checked even for "final" value types (e.g. Int)
// to treat type parameter `T` with `Int` upper bound as value type.
// This behavior is observed on Kotlin JVM and used in interop implementation.
//
// However to optimize this method only first supertype is checked
// (it is supposed to be enough in all sane cases).
val firstSupertype = this.constructor.supertypes.firstOrNull() ?: return false
return firstSupertype.isRepresentedAs(valueType)
}
/**
* @return `true` if this type without `null` value must be represented as given value type in generated code.
*
* TODO: this method can be considered as a hack; rework its usages.
*/
tailrec fun KotlinType.notNullableIsRepresentedAs(valueType: ValueType): Boolean {
if (this.isConstructedFromGivenClass(valueType.classFqName)) {
return true
}
// See comment in [isRepresentedAs].
val firstSupertype = this.constructor.supertypes.firstOrNull() ?: return false
return firstSupertype.notNullableIsRepresentedAs(valueType)
}
fun KotlinType.isValueType() = ValueType.values().any { this.isRepresentedAs(it) }
/**
* @return the [ValueType] given type represented in generated code as,
* or `null` if represented as object reference.
*/
val KotlinType.correspondingValueType: ValueType?
get() = ValueType.values().firstOrNull {
isRepresentedAs(it)
}
//////
private fun IrType.isConstructedFromGivenClass(fqName: FqNameUnsafe) =
(this.classifierOrNull as? IrClassSymbol)?.descriptor?.fqNameSafe?.toUnsafe() == fqName
private val IrClassifierSymbol.ownerSuperTypes: List<IrType>
get() = when (this) {
is IrClassSymbol -> this.owner.superTypes
is IrTypeParameterSymbol -> this.owner.superTypes
else -> error(this)
}
/**
* @return `true` if this type must be represented as given value type in generated code.
*/
tailrec fun IrType.isRepresentedAs(valueType: ValueType): Boolean {
if (this !is IrSimpleType) {
return false
}
if (this.hasQuestionMark && !valueType.isNullable) {
return false
}
if (this.isConstructedFromGivenClass(valueType.classFqName)) {
return true
}
// Supertypes should be checked even for "final" value types (e.g. Int)
// to treat type parameter `T` with `Int` upper bound as value type.
// This behavior is observed on Kotlin JVM and used in interop implementation.
//
// However to optimize this method only first supertype is checked
// (it is supposed to be enough in all sane cases).
val firstSupertype = this.classifier.ownerSuperTypes.firstOrNull() ?: return false
return firstSupertype.isRepresentedAs(valueType)
}
/**
* @return `true` if this type without `null` value must be represented as given value type in generated code.
*
* TODO: this method can be considered as a hack; rework its usages.
*/
tailrec fun IrType.notNullableIsRepresentedAs(valueType: ValueType): Boolean {
if (this.isConstructedFromGivenClass(valueType.classFqName)) {
return true
}
// See comment in [isRepresentedAs].
val firstSupertype = this.classifierOrNull?.ownerSuperTypes?.firstOrNull() ?: return false
return firstSupertype.notNullableIsRepresentedAs(valueType)
}
fun IrType.isValueType() = ValueType.values().any { this.isRepresentedAs(it) }
/**
* @return the [ValueType] given type represented in generated code as,
* or `null` if represented as object reference.
*/
val IrType.correspondingValueType: ValueType?
get() = ValueType.values().firstOrNull {
isRepresentedAs(it)
}
@@ -16,8 +16,9 @@
package org.jetbrains.kotlin.backend.konan.descriptors
import org.jetbrains.kotlin.backend.konan.binaryTypeIsReference
import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
import org.jetbrains.kotlin.backend.konan.isValueType
import org.jetbrains.kotlin.backend.konan.isInlined
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
@@ -90,18 +91,13 @@ internal val FunctionDescriptor.isIntrinsic: Boolean
get() = this.descriptor.annotations.hasAnnotation(intrinsicAnnotation)
internal val org.jetbrains.kotlin.descriptors.DeclarationDescriptor.isFrozen: Boolean
get() = this.annotations.hasAnnotation(frozenAnnotation)
get() = this.annotations.hasAnnotation(frozenAnnotation) ||
// RTTI is used for non-reference type box:
this is org.jetbrains.kotlin.descriptors.ClassDescriptor && !this.defaultType.binaryTypeIsReference()
internal val DeclarationDescriptor.isFrozen: Boolean
get() = this.descriptor.isFrozen
private val intrinsicTypes = setOf(
"kotlin.Boolean", "kotlin.Char",
"kotlin.Byte", "kotlin.Short",
"kotlin.Int", "kotlin.Long",
"kotlin.Float", "kotlin.Double"
)
internal val arrayTypes = setOf(
"kotlin.Array",
"kotlin.ByteArray",
@@ -115,9 +111,6 @@ internal val arrayTypes = setOf(
"konan.ImmutableBinaryBlob"
)
internal val ClassDescriptor.isIntrinsic: Boolean
get() = this.fqNameSafe.asString() in intrinsicTypes
internal val ClassDescriptor.isArray: Boolean
get() = this.fqNameSafe.asString() in arrayTypes
@@ -131,17 +124,19 @@ fun ClassDescriptor.isAbstract() = this.modality == Modality.SEALED || this.moda
internal fun FunctionDescriptor.hasValueTypeAt(index: Int): Boolean {
when (index) {
0 -> return !isSuspend && returnType.let { (it.isValueType() || it.isUnit()) }
1 -> return extensionReceiverParameter.let { it != null && it.type.isValueType() }
else -> return this.valueParameters[index - 2].type.isValueType()
0 -> return !isSuspend && returnType.let { (it.isInlined() || it.isUnit()) }
1 -> return dispatchReceiverParameter.let { it != null && it.type.isInlined() }
2 -> return extensionReceiverParameter.let { it != null && it.type.isInlined() }
else -> return this.valueParameters[index - 3].type.isInlined()
}
}
internal fun FunctionDescriptor.hasReferenceAt(index: Int): Boolean {
when (index) {
0 -> return isSuspend || returnType.let { !it.isValueType() && !it.isUnit() }
1 -> return extensionReceiverParameter.let { it != null && !it.type.isValueType() }
else -> return !this.valueParameters[index - 2].type.isValueType()
0 -> return isSuspend || returnType.let { !it.isInlined() && !it.isUnit() }
1 -> return dispatchReceiverParameter.let { it != null && !it.type.isInlined() }
2 -> return extensionReceiverParameter.let { it != null && !it.type.isInlined() }
else -> return !this.valueParameters[index - 3].type.isInlined()
}
}
@@ -149,7 +144,7 @@ private fun FunctionDescriptor.needBridgeToAt(target: FunctionDescriptor, index:
= hasValueTypeAt(index) xor target.hasValueTypeAt(index)
internal fun FunctionDescriptor.needBridgeTo(target: FunctionDescriptor)
= (0..this.valueParameters.size + 1).any { needBridgeToAt(target, it) }
= (0..this.valueParameters.size + 2).any { needBridgeToAt(target, it) }
internal val SimpleFunctionDescriptor.target: SimpleFunctionDescriptor
get() = (if (modality == Modality.ABSTRACT) this else resolveFakeOverride()).original
@@ -174,7 +169,7 @@ private fun FunctionDescriptor.bridgeDirectionToAt(target: FunctionDescriptor, i
}
internal class BridgeDirections(val array: Array<BridgeDirection>) {
constructor(parametersCount: Int): this(Array<BridgeDirection>(parametersCount + 2, { BridgeDirection.NOT_NEEDED }))
constructor(parametersCount: Int): this(Array<BridgeDirection>(parametersCount + 3, { BridgeDirection.NOT_NEEDED }))
fun allNotNeeded(): Boolean = array.all { it == BridgeDirection.NOT_NEEDED }
@@ -17,12 +17,9 @@
package org.jetbrains.kotlin.backend.konan.ir
import org.jetbrains.kotlin.backend.common.COROUTINE_SUSPENDED_NAME
import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.backend.common.ir.Ir
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.KONAN_FUNCTION_INTERFACES_MAX_PARAMETERS
import org.jetbrains.kotlin.backend.konan.ValueType
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.konanInternal
import org.jetbrains.kotlin.backend.konan.irasdescriptors.typeWith
import org.jetbrains.kotlin.backend.konan.llvm.findMainEntryPoint
@@ -46,7 +43,6 @@ import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
@@ -182,28 +178,16 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
val getNativeNullPtr = symbolTable.referenceSimpleFunction(context.builtIns.getNativeNullPtr)
val boxFunctions = ValueType.values().associate {
val boxFunctionName = "box${it.classFqName.shortName()}"
it to symbolTable.referenceSimpleFunction(context.getInternalFunctions(boxFunctionName).single())
val boxCachePredicates = BoxCache.values().associate {
val name = "in${it.name.toLowerCase().capitalize()}BoxCache"
it to symbolTable.referenceSimpleFunction(context.getInternalFunctions(name).single())
}
val boxClasses = ValueType.values().associate {
it to symbolTable.referenceClass(context.getInternalClass("${it.classFqName.shortName()}Box"))
val boxCacheGetters = BoxCache.values().associate {
val name = "getCached${it.name.toLowerCase().capitalize()}Box"
it to symbolTable.referenceSimpleFunction(context.getInternalFunctions(name).single())
}
val valueClassToBox = ValueType.values().associate {
val valueClassId = ClassId.topLevel(it.classFqName.toSafe())
val valueClassDescriptor = context.builtIns.builtInsModule.findClassAcrossModuleDependencies(valueClassId)!!
symbolTable.referenceClass(valueClassDescriptor) to boxClasses[it]!!
}
val unboxFunctions = ValueType.values().mapNotNull {
val unboxFunctionName = "unbox${it.classFqName.shortName()}"
context.getInternalFunctions(unboxFunctionName).atMostOne()?.let { descriptor ->
it to symbolTable.referenceSimpleFunction(descriptor)
}
}.toMap()
val immutableBinaryBlob = symbolTable.referenceClass(
builtInsPackage("konan").getContributedClassifier(
Name.identifier("ImmutableBinaryBlob"), NoLookupLocation.FROM_BACKEND
@@ -216,7 +200,9 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
val areEqualByValue = context.getInternalFunctions("areEqualByValue").map {
symbolTable.referenceSimpleFunction(it)
}
}.associateBy { it.descriptor.valueParameters[0].type.computePrimitiveBinaryTypeOrNull()!! }
val reinterpret = symbolTable.referenceSimpleFunction(context.getInternalFunctions("reinterpret").single())
val ieee754Equals = context.getInternalFunctions("ieee754Equals").map {
symbolTable.referenceSimpleFunction(it)
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.backend.konan.llvm
import llvm.LLVMTypeRef
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
import org.jetbrains.kotlin.backend.konan.isValueType
import org.jetbrains.kotlin.backend.konan.isInlined
import org.jetbrains.kotlin.backend.konan.library.KonanLibraryReader
import org.jetbrains.kotlin.backend.konan.optimizations.DataFlowIR
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -177,7 +177,7 @@ private val FunctionDescriptor.signature: String
val signatureSuffix =
when {
this.typeParameters.isNotEmpty() -> "Generic"
returnType.isValueType() -> "ValueType"
returnType.isInlined() -> "ValueType"
!returnType.isUnitOrNullableUnit() -> typeToHashString(returnType)
else -> ""
}
@@ -17,41 +17,33 @@
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.*
import org.jetbrains.kotlin.backend.konan.ValueType
import org.jetbrains.kotlin.backend.konan.isRepresentedAs
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.optimizations.DataFlowIR
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isUnit
private val valueTypes = ValueType.values().associate {
private val primitiveToLlvm = PrimitiveBinaryType.values().associate {
it to when (it) {
ValueType.BOOLEAN -> LLVMInt1Type()
ValueType.BYTE -> LLVMInt8Type()
ValueType.SHORT, ValueType.CHAR -> LLVMInt16Type()
ValueType.INT -> LLVMInt32Type()
ValueType.LONG -> LLVMInt64Type()
ValueType.FLOAT -> LLVMFloatType()
ValueType.DOUBLE -> LLVMDoubleType()
PrimitiveBinaryType.BOOLEAN -> LLVMInt1Type()
PrimitiveBinaryType.BYTE -> LLVMInt8Type()
PrimitiveBinaryType.SHORT -> LLVMInt16Type()
PrimitiveBinaryType.INT -> LLVMInt32Type()
PrimitiveBinaryType.LONG -> LLVMInt64Type()
PrimitiveBinaryType.FLOAT -> LLVMFloatType()
PrimitiveBinaryType.DOUBLE -> LLVMDoubleType()
ValueType.NATIVE_PTR, ValueType.NATIVE_POINTED, ValueType.C_POINTER -> int8TypePtr
PrimitiveBinaryType.POINTER -> int8TypePtr
}!!
}
internal val ValueType.llvmType
get() = valueTypes[this]!!
private fun RuntimeAware.getLlvmType(primitiveBinaryType: PrimitiveBinaryType?) =
primitiveBinaryType?.let { primitiveToLlvm[it]!! } ?: this.kObjHeaderPtr
internal fun RuntimeAware.getLLVMType(type: IrType): LLVMTypeRef {
for ((valueType, llvmType) in valueTypes) {
if (type.isRepresentedAs(valueType)) {
return llvmType
}
}
return this.kObjHeaderPtr
}
internal fun RuntimeAware.getLLVMType(type: IrType): LLVMTypeRef =
getLlvmType(type.computePrimitiveBinaryTypeOrNull())
internal fun RuntimeAware.getLLVMType(type: DataFlowIR.Type) =
type.correspondingValueType?.let { valueTypes[it]!! } ?: this.kObjHeaderPtr
getLlvmType(type.primitiveBinaryType)
internal fun RuntimeAware.getLLVMReturnType(type: IrType): LLVMTypeRef {
return when {
@@ -43,7 +43,6 @@ import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
private val threadLocalAnnotationFqName = FqName("konan.ThreadLocal")
@@ -160,10 +159,6 @@ internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid {
super.visitClass(declaration)
val descriptor = declaration
if (descriptor.isIntrinsic) {
// do not generate any code for intrinsic classes as they require special handling
return
}
generator.generate(descriptor)
@@ -407,7 +402,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
appendingTo(bbLocalDeinit) {
context.llvm.fileInitializers.forEach {
val descriptor = it
if (descriptor.type.isValueType())
if (!descriptor.type.binaryTypeIsReference())
return@forEach // Is not a subject for memory management.
val address = context.llvmDeclarations.forStaticField(descriptor).storage
storeAny(codegen.kNullObjHeaderPtr, address)
@@ -508,7 +503,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
override fun visitConstructor(declaration: IrConstructor) {
context.log{"visitConstructor : ${ir2string(declaration)}"}
if (declaration.descriptor.containingDeclaration.defaultType.isValueType()) {
if (declaration.constructedClass.isInlined()) {
// Do not generate any ctors for value types.
return
}
@@ -726,10 +721,6 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
//-------------------------------------------------------------------------//
override fun visitProperty(declaration: IrProperty) {
val container = declaration.descriptor.containingDeclaration
// For value types with real backing field there's no point to generate an accessor.
if (container is ClassDescriptor && container.defaultType.isValueType() && declaration.backingField != null)
return
declaration.getter?.acceptVoid(this)
declaration.setter?.acceptVoid(this)
declaration.backingField?.acceptVoid(this)
@@ -1253,10 +1244,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
context.log{"evaluateCast : ${ir2string(value)}"}
val dstDescriptor = value.typeOperand.getClass()!!
assert(!dstDescriptor.defaultType.isValueType() &&
!value.argument.type.isValueType())
val srcArg = evaluateExpression(value.argument) // Evaluate src expression.
assert(srcArg.type == codegen.kObjHeaderPtr)
if (dstDescriptor.defaultType.isObjCObjectType()) {
with(functionGenerationContext) {
@@ -1412,6 +1401,9 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
val valueToAssign = evaluateExpression(value.value)
if (value.descriptor.dispatchReceiverParameter != null) {
val thisPtr = evaluateExpression(value.receiver!!)
assert(thisPtr.type == codegen.kObjHeaderPtr) {
LLVMPrintTypeToString(thisPtr.type)?.toKString().toString()
}
if (needMutationCheck(value.descriptor.containingDeclaration)) {
functionGenerationContext.call(context.llvm.mutationCheck,
listOf(functionGenerationContext.bitcast(codegen.kObjHeaderPtr, thisPtr)),
@@ -2107,7 +2099,20 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
assert (arg0.type == arg1.type,
{ "Types are different: '${llvmtype2string(arg0.type)}' and '${llvmtype2string(arg1.type)}'" })
return functionGenerationContext.icmpEq(arg0, arg1)
val typeKind = LLVMGetTypeKind(arg0.type)
with (functionGenerationContext) {
return when (typeKind) {
LLVMTypeKind.LLVMFloatTypeKind, LLVMTypeKind.LLVMDoubleTypeKind -> {
val numBits = LLVMSizeOfTypeInBits(codegen.llvmTargetData, arg0.type).toInt()
val integerType = LLVMIntType(numBits)!!
icmpEq(bitcast(integerType, arg0), bitcast(integerType, arg1))
}
LLVMTypeKind.LLVMIntegerTypeKind, LLVMTypeKind.LLVMPointerTypeKind -> icmpEq(arg0, arg1)
else -> error(typeKind)
}
}
}
"konan.internal.ieee754Equals" -> {
val arg0 = args[0]
@@ -2127,7 +2132,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
return when (descriptor) {
interop.interpretNullablePointed, interop.interpretCPointer,
interop.nativePointedGetRawPointer, interop.cPointerGetRawValue -> args.single()
interop.nativePointedGetRawPointer, interop.cPointerGetRawValue, // TODO: implement through `reinterpret`
context.ir.symbols.reinterpret.descriptor -> args.single()
in interop.readPrimitive -> {
val pointerType = pointerType(codegen.getLLVMType(function.returnType))
@@ -2184,10 +2190,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
functionGenerationContext.unreachable()
kNullInt8Ptr
} else {
val irClass = context.ir.symbols.valueClassToBox[typeArgumentClass.symbol]?.owner
?: typeArgumentClass
val typeInfo = codegen.typeInfoValue(irClass)
val typeInfo = codegen.typeInfoValue(typeArgumentClass)
LLVMConstBitCast(typeInfo, kInt8Ptr)!!
}
}
@@ -209,12 +209,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
}
override fun visitClass(declaration: IrClass) {
if (declaration.isIntrinsic) {
// do not generate any declarations for intrinsic classes as they require special handling
} else {
this.classes[declaration] = createClassDeclarations(declaration)
}
this.classes[declaration] = createClassDeclarations(declaration)
super.visitClass(declaration)
}
@@ -378,7 +373,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
val containingClass = descriptor.containingClass
if (containingClass != null) {
val classDeclarations = this.classes[containingClass] ?: error(containingClass.toString())
val classDeclarations = this.classes[containingClass] ?: error(containingClass.descriptor.toString())
val allFields = classDeclarations.fields
@@ -23,7 +23,6 @@ import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.constants.StringValue
internal class RTTIGenerator(override val context: Context) : ContextUtils {
@@ -371,19 +370,14 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
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.owner == descriptor }
?.key?.owner ?: descriptor
return if (descriptorForReflection.isAnonymousObject) {
return if (descriptor.isAnonymousObject) {
ReflectionInfo(packageName = null, relativeName = null)
} else if (descriptorForReflection.isLocal) {
ReflectionInfo(packageName = null, relativeName = descriptorForReflection.name.asString())
} else if (descriptor.isLocal) {
ReflectionInfo(packageName = null, relativeName = descriptor.name.asString())
} else {
ReflectionInfo(
packageName = descriptorForReflection.findPackage().fqName.asString(),
relativeName = generateSequence(descriptorForReflection, { it.parent as? ClassDescriptor })
packageName = descriptor.findPackage().fqName.asString(),
relativeName = generateSequence(descriptor, { it.parent as? ClassDescriptor })
.toList().reversed()
.joinToString(".") { it.name.asString() }
)
@@ -340,14 +340,24 @@ private val ObjCExportCodeGenerator.kotlinToObjCFunctionType: LLVMTypeRef
get() = functionType(int8TypePtr, false, codegen.kObjHeaderPtr)
private fun ObjCExportCodeGenerator.emitBoxConverter(objCValueType: ObjCValueType) {
val valueType = objCValueType.kotlinValueType
val symbols = context.ir.symbols
val irBuiltIns = context.irBuiltIns
val name = "${valueType.classFqName.shortName()}To${objCValueType.nsNumberName}"
val boxClass = when (objCValueType) {
ObjCValueType.BOOL -> irBuiltIns.booleanClass
ObjCValueType.UNSIGNED_SHORT -> irBuiltIns.charClass
ObjCValueType.CHAR -> irBuiltIns.byteClass
ObjCValueType.SHORT -> irBuiltIns.shortClass
ObjCValueType.INT -> irBuiltIns.intClass
ObjCValueType.LONG_LONG -> irBuiltIns.longClass
ObjCValueType.FLOAT -> irBuiltIns.floatClass
ObjCValueType.DOUBLE -> irBuiltIns.doubleClass
}.owner
val name = "${boxClass.name}To${objCValueType.nsNumberName}"
val converter = generateFunction(codegen, kotlinToObjCFunctionType, name) {
val unboxFunction = symbols.getUnboxFunction(valueType).owner.llvmFunction
val unboxFunction = context.getUnboxFunction(boxClass).llvmFunction
val kotlinValue = callFromBridge(
unboxFunction,
listOf(param(0)),
@@ -361,8 +371,6 @@ private fun ObjCExportCodeGenerator.emitBoxConverter(objCValueType: ObjCValueTyp
}
LLVMSetLinkage(converter, LLVMLinkage.LLVMPrivateLinkage)
val boxClass = symbols.boxClasses[valueType]!!
setObjCExportTypeInfo(boxClass.descriptor, constPointer(converter))
}
@@ -18,25 +18,32 @@ package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.AbstractValueUsageTransformer
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.target
import org.jetbrains.kotlin.backend.konan.irasdescriptors.constructedClass
import org.jetbrains.kotlin.backend.konan.irasdescriptors.containsNull
import org.jetbrains.kotlin.backend.konan.irasdescriptors.isOverridable
import org.jetbrains.kotlin.backend.konan.irasdescriptors.isSuspend
import org.jetbrains.kotlin.backend.konan.irasdescriptors.makeNullableAsSpecified
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addIfNotNull
/**
* Boxes and unboxes values of value types when necessary.
@@ -47,6 +54,7 @@ internal class Autoboxing(val context: Context) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(transformer)
irFile.transformChildrenVoid(InlineClassTransformer(context))
}
}
@@ -60,68 +68,18 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
// TODO: should we handle the cases when expression type
// is not equal to e.g. called function return type?
/**
* @return type to use for runtime type checks instead of given one (e.g. `IntBox` instead of `Int`)
*/
private fun getRuntimeReferenceType(type: IrType): IrType {
ValueType.values().forEach {
if (type.notNullableIsRepresentedAs(it)) {
return getBoxType(it).makeNullableAsSpecified(type.containsNull())
}
}
return type
}
override fun IrExpression.useInTypeOperator(operator: IrTypeOperator, typeOperand: IrType): IrExpression {
return if (operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT ||
operator == IrTypeOperator.IMPLICIT_INTEGER_COERCION) {
this
} else if (operator == IrTypeOperator.IMPLICIT_CAST) {
this.useAs(typeOperand)
} else {
// Codegen expects the argument of type-checking operator to be an object reference:
this.useAs(context.irBuiltIns.anyNType)
}
}
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
super.visitTypeOperator(expression).let {
// Assume that the transformer doesn't replace the entire expression for simplicity:
assert (it === expression)
}
val newTypeOperand = getRuntimeReferenceType(expression.typeOperand)
val newTypeOperandClassifier = newTypeOperand.classifierOrFail
return when (expression.operator) {
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT,
IrTypeOperator.IMPLICIT_INTEGER_COERCION -> expression
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST,
IrTypeOperator.IMPLICIT_NOTNULL, IrTypeOperator.SAFE_CAST -> {
val newExpressionType = if (expression.operator == IrTypeOperator.SAFE_CAST) {
newTypeOperand.makeNullable()
} else {
newTypeOperand
}
IrTypeOperatorCallImpl(expression.startOffset, expression.endOffset,
newExpressionType, expression.operator, newTypeOperand, newTypeOperandClassifier,
expression.argument).useAs(expression.type)
}
IrTypeOperator.INSTANCEOF, IrTypeOperator.NOT_INSTANCEOF -> if (newTypeOperand == expression.typeOperand) {
// Do not create new expression if nothing changes:
expression
} else {
IrTypeOperatorCallImpl(expression.startOffset, expression.endOffset,
expression.type, expression.operator, newTypeOperand, newTypeOperandClassifier,
expression.argument)
}
}
}
private var currentFunctionDescriptor: IrFunction? = null
override fun visitFunction(declaration: IrFunction): IrStatement {
@@ -143,8 +101,8 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
}
override fun IrExpression.useAs(type: IrType): IrExpression {
val interop = context.interopBuiltIns
if (this.isNullConst() && interop.nullableInteropValueTypes.any { type.isRepresentedAs(it) }) {
if (this.isNullConst() && type.isNullablePointer()) {
// TODO: consider using IrConst with proper type.
return IrCallImpl(
startOffset,
endOffset,
@@ -156,6 +114,7 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
val actualType = when (this) {
is IrCall -> {
if (this.symbol.owner.isSuspend) irBuiltIns.anyNType
else if (this.symbol == symbols.reinterpret) this.getTypeArgument(1)!!
else this.callTarget.returnType
}
is IrGetField -> this.symbol.owner.type
@@ -165,6 +124,8 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
// TODO: is it a workaround for inconsistent IR?
this.typeOperand
IrTypeOperator.CAST -> context.irBuiltIns.anyNType
else -> this.type
}
@@ -174,6 +135,9 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
return this.adaptIfNecessary(actualType, type)
}
private fun IrType.isNullablePointer(): Boolean =
this.containsNull() && this.computePrimitiveBinaryTypeOrNull() == PrimitiveBinaryType.POINTER
private val IrFunctionAccessExpression.target: IrFunction get() = when (this) {
is IrCall -> this.callTarget
is IrDelegatingConstructorCall -> this.symbol.owner
@@ -231,6 +195,289 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
return this
}
private fun getBoxType(valueType: ValueType) = symbols.boxClasses[valueType]!!.owner.defaultType
override fun visitCall(expression: IrCall): IrExpression {
return when (expression.symbol) {
symbols.reinterpret -> {
expression.transformChildrenVoid()
// TODO: check types has the same binary representation.
val oldType = expression.getTypeArgument(0)!!
val newType = expression.getTypeArgument(1)!!
assert(oldType.computePrimitiveBinaryTypeOrNull() == newType.computePrimitiveBinaryTypeOrNull())
expression.extensionReceiver = expression.extensionReceiver!!.useAs(oldType)
expression
}
else -> super.visitCall(expression)
}
}
}
private class InlineClassTransformer(private val context: Context) : IrBuildingTransformer(context) {
private val symbols = context.ir.symbols
private val irBuiltIns = context.irBuiltIns
override fun visitClass(declaration: IrClass): IrStatement {
super.visitClass(declaration)
if (declaration.isInlined()) {
if (declaration.isUsedAsBoxClass()) {
buildBoxFunction(declaration, context.getBoxFunction(declaration))
buildUnboxFunction(declaration, context.getUnboxFunction(declaration))
}
declaration.constructors.filter { !it.isPrimary }.toList().mapTo(declaration.declarations) {
context.getLoweredInlineClassConstructor(it)
}
}
return declaration
}
private fun IrField.isInlinedClassField(): Boolean {
val parentClass = this.parent as? IrClass
return parentClass != null && parentClass.isInlined()
}
override fun visitGetField(expression: IrGetField): IrExpression {
super.visitGetField(expression)
return if (expression.symbol.owner.isInlinedClassField()) {
expression.receiver!!
} else {
expression
}
}
override fun visitSetField(expression: IrSetField): IrExpression {
super.visitSetField(expression)
return if (expression.symbol.owner.isInlinedClassField()) {
// TODO: it is better to get rid of functions setting such fields.
val startOffset = expression.startOffset
val endOffset = expression.endOffset
IrBlockImpl(startOffset, endOffset, irBuiltIns.unitType).apply {
statements.addIfNotNull(expression.receiver)
statements += expression.value
statements += IrCallImpl(startOffset, endOffset, irBuiltIns.nothingType, symbols.ThrowNullPointerException)
statements += IrGetObjectValueImpl(startOffset, endOffset, irBuiltIns.unitType, irBuiltIns.unitClass)
}
} else {
expression
}
}
override fun visitCall(expression: IrCall): IrExpression {
super.visitCall(expression)
val function = expression.symbol.owner
return if (function is IrConstructor && function.constructedClass.isInlined()) {
builder.lowerConstructorCallToValue(expression, function)
} else {
expression
}
}
override fun visitConstructor(declaration: IrConstructor): IrStatement {
val classIsInlined = declaration.constructedClass.isInlined()
if (classIsInlined && !declaration.isPrimary) {
buildLoweredSecondaryConstructor(declaration)
}
if (classIsInlined || !declaration.returnType.binaryTypeIsReference()) {
// TODO: fix DFG building and nullify the body instead.
(declaration.body as IrBlockBody).statements.clear()
}
return super.visitConstructor(declaration)
}
private fun IrBuilderWithScope.irIsNull(expression: IrExpression): IrExpression {
val binary = expression.type.computeBinaryType()
return when (binary) {
is BinaryType.Primitive -> {
assert(binary.type == PrimitiveBinaryType.POINTER)
irCall(symbols.areEqualByValue[binary.type]!!.owner).apply {
putValueArgument(0, expression)
putValueArgument(1, irNullPointer())
}
}
is BinaryType.Reference -> irCall(context.irBuiltIns.eqeqeqFun).apply {
putValueArgument(0, expression)
putValueArgument(1, irNull())
}
}
}
private fun buildBoxFunction(irClass: IrClass, function: IrFunction) {
val builder = context.createIrBuilder(function.symbol)
val cache = BoxCache.values().toList().atMostOne { context.irBuiltIns.getKotlinClass(it) == irClass }
function.body = builder.irBlockBody(function) {
val valueToBox = function.valueParameters[0]
if (valueToBox.type.containsNull()) {
+irIfThen(
condition = irIsNull(irGet(valueToBox)),
thenPart = irReturn(irNull())
)
}
if (cache != null) {
+irIfThen(
condition = irCall(symbols.boxCachePredicates[cache]!!.owner).apply {
putValueArgument(0, irGet(valueToBox))
},
thenPart = irReturn(irCall(symbols.boxCacheGetters[cache]!!.owner).apply {
putValueArgument(0, irGet(valueToBox))
})
)
}
// Note: IR variable created below has reference type intentionally.
val box = irTemporary(irCall(symbols.createUninitializedInstance.owner).also {
it.putTypeArgument(0, irClass.defaultType)
})
+irSetField(irGet(box), getInlineClassBackingField(irClass), irGet(valueToBox))
+irReturn(irGet(box))
}
irClass.declarations += function
}
private fun IrBuilderWithScope.irNullPointerOrReference(type: IrType): IrExpression =
if (type.binaryTypeIsReference()) {
irNull()
} else {
irNullPointer()
}
private fun IrBuilderWithScope.irNullPointer(): IrExpression = irCall(symbols.getNativeNullPtr.owner)
private fun buildUnboxFunction(irClass: IrClass, function: IrFunction) {
val builder = context.createIrBuilder(function.symbol)
function.body = builder.irBlockBody(function) {
val boxParameter = function.valueParameters.single()
if (boxParameter.type.containsNull()) {
+irIfThen(
condition = irEqeqeq(irGet(boxParameter), irNull()),
thenPart = irReturn(irNullPointerOrReference(function.returnType))
)
}
+irReturn(irGetField(irGet(boxParameter), getInlineClassBackingField(irClass)))
}
irClass.declarations += function
}
private fun IrBuilderWithScope.lowerConstructorCallToValue(
expression: IrMemberAccessExpression,
callee: IrConstructor
): IrExpression = if (callee.isPrimary) {
expression.getValueArgument(0)!!
} else {
this.at(expression).irCall(this@InlineClassTransformer.context.getLoweredInlineClassConstructor(callee)).apply {
(0 until expression.valueArgumentsCount).forEach {
putValueArgument(it, expression.getValueArgument(it)!!)
}
}
}
private fun buildLoweredSecondaryConstructor(irConstructor: IrConstructor) {
val result = context.getLoweredInlineClassConstructor(irConstructor)
val irClass = irConstructor.parentAsClass
result.body = context.createIrBuilder(result.symbol).irBlockBody(result) {
lateinit var thisVar: IrVariable
val parameterMapping = result.valueParameters.associateBy {
irConstructor.valueParameters[it.index].symbol
}
(irConstructor.body as IrBlockBody).statements.forEach { statement ->
+statement.transform(object : IrElementTransformerVoid() {
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
expression.transformChildrenVoid()
val value = lowerConstructorCallToValue(expression, expression.symbol.owner)
return irBlock(expression) {
thisVar = irTemporary(value)
}
}
override fun visitGetValue(expression: IrGetValue): IrExpression {
expression.transformChildrenVoid()
if (expression.symbol == irClass.thisReceiver?.symbol) {
return irGet(thisVar)
}
parameterMapping[expression.symbol]?.let { return irGet(it) }
return expression
}
override fun visitReturn(expression: IrReturn): IrExpression {
expression.transformChildrenVoid()
if (expression.returnTargetSymbol == irConstructor.symbol) {
return irReturn(irBlock(expression.startOffset, expression.endOffset) {
+expression.value
+irGet(thisVar)
})
}
return expression
}
}, null)
}
+irReturn(irGet(thisVar))
}
}
private fun getInlineClassBackingField(irClass: IrClass): IrField =
irClass.declarations.filterIsInstance<IrProperty>().mapNotNull { it.backingField }.single()
}
private val Context.getLoweredInlineClassConstructor: (IrConstructor) -> IrSimpleFunction by Context.lazyMapMember { irConstructor ->
require(irConstructor.constructedClass.isInlined())
require(!irConstructor.isPrimary)
val descriptor = SimpleFunctionDescriptorImpl.create(
irConstructor.descriptor.containingDeclaration,
irConstructor.descriptor.annotations,
Name.special("<constructor>"),
CallableMemberDescriptor.Kind.SYNTHESIZED,
irConstructor.descriptor.source
)
val parameterDescriptors = irConstructor.descriptor.valueParameters.map {
it.copy(descriptor, it.name, it.index)
}
descriptor.initialize(
null,
null,
emptyList(),
parameterDescriptors,
irConstructor.descriptor.returnType,
Modality.FINAL,
irConstructor.visibility
)
IrFunctionImpl(
irConstructor.startOffset,
irConstructor.endOffset,
IrDeclarationOrigin.DEFINED,
descriptor
).apply {
parent = irConstructor.parent
returnType = irConstructor.returnType
irConstructor.valueParameters.mapTo(this.valueParameters) {
it.copy(this.descriptor.valueParameters[it.index])
}
}
}
@@ -21,10 +21,9 @@ import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer
import org.jetbrains.kotlin.backend.common.lower.at
import org.jetbrains.kotlin.backend.common.lower.irNot
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.irasdescriptors.containsNull
import org.jetbrains.kotlin.backend.konan.irasdescriptors.isSubtypeOf
import org.jetbrains.kotlin.backend.konan.isValueType
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrFile
@@ -40,6 +39,8 @@ import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isNothing
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.defaultOrNullableType
import org.jetbrains.kotlin.ir.util.isNullConst
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
@@ -98,7 +99,7 @@ internal class BuiltinOperatorLowering(val context: Context) : FileLoweringPass,
val lhs = expression.getValueArgument(0)!!
val rhs = expression.getValueArgument(1)!!
return if (lhs.type.isValueType() && rhs.type.isValueType()) {
return if (lhs.type.isInlined() && rhs.type.isInlined()) {
// Achieve the same behavior as with JVM BE: if both sides of `===` are values, then compare by value:
lowerEqeq(expression)
// Note: such comparisons are deprecated.
@@ -107,9 +108,13 @@ internal class BuiltinOperatorLowering(val context: Context) : FileLoweringPass,
}
}
private fun IrBuilderWithScope.irLogicalAnd(lhs: IrExpression, rhs: IrExpression) = context.andand(lhs, rhs)
private fun IrBuilderWithScope.irIsNull(exp: IrExpression) = irEqeqeq(exp, irNull())
private fun IrBuilderWithScope.irIsNotNull(exp: IrExpression) = irNot(irEqeqeq(exp, irNull()))
private fun IrBuilderWithScope.reinterpret(expression: IrExpression, toType: IrType) =
reinterpret(expression, expression.type, toType)
private fun IrBuilderWithScope.reinterpret(expression: IrExpression, fromType: IrType, toType: IrType) =
irCall(symbols.reinterpret.owner, listOf(fromType, toType)).apply {
extensionReceiver = expression
}
private fun lowerEqeq(expression: IrCall): IrExpression {
// TODO: optimize boxing?
@@ -118,79 +123,127 @@ internal class BuiltinOperatorLowering(val context: Context) : FileLoweringPass,
val lhs = expression.getValueArgument(0)!!
val rhs = expression.getValueArgument(1)!!
val nullableNothingType = builtIns.nullableNothingType
if (lhs.type.isSubtypeOf(nullableNothingType) && rhs.type.isSubtypeOf(nullableNothingType)) {
// Compare by reference if each part is either `Nothing` or `Nothing?`:
return irEqeqeq(lhs, rhs)
if (rhs.isNullConst()) {
return irEqeqNull(lhs)
}
// Find a type-compatible `konan.internal.areEqualByValue` intrinsic:
selectIntrinsic(symbols.areEqualByValue, lhs.type, rhs.type, false)?.let {
return irCall(it).apply {
putValueArgument(0, lhs)
putValueArgument(1, rhs)
}
if (lhs.isNullConst()) {
return irEqeqNull(rhs)
}
if (lhs.isNullConst() || rhs.isNullConst()) {
// or compare by reference if left or right part is `null`:
return irEqeqeq(lhs, rhs)
}
// TODO: areEqualByValue and ieee754Equals intrinsics are specially treated by code generator
// and thus can be declared synthetically in the compiler instead of explicitly in the runtime.
fun callEquals(lhs: IrExpression, rhs: IrExpression) =
if (expression.descriptor in ieee754EqualsDescriptors())
// Find a type-compatible `konan.internal.ieee754Equals` intrinsic:
irCall(selectIntrinsic(symbols.ieee754Equals, lhs.type, rhs.type, true)!!).apply {
putValueArgument(0, lhs)
putValueArgument(1, rhs)
}
else
irCall(symbols.equals).apply {
dispatchReceiver = lhs
putValueArgument(0, rhs)
}
val lhsIsNotNullable = !lhs.type.containsNull()
val rhsIsNotNullable = !rhs.type.containsNull()
return if (expression.descriptor in ieee754EqualsDescriptors()) {
if (lhsIsNotNullable && rhsIsNotNullable)
callEquals(lhs, rhs)
else irBlock {
val lhsTemp = irTemporary(lhs)
val rhsTemp = irTemporary(rhs)
if (lhsIsNotNullable xor rhsIsNotNullable) { // Exactly one nullable.
+irLogicalAnd(
irIsNotNull(irGet(if (lhsIsNotNullable) rhsTemp else lhsTemp)),
callEquals(irGet(lhsTemp), irGet(rhsTemp))
)
} else { // Both are nullable.
+irIfThenElse(context.irBuiltIns.booleanType, irIsNull(irGet(lhsTemp)),
irIsNull(irGet(rhsTemp)),
irLogicalAnd(
irIsNotNull(irGet(rhsTemp)),
callEquals(irGet(lhsTemp), irGet(rhsTemp))
)
)
if (expression.symbol == irBuiltins.eqeqSymbol) {
lhs.type.getInlinedClass()?.let {
if (it == rhs.type.getInlinedClass()) {
return genInlineClassEquals(expression.descriptor, rhs, lhs)
}
}
} else {
if (lhsIsNotNullable)
callEquals(lhs, rhs)
else {
irBlock {
val lhsTemp = irTemporary(lhs)
if (rhsIsNotNullable)
+irLogicalAnd(irIsNotNull(irGet(lhsTemp)), callEquals(irGet(lhsTemp), rhs))
else {
val rhsTemp = irTemporary(rhs)
+irIfThenElse(irBuiltins.booleanType, irIsNull(irGet(lhsTemp)),
irIsNull(irGet(rhsTemp)),
}
return genFloatingOrReferenceEquals(expression.descriptor, lhs, rhs)
}
}
fun IrBuilderWithScope.genInlineClassEquals(
descriptor: FunctionDescriptor,
rhs: IrExpression,
lhs: IrExpression
): IrExpression {
val lhsBinaryType = lhs.type.computeBinaryType()
return when (lhsBinaryType) {
is BinaryType.Primitive -> {
val areEqualByValue = symbols.areEqualByValue[lhsBinaryType.type]!!.owner
irCall(areEqualByValue).apply {
putValueArgument(0, reinterpret(lhs, areEqualByValue.valueParameters[0].type))
putValueArgument(1, reinterpret(rhs, areEqualByValue.valueParameters[1].type))
}
}
is BinaryType.Reference -> {
// TODO: don't use binaryType.nullable.
val lhsRawType = irBuiltins.anyClass.owner.defaultOrNullableType(lhsBinaryType.nullable)
val rhsBinaryType = rhs.type.computeBinaryType() as BinaryType.Reference<*>
val rhsRawType = irBuiltins.anyClass.owner.defaultOrNullableType(rhsBinaryType.nullable)
genFloatingOrReferenceEquals(
descriptor,
reinterpret(lhs, lhsRawType),
reinterpret(rhs, rhsRawType)
)
}
}
}
private fun IrBuilderWithScope.irEqeqNull(expression: IrExpression): IrExpression {
val type = expression.type.makeNullable()
val primitiveBinaryTypeOrNull = type.computePrimitiveBinaryTypeOrNull()
return when (primitiveBinaryTypeOrNull) {
null -> irEqeqeq(reinterpret(expression, type, irBuiltins.anyNType), irNull())
PrimitiveBinaryType.POINTER -> irCall(symbols.areEqualByValue[PrimitiveBinaryType.POINTER]!!.owner).apply {
putValueArgument(0, reinterpret(expression, type, symbols.nativePtrType))
putValueArgument(1, reinterpret(irNull(), type, symbols.nativePtrType))
}
else -> error("Nullable type ${type.toKotlinType()} is $primitiveBinaryTypeOrNull")
}
}
private fun IrBuilderWithScope.irLogicalAnd(lhs: IrExpression, rhs: IrExpression) = context.andand(lhs, rhs)
private fun IrBuilderWithScope.irIsNull(exp: IrExpression) = irEqeqeq(exp, irNull())
private fun IrBuilderWithScope.irIsNotNull(exp: IrExpression) = irNot(irEqeqeq(exp, irNull()))
private fun IrBuilderWithScope.genFloatingOrReferenceEquals(descriptor: FunctionDescriptor, lhs: IrExpression, rhs: IrExpression): IrExpression {
// TODO: areEqualByValue and ieee754Equals intrinsics are specially treated by code generator
// and thus can be declared synthetically in the compiler instead of explicitly in the runtime.
fun callEquals(lhs: IrExpression, rhs: IrExpression) =
if (descriptor in ieee754EqualsDescriptors())
// Find a type-compatible `konan.internal.ieee754Equals` intrinsic:
irCall(selectIntrinsic(symbols.ieee754Equals, lhs.type, rhs.type, true)!!).apply {
putValueArgument(0, lhs)
putValueArgument(1, rhs)
}
else
irCall(symbols.equals).apply {
dispatchReceiver = lhs
putValueArgument(0, rhs)
}
val lhsIsNotNullable = !lhs.type.containsNull()
val rhsIsNotNullable = !rhs.type.containsNull()
return if (descriptor in ieee754EqualsDescriptors()) {
if (lhsIsNotNullable && rhsIsNotNullable)
callEquals(lhs, rhs)
else irBlock {
val lhsTemp = irTemporary(lhs)
val rhsTemp = irTemporary(rhs)
if (lhsIsNotNullable xor rhsIsNotNullable) { // Exactly one nullable.
+irLogicalAnd(
irIsNotNull(irGet(if (lhsIsNotNullable) rhsTemp else lhsTemp)),
callEquals(irGet(lhsTemp), irGet(rhsTemp))
)
} else { // Both are nullable.
+irIfThenElse(context.irBuiltIns.booleanType, irIsNull(irGet(lhsTemp)),
irIsNull(irGet(rhsTemp)),
irLogicalAnd(
irIsNotNull(irGet(rhsTemp)),
callEquals(irGet(lhsTemp), irGet(rhsTemp))
)
}
)
}
}
} else {
if (lhsIsNotNullable)
callEquals(lhs, rhs)
else {
irBlock {
val lhsTemp = irTemporary(lhs)
if (rhsIsNotNullable)
+irLogicalAnd(irIsNotNull(irGet(lhsTemp)), callEquals(irGet(lhsTemp), rhs))
else {
val rhsTemp = irTemporary(rhs)
+irIfThenElse(irBuiltins.booleanType, irIsNull(irGet(lhsTemp)),
irIsNull(irGet(rhsTemp)),
callEquals(irGet(lhsTemp), irGet(rhsTemp))
)
}
}
}
@@ -7,7 +7,6 @@ import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.irasdescriptors.containsNull
import org.jetbrains.kotlin.backend.konan.irasdescriptors.getClass
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrFile
@@ -24,7 +23,6 @@ import org.jetbrains.kotlin.ir.util.getPropertyGetter
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.isNullable
/** Look for when-constructs where subject is enum entry.
* Replace branches that are comparisons with compile-time known enum entries
@@ -110,9 +108,7 @@ internal class EnumWhenLowering(private val context: Context) : IrElementTransfo
return tryLower(expression)
}
private val areEqualByValue = context.ir.symbols.areEqualByValue.first {
it.owner.valueParameters[0].type.classifierOrNull == context.ir.symbols.int
}
private val areEqualByValue = context.irBuiltIns.eqeqSymbol
// We are looking for branch that is a comparison of the subject and another enum entry.
private fun tryLower(call: IrCall): IrExpression {
@@ -732,10 +732,11 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
expression.transformChildrenVoid(this)
builder.at(expression)
val descriptor = expression.descriptor.original
val function = expression.symbol.owner
if (descriptor is ClassConstructorDescriptor) {
val type = descriptor.constructedClass.defaultType
if (type.isRepresentedAs(ValueType.C_POINTER) || type.isRepresentedAs(ValueType.NATIVE_POINTED)) {
if (function is IrConstructor) {
val inlinedClass = function.returnType.getInlinedClass()
if (inlinedClass?.descriptor == interop.cPointer || inlinedClass?.descriptor == interop.nativePointed) {
throw Error("Native interop types constructors must not be called directly")
}
}
@@ -0,0 +1,57 @@
/*
* Copyright 2010-2018 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 org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.lower.DefaultParameterInjector
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.irCall
internal class KonanDefaultParameterInjector(private val konanContext: KonanBackendContext)
: DefaultParameterInjector(konanContext) {
override fun nullConst(expression: IrElement, type: IrType): IrExpression {
val symbols = konanContext.ir.symbols
val startOffset = expression.startOffset
val endOffset = expression.endOffset
val nullConstOfEquivalentType = when (type.computePrimitiveBinaryTypeOrNull()) {
null -> IrConstImpl.constNull(startOffset, endOffset, context.irBuiltIns.nothingNType)
PrimitiveBinaryType.BOOLEAN -> IrConstImpl.boolean(startOffset, endOffset, type, false)
PrimitiveBinaryType.BYTE -> IrConstImpl.byte(startOffset, endOffset, type, 0)
PrimitiveBinaryType.SHORT -> IrConstImpl.short(startOffset, endOffset, type, 0)
PrimitiveBinaryType.INT -> IrConstImpl.int(startOffset, endOffset, type, 0)
PrimitiveBinaryType.LONG -> IrConstImpl.long(startOffset, endOffset, type, 0)
PrimitiveBinaryType.FLOAT -> IrConstImpl.float(startOffset, endOffset, type, 0.0F)
PrimitiveBinaryType.DOUBLE -> IrConstImpl.double(startOffset, endOffset, type, 0.0)
PrimitiveBinaryType.POINTER -> irCall(startOffset, endOffset, symbols.getNativeNullPtr.owner, emptyList())
}
return irCall(
startOffset,
endOffset,
symbols.reinterpret.owner,
listOf(nullConstOfEquivalentType.type, type)
).apply {
extensionReceiver = nullConstOfEquivalentType
}
}
}
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.types.typeUtil.supertypes
import org.jetbrains.kotlin.utils.addIfNotNull
@@ -638,7 +639,7 @@ abstract class ObjCExportHeaderGenerator(
internal fun mapReferenceType(kotlinType: KotlinType): ObjCReferenceType =
mapReferenceTypeIgnoringNullability(kotlinType).let {
if (TypeUtils.isNullableType(kotlinType)) {
if (kotlinType.unwrapInlinedClasses().isNullable()) {
ObjCNullableReferenceType(it)
} else {
it
@@ -683,7 +684,8 @@ abstract class ObjCExportHeaderGenerator(
// TODO: translate `where T : BaseClass, T : SomeInterface` to `BaseClass* <SomeInterface>`
if (classDescriptor == builtIns.any || classDescriptor in hiddenTypes) {
// TODO: expose custom inline class boxes properly.
if (classDescriptor == builtIns.any || classDescriptor in hiddenTypes || classDescriptor.isInlined()) {
return ObjCIdType
}
@@ -22,9 +22,9 @@ import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenDescriptors
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPublicApi
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isUnit
@@ -67,7 +67,7 @@ internal fun ObjCExportMapper.shouldBeExposed(descriptor: ClassDescriptor): Bool
descriptor.isEffectivelyPublicApi && !descriptor.defaultType.isObjCObjectType() && when (descriptor.kind) {
ClassKind.CLASS, ClassKind.INTERFACE, ClassKind.ENUM_CLASS, ClassKind.OBJECT -> true
ClassKind.ENUM_ENTRY, ClassKind.ANNOTATION_CLASS -> false
} && !descriptor.isExpect && !isSpecialMapped(descriptor)
} && !descriptor.isExpect && !isSpecialMapped(descriptor) && !descriptor.isInlined()
private fun ObjCExportMapper.isBase(descriptor: CallableMemberDescriptor): Boolean =
descriptor.overriddenDescriptors.all { !shouldBeExposed(it) }
@@ -110,15 +110,28 @@ internal fun ObjCExportMapper.doesThrow(method: FunctionDescriptor): Boolean = m
it.overriddenDescriptors.isEmpty() && it.annotations.hasAnnotation(KonanBuiltIns.FqNames.throws)
}
private fun ObjCExportMapper.bridgeType(kotlinType: KotlinType): TypeBridge {
val valueType = kotlinType.correspondingValueType
?: return ReferenceBridge
val objCValueType = ObjCValueType.values().singleOrNull { it.kotlinValueType == valueType }
?: error("Can't produce $kotlinType to framework API")
return ValueTypeBridge(objCValueType)
}
private fun ObjCExportMapper.bridgeType(kotlinType: KotlinType): TypeBridge = kotlinType.unwrapToPrimitiveOrReference(
eachInlinedClass = { _, _ ->
// unsigned types to be handled.
},
ifPrimitive = { primitiveType, _ ->
val objCValueType = when (primitiveType) {
KonanPrimitiveType.BOOLEAN -> ObjCValueType.BOOL
KonanPrimitiveType.CHAR -> ObjCValueType.UNSIGNED_SHORT
KonanPrimitiveType.BYTE -> ObjCValueType.CHAR
KonanPrimitiveType.SHORT -> ObjCValueType.SHORT
KonanPrimitiveType.INT -> ObjCValueType.INT
KonanPrimitiveType.LONG -> ObjCValueType.LONG_LONG
KonanPrimitiveType.FLOAT -> ObjCValueType.FLOAT
KonanPrimitiveType.DOUBLE -> ObjCValueType.DOUBLE
KonanPrimitiveType.NON_NULL_NATIVE_PTR -> error("Can't produce pointer type to framework API") // TODO
}
ValueTypeBridge(objCValueType)
},
ifReference = {
ReferenceBridge
}
)
private fun ObjCExportMapper.bridgeParameter(parameter: ParameterDescriptor): MethodBridgeValueParameter =
MethodBridgeValueParameter.Mapped(bridgeType(parameter.type))
@@ -16,8 +16,6 @@
package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.backend.konan.ValueType
sealed class ObjCType {
final override fun toString(): String = this.render()
@@ -114,18 +112,17 @@ object ObjCVoidType : ObjCType() {
}
internal enum class ObjCValueType(
val kotlinValueType: ValueType, // It is here for simplicity.
val encoding: String
) {
BOOL(ValueType.BOOLEAN, "c"),
CHAR(ValueType.BYTE, "c"),
UNSIGNED_SHORT(ValueType.CHAR, "S"),
SHORT(ValueType.SHORT, "s"),
INT(ValueType.INT, "i"),
LONG_LONG(ValueType.LONG, "q"),
FLOAT(ValueType.FLOAT, "f"),
DOUBLE(ValueType.DOUBLE, "d")
BOOL("c"),
CHAR("c"),
SHORT("s"),
UNSIGNED_SHORT("S"),
INT("i"),
LONG_LONG("q"),
FLOAT("f"),
DOUBLE("d")
;
@@ -21,15 +21,11 @@ import org.jetbrains.kotlin.backend.common.ir.ir2stringWhole
import org.jetbrains.kotlin.backend.common.peek
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenDescriptors
import org.jetbrains.kotlin.backend.konan.correspondingValueType
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.backend.konan.descriptors.target
import org.jetbrains.kotlin.backend.konan.getTypeConversion
import org.jetbrains.kotlin.backend.konan.ir.KonanIr
import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
import org.jetbrains.kotlin.backend.konan.isValueType
import org.jetbrains.kotlin.backend.konan.llvm.functionName
import org.jetbrains.kotlin.backend.konan.llvm.localHash
import org.jetbrains.kotlin.descriptors.ClassKind
@@ -38,6 +34,7 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.constructors
@@ -49,43 +46,6 @@ import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.util.OperatorNameConventions
private fun getClassWithBoxingIncluded(type: IrType, ir: KonanIr): ClassDescriptor? {
/*
* Some primitive types can be null and some can't. Those that can't must be replaced with the corresponding box.
* Int -> Int
* Int? -> IntBox
* but
* CPointer -> CPointer
* CPointer? -> CPointer
*/
return if (type.correspondingValueType == null && type.makeNotNull().correspondingValueType != null)
ir.symbols.getTypeConversion(type.makeNotNull(), type)!!.owner.returnType.getClass()!!
else
type.getClass()
}
private fun computeErasure(type: IrType, ir: KonanIr, erasure: MutableList<ClassDescriptor>) {
val irClass = getClassWithBoxingIncluded(type, ir)
if (irClass != null) {
erasure += irClass
} else {
val classifier = type.classifierOrFail
if (classifier is IrTypeParameterSymbol) {
classifier.owner.superTypes.forEach {
computeErasure(it, ir, erasure)
}
} else {
TODO(classifier.descriptor.toString())
}
}
}
internal fun IrType.erasure(context: Context): List<ClassDescriptor> {
val result = mutableListOf<ClassDescriptor>()
computeErasure(this, context.ir, result)
return result
}
private fun IrClass.getOverridingOf(function: FunctionDescriptor) = (function as? SimpleFunctionDescriptor)?.let {
it.allOverriddenDescriptors.atMostOne { it.parent == this }
}
@@ -516,7 +476,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
)
val throwsNode = DataFlowIR.Node.Variable(
values = thrownValues.map { expressionToEdge(it) },
type = symbolTable.mapClass(context.ir.symbols.throwable.owner),
type = symbolTable.mapClassReferenceType(context.ir.symbols.throwable.owner),
kind = DataFlowIR.VariableKind.Temporary
)
variables.forEach { descriptor, node ->
@@ -535,7 +495,10 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
private fun expressionToEdge(expression: IrExpression) =
if (expression is IrTypeOperatorCall && expression.operator.isCast())
DataFlowIR.Edge(getNode(expression.argument), symbolTable.mapType(expression.typeOperand))
DataFlowIR.Edge(
getNode(expression.argument),
symbolTable.mapClassReferenceType(expression.typeOperand.getClass()!!)
)
else DataFlowIR.Edge(getNode(expression), null)
private fun getNode(expression: IrExpression): DataFlowIR.Node {
@@ -595,7 +558,9 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
expressionToEdge(value.getValueArgument(0)!!), expressionToEdge(value.getValueArgument(1)!!))
createUninitializedInstanceSymbol ->
DataFlowIR.Node.AllocInstance(symbolTable.mapType(value.getTypeArgument(0)!!))
DataFlowIR.Node.AllocInstance(symbolTable.mapClassReferenceType(
value.getTypeArgument(0)!!.getClass()!!
))
initInstanceSymbol -> {
val thiz = expressionToEdge(value.getValueArgument(0)!!)
@@ -605,7 +570,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
DataFlowIR.Node.StaticCall(
symbolTable.mapFunction(callee),
arguments,
symbolTable.mapClass(callee.constructedClass),
symbolTable.mapClassReferenceType(callee.constructedClass),
null
)
}
@@ -624,7 +589,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
DataFlowIR.Node.NewObject(
symbolTable.mapFunction(callee),
arguments,
symbolTable.mapClass(callee.constructedClass),
symbolTable.mapClassReferenceType(callee.constructedClass),
value
)
} else {
@@ -632,27 +597,22 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
if (callee.isOverridable && value.superQualifier == null) {
val owner = callee.containingDeclaration as ClassDescriptor
val actualReceiverType = value.dispatchReceiver!!.type
val actualReceiverClassifier = actualReceiverType.classifierOrFail
val receiverType =
if (actualReceiverType.classifierOrNull is IrTypeParameterSymbol
if (actualReceiverClassifier is IrTypeParameterSymbol
|| !callee.isReal /* Could be a bridge. */)
symbolTable.mapClass(owner)
symbolTable.mapClassReferenceType(owner)
else {
val actualClassAtCallsite =
actualReceiverType.classifierOrFail.descriptor
as org.jetbrains.kotlin.descriptors.ClassDescriptor
(actualReceiverClassifier as IrClassSymbol).descriptor
// assert (DescriptorUtils.isSubclass(actualClassAtCallsite, owner.descriptor)) {
// "Expected an inheritor of ${owner.descriptor}, but was $actualClassAtCallsite"
// }
if (DescriptorUtils.isSubclass(actualClassAtCallsite, owner.descriptor)) {
symbolTable.mapType(
actualReceiverType.let {
if (it.isValueType()) // A virtual call on a value type - it must be boxed.
context.ir.symbols.getTypeConversion(it, it.makeNullable())!!.owner.returnType
else it
}
)
symbolTable.mapClassReferenceType(actualReceiverClassifier.owner) // Box if inline class.
} else {
symbolTable.mapClass(owner)
symbolTable.mapClassReferenceType(owner)
}
}
if (owner.isInterface) {
@@ -17,7 +17,7 @@
package org.jetbrains.kotlin.backend.konan.optimizations
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.ValueType
import org.jetbrains.kotlin.backend.konan.PrimitiveBinaryType
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import sun.misc.Unsafe
import kotlin.reflect.KClass
@@ -204,15 +204,15 @@ internal object DFGSerializer {
}
}
class TypeBase(val isFinal: Boolean, val isAbstract: Boolean, val correspondingValueType: ValueType?, val name: String?) {
class TypeBase(val isFinal: Boolean, val isAbstract: Boolean, val primitiveBinaryType: PrimitiveBinaryType?, val name: String?) {
constructor(data: ArraySlice) : this(data.readBoolean(), data.readBoolean(),
data.readNullableInt()?.let { ValueType.values()[it] }, data.readNullableString())
data.readNullableInt()?.let { PrimitiveBinaryType.values()[it] }, data.readNullableString())
fun write(result: ArraySlice) {
result.writeBoolean(isFinal)
result.writeBoolean(isAbstract)
result.writeNullableInt(correspondingValueType?.ordinal)
result.writeNullableInt(primitiveBinaryType?.ordinal)
result.writeNullableString(name)
}
}
@@ -776,7 +776,8 @@ internal object DFGSerializer {
fun serialize(context: Context, moduleDFG: ModuleDFG) {
val symbolTable = moduleDFG.symbolTable
val typeMap = (symbolTable.classMap.values + DataFlowIR.Type.Virtual).distinct().withIndex().associateBy({ it.value }, { it.index })
val typeList = symbolTable.classMap.values + symbolTable.primitiveMap.values + DataFlowIR.Type.Virtual
val typeMap = typeList.distinct().withIndex().associateBy({ it.value }, { it.index })
val functionSymbolMap = symbolTable.functionMap.values.distinct().withIndex().associateBy({ it.value }, { it.index })
DEBUG_OUTPUT(0) {
println("TYPES: ${typeMap.size}, " +
@@ -790,7 +791,7 @@ internal object DFGSerializer {
.map {
fun buildTypeBase(type: DataFlowIR.Type) =
TypeBase(type.isFinal, type.isAbstract, type.correspondingValueType, type.name)
TypeBase(type.isFinal, type.isAbstract, type.primitiveBinaryType, type.name)
fun buildTypeIntestines(type: DataFlowIR.Type.Declared) =
DeclaredType(
@@ -962,14 +963,14 @@ internal object DFGSerializer {
when {
external != null ->
DataFlowIR.Type.External(external.hash, external.base.isFinal, external.base.isAbstract,
external.base.correspondingValueType, external.base.name)
external.base.primitiveBinaryType, external.base.name)
public != null -> {
val symbolTableIndex = public.intestines.index
if (symbolTableIndex >= 0)
++module.numberOfClasses
DataFlowIR.Type.Public(public.hash, public.intestines.base.isFinal,
public.intestines.base.isAbstract, public.intestines.base.correspondingValueType,
public.intestines.base.isAbstract, public.intestines.base.primitiveBinaryType,
module, symbolTableIndex, public.intestines.base.name).also {
publicTypesMap.put(it.hash, it)
allTypes += it
@@ -981,7 +982,7 @@ internal object DFGSerializer {
if (symbolTableIndex >= 0)
++module.numberOfClasses
DataFlowIR.Type.Private(privateTypeIndex++, private.intestines.base.isFinal,
private.intestines.base.isAbstract, private.intestines.base.correspondingValueType,
private.intestines.base.isAbstract, private.intestines.base.primitiveBinaryType,
module, symbolTableIndex, private.intestines.base.name).also {
allTypes += it
}
@@ -50,13 +50,14 @@ import org.jetbrains.kotlin.resolve.constants.IntValue
internal object DataFlowIR {
abstract class Type(val isFinal: Boolean, val isAbstract: Boolean,
val correspondingValueType: ValueType?, val name: String?) {
val primitiveBinaryType: PrimitiveBinaryType?,
val name: String?) {
// Special marker type forbidding devirtualization on its instances.
object Virtual : Declared(false, true, null, null, -1, "\$VIRTUAL")
class External(val hash: Long, isFinal: Boolean, isAbstract: Boolean,
correspondingValueType: ValueType?, name: String? = null)
: Type(isFinal, isAbstract, correspondingValueType, name) {
primitiveBinaryType: PrimitiveBinaryType?, name: String? = null)
: Type(isFinal, isAbstract, primitiveBinaryType, name) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is External) return false
@@ -73,17 +74,17 @@ internal object DataFlowIR {
}
}
abstract class Declared(isFinal: Boolean, isAbstract: Boolean, correspondingValueType: ValueType?,
abstract class Declared(isFinal: Boolean, isAbstract: Boolean, primitiveBinaryType: PrimitiveBinaryType?,
val module: Module?, val symbolTableIndex: Int, name: String?)
: Type(isFinal, isAbstract, correspondingValueType, name) {
: Type(isFinal, isAbstract, primitiveBinaryType, name) {
val superTypes = mutableListOf<Type>()
val vtable = mutableListOf<FunctionSymbol>()
val itable = mutableMapOf<Long, FunctionSymbol>()
}
class Public(val hash: Long, isFinal: Boolean, isAbstract: Boolean, correspondingValueType: ValueType?,
class Public(val hash: Long, isFinal: Boolean, isAbstract: Boolean, primitiveBinaryType: PrimitiveBinaryType?,
module: Module, symbolTableIndex: Int, name: String? = null)
: Declared(isFinal, isAbstract, correspondingValueType, module, symbolTableIndex, name) {
: Declared(isFinal, isAbstract, primitiveBinaryType, module, symbolTableIndex, name) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Public) return false
@@ -100,9 +101,9 @@ internal object DataFlowIR {
}
}
class Private(val index: Int, isFinal: Boolean, isAbstract: Boolean, correspondingValueType: ValueType?,
class Private(val index: Int, isFinal: Boolean, isAbstract: Boolean, primitiveBinaryType: PrimitiveBinaryType?,
module: Module, symbolTableIndex: Int, name: String? = null)
: Declared(isFinal, isAbstract, correspondingValueType, module, symbolTableIndex, name) {
: Declared(isFinal, isAbstract, primitiveBinaryType, module, symbolTableIndex, name) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Private) return false
@@ -450,6 +451,7 @@ internal object DataFlowIR {
private inline fun takeName(block: () -> String) = if (TAKE_NAMES) block() else null
val classMap = mutableMapOf<ClassDescriptor, Type>()
val primitiveMap = mutableMapOf<PrimitiveBinaryType, Type>()
val functionMap = mutableMapOf<DeclarationDescriptor, FunctionSymbol>()
private val NAME_ESCAPES = Name.identifier("Escapes")
@@ -490,41 +492,40 @@ internal object DataFlowIR {
override fun visitClass(declaration: IrClass) {
declaration.acceptChildrenVoid(this)
mapClass(declaration)
mapClassReferenceType(declaration)
}
}, data = null)
}
private fun ClassDescriptor.isFinal() = modality == Modality.FINAL && kind != ClassKind.ENUM_CLASS
fun mapClass(descriptor: ClassDescriptor): Type {
fun mapClassReferenceType(descriptor: ClassDescriptor): Type {
// Do not try to devirtualize ObjC classes.
if (descriptor.module.name == Name.special("<forward declarations>") || descriptor.isObjCClass())
return Type.Virtual
val isFinal = descriptor.isFinal()
val isAbstract = descriptor.isAbstract()
val correspondingValueType = descriptor.defaultType.correspondingValueType
val name = descriptor.fqNameSafe.asString()
if (descriptor.module != irModule.descriptor)
return classMap.getOrPut(descriptor) {
Type.External(name.localHash.value, isFinal, isAbstract, correspondingValueType, takeName { name })
Type.External(name.localHash.value, isFinal, isAbstract, null, takeName { name })
}
classMap[descriptor]?.let { return it }
val placeToClassTable = correspondingValueType == null
val placeToClassTable = true
val symbolTableIndex = if (placeToClassTable) module.numberOfClasses++ else -1
val type = if (descriptor.isExported())
Type.Public(name.localHash.value, isFinal, isAbstract, correspondingValueType,
Type.Public(name.localHash.value, isFinal, isAbstract, null,
module, symbolTableIndex, takeName { name })
else
Type.Private(privateTypeIndex++, isFinal, isAbstract, correspondingValueType,
Type.Private(privateTypeIndex++, isFinal, isAbstract, null,
module, symbolTableIndex, takeName { name })
classMap[descriptor] = type
type.superTypes += descriptor.superTypes.map { mapType(it) }
type.superTypes += descriptor.superTypes.map { mapClassReferenceType(it.getClass()!!) }
if (!isAbstract) {
val vtableBuilder = context.getVtableBuilder(descriptor)
type.vtable += vtableBuilder.vtableEntries.map { mapFunction(it.getImplementation(context)!!) }
@@ -541,7 +542,26 @@ internal object DataFlowIR {
return erasure.singleOrNull { !it.isInterface } ?: context.ir.symbols.any.owner
}
fun mapType(type: IrType) = mapClass(choosePrimary(type.erasure(context)))
fun mapPrimitiveBinaryType(primitiveBinaryType: PrimitiveBinaryType): Type =
primitiveMap.getOrPut(primitiveBinaryType) {
Type.Public(
primitiveBinaryType.ordinal.toLong(),
true,
false,
primitiveBinaryType,
module,
-1,
null
)
}
fun mapType(type: IrType): Type {
val binaryType = type.computeBinaryType()
return when (binaryType) {
is BinaryType.Primitive -> mapPrimitiveBinaryType(binaryType.type)
is BinaryType.Reference -> mapClassReferenceType(choosePrimary(binaryType.types.toList()))
}
}
// TODO: use from LlvmDeclarations.
private fun getFqName(descriptor: DeclarationDescriptor): FqName =
@@ -602,7 +622,7 @@ internal object DataFlowIR {
symbol.parameterTypes =
(descriptor.allParameters.map { it.type } + (if (descriptor.isSuspend) listOf(continuationType) else emptyList()))
.map { mapClass(choosePrimary(it.erasure(context))) }
.map { mapType(it) }
.toTypedArray()
symbol.returnType = mapType(if (descriptor.isSuspend)
context.irBuiltIns.anyType
@@ -622,7 +642,7 @@ internal object DataFlowIR {
functionMap[it] = symbol
symbol.parameterTypes = emptyArray()
symbol.returnType = mapClass(context.ir.symbols.unit.owner)
symbol.returnType = mapClassReferenceType(context.ir.symbols.unit.owner)
return symbol
}
@@ -979,11 +979,13 @@ internal object Devirtualization {
private fun devirtualize(irModule: IrModuleFragment, context: Context,
moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG,
devirtualizedCallSites: Map<IrCall, DevirtualizedCallSite>) {
val nativePtrType = context.builtIns.nativePtr.defaultType
val nativePtrEqualityOperatorSymbol = context.ir.symbols.areEqualByValue.single { it.descriptor.valueParameters[0].type == nativePtrType }
val nativePtrType = context.ir.symbols.nativePtrType
val nativePtrEqualityOperatorSymbol = context.ir.symbols.areEqualByValue[PrimitiveBinaryType.POINTER]!!
val optimize = context.shouldOptimize()
/*
val boxFunctions = ValueType.values().associate { context.ir.symbols.boxFunctions[it]!! to it }
val unboxFunctions = ValueType.values().associate { context.ir.symbols.getUnboxFunction(it) to it }
*/
fun DataFlowIR.Type.resolved(): DataFlowIR.Type.Declared {
if (this is DataFlowIR.Type.Declared) return this
@@ -997,7 +999,11 @@ internal object Devirtualization {
return this
}
/*
fun IrExpression.isBoxOrUnboxCall() = this is IrCall && (boxFunctions[symbol] != null || unboxFunctions[symbol] != null)
*/
fun IrExpression.isBoxOrUnboxCall() = false
fun IrBuilderWithScope.irCoerce(value: IrExpression, coercion: IrFunctionSymbol?) =
if (coercion == null)
@@ -1023,6 +1029,7 @@ internal object Devirtualization {
, coercion.symbol)
}
/*
fun assertCoercionsMatch(coercion1: IrFunctionSymbol, coercion2: IrFunctionSymbol) {
boxFunctions[coercion1]?.let { assert (unboxFunctions[coercion2] == it)
{ "Incosistent coercions: ${coercion1.descriptor}, ${coercion2.descriptor}" }
@@ -1044,6 +1051,7 @@ internal object Devirtualization {
assertCoercionsMatch(coercion, prevCoercion)
return irGet(value)
}
*/
fun IrBuilderWithScope.irDevirtualizedCall(callee: IrCall, actualType: IrType,
devirtualizedCallee: DataFlowIR.FunctionSymbol.Declared) =
@@ -1067,7 +1075,7 @@ internal object Devirtualization {
extensionReceiver: PossiblyCoercedValue?,
parameters: Map<ValueParameterDescriptor, PossiblyCoercedValue>) =
actualCallee.bridgeTarget.let {
if (it == null)
// if (it == null)
irDevirtualizedCall(callee, actualType, actualCallee).apply {
this.dispatchReceiver = irGet(receiver)
this.extensionReceiver = extensionReceiver?.getFullValue(this@irDevirtualizedCall)
@@ -1075,6 +1083,7 @@ internal object Devirtualization {
putValueArgument(it.index, parameters[it]!!.getFullValue(this@irDevirtualizedCall))
}
}
/*
else {
val bridgeTarget = it.resolved() as DataFlowIR.FunctionSymbol.Declared
val callResult = irDevirtualizedCall(callee, actualType, bridgeTarget).apply {
@@ -1102,6 +1111,7 @@ internal object Devirtualization {
actualCallee.returnType.resolved().correspondingValueType)
irCoerce(callResult, returnCoercion)
}
*/
}
irModule.transformChildrenVoid(object: IrElementTransformerVoidWithContext() {
@@ -1114,10 +1124,12 @@ internal object Devirtualization {
arg.argument
else arg
if (!uncastedArg.isBoxOrUnboxCall()) return expression
/*
val argarg = (uncastedArg as IrCall).getArguments().single().second
if (boxFunctions[expression.symbol].let { it != null && it == unboxFunctions[uncastedArg.symbol] }
|| unboxFunctions[expression.symbol].let { it != null && it == boxFunctions[uncastedArg.symbol] })
return argarg
*/
return expression
}
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.backend.konan.DirectedGraphMultiNode
import org.jetbrains.kotlin.backend.konan.llvm.Lifetime
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.ir.util.defaultType
internal object EscapeAnalysis {
@@ -106,7 +107,7 @@ internal object EscapeAnalysis {
}
fun analyze(): Map<DataFlowIR.FunctionSymbol, FunctionAnalysisResult> {
val nothing = moduleDFG.symbolTable.mapClass(context.ir.symbols.nothing.owner).resolved()
val nothing = moduleDFG.symbolTable.mapClassReferenceType(context.ir.symbols.nothing.owner).resolved()
return callGraph.nodes.associateBy({ it.symbol }) {
val function = functions[it.symbol]!!
val body = function.body
@@ -17,8 +17,8 @@
package org.jetbrains.kotlin.backend.konan.serialization
import org.jetbrains.kotlin.backend.konan.descriptors.isExpectMember
import org.jetbrains.kotlin.backend.konan.getInlinedClass
import org.jetbrains.kotlin.backend.konan.getObjCMethodInfo
import org.jetbrains.kotlin.backend.konan.isValueType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.types.KotlinType
@@ -222,7 +222,7 @@ private val FunctionDescriptor.signature: String
val signatureSuffix =
when {
this.typeParameters.isNotEmpty() -> "Generic"
returnType.let { it != null && it.isValueType() } -> "ValueType"
returnType?.getInlinedClass() != null -> "ValueType"
returnType.let { it != null && !KotlinBuiltIns.isUnitOrNullableUnit(it) } -> typeToHashString(returnType!!)
else -> ""
}
@@ -736,3 +736,6 @@ val IrTypeArgument.typeOrNull: IrType? get() = (this as? IrTypeProjection)?.type
val IrType.isSimpleTypeWithQuestionMark: Boolean
get() = this is IrSimpleType && this.hasQuestionMark
fun IrClass.defaultOrNullableType(hasQuestionMark: Boolean) =
if (hasQuestionMark) this.defaultType.makeNullable() else this.defaultType
+4
View File
@@ -379,6 +379,10 @@ task function_defaults_with_vararg2(type: RunKonanTest) {
source = "codegen/function/defaultsWithVarArg2.kt"
}
task function_defaults_with_inline_classes(type: RunKonanTest) {
source = "codegen/function/defaultsWithInlineClasses.kt"
}
task sum_3const(type: RunKonanTest) {
source = "codegen/function/sum_3const.kt"
}
@@ -0,0 +1,11 @@
package codegen.function.defaultsWithInlineClasses
import kotlin.test.*
inline class Foo(val value: Int)
fun foo(x: Foo = Foo(42)) = x.value
@Test fun runTest() {
assertEquals(foo(), 42)
assertEquals(foo(Foo(17)), 17)
}
@@ -193,3 +193,14 @@ fun getNamedObjectInterface(): OpenClassI = WithCompanionAndObject.Named
typealias EE = Enumeration
fun EE.getAnswer() : EE = Enumeration.ANSWER
inline class IC1(val value: Int)
inline class IC2(val value: String)
inline class IC3(val value: TripleVals<Any?>?)
fun box(ic1: IC1): Any = ic1
fun box(ic2: IC2): Any = ic2
fun box(ic3: IC3): Any = ic3
fun concatenateInlineClassValues(ic1: IC1, ic1N: IC1?, ic2: IC2, ic2N: IC2?, ic3: IC3, ic3N: IC3?): String =
"${ic1.value} ${ic1N?.value} ${ic2.value} ${ic2N?.value} ${ic3.value} ${ic3N?.value}"
@@ -277,6 +277,25 @@ func testCompanionObj() throws {
try assertEquals(actual: Values.getNamedObjectInterface().iFun(), expected: named.iFun(), "Named object's method")
}
func testInlineClasses() throws {
let ic1: Int32 = 42
let ic1N = Values.box(ic1: 17)
let ic2 = "foo"
let ic2N = "bar"
let ic3 = ValuesTripleVals(first: 1, second: 2, third: 3)
let ic3N = Values.box(ic3: nil)
try assertEquals(
actual: Values.concatenateInlineClassValues(ic1: ic1, ic1N: ic1N, ic2: ic2, ic2N: ic2N, ic3: ic3, ic3N: ic3N),
expected: "42 17 foo bar TripleVals(first=1, second=2, third=3) null"
)
try assertEquals(
actual: Values.concatenateInlineClassValues(ic1: ic1, ic1N: nil, ic2: ic2, ic2N: nil, ic3: nil, ic3N: nil),
expected: "42 null foo null null null"
)
}
// -------- Execution of the test --------
class ValuesTests : TestProvider {
@@ -307,6 +326,7 @@ class ValuesTests : TestProvider {
TestCase(name: "TestEnum", method: withAutorelease(testEnum)),
TestCase(name: "TestDataClass", method: withAutorelease(testDataClass)),
TestCase(name: "TestCompanionObj", method: withAutorelease(testCompanionObj)),
TestCase(name: "TestInlineClasses", method: withAutorelease(testInlineClasses)),
]
}
}
@@ -70,4 +70,14 @@ class Impl2 : Impl1() {
override fun foo(arg0: String, arg1: Int, arg2: I) {
println("Impl2.I: $arg0 $arg1 ${arg2::class.qualifiedName}")
}
}
}
inline class IC1(val value: Int)
inline class IC2(val value: String)
inline class IC3(val value: Base?)
fun useInlineClasses(ic1: IC1, ic2: IC2, ic3: IC3) {
assert(ic1.value == 42)
assert(ic2.value == "bar")
assert(ic3.value is Base)
}
@@ -40,6 +40,8 @@ int main(void) {
__ kotlin.root.topLevelFunctionVoid(42, 0);
printf("topLevel = %d %d\n", topLevelFunctionFromC(780, 3), __ kotlin.root.topLevelFunctionFromCShort(5, 2));
__ kotlin.root.useInlineClasses(42, "bar", base);
__ DisposeString(string);
__ DisposeStablePointer(base.pinned);
__ DisposeStablePointer(child.pinned);
+15 -221
View File
@@ -17,258 +17,52 @@
package konan.internal
@SymbolName("getCachedBooleanBox")
external fun getCachedBooleanBox(value: Boolean): BooleanBox
external fun getCachedBooleanBox(value: Boolean): Boolean?
@SymbolName("inBooleanBoxCache")
external fun inBooleanBoxCache(value: Boolean): Boolean
@SymbolName("getCachedByteBox")
external fun getCachedByteBox(value: Byte): ByteBox
external fun getCachedByteBox(value: Byte): Byte?
@SymbolName("inByteBoxCache")
external fun inByteBoxCache(value: Byte): Boolean
@SymbolName("getCachedCharBox")
external fun getCachedCharBox(value: Char): CharBox
external fun getCachedCharBox(value: Char): Char?
@SymbolName("inCharBoxCache")
external fun inCharBoxCache(value: Char): Boolean
@SymbolName("getCachedShortBox")
external fun getCachedShortBox(value: Short): ShortBox
external fun getCachedShortBox(value: Short): Short?
@SymbolName("inShortBoxCache")
external fun inShortBoxCache(value: Short): Boolean
@SymbolName("getCachedIntBox")
external fun getCachedIntBox(idx: Int): IntBox
external fun getCachedIntBox(idx: Int): Int?
@SymbolName("inIntBoxCache")
external fun inIntBoxCache(value: Int): Boolean
@SymbolName("getCachedLongBox")
external fun getCachedLongBox(value: Long): LongBox
external fun getCachedLongBox(value: Long): Long?
@SymbolName("inLongBoxCache")
external fun inLongBoxCache(value: Long): Boolean
@Frozen
class BooleanBox(val value: Boolean) : Comparable<Boolean> {
override fun equals(other: Any?): Boolean {
if (other !is BooleanBox) {
return false
}
return this.value == other.value
}
override fun hashCode() = value.hashCode()
override fun toString() = value.toString()
override fun compareTo(other: Boolean): Int = value.compareTo(other)
}
// TODO: functions below are used for ObjCExport, move and rename them correspondigly.
@ExportForCppRuntime("Kotlin_boxBoolean")
fun boxBoolean(value: Boolean) = if (inBooleanBoxCache(value)) {
getCachedBooleanBox(value)
} else {
BooleanBox(value)
}
@Frozen
class CharBox(val value: Char) : Comparable<Char> {
override fun equals(other: Any?): Boolean {
if (other !is CharBox) {
return false
}
return this.value == other.value
}
override fun hashCode() = value.hashCode()
override fun toString() = value.toString()
override fun compareTo(other: Char): Int = value.compareTo(other)
}
fun boxBoolean(value: Boolean): Boolean? = value
@ExportForCppRuntime("Kotlin_boxChar")
fun boxChar(value: Char) = if (inCharBoxCache(value)) {
getCachedCharBox(value)
} else {
CharBox(value)
}
@Frozen
class ByteBox(val value: Byte) : Number(), Comparable<Byte> {
override fun equals(other: Any?): Boolean {
if (other !is ByteBox) {
return false
}
return this.value == other.value
}
override fun hashCode() = value.hashCode()
override fun toString() = value.toString()
override fun compareTo(other: Byte): Int = value.compareTo(other)
override fun toByte() = value.toByte()
override fun toChar() = value.toChar()
override fun toShort() = value.toShort()
override fun toInt() = value.toInt()
override fun toLong() = value.toLong()
override fun toFloat() = value.toFloat()
override fun toDouble() = value.toDouble()
}
fun boxChar(value: Char): Char? = value
@ExportForCppRuntime("Kotlin_boxByte")
fun boxByte(value: Byte) = if (inByteBoxCache(value)) {
getCachedByteBox(value)
} else {
ByteBox(value)
}
@Frozen
class ShortBox(val value: Short) : Number(), Comparable<Short> {
override fun equals(other: Any?): Boolean {
if (other !is ShortBox) {
return false
}
return this.value == other.value
}
override fun hashCode() = value.hashCode()
override fun toString() = value.toString()
override fun compareTo(other: Short): Int = value.compareTo(other)
override fun toByte() = value.toByte()
override fun toChar() = value.toChar()
override fun toShort() = value.toShort()
override fun toInt() = value.toInt()
override fun toLong() = value.toLong()
override fun toFloat() = value.toFloat()
override fun toDouble() = value.toDouble()
}
fun boxByte(value: Byte): Byte? = value
@ExportForCppRuntime("Kotlin_boxShort")
fun boxShort(value: Short) = if (inShortBoxCache(value)) {
getCachedShortBox(value)
} else {
ShortBox(value)
}
@Frozen
class IntBox(val value: Int) : Number(), Comparable<Int> {
override fun equals(other: Any?): Boolean {
if (other !is IntBox) {
return false
}
return this.value == other.value
}
override fun hashCode() = value.hashCode()
override fun toString() = value.toString()
override fun compareTo(other: Int): Int = value.compareTo(other)
override fun toByte() = value.toByte()
override fun toChar() = value.toChar()
override fun toShort() = value.toShort()
override fun toInt() = value.toInt()
override fun toLong() = value.toLong()
override fun toFloat() = value.toFloat()
override fun toDouble() = value.toDouble()
}
fun boxShort(value: Short): Short? = value
@ExportForCppRuntime("Kotlin_boxInt")
fun boxInt(value: Int) = if (inIntBoxCache(value)) {
getCachedIntBox(value)
} else {
IntBox(value)
}
@Frozen
class LongBox(val value: Long) : Number(), Comparable<Long> {
override fun equals(other: Any?): Boolean {
if (other !is LongBox) {
return false
}
return this.value == other.value
}
override fun hashCode() = value.hashCode()
override fun toString() = value.toString()
override fun compareTo(other: Long): Int = value.compareTo(other)
override fun toByte() = value.toByte()
override fun toChar() = value.toChar()
override fun toShort() = value.toShort()
override fun toInt() = value.toInt()
override fun toLong() = value.toLong()
override fun toFloat() = value.toFloat()
override fun toDouble() = value.toDouble()
}
fun boxInt(value: Int): Int? = value
@ExportForCppRuntime("Kotlin_boxLong")
fun boxLong(value: Long) = if (inLongBoxCache(value)) {
getCachedLongBox(value)
} else {
LongBox(value)
}
@Frozen
class FloatBox(val value: Float) : Number(), Comparable<Float> {
override fun equals(other: Any?): Boolean {
if (other !is FloatBox) {
return false
}
return this.value.equals(other.value)
}
override fun hashCode() = value.hashCode()
override fun toString() = value.toString()
override fun compareTo(other: Float): Int = value.compareTo(other)
override fun toByte() = value.toByte()
override fun toChar() = value.toChar()
override fun toShort() = value.toShort()
override fun toInt() = value.toInt()
override fun toLong() = value.toLong()
override fun toFloat() = value.toFloat()
override fun toDouble() = value.toDouble()
}
fun boxLong(value: Long): Long? = value
@ExportForCppRuntime("Kotlin_boxFloat")
fun boxFloat(value: Float) = FloatBox(value)
@Frozen
class DoubleBox(val value: Double) : Number(), Comparable<Double> {
override fun equals(other: Any?): Boolean {
if (other !is DoubleBox) {
return false
}
return this.value.equals(other.value)
}
override fun hashCode() = value.hashCode()
override fun toString() = value.toString()
override fun compareTo(other: Double): Int = value.compareTo(other)
override fun toByte() = value.toByte()
override fun toChar() = value.toChar()
override fun toShort() = value.toShort()
override fun toInt() = value.toInt()
override fun toLong() = value.toLong()
override fun toFloat() = value.toFloat()
override fun toDouble() = value.toDouble()
}
fun boxFloat(value: Float): Float? = value
@ExportForCppRuntime("Kotlin_boxDouble")
fun boxDouble(value: Double) = DoubleBox(value)
fun boxDouble(value: Double): Double? = value
@@ -20,16 +20,20 @@ import kotlinx.cinterop.CPointer
import kotlinx.cinterop.NativePointed
import kotlinx.cinterop.NativePtr
@Intrinsic external fun areEqualByValue(first: Boolean, second: Boolean): Boolean
@Intrinsic external fun areEqualByValue(first: Char, second: Char): Boolean
@Intrinsic external fun areEqualByValue(first: Byte, second: Byte): Boolean
@Intrinsic external fun areEqualByValue(first: Short, second: Short): Boolean
@Intrinsic external fun areEqualByValue(first: Int, second: Int): Boolean
@Intrinsic external fun areEqualByValue(first: Long, second: Long): Boolean
@Intrinsic @PublishedApi external internal fun areEqualByValue(first: Boolean, second: Boolean): Boolean
@Intrinsic @PublishedApi external internal fun areEqualByValue(first: Byte, second: Byte): Boolean
@Intrinsic @PublishedApi external internal fun areEqualByValue(first: Short, second: Short): Boolean
@Intrinsic @PublishedApi external internal fun areEqualByValue(first: Int, second: Int): Boolean
@Intrinsic @PublishedApi external internal fun areEqualByValue(first: Long, second: Long): Boolean
@Intrinsic @PublishedApi external internal fun areEqualByValue(first: NativePtr, second: NativePtr): Boolean
@Intrinsic external fun ieee754Equals(first: Float, second: Float): Boolean
@Intrinsic external fun ieee754Equals(first: Double, second: Double): Boolean
// Bitwise equality:
@Intrinsic @PublishedApi external internal fun areEqualByValue(first: Float, second: Float): Boolean
@Intrinsic @PublishedApi external internal fun areEqualByValue(first: Double, second: Double): Boolean
@Intrinsic external fun areEqualByValue(first: NativePtr, second: NativePtr): Boolean
@Intrinsic external fun areEqualByValue(first: NativePointed?, second: NativePointed?): Boolean
@Intrinsic external fun areEqualByValue(first: CPointer<*>?, second: CPointer<*>?): Boolean
// IEEE754 equality:
@Intrinsic @PublishedApi external internal fun ieee754Equals(first: Float, second: Float): Boolean
@Intrinsic @PublishedApi external internal fun ieee754Equals(first: Double, second: Double): Boolean
// Reinterprets this value from T to R having the same binary representation (e.g. to unwrap inline class).
@Intrinsic @PublishedApi external internal fun <T, R> T.reinterpret(): R
@@ -18,7 +18,7 @@ package konan.internal
@Intrinsic external fun getNativeNullPtr(): NativePtr
class NativePtr private constructor() {
class NativePtr @PublishedApi internal constructor(private val value: NonNullNativePtr?) {
companion object {
val NULL = getNativeNullPtr()
}
@@ -33,3 +33,9 @@ class NativePtr private constructor() {
override fun toString() = "0x${this.toLong().toString(16)}"
}
@PublishedApi
internal inline class NonNullNativePtr(val value: NotNullPointerValue) { // TODO: refactor to use this type widely.
@Suppress("NOTHING_TO_INLINE")
inline fun toNativePtr() = NativePtr(this)
}
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2018 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
internal class BooleanValue
internal class ByteValue
internal class ShortValue
internal class IntValue
internal class LongValue
internal class FloatValue
internal class DoubleValue
internal class NotNullPointerValue
+1 -1
View File
@@ -20,7 +20,7 @@ package kotlin
* Represents a value which is either `true` or `false`. On the JVM, non-nullable values of this type are
* represented as values of the primitive type `boolean`.
*/
public final class Boolean : Comparable<Boolean> {
public final class Boolean private constructor(private val value: konan.internal.BooleanValue) : Comparable<Boolean> {
/**
* Returns the inverse of this boolean.
*/
+3 -3
View File
@@ -20,7 +20,7 @@ package kotlin
* Represents a 16-bit Unicode character.
* On the JVM, non-nullable values of this type are represented as values of the primitive type `char`.
*/
public final class Char : Comparable<Char> {
public final class Char private constructor(private val value: konan.internal.ShortValue) : Comparable<Char> {
/**
* Compares this value with the specified value for order.
@@ -133,10 +133,10 @@ public final class Char : Comparable<Char> {
}
// Konan-specific.
public fun equals(other: Char): Boolean = konan.internal.areEqualByValue(this, other)
public fun equals(other: Char): Boolean = this == other
public override fun equals(other: Any?): Boolean =
other is Char && konan.internal.areEqualByValue(this, other)
other is Char && this.equals(other)
@SymbolName("Kotlin_Char_toString")
external public override fun toString(): String
+6 -6
View File
@@ -22,7 +22,7 @@ import konan.internal.NumberConverter
* Represents a 8-bit signed integer.
* On the JVM, non-nullable values of this type are represented as values of the primitive type `byte`.
*/
public final class Byte : Number(), Comparable<Byte> {
public final class Byte private constructor(private val value: konan.internal.ByteValue) : Number(), Comparable<Byte> {
companion object {
/**
* A constant holding the minimum value an instance of Byte can have.
@@ -236,7 +236,7 @@ public final class Byte : Number(), Comparable<Byte> {
* Represents a 16-bit signed integer.
* On the JVM, non-nullable values of this type are represented as values of the primitive type `short`.
*/
public final class Short : Number(), Comparable<Short> {
public final class Short private constructor(private val value: konan.internal.ShortValue) : Number(), Comparable<Short> {
companion object {
/**
* A constant holding the minimum value an instance of Short can have.
@@ -450,7 +450,7 @@ public final class Short : Number(), Comparable<Short> {
* Represents a 32-bit signed integer.
* On the JVM, non-nullable values of this type are represented as values of the primitive type `int`.
*/
public final class Int : Number(), Comparable<Int> {
public final class Int private constructor(private val value: konan.internal.IntValue) : Number(), Comparable<Int> {
companion object {
/**
* A constant holding the minimum value an instance of Int can have.
@@ -686,7 +686,7 @@ public final class Int : Number(), Comparable<Int> {
* Represents a 64-bit signed integer.
* On the JVM, non-nullable values of this type are represented as values of the primitive type `long`.
*/
public final class Long : Number(), Comparable<Long> {
public final class Long private constructor(private val value: konan.internal.LongValue) : Number(), Comparable<Long> {
companion object {
/**
* A constant holding the minimum value an instance of Long can have.
@@ -922,7 +922,7 @@ public final class Long : Number(), Comparable<Long> {
* Represents a single-precision 32-bit IEEE 754 floating point number.
* On the JVM, non-nullable values of this type are represented as values of the primitive type `float`.
*/
public final class Float : Number(), Comparable<Float> {
public final class Float private constructor(private val value: konan.internal.FloatValue) : Number(), Comparable<Float> {
companion object {
/**
* A constant holding the smallest *positive* nonzero value of Float.
@@ -1141,7 +1141,7 @@ public final class Float : Number(), Comparable<Float> {
* Represents a double-precision 64-bit IEEE 754 floating point number.
* On the JVM, non-nullable values of this type are represented as values of the primitive type `double`.
*/
public final class Double : Number(), Comparable<Double> {
public final class Double private constructor(private val value: konan.internal.DoubleValue) : Number(), Comparable<Double> {
companion object {
/**
* A constant holding the smallest *positive* nonzero value of Double.