v1.5.5 release
This commit is contained in:
1 parent
14214e91e1
commit
9735774efd
13 files changed
+269
-34
No files matched your search
@@ -2881,14 +2881,18 @@ class BytecodeCompiler(
|
||||
val slot = resolveCapturedOwnerScopeSlot(localTarget) ?: resolveSlot(localTarget) ?: return null
|
||||
val targetType = slotTypes[slot] ?: SlotType.OBJ
|
||||
if (!localTarget.isMutable) {
|
||||
if (targetType != SlotType.OBJ && targetType != SlotType.UNKNOWN) return compileEvalRef(ref)
|
||||
val rhs = compileRef(ref.value) ?: return compileEvalRef(ref)
|
||||
if (!shouldUseAssignOpForImmutableLocal(slot, targetType, ref.op)) {
|
||||
emitImmutableLocalReassignError(localTarget.name, localTarget.pos())
|
||||
return rhs
|
||||
}
|
||||
val rhsObj = ensureObjSlot(rhs)
|
||||
val nameId = builder.addConst(BytecodeConst.StringVal(localTarget.name))
|
||||
if (nameId > 0xFFFF) return compileEvalRef(ref)
|
||||
val dst = allocSlot()
|
||||
builder.emit(Opcode.ASSIGN_OP_OBJ, ref.op.ordinal, slot, rhsObj.slot, dst, nameId)
|
||||
updateSlotType(dst, SlotType.OBJ)
|
||||
slotObjClass[slot]?.let { slotObjClass[dst] = it }
|
||||
return CompiledValue(dst, SlotType.OBJ)
|
||||
}
|
||||
var rhs = compileRef(ref.value) ?: return compileEvalRef(ref)
|
||||
@@ -8446,6 +8450,38 @@ class BytecodeCompiler(
|
||||
builder.emit(Opcode.THROW, posId, msgSlot)
|
||||
return msgSlot
|
||||
}
|
||||
|
||||
private fun assignOpMethodName(op: BinOp): String? = when (op) {
|
||||
BinOp.PLUS -> "plusAssign"
|
||||
BinOp.MINUS -> "minusAssign"
|
||||
BinOp.STAR -> "mulAssign"
|
||||
BinOp.SLASH -> "divAssign"
|
||||
BinOp.PERCENT -> "modAssign"
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun knownBuiltinAssignOpSupport(cls: ObjClass, op: BinOp): Boolean? = when (cls) {
|
||||
ObjList.type -> op == BinOp.PLUS || op == BinOp.MINUS
|
||||
ObjObservableList.type -> op == BinOp.PLUS || op == BinOp.MINUS
|
||||
ObjSet.type -> op == BinOp.PLUS
|
||||
ObjMap.type -> op == BinOp.PLUS
|
||||
ObjRingBuffer.type -> op == BinOp.PLUS
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun shouldUseAssignOpForImmutableLocal(slot: Int, targetType: SlotType, op: BinOp): Boolean {
|
||||
return when (targetType) {
|
||||
SlotType.INT, SlotType.REAL, SlotType.BOOL -> false
|
||||
SlotType.OBJ, SlotType.UNKNOWN -> {
|
||||
val cls = slotObjClass[slot] ?: return true
|
||||
val methodName = assignOpMethodName(op)
|
||||
if (methodName != null && cls.getInstanceMemberOrNull(methodName, includeStatic = false) != null) {
|
||||
return true
|
||||
}
|
||||
knownBuiltinAssignOpSupport(cls, op) ?: true
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun binaryLeft(ref: BinaryOpRef): ObjRef = ref.left
|
||||
private fun binaryRight(ref: BinaryOpRef): ObjRef = ref.right
|
||||
private fun binaryOp(ref: BinaryOpRef): BinOp = ref.op
|
||||
|
||||
@@ -104,14 +104,14 @@ class WebsiteSamplesTest {
|
||||
val name = "Lyng"
|
||||
val base = { id:, name: } // Shorthand for id: id, name: name
|
||||
|
||||
val full = { ...base, version: "1.5.4", status: "stable" }
|
||||
val full = { ...base, version: "1.5.5", status: "stable" }
|
||||
full
|
||||
""".trimIndent())
|
||||
assertTrue(result is ObjMap)
|
||||
val m = result.map
|
||||
assertEquals(101L, (m[ObjString("id")] as ObjInt).value)
|
||||
assertEquals("Lyng", (m[ObjString("name")] as ObjString).value)
|
||||
assertEquals("1.5.4", (m[ObjString("version")] as ObjString).value)
|
||||
assertEquals("1.5.5", (m[ObjString("version")] as ObjString).value)
|
||||
assertEquals("stable", (m[ObjString("status")] as ObjString).value)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ package net.sergeych.lyng
|
||||
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertContains
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class OperatorOverloadingTest {
|
||||
@Test
|
||||
@@ -63,6 +65,24 @@ class OperatorOverloadingTest {
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBuiltinListPlusAssignOnVal() = runTest {
|
||||
eval("""
|
||||
val list = [1, 2]
|
||||
list += 3
|
||||
assertEquals([1, 2, 3], list)
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBuiltinListMinusAssignOnVal() = runTest {
|
||||
eval("""
|
||||
val list = [1, 2, 3]
|
||||
list -= 2
|
||||
assertEquals([1, 3], list)
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPlusAssignFallback() = runTest {
|
||||
eval("""
|
||||
@@ -76,6 +96,130 @@ class OperatorOverloadingTest {
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPlusAssignFallbackOnValReportsReadonlyError() = runTest {
|
||||
val ex = assertFailsWith<ScriptError> {
|
||||
eval("""
|
||||
class Vector(var x: Int, var y: Int) {
|
||||
fun plus(other: Vector) = Vector(this.x + other.x, this.y + other.y)
|
||||
fun equals(other: Vector) = this.x == other.x && this.y == other.y
|
||||
}
|
||||
val v = Vector(1, 2)
|
||||
v += Vector(3, 4)
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
assertContains(ex.errorMessage, "can't reassign val v")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMinusAssignOverloadingOnVal() = runTest {
|
||||
eval("""
|
||||
class Counter(var n: Int) {
|
||||
fun minusAssign(x: Int) { this.n = this.n - x }
|
||||
}
|
||||
val c = Counter(10)
|
||||
c -= 3
|
||||
assertEquals(7, c.n)
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMinusAssignFallbackOnValReportsReadonlyError() = runTest {
|
||||
val ex = assertFailsWith<ScriptError> {
|
||||
eval("""
|
||||
class Counter(var n: Int) {
|
||||
fun minus(x: Int) = Counter(this.n - x)
|
||||
}
|
||||
val c = Counter(10)
|
||||
c -= 3
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
assertContains(ex.errorMessage, "can't reassign val c")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMulAssignOverloadingOnVal() = runTest {
|
||||
eval("""
|
||||
class Counter(var n: Int) {
|
||||
fun mulAssign(x: Int) { this.n = this.n * x }
|
||||
}
|
||||
val c = Counter(10)
|
||||
c *= 3
|
||||
assertEquals(30, c.n)
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMulAssignFallbackOnValReportsReadonlyError() = runTest {
|
||||
val ex = assertFailsWith<ScriptError> {
|
||||
eval("""
|
||||
class Counter(var n: Int) {
|
||||
fun times(x: Int) = Counter(this.n * x)
|
||||
}
|
||||
val c = Counter(10)
|
||||
c *= 3
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
assertContains(ex.errorMessage, "can't reassign val c")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDivAssignOverloadingOnVal() = runTest {
|
||||
eval("""
|
||||
class Counter(var n: Int) {
|
||||
fun divAssign(x: Int) { this.n = this.n / x }
|
||||
}
|
||||
val c = Counter(21)
|
||||
c /= 3
|
||||
assertEquals(7, c.n)
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDivAssignFallbackOnValReportsReadonlyError() = runTest {
|
||||
val ex = assertFailsWith<ScriptError> {
|
||||
eval("""
|
||||
class Counter(var n: Int) {
|
||||
fun div(x: Int) = Counter(this.n / x)
|
||||
}
|
||||
val c = Counter(21)
|
||||
c /= 3
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
assertContains(ex.errorMessage, "can't reassign val c")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testModAssignOverloadingOnVal() = runTest {
|
||||
eval("""
|
||||
class Counter(var n: Int) {
|
||||
fun modAssign(x: Int) { this.n = this.n % x }
|
||||
}
|
||||
val c = Counter(23)
|
||||
c %= 5
|
||||
assertEquals(3, c.n)
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testModAssignFallbackOnValReportsReadonlyError() = runTest {
|
||||
val ex = assertFailsWith<ScriptError> {
|
||||
eval("""
|
||||
class Counter(var n: Int) {
|
||||
fun mod(x: Int) = Counter(this.n % x)
|
||||
}
|
||||
val c = Counter(23)
|
||||
c %= 5
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
assertContains(ex.errorMessage, "can't reassign val c")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCompareOverloading() = runTest {
|
||||
eval("""
|
||||
|
||||
Reference in new issue
Block a user