Make List/Set += and -= element-type aware
This commit is contained in:
parent
3bfb80a7c1
commit
c9021eb9cf
@ -500,16 +500,12 @@ open class Scope(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private fun declaredCollectionElementTypeForValue(value: Obj, rawName: String): TypeDecl? {
|
||||||
* Best-effort lookup of the declared Set element type for a runtime set instance.
|
|
||||||
* Returns null when type info is unavailable.
|
|
||||||
*/
|
|
||||||
fun declaredSetElementTypeForValue(value: Obj): TypeDecl? {
|
|
||||||
var s: Scope? = this
|
var s: Scope? = this
|
||||||
var hops = 0
|
var hops = 0
|
||||||
while (s != null && hops++ < 1024) {
|
while (s != null && hops++ < 1024) {
|
||||||
val decl = s.declaredTypeForValueInThisScope(value)
|
val decl = s.declaredTypeForValueInThisScope(value)
|
||||||
if (decl is TypeDecl.Generic && decl.name.substringAfterLast('.') == "Set") {
|
if (decl is TypeDecl.Generic && decl.name.substringAfterLast('.') == rawName) {
|
||||||
return decl.args.firstOrNull()
|
return decl.args.firstOrNull()
|
||||||
}
|
}
|
||||||
s = s.parent
|
s = s.parent
|
||||||
@ -517,6 +513,20 @@ open class Scope(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Best-effort lookup of the declared Set element type for a runtime set instance.
|
||||||
|
* Returns null when type info is unavailable.
|
||||||
|
*/
|
||||||
|
fun declaredSetElementTypeForValue(value: Obj): TypeDecl? =
|
||||||
|
declaredCollectionElementTypeForValue(value, "Set")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Best-effort lookup of the declared List element type for a runtime list instance.
|
||||||
|
* Returns null when type info is unavailable.
|
||||||
|
*/
|
||||||
|
fun declaredListElementTypeForValue(value: Obj): TypeDecl? =
|
||||||
|
declaredCollectionElementTypeForValue(value, "List")
|
||||||
|
|
||||||
internal fun applySlotPlanReset(plan: Map<String, Int>, records: Map<String, ObjRecord>) {
|
internal fun applySlotPlanReset(plan: Map<String, Int>, records: Map<String, ObjRecord>) {
|
||||||
if (plan.isEmpty()) return
|
if (plan.isEmpty()) return
|
||||||
slots.clear()
|
slots.clear()
|
||||||
|
|||||||
@ -30,6 +30,15 @@ import net.sergeych.lynon.LynonEncoder
|
|||||||
import net.sergeych.lynon.LynonType
|
import net.sergeych.lynon.LynonType
|
||||||
|
|
||||||
class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
||||||
|
private fun shouldTreatAsSingleElement(scope: Scope, other: Obj): Boolean {
|
||||||
|
if (!other.isInstanceOf(ObjIterable)) return true
|
||||||
|
val declaredElementType = scope.declaredListElementTypeForValue(this)
|
||||||
|
if (declaredElementType != null && matchesTypeDecl(scope, other, declaredElementType)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (other is ObjString || other is ObjBuffer) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun equals(scope: Scope, other: Obj): Boolean {
|
override suspend fun equals(scope: Scope, other: Obj): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
@ -127,7 +136,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
|||||||
other is ObjList ->
|
other is ObjList ->
|
||||||
ObjList((list + other.list).toMutableList())
|
ObjList((list + other.list).toMutableList())
|
||||||
|
|
||||||
other.isInstanceOf(ObjIterable) && other !is ObjString && other !is ObjBuffer -> {
|
!shouldTreatAsSingleElement(scope, other) && other.isInstanceOf(ObjIterable) -> {
|
||||||
val l = other.callMethod<ObjList>(scope, "toList")
|
val l = other.callMethod<ObjList>(scope, "toList")
|
||||||
ObjList((list + l.list).toMutableList())
|
ObjList((list + l.list).toMutableList())
|
||||||
}
|
}
|
||||||
@ -143,7 +152,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
|||||||
override suspend fun plusAssign(scope: Scope, other: Obj): Obj {
|
override suspend fun plusAssign(scope: Scope, other: Obj): Obj {
|
||||||
if (other is ObjList) {
|
if (other is ObjList) {
|
||||||
list.addAll(other.list)
|
list.addAll(other.list)
|
||||||
} else if (other.isInstanceOf(ObjIterable) && other !is ObjString && other !is ObjBuffer) {
|
} else if (!shouldTreatAsSingleElement(scope, other) && other.isInstanceOf(ObjIterable)) {
|
||||||
val otherList = (other.invokeInstanceMethod(scope, "toList") as ObjList).list
|
val otherList = (other.invokeInstanceMethod(scope, "toList") as ObjList).list
|
||||||
list.addAll(otherList)
|
list.addAll(otherList)
|
||||||
} else {
|
} else {
|
||||||
@ -152,6 +161,43 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun minus(scope: Scope, other: Obj): Obj {
|
||||||
|
val out = list.toMutableList()
|
||||||
|
if (shouldTreatAsSingleElement(scope, other)) {
|
||||||
|
out.remove(other)
|
||||||
|
return ObjList(out)
|
||||||
|
}
|
||||||
|
if (other.isInstanceOf(ObjIterable)) {
|
||||||
|
val toRemove = mutableSetOf<Obj>()
|
||||||
|
other.enumerate(scope) {
|
||||||
|
toRemove += it
|
||||||
|
true
|
||||||
|
}
|
||||||
|
out.removeAll { toRemove.contains(it) }
|
||||||
|
return ObjList(out)
|
||||||
|
}
|
||||||
|
out.remove(other)
|
||||||
|
return ObjList(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun minusAssign(scope: Scope, other: Obj): Obj {
|
||||||
|
if (shouldTreatAsSingleElement(scope, other)) {
|
||||||
|
list.remove(other)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
if (other.isInstanceOf(ObjIterable)) {
|
||||||
|
val toRemove = mutableSetOf<Obj>()
|
||||||
|
other.enumerate(scope) {
|
||||||
|
toRemove += it
|
||||||
|
true
|
||||||
|
}
|
||||||
|
list.removeAll { toRemove.contains(it) }
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
list.remove(other)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun contains(scope: Scope, other: Obj): Boolean {
|
override suspend fun contains(scope: Scope, other: Obj): Boolean {
|
||||||
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
|
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
|
||||||
// Fast path: int membership in a list of ints (common case in benches)
|
// Fast path: int membership in a list of ints (common case in benches)
|
||||||
|
|||||||
@ -416,6 +416,23 @@ class TypesTest {
|
|||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testListTyped() = runTest {
|
||||||
|
eval("""
|
||||||
|
var l = List<String>()
|
||||||
|
val typed: List<String> = l
|
||||||
|
assertEquals(List(), typed)
|
||||||
|
|
||||||
|
l += "foo"
|
||||||
|
assertEquals(List("foo"), l)
|
||||||
|
l -= "foo"
|
||||||
|
assertEquals(List(), l)
|
||||||
|
|
||||||
|
l += ["foo", "bar"]
|
||||||
|
assertEquals(List("foo", "bar"), l)
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
|
||||||
// @Test
|
// @Test
|
||||||
// fun testAliasesInGenerics1() = runTest {
|
// fun testAliasesInGenerics1() = runTest {
|
||||||
// val scope = Script.newScope()
|
// val scope = Script.newScope()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user