fixed mist edge cases with new language logic
This commit is contained in:
parent
b9831a422a
commit
91e6ae29ce
@ -46,7 +46,7 @@ class ClosureScope(val callScope: Scope, val closureScope: Scope) :
|
||||
tryGetLocalRecord(this, name, currentClassCtx)?.let { return it }
|
||||
|
||||
// 2. Lexical environment (captured locals from entire ancestry)
|
||||
closureScope.chainLookupIgnoreClosure(name, followClosure = true)?.let { return it }
|
||||
closureScope.chainLookupIgnoreClosure(name, followClosure = true, caller = currentClassCtx)?.let { return it }
|
||||
|
||||
// 3. Lexical this members (captured receiver)
|
||||
val receiver = thisObj
|
||||
|
||||
@ -1482,7 +1482,14 @@ class Compiler(
|
||||
miniSink?.onInitDecl(MiniInitDecl(MiniRange(id.pos, range.end), id.pos))
|
||||
}
|
||||
val initStmt = statement(id.pos) { scp ->
|
||||
block.execute(scp)
|
||||
val cls = scp.thisObj.objClass
|
||||
val saved = scp.currentClassCtx
|
||||
scp.currentClassCtx = cls
|
||||
try {
|
||||
block.execute(scp)
|
||||
} finally {
|
||||
scp.currentClassCtx = saved
|
||||
}
|
||||
ObjVoid
|
||||
}
|
||||
statement {
|
||||
@ -3313,9 +3320,11 @@ class Compiler(
|
||||
val setArg = cc.requireToken(Token.Type.ID, "Expected setter argument name")
|
||||
cc.requireToken(Token.Type.RPAREN)
|
||||
miniSink?.onEnterFunction(null)
|
||||
setter = if (cc.peekNextNonWhitespace().type == Token.Type.LBRACE) {
|
||||
val finalSetter = if (cc.peekNextNonWhitespace().type == Token.Type.LBRACE) {
|
||||
cc.skipWsTokens()
|
||||
val body = parseBlock()
|
||||
val body = inCodeContext(CodeContext.Function("<setter>")) {
|
||||
parseBlock()
|
||||
}
|
||||
statement(body.pos) { scope ->
|
||||
val value = scope.args.list.firstOrNull() ?: ObjNull
|
||||
scope.addItem(setArg.value, true, value, recordType = ObjRecord.Type.Argument)
|
||||
@ -3324,11 +3333,12 @@ class Compiler(
|
||||
} else if (cc.peekNextNonWhitespace().type == Token.Type.ASSIGN) {
|
||||
cc.skipWsTokens()
|
||||
cc.next() // consume '='
|
||||
val expr = parseExpression() ?: throw ScriptError(
|
||||
cc.current().pos,
|
||||
"Expected setter expression"
|
||||
)
|
||||
val st = expr
|
||||
val st = inCodeContext(CodeContext.Function("<setter>")) {
|
||||
parseExpression() ?: throw ScriptError(
|
||||
cc.current().pos,
|
||||
"Expected setter expression"
|
||||
)
|
||||
}
|
||||
statement(st.pos) { scope ->
|
||||
val value = scope.args.list.firstOrNull() ?: ObjNull
|
||||
scope.addItem(setArg.value, true, value, recordType = ObjRecord.Type.Argument)
|
||||
@ -3337,6 +3347,7 @@ class Compiler(
|
||||
} else {
|
||||
throw ScriptError(cc.current().pos, "Expected { or = after set(...)")
|
||||
}
|
||||
setter = finalSetter
|
||||
miniSink?.onExitFunction(cc.currentPos())
|
||||
} else {
|
||||
// private set without body: setter remains null, visibility is restricted
|
||||
@ -3383,6 +3394,7 @@ class Compiler(
|
||||
// In true class bodies (not inside a function), store fields under a class-qualified key to support MI collisions
|
||||
// Do NOT infer declaring class from runtime thisObj here; only the compile-time captured
|
||||
// ClassBody qualifies for class-field storage. Otherwise, this is a plain local.
|
||||
isProperty = getter != null || setter != null
|
||||
val declaringClassName = declaringClassNameCaptured
|
||||
if (declaringClassName == null) {
|
||||
if (context.containsLocal(name))
|
||||
@ -3488,7 +3500,8 @@ class Compiler(
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride,
|
||||
pos = start
|
||||
pos = start,
|
||||
prop = prop
|
||||
)
|
||||
} else {
|
||||
cls.createField(
|
||||
|
||||
@ -107,9 +107,19 @@ open class Scope(
|
||||
* and bindings of each frame. Instance/class member fallback must be decided by the caller.
|
||||
*/
|
||||
internal fun tryGetLocalRecord(s: Scope, name: String, caller: net.sergeych.lyng.obj.ObjClass?): ObjRecord? {
|
||||
caller?.let { ctx ->
|
||||
s.objects["${ctx.className}::$name"]?.let { rec ->
|
||||
if (rec.visibility == Visibility.Private) return rec
|
||||
}
|
||||
}
|
||||
s.objects[name]?.let { rec ->
|
||||
if (rec.declaringClass == null || canAccessMember(rec.visibility, rec.declaringClass, caller)) return rec
|
||||
}
|
||||
caller?.let { ctx ->
|
||||
s.localBindings["${ctx.className}::$name"]?.let { rec ->
|
||||
if (rec.visibility == Visibility.Private) return rec
|
||||
}
|
||||
}
|
||||
s.localBindings[name]?.let { rec ->
|
||||
if (rec.declaringClass == null || canAccessMember(rec.visibility, rec.declaringClass, caller)) return rec
|
||||
}
|
||||
@ -120,13 +130,14 @@ open class Scope(
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun chainLookupIgnoreClosure(name: String, followClosure: Boolean = false): ObjRecord? {
|
||||
internal fun chainLookupIgnoreClosure(name: String, followClosure: Boolean = false, caller: net.sergeych.lyng.obj.ObjClass? = null): ObjRecord? {
|
||||
var s: Scope? = this
|
||||
// use frameId to detect unexpected structural cycles in the parent chain
|
||||
val visited = HashSet<Long>(4)
|
||||
val effectiveCaller = caller ?: currentClassCtx
|
||||
while (s != null) {
|
||||
if (!visited.add(s.frameId)) return null
|
||||
tryGetLocalRecord(s, name, currentClassCtx)?.let { return it }
|
||||
tryGetLocalRecord(s, name, effectiveCaller)?.let { return it }
|
||||
s = if (followClosure && s is ClosureScope) s.closureScope else s.parent
|
||||
}
|
||||
return null
|
||||
@ -334,12 +345,7 @@ open class Scope(
|
||||
if (name == "this") return thisObj.asReadonly
|
||||
|
||||
// 1. Prefer direct locals/bindings declared in this frame
|
||||
objects[name]?.let { rec ->
|
||||
if (rec.declaringClass == null || canAccessMember(rec.visibility, rec.declaringClass, currentClassCtx)) return rec
|
||||
}
|
||||
localBindings[name]?.let { rec ->
|
||||
if (rec.declaringClass == null || canAccessMember(rec.visibility, rec.declaringClass, currentClassCtx)) return rec
|
||||
}
|
||||
tryGetLocalRecord(this, name, currentClassCtx)?.let { return it }
|
||||
|
||||
// 2. Then, check members of thisObj
|
||||
val receiver = thisObj
|
||||
|
||||
@ -253,28 +253,29 @@ open class ObjClass(
|
||||
// remains stable even when call frames are pooled and reused.
|
||||
val stableParent = classScope ?: scope.parent
|
||||
instance.instanceScope = Scope(stableParent, scope.args, scope.pos, instance)
|
||||
// println("[DEBUG_LOG] createInstance: created $instance scope@${instance.instanceScope.hashCode()}")
|
||||
instance.instanceScope.currentClassCtx = null
|
||||
// Expose instance methods (and other callable members) directly in the instance scope for fast lookup
|
||||
// This mirrors Obj.autoInstanceScope behavior for ad-hoc scopes and makes fb.method() resolution robust
|
||||
|
||||
for (cls in mro) {
|
||||
// 1) members-defined methods
|
||||
// 1) members-defined methods and fields
|
||||
for ((k, v) in cls.members) {
|
||||
if (v.value is Statement || v.type == ObjRecord.Type.Delegated) {
|
||||
val key = if (v.visibility == Visibility.Private) "${cls.className}::$k" else k
|
||||
if (!v.isAbstract && (v.value is Statement || v.type == ObjRecord.Type.Delegated || v.type == ObjRecord.Type.Field)) {
|
||||
val key = if (v.visibility == Visibility.Private || v.type == ObjRecord.Type.Field || v.type == ObjRecord.Type.Delegated) "${cls.className}::$k" else k
|
||||
if (!instance.instanceScope.objects.containsKey(key)) {
|
||||
instance.instanceScope.objects[key] = if (v.type == ObjRecord.Type.Delegated) v.copy() else v
|
||||
instance.instanceScope.objects[key] = if (v.type == ObjRecord.Type.Fun) v else v.copy()
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2) class-scope methods registered during class-body execution
|
||||
// 2) class-scope members registered during class-body execution
|
||||
cls.classScope?.objects?.forEach { (k, rec) ->
|
||||
if (rec.value is Statement || rec.type == ObjRecord.Type.Delegated) {
|
||||
val key = if (rec.visibility == Visibility.Private) "${cls.className}::$k" else k
|
||||
// ONLY copy methods and delegated members from class scope to instance scope.
|
||||
// Fields in class scope are static fields and must NOT be per-instance.
|
||||
if (!rec.isAbstract && (rec.value is Statement || rec.type == ObjRecord.Type.Delegated)) {
|
||||
val key = if (rec.visibility == Visibility.Private || rec.type == ObjRecord.Type.Delegated) "${cls.className}::$k" else k
|
||||
// if not already present, copy reference for dispatch
|
||||
if (!instance.instanceScope.objects.containsKey(key)) {
|
||||
instance.instanceScope.objects[key] = if (rec.type == ObjRecord.Type.Delegated) rec.copy() else rec
|
||||
instance.instanceScope.objects[key] = if (rec.type == ObjRecord.Type.Fun) rec else rec.copy()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -512,12 +513,13 @@ open class ObjClass(
|
||||
isClosed: Boolean = false,
|
||||
isOverride: Boolean = false,
|
||||
pos: Pos = Pos.builtIn,
|
||||
prop: ObjProperty? = null
|
||||
) {
|
||||
val g = getter?.let { statement { it() } }
|
||||
val s = setter?.let { statement { it(requiredArg(0)); ObjVoid } }
|
||||
val prop = if (isAbstract) ObjNull else ObjProperty(name, g, s)
|
||||
val finalProp = prop ?: if (isAbstract) ObjNull else ObjProperty(name, g, s)
|
||||
createField(
|
||||
name, prop, false, visibility, writeVisibility, pos, declaringClass,
|
||||
name, finalProp, false, visibility, writeVisibility, pos, declaringClass,
|
||||
isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride,
|
||||
type = ObjRecord.Type.Property
|
||||
)
|
||||
|
||||
@ -36,6 +36,13 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
||||
|
||||
// 0. Private mangled of current class context
|
||||
caller?.let { c ->
|
||||
// Check for private methods/properties
|
||||
c.members[name]?.let { rec ->
|
||||
if (rec.visibility == Visibility.Private) {
|
||||
return resolveRecord(scope, rec, name, c)
|
||||
}
|
||||
}
|
||||
// Check for private fields (stored in instanceScope)
|
||||
val mangled = "${c.className}::$name"
|
||||
instanceScope.objects[mangled]?.let { rec ->
|
||||
if (rec.visibility == Visibility.Private) {
|
||||
@ -59,8 +66,9 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
||||
instanceScope.objects[name]?.let { rec ->
|
||||
val decl = rec.declaringClass
|
||||
// Unmangled access is only allowed if it's public OR we are inside the same instance's method
|
||||
if ((scope.thisObj === this && caller != null) || canAccessMember(rec.visibility, decl, caller))
|
||||
if ((scope.thisObj === this && caller != null) || canAccessMember(rec.visibility, decl, caller)) {
|
||||
return resolveRecord(scope, rec, name, decl)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Fall back to super (handles class members and extensions)
|
||||
@ -113,6 +121,14 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
||||
|
||||
// 0. Private mangled of current class context
|
||||
caller?.let { c ->
|
||||
// Check for private methods/properties
|
||||
c.members[name]?.let { rec ->
|
||||
if (rec.visibility == Visibility.Private) {
|
||||
updateRecord(scope, resolveRecord(scope, rec, name, c), name, newValue, c)
|
||||
return
|
||||
}
|
||||
}
|
||||
// Check for private fields (stored in instanceScope)
|
||||
val mangled = "${c.className}::$name"
|
||||
instanceScope.objects[mangled]?.let { rec ->
|
||||
if (rec.visibility == Visibility.Private) {
|
||||
@ -343,7 +359,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
||||
if (other.objClass != objClass) return -1
|
||||
for (f in comparableVars) {
|
||||
val a = f.value.value
|
||||
val b = other.instanceScope[f.key]!!.value
|
||||
val b = other.instanceScope.objects[f.key]?.value ?: scope.raiseError("Internal error: field ${f.key} not found in other instance")
|
||||
val d = a.compareTo(scope, b)
|
||||
if (d != 0) return d
|
||||
}
|
||||
@ -370,7 +386,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
|
||||
val caller = scope.currentClassCtx
|
||||
if (!canAccessMember(rec.visibility, decl, caller))
|
||||
scope.raiseError(ObjIllegalAccessException(scope, "can't access field $name (declared in ${decl.className})"))
|
||||
return resolveRecord(scope, rec, name, decl)
|
||||
return instance.resolveRecord(scope, rec, name, decl)
|
||||
}
|
||||
// Then try instance locals (unmangled) only if startClass is the dynamic class itself
|
||||
if (startClass === instance.objClass) {
|
||||
@ -384,7 +400,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
|
||||
"can't access field $name (declared in ${decl?.className ?: "?"})"
|
||||
)
|
||||
)
|
||||
return resolveRecord(scope, rec, name, decl)
|
||||
return instance.resolveRecord(scope, rec, name, decl)
|
||||
}
|
||||
}
|
||||
// Finally try methods/properties starting from ancestor
|
||||
@ -394,7 +410,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
|
||||
if (!canAccessMember(r.visibility, decl, caller))
|
||||
scope.raiseError(ObjIllegalAccessException(scope, "can't access field $name (declared in ${decl.className})"))
|
||||
|
||||
return resolveRecord(scope, r, name, decl)
|
||||
return instance.resolveRecord(scope, r, name, decl)
|
||||
}
|
||||
|
||||
override suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user