Support inline classes (#1840)
This commit is contained in:
committed by
GitHub
parent
699d530e87
commit
cd4f116f78
@@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+31
@@ -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
|
||||
}
|
||||
+195
-60
@@ -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
|
||||
}
|
||||
+49
-35
@@ -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
|
||||
|
||||
+45
-4
@@ -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"))
|
||||
}
|
||||
|
||||
+298
@@ -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
|
||||
|
||||
}
|
||||
+1
-3
@@ -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)
|
||||
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+2
@@ -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")
|
||||
}
|
||||
|
||||
-171
@@ -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)
|
||||
}
|
||||
+15
-20
@@ -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 }
|
||||
|
||||
|
||||
+10
-24
@@ -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)
|
||||
|
||||
+2
-2
@@ -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 -> ""
|
||||
}
|
||||
|
||||
+15
-23
@@ -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 {
|
||||
|
||||
+23
-20
@@ -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)!!
|
||||
}
|
||||
}
|
||||
|
||||
+2
-7
@@ -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
|
||||
|
||||
|
||||
+5
-11
@@ -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() }
|
||||
)
|
||||
|
||||
+14
-6
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
+309
-62
@@ -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])
|
||||
}
|
||||
}
|
||||
}
|
||||
+124
-71
@@ -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))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-5
@@ -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 {
|
||||
|
||||
+4
-3
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
+57
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+25
-12
@@ -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))
|
||||
|
||||
+8
-11
@@ -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")
|
||||
|
||||
;
|
||||
|
||||
|
||||
+19
-59
@@ -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) {
|
||||
|
||||
+10
-9
@@ -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
|
||||
}
|
||||
|
||||
+40
-20
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+15
-3
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user