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.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",
|
||||
|
||||
+14
-7
@@ -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<IrE
|
||||
val thisPtr = evaluateExpression(value.receiver!!)
|
||||
return functionGenerationContext.loadSlot(
|
||||
fieldPtrOfClass(thisPtr, value.symbol.owner), value.descriptor.isVar())
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
assert (value.receiver == null)
|
||||
val ptr = context.llvmDeclarations.forStaticField(value.symbol.owner).storage
|
||||
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 {
|
||||
context.log{"evaluateSetField : ${ir2string(value)}"}
|
||||
val valueToAssign = evaluateExpression(value.value)
|
||||
if (value.descriptor.dispatchReceiverParameter != null) {
|
||||
val thisPtr = evaluateExpression(value.receiver!!)
|
||||
functionGenerationContext.call(context.llvm.mutationCheck,
|
||||
listOf(functionGenerationContext.bitcast(codegen.kObjHeaderPtr, thisPtr)),
|
||||
Lifetime.IRRELEVANT, ExceptionHandler.Caller)
|
||||
if (needMutationCheck(value.descriptor.containingDeclaration)) {
|
||||
functionGenerationContext.call(context.llvm.mutationCheck,
|
||||
listOf(functionGenerationContext.bitcast(codegen.kObjHeaderPtr, thisPtr)),
|
||||
Lifetime.IRRELEVANT, ExceptionHandler.Caller)
|
||||
}
|
||||
functionGenerationContext.storeAny(valueToAssign, fieldPtrOfClass(thisPtr, value.symbol.owner))
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
assert (value.receiver == null)
|
||||
val globalValue = context.llvmDeclarations.forStaticField(value.symbol.owner).storage
|
||||
functionGenerationContext.storeAny(valueToAssign, globalValue)
|
||||
|
||||
+12
@@ -29,6 +29,13 @@ import org.jetbrains.kotlin.resolve.constants.StringValue
|
||||
|
||||
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) :
|
||||
Struct(runtime.fieldTableRecordType, nameSignature, Int32(fieldOffset))
|
||||
|
||||
@@ -50,6 +57,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
fieldsCount: Int,
|
||||
packageName: String?,
|
||||
relativeName: String?,
|
||||
flags: Int,
|
||||
extendedInfo: ConstPointer,
|
||||
writableTypeInfo: ConstPointer?) :
|
||||
|
||||
@@ -78,6 +86,8 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
kotlinStringLiteral(packageName),
|
||||
kotlinStringLiteral(relativeName),
|
||||
|
||||
Int32(flags),
|
||||
|
||||
extendedInfo,
|
||||
|
||||
*listOfNotNull(writableTypeInfo).toTypedArray()
|
||||
@@ -203,6 +213,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
fieldsPtr, if (classDesc.isInterface) -1 else fields.size,
|
||||
reflectionInfo.packageName,
|
||||
reflectionInfo.relativeName,
|
||||
flagsFromClass(classDesc),
|
||||
makeExtendedInfo(classDesc),
|
||||
llvmDeclarations.writableTypeInfoGlobal?.pointer
|
||||
)
|
||||
@@ -345,6 +356,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
fields = fieldsPtr, fieldsCount = fieldsCount,
|
||||
packageName = reflectionInfo.packageName,
|
||||
relativeName = reflectionInfo.relativeName,
|
||||
flags = flagsFromClass(descriptor),
|
||||
extendedInfo = NullPointer(runtime.extendedTypeInfoType),
|
||||
writableTypeInfo = writableTypeInfo
|
||||
), vtable)
|
||||
|
||||
+2
-2
@@ -199,7 +199,7 @@ internal class TestProcessor (val context: KonanBackendContext) {
|
||||
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,
|
||||
function: IrFunctionSymbol,
|
||||
@@ -263,7 +263,7 @@ internal class TestProcessor (val context: KonanBackendContext) {
|
||||
val symbol = declaration.symbol
|
||||
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()) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -663,6 +663,14 @@ task freeze_stress(type: RunKonanTest) {
|
||||
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) {
|
||||
disabled = (project.testTarget == 'wasm32') // Workers need pthreads.
|
||||
goldValue = "true\n"
|
||||
|
||||
@@ -38,6 +38,6 @@ data class SharedData(val string: String, val int: Int, val member: SharedDataMe
|
||||
future.consume {
|
||||
result -> println("Main: $result")
|
||||
}
|
||||
worker.requestTermination().consume { _ -> }
|
||||
worker.requestTermination().result()
|
||||
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.*
|
||||
|
||||
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<String>) {
|
||||
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")
|
||||
}
|
||||
@@ -23,9 +23,22 @@
|
||||
#include "Natives.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>
|
||||
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<T>(destinationArray, toIndex),
|
||||
PrimitiveArrayAddressOfElementAt<T>(array, fromIndex),
|
||||
count * sizeof(T));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const ArrayHeader anEmptyArray = {
|
||||
const_cast<TypeInfo*>(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<uint32_t>(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<uint32_t>(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<uint32_t>(index + 1) >= array->count_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
mutabilityCheck(thiz);
|
||||
*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_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
mutabilityCheck(thiz);
|
||||
*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_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
mutabilityCheck(thiz);
|
||||
*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_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
mutabilityCheck(thiz);
|
||||
*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_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
mutabilityCheck(thiz);
|
||||
*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_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
mutabilityCheck(thiz);
|
||||
*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_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
mutabilityCheck(thiz);
|
||||
*PrimitiveArrayAddressOfElementAt<KChar>(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<uint32_t>(index) >= array->count_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
mutabilityCheck(thiz);
|
||||
*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_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
mutabilityCheck(thiz);
|
||||
*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_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
mutabilityCheck(thiz);
|
||||
for (KInt index = fromIndex; index < toIndex; ++index) {
|
||||
*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_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
mutabilityCheck(thiz);
|
||||
*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_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
mutabilityCheck(thiz);
|
||||
*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_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
mutabilityCheck(thiz);
|
||||
*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_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
mutabilityCheck(thiz);
|
||||
*PrimitiveArrayAddressOfElementAt<KBoolean>(array, index) = value;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -264,6 +264,9 @@ class Container {
|
||||
void SetHeader(ObjHeader* obj, const TypeInfo* type_info) {
|
||||
obj->container_ = header_;
|
||||
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) {
|
||||
obj->container_ = currentChunk_->asHeader();
|
||||
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_;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Boolean> {
|
||||
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<Char> {
|
||||
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<Byte> {
|
||||
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<Short> {
|
||||
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<Int> {
|
||||
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<Long> {
|
||||
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<Float> {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is FloatBox) {
|
||||
@@ -239,6 +245,7 @@ class FloatBox(val value: Float) : Number(), Comparable<Float> {
|
||||
@ExportForCppRuntime("Kotlin_boxFloat")
|
||||
fun boxFloat(value: Float) = FloatBox(value)
|
||||
|
||||
@Immutable
|
||||
class DoubleBox(val value: Double) : Number(), Comparable<Double> {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is DoubleBox) {
|
||||
|
||||
@@ -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<CPointed>) : CValuesRef<CPointed>() {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is CPointerBox) {
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package kotlin
|
||||
|
||||
|
||||
@ExportTypeInfo("theStringTypeInfo")
|
||||
@konan.internal.Immutable
|
||||
public final class String : Comparable<String>, CharSequence {
|
||||
public companion object {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user