diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt index c7691fb..b84b9d9 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt @@ -2204,8 +2204,8 @@ class CmdAssignOpObj( } if (result == null) { val name = (frame.fn.constants.getOrNull(nameId) as? BytecodeConst.StringVal)?.value - if (name != null) frame.ensureScope().raiseIllegalAssignment("symbol is readonly: $name") - frame.ensureScope().raiseIllegalAssignment("symbol is readonly") + if (name != null) frame.ensureScope().raiseIllegalAssignment("can't reassign val $name") + frame.ensureScope().raiseIllegalAssignment("can't reassign val") } frame.storeObjResult(dst, result) return @@ -4552,7 +4552,7 @@ class CmdFrame( type = inherited.type ) copied.delegate = inherited.delegate - return@mapIndexed copied + return@mapIndexed freezeImmutableCaptureRecord(copied) } } val isMutable = fn.localSlotMutables.getOrNull(localIndex) ?: false @@ -4576,11 +4576,13 @@ class CmdFrame( val record = findNamedExistingRecord(scope, name) if (record != null) { val value = record.value - return@mapIndexed when (value) { + return@mapIndexed freezeImmutableCaptureRecord( + when (value) { is FrameSlotRef -> ObjRecord(value, isMutable) is RecordSlotRef -> ObjRecord(value, isMutable) else -> ObjRecord(value, isMutable) - } + } + ) } if (hasNamedScopeBinding(scope, name)) { throw ScriptError( @@ -4589,11 +4591,13 @@ class CmdFrame( ) } } - when (raw) { - is FrameSlotRef -> ObjRecord(raw, isMutable) - is RecordSlotRef -> ObjRecord(raw, isMutable) - else -> ObjRecord(FrameSlotRef(frame, localIndex), isMutable) - } + freezeImmutableCaptureRecord( + when (raw) { + is FrameSlotRef -> ObjRecord(raw, isMutable) + is RecordSlotRef -> ObjRecord(raw, isMutable) + else -> ObjRecord(FrameSlotRef(frame, localIndex), isMutable) + } + ) } } diff --git a/lynglib/src/commonTest/kotlin/net/sergeych/lyng/OptTest.kt b/lynglib/src/commonTest/kotlin/net/sergeych/lyng/OptTest.kt index 602acd6..2287ce3 100644 --- a/lynglib/src/commonTest/kotlin/net/sergeych/lyng/OptTest.kt +++ b/lynglib/src/commonTest/kotlin/net/sergeych/lyng/OptTest.kt @@ -90,4 +90,48 @@ class OptTest { } assertContains(ex.errorMessage, "can't reassign val a") } + + @Test + fun testAssignOpErrorMessageFromExample() = runTest { + val source = Source( + "examples/error1.lyng", + """ + val a = 1 + a += 2 + """.trimIndent() + ) + + val ex = assertFailsWith { + Script.newScope().eval(source) + } + + assertContains(ex.errorMessage, "can't reassign val a") + } + + @Test + fun testClosuresInLaunchPool() = runTest { + eval($$""" + val result = Set() + val mu = Mutex() + fn doSomething(value) { + delay(100) + println(value) + mu.withLock { + result += value + } + } + + val lp = LaunchPool(4, 1000) + for (i in 1 .. 10) { + val ii: Int = i + lp.launch { + doSomething( ii ) + } + } + println("all tasks were placed into lauchpool") + lp.closeAndJoin() + println("ALL DONE: $result") + assertEquals((1..10).toSet(), result) + """.trimIndent()) + } }