+ @Tranient for serialization
This commit is contained in:
1 parent
05e15e8e42
commit
b7dfda2f5d
14 files changed
+450
-54
No files matched your search
@@ -71,7 +71,8 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
||||
value.byValueCopy(),
|
||||
a.visibility ?: defaultVisibility,
|
||||
recordType = ObjRecord.Type.Argument,
|
||||
declaringClass = declaringClass)
|
||||
declaringClass = declaringClass,
|
||||
isTransient = a.isTransient)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -82,7 +83,8 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
||||
value.byValueCopy(),
|
||||
a.visibility ?: defaultVisibility,
|
||||
recordType = ObjRecord.Type.Argument,
|
||||
declaringClass = declaringClass)
|
||||
declaringClass = declaringClass,
|
||||
isTransient = a.isTransient)
|
||||
}
|
||||
|
||||
// Prepare positional args and parameter count, handle tail-block binding
|
||||
@@ -239,5 +241,6 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
||||
val defaultValue: Statement? = null,
|
||||
val accessType: AccessType? = null,
|
||||
val visibility: Visibility? = null,
|
||||
val isTransient: Boolean = false,
|
||||
)
|
||||
}
|
||||
@@ -283,11 +283,13 @@ class Compiler(
|
||||
}
|
||||
|
||||
private var lastAnnotation: (suspend (Scope, ObjString, Statement) -> Statement)? = null
|
||||
private var isTransientFlag: Boolean = false
|
||||
private var lastLabel: String? = null
|
||||
|
||||
private suspend fun parseStatement(braceMeansLambda: Boolean = false): Statement? {
|
||||
lastAnnotation = null
|
||||
lastLabel = null
|
||||
isTransientFlag = false
|
||||
while (true) {
|
||||
val t = cc.next()
|
||||
return when (t.type) {
|
||||
@@ -306,6 +308,10 @@ class Compiler(
|
||||
|
||||
Token.Type.ATLABEL -> {
|
||||
val label = t.value
|
||||
if (label == "Transient") {
|
||||
isTransientFlag = true
|
||||
continue
|
||||
}
|
||||
if (cc.peekNextNonWhitespace().type == Token.Type.LBRACE) {
|
||||
lastLabel = label
|
||||
}
|
||||
@@ -887,7 +893,15 @@ class Compiler(
|
||||
Token.Type.NEWLINE -> {}
|
||||
Token.Type.MULTILINE_COMMENT, Token.Type.SINGLE_LINE_COMMENT -> {}
|
||||
|
||||
Token.Type.ID -> {
|
||||
Token.Type.ID, Token.Type.ATLABEL -> {
|
||||
var isTransient = false
|
||||
if (t.type == Token.Type.ATLABEL) {
|
||||
if (t.value == "Transient") {
|
||||
isTransient = true
|
||||
t = cc.next()
|
||||
} else throw ScriptError(t.pos, "Unexpected label in argument list")
|
||||
}
|
||||
|
||||
// visibility
|
||||
val visibility = if (isClassDeclaration && t.value == "private") {
|
||||
t = cc.next()
|
||||
@@ -931,7 +945,8 @@ class Compiler(
|
||||
isEllipsis,
|
||||
defaultValue,
|
||||
access,
|
||||
visibility
|
||||
visibility,
|
||||
isTransient
|
||||
)
|
||||
|
||||
// important: valid argument list continues with ',' and ends with '->' or ')'
|
||||
@@ -2015,6 +2030,7 @@ class Compiler(
|
||||
|
||||
val newClass = ObjInstanceClass(className, *parentClasses.toTypedArray())
|
||||
newClass.isAnonymous = nameToken == null
|
||||
newClass.constructorMeta = ArgsDeclaration(emptyList(), Token.Type.RPAREN)
|
||||
for (i in parentClasses.indices) {
|
||||
val argsList = baseSpecs[i].args
|
||||
// In object, we evaluate parent args once at creation time
|
||||
@@ -2194,6 +2210,7 @@ class Compiler(
|
||||
// but we should pass Pos.builtIn to skip validation for now if needed,
|
||||
// or p.pos to allow it.
|
||||
pos = Pos.builtIn,
|
||||
isTransient = p.isTransient,
|
||||
type = ObjRecord.Type.ConstructorField
|
||||
)
|
||||
}
|
||||
@@ -2663,7 +2680,9 @@ class Compiler(
|
||||
isOverride: Boolean = false,
|
||||
isExtern: Boolean = false,
|
||||
isStatic: Boolean = false,
|
||||
isTransient: Boolean = isTransientFlag
|
||||
): Statement {
|
||||
isTransientFlag = false
|
||||
val actualExtern = isExtern || (codeContexts.lastOrNull() as? CodeContext.ClassBody)?.isExtern == true
|
||||
var t = cc.next()
|
||||
val start = t.pos
|
||||
@@ -2833,16 +2852,16 @@ class Compiler(
|
||||
|
||||
val th = context.thisObj
|
||||
if (isStatic) {
|
||||
(th as ObjClass).createClassField(name, ObjUnset, false, visibility, null, start, type = ObjRecord.Type.Delegated).apply {
|
||||
(th as ObjClass).createClassField(name, ObjUnset, false, visibility, null, start, isTransient = isTransient, type = ObjRecord.Type.Delegated).apply {
|
||||
delegate = finalDelegate
|
||||
}
|
||||
context.addItem(name, false, ObjUnset, visibility, recordType = ObjRecord.Type.Delegated).apply {
|
||||
context.addItem(name, false, ObjUnset, visibility, recordType = ObjRecord.Type.Delegated, isTransient = isTransient).apply {
|
||||
delegate = finalDelegate
|
||||
}
|
||||
} else if (th is ObjClass) {
|
||||
val cls: ObjClass = th
|
||||
val storageName = "${cls.className}::$name"
|
||||
cls.createField(name, ObjUnset, false, visibility, null, start, declaringClass = cls, isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride, type = ObjRecord.Type.Delegated)
|
||||
cls.createField(name, ObjUnset, false, visibility, null, start, declaringClass = cls, isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride, isTransient = isTransient, type = ObjRecord.Type.Delegated)
|
||||
cls.instanceInitializers += statement(start) { scp ->
|
||||
val accessType2 = scp.resolveQualifiedIdentifier("DelegateAccess.Callable")
|
||||
val initValue2 = delegateExpression.execute(scp)
|
||||
@@ -2851,13 +2870,13 @@ class Compiler(
|
||||
} catch (e: Exception) {
|
||||
initValue2
|
||||
}
|
||||
scp.addItem(storageName, false, ObjUnset, visibility, null, recordType = ObjRecord.Type.Delegated, isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride).apply {
|
||||
scp.addItem(storageName, false, ObjUnset, visibility, null, recordType = ObjRecord.Type.Delegated, isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride, isTransient = isTransient).apply {
|
||||
delegate = finalDelegate2
|
||||
}
|
||||
ObjVoid
|
||||
}
|
||||
} else {
|
||||
context.addItem(name, false, ObjUnset, visibility, recordType = ObjRecord.Type.Delegated).apply {
|
||||
context.addItem(name, false, ObjUnset, visibility, recordType = ObjRecord.Type.Delegated, isTransient = isTransient).apply {
|
||||
delegate = finalDelegate
|
||||
}
|
||||
}
|
||||
@@ -2986,8 +3005,10 @@ class Compiler(
|
||||
isClosed: Boolean = false,
|
||||
isOverride: Boolean = false,
|
||||
isStatic: Boolean = false,
|
||||
isExtern: Boolean = false
|
||||
isExtern: Boolean = false,
|
||||
isTransient: Boolean = isTransientFlag
|
||||
): Statement {
|
||||
isTransientFlag = false
|
||||
val actualExtern = isExtern || (codeContexts.lastOrNull() as? CodeContext.ClassBody)?.isExtern == true
|
||||
val nextToken = cc.next()
|
||||
val start = nextToken.pos
|
||||
@@ -3031,7 +3052,7 @@ class Compiler(
|
||||
return statement(start) { context ->
|
||||
val value = initialExpression.execute(context)
|
||||
for (name in names) {
|
||||
context.addItem(name, true, ObjVoid, visibility)
|
||||
context.addItem(name, true, ObjVoid, visibility, isTransient = isTransient)
|
||||
}
|
||||
pattern.setAt(start, context, value)
|
||||
if (!isMutable) {
|
||||
@@ -3233,17 +3254,18 @@ class Compiler(
|
||||
visibility,
|
||||
null,
|
||||
start,
|
||||
isTransient = isTransient,
|
||||
type = ObjRecord.Type.Delegated
|
||||
).apply {
|
||||
delegate = finalDelegate
|
||||
}
|
||||
// Also expose in current init scope
|
||||
addItem(name, isMutable, ObjUnset, visibility, null, ObjRecord.Type.Delegated).apply {
|
||||
addItem(name, isMutable, ObjUnset, visibility, null, ObjRecord.Type.Delegated, isTransient = isTransient).apply {
|
||||
delegate = finalDelegate
|
||||
}
|
||||
} else {
|
||||
(thisObj as ObjClass).createClassField(name, initValue, isMutable, visibility, null, start)
|
||||
addItem(name, isMutable, initValue, visibility, null, ObjRecord.Type.Field)
|
||||
(thisObj as ObjClass).createClassField(name, initValue, isMutable, visibility, null, start, isTransient = isTransient)
|
||||
addItem(name, isMutable, initValue, visibility, null, ObjRecord.Type.Field, isTransient = isTransient)
|
||||
}
|
||||
ObjVoid
|
||||
}
|
||||
@@ -3429,6 +3451,7 @@ class Compiler(
|
||||
visibility,
|
||||
setterVisibility,
|
||||
start,
|
||||
isTransient = isTransient,
|
||||
type = ObjRecord.Type.Delegated,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
@@ -3448,7 +3471,8 @@ class Compiler(
|
||||
recordType = ObjRecord.Type.Delegated,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride
|
||||
isOverride = isOverride,
|
||||
isTransient = isTransient
|
||||
).apply {
|
||||
delegate = finalDelegate
|
||||
}
|
||||
@@ -3469,7 +3493,8 @@ class Compiler(
|
||||
recordType = ObjRecord.Type.Delegated,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride
|
||||
isOverride = isOverride,
|
||||
isTransient = isTransient
|
||||
)
|
||||
rec.delegate = finalDelegate
|
||||
return@statement finalDelegate
|
||||
@@ -3488,7 +3513,8 @@ class Compiler(
|
||||
recordType = ObjRecord.Type.Delegated,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride
|
||||
isOverride = isOverride,
|
||||
isTransient = isTransient
|
||||
)
|
||||
rec.delegate = finalDelegate
|
||||
return@statement finalDelegate
|
||||
@@ -3524,6 +3550,7 @@ class Compiler(
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride,
|
||||
isTransient = isTransient,
|
||||
type = ObjRecord.Type.Field
|
||||
)
|
||||
}
|
||||
@@ -3553,7 +3580,8 @@ class Compiler(
|
||||
recordType = ObjRecord.Type.Property,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride
|
||||
isOverride = isOverride,
|
||||
isTransient = isTransient
|
||||
)
|
||||
prop
|
||||
}
|
||||
@@ -3576,6 +3604,7 @@ class Compiler(
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride,
|
||||
pos = start,
|
||||
isTransient = isTransient,
|
||||
type = ObjRecord.Type.Field
|
||||
)
|
||||
|
||||
@@ -3591,7 +3620,8 @@ class Compiler(
|
||||
recordType = ObjRecord.Type.Field,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride
|
||||
isOverride = isOverride,
|
||||
isTransient = isTransient
|
||||
)
|
||||
ObjVoid
|
||||
}
|
||||
@@ -3608,14 +3638,15 @@ class Compiler(
|
||||
recordType = ObjRecord.Type.Field,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride
|
||||
isOverride = isOverride,
|
||||
isTransient = isTransient
|
||||
)
|
||||
initValue
|
||||
}
|
||||
} else {
|
||||
// Not in class body: regular local/var declaration
|
||||
val initValue = initialExpression?.execute(context)?.byValueCopy() ?: ObjNull
|
||||
context.addItem(name, isMutable, initValue, visibility, recordType = ObjRecord.Type.Other)
|
||||
context.addItem(name, isMutable, initValue, visibility, recordType = ObjRecord.Type.Other, isTransient = isTransient)
|
||||
initValue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,7 +516,8 @@ open class Scope(
|
||||
declaringClass: net.sergeych.lyng.obj.ObjClass? = currentClassCtx,
|
||||
isAbstract: Boolean = false,
|
||||
isClosed: Boolean = false,
|
||||
isOverride: Boolean = false
|
||||
isOverride: Boolean = false,
|
||||
isTransient: Boolean = false
|
||||
): ObjRecord {
|
||||
val rec = ObjRecord(
|
||||
value, isMutable, visibility, writeVisibility,
|
||||
@@ -524,7 +525,8 @@ open class Scope(
|
||||
type = recordType,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride
|
||||
isOverride = isOverride,
|
||||
isTransient = isTransient
|
||||
)
|
||||
objects[name] = rec
|
||||
// Index this binding within the current frame to help resolve locals across suspension
|
||||
|
||||
@@ -17,16 +17,24 @@
|
||||
|
||||
package net.sergeych.lyng.obj
|
||||
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import net.sergeych.lyng.*
|
||||
import net.sergeych.lyng.miniast.*
|
||||
import net.sergeych.lynon.LynonDecoder
|
||||
import net.sergeych.lynon.LynonEncoder
|
||||
import net.sergeych.lynon.LynonType
|
||||
|
||||
// Simple id generator for class identities (not thread-safe; fine for scripts)
|
||||
private object ClassIdGen { var c: Long = 1L; fun nextId(): Long = c++ }
|
||||
|
||||
val ObjClassType by lazy {
|
||||
ObjClass("Class").apply {
|
||||
object : ObjClass("Class") {
|
||||
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {
|
||||
val name = decoder.decodeObject(scope, ObjString.type, null) as ObjString
|
||||
return scope.resolveQualifiedIdentifier(name.value)
|
||||
}
|
||||
}.apply {
|
||||
addPropertyDoc(
|
||||
name = "className",
|
||||
doc = "Full name of this class including package if available.",
|
||||
@@ -451,6 +459,7 @@ open class ObjClass(
|
||||
isAbstract: Boolean = false,
|
||||
isClosed: Boolean = false,
|
||||
isOverride: Boolean = false,
|
||||
isTransient: Boolean = false,
|
||||
type: ObjRecord.Type = ObjRecord.Type.Field,
|
||||
): ObjRecord {
|
||||
// Validation of override rules: only for non-system declarations
|
||||
@@ -494,6 +503,7 @@ open class ObjClass(
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride,
|
||||
isTransient = isTransient,
|
||||
type = type
|
||||
)
|
||||
members[name] = rec
|
||||
@@ -514,13 +524,14 @@ open class ObjClass(
|
||||
visibility: Visibility = Visibility.Public,
|
||||
writeVisibility: Visibility? = null,
|
||||
pos: Pos = Pos.builtIn,
|
||||
isTransient: Boolean = false,
|
||||
type: ObjRecord.Type = ObjRecord.Type.Field
|
||||
): ObjRecord {
|
||||
initClassScope()
|
||||
val existing = classScope!!.objects[name]
|
||||
if (existing != null)
|
||||
throw ScriptError(pos, "$name is already defined in $objClass or one of its supertypes")
|
||||
val rec = classScope!!.addItem(name, isMutable, initialValue, visibility, writeVisibility, recordType = type)
|
||||
val rec = classScope!!.addItem(name, isMutable, initialValue, visibility, writeVisibility, recordType = type, isTransient = isTransient)
|
||||
// Structural change: bump layout version for PIC invalidation
|
||||
layoutVersion += 1
|
||||
return rec
|
||||
@@ -707,6 +718,22 @@ open class ObjClass(
|
||||
return super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
||||
}
|
||||
|
||||
override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) {
|
||||
if (isAnonymous) scope.raiseError("Cannot serialize anonymous class")
|
||||
encoder.encodeObject(scope, classNameObj, ObjString.type.lynonType())
|
||||
}
|
||||
|
||||
override suspend fun toJson(scope: Scope): JsonElement {
|
||||
val result = mutableMapOf<String, JsonElement>()
|
||||
result["__class_name"] = classNameObj.toJson(scope)
|
||||
classScope?.objects?.forEach { (name, rec) ->
|
||||
if (rec.type.serializable && rec.visibility.isPublic && !rec.isTransient) {
|
||||
result[name] = rec.value.toJson(scope)
|
||||
}
|
||||
}
|
||||
return JsonObject(result)
|
||||
}
|
||||
|
||||
open suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj =
|
||||
scope.raiseNotImplemented()
|
||||
|
||||
|
||||
@@ -338,7 +338,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
||||
// values, so we save size of the construction:
|
||||
|
||||
// using objlist allow for some optimizations:
|
||||
val params = meta.params.map { readField(scope, it.name).value }
|
||||
val params = meta.params.filter { !it.isTransient }.map { readField(scope, it.name).value }
|
||||
encoder.encodeAnyList(scope, params)
|
||||
val vars = serializingVars.values.map { it.value }
|
||||
if (vars.isNotEmpty()) {
|
||||
@@ -357,8 +357,10 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
||||
val result = mutableMapOf<String, JsonElement>()
|
||||
val meta = objClass.constructorMeta
|
||||
?: scope.raiseError("can't serialize non-serializable object (no constructor meta)")
|
||||
for (entry in meta.params)
|
||||
result[entry.name] = readField(scope, entry.name).value.toJson(scope)
|
||||
for (entry in meta.params) {
|
||||
if (!entry.isTransient)
|
||||
result[entry.name] = readField(scope, entry.name).value.toJson(scope)
|
||||
}
|
||||
for (i in serializingVars) {
|
||||
// remove T:: prefix from the field name for JSON
|
||||
val parts = i.key.split("::")
|
||||
@@ -377,7 +379,8 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
||||
it.value.type.serializable &&
|
||||
it.value.type == ObjRecord.Type.Field &&
|
||||
it.value.isMutable &&
|
||||
!metaParams.contains(it.key)
|
||||
!metaParams.contains(it.key) &&
|
||||
!it.value.isTransient
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +401,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
||||
|
||||
protected val comparableVars: Map<String, ObjRecord> by lazy {
|
||||
instanceScope.objects.filter {
|
||||
it.value.type.comparable && (it.value.type != ObjRecord.Type.Field || it.value.isMutable)
|
||||
it.value.type.comparable && (it.value.type != ObjRecord.Type.Field || it.value.isMutable) && !it.value.isTransient
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
|
||||
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,13 +28,26 @@ import net.sergeych.lynon.LynonType
|
||||
class ObjInstanceClass(val name: String, vararg parents: ObjClass) : ObjClass(name, *parents) {
|
||||
|
||||
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {
|
||||
val args = decoder.decodeAnyList(scope)
|
||||
val actualSize = constructorMeta?.params?.size ?: 0
|
||||
if (args.size > actualSize)
|
||||
scope.raiseIllegalArgument("constructor $name has only $actualSize but serialized version has ${args.size}")
|
||||
val newScope = scope.createChildScope(args = Arguments(args))
|
||||
val instance = createInstance(newScope)
|
||||
initializeInstance(instance, newScope.args, runConstructors = false)
|
||||
val serializedArgs = decoder.decodeAnyList(scope)
|
||||
val meta = constructorMeta ?: scope.raiseError("no constructor meta for $name")
|
||||
val nonTransientCount = meta.params.count { !it.isTransient }
|
||||
if (serializedArgs.size != nonTransientCount)
|
||||
scope.raiseIllegalArgument("constructor $name expects $nonTransientCount non-transient arguments, but serialized version has ${serializedArgs.size}")
|
||||
|
||||
var sIdx = 0
|
||||
val namedArgs = mutableMapOf<String, Obj>()
|
||||
for (p in meta.params) {
|
||||
if (!p.isTransient) {
|
||||
namedArgs[p.name] = serializedArgs[sIdx++]
|
||||
} else if (p.defaultValue == null) {
|
||||
// If transient parameter has no default value, we use ObjNull to avoid "too few arguments" error
|
||||
namedArgs[p.name] = ObjNull
|
||||
}
|
||||
}
|
||||
// Using named arguments allows the constructor to apply default values for transient parameters
|
||||
val newArgs = Arguments(list = emptyList<Obj>(), named = namedArgs)
|
||||
val instance = createInstance(scope.createChildScope(args = newArgs))
|
||||
initializeInstance(instance, newArgs, runConstructors = false)
|
||||
return instance.apply {
|
||||
deserializeStateVars(scope, decoder)
|
||||
invokeInstanceMethod(scope, "onDeserialized") { ObjVoid }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
|
||||
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ package net.sergeych.lynon
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjClass
|
||||
import net.sergeych.lyng.obj.ObjInstance
|
||||
import net.sergeych.lyng.obj.ObjString
|
||||
|
||||
open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSettings.default) {
|
||||
@@ -79,15 +80,16 @@ open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSe
|
||||
private suspend fun decodeClassObj(scope: Scope): ObjClass {
|
||||
val className = decodeObject(scope, ObjString.type, null) as ObjString
|
||||
return scope.get(className.value)?.value?.let {
|
||||
if (it !is ObjClass)
|
||||
scope.raiseClassCastError("Expected obj class but got ${it::class.simpleName}")
|
||||
it
|
||||
if (it is ObjClass) return it
|
||||
if (it is ObjInstance && it.objClass.className == className.value) return it.objClass
|
||||
scope.raiseClassCastError("Expected obj class but got ${it::class.simpleName}")
|
||||
} ?: run {
|
||||
// Use Scope API that mirrors compiler-emitted ObjRef chain for qualified identifiers
|
||||
val evaluated = scope.resolveQualifiedIdentifier(className.value)
|
||||
if (evaluated !is ObjClass)
|
||||
scope.raiseClassCastError("Expected obj class but got ${evaluated::class.simpleName}")
|
||||
evaluated
|
||||
if (evaluated is ObjClass) return evaluated
|
||||
if (evaluated is ObjInstance && evaluated.objClass.className == className.value) return evaluated.objClass
|
||||
scope.raiseClassCastError("Expected obj class but got ${evaluated::class.simpleName}")
|
||||
evaluated as ObjClass // unreachable but for compiler
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user