From afc7d1dd9ea9b6a9a7bf5cb8dbf080ca6ab64b9d Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Wed, 6 Jun 2018 11:59:16 +0300 Subject: [PATCH] Make String and box types frozen by default, more freeze checks (#1645) --- .../konan/descriptors/DescriptorUtils.kt | 11 +- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 21 ++-- .../backend/konan/llvm/RTTIGenerator.kt | 12 +++ .../backend/konan/lower/TestProcessor.kt | 4 +- backend.native/tests/build.gradle | 8 ++ .../tests/runtime/workers/freeze0.kt | 2 +- .../tests/runtime/workers/freeze2.kt | 101 ++++++++++++++++++ .../tests/runtime/workers/worker3.kt | 12 +-- runtime/src/main/cpp/Arrays.cpp | 46 ++++++-- runtime/src/main/cpp/Exceptions.h | 2 + runtime/src/main/cpp/Memory.h | 4 + runtime/src/main/cpp/TypeInfo.h | 7 ++ runtime/src/main/cpp/Worker.cpp | 2 +- runtime/src/main/kotlin/konan/BinaryBlob.kt | 1 + .../main/kotlin/konan/internal/Annotations.kt | 6 ++ .../src/main/kotlin/konan/internal/Boxing.kt | 9 +- .../kotlin/konan/internal/InteropBoxing.kt | 3 + .../kotlin/konan/internal/RuntimeUtils.kt | 5 + runtime/src/main/kotlin/kotlin/String.kt | 2 +- 19 files changed, 226 insertions(+), 32 deletions(-) create mode 100644 backend.native/tests/runtime/workers/freeze2.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt index cd0e0c73975..528135f8910 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt @@ -18,13 +18,9 @@ package org.jetbrains.kotlin.backend.konan.descriptors 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 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.util.simpleFunctions import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.types.SimpleType 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 immutableAnnotation = FqName("konan.internal.Immutable") // TODO: don't forget to remove descriptor access here. internal val FunctionDescriptor.isIntrinsic: Boolean 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( "kotlin.Boolean", "kotlin.Char", "kotlin.Byte", "kotlin.Short", diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 0a310576c5a..0c4151479b2 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -42,6 +42,7 @@ 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.konan.target.KonanTarget +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils @@ -1397,8 +1398,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map println("Main: $result") } - worker.requestTermination().consume { _ -> } + worker.requestTermination().result() println("OK") } \ No newline at end of file diff --git a/backend.native/tests/runtime/workers/freeze2.kt b/backend.native/tests/runtime/workers/freeze2.kt new file mode 100644 index 00000000000..d2aa907cd25 --- /dev/null +++ b/backend.native/tests/runtime/workers/freeze2.kt @@ -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 {a0.int++ } + + val a1 = ByteArray(2) + a1[1]++ + a1.freeze() + assertFailsWith { a1[1]++ } + + val a2 = ShortArray(2) + a2[1]++ + a2.freeze() + assertFailsWith { a2[1]++ } + + val a3 = IntArray(2) + a3[1]++ + a3.freeze() + assertFailsWith { a3[1]++ } + + val a4 = LongArray(2) + a4[1]++ + a4.freeze() + assertFailsWith { a4[1]++ } + + val a5 = BooleanArray(2) + a5[1] = true + a5.freeze() + assertFailsWith { a5[1] = false } + + val a6 = CharArray(2) + a6[1] = 'a' + a6.freeze() + assertFailsWith { a6[1] = 'b' } + + val a7 = FloatArray(2) + a7[1] = 1.0f + a7.freeze() + assertFailsWith { a7[1] = 2.0f } + + val a8 = DoubleArray(2) + a8[1] = 1.0 + a8.freeze() + assertFailsWith { 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") +} \ No newline at end of file diff --git a/backend.native/tests/runtime/workers/worker3.kt b/backend.native/tests/runtime/workers/worker3.kt index d7d13388a5f..99a40798fb8 100644 --- a/backend.native/tests/runtime/workers/worker3.kt +++ b/backend.native/tests/runtime/workers/worker3.kt @@ -4,7 +4,8 @@ import kotlin.test.* 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) @Test fun runTest() { @@ -13,19 +14,18 @@ data class WorkerResult(val intResult: Int, val stringResult: String) fun main(args: Array) { val worker = startWorker() - val s = "zzz${args.size.toString()}" - + val dataParam = DataParam(17) val future = try { worker.schedule(TransferMode.CHECKED, - { WorkerArgument(42, s) }, - { input -> WorkerResult(input.intParam, input.stringParam + " result") } + { WorkerArgument(42, dataParam) }, + { input -> WorkerResult(input.intParam, input.dataParam.toString() + " result") } ) } catch (e: IllegalStateException) { null } if (future != null) println("Fail 1") - if (s != "zzz0") println("Fail 2") + if (dataParam.int != 17) println("Fail 2") worker.requestTermination().consume { _ -> } println("OK") } \ No newline at end of file diff --git a/runtime/src/main/cpp/Arrays.cpp b/runtime/src/main/cpp/Arrays.cpp index 845e4c413c2..8fc6418cab6 100644 --- a/runtime/src/main/cpp/Arrays.cpp +++ b/runtime/src/main/cpp/Arrays.cpp @@ -23,9 +23,22 @@ #include "Natives.h" #include "Types.h" +namespace { + +const ArrayHeader anEmptyArray = { + const_cast(theArrayTypeInfo), /* permanent object */ 0, /* element count */ 0 +}; + +ALWAYS_INLINE inline void mutabilityCheck(KConstRef thiz) { + // TODO: optimize it! + if (thiz->container()->frozen()) { + ThrowInvalidMutabilityException(); + } +} + template -static inline void copyImpl(KConstRef thiz, KInt fromIndex, - KRef destination, KInt toIndex, KInt count) { +inline void copyImpl(KConstRef thiz, KInt fromIndex, + KRef destination, KInt toIndex, KInt count) { const ArrayHeader* array = thiz->array(); ArrayHeader* destinationArray = destination->array(); if (count < 0 || @@ -33,18 +46,12 @@ static inline void copyImpl(KConstRef thiz, KInt fromIndex, toIndex < 0 || count > destinationArray->count_ - toIndex) { ThrowArrayIndexOutOfBoundsException(); } - + mutabilityCheck(destination); memmove(PrimitiveArrayAddressOfElementAt(destinationArray, toIndex), PrimitiveArrayAddressOfElementAt(array, fromIndex), count * sizeof(T)); } -namespace { - -const ArrayHeader anEmptyArray = { - const_cast(theArrayTypeInfo), /* permanent object */ 0, /* element count */ 0 -}; - } // namespace extern "C" { @@ -65,6 +72,7 @@ void Kotlin_Array_set(KRef thiz, KInt index, KConstRef value) { if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); 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_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); for (KInt index = fromIndex; index < toIndex; ++index) { UpdateRef(ArrayAddressOfElementAt(array, index), value); } @@ -92,6 +101,7 @@ void Kotlin_Array_copyImpl(KConstRef thiz, KInt fromIndex, toIndex < 0 || count > destinationArray->count_ - toIndex) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(destination); if (fromIndex >= toIndex) { for (int index = 0; index < count; index++) { UpdateRef(ArrayAddressOfElementAt(destinationArray, toIndex + index), @@ -123,6 +133,7 @@ void Kotlin_ByteArray_set(KRef thiz, KInt index, KByte value) { if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); *ByteArrayAddressOfElementAt(array, index) = value; } @@ -184,6 +195,7 @@ void Kotlin_ByteArray_setCharAt(KRef thiz, KInt index, KChar value) { if (static_cast(index + 1) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); *reinterpret_cast(ByteArrayAddressOfElementAt(array, index)) = value; } @@ -192,6 +204,7 @@ void Kotlin_ByteArray_setShortAt(KRef thiz, KInt index, KShort value) { if (static_cast(index + 1) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); *reinterpret_cast(ByteArrayAddressOfElementAt(array, index)) = value; } @@ -200,6 +213,7 @@ void Kotlin_ByteArray_setIntAt(KRef thiz, KInt index, KInt value) { if (static_cast(index + 3) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); *reinterpret_cast(ByteArrayAddressOfElementAt(array, index)) = value; } @@ -208,6 +222,7 @@ void Kotlin_ByteArray_setLongAt(KRef thiz, KInt index, KLong value) { if (static_cast(index + 7) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); *reinterpret_cast(ByteArrayAddressOfElementAt(array, index)) = value; } @@ -216,6 +231,7 @@ void Kotlin_ByteArray_setFloatAt(KRef thiz, KInt index, KFloat value) { if (static_cast(index + 3) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); *reinterpret_cast(ByteArrayAddressOfElementAt(array, index)) = value; } @@ -224,6 +240,7 @@ void Kotlin_ByteArray_setDoubleAt(KRef thiz, KInt index, KDouble value) { if (static_cast(index + 7) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); *reinterpret_cast(ByteArrayAddressOfElementAt(array, index)) = value; } @@ -240,11 +257,15 @@ void Kotlin_CharArray_set(KRef thiz, KInt index, KChar value) { if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); *PrimitiveArrayAddressOfElementAt(array, index) = value; } OBJ_GETTER(Kotlin_CharArray_copyOf, KConstRef thiz, KInt newSize) { const ArrayHeader* array = thiz->array(); + if (newSize < 0) { + ThrowIllegalArgumentException(); + } ArrayHeader* result = AllocArrayInstance( array->type_info(), newSize, OBJ_RESULT)->array(); 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(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); *PrimitiveArrayAddressOfElementAt(array, index) = value; } @@ -294,6 +316,7 @@ void Kotlin_IntArray_set(KRef thiz, KInt index, KInt value) { if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); *PrimitiveArrayAddressOfElementAt(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_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); for (KInt index = fromIndex; index < toIndex; ++index) { *PrimitiveArrayAddressOfElementAt(array, index) = value; } @@ -366,6 +390,7 @@ void Kotlin_LongArray_set(KRef thiz, KInt index, KLong value) { if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); *PrimitiveArrayAddressOfElementAt(array, index) = value; } @@ -387,6 +412,7 @@ void Kotlin_FloatArray_set(KRef thiz, KInt index, KFloat value) { if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); *PrimitiveArrayAddressOfElementAt(array, index) = value; } @@ -408,6 +434,7 @@ void Kotlin_DoubleArray_set(KRef thiz, KInt index, KDouble value) { if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); *PrimitiveArrayAddressOfElementAt(array, index) = value; } @@ -429,6 +456,7 @@ void Kotlin_BooleanArray_set(KRef thiz, KInt index, KBoolean value) { if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); } + mutabilityCheck(thiz); *PrimitiveArrayAddressOfElementAt(array, index) = value; } diff --git a/runtime/src/main/cpp/Exceptions.h b/runtime/src/main/cpp/Exceptions.h index b50128f45f0..d69e0a218e9 100644 --- a/runtime/src/main/cpp/Exceptions.h +++ b/runtime/src/main/cpp/Exceptions.h @@ -50,6 +50,8 @@ void RUNTIME_NORETURN ThrowOutOfMemoryError(); void RUNTIME_NORETURN ThrowNotImplementedError(); // Throws illegal character conversion exception (used in UTF8/UTF16 conversions). void RUNTIME_NORETURN ThrowIllegalCharacterConversionException(); +void RUNTIME_NORETURN ThrowIllegalArgumentException(); +void RUNTIME_NORETURN ThrowInvalidMutabilityException(); // Prints out mesage of Throwable. void PrintThrowable(KRef); diff --git a/runtime/src/main/cpp/Memory.h b/runtime/src/main/cpp/Memory.h index e71c10982a2..99f0254ba24 100644 --- a/runtime/src/main/cpp/Memory.h +++ b/runtime/src/main/cpp/Memory.h @@ -264,6 +264,9 @@ class Container { void SetHeader(ObjHeader* obj, const TypeInfo* type_info) { obj->container_ = header_; obj->typeInfoOrMeta_ = const_cast(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) { obj->container_ = currentChunk_->asHeader(); obj->typeInfoOrMeta_ = const_cast(typeInfo); + // Here we do not take into account typeInfo's immutability for ARC strategy, as there's no ARC. } ContainerChunk* currentChunk_; diff --git a/runtime/src/main/cpp/TypeInfo.h b/runtime/src/main/cpp/TypeInfo.h index d280ec2a14f..8700c0e4d05 100644 --- a/runtime/src/main/cpp/TypeInfo.h +++ b/runtime/src/main/cpp/TypeInfo.h @@ -57,6 +57,10 @@ enum Konan_RuntimeType { RT_BOOLEAN = 9 }; +enum Konan_TypeFlags { + TF_IMMUTABLE = 1 << 0 +}; + // Extended information about a type. struct ExtendedTypeInfo { // Number of fields (negated Konan_RuntimeType for array types). @@ -104,6 +108,9 @@ struct TypeInfo { // or `null` if the class is anonymous. ObjHeader* relativeName_; + // Various flags. + int32_t flags_; + // Extended RTTI. const ExtendedTypeInfo* extendedInfo_; diff --git a/runtime/src/main/cpp/Worker.cpp b/runtime/src/main/cpp/Worker.cpp index 1e0a58de8aa..2c6718acccd 100644 --- a/runtime/src/main/cpp/Worker.cpp +++ b/runtime/src/main/cpp/Worker.cpp @@ -577,7 +577,7 @@ void Kotlin_Worker_freezeInternal(KRef object) { } KBoolean Kotlin_Worker_isFrozenInternal(KRef object) { - return object == nullptr || object->container()->frozen(); + return object == nullptr || object->container()->permanentOrFrozen(); } } // extern "C" diff --git a/runtime/src/main/kotlin/konan/BinaryBlob.kt b/runtime/src/main/kotlin/konan/BinaryBlob.kt index b07669038db..8e46fd9a7e0 100644 --- a/runtime/src/main/kotlin/konan/BinaryBlob.kt +++ b/runtime/src/main/kotlin/konan/BinaryBlob.kt @@ -22,6 +22,7 @@ import kotlinx.cinterop.* * An immutable compile-time array of bytes. */ @ExportTypeInfo("theImmutableBinaryBlobTypeInfo") +@Immutable public final class ImmutableBinaryBlob private constructor() { public val size: Int get() = getArrayLength() diff --git a/runtime/src/main/kotlin/konan/internal/Annotations.kt b/runtime/src/main/kotlin/konan/internal/Annotations.kt index f51d6f5d525..d069654d177 100644 --- a/runtime/src/main/kotlin/konan/internal/Annotations.kt +++ b/runtime/src/main/kotlin/konan/internal/Annotations.kt @@ -58,3 +58,9 @@ annotation class ExportForCompiler @Retention(AnnotationRetention.BINARY) annotation class InlineConstructor +/** + * Class is immutable and is frozen by default. + */ +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.BINARY) +annotation class Immutable diff --git a/runtime/src/main/kotlin/konan/internal/Boxing.kt b/runtime/src/main/kotlin/konan/internal/Boxing.kt index 92e8dc87109..42417f5a671 100644 --- a/runtime/src/main/kotlin/konan/internal/Boxing.kt +++ b/runtime/src/main/kotlin/konan/internal/Boxing.kt @@ -16,7 +16,6 @@ package konan.internal - @SymbolName("getCachedBooleanBox") external fun getCachedBooleanBox(value: Boolean): BooleanBox @SymbolName("inBooleanBoxCache") @@ -42,6 +41,7 @@ external fun getCachedLongBox(value: Long): LongBox @SymbolName("inLongBoxCache") external fun inLongBoxCache(value: Long): Boolean +@Immutable class BooleanBox(val value: Boolean) : Comparable { override fun equals(other: Any?): Boolean { if (other !is BooleanBox) { @@ -65,6 +65,7 @@ fun boxBoolean(value: Boolean) = if (inBooleanBoxCache(value)) { BooleanBox(value) } +@Immutable class CharBox(val value: Char) : Comparable { override fun equals(other: Any?): Boolean { if (other !is CharBox) { @@ -88,6 +89,7 @@ fun boxChar(value: Char) = if (inCharBoxCache(value)) { CharBox(value) } +@Immutable class ByteBox(val value: Byte) : Number(), Comparable { override fun equals(other: Any?): Boolean { if (other !is ByteBox) { @@ -119,6 +121,7 @@ fun boxByte(value: Byte) = if (inByteBoxCache(value)) { ByteBox(value) } +@Immutable class ShortBox(val value: Short) : Number(), Comparable { override fun equals(other: Any?): Boolean { if (other !is ShortBox) { @@ -150,6 +153,7 @@ fun boxShort(value: Short) = if (inShortBoxCache(value)) { ShortBox(value) } +@Immutable class IntBox(val value: Int) : Number(), Comparable { override fun equals(other: Any?): Boolean { if (other !is IntBox) { @@ -181,6 +185,7 @@ fun boxInt(value: Int) = if (inIntBoxCache(value)) { IntBox(value) } +@Immutable class LongBox(val value: Long) : Number(), Comparable { override fun equals(other: Any?): Boolean { if (other !is LongBox) { @@ -212,6 +217,7 @@ fun boxLong(value: Long) = if (inLongBoxCache(value)) { LongBox(value) } +@Immutable class FloatBox(val value: Float) : Number(), Comparable { override fun equals(other: Any?): Boolean { if (other !is FloatBox) { @@ -239,6 +245,7 @@ class FloatBox(val value: Float) : Number(), Comparable { @ExportForCppRuntime("Kotlin_boxFloat") fun boxFloat(value: Float) = FloatBox(value) +@Immutable class DoubleBox(val value: Double) : Number(), Comparable { override fun equals(other: Any?): Boolean { if (other !is DoubleBox) { diff --git a/runtime/src/main/kotlin/konan/internal/InteropBoxing.kt b/runtime/src/main/kotlin/konan/internal/InteropBoxing.kt index 4c8c43e2567..530229d449b 100644 --- a/runtime/src/main/kotlin/konan/internal/InteropBoxing.kt +++ b/runtime/src/main/kotlin/konan/internal/InteropBoxing.kt @@ -18,6 +18,7 @@ package konan.internal import kotlinx.cinterop.* +@Immutable class NativePtrBox(val value: NativePtr) { override fun equals(other: Any?): Boolean { if (other !is NativePtrBox) { @@ -34,6 +35,7 @@ class NativePtrBox(val value: NativePtr) { fun boxNativePtr(value: NativePtr) = NativePtrBox(value) +@Immutable class NativePointedBox(val value: NativePointed) { override fun equals(other: Any?): Boolean { 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 unboxNativePointed(box: NativePointedBox?) = box?.value +@Immutable class CPointerBox(val value: CPointer) : CValuesRef() { override fun equals(other: Any?): Boolean { if (other !is CPointerBox) { diff --git a/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt b/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt index 8660c992aa2..903f6bbf2ce 100644 --- a/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt +++ b/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt @@ -65,6 +65,11 @@ fun ThrowUninitializedPropertyAccessException(): Nothing { throw UninitializedPropertyAccessException() } +@ExportForCppRuntime +internal fun ThrowIllegalArgumentException() : Nothing { + throw IllegalArgumentException() +} + @ExportForCppRuntime internal fun ThrowNotImplementedError(): Nothing { throw NotImplementedError("An operation is not implemented.") diff --git a/runtime/src/main/kotlin/kotlin/String.kt b/runtime/src/main/kotlin/kotlin/String.kt index c201ffd5d90..bf0944f0cd7 100644 --- a/runtime/src/main/kotlin/kotlin/String.kt +++ b/runtime/src/main/kotlin/kotlin/String.kt @@ -16,8 +16,8 @@ package kotlin - @ExportTypeInfo("theStringTypeInfo") +@konan.internal.Immutable public final class String : Comparable, CharSequence { public companion object { }