Make String and box types frozen by default, more freeze checks (#1645)
This commit is contained in:
+7
-4
@@ -18,13 +18,9 @@ package org.jetbrains.kotlin.backend.konan.descriptors
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
|
import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
|
||||||
import org.jetbrains.kotlin.backend.konan.isValueType
|
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
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||||
import org.jetbrains.kotlin.ir.util.simpleFunctions
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.types.SimpleType
|
import org.jetbrains.kotlin.types.SimpleType
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||||
@@ -87,11 +83,18 @@ internal fun IrSimpleFunction.resolveFakeOverride(): IrSimpleFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val intrinsicAnnotation = FqName("konan.internal.Intrinsic")
|
private val intrinsicAnnotation = FqName("konan.internal.Intrinsic")
|
||||||
|
private val immutableAnnotation = FqName("konan.internal.Immutable")
|
||||||
|
|
||||||
// TODO: don't forget to remove descriptor access here.
|
// TODO: don't forget to remove descriptor access here.
|
||||||
internal val FunctionDescriptor.isIntrinsic: Boolean
|
internal val FunctionDescriptor.isIntrinsic: Boolean
|
||||||
get() = this.descriptor.annotations.hasAnnotation(intrinsicAnnotation)
|
get() = this.descriptor.annotations.hasAnnotation(intrinsicAnnotation)
|
||||||
|
|
||||||
|
internal val org.jetbrains.kotlin.descriptors.DeclarationDescriptor.isImmutable: Boolean
|
||||||
|
get() = this.annotations.hasAnnotation(immutableAnnotation)
|
||||||
|
|
||||||
|
internal val DeclarationDescriptor.isImmutable: Boolean
|
||||||
|
get() = this.descriptor.isImmutable
|
||||||
|
|
||||||
private val intrinsicTypes = setOf(
|
private val intrinsicTypes = setOf(
|
||||||
"kotlin.Boolean", "kotlin.Char",
|
"kotlin.Boolean", "kotlin.Char",
|
||||||
"kotlin.Byte", "kotlin.Short",
|
"kotlin.Byte", "kotlin.Short",
|
||||||
|
|||||||
+14
-7
@@ -42,6 +42,7 @@ import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
|||||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
@@ -1397,8 +1398,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
val thisPtr = evaluateExpression(value.receiver!!)
|
val thisPtr = evaluateExpression(value.receiver!!)
|
||||||
return functionGenerationContext.loadSlot(
|
return functionGenerationContext.loadSlot(
|
||||||
fieldPtrOfClass(thisPtr, value.symbol.owner), value.descriptor.isVar())
|
fieldPtrOfClass(thisPtr, value.symbol.owner), value.descriptor.isVar())
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
assert (value.receiver == null)
|
assert (value.receiver == null)
|
||||||
val ptr = context.llvmDeclarations.forStaticField(value.symbol.owner).storage
|
val ptr = context.llvmDeclarations.forStaticField(value.symbol.owner).storage
|
||||||
return functionGenerationContext.loadSlot(ptr, value.descriptor.isVar())
|
return functionGenerationContext.loadSlot(ptr, value.descriptor.isVar())
|
||||||
@@ -1406,18 +1406,25 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
// TODO: rewrite in IR!
|
||||||
|
private fun needMutationCheck(descriptor: org.jetbrains.kotlin.descriptors.DeclarationDescriptor): Boolean {
|
||||||
|
// For now we omit mutation checks on immutable types, as this allows initialization in constructor
|
||||||
|
// and it is assumed that API doesn't allow to change them.
|
||||||
|
return !descriptor.isImmutable
|
||||||
|
}
|
||||||
|
|
||||||
private fun evaluateSetField(value: IrSetField): LLVMValueRef {
|
private fun evaluateSetField(value: IrSetField): LLVMValueRef {
|
||||||
context.log{"evaluateSetField : ${ir2string(value)}"}
|
context.log{"evaluateSetField : ${ir2string(value)}"}
|
||||||
val valueToAssign = evaluateExpression(value.value)
|
val valueToAssign = evaluateExpression(value.value)
|
||||||
if (value.descriptor.dispatchReceiverParameter != null) {
|
if (value.descriptor.dispatchReceiverParameter != null) {
|
||||||
val thisPtr = evaluateExpression(value.receiver!!)
|
val thisPtr = evaluateExpression(value.receiver!!)
|
||||||
functionGenerationContext.call(context.llvm.mutationCheck,
|
if (needMutationCheck(value.descriptor.containingDeclaration)) {
|
||||||
listOf(functionGenerationContext.bitcast(codegen.kObjHeaderPtr, thisPtr)),
|
functionGenerationContext.call(context.llvm.mutationCheck,
|
||||||
Lifetime.IRRELEVANT, ExceptionHandler.Caller)
|
listOf(functionGenerationContext.bitcast(codegen.kObjHeaderPtr, thisPtr)),
|
||||||
|
Lifetime.IRRELEVANT, ExceptionHandler.Caller)
|
||||||
|
}
|
||||||
functionGenerationContext.storeAny(valueToAssign, fieldPtrOfClass(thisPtr, value.symbol.owner))
|
functionGenerationContext.storeAny(valueToAssign, fieldPtrOfClass(thisPtr, value.symbol.owner))
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
assert (value.receiver == null)
|
assert (value.receiver == null)
|
||||||
val globalValue = context.llvmDeclarations.forStaticField(value.symbol.owner).storage
|
val globalValue = context.llvmDeclarations.forStaticField(value.symbol.owner).storage
|
||||||
functionGenerationContext.storeAny(valueToAssign, globalValue)
|
functionGenerationContext.storeAny(valueToAssign, globalValue)
|
||||||
|
|||||||
+12
@@ -29,6 +29,13 @@ import org.jetbrains.kotlin.resolve.constants.StringValue
|
|||||||
|
|
||||||
internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||||
|
|
||||||
|
private fun flagsFromClass(classDescriptor: ClassDescriptor): Int {
|
||||||
|
var result = 0
|
||||||
|
if (classDescriptor.isImmutable)
|
||||||
|
result = result or 1 /* TF_IMMUTABLE */
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
private inner class FieldTableRecord(val nameSignature: LocalHash, val fieldOffset: Int) :
|
private inner class FieldTableRecord(val nameSignature: LocalHash, val fieldOffset: Int) :
|
||||||
Struct(runtime.fieldTableRecordType, nameSignature, Int32(fieldOffset))
|
Struct(runtime.fieldTableRecordType, nameSignature, Int32(fieldOffset))
|
||||||
|
|
||||||
@@ -50,6 +57,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
fieldsCount: Int,
|
fieldsCount: Int,
|
||||||
packageName: String?,
|
packageName: String?,
|
||||||
relativeName: String?,
|
relativeName: String?,
|
||||||
|
flags: Int,
|
||||||
extendedInfo: ConstPointer,
|
extendedInfo: ConstPointer,
|
||||||
writableTypeInfo: ConstPointer?) :
|
writableTypeInfo: ConstPointer?) :
|
||||||
|
|
||||||
@@ -78,6 +86,8 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
kotlinStringLiteral(packageName),
|
kotlinStringLiteral(packageName),
|
||||||
kotlinStringLiteral(relativeName),
|
kotlinStringLiteral(relativeName),
|
||||||
|
|
||||||
|
Int32(flags),
|
||||||
|
|
||||||
extendedInfo,
|
extendedInfo,
|
||||||
|
|
||||||
*listOfNotNull(writableTypeInfo).toTypedArray()
|
*listOfNotNull(writableTypeInfo).toTypedArray()
|
||||||
@@ -203,6 +213,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
fieldsPtr, if (classDesc.isInterface) -1 else fields.size,
|
fieldsPtr, if (classDesc.isInterface) -1 else fields.size,
|
||||||
reflectionInfo.packageName,
|
reflectionInfo.packageName,
|
||||||
reflectionInfo.relativeName,
|
reflectionInfo.relativeName,
|
||||||
|
flagsFromClass(classDesc),
|
||||||
makeExtendedInfo(classDesc),
|
makeExtendedInfo(classDesc),
|
||||||
llvmDeclarations.writableTypeInfoGlobal?.pointer
|
llvmDeclarations.writableTypeInfoGlobal?.pointer
|
||||||
)
|
)
|
||||||
@@ -345,6 +356,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
fields = fieldsPtr, fieldsCount = fieldsCount,
|
fields = fieldsPtr, fieldsCount = fieldsCount,
|
||||||
packageName = reflectionInfo.packageName,
|
packageName = reflectionInfo.packageName,
|
||||||
relativeName = reflectionInfo.relativeName,
|
relativeName = reflectionInfo.relativeName,
|
||||||
|
flags = flagsFromClass(descriptor),
|
||||||
extendedInfo = NullPointer(runtime.extendedTypeInfoType),
|
extendedInfo = NullPointer(runtime.extendedTypeInfoType),
|
||||||
writableTypeInfo = writableTypeInfo
|
writableTypeInfo = writableTypeInfo
|
||||||
), vtable)
|
), vtable)
|
||||||
|
|||||||
+2
-2
@@ -199,7 +199,7 @@ internal class TestProcessor (val context: KonanBackendContext) {
|
|||||||
element.acceptChildrenVoid(this)
|
element.acceptChildrenVoid(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun IrFunctionSymbol.hasAnnotatoin(fqName: FqName) = descriptor.annotations.any { it.fqName == fqName }
|
fun IrFunctionSymbol.hasAnnotation(fqName: FqName) = descriptor.annotations.any { it.fqName == fqName }
|
||||||
|
|
||||||
fun registerClassFunction(classDescriptor: ClassDescriptor,
|
fun registerClassFunction(classDescriptor: ClassDescriptor,
|
||||||
function: IrFunctionSymbol,
|
function: IrFunctionSymbol,
|
||||||
@@ -263,7 +263,7 @@ internal class TestProcessor (val context: KonanBackendContext) {
|
|||||||
val symbol = declaration.symbol
|
val symbol = declaration.symbol
|
||||||
val owner = declaration.descriptor.containingDeclaration
|
val owner = declaration.descriptor.containingDeclaration
|
||||||
|
|
||||||
val kinds = FunctionKind.values().filter { symbol.hasAnnotatoin(it.annotationFqName) }
|
val kinds = FunctionKind.values().filter { symbol.hasAnnotation(it.annotationFqName) }
|
||||||
if (kinds.isEmpty()) {
|
if (kinds.isEmpty()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -663,6 +663,14 @@ task freeze_stress(type: RunKonanTest) {
|
|||||||
source = "runtime/workers/freeze_stress.kt"
|
source = "runtime/workers/freeze_stress.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task freeze2(type: RunKonanTest) {
|
||||||
|
disabled = (project.testTarget == 'wasm32') // No exceptions on WASM.
|
||||||
|
goldValue =
|
||||||
|
"Worker 1: Hello world\n" + "Worker2: 42\n" +
|
||||||
|
"Worker3: 239.0\n" + "Worker4: a\n" + "OK\n"
|
||||||
|
source = "runtime/workers/freeze2.kt"
|
||||||
|
}
|
||||||
|
|
||||||
task enumIdentity(type: RunKonanTest) {
|
task enumIdentity(type: RunKonanTest) {
|
||||||
disabled = (project.testTarget == 'wasm32') // Workers need pthreads.
|
disabled = (project.testTarget == 'wasm32') // Workers need pthreads.
|
||||||
goldValue = "true\n"
|
goldValue = "true\n"
|
||||||
|
|||||||
@@ -38,6 +38,6 @@ data class SharedData(val string: String, val int: Int, val member: SharedDataMe
|
|||||||
future.consume {
|
future.consume {
|
||||||
result -> println("Main: $result")
|
result -> println("Main: $result")
|
||||||
}
|
}
|
||||||
worker.requestTermination().consume { _ -> }
|
worker.requestTermination().result()
|
||||||
println("OK")
|
println("OK")
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* 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 runtime.workers.freeze2
|
||||||
|
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
import konan.worker.*
|
||||||
|
|
||||||
|
data class Data(var int: Int)
|
||||||
|
|
||||||
|
@Test fun runTest() {
|
||||||
|
// Ensure that we can not mutate frozen objects and arrays.
|
||||||
|
val a0 = Data(2)
|
||||||
|
a0.int++
|
||||||
|
a0.freeze()
|
||||||
|
assertFailsWith<InvalidMutabilityException> {a0.int++ }
|
||||||
|
|
||||||
|
val a1 = ByteArray(2)
|
||||||
|
a1[1]++
|
||||||
|
a1.freeze()
|
||||||
|
assertFailsWith<InvalidMutabilityException> { a1[1]++ }
|
||||||
|
|
||||||
|
val a2 = ShortArray(2)
|
||||||
|
a2[1]++
|
||||||
|
a2.freeze()
|
||||||
|
assertFailsWith<InvalidMutabilityException> { a2[1]++ }
|
||||||
|
|
||||||
|
val a3 = IntArray(2)
|
||||||
|
a3[1]++
|
||||||
|
a3.freeze()
|
||||||
|
assertFailsWith<InvalidMutabilityException> { a3[1]++ }
|
||||||
|
|
||||||
|
val a4 = LongArray(2)
|
||||||
|
a4[1]++
|
||||||
|
a4.freeze()
|
||||||
|
assertFailsWith<InvalidMutabilityException> { a4[1]++ }
|
||||||
|
|
||||||
|
val a5 = BooleanArray(2)
|
||||||
|
a5[1] = true
|
||||||
|
a5.freeze()
|
||||||
|
assertFailsWith<InvalidMutabilityException> { a5[1] = false }
|
||||||
|
|
||||||
|
val a6 = CharArray(2)
|
||||||
|
a6[1] = 'a'
|
||||||
|
a6.freeze()
|
||||||
|
assertFailsWith<InvalidMutabilityException> { a6[1] = 'b' }
|
||||||
|
|
||||||
|
val a7 = FloatArray(2)
|
||||||
|
a7[1] = 1.0f
|
||||||
|
a7.freeze()
|
||||||
|
assertFailsWith<InvalidMutabilityException> { a7[1] = 2.0f }
|
||||||
|
|
||||||
|
val a8 = DoubleArray(2)
|
||||||
|
a8[1] = 1.0
|
||||||
|
a8.freeze()
|
||||||
|
assertFailsWith<InvalidMutabilityException> { a8[1] = 2.0 }
|
||||||
|
|
||||||
|
// Ensure that String and integral boxes are frozen by default, by passing local to the worker.
|
||||||
|
val worker = startWorker()
|
||||||
|
var data: Any = "Hello" + " " + "world"
|
||||||
|
assert(data.isFrozen)
|
||||||
|
worker.schedule(TransferMode.CHECKED, { data } ) {
|
||||||
|
input -> println("Worker 1: $input")
|
||||||
|
}.result()
|
||||||
|
|
||||||
|
data = 42
|
||||||
|
assert(data.isFrozen)
|
||||||
|
worker.schedule(TransferMode.CHECKED, { data } ) {
|
||||||
|
input -> println("Worker2: $input")
|
||||||
|
}.result()
|
||||||
|
|
||||||
|
data = 239.0
|
||||||
|
assert(data.isFrozen)
|
||||||
|
worker.schedule(TransferMode.CHECKED, { data } ) {
|
||||||
|
input -> println("Worker3: $input")
|
||||||
|
}.result()
|
||||||
|
|
||||||
|
data = 'a'
|
||||||
|
assert(data.isFrozen)
|
||||||
|
worker.schedule(TransferMode.CHECKED, { data } ) {
|
||||||
|
input -> println("Worker4: $input")
|
||||||
|
}.result()
|
||||||
|
|
||||||
|
worker.requestTermination().result()
|
||||||
|
|
||||||
|
println("OK")
|
||||||
|
}
|
||||||
@@ -4,7 +4,8 @@ import kotlin.test.*
|
|||||||
|
|
||||||
import konan.worker.*
|
import konan.worker.*
|
||||||
|
|
||||||
data class WorkerArgument(val intParam: Int, val stringParam: String)
|
data class DataParam(var int: Int)
|
||||||
|
data class WorkerArgument(val intParam: Int, val dataParam: DataParam)
|
||||||
data class WorkerResult(val intResult: Int, val stringResult: String)
|
data class WorkerResult(val intResult: Int, val stringResult: String)
|
||||||
|
|
||||||
@Test fun runTest() {
|
@Test fun runTest() {
|
||||||
@@ -13,19 +14,18 @@ data class WorkerResult(val intResult: Int, val stringResult: String)
|
|||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
val worker = startWorker()
|
val worker = startWorker()
|
||||||
val s = "zzz${args.size.toString()}"
|
val dataParam = DataParam(17)
|
||||||
|
|
||||||
val future = try {
|
val future = try {
|
||||||
worker.schedule(TransferMode.CHECKED,
|
worker.schedule(TransferMode.CHECKED,
|
||||||
{ WorkerArgument(42, s) },
|
{ WorkerArgument(42, dataParam) },
|
||||||
{ input -> WorkerResult(input.intParam, input.stringParam + " result") }
|
{ input -> WorkerResult(input.intParam, input.dataParam.toString() + " result") }
|
||||||
)
|
)
|
||||||
} catch (e: IllegalStateException) {
|
} catch (e: IllegalStateException) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
if (future != null)
|
if (future != null)
|
||||||
println("Fail 1")
|
println("Fail 1")
|
||||||
if (s != "zzz0") println("Fail 2")
|
if (dataParam.int != 17) println("Fail 2")
|
||||||
worker.requestTermination().consume { _ -> }
|
worker.requestTermination().consume { _ -> }
|
||||||
println("OK")
|
println("OK")
|
||||||
}
|
}
|
||||||
@@ -23,9 +23,22 @@
|
|||||||
#include "Natives.h"
|
#include "Natives.h"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const ArrayHeader anEmptyArray = {
|
||||||
|
const_cast<TypeInfo*>(theArrayTypeInfo), /* permanent object */ 0, /* element count */ 0
|
||||||
|
};
|
||||||
|
|
||||||
|
ALWAYS_INLINE inline void mutabilityCheck(KConstRef thiz) {
|
||||||
|
// TODO: optimize it!
|
||||||
|
if (thiz->container()->frozen()) {
|
||||||
|
ThrowInvalidMutabilityException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static inline void copyImpl(KConstRef thiz, KInt fromIndex,
|
inline void copyImpl(KConstRef thiz, KInt fromIndex,
|
||||||
KRef destination, KInt toIndex, KInt count) {
|
KRef destination, KInt toIndex, KInt count) {
|
||||||
const ArrayHeader* array = thiz->array();
|
const ArrayHeader* array = thiz->array();
|
||||||
ArrayHeader* destinationArray = destination->array();
|
ArrayHeader* destinationArray = destination->array();
|
||||||
if (count < 0 ||
|
if (count < 0 ||
|
||||||
@@ -33,18 +46,12 @@ static inline void copyImpl(KConstRef thiz, KInt fromIndex,
|
|||||||
toIndex < 0 || count > destinationArray->count_ - toIndex) {
|
toIndex < 0 || count > destinationArray->count_ - toIndex) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(destination);
|
||||||
memmove(PrimitiveArrayAddressOfElementAt<T>(destinationArray, toIndex),
|
memmove(PrimitiveArrayAddressOfElementAt<T>(destinationArray, toIndex),
|
||||||
PrimitiveArrayAddressOfElementAt<T>(array, fromIndex),
|
PrimitiveArrayAddressOfElementAt<T>(array, fromIndex),
|
||||||
count * sizeof(T));
|
count * sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
const ArrayHeader anEmptyArray = {
|
|
||||||
const_cast<TypeInfo*>(theArrayTypeInfo), /* permanent object */ 0, /* element count */ 0
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -65,6 +72,7 @@ void Kotlin_Array_set(KRef thiz, KInt index, KConstRef value) {
|
|||||||
if (static_cast<uint32_t>(index) >= array->count_) {
|
if (static_cast<uint32_t>(index) >= array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
UpdateRef(ArrayAddressOfElementAt(array, index), value);
|
UpdateRef(ArrayAddressOfElementAt(array, index), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,6 +86,7 @@ void Kotlin_Array_fillImpl(KRef thiz, KInt fromIndex, KInt toIndex, KRef value)
|
|||||||
if (fromIndex < 0 || toIndex < fromIndex || toIndex > array->count_) {
|
if (fromIndex < 0 || toIndex < fromIndex || toIndex > array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
for (KInt index = fromIndex; index < toIndex; ++index) {
|
for (KInt index = fromIndex; index < toIndex; ++index) {
|
||||||
UpdateRef(ArrayAddressOfElementAt(array, index), value);
|
UpdateRef(ArrayAddressOfElementAt(array, index), value);
|
||||||
}
|
}
|
||||||
@@ -92,6 +101,7 @@ void Kotlin_Array_copyImpl(KConstRef thiz, KInt fromIndex,
|
|||||||
toIndex < 0 || count > destinationArray->count_ - toIndex) {
|
toIndex < 0 || count > destinationArray->count_ - toIndex) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(destination);
|
||||||
if (fromIndex >= toIndex) {
|
if (fromIndex >= toIndex) {
|
||||||
for (int index = 0; index < count; index++) {
|
for (int index = 0; index < count; index++) {
|
||||||
UpdateRef(ArrayAddressOfElementAt(destinationArray, toIndex + index),
|
UpdateRef(ArrayAddressOfElementAt(destinationArray, toIndex + index),
|
||||||
@@ -123,6 +133,7 @@ void Kotlin_ByteArray_set(KRef thiz, KInt index, KByte value) {
|
|||||||
if (static_cast<uint32_t>(index) >= array->count_) {
|
if (static_cast<uint32_t>(index) >= array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
*ByteArrayAddressOfElementAt(array, index) = value;
|
*ByteArrayAddressOfElementAt(array, index) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,6 +195,7 @@ void Kotlin_ByteArray_setCharAt(KRef thiz, KInt index, KChar value) {
|
|||||||
if (static_cast<uint32_t>(index + 1) >= array->count_) {
|
if (static_cast<uint32_t>(index + 1) >= array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
*reinterpret_cast<KChar*>(ByteArrayAddressOfElementAt(array, index)) = value;
|
*reinterpret_cast<KChar*>(ByteArrayAddressOfElementAt(array, index)) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,6 +204,7 @@ void Kotlin_ByteArray_setShortAt(KRef thiz, KInt index, KShort value) {
|
|||||||
if (static_cast<uint32_t>(index + 1) >= array->count_) {
|
if (static_cast<uint32_t>(index + 1) >= array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
*reinterpret_cast<KShort*>(ByteArrayAddressOfElementAt(array, index)) = value;
|
*reinterpret_cast<KShort*>(ByteArrayAddressOfElementAt(array, index)) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,6 +213,7 @@ void Kotlin_ByteArray_setIntAt(KRef thiz, KInt index, KInt value) {
|
|||||||
if (static_cast<uint32_t>(index + 3) >= array->count_) {
|
if (static_cast<uint32_t>(index + 3) >= array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
*reinterpret_cast<KInt*>(ByteArrayAddressOfElementAt(array, index)) = value;
|
*reinterpret_cast<KInt*>(ByteArrayAddressOfElementAt(array, index)) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,6 +222,7 @@ void Kotlin_ByteArray_setLongAt(KRef thiz, KInt index, KLong value) {
|
|||||||
if (static_cast<uint32_t>(index + 7) >= array->count_) {
|
if (static_cast<uint32_t>(index + 7) >= array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
*reinterpret_cast<KLong*>(ByteArrayAddressOfElementAt(array, index)) = value;
|
*reinterpret_cast<KLong*>(ByteArrayAddressOfElementAt(array, index)) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,6 +231,7 @@ void Kotlin_ByteArray_setFloatAt(KRef thiz, KInt index, KFloat value) {
|
|||||||
if (static_cast<uint32_t>(index + 3) >= array->count_) {
|
if (static_cast<uint32_t>(index + 3) >= array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
*reinterpret_cast<KFloat*>(ByteArrayAddressOfElementAt(array, index)) = value;
|
*reinterpret_cast<KFloat*>(ByteArrayAddressOfElementAt(array, index)) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,6 +240,7 @@ void Kotlin_ByteArray_setDoubleAt(KRef thiz, KInt index, KDouble value) {
|
|||||||
if (static_cast<uint32_t>(index + 7) >= array->count_) {
|
if (static_cast<uint32_t>(index + 7) >= array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
*reinterpret_cast<KDouble*>(ByteArrayAddressOfElementAt(array, index)) = value;
|
*reinterpret_cast<KDouble*>(ByteArrayAddressOfElementAt(array, index)) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,11 +257,15 @@ void Kotlin_CharArray_set(KRef thiz, KInt index, KChar value) {
|
|||||||
if (static_cast<uint32_t>(index) >= array->count_) {
|
if (static_cast<uint32_t>(index) >= array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
*PrimitiveArrayAddressOfElementAt<KChar>(array, index) = value;
|
*PrimitiveArrayAddressOfElementAt<KChar>(array, index) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
OBJ_GETTER(Kotlin_CharArray_copyOf, KConstRef thiz, KInt newSize) {
|
OBJ_GETTER(Kotlin_CharArray_copyOf, KConstRef thiz, KInt newSize) {
|
||||||
const ArrayHeader* array = thiz->array();
|
const ArrayHeader* array = thiz->array();
|
||||||
|
if (newSize < 0) {
|
||||||
|
ThrowIllegalArgumentException();
|
||||||
|
}
|
||||||
ArrayHeader* result = AllocArrayInstance(
|
ArrayHeader* result = AllocArrayInstance(
|
||||||
array->type_info(), newSize, OBJ_RESULT)->array();
|
array->type_info(), newSize, OBJ_RESULT)->array();
|
||||||
KInt toCopy = array->count_ < newSize ? array->count_ : newSize;
|
KInt toCopy = array->count_ < newSize ? array->count_ : newSize;
|
||||||
@@ -273,6 +294,7 @@ void Kotlin_ShortArray_set(KRef thiz, KInt index, KShort value) {
|
|||||||
if (static_cast<uint32_t>(index) >= array->count_) {
|
if (static_cast<uint32_t>(index) >= array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
*PrimitiveArrayAddressOfElementAt<KShort>(array, index) = value;
|
*PrimitiveArrayAddressOfElementAt<KShort>(array, index) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,6 +316,7 @@ void Kotlin_IntArray_set(KRef thiz, KInt index, KInt value) {
|
|||||||
if (static_cast<uint32_t>(index) >= array->count_) {
|
if (static_cast<uint32_t>(index) >= array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
*PrimitiveArrayAddressOfElementAt<KInt>(array, index) = value;
|
*PrimitiveArrayAddressOfElementAt<KInt>(array, index) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,6 +330,7 @@ void Kotlin_IntArray_fillImpl(KRef thiz, KInt fromIndex, KInt toIndex, KInt valu
|
|||||||
if (fromIndex < 0 || toIndex < fromIndex || toIndex >= array->count_) {
|
if (fromIndex < 0 || toIndex < fromIndex || toIndex >= array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
for (KInt index = fromIndex; index < toIndex; ++index) {
|
for (KInt index = fromIndex; index < toIndex; ++index) {
|
||||||
*PrimitiveArrayAddressOfElementAt<KInt>(array, index) = value;
|
*PrimitiveArrayAddressOfElementAt<KInt>(array, index) = value;
|
||||||
}
|
}
|
||||||
@@ -366,6 +390,7 @@ void Kotlin_LongArray_set(KRef thiz, KInt index, KLong value) {
|
|||||||
if (static_cast<uint32_t>(index) >= array->count_) {
|
if (static_cast<uint32_t>(index) >= array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
*PrimitiveArrayAddressOfElementAt<KLong>(array, index) = value;
|
*PrimitiveArrayAddressOfElementAt<KLong>(array, index) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,6 +412,7 @@ void Kotlin_FloatArray_set(KRef thiz, KInt index, KFloat value) {
|
|||||||
if (static_cast<uint32_t>(index) >= array->count_) {
|
if (static_cast<uint32_t>(index) >= array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
*PrimitiveArrayAddressOfElementAt<KFloat>(array, index) = value;
|
*PrimitiveArrayAddressOfElementAt<KFloat>(array, index) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,6 +434,7 @@ void Kotlin_DoubleArray_set(KRef thiz, KInt index, KDouble value) {
|
|||||||
if (static_cast<uint32_t>(index) >= array->count_) {
|
if (static_cast<uint32_t>(index) >= array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
*PrimitiveArrayAddressOfElementAt<KDouble>(array, index) = value;
|
*PrimitiveArrayAddressOfElementAt<KDouble>(array, index) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,6 +456,7 @@ void Kotlin_BooleanArray_set(KRef thiz, KInt index, KBoolean value) {
|
|||||||
if (static_cast<uint32_t>(index) >= array->count_) {
|
if (static_cast<uint32_t>(index) >= array->count_) {
|
||||||
ThrowArrayIndexOutOfBoundsException();
|
ThrowArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
mutabilityCheck(thiz);
|
||||||
*PrimitiveArrayAddressOfElementAt<KBoolean>(array, index) = value;
|
*PrimitiveArrayAddressOfElementAt<KBoolean>(array, index) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ void RUNTIME_NORETURN ThrowOutOfMemoryError();
|
|||||||
void RUNTIME_NORETURN ThrowNotImplementedError();
|
void RUNTIME_NORETURN ThrowNotImplementedError();
|
||||||
// Throws illegal character conversion exception (used in UTF8/UTF16 conversions).
|
// Throws illegal character conversion exception (used in UTF8/UTF16 conversions).
|
||||||
void RUNTIME_NORETURN ThrowIllegalCharacterConversionException();
|
void RUNTIME_NORETURN ThrowIllegalCharacterConversionException();
|
||||||
|
void RUNTIME_NORETURN ThrowIllegalArgumentException();
|
||||||
|
void RUNTIME_NORETURN ThrowInvalidMutabilityException();
|
||||||
// Prints out mesage of Throwable.
|
// Prints out mesage of Throwable.
|
||||||
void PrintThrowable(KRef);
|
void PrintThrowable(KRef);
|
||||||
|
|
||||||
|
|||||||
@@ -264,6 +264,9 @@ class Container {
|
|||||||
void SetHeader(ObjHeader* obj, const TypeInfo* type_info) {
|
void SetHeader(ObjHeader* obj, const TypeInfo* type_info) {
|
||||||
obj->container_ = header_;
|
obj->container_ = header_;
|
||||||
obj->typeInfoOrMeta_ = const_cast<TypeInfo*>(type_info);
|
obj->typeInfoOrMeta_ = const_cast<TypeInfo*>(type_info);
|
||||||
|
// Take into account typeInfo's immutability for ARC strategy.
|
||||||
|
if ((type_info->flags_ & TF_IMMUTABLE) != 0)
|
||||||
|
header_->refCount_ |= CONTAINER_TAG_FROZEN;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -341,6 +344,7 @@ class ArenaContainer {
|
|||||||
void setHeader(ObjHeader* obj, const TypeInfo* typeInfo) {
|
void setHeader(ObjHeader* obj, const TypeInfo* typeInfo) {
|
||||||
obj->container_ = currentChunk_->asHeader();
|
obj->container_ = currentChunk_->asHeader();
|
||||||
obj->typeInfoOrMeta_ = const_cast<TypeInfo*>(typeInfo);
|
obj->typeInfoOrMeta_ = const_cast<TypeInfo*>(typeInfo);
|
||||||
|
// Here we do not take into account typeInfo's immutability for ARC strategy, as there's no ARC.
|
||||||
}
|
}
|
||||||
|
|
||||||
ContainerChunk* currentChunk_;
|
ContainerChunk* currentChunk_;
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ enum Konan_RuntimeType {
|
|||||||
RT_BOOLEAN = 9
|
RT_BOOLEAN = 9
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Konan_TypeFlags {
|
||||||
|
TF_IMMUTABLE = 1 << 0
|
||||||
|
};
|
||||||
|
|
||||||
// Extended information about a type.
|
// Extended information about a type.
|
||||||
struct ExtendedTypeInfo {
|
struct ExtendedTypeInfo {
|
||||||
// Number of fields (negated Konan_RuntimeType for array types).
|
// Number of fields (negated Konan_RuntimeType for array types).
|
||||||
@@ -104,6 +108,9 @@ struct TypeInfo {
|
|||||||
// or `null` if the class is anonymous.
|
// or `null` if the class is anonymous.
|
||||||
ObjHeader* relativeName_;
|
ObjHeader* relativeName_;
|
||||||
|
|
||||||
|
// Various flags.
|
||||||
|
int32_t flags_;
|
||||||
|
|
||||||
// Extended RTTI.
|
// Extended RTTI.
|
||||||
const ExtendedTypeInfo* extendedInfo_;
|
const ExtendedTypeInfo* extendedInfo_;
|
||||||
|
|
||||||
|
|||||||
@@ -577,7 +577,7 @@ void Kotlin_Worker_freezeInternal(KRef object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KBoolean Kotlin_Worker_isFrozenInternal(KRef object) {
|
KBoolean Kotlin_Worker_isFrozenInternal(KRef object) {
|
||||||
return object == nullptr || object->container()->frozen();
|
return object == nullptr || object->container()->permanentOrFrozen();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import kotlinx.cinterop.*
|
|||||||
* An immutable compile-time array of bytes.
|
* An immutable compile-time array of bytes.
|
||||||
*/
|
*/
|
||||||
@ExportTypeInfo("theImmutableBinaryBlobTypeInfo")
|
@ExportTypeInfo("theImmutableBinaryBlobTypeInfo")
|
||||||
|
@Immutable
|
||||||
public final class ImmutableBinaryBlob private constructor() {
|
public final class ImmutableBinaryBlob private constructor() {
|
||||||
public val size: Int
|
public val size: Int
|
||||||
get() = getArrayLength()
|
get() = getArrayLength()
|
||||||
|
|||||||
@@ -58,3 +58,9 @@ annotation class ExportForCompiler
|
|||||||
@Retention(AnnotationRetention.BINARY)
|
@Retention(AnnotationRetention.BINARY)
|
||||||
annotation class InlineConstructor
|
annotation class InlineConstructor
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class is immutable and is frozen by default.
|
||||||
|
*/
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class Immutable
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package konan.internal
|
package konan.internal
|
||||||
|
|
||||||
|
|
||||||
@SymbolName("getCachedBooleanBox")
|
@SymbolName("getCachedBooleanBox")
|
||||||
external fun getCachedBooleanBox(value: Boolean): BooleanBox
|
external fun getCachedBooleanBox(value: Boolean): BooleanBox
|
||||||
@SymbolName("inBooleanBoxCache")
|
@SymbolName("inBooleanBoxCache")
|
||||||
@@ -42,6 +41,7 @@ external fun getCachedLongBox(value: Long): LongBox
|
|||||||
@SymbolName("inLongBoxCache")
|
@SymbolName("inLongBoxCache")
|
||||||
external fun inLongBoxCache(value: Long): Boolean
|
external fun inLongBoxCache(value: Long): Boolean
|
||||||
|
|
||||||
|
@Immutable
|
||||||
class BooleanBox(val value: Boolean) : Comparable<Boolean> {
|
class BooleanBox(val value: Boolean) : Comparable<Boolean> {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is BooleanBox) {
|
if (other !is BooleanBox) {
|
||||||
@@ -65,6 +65,7 @@ fun boxBoolean(value: Boolean) = if (inBooleanBoxCache(value)) {
|
|||||||
BooleanBox(value)
|
BooleanBox(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
class CharBox(val value: Char) : Comparable<Char> {
|
class CharBox(val value: Char) : Comparable<Char> {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is CharBox) {
|
if (other !is CharBox) {
|
||||||
@@ -88,6 +89,7 @@ fun boxChar(value: Char) = if (inCharBoxCache(value)) {
|
|||||||
CharBox(value)
|
CharBox(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
class ByteBox(val value: Byte) : Number(), Comparable<Byte> {
|
class ByteBox(val value: Byte) : Number(), Comparable<Byte> {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is ByteBox) {
|
if (other !is ByteBox) {
|
||||||
@@ -119,6 +121,7 @@ fun boxByte(value: Byte) = if (inByteBoxCache(value)) {
|
|||||||
ByteBox(value)
|
ByteBox(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
class ShortBox(val value: Short) : Number(), Comparable<Short> {
|
class ShortBox(val value: Short) : Number(), Comparable<Short> {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is ShortBox) {
|
if (other !is ShortBox) {
|
||||||
@@ -150,6 +153,7 @@ fun boxShort(value: Short) = if (inShortBoxCache(value)) {
|
|||||||
ShortBox(value)
|
ShortBox(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
class IntBox(val value: Int) : Number(), Comparable<Int> {
|
class IntBox(val value: Int) : Number(), Comparable<Int> {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is IntBox) {
|
if (other !is IntBox) {
|
||||||
@@ -181,6 +185,7 @@ fun boxInt(value: Int) = if (inIntBoxCache(value)) {
|
|||||||
IntBox(value)
|
IntBox(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
class LongBox(val value: Long) : Number(), Comparable<Long> {
|
class LongBox(val value: Long) : Number(), Comparable<Long> {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is LongBox) {
|
if (other !is LongBox) {
|
||||||
@@ -212,6 +217,7 @@ fun boxLong(value: Long) = if (inLongBoxCache(value)) {
|
|||||||
LongBox(value)
|
LongBox(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
class FloatBox(val value: Float) : Number(), Comparable<Float> {
|
class FloatBox(val value: Float) : Number(), Comparable<Float> {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is FloatBox) {
|
if (other !is FloatBox) {
|
||||||
@@ -239,6 +245,7 @@ class FloatBox(val value: Float) : Number(), Comparable<Float> {
|
|||||||
@ExportForCppRuntime("Kotlin_boxFloat")
|
@ExportForCppRuntime("Kotlin_boxFloat")
|
||||||
fun boxFloat(value: Float) = FloatBox(value)
|
fun boxFloat(value: Float) = FloatBox(value)
|
||||||
|
|
||||||
|
@Immutable
|
||||||
class DoubleBox(val value: Double) : Number(), Comparable<Double> {
|
class DoubleBox(val value: Double) : Number(), Comparable<Double> {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is DoubleBox) {
|
if (other !is DoubleBox) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package konan.internal
|
|||||||
|
|
||||||
import kotlinx.cinterop.*
|
import kotlinx.cinterop.*
|
||||||
|
|
||||||
|
@Immutable
|
||||||
class NativePtrBox(val value: NativePtr) {
|
class NativePtrBox(val value: NativePtr) {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is NativePtrBox) {
|
if (other !is NativePtrBox) {
|
||||||
@@ -34,6 +35,7 @@ class NativePtrBox(val value: NativePtr) {
|
|||||||
|
|
||||||
fun boxNativePtr(value: NativePtr) = NativePtrBox(value)
|
fun boxNativePtr(value: NativePtr) = NativePtrBox(value)
|
||||||
|
|
||||||
|
@Immutable
|
||||||
class NativePointedBox(val value: NativePointed) {
|
class NativePointedBox(val value: NativePointed) {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is NativePointedBox) {
|
if (other !is NativePointedBox) {
|
||||||
@@ -54,6 +56,7 @@ class NativePointedBox(val value: NativePointed) {
|
|||||||
fun boxNativePointed(value: NativePointed?) = if (value != null) NativePointedBox(value) else null
|
fun boxNativePointed(value: NativePointed?) = if (value != null) NativePointedBox(value) else null
|
||||||
fun unboxNativePointed(box: NativePointedBox?) = box?.value
|
fun unboxNativePointed(box: NativePointedBox?) = box?.value
|
||||||
|
|
||||||
|
@Immutable
|
||||||
class CPointerBox(val value: CPointer<CPointed>) : CValuesRef<CPointed>() {
|
class CPointerBox(val value: CPointer<CPointed>) : CValuesRef<CPointed>() {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is CPointerBox) {
|
if (other !is CPointerBox) {
|
||||||
|
|||||||
@@ -65,6 +65,11 @@ fun ThrowUninitializedPropertyAccessException(): Nothing {
|
|||||||
throw UninitializedPropertyAccessException()
|
throw UninitializedPropertyAccessException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExportForCppRuntime
|
||||||
|
internal fun ThrowIllegalArgumentException() : Nothing {
|
||||||
|
throw IllegalArgumentException()
|
||||||
|
}
|
||||||
|
|
||||||
@ExportForCppRuntime
|
@ExportForCppRuntime
|
||||||
internal fun ThrowNotImplementedError(): Nothing {
|
internal fun ThrowNotImplementedError(): Nothing {
|
||||||
throw NotImplementedError("An operation is not implemented.")
|
throw NotImplementedError("An operation is not implemented.")
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
package kotlin
|
package kotlin
|
||||||
|
|
||||||
|
|
||||||
@ExportTypeInfo("theStringTypeInfo")
|
@ExportTypeInfo("theStringTypeInfo")
|
||||||
|
@konan.internal.Immutable
|
||||||
public final class String : Comparable<String>, CharSequence {
|
public final class String : Comparable<String>, CharSequence {
|
||||||
public companion object {
|
public companion object {
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user