abstract classes, interfaces, MI auto implementation and fine-grained visibility
This commit is contained in:
1 parent
96e1ffc7d5
commit
11eadc1d9f
23 files changed
+861
-200
No files matched your search
@@ -53,26 +53,71 @@ class ClosureScope(val callScope: Scope, val closureScope: Scope) :
|
||||
super.objects[name]?.let { return it }
|
||||
super.localBindings[name]?.let { return it }
|
||||
|
||||
// 2) Members on the captured receiver instance
|
||||
(closureScope.thisObj as? net.sergeych.lyng.obj.ObjInstance)
|
||||
?.instanceScope
|
||||
?.objects
|
||||
?.get(name)
|
||||
?.let { rec ->
|
||||
if (canAccessMember(rec.visibility, rec.declaringClass, currentClassCtx)) return rec
|
||||
// 1a) Priority: if we are in a class context, prefer our own private members to support
|
||||
// non-virtual private dispatch. This prevents a subclass from accidentally
|
||||
// capturing a private member call from a base class.
|
||||
// We only return non-field/non-property members (methods) here; fields must
|
||||
// be resolved via instance storage in priority 2.
|
||||
currentClassCtx?.let { ctx ->
|
||||
ctx.members[name]?.let { rec ->
|
||||
if (rec.visibility == Visibility.Private &&
|
||||
rec.type != net.sergeych.lyng.obj.ObjRecord.Type.Field &&
|
||||
rec.type != net.sergeych.lyng.obj.ObjRecord.Type.Property) return rec
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Members on the captured receiver instance
|
||||
(closureScope.thisObj as? net.sergeych.lyng.obj.ObjInstance)?.let { inst ->
|
||||
// Check direct locals in instance scope (unmangled)
|
||||
inst.instanceScope.objects[name]?.let { rec ->
|
||||
if (rec.type != net.sergeych.lyng.obj.ObjRecord.Type.Property &&
|
||||
canAccessMember(rec.visibility, rec.declaringClass, currentClassCtx)) return rec
|
||||
}
|
||||
// Check mangled names for fields along MRO
|
||||
for (cls in inst.objClass.mro) {
|
||||
inst.instanceScope.objects["${cls.className}::$name"]?.let { rec ->
|
||||
if (rec.type != net.sergeych.lyng.obj.ObjRecord.Type.Property &&
|
||||
canAccessMember(rec.visibility, rec.declaringClass ?: cls, currentClassCtx)) return rec
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
findExtension(closureScope.thisObj.objClass, name)?.let { return it }
|
||||
closureScope.thisObj.objClass.getInstanceMemberOrNull(name)?.let { rec ->
|
||||
if (canAccessMember(rec.visibility, rec.declaringClass, currentClassCtx)) return rec
|
||||
if (canAccessMember(rec.visibility, rec.declaringClass, currentClassCtx)) {
|
||||
// Return only non-field/non-property members (methods) from class-level records.
|
||||
// Fields and properties must be resolved via instance storage (mangled) or readField.
|
||||
if (rec.type != net.sergeych.lyng.obj.ObjRecord.Type.Field &&
|
||||
rec.type != net.sergeych.lyng.obj.ObjRecord.Type.Property &&
|
||||
!rec.isAbstract) return rec
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Closure scope chain (locals/parents + members), ignore ClosureScope overrides to prevent recursion
|
||||
closureScope.chainLookupWithMembers(name, currentClassCtx)?.let { return it }
|
||||
|
||||
// 4) Caller `this` members
|
||||
(callScope.thisObj as? net.sergeych.lyng.obj.ObjInstance)?.let { inst ->
|
||||
// Check direct locals in instance scope (unmangled)
|
||||
inst.instanceScope.objects[name]?.let { rec ->
|
||||
if (rec.type != net.sergeych.lyng.obj.ObjRecord.Type.Property &&
|
||||
canAccessMember(rec.visibility, rec.declaringClass, currentClassCtx)) return rec
|
||||
}
|
||||
// Check mangled names for fields along MRO
|
||||
for (cls in inst.objClass.mro) {
|
||||
inst.instanceScope.objects["${cls.className}::$name"]?.let { rec ->
|
||||
if (rec.type != net.sergeych.lyng.obj.ObjRecord.Type.Property &&
|
||||
canAccessMember(rec.visibility, rec.declaringClass ?: cls, currentClassCtx)) return rec
|
||||
}
|
||||
}
|
||||
}
|
||||
findExtension(callScope.thisObj.objClass, name)?.let { return it }
|
||||
callScope.thisObj.objClass.getInstanceMemberOrNull(name)?.let { rec ->
|
||||
if (canAccessMember(rec.visibility, rec.declaringClass, currentClassCtx)) return rec
|
||||
if (canAccessMember(rec.visibility, rec.declaringClass, currentClassCtx)) {
|
||||
if (rec.type != net.sergeych.lyng.obj.ObjRecord.Type.Field &&
|
||||
rec.type != net.sergeych.lyng.obj.ObjRecord.Type.Property &&
|
||||
!rec.isAbstract) return rec
|
||||
}
|
||||
}
|
||||
|
||||
// 5) Caller chain (locals/parents + members)
|
||||
|
||||
@@ -236,7 +236,7 @@ class Compiler(
|
||||
cc.next()
|
||||
pendingDeclStart = t.pos
|
||||
pendingDeclDoc = consumePendingDoc()
|
||||
val st = parseFunctionDeclaration(isOpen = false, isExtern = false, isStatic = false)
|
||||
val st = parseFunctionDeclaration(isExtern = false, isStatic = false)
|
||||
statements += st
|
||||
continue
|
||||
}
|
||||
@@ -1298,11 +1298,93 @@ class Compiler(
|
||||
else
|
||||
null
|
||||
|
||||
private suspend fun parseDeclarationWithModifiers(firstId: Token): Statement {
|
||||
val modifiers = mutableSetOf<String>()
|
||||
var currentToken = firstId
|
||||
|
||||
while (true) {
|
||||
when (currentToken.value) {
|
||||
"private", "protected", "static", "abstract", "closed", "override", "extern", "open" -> {
|
||||
modifiers.add(currentToken.value)
|
||||
val next = cc.peekNextNonWhitespace()
|
||||
if (next.type == Token.Type.ID) {
|
||||
currentToken = cc.next()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
else -> break
|
||||
}
|
||||
}
|
||||
|
||||
val visibility = when {
|
||||
modifiers.contains("private") -> Visibility.Private
|
||||
modifiers.contains("protected") -> Visibility.Protected
|
||||
else -> Visibility.Public
|
||||
}
|
||||
val isStatic = modifiers.contains("static")
|
||||
val isAbstract = modifiers.contains("abstract")
|
||||
val isClosed = modifiers.contains("closed")
|
||||
val isOverride = modifiers.contains("override")
|
||||
val isExtern = modifiers.contains("extern")
|
||||
|
||||
if (isStatic && (isAbstract || isOverride || isClosed))
|
||||
throw ScriptError(currentToken.pos, "static members cannot be abstract, closed or override")
|
||||
|
||||
if (visibility == Visibility.Private && isAbstract)
|
||||
throw ScriptError(currentToken.pos, "abstract members cannot be private")
|
||||
|
||||
pendingDeclStart = firstId.pos
|
||||
pendingDeclDoc = consumePendingDoc()
|
||||
|
||||
val isMember = (codeContexts.lastOrNull() is CodeContext.ClassBody)
|
||||
|
||||
if (!isMember && (isOverride || isClosed))
|
||||
throw ScriptError(currentToken.pos, "modifiers override and closed are only allowed for class members")
|
||||
|
||||
if (!isMember && isAbstract && currentToken.value != "class")
|
||||
throw ScriptError(currentToken.pos, "modifier abstract at top level is only allowed for classes")
|
||||
|
||||
return when (currentToken.value) {
|
||||
"val" -> parseVarDeclaration(false, visibility, isAbstract, isClosed, isOverride, isStatic)
|
||||
"var" -> parseVarDeclaration(true, visibility, isAbstract, isClosed, isOverride, isStatic)
|
||||
"fun", "fn" -> parseFunctionDeclaration(visibility, isAbstract, isClosed, isOverride, isExtern, isStatic)
|
||||
"class" -> {
|
||||
if (isStatic || isClosed || isOverride || isExtern)
|
||||
throw ScriptError(currentToken.pos, "unsupported modifiers for class: ${modifiers.joinToString(" ")}")
|
||||
parseClassDeclaration(isAbstract)
|
||||
}
|
||||
|
||||
"interface" -> {
|
||||
if (isStatic || isClosed || isOverride || isExtern || isAbstract)
|
||||
throw ScriptError(
|
||||
currentToken.pos,
|
||||
"unsupported modifiers for interface: ${modifiers.joinToString(" ")}"
|
||||
)
|
||||
// interface is synonym for abstract class
|
||||
parseClassDeclaration(isAbstract = true)
|
||||
}
|
||||
|
||||
else -> throw ScriptError(currentToken.pos, "expected declaration after modifiers, found ${currentToken.value}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse keyword-starting statement.
|
||||
* @return parsed statement or null if, for example. [id] is not among keywords
|
||||
*/
|
||||
private suspend fun parseKeywordStatement(id: Token): Statement? = when (id.value) {
|
||||
"abstract", "closed", "override", "extern", "private", "protected", "static" -> {
|
||||
parseDeclarationWithModifiers(id)
|
||||
}
|
||||
|
||||
"interface" -> {
|
||||
pendingDeclStart = id.pos
|
||||
pendingDeclDoc = consumePendingDoc()
|
||||
parseClassDeclaration(isAbstract = true)
|
||||
}
|
||||
|
||||
"val" -> {
|
||||
pendingDeclStart = id.pos
|
||||
pendingDeclDoc = consumePendingDoc()
|
||||
@@ -1318,71 +1400,15 @@ class Compiler(
|
||||
"fun" -> {
|
||||
pendingDeclStart = id.pos
|
||||
pendingDeclDoc = consumePendingDoc()
|
||||
parseFunctionDeclaration(isOpen = false, isExtern = false, isStatic = false)
|
||||
parseFunctionDeclaration(isExtern = false, isStatic = false)
|
||||
}
|
||||
|
||||
"fn" -> {
|
||||
pendingDeclStart = id.pos
|
||||
pendingDeclDoc = consumePendingDoc()
|
||||
parseFunctionDeclaration(isOpen = false, isExtern = false, isStatic = false)
|
||||
parseFunctionDeclaration(isExtern = false, isStatic = false)
|
||||
}
|
||||
// Visibility modifiers for declarations: private/protected val/var/fun/fn
|
||||
"private" -> {
|
||||
var k = cc.requireToken(Token.Type.ID, "declaration expected after 'private'")
|
||||
var isStatic = false
|
||||
if (k.value == "static") {
|
||||
isStatic = true
|
||||
k = cc.requireToken(Token.Type.ID, "declaration expected after 'private static'")
|
||||
}
|
||||
when (k.value) {
|
||||
"val" -> parseVarDeclaration(false, Visibility.Private, isStatic = isStatic)
|
||||
"var" -> parseVarDeclaration(true, Visibility.Private, isStatic = isStatic)
|
||||
"fun" -> parseFunctionDeclaration(
|
||||
visibility = Visibility.Private,
|
||||
isOpen = false,
|
||||
isExtern = false,
|
||||
isStatic = isStatic
|
||||
)
|
||||
|
||||
"fn" -> parseFunctionDeclaration(
|
||||
visibility = Visibility.Private,
|
||||
isOpen = false,
|
||||
isExtern = false,
|
||||
isStatic = isStatic
|
||||
)
|
||||
|
||||
else -> k.raiseSyntax("unsupported private declaration kind: ${k.value}")
|
||||
}
|
||||
}
|
||||
|
||||
"protected" -> {
|
||||
var k = cc.requireToken(Token.Type.ID, "declaration expected after 'protected'")
|
||||
var isStatic = false
|
||||
if (k.value == "static") {
|
||||
isStatic = true
|
||||
k = cc.requireToken(Token.Type.ID, "declaration expected after 'protected static'")
|
||||
}
|
||||
when (k.value) {
|
||||
"val" -> parseVarDeclaration(false, Visibility.Protected, isStatic = isStatic)
|
||||
"var" -> parseVarDeclaration(true, Visibility.Protected, isStatic = isStatic)
|
||||
"fun" -> parseFunctionDeclaration(
|
||||
visibility = Visibility.Protected,
|
||||
isOpen = false,
|
||||
isExtern = false,
|
||||
isStatic = isStatic
|
||||
)
|
||||
|
||||
"fn" -> parseFunctionDeclaration(
|
||||
visibility = Visibility.Protected,
|
||||
isOpen = false,
|
||||
isExtern = false,
|
||||
isStatic = isStatic
|
||||
)
|
||||
|
||||
else -> k.raiseSyntax("unsupported protected declaration kind: ${k.value}")
|
||||
}
|
||||
}
|
||||
|
||||
"while" -> parseWhileStatement()
|
||||
"do" -> parseDoWhileStatement()
|
||||
"for" -> parseForStatement()
|
||||
@@ -1444,17 +1470,17 @@ class Compiler(
|
||||
)
|
||||
|
||||
cc.matchQualifiers("fn", "private") -> parseFunctionDeclaration(Visibility.Private, isExtern)
|
||||
cc.matchQualifiers("fun", "open") -> parseFunctionDeclaration(isOpen = true, isExtern = isExtern)
|
||||
cc.matchQualifiers("fn", "open") -> parseFunctionDeclaration(isOpen = true, isExtern = isExtern)
|
||||
cc.matchQualifiers("fun", "open") -> parseFunctionDeclaration(isExtern = isExtern)
|
||||
cc.matchQualifiers("fn", "open") -> parseFunctionDeclaration(isExtern = isExtern)
|
||||
|
||||
cc.matchQualifiers("fun") -> {
|
||||
pendingDeclStart = id.pos; pendingDeclDoc =
|
||||
consumePendingDoc(); parseFunctionDeclaration(isOpen = false, isExtern = isExtern)
|
||||
consumePendingDoc(); parseFunctionDeclaration(isExtern = isExtern)
|
||||
}
|
||||
|
||||
cc.matchQualifiers("fn") -> {
|
||||
pendingDeclStart = id.pos; pendingDeclDoc =
|
||||
consumePendingDoc(); parseFunctionDeclaration(isOpen = false, isExtern = isExtern)
|
||||
consumePendingDoc(); parseFunctionDeclaration(isExtern = isExtern)
|
||||
}
|
||||
|
||||
cc.matchQualifiers("val", "private", "static") -> {
|
||||
@@ -1821,7 +1847,7 @@ class Compiler(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun parseClassDeclaration(): Statement {
|
||||
private suspend fun parseClassDeclaration(isAbstract: Boolean = false): Statement {
|
||||
val nameToken = cc.requireToken(Token.Type.ID)
|
||||
val startPos = pendingDeclStart ?: nameToken.pos
|
||||
val doc = pendingDeclDoc ?: consumePendingDoc()
|
||||
@@ -1947,6 +1973,7 @@ class Compiler(
|
||||
}
|
||||
|
||||
val newClass = ObjInstanceClass(className, *parentClasses.toTypedArray()).also {
|
||||
it.isAbstract = isAbstract
|
||||
it.instanceConstructor = constructorCode
|
||||
it.constructorMeta = constructorArgsDeclaration
|
||||
// Attach per-parent constructor args (thunks) if provided
|
||||
@@ -1954,6 +1981,22 @@ class Compiler(
|
||||
val argsList = baseSpecs[i].args
|
||||
if (argsList != null) it.directParentArgs[parentClasses[i]] = argsList
|
||||
}
|
||||
// Register constructor fields in the class members
|
||||
constructorArgsDeclaration?.params?.forEach { p ->
|
||||
if (p.accessType != null) {
|
||||
it.createField(
|
||||
p.name, ObjNull,
|
||||
isMutable = p.accessType == AccessType.Var,
|
||||
visibility = p.visibility ?: Visibility.Public,
|
||||
declaringClass = it,
|
||||
// Constructor fields are not currently supporting override/closed in parser
|
||||
// but we should pass Pos.builtIn to skip validation for now if needed,
|
||||
// or p.pos to allow it.
|
||||
pos = Pos.builtIn,
|
||||
type = ObjRecord.Type.ConstructorField
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addItem(className, false, newClass)
|
||||
@@ -1974,7 +2017,7 @@ class Compiler(
|
||||
val v = rec.value
|
||||
if (v is Statement) {
|
||||
if (newClass.members[k] == null) {
|
||||
newClass.addFn(k, isOpen = true) {
|
||||
newClass.addFn(k, isMutable = true, pos = rec.importedFrom?.pos ?: nameToken.pos) {
|
||||
(thisObj as? ObjInstance)?.let { i ->
|
||||
v.execute(ClosureScope(this, i.instanceScope))
|
||||
} ?: v.execute(thisObj.autoInstanceScope(this))
|
||||
@@ -1982,6 +2025,7 @@ class Compiler(
|
||||
}
|
||||
}
|
||||
}
|
||||
newClass.checkAbstractSatisfaction(nameToken.pos)
|
||||
// Debug summary: list registered instance methods and class-scope functions for this class
|
||||
newClass
|
||||
}
|
||||
@@ -2395,7 +2439,9 @@ class Compiler(
|
||||
|
||||
private suspend fun parseFunctionDeclaration(
|
||||
visibility: Visibility = Visibility.Public,
|
||||
@Suppress("UNUSED_PARAMETER") isOpen: Boolean = false,
|
||||
isAbstract: Boolean = false,
|
||||
isClosed: Boolean = false,
|
||||
isOverride: Boolean = false,
|
||||
isExtern: Boolean = false,
|
||||
isStatic: Boolean = false,
|
||||
): Statement {
|
||||
@@ -2480,7 +2526,12 @@ class Compiler(
|
||||
localDeclCountStack.add(0)
|
||||
val fnStatements = if (isExtern)
|
||||
statement { raiseError("extern function not provided: $name") }
|
||||
else
|
||||
else if (isAbstract) {
|
||||
val next = cc.peekNextNonWhitespace()
|
||||
if (next.type == Token.Type.ASSIGN || next.type == Token.Type.LBRACE)
|
||||
throw ScriptError(next.pos, "abstract function $name cannot have a body")
|
||||
null
|
||||
} else
|
||||
withLocalNames(paramNames) {
|
||||
val next = cc.peekNextNonWhitespace()
|
||||
if (next.type == Token.Type.ASSIGN) {
|
||||
@@ -2518,7 +2569,7 @@ class Compiler(
|
||||
if (extTypeName != null) {
|
||||
context.thisObj = callerContext.thisObj
|
||||
}
|
||||
fnStatements.execute(context)
|
||||
fnStatements?.execute(context) ?: ObjVoid
|
||||
}
|
||||
// parentContext
|
||||
val fnCreateStatement = statement(start) { context ->
|
||||
@@ -2550,7 +2601,15 @@ class Compiler(
|
||||
if (!isStatic && th is ObjClass) {
|
||||
// Instance method declared inside a class body: register on the class
|
||||
val cls: ObjClass = th
|
||||
cls.addFn(name, isOpen = true, visibility = visibility) {
|
||||
cls.addFn(
|
||||
name,
|
||||
isMutable = true,
|
||||
visibility = visibility,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride,
|
||||
pos = start
|
||||
) {
|
||||
// Execute with the instance as receiver; set caller lexical class for visibility
|
||||
val savedCtx = this.currentClassCtx
|
||||
this.currentClassCtx = cls
|
||||
@@ -2628,7 +2687,9 @@ class Compiler(
|
||||
private suspend fun parseVarDeclaration(
|
||||
isMutable: Boolean,
|
||||
visibility: Visibility,
|
||||
@Suppress("UNUSED_PARAMETER") isOpen: Boolean = false,
|
||||
isAbstract: Boolean = false,
|
||||
isClosed: Boolean = false,
|
||||
isOverride: Boolean = false,
|
||||
isStatic: Boolean = false
|
||||
): Statement {
|
||||
val nextToken = cc.next()
|
||||
@@ -2741,7 +2802,15 @@ class Compiler(
|
||||
// Register the local name at compile time so that subsequent identifiers can be emitted as fast locals
|
||||
if (!isStatic) declareLocalName(name)
|
||||
|
||||
val isDelegate = if (!isProperty && eqToken.isId("by")) {
|
||||
val isDelegate = if (isAbstract) {
|
||||
if (!isProperty && (eqToken.type == Token.Type.ASSIGN || eqToken.isId("by")))
|
||||
throw ScriptError(eqToken.pos, "abstract variable $name cannot have an initializer or delegate")
|
||||
// Abstract variables don't have initializers
|
||||
cc.restorePos(markBeforeEq)
|
||||
cc.skipWsTokens()
|
||||
setNull = true
|
||||
false
|
||||
} else if (!isProperty && eqToken.isId("by")) {
|
||||
true
|
||||
} else {
|
||||
if (!isProperty && eqToken.type != Token.Type.ASSIGN) {
|
||||
@@ -2945,22 +3014,58 @@ class Compiler(
|
||||
val isClassScope = context.thisObj is ObjClass && (context.thisObj !is ObjInstance)
|
||||
if (isClassScope) {
|
||||
val cls = context.thisObj as ObjClass
|
||||
// Register the property
|
||||
cls.instanceInitializers += statement(start) { scp ->
|
||||
scp.addItem(
|
||||
storageName,
|
||||
isMutable,
|
||||
prop,
|
||||
visibility,
|
||||
setterVisibility,
|
||||
recordType = ObjRecord.Type.Property
|
||||
// Register in class members for reflection/MRO/satisfaction checks
|
||||
if (isProperty) {
|
||||
cls.addProperty(
|
||||
name,
|
||||
visibility = visibility,
|
||||
writeVisibility = setterVisibility,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride,
|
||||
pos = start
|
||||
)
|
||||
ObjVoid
|
||||
} else {
|
||||
cls.createField(
|
||||
name,
|
||||
ObjNull,
|
||||
isMutable = isMutable,
|
||||
visibility = visibility,
|
||||
writeVisibility = setterVisibility,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride,
|
||||
type = ObjRecord.Type.Field
|
||||
)
|
||||
}
|
||||
|
||||
// Register the property/field initialization thunk
|
||||
if (!isAbstract) {
|
||||
cls.instanceInitializers += statement(start) { scp ->
|
||||
scp.addItem(
|
||||
storageName,
|
||||
isMutable,
|
||||
prop,
|
||||
visibility,
|
||||
setterVisibility,
|
||||
recordType = ObjRecord.Type.Property,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride
|
||||
)
|
||||
ObjVoid
|
||||
}
|
||||
}
|
||||
ObjVoid
|
||||
} else {
|
||||
// We are in instance scope already: perform initialization immediately
|
||||
context.addItem(storageName, isMutable, prop, visibility, setterVisibility, recordType = ObjRecord.Type.Property)
|
||||
context.addItem(
|
||||
storageName, isMutable, prop, visibility, setterVisibility,
|
||||
recordType = ObjRecord.Type.Property,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride
|
||||
)
|
||||
prop
|
||||
}
|
||||
} else {
|
||||
@@ -2971,22 +3076,51 @@ class Compiler(
|
||||
val isClassScope = context.thisObj is ObjClass && (context.thisObj !is ObjInstance)
|
||||
if (isClassScope) {
|
||||
val cls = context.thisObj as ObjClass
|
||||
// Register in class members for reflection/MRO/satisfaction checks
|
||||
cls.createField(
|
||||
name,
|
||||
ObjNull,
|
||||
isMutable = isMutable,
|
||||
visibility = visibility,
|
||||
writeVisibility = setterVisibility,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride,
|
||||
pos = start,
|
||||
type = ObjRecord.Type.Field
|
||||
)
|
||||
|
||||
// Defer: at instance construction, evaluate initializer in instance scope and store under mangled name
|
||||
val initStmt = statement(start) { scp ->
|
||||
val initValue =
|
||||
initialExpression?.execute(scp)?.byValueCopy() ?: if (isLateInitVal) ObjUnset else ObjNull
|
||||
// Preserve mutability of declaration: do NOT use addOrUpdateItem here, as it creates mutable records
|
||||
scp.addItem(storageName, isMutable, initValue, visibility, setterVisibility, recordType = ObjRecord.Type.Field)
|
||||
ObjVoid
|
||||
if (!isAbstract) {
|
||||
val initStmt = statement(start) { scp ->
|
||||
val initValue =
|
||||
initialExpression?.execute(scp)?.byValueCopy()
|
||||
?: if (isLateInitVal) ObjUnset else ObjNull
|
||||
// Preserve mutability of declaration: do NOT use addOrUpdateItem here, as it creates mutable records
|
||||
scp.addItem(
|
||||
storageName, isMutable, initValue, visibility, setterVisibility,
|
||||
recordType = ObjRecord.Type.Field,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride
|
||||
)
|
||||
ObjVoid
|
||||
}
|
||||
cls.instanceInitializers += initStmt
|
||||
}
|
||||
cls.instanceInitializers += initStmt
|
||||
ObjVoid
|
||||
} else {
|
||||
// We are in instance scope already: perform initialization immediately
|
||||
val initValue =
|
||||
initialExpression?.execute(context)?.byValueCopy() ?: if (isLateInitVal) ObjUnset else ObjNull
|
||||
// Preserve mutability of declaration: create record with correct mutability
|
||||
context.addItem(storageName, isMutable, initValue, visibility, setterVisibility, recordType = ObjRecord.Type.Field)
|
||||
context.addItem(
|
||||
storageName, isMutable, initValue, visibility, setterVisibility,
|
||||
recordType = ObjRecord.Type.Field,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride
|
||||
)
|
||||
initValue
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -155,7 +155,10 @@ open class Scope(
|
||||
this.extensions[cls]?.get(name)?.let { return it }
|
||||
}
|
||||
return thisObj.objClass.getInstanceMemberOrNull(name)?.let { rec ->
|
||||
if (canAccessMember(rec.visibility, rec.declaringClass, currentClassCtx)) rec else null
|
||||
if (canAccessMember(rec.visibility, rec.declaringClass, currentClassCtx)) {
|
||||
if (rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.Property || rec.isAbstract) null
|
||||
else rec
|
||||
} else null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +179,11 @@ open class Scope(
|
||||
s.extensions[cls]?.get(name)?.let { return it }
|
||||
}
|
||||
s.thisObj.objClass.getInstanceMemberOrNull(name)?.let { rec ->
|
||||
if (canAccessMember(rec.visibility, rec.declaringClass, caller)) return rec
|
||||
if (canAccessMember(rec.visibility, rec.declaringClass, caller)) {
|
||||
if (rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.Property || rec.isAbstract) {
|
||||
// ignore fields, properties and abstracts here, they will be handled by the caller via readField
|
||||
} else return rec
|
||||
}
|
||||
}
|
||||
s = s.parent
|
||||
}
|
||||
@@ -332,7 +339,10 @@ open class Scope(
|
||||
?: parent?.get(name)
|
||||
// Finally, fallback to class members on thisObj
|
||||
?: thisObj.objClass.getInstanceMemberOrNull(name)?.let { rec ->
|
||||
if (canAccessMember(rec.visibility, rec.declaringClass, currentClassCtx)) rec else null
|
||||
if (canAccessMember(rec.visibility, rec.declaringClass, currentClassCtx)) {
|
||||
if (rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.Property || rec.isAbstract) null
|
||||
else rec
|
||||
} else null
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -435,7 +445,10 @@ open class Scope(
|
||||
value: Obj,
|
||||
visibility: Visibility = Visibility.Public,
|
||||
writeVisibility: Visibility? = null,
|
||||
recordType: ObjRecord.Type = ObjRecord.Type.Other
|
||||
recordType: ObjRecord.Type = ObjRecord.Type.Other,
|
||||
isAbstract: Boolean = false,
|
||||
isClosed: Boolean = false,
|
||||
isOverride: Boolean = false
|
||||
): ObjRecord =
|
||||
objects[name]?.let {
|
||||
if( !it.isMutable )
|
||||
@@ -449,7 +462,7 @@ open class Scope(
|
||||
callScope.localBindings[name] = it
|
||||
}
|
||||
it
|
||||
} ?: addItem(name, true, value, visibility, writeVisibility, recordType)
|
||||
} ?: addItem(name, true, value, visibility, writeVisibility, recordType, isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride)
|
||||
|
||||
fun addItem(
|
||||
name: String,
|
||||
@@ -458,9 +471,19 @@ open class Scope(
|
||||
visibility: Visibility = Visibility.Public,
|
||||
writeVisibility: Visibility? = null,
|
||||
recordType: ObjRecord.Type = ObjRecord.Type.Other,
|
||||
declaringClass: net.sergeych.lyng.obj.ObjClass? = currentClassCtx
|
||||
declaringClass: net.sergeych.lyng.obj.ObjClass? = currentClassCtx,
|
||||
isAbstract: Boolean = false,
|
||||
isClosed: Boolean = false,
|
||||
isOverride: Boolean = false
|
||||
): ObjRecord {
|
||||
val rec = ObjRecord(value, isMutable, visibility, writeVisibility, declaringClass = declaringClass, type = recordType)
|
||||
val rec = ObjRecord(
|
||||
value, isMutable, visibility, writeVisibility,
|
||||
declaringClass = declaringClass,
|
||||
type = recordType,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride
|
||||
)
|
||||
objects[name] = rec
|
||||
// Index this binding within the current frame to help resolve locals across suspension
|
||||
localBindings[name] = rec
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package net.sergeych.lyng
|
||||
|
||||
enum class Visibility {
|
||||
Public, Private, Protected;//, Internal
|
||||
Public, Protected, Private;//, Internal
|
||||
val isPublic by lazy { this == Public }
|
||||
@Suppress("unused")
|
||||
val isProtected by lazy { this == Protected }
|
||||
|
||||
@@ -41,7 +41,8 @@ private val fallbackKeywordIds = setOf(
|
||||
// boolean operators
|
||||
"and", "or", "not",
|
||||
// declarations & modifiers
|
||||
"fun", "fn", "class", "enum", "val", "var", "import", "package",
|
||||
"fun", "fn", "class", "interface", "enum", "val", "var", "import", "package",
|
||||
"abstract", "closed", "override",
|
||||
"private", "protected", "static", "open", "extern", "init", "get", "set", "by",
|
||||
// control flow and misc
|
||||
"if", "else", "when", "while", "do", "for", "try", "catch", "finally",
|
||||
|
||||
@@ -112,8 +112,8 @@ object BuiltinDocRegistry : BuiltinDocSource {
|
||||
fun extensionMemberNamesFor(className: String): List<String> {
|
||||
val src = try { rootLyng } catch (_: Throwable) { null } ?: return emptyList()
|
||||
val out = LinkedHashSet<String>()
|
||||
// Match lines like: fun String.trim(...) or val Int.isEven = ...
|
||||
val re = Regex("^\\s*(?:fun|val|var)\\s+${className}\\.([A-Za-z_][A-Za-z0-9_]*)\\b", RegexOption.MULTILINE)
|
||||
// Match lines like: fun String.trim(...) or val Int.isEven = ... (allowing modifiers)
|
||||
val re = Regex("^\\s*(?:(?:abstract|override|closed|private|protected|static|open|extern)\\s+)*(?:fun|val|var)\\s+${className}\\.([A-Za-z_][A-Za-z0-9_]*)\\b", RegexOption.MULTILINE)
|
||||
re.findAll(src).forEach { m ->
|
||||
val name = m.groupValues.getOrNull(1)?.trim()
|
||||
if (!name.isNullOrEmpty()) out.add(name)
|
||||
@@ -371,20 +371,20 @@ private object StdlibInlineDocIndex {
|
||||
else -> {
|
||||
// Non-comment, non-blank: try to match a declaration just after comments
|
||||
if (buf.isNotEmpty()) {
|
||||
// fun/val/var Class.name( ... )
|
||||
val mExt = Regex("^(?:fun|val|var)\\s+([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z_][A-Za-z0-9_]*)\\b").find(line)
|
||||
// fun/val/var Class.name( ... ) (allowing modifiers)
|
||||
val mExt = Regex("^(?:(?:abstract|override|closed|private|protected|static|open|extern)\\s+)*(?:fun|val|var)\\s+([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z_][A-Za-z0-9_]*)\\b").find(line)
|
||||
if (mExt != null) {
|
||||
val (cls, name) = mExt.destructured
|
||||
flushTo(Key.Method(cls, name))
|
||||
} else {
|
||||
// fun name( ... )
|
||||
val mTop = Regex("^fun\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*\\(").find(line)
|
||||
// fun name( ... ) (allowing modifiers)
|
||||
val mTop = Regex("^(?:(?:abstract|override|closed|private|protected|static|open|extern)\\s+)*fun\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*\\(").find(line)
|
||||
if (mTop != null) {
|
||||
val (name) = mTop.destructured
|
||||
flushTo(Key.TopFun(name))
|
||||
} else {
|
||||
// class Name
|
||||
val mClass = Regex("^class\\s+([A-Za-z_][A-Za-z0-9_]*)\\b").find(line)
|
||||
// class/interface Name (allowing modifiers)
|
||||
val mClass = Regex("^(?:(?:abstract|private|protected|static|open|extern)\\s+)*(?:class|interface)\\s+([A-Za-z_][A-Za-z0-9_]*)\\b").find(line)
|
||||
if (mClass != null) {
|
||||
val (name) = mClass.destructured
|
||||
flushTo(Key.Clazz(name))
|
||||
|
||||
@@ -435,16 +435,16 @@ object DocLookupUtils {
|
||||
if (start !in 0..end) return emptyMap()
|
||||
val body = text.substring(start, end)
|
||||
val map = LinkedHashMap<String, ScannedSig>()
|
||||
// fun name(params): Type
|
||||
val funRe = Regex("^\\s*fun\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*\\(([^)]*)\\)\\s*(?::\\s*([A-Za-z_][A-Za-z0-9_]*))?", RegexOption.MULTILINE)
|
||||
// fun name(params): Type (allowing modifiers like abstract, override, closed)
|
||||
val funRe = Regex("^\\s*(?:(?:abstract|override|closed|private|protected|static|open|extern)\\s+)*fun\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*\\(([^)]*)\\)\\s*(?::\\s*([A-Za-z_][A-Za-z0-9_]*))?", RegexOption.MULTILINE)
|
||||
for (m in funRe.findAll(body)) {
|
||||
val name = m.groupValues.getOrNull(1) ?: continue
|
||||
val params = m.groupValues.getOrNull(2)?.split(',')?.mapNotNull { it.trim().takeIf { it.isNotEmpty() } } ?: emptyList()
|
||||
val type = m.groupValues.getOrNull(3)?.takeIf { it.isNotBlank() }
|
||||
map[name] = ScannedSig("fun", params, type)
|
||||
}
|
||||
// val/var name: Type
|
||||
val valRe = Regex("^\\s*(val|var)\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*(?::\\s*([A-Za-z_][A-Za-z0-9_]*))?", RegexOption.MULTILINE)
|
||||
// val/var name: Type (allowing modifiers)
|
||||
val valRe = Regex("^\\s*(?:(?:abstract|override|closed|private|protected|static|open|extern)\\s+)*(val|var)\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*(?::\\s*([A-Za-z_][A-Za-z0-9_]*))?", RegexOption.MULTILINE)
|
||||
for (m in valRe.findAll(body)) {
|
||||
val kind = m.groupValues.getOrNull(1) ?: continue
|
||||
val name = m.groupValues.getOrNull(2) ?: continue
|
||||
|
||||
@@ -89,7 +89,7 @@ open class Obj {
|
||||
for (cls in objClass.mro) {
|
||||
if (cls.className == "Obj") break
|
||||
val rec = cls.members[name] ?: cls.classScope?.objects?.get(name)
|
||||
if (rec != null) {
|
||||
if (rec != null && !rec.isAbstract && rec.type != ObjRecord.Type.Property) {
|
||||
val decl = rec.declaringClass ?: cls
|
||||
val caller = scope.currentClassCtx
|
||||
if (!canAccessMember(rec.visibility, decl, caller))
|
||||
@@ -347,8 +347,12 @@ open class Obj {
|
||||
// 1. Hierarchy members (excluding root fallback)
|
||||
for (cls in objClass.mro) {
|
||||
if (cls.className == "Obj") break
|
||||
cls.members[name]?.let { return resolveRecord(scope, it, name, it.declaringClass) }
|
||||
cls.classScope?.objects?.get(name)?.let { return resolveRecord(scope, it, name, it.declaringClass) }
|
||||
val rec = cls.members[name] ?: cls.classScope?.objects?.get(name)
|
||||
if (rec != null) {
|
||||
if (!rec.isAbstract) {
|
||||
return resolveRecord(scope, rec, name, rec.declaringClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Extensions
|
||||
@@ -393,8 +397,11 @@ open class Obj {
|
||||
// 1. Hierarchy members (excluding root fallback)
|
||||
for (cls in objClass.mro) {
|
||||
if (cls.className == "Obj") break
|
||||
field = cls.members[name] ?: cls.classScope?.objects?.get(name)
|
||||
if (field != null) break
|
||||
val rec = cls.members[name] ?: cls.classScope?.objects?.get(name)
|
||||
if (rec != null && !rec.isAbstract) {
|
||||
field = rec
|
||||
break
|
||||
}
|
||||
}
|
||||
// 2. Extensions
|
||||
if (field == null) {
|
||||
|
||||
@@ -91,6 +91,8 @@ open class ObjClass(
|
||||
vararg parents: ObjClass,
|
||||
) : Obj() {
|
||||
|
||||
var isAbstract: Boolean = false
|
||||
|
||||
// Stable identity and simple structural version for PICs
|
||||
val classId: Long = ClassIdGen.nextId()
|
||||
var layoutVersion: Int = 0
|
||||
@@ -178,7 +180,13 @@ open class ObjClass(
|
||||
val mro: List<ObjClass> by lazy {
|
||||
val base = c3Linearize(this, mutableMapOf())
|
||||
if (this.className == "Obj" || base.any { it.className == "Obj" }) base
|
||||
else base + rootObjectType
|
||||
else {
|
||||
// During very early bootstrap rootObjectType might not be initialized yet.
|
||||
// We use a safe check here.
|
||||
@Suppress("UNNECESSARY_SAFE_CALL")
|
||||
val root = net.sergeych.lyng.obj.Obj.rootObjectType
|
||||
if (root != null) base + root else base
|
||||
}
|
||||
}
|
||||
|
||||
/** Parents in C3 order (no self). */
|
||||
@@ -204,6 +212,7 @@ open class ObjClass(
|
||||
override suspend fun compareTo(scope: Scope, other: Obj): Int = if (other === this) 0 else -1
|
||||
|
||||
override suspend fun callOn(scope: Scope): Obj {
|
||||
if (isAbstract) scope.raiseError("can't instantiate abstract class $className")
|
||||
val instance = createInstance(scope)
|
||||
initializeInstance(instance, scope.args, runConstructors = true)
|
||||
return instance
|
||||
@@ -347,14 +356,54 @@ open class ObjClass(
|
||||
visibility: Visibility = Visibility.Public,
|
||||
writeVisibility: Visibility? = null,
|
||||
pos: Pos = Pos.builtIn,
|
||||
declaringClass: ObjClass? = this
|
||||
declaringClass: ObjClass? = this,
|
||||
isAbstract: Boolean = false,
|
||||
isClosed: Boolean = false,
|
||||
isOverride: Boolean = false,
|
||||
type: ObjRecord.Type = ObjRecord.Type.Field,
|
||||
) {
|
||||
// Validation of override rules: only for non-system declarations
|
||||
if (pos != Pos.builtIn) {
|
||||
val existing = getInstanceMemberOrNull(name)
|
||||
var actualOverride = false
|
||||
if (existing != null && existing.declaringClass != this) {
|
||||
// If the existing member is private in the ancestor, it's not visible for overriding.
|
||||
// It should be treated as a new member in this class.
|
||||
if (!existing.visibility.isPublic && !canAccessMember(existing.visibility, existing.declaringClass, this)) {
|
||||
// It's effectively not there for us, so actualOverride remains false
|
||||
} else {
|
||||
actualOverride = true
|
||||
// It's an override (implicit or explicit)
|
||||
if (existing.isClosed)
|
||||
throw ScriptError(pos, "can't override closed member $name from ${existing.declaringClass?.className}")
|
||||
|
||||
if (!isOverride)
|
||||
throw ScriptError(pos, "member $name overrides parent member but 'override' keyword is missing")
|
||||
|
||||
if (visibility.ordinal > existing.visibility.ordinal)
|
||||
throw ScriptError(pos, "can't narrow visibility of $name from ${existing.visibility} to $visibility")
|
||||
}
|
||||
}
|
||||
|
||||
if (isOverride && !actualOverride) {
|
||||
throw ScriptError(pos, "member $name is marked 'override' but does not override anything")
|
||||
}
|
||||
}
|
||||
|
||||
// Allow overriding ancestors: only prevent redefinition if THIS class already defines an immutable member
|
||||
val existingInSelf = members[name]
|
||||
if (existingInSelf != null && existingInSelf.isMutable == false)
|
||||
throw ScriptError(pos, "$name is already defined in $objClass")
|
||||
|
||||
// Install/override in this class
|
||||
members[name] = ObjRecord(initialValue, isMutable, visibility, writeVisibility, declaringClass = declaringClass)
|
||||
members[name] = ObjRecord(
|
||||
initialValue, isMutable, visibility, writeVisibility,
|
||||
declaringClass = declaringClass,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride,
|
||||
type = type
|
||||
)
|
||||
// Structural change: bump layout version for PIC invalidation
|
||||
layoutVersion += 1
|
||||
}
|
||||
@@ -383,14 +432,22 @@ open class ObjClass(
|
||||
|
||||
fun addFn(
|
||||
name: String,
|
||||
isOpen: Boolean = false,
|
||||
isMutable: Boolean = false,
|
||||
visibility: Visibility = Visibility.Public,
|
||||
writeVisibility: Visibility? = null,
|
||||
declaringClass: ObjClass? = this,
|
||||
code: suspend Scope.() -> Obj
|
||||
isAbstract: Boolean = false,
|
||||
isClosed: Boolean = false,
|
||||
isOverride: Boolean = false,
|
||||
pos: Pos = Pos.builtIn,
|
||||
code: (suspend Scope.() -> Obj)? = null
|
||||
) {
|
||||
val stmt = statement { code() }
|
||||
createField(name, stmt, isOpen, visibility, writeVisibility, Pos.builtIn, declaringClass)
|
||||
val stmt = code?.let { statement { it() } } ?: ObjNull
|
||||
createField(
|
||||
name, stmt, isMutable, visibility, writeVisibility, pos, declaringClass,
|
||||
isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride,
|
||||
type = ObjRecord.Type.Fun
|
||||
)
|
||||
}
|
||||
|
||||
fun addConst(name: String, value: Obj) = createField(name, value, isMutable = false)
|
||||
@@ -401,13 +458,20 @@ open class ObjClass(
|
||||
setter: (suspend Scope.(Obj) -> Unit)? = null,
|
||||
visibility: Visibility = Visibility.Public,
|
||||
writeVisibility: Visibility? = null,
|
||||
declaringClass: ObjClass? = this
|
||||
declaringClass: ObjClass? = this,
|
||||
isAbstract: Boolean = false,
|
||||
isClosed: Boolean = false,
|
||||
isOverride: Boolean = false,
|
||||
pos: Pos = Pos.builtIn,
|
||||
) {
|
||||
val g = getter?.let { statement { it() } }
|
||||
val s = setter?.let { statement { it(requiredArg(0)); ObjVoid } }
|
||||
val prop = ObjProperty(name, g, s)
|
||||
members[name] = ObjRecord(prop, false, visibility, writeVisibility, declaringClass, type = ObjRecord.Type.Property)
|
||||
layoutVersion += 1
|
||||
val prop = if (isAbstract) ObjNull else ObjProperty(name, g, s)
|
||||
createField(
|
||||
name, prop, false, visibility, writeVisibility, pos, declaringClass,
|
||||
isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride,
|
||||
type = ObjRecord.Type.Property
|
||||
)
|
||||
}
|
||||
|
||||
fun addClassConst(name: String, value: Obj) = createClassField(name, value)
|
||||
@@ -442,6 +506,38 @@ open class ObjClass(
|
||||
getInstanceMemberOrNull(name)
|
||||
?: throw ScriptError(atPos, "symbol doesn't exist: $name")
|
||||
|
||||
fun findFirstConcreteMember(name: String): ObjRecord? {
|
||||
for (cls in mro) {
|
||||
cls.members[name]?.let {
|
||||
if (!it.isAbstract) return it
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun checkAbstractSatisfaction(pos: Pos) {
|
||||
if (isAbstract) return
|
||||
|
||||
val missing = mutableSetOf<String>()
|
||||
for (cls in mroParents) {
|
||||
for ((name, rec) in cls.members) {
|
||||
if (rec.isAbstract) {
|
||||
val current = findFirstConcreteMember(name)
|
||||
if (current == null) {
|
||||
missing.add(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (missing.isNotEmpty()) {
|
||||
throw ScriptError(
|
||||
pos,
|
||||
"class $className is not abstract and does not implement abstract members: ${missing.joinToString(", ")}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve member starting from a specific ancestor class [start], not from this class.
|
||||
* Searches [start] first, then traverses its linearized parents.
|
||||
|
||||
@@ -58,10 +58,16 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
||||
// self first, then parents
|
||||
fun findMangled(): ObjRecord? {
|
||||
// self
|
||||
instanceScope.objects["${cls.className}::$name"]?.let { return it }
|
||||
instanceScope.objects["${cls.className}::$name"]?.let {
|
||||
if (name == "c") println("[DEBUG_LOG] findMangled('c') found in self (${cls.className}): value=${it.value}")
|
||||
return it
|
||||
}
|
||||
// ancestors in deterministic C3 order
|
||||
for (p in cls.mroParents) {
|
||||
instanceScope.objects["${p.className}::$name"]?.let { return it }
|
||||
instanceScope.objects["${p.className}::$name"]?.let {
|
||||
if (name == "c") println("[DEBUG_LOG] findMangled('c') found in parent (${p.className}): value=${it.value}")
|
||||
return it
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -125,6 +131,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
||||
|
||||
val rec = findMangled()
|
||||
if (rec != null) {
|
||||
if (name == "c") println("[DEBUG_LOG] writeField('c') found in mangled: value was ${rec.value}, setting to $newValue")
|
||||
val declaring = when {
|
||||
instanceScope.objects.containsKey("${cls.className}::$name") -> cls
|
||||
else -> cls.mroParents.firstOrNull { instanceScope.objects.containsKey("${it.className}::$name") }
|
||||
@@ -155,34 +162,40 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
||||
onNotFoundResult: (suspend () -> Obj?)?
|
||||
): Obj =
|
||||
instanceScope[name]?.let { rec ->
|
||||
val decl = rec.declaringClass
|
||||
val caller = scope.currentClassCtx ?: if (scope.thisObj === this) objClass else null
|
||||
if (!canAccessMember(rec.visibility, decl, caller))
|
||||
scope.raiseError(
|
||||
ObjAccessException(
|
||||
scope,
|
||||
"can't invoke method $name (declared in ${decl?.className ?: "?"})"
|
||||
if (rec.type == ObjRecord.Type.Property || rec.isAbstract) null
|
||||
else {
|
||||
val decl = rec.declaringClass
|
||||
val caller = scope.currentClassCtx ?: if (scope.thisObj === this) objClass else null
|
||||
if (!canAccessMember(rec.visibility, decl, caller))
|
||||
scope.raiseError(
|
||||
ObjAccessException(
|
||||
scope,
|
||||
"can't invoke method $name (declared in ${decl?.className ?: "?"})"
|
||||
)
|
||||
)
|
||||
rec.value.invoke(
|
||||
instanceScope,
|
||||
this,
|
||||
args
|
||||
)
|
||||
rec.value.invoke(
|
||||
instanceScope,
|
||||
this,
|
||||
args
|
||||
)
|
||||
}
|
||||
}
|
||||
?: run {
|
||||
// fallback: class-scope function (registered during class body execution)
|
||||
objClass.classScope?.objects?.get(name)?.let { rec ->
|
||||
val decl = rec.declaringClass
|
||||
val caller = scope.currentClassCtx ?: if (scope.thisObj === this) objClass else null
|
||||
if (!canAccessMember(rec.visibility, decl, caller))
|
||||
scope.raiseError(
|
||||
ObjAccessException(
|
||||
scope,
|
||||
"can't invoke method $name (declared in ${decl?.className ?: "?"})"
|
||||
if (rec.type == ObjRecord.Type.Property || rec.isAbstract) null
|
||||
else {
|
||||
val decl = rec.declaringClass
|
||||
val caller = scope.currentClassCtx ?: if (scope.thisObj === this) objClass else null
|
||||
if (!canAccessMember(rec.visibility, decl, caller))
|
||||
scope.raiseError(
|
||||
ObjAccessException(
|
||||
scope,
|
||||
"can't invoke method $name (declared in ${decl?.className ?: "?"})"
|
||||
)
|
||||
)
|
||||
)
|
||||
rec.value.invoke(instanceScope, this, args)
|
||||
rec.value.invoke(instanceScope, this, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
||||
|
||||
@@ -32,6 +32,9 @@ data class ObjRecord(
|
||||
var importedFrom: Scope? = null,
|
||||
val isTransient: Boolean = false,
|
||||
val type: Type = Type.Other,
|
||||
val isAbstract: Boolean = false,
|
||||
val isClosed: Boolean = false,
|
||||
val isOverride: Boolean = false,
|
||||
) {
|
||||
val effectiveWriteVisibility: Visibility get() = writeVisibility ?: visibility
|
||||
enum class Type(val comparable: Boolean = false,val serializable: Boolean = false) {
|
||||
|
||||
@@ -1182,8 +1182,11 @@ class MethodCallRef(
|
||||
var hierarchyMember: ObjRecord? = null
|
||||
for (cls in base.objClass.mro) {
|
||||
if (cls.className == "Obj") break
|
||||
hierarchyMember = cls.members[name] ?: cls.classScope?.objects?.get(name)
|
||||
if (hierarchyMember != null) break
|
||||
val rec = cls.members[name] ?: cls.classScope?.objects?.get(name)
|
||||
if (rec != null && !rec.isAbstract && rec.type != ObjRecord.Type.Field) {
|
||||
hierarchyMember = rec
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (hierarchyMember != null) {
|
||||
|
||||
Reference in new issue
Block a user