[K/N] Any.isFrozen is now always false when freezing is disabled
This commit is contained in:
+5
-4
@@ -11,22 +11,23 @@ package org.jetbrains.kotlin.backend.konan
|
||||
*
|
||||
* If [enableFreezeAtRuntime] is false then `Any.freeze()` and `checkIfFrozen(ref: Any?)` are no-op.
|
||||
* [freezeImplicit] enabled freezing for @Frozen types and @SharedImmutable globals (i.e. implicit calls to `Any.freeze()`).
|
||||
* If [enableFreezeChecks] is false, than `Any.isFrozen() will always return false, and no [InvalidMutabilityException] can be thrown
|
||||
*/
|
||||
enum class Freezing(val enableFreezeAtRuntime: Boolean, val freezeImplicit: Boolean) {
|
||||
enum class Freezing(val enableFreezeAtRuntime: Boolean, val freezeImplicit: Boolean, val enableFreezeChecks: Boolean) {
|
||||
/**
|
||||
* Enable freezing in `Any.freeze()` as well as for @Frozen types and @SharedImmutable globals.
|
||||
*/
|
||||
Full(true, true),
|
||||
Full(true, true, true),
|
||||
|
||||
/**
|
||||
* Enable freezing only in explicit calls to `Any.freeze()`.
|
||||
*/
|
||||
ExplicitOnly(true, false),
|
||||
ExplicitOnly(true, false, true),
|
||||
|
||||
/**
|
||||
* No freezing at all.
|
||||
*/
|
||||
Disabled(false, false);
|
||||
Disabled(false, false, false);
|
||||
|
||||
companion object {
|
||||
// Users might depend on runtime guarantees of freezing, so it should be enabled by default.
|
||||
|
||||
+1
@@ -2742,6 +2742,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
overrideRuntimeGlobal("Kotlin_destroyRuntimeMode", Int32(context.config.destroyRuntimeMode.value))
|
||||
overrideRuntimeGlobal("Kotlin_workerExceptionHandling", Int32(context.config.workerExceptionHandling.value))
|
||||
overrideRuntimeGlobal("Kotlin_freezingEnabled", Int32(if (context.config.freezing.enableFreezeAtRuntime) 1 else 0))
|
||||
overrideRuntimeGlobal("Kotlin_freezingChecksEnabled", Int32(if (context.config.freezing.enableFreezeChecks) 1 else 0))
|
||||
overrideRuntimeGlobal("Kotlin_gcSchedulerType", Int32(context.config.gcSchedulerType.value))
|
||||
val getSourceInfoFunctionName = when (context.config.sourceInfoType) {
|
||||
SourceInfoType.NOOP -> null
|
||||
|
||||
@@ -1152,52 +1152,68 @@ standaloneTest("worker_threadlocal_no_leak") {
|
||||
source = "runtime/workers/worker_threadlocal_no_leak.kt"
|
||||
}
|
||||
|
||||
task freeze0(type: KonanLocalTest) {
|
||||
standaloneTest("freeze0") {
|
||||
enabled = (project.testTarget != 'wasm32') // No workers on WASM.
|
||||
useGoldenData = true
|
||||
flags = ["-Xbinary=freezing=full", "-tr"]
|
||||
source = "runtime/workers/freeze0.kt"
|
||||
testLogger = KonanTest.Logger.SILENT
|
||||
}
|
||||
|
||||
task freeze1(type: KonanLocalTest) {
|
||||
standaloneTest("freeze1") {
|
||||
enabled = (project.testTarget != 'wasm32') // No exceptions on WASM.
|
||||
useGoldenData = true
|
||||
flags = ["-Xbinary=freezing=full", "-tr"]
|
||||
source = "runtime/workers/freeze1.kt"
|
||||
testLogger = KonanTest.Logger.SILENT
|
||||
}
|
||||
|
||||
task freeze_stress(type: KonanLocalTest) {
|
||||
standaloneTest("freeze_stress") {
|
||||
enabled = (project.testTarget != 'wasm32') // No exceptions on WASM.
|
||||
useGoldenData = true
|
||||
flags = ["-Xbinary=freezing=full", "-tr"]
|
||||
source = "runtime/workers/freeze_stress.kt"
|
||||
testLogger = KonanTest.Logger.SILENT
|
||||
}
|
||||
|
||||
task freeze2(type: KonanLocalTest) {
|
||||
standaloneTest("freeze2") {
|
||||
enabled = (project.testTarget != 'wasm32') // No exceptions on WASM.
|
||||
useGoldenData = true
|
||||
flags = ["-Xbinary=freezing=full", "-tr"]
|
||||
source = "runtime/workers/freeze2.kt"
|
||||
testLogger = KonanTest.Logger.SILENT
|
||||
}
|
||||
|
||||
task freeze3(type: KonanLocalTest) {
|
||||
standaloneTest("freeze3") {
|
||||
enabled = (project.testTarget != 'wasm32') // No exceptions on WASM.
|
||||
useGoldenData = true
|
||||
flags = ["-Xbinary=freezing=full", "-tr"]
|
||||
source = "runtime/workers/freeze3.kt"
|
||||
testLogger = KonanTest.Logger.SILENT
|
||||
}
|
||||
|
||||
task freeze4(type: KonanLocalTest) {
|
||||
standaloneTest("freeze4") {
|
||||
enabled = (project.testTarget != 'wasm32') // No exceptions on WASM.
|
||||
useGoldenData = true
|
||||
flags = ["-Xbinary=freezing=full", "-tr"]
|
||||
source = "runtime/workers/freeze4.kt"
|
||||
testLogger = KonanTest.Logger.SILENT
|
||||
}
|
||||
|
||||
task freeze5(type: KonanLocalTest) {
|
||||
standaloneTest("freeze5") {
|
||||
useGoldenData = true
|
||||
flags = ["-Xbinary=freezing=full", "-tr"]
|
||||
source = "runtime/workers/freeze5.kt"
|
||||
testLogger = KonanTest.Logger.SILENT
|
||||
}
|
||||
|
||||
task freeze6(type: KonanLocalTest) {
|
||||
standaloneTest("freeze6") {
|
||||
enabled = (project.testTarget != 'wasm32') && // No exceptions on WASM.
|
||||
!isNoopGC
|
||||
useGoldenData = true
|
||||
flags = ["-Xbinary=freezing=full", "-tr"]
|
||||
source = "runtime/workers/freeze6.kt"
|
||||
testLogger = KonanTest.Logger.SILENT
|
||||
}
|
||||
|
||||
task atomic0(type: KonanLocalTest) {
|
||||
|
||||
@@ -15,17 +15,18 @@ private class NSObjectImpl : NSObject() {
|
||||
assertFalse(obj.isFrozen)
|
||||
|
||||
obj.x = 222
|
||||
obj.freeze()
|
||||
assertTrue(obj.isFrozen)
|
||||
|
||||
runInWorker {
|
||||
val obj1 = array.objectAtIndex(0) as NSObjectImpl
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
obj1.x = 333
|
||||
if (Platform.isFreezingEnabled) {
|
||||
obj.freeze()
|
||||
assertTrue(obj.isFrozen)
|
||||
runInWorker {
|
||||
val obj1 = array.objectAtIndex(0) as NSObjectImpl
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
obj1.x = 333
|
||||
}
|
||||
}
|
||||
assertEquals(222, obj.x)
|
||||
}
|
||||
|
||||
assertEquals(222, obj.x)
|
||||
|
||||
// TODO: test [obj release] etc.
|
||||
}
|
||||
|
||||
@@ -2627,6 +2627,7 @@ __attribute__((swift_name("ValuesKt")))
|
||||
+ (KtTripleVals<id> * _Nullable)getValue3:(id _Nullable)receiver __attribute__((swift_name("getValue3(_:)")));
|
||||
+ (KtTripleVals<id> * _Nullable)getValueOrNull3:(id _Nullable)receiver __attribute__((swift_name("getValueOrNull3(_:)")));
|
||||
+ (BOOL)isFrozenObj:(id)obj __attribute__((swift_name("isFrozen(obj:)")));
|
||||
+ (BOOL)isFreezingEnabled __attribute__((swift_name("isFreezingEnabled()")));
|
||||
+ (id)kotlinLambdaBlock:(id (^)(id))block __attribute__((swift_name("kotlinLambda(block:)")));
|
||||
+ (int64_t)multiplyInt:(int32_t)int_ long:(int64_t)long_ __attribute__((swift_name("multiply(int:long:)")));
|
||||
|
||||
|
||||
@@ -2569,6 +2569,7 @@ __attribute__((swift_name("ValuesKt")))
|
||||
+ (KtTripleVals<id> * _Nullable)getValue3:(id _Nullable)receiver __attribute__((swift_name("getValue3(_:)")));
|
||||
+ (KtTripleVals<id> * _Nullable)getValueOrNull3:(id _Nullable)receiver __attribute__((swift_name("getValueOrNull3(_:)")));
|
||||
+ (BOOL)isFrozenObj:(id)obj __attribute__((swift_name("isFrozen(obj:)")));
|
||||
+ (BOOL)isFreezingEnabled __attribute__((swift_name("isFreezingEnabled()")));
|
||||
+ (id)kotlinLambdaBlock:(id (^)(id))block __attribute__((swift_name("kotlinLambda(block:)")));
|
||||
+ (int64_t)multiplyInt:(int32_t)int_ long:(int64_t)long_ __attribute__((swift_name("multiply(int:long:)")));
|
||||
|
||||
|
||||
@@ -2569,6 +2569,7 @@ __attribute__((swift_name("ValuesKt")))
|
||||
+ (KtTripleVals * _Nullable)getValue3:(id _Nullable)receiver __attribute__((swift_name("getValue3(_:)")));
|
||||
+ (KtTripleVals * _Nullable)getValueOrNull3:(id _Nullable)receiver __attribute__((swift_name("getValueOrNull3(_:)")));
|
||||
+ (BOOL)isFrozenObj:(id)obj __attribute__((swift_name("isFrozen(obj:)")));
|
||||
+ (BOOL)isFreezingEnabled __attribute__((swift_name("isFreezingEnabled()")));
|
||||
+ (id)kotlinLambdaBlock:(id (^)(id))block __attribute__((swift_name("kotlinLambda(block:)")));
|
||||
+ (int64_t)multiplyInt:(int32_t)int_ long:(int64_t)long_ __attribute__((swift_name("multiply(int:long:)")));
|
||||
|
||||
|
||||
@@ -250,6 +250,7 @@ fun IC3.getValue3() = value
|
||||
fun IC3?.getValueOrNull3() = this?.value
|
||||
|
||||
fun isFrozen(obj: Any): Boolean = obj.isFrozen
|
||||
fun isFreezingEnabled() = Platform.isFreezingEnabled
|
||||
fun kotlinLambda(block: (Any) -> Any): Any = block
|
||||
|
||||
fun multiply(int: Int, long: Long) = int * long
|
||||
|
||||
@@ -590,6 +590,9 @@ class TestSharedIImpl : NSObject, I {
|
||||
}
|
||||
|
||||
func testShared() throws {
|
||||
if !ValuesKt.isFreezingEnabled() {
|
||||
return;
|
||||
}
|
||||
func assertFrozen(_ obj: AnyObject) throws {
|
||||
try assertTrue(ValuesKt.isFrozen(obj: obj), "isFrozen(\(obj))")
|
||||
}
|
||||
|
||||
@@ -50,24 +50,26 @@ import kotlin.native.concurrent.*
|
||||
}
|
||||
expect(0) { results.size }
|
||||
|
||||
array.freeze()
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
array.setShortAt(0, 2.toShort())
|
||||
}
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
array.setCharAt(0, 'a')
|
||||
}
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
array.setIntAt(0, 2)
|
||||
}
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
array.setLongAt(0, 2)
|
||||
}
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
array.setFloatAt(0, 1.0f)
|
||||
}
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
array.setDoubleAt(0, 1.0)
|
||||
if (Platform.isFreezingEnabled) {
|
||||
array.freeze()
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
array.setShortAt(0, 2.toShort())
|
||||
}
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
array.setCharAt(0, 'a')
|
||||
}
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
array.setIntAt(0, 2)
|
||||
}
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
array.setLongAt(0, 2)
|
||||
}
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
array.setFloatAt(0, 1.0f)
|
||||
}
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
array.setDoubleAt(0, 1.0)
|
||||
}
|
||||
}
|
||||
println("OK")
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlin.native.internal.GC
|
||||
import kotlin.native.*
|
||||
import kotlin.native.ref.WeakReference
|
||||
import kotlin.text.Regex
|
||||
|
||||
@@ -600,6 +601,7 @@ fun createCyclicGarbageFrozen(): Triple<AtomicReference<WorkerBoundReference<B1>
|
||||
|
||||
@Test
|
||||
fun doesNotCollectCyclicGarbageFrozen() {
|
||||
if (!Platform.isFreezingEnabled) return
|
||||
val (ref1Owner, ref1Weak, ref2Weak) = createCyclicGarbageFrozen()
|
||||
|
||||
ref1Owner.value = null
|
||||
@@ -630,6 +632,7 @@ fun createCrossThreadCyclicGarbageFrozen(
|
||||
|
||||
@Test
|
||||
fun doesNotCollectCrossThreadCyclicGarbageFrozen() {
|
||||
if (!Platform.isFreezingEnabled) return
|
||||
val worker = Worker.start()
|
||||
|
||||
val (ref1Owner, ref1Weak, ref2Weak) = createCrossThreadCyclicGarbageFrozen(worker)
|
||||
@@ -674,6 +677,7 @@ fun dispose(refOwner: AtomicReference<WorkerBoundReference<C1>?>) {
|
||||
|
||||
@Test
|
||||
fun doesNotCollectCyclicGarbageWithAtomicsFrozen() {
|
||||
if (!Platform.isFreezingEnabled) return
|
||||
val (ref1Owner, ref1Weak, ref2Weak) = createCyclicGarbageWithAtomicsFrozen()
|
||||
|
||||
ref1Owner.value = null
|
||||
@@ -723,6 +727,7 @@ fun createCrossThreadCyclicGarbageWithAtomicsFrozen(
|
||||
|
||||
@Test
|
||||
fun doesNotCollectCrossThreadCyclicGarbageWithAtomicsFrozen() {
|
||||
if (!Platform.isFreezingEnabled) return
|
||||
val worker = Worker.start()
|
||||
|
||||
val (ref1Owner, ref1Weak, ref2Weak) = createCrossThreadCyclicGarbageWithAtomicsFrozen(worker)
|
||||
@@ -835,6 +840,7 @@ fun testDoubleFreeze() {
|
||||
|
||||
@Test
|
||||
fun testDoubleFreezeWithFreezeBlocker() {
|
||||
if (!Platform.isFreezingEnabled) return
|
||||
val ref = WorkerBoundReference(A(3))
|
||||
val wrapper = Wrapper(ref)
|
||||
wrapper.ensureNeverFrozen()
|
||||
|
||||
@@ -93,10 +93,12 @@ fun test4() {
|
||||
ref.compareAndSwap(null, Data(2))
|
||||
assertEquals(2, ref.value!!.value)
|
||||
}
|
||||
run {
|
||||
val ref = AtomicReference<Data?>(null).freeze()
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
ref.compareAndSwap(null, Data(2))
|
||||
if (Platform.isFreezingEnabled) {
|
||||
run {
|
||||
val ref = AtomicReference<Data?>(null).freeze()
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
ref.compareAndSwap(null, Data(2))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,11 +139,13 @@ fun test7() {
|
||||
ref.value = Array(1) { "po" }
|
||||
assertEquals(ref.value[0], "po")
|
||||
ref.freeze()
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
ref.value = Array(1) { "no" }
|
||||
}
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
ref.value[0] = "go"
|
||||
if (Platform.isFreezingEnabled) {
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
ref.value = Array(1) { "no" }
|
||||
}
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
ref.value[0] = "go"
|
||||
}
|
||||
}
|
||||
ref.value = Array(1) { "so" }.freeze()
|
||||
assertEquals(ref.value[0], "so")
|
||||
|
||||
@@ -81,9 +81,11 @@ private val checkedLazyModes =
|
||||
|
||||
|
||||
@Test fun runTest3() {
|
||||
for (mode in checkedLazyModes) {
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
println(Lazy(mode).freezer)
|
||||
if (Platform.isFreezingEnabled) {
|
||||
for (mode in checkedLazyModes) {
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
println(Lazy(mode).freezer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ RUNTIME_WEAK int32_t Kotlin_destroyRuntimeMode = 1;
|
||||
RUNTIME_WEAK int32_t Kotlin_gcSchedulerType = 2;
|
||||
RUNTIME_WEAK int32_t Kotlin_workerExceptionHandling = 0;
|
||||
RUNTIME_WEAK int32_t Kotlin_freezingEnabled = 1;
|
||||
RUNTIME_WEAK int32_t Kotlin_freezingChecksEnabled = 1;
|
||||
RUNTIME_WEAK const Kotlin_getSourceInfo_FunctionType Kotlin_getSourceInfo_Function = nullptr;
|
||||
#ifdef KONAN_ANDROID
|
||||
RUNTIME_WEAK int32_t Kotlin_printToAndroidLogcat = 1;
|
||||
@@ -32,6 +33,10 @@ ALWAYS_INLINE bool compiler::freezingEnabled() noexcept {
|
||||
return Kotlin_freezingEnabled != 0;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool compiler::freezingChecksEnabled() noexcept {
|
||||
return Kotlin_freezingChecksEnabled != 0;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE compiler::GCSchedulerType compiler::getGCSchedulerType() noexcept {
|
||||
return static_cast<compiler::GCSchedulerType>(Kotlin_gcSchedulerType);
|
||||
}
|
||||
|
||||
@@ -79,6 +79,8 @@ ALWAYS_INLINE inline std::string_view runtimeLogs() noexcept {
|
||||
}
|
||||
|
||||
bool freezingEnabled() noexcept;
|
||||
bool freezingChecksEnabled() noexcept;
|
||||
|
||||
|
||||
ALWAYS_INLINE inline int getSourceInfo(void* addr, SourceInfo *result, int result_size) {
|
||||
if (Kotlin_getSourceInfo_Function == nullptr) {
|
||||
|
||||
@@ -365,6 +365,10 @@ KBoolean Konan_Platform_isDebugBinary() {
|
||||
return kotlin::compiler::shouldContainDebugInfo();
|
||||
}
|
||||
|
||||
KBoolean Konan_Platform_isFreezingEnabled() {
|
||||
return kotlin::compiler::freezingChecksEnabled();
|
||||
}
|
||||
|
||||
bool Kotlin_memoryLeakCheckerEnabled() {
|
||||
return g_checkLeaks;
|
||||
}
|
||||
|
||||
@@ -1208,6 +1208,7 @@ void Kotlin_Worker_freezeInternal(KRef object) {
|
||||
}
|
||||
|
||||
KBoolean Kotlin_Worker_isFrozenInternal(KRef object) {
|
||||
if (!compiler::freezingChecksEnabled()) return false;
|
||||
return object == nullptr || isPermanentOrFrozen(object);
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,15 @@ public object Platform {
|
||||
public val isDebugBinary: Boolean
|
||||
get() = Platform_isDebugBinary()
|
||||
|
||||
/**
|
||||
* If freezing is enabled.
|
||||
*
|
||||
* This value would be false, only if binary option `freezing` is equal to `disabled`. This is default when
|
||||
* [memoryModel] is equal to [MemoryModel.EXPERIMENTAL].
|
||||
*/
|
||||
public val isFreezingEnabled: Boolean
|
||||
get() = Platform_isFreezingEnabled()
|
||||
|
||||
/**
|
||||
* If the memory leak checker is activated, by default `true` in debug mode, `false` in release.
|
||||
* When memory leak checker is activated, and leak is detected during last Kotlin context
|
||||
@@ -145,6 +154,9 @@ private external fun Platform_getMemoryModel(): Int
|
||||
@GCUnsafeCall("Konan_Platform_isDebugBinary")
|
||||
private external fun Platform_isDebugBinary(): Boolean
|
||||
|
||||
@GCUnsafeCall("Konan_Platform_isFreezingEnabled")
|
||||
private external fun Platform_isFreezingEnabled(): Boolean
|
||||
|
||||
@GCUnsafeCall("Konan_Platform_getMemoryLeakChecker")
|
||||
private external fun Platform_getMemoryLeakChecker(): Boolean
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
using namespace kotlin;
|
||||
|
||||
bool mm::IsFrozen(const ObjHeader* object) noexcept {
|
||||
if (!compiler::freezingChecksEnabled()) return false;
|
||||
if (object->permanent()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@ void ObjHeader::destroyMetaObject(ObjHeader* object) {
|
||||
|
||||
ALWAYS_INLINE bool isPermanentOrFrozen(const ObjHeader* obj) {
|
||||
// TODO: Freeze TF_IMMUTABLE objects upon creation.
|
||||
if (!compiler::freezingChecksEnabled()) return false;
|
||||
return mm::IsFrozen(obj) || ((obj->type_info()->flags_ & TF_IMMUTABLE) != 0);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user