another MI bug fixed, added tests for MI serialization

This commit is contained in:
sergeych committed 2025-11-17 15:09:02 +01:00
1 parent 1e6dd89778
commit 41657b3558
4 files changed
+106 -13

No files matched your search

@@ -64,7 +64,7 @@ open class ObjClass(
/**
* All ancestors as a Set for fast `isInstanceOf` checks. Order is not guaranteed here and
* must not be used for resolution — use [parentsLinearized] instead.
* must not be used for resolution
*/
val allParentsSet: Set<ObjClass> =
buildSet {
@@ -173,7 +173,19 @@ open class ObjClass(
if (isRoot) {
c.constructorMeta?.let { meta ->
val argsHere = argsForThis ?: Arguments.EMPTY
// Assign constructor params into instance scope (unmangled)
meta.assignToContext(instance.instanceScope, argsHere)
// Also expose them under MI-mangled storage keys `${Class}::name` so qualified views can access them
// and so that base-class casts like `(obj as Base).field` work.
for (p in meta.params) {
val rec = instance.instanceScope.objects[p.name]
if (rec != null) {
val mangled = "${c.className}::${p.name}"
// Always point the mangled name to the current record to keep writes consistent
// across re-bindings (e.g., second pass before ctor)
instance.instanceScope.objects[mangled] = rec
}
}
}
}
// Initialize direct parents first, in order
@@ -203,6 +215,15 @@ open class ObjClass(
c.constructorMeta?.let { meta ->
val argsHere = argsForThis ?: Arguments.EMPTY
meta.assignToContext(instance.instanceScope, argsHere)
// Ensure mangled aliases exist for qualified access starting from this class
for (p in meta.params) {
val rec = instance.instanceScope.objects[p.name]
if (rec != null) {
val mangled = "${c.className}::${p.name}"
// Overwrite to ensure alias refers to the latest ObjRecord after re-binding
instance.instanceScope.objects[mangled] = rec
}
}
}
val execScope = instance.instanceScope.createChildScope(args = argsForThis ?: Arguments.EMPTY, newThisObj = instance)
ctor.execute(execScope)
@@ -259,7 +280,7 @@ open class ObjClass(
fun addFn(
name: String,
isOpen: Boolean = false,
visibility: net.sergeych.lyng.Visibility = net.sergeych.lyng.Visibility.Public,
visibility: Visibility = Visibility.Public,
code: suspend Scope.() -> Obj
) {
val stmt = statement { code() }
@@ -159,7 +159,10 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
private val publicFields: Map<String, ObjRecord>
get() = instanceScope.objects.filter { it.value.visibility.isPublic && it.value.type.serializable }
get() = instanceScope.objects.filter {
// Expose only human-facing fields: skip MI-mangled storage entries like "Class::name"
!it.key.contains("::") && it.value.visibility.isPublic && it.value.type.serializable
}
override fun toString(): String {
val fields = publicFields.map { "${it.key}=${it.value.value}" }.joinToString(",")
@@ -237,7 +240,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
// Visibility: declaring class is the qualified ancestor for mangled storage
val decl = rec.declaringClass ?: startClass
val caller = scope.currentClassCtx
if (!net.sergeych.lyng.canAccessMember(rec.visibility, decl, caller))
if (!canAccessMember(rec.visibility, decl, caller))
scope.raiseError(ObjAccessException(scope, "can't access field $name (declared in ${decl.className})"))
return rec
}
@@ -246,7 +249,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
instance.instanceScope[name]?.let { rec ->
val decl = rec.declaringClass ?: instance.objClass.findDeclaringClassOf(name)
val caller = scope.currentClassCtx
if (!net.sergeych.lyng.canAccessMember(rec.visibility, decl, caller))
if (!canAccessMember(rec.visibility, decl, caller))
scope.raiseError(ObjAccessException(scope, "can't access field $name (declared in ${decl?.className ?: "?"})"))
return rec
}
@@ -255,7 +258,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
val r = memberFromAncestor(name) ?: scope.raiseError("no such field: $name")
val decl = r.declaringClass ?: startClass
val caller = scope.currentClassCtx
if (!net.sergeych.lyng.canAccessMember(r.visibility, decl, caller))
if (!canAccessMember(r.visibility, decl, caller))
scope.raiseError(ObjAccessException(scope, "can't access field $name (declared in ${decl.className})"))
return when (val value = r.value) {
is net.sergeych.lyng.Statement -> ObjRecord(value.execute(instance.instanceScope.createChildScope(scope.pos, newThisObj = instance)), r.isMutable)
@@ -269,7 +272,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
instance.instanceScope.objects[mangled]?.let { f ->
val decl = f.declaringClass ?: startClass
val caller = scope.currentClassCtx
if (!net.sergeych.lyng.canAccessMember(f.visibility, decl, caller))
if (!canAccessMember(f.visibility, decl, caller))
ObjIllegalAssignmentException(scope, "can't assign to field $name (declared in ${decl.className})").raise()
if (!f.isMutable) ObjIllegalAssignmentException(scope, "can't reassign val $name").raise()
if (f.value.assign(scope, newValue) == null) f.value = newValue
@@ -280,7 +283,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
instance.instanceScope[name]?.let { f ->
val decl = f.declaringClass ?: instance.objClass.findDeclaringClassOf(name)
val caller = scope.currentClassCtx
if (!net.sergeych.lyng.canAccessMember(f.visibility, decl, caller))
if (!canAccessMember(f.visibility, decl, caller))
ObjIllegalAssignmentException(scope, "can't assign to field $name (declared in ${decl?.className ?: "?"})").raise()
if (!f.isMutable) ObjIllegalAssignmentException(scope, "can't reassign val $name").raise()
if (f.value.assign(scope, newValue) == null) f.value = newValue
@@ -290,7 +293,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
val r = memberFromAncestor(name) ?: scope.raiseError("no such field: $name")
val decl = r.declaringClass ?: startClass
val caller = scope.currentClassCtx
if (!net.sergeych.lyng.canAccessMember(r.visibility, decl, caller))
if (!canAccessMember(r.visibility, decl, caller))
ObjIllegalAssignmentException(scope, "can't assign to field $name (declared in ${decl.className})").raise()
if (!r.isMutable) scope.raiseError("can't assign to read-only field: $name")
if (r.value.assign(scope, newValue) == null) r.value = newValue
@@ -301,7 +304,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
memberFromAncestor(name)?.let { rec ->
val decl = rec.declaringClass ?: startClass
val caller = scope.currentClassCtx
if (!net.sergeych.lyng.canAccessMember(rec.visibility, decl, caller))
if (!canAccessMember(rec.visibility, decl, caller))
scope.raiseError(ObjAccessException(scope, "can't invoke method $name (declared in ${decl.className})"))
val saved = instance.instanceScope.currentClassCtx
instance.instanceScope.currentClassCtx = decl
@@ -316,7 +319,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
instance.instanceScope[name]?.let { rec ->
val decl = rec.declaringClass ?: instance.objClass.findDeclaringClassOf(name)
val caller = scope.currentClassCtx
if (!net.sergeych.lyng.canAccessMember(rec.visibility, decl, caller))
if (!canAccessMember(rec.visibility, decl, caller))
scope.raiseError(ObjAccessException(scope, "can't invoke method $name (declared in ${decl?.className ?: "?"})"))
val saved = instance.instanceScope.currentClassCtx
instance.instanceScope.currentClassCtx = decl