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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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? {
|
||||
private fun declaredCollectionElementTypeForValue(value: Obj, rawName: String): TypeDecl? {
|
||||
var s: Scope? = this
|
||||
var hops = 0
|
||||
while (s != null && hops++ < 1024) {
|
||||
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()
|
||||
}
|
||||
s = s.parent
|
||||
@ -517,6 +513,20 @@ open class Scope(
|
||||
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>) {
|
||||
if (plan.isEmpty()) return
|
||||
slots.clear()
|
||||
|
||||
@ -30,6 +30,15 @@ import net.sergeych.lynon.LynonEncoder
|
||||
import net.sergeych.lynon.LynonType
|
||||
|
||||
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 {
|
||||
if (this === other) return true
|
||||
@ -127,7 +136,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
||||
other is ObjList ->
|
||||
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")
|
||||
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 {
|
||||
if (other is ObjList) {
|
||||
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
|
||||
list.addAll(otherList)
|
||||
} else {
|
||||
@ -152,6 +161,43 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
||||
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 {
|
||||
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
|
||||
// Fast path: int membership in a list of ints (common case in benches)
|
||||
|
||||
@ -416,6 +416,23 @@ class TypesTest {
|
||||
""".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
|
||||
// fun testAliasesInGenerics1() = runTest {
|
||||
// val scope = Script.newScope()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user