Fix argument priority handling in ObjInstance resolution logic and add corresponding tests
This commit is contained in:
parent
7ab439d949
commit
827df9c8cd
@ -347,7 +347,16 @@ open class Scope(
|
||||
// 1. Prefer direct locals/bindings declared in this frame
|
||||
tryGetLocalRecord(this, name, currentClassCtx)?.let { return it }
|
||||
|
||||
// 2. Then, check members of thisObj
|
||||
val p = parent
|
||||
|
||||
// 2. If we share thisObj with parent, delegate to parent to maintain
|
||||
// "locals shadow members" priority across the this-context.
|
||||
if (p != null && p.thisObj === thisObj) {
|
||||
return p.get(name)
|
||||
}
|
||||
|
||||
// 3. Otherwise, we are the "primary" scope for this thisObj (or have no parent),
|
||||
// so check members of thisObj before walking up to a different this-context.
|
||||
val receiver = thisObj
|
||||
val effectiveClass = receiver as? ObjClass ?: receiver.objClass
|
||||
for (cls in effectiveClass.mro) {
|
||||
@ -365,8 +374,8 @@ open class Scope(
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Finally, walk up ancestry
|
||||
return parent?.get(name)
|
||||
// 4. Finally, walk up ancestry to a scope with a different thisObj context
|
||||
return p?.get(name)
|
||||
}
|
||||
|
||||
// Slot fast-path API
|
||||
|
||||
@ -76,6 +76,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
||||
}
|
||||
|
||||
override suspend fun resolveRecord(scope: Scope, obj: ObjRecord, name: String, decl: ObjClass?): ObjRecord {
|
||||
if (obj.type.isArgument) return super.resolveRecord(scope, obj, name, decl)
|
||||
if (obj.type == ObjRecord.Type.Delegated) {
|
||||
val d = decl ?: obj.declaringClass
|
||||
val storageName = "${d?.className}::$name"
|
||||
|
||||
@ -20,6 +20,7 @@ import net.sergeych.lyng.Script
|
||||
import net.sergeych.lyng.eval
|
||||
import net.sergeych.lyng.obj.ObjInstance
|
||||
import net.sergeych.lyng.obj.ObjList
|
||||
import net.sergeych.lyng.toSource
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFails
|
||||
@ -731,4 +732,48 @@ class OOTest {
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testArgsPriority() = runTest {
|
||||
eval("""
|
||||
class A(id) {
|
||||
var stored = null
|
||||
// Arguments should have priority on
|
||||
// instance fields
|
||||
fun setStored(id) { stored = id }
|
||||
}
|
||||
val a = A(1)
|
||||
assertEquals(1, a.id)
|
||||
assertEquals(null, a.stored)
|
||||
|
||||
// Check that arguments of the call have the priority:
|
||||
a.setStored(2)
|
||||
assertEquals(1, a.id)
|
||||
assertEquals(2, a.stored)
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
/**
|
||||
* Demonstrates that function parameters are shadowed by class methods of the same name
|
||||
* when accessed within a block, but not in a single expression.
|
||||
*/
|
||||
@Test
|
||||
fun testParameterShadowingConflict() = runTest {
|
||||
val scope = Script.newScope()
|
||||
val result = scope.eval("""
|
||||
class Tester() {
|
||||
fun id() { "method" }
|
||||
// This correctly returns "success"
|
||||
fun checkOk(id) = id
|
||||
// This incorrectly returns the 'id' method (a Callable) instead of "success"
|
||||
fun checkFail(id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
val t = Tester()
|
||||
if (t.checkOk("success") != "success") throw "checkOk failed"
|
||||
t.checkFail("success")
|
||||
""".trimIndent().toSource("repro"))
|
||||
|
||||
assertEquals("success", result.toString(), "Parameter 'id' should shadow method 'id' in block")
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user