Refactored ObjRef evaluation logic for enhanced performance and consistency across unary, binary, and field operations. Added evalValue overrides for more streamlined value resolution.

This commit is contained in:
sergeych committed 2026-01-11 01:42:53 +01:00
1 parent 827df9c8cd
commit 6b957ae6a3
28 files changed
+1339 -506

No files matched your search

@@ -169,9 +169,9 @@ class Compiler(
miniSink?.onScriptStart(start)
do {
val t = cc.current()
if (t.type == Token.Type.NEWLINE || t.type == Token.Type.SINLGE_LINE_COMMENT || t.type == Token.Type.MULTILINE_COMMENT) {
if (t.type == Token.Type.NEWLINE || t.type == Token.Type.SINGLE_LINE_COMMENT || t.type == Token.Type.MULTILINE_COMMENT) {
when (t.type) {
Token.Type.SINLGE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT -> pushPendingDocToken(t)
Token.Type.SINGLE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT -> pushPendingDocToken(t)
Token.Type.NEWLINE -> {
// A standalone newline not immediately following a comment resets doc buffer
if (!prevWasComment) clearPendingDoc() else prevWasComment = false
@@ -316,7 +316,7 @@ class Compiler(
}
Token.Type.LABEL -> continue
Token.Type.SINLGE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT -> continue
Token.Type.SINGLE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT -> continue
Token.Type.NEWLINE -> continue
@@ -407,7 +407,7 @@ class Compiler(
val t = cc.next()
val startPos = t.pos
when (t.type) {
// Token.Type.NEWLINE, Token.Type.SINLGE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT-> {
// Token.Type.NEWLINE, Token.Type.SINGLE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT-> {
// continue
// }
@@ -606,7 +606,7 @@ class Compiler(
// to skip in parseExpression:
val current = cc.current()
val right =
if (current.type == Token.Type.NEWLINE || current.type == Token.Type.SINLGE_LINE_COMMENT)
if (current.type == Token.Type.NEWLINE || current.type == Token.Type.SINGLE_LINE_COMMENT)
null
else
parseExpression()
@@ -887,7 +887,7 @@ class Compiler(
}
Token.Type.NEWLINE -> {}
Token.Type.MULTILINE_COMMENT, Token.Type.SINLGE_LINE_COMMENT -> {}
Token.Type.MULTILINE_COMMENT, Token.Type.SINGLE_LINE_COMMENT -> {}
Token.Type.ID -> {
// visibility
@@ -2734,7 +2734,8 @@ class Compiler(
doc = declDocLocal,
nameStart = nameStartPos,
receiver = receiverMini,
isExtern = actualExtern
isExtern = actualExtern,
isStatic = isStatic
)
miniSink?.onFunDecl(node)
pendingDeclDoc = null
@@ -2941,10 +2942,11 @@ class Compiler(
doc = declDocLocal,
nameStart = nameStartPos,
receiver = receiverMini,
isExtern = actualExtern
isExtern = actualExtern,
isStatic = isStatic
)
miniSink?.onFunDecl(node)
miniSink?.onExitFunction(cc.currentPos())
miniSink?.onFunDecl(node)
}
}
@@ -3002,7 +3004,8 @@ class Compiler(
initRange = null,
doc = pendingDeclDoc,
nameStart = namePos,
isExtern = actualExtern
isExtern = actualExtern,
isStatic = false
)
miniSink?.onValDecl(node)
}
@@ -3194,7 +3197,8 @@ class Compiler(
doc = pendingDeclDoc,
nameStart = nameStartPos,
receiver = receiverMini,
isExtern = actualExtern
isExtern = actualExtern,
isStatic = isStatic
)
miniSink?.onValDecl(node)
pendingDeclDoc = null
@@ -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.
@@ -238,7 +238,7 @@ class CompilerContext(val tokens: List<Token>) {
}
}
Token.Type.MULTILINE_COMMENT, Token.Type.SINLGE_LINE_COMMENT, Token.Type.NEWLINE -> {}
Token.Type.MULTILINE_COMMENT, Token.Type.SINGLE_LINE_COMMENT, Token.Type.NEWLINE -> {}
else -> {
restorePos(pos); return false
}
@@ -265,6 +265,6 @@ class CompilerContext(val tokens: List<Token>) {
}
companion object {
val wstokens = setOf(Token.Type.NEWLINE, Token.Type.MULTILINE_COMMENT, Token.Type.SINLGE_LINE_COMMENT)
val wstokens = setOf(Token.Type.NEWLINE, Token.Type.MULTILINE_COMMENT, Token.Type.SINGLE_LINE_COMMENT)
}
}
@@ -131,7 +131,7 @@ private class Parser(fromPos: Pos) {
pos.advance()
val body = loadToEndOfLine()
// Include the leading '//' and do not trim; keep exact lexeme (excluding preceding codepoint)
Token("//" + body, from, Token.Type.SINLGE_LINE_COMMENT)
Token("//" + body, from, Token.Type.SINGLE_LINE_COMMENT)
}
'*' -> {
@@ -22,7 +22,7 @@ data class Token(val value: String, val pos: Pos, val type: Type) {
throw ScriptError(pos, text)
}
val isComment: Boolean by lazy { type == Type.SINLGE_LINE_COMMENT || type == Type.MULTILINE_COMMENT }
val isComment: Boolean by lazy { type == Type.SINGLE_LINE_COMMENT || type == Type.MULTILINE_COMMENT }
fun isId(text: String) =
type == Type.ID && value == text
@@ -41,7 +41,7 @@ data class Token(val value: String, val pos: Pos, val type: Type) {
SHUTTLE,
AND, BITAND, OR, BITOR, BITXOR, NOT, BITNOT, DOT, ARROW, EQARROW, QUESTION, COLONCOLON,
SHL, SHR,
SINLGE_LINE_COMMENT, MULTILINE_COMMENT,
SINGLE_LINE_COMMENT, MULTILINE_COMMENT,
LABEL, ATLABEL, // label@ at@label
// type-checking/casting
AS, ASNULL, OBJECT,
@@ -67,7 +67,7 @@ private fun kindOf(type: Type, value: String): HighlightKind? = when (type) {
Type.REGEX -> HighlightKind.Regex
// comments
Type.SINLGE_LINE_COMMENT, Type.MULTILINE_COMMENT -> HighlightKind.Comment
Type.SINGLE_LINE_COMMENT, Type.MULTILINE_COMMENT -> HighlightKind.Comment
// punctuation
Type.LPAREN, Type.RPAREN, Type.LBRACE, Type.RBRACE, Type.LBRACKET, Type.RBRACKET,
@@ -148,6 +148,8 @@ class ModuleDocsBuilder internal constructor(private val moduleName: String) {
body = null,
doc = md,
nameStart = Pos.builtIn,
isExtern = false,
isStatic = false
)
}
@@ -167,6 +169,8 @@ class ModuleDocsBuilder internal constructor(private val moduleName: String) {
initRange = null,
doc = md,
nameStart = Pos.builtIn,
isExtern = false,
isStatic = false
)
}
@@ -33,6 +33,7 @@ data class CompletionItem(
val kind: Kind,
val tailText: String? = null,
val typeText: String? = null,
val priority: Double = 0.0,
)
enum class Kind { Function, Class_, Enum, Value, Method, Field }
@@ -107,7 +108,7 @@ object CompletionEngineLight {
val locals = DocLookupUtils.extractLocalsAt(text, caret)
for (name in locals) {
if (name.startsWith(prefix, true)) {
out.add(CompletionItem(name, Kind.Value))
out.add(CompletionItem(name, Kind.Value, priority = 150.0))
}
}
@@ -168,17 +169,17 @@ object CompletionEngineLight {
when (node) {
is MiniFunDecl -> {
for (p in node.params) {
add(CompletionItem(p.name, Kind.Value, typeText = typeOf(p.type)))
add(CompletionItem(p.name, Kind.Value, typeText = typeOf(p.type), priority = 200.0))
}
}
is MiniClassDecl -> {
// Propose constructor parameters (ctorFields)
for (p in node.ctorFields) {
add(CompletionItem(p.name, if (p.mutable) Kind.Value else Kind.Field, typeText = typeOf(p.type)))
add(CompletionItem(p.name, if (p.mutable) Kind.Value else Kind.Field, typeText = typeOf(p.type), priority = 200.0))
}
// Propose class-level fields
for (p in node.classFields) {
add(CompletionItem(p.name, if (p.mutable) Kind.Value else Kind.Field, typeText = typeOf(p.type)))
add(CompletionItem(p.name, if (p.mutable) Kind.Value else Kind.Field, typeText = typeOf(p.type), priority = 100.0))
}
// Process members (methods/fields)
for (m in node.members) {
@@ -189,10 +190,10 @@ object CompletionEngineLight {
when (m) {
is MiniMemberFunDecl -> {
val params = m.params.joinToString(", ") { it.name }
add(CompletionItem(m.name, Kind.Method, tailText = "(${params})", typeText = typeOf(m.returnType)))
add(CompletionItem(m.name, Kind.Method, tailText = "(${params})", typeText = typeOf(m.returnType), priority = 100.0))
}
is MiniMemberValDecl -> {
add(CompletionItem(m.name, if (m.mutable) Kind.Value else Kind.Field, typeText = typeOf(m.type)))
add(CompletionItem(m.name, if (m.mutable) Kind.Value else Kind.Field, typeText = typeOf(m.type), priority = 100.0))
}
is MiniInitDecl -> {}
}
@@ -200,7 +201,7 @@ object CompletionEngineLight {
}
is MiniMemberFunDecl -> {
for (p in node.params) {
add(CompletionItem(p.name, Kind.Value, typeText = typeOf(p.type)))
add(CompletionItem(p.name, Kind.Value, typeText = typeOf(p.type), priority = 200.0))
}
}
else -> {}
@@ -250,7 +251,7 @@ object CompletionEngineLight {
"Array" -> listOf("Collection", "Iterable").forEach { if (visited.add(it)) addMembersOf(it, false) }
}
fun emitGroup(map: LinkedHashMap<String, MutableList<MiniMemberDecl>>) {
fun emitGroup(map: LinkedHashMap<String, MutableList<MiniMemberDecl>>, groupPriority: Double) {
for (name in map.keys.sortedBy { it.lowercase() }) {
val variants = map[name] ?: continue
// Prefer a method with a known return type; else any method; else first variant
@@ -265,7 +266,7 @@ object CompletionEngineLight {
val params = rep.params.joinToString(", ") { it.name }
val extra = variants.count { it is MiniMemberFunDecl } - 1
val ov = if (extra > 0) " (+$extra overloads)" else ""
val ci = CompletionItem(name, Kind.Method, tailText = "(${params})$ov", typeText = typeOf(rep.returnType))
val ci = CompletionItem(name, Kind.Method, tailText = "(${params})$ov", typeText = typeOf(rep.returnType), priority = groupPriority)
if (ci.name.startsWith(prefix, true)) out += ci
}
is MiniMemberValDecl -> {
@@ -273,7 +274,7 @@ object CompletionEngineLight {
val chosen = variants.asSequence()
.filterIsInstance<MiniMemberValDecl>()
.firstOrNull { it.type != null } ?: rep
val ci = CompletionItem(name, Kind.Field, typeText = typeOf(chosen.type))
val ci = CompletionItem(name, Kind.Field, typeText = typeOf(chosen.type), priority = groupPriority)
if (ci.name.startsWith(prefix, true)) out += ci
}
is MiniInitDecl -> {}
@@ -281,8 +282,8 @@ object CompletionEngineLight {
}
}
emitGroup(directMap)
emitGroup(inheritedMap)
emitGroup(directMap, 100.0)
emitGroup(inheritedMap, 0.0)
// Supplement with extension members (both stdlib and local)
run {
@@ -296,22 +297,22 @@ object CompletionEngineLight {
val ci = when (m) {
is MiniMemberFunDecl -> {
val params = m.params.joinToString(", ") { it.name }
CompletionItem(name, Kind.Method, tailText = "(${params})", typeText = typeOf(m.returnType))
CompletionItem(name, Kind.Method, tailText = "(${params})", typeText = typeOf(m.returnType), priority = 50.0)
}
is MiniFunDecl -> {
val params = m.params.joinToString(", ") { it.name }
CompletionItem(name, Kind.Method, tailText = "(${params})", typeText = typeOf(m.returnType))
CompletionItem(name, Kind.Method, tailText = "(${params})", typeText = typeOf(m.returnType), priority = 50.0)
}
is MiniMemberValDecl -> CompletionItem(name, Kind.Field, typeText = typeOf(m.type))
is MiniValDecl -> CompletionItem(name, Kind.Field, typeText = typeOf(m.type))
else -> CompletionItem(name, Kind.Method, tailText = "()", typeText = null)
is MiniMemberValDecl -> CompletionItem(name, Kind.Field, typeText = typeOf(m.type), priority = 50.0)
is MiniValDecl -> CompletionItem(name, Kind.Field, typeText = typeOf(m.type), priority = 50.0)
else -> CompletionItem(name, Kind.Method, tailText = "()", typeText = null, priority = 50.0)
}
if (ci.name.startsWith(prefix, true)) {
out += ci
already.add(name)
}
} else {
val ci = CompletionItem(name, Kind.Method, tailText = "()", typeText = null)
val ci = CompletionItem(name, Kind.Method, tailText = "()", typeText = null, priority = 50.0)
if (ci.name.startsWith(prefix, true)) {
out += ci
already.add(name)
@@ -87,6 +87,7 @@ sealed interface MiniNamedDecl : MiniNode {
// Start position of the declaration name identifier in source; end can be derived as start + name.length
val nameStart: Pos
val isExtern: Boolean
val isStatic: Boolean
}
sealed interface MiniDecl : MiniNamedDecl
@@ -114,7 +115,8 @@ data class MiniFunDecl(
override val doc: MiniDoc?,
override val nameStart: Pos,
val receiver: MiniTypeRef? = null,
override val isExtern: Boolean = false
override val isExtern: Boolean = false,
override val isStatic: Boolean = false,
) : MiniDecl
data class MiniValDecl(
@@ -126,7 +128,8 @@ data class MiniValDecl(
override val doc: MiniDoc?,
override val nameStart: Pos,
val receiver: MiniTypeRef? = null,
override val isExtern: Boolean = false
override val isExtern: Boolean = false,
override val isStatic: Boolean = false,
) : MiniDecl
data class MiniClassDecl(
@@ -141,6 +144,7 @@ data class MiniClassDecl(
// Built-in extension: list of member declarations (functions and fields)
val members: List<MiniMemberDecl> = emptyList(),
override val isExtern: Boolean = false,
override val isStatic: Boolean = false,
val isObject: Boolean = false
) : MiniDecl
@@ -150,7 +154,8 @@ data class MiniEnumDecl(
val entries: List<String>,
override val doc: MiniDoc?,
override val nameStart: Pos,
override val isExtern: Boolean = false
override val isExtern: Boolean = false,
override val isStatic: Boolean = false,
) : MiniDecl
data class MiniCtorField(
@@ -175,7 +180,7 @@ data class MiniIdentifier(
// --- Class member declarations (for built-in/registry docs) ---
sealed interface MiniMemberDecl : MiniNamedDecl {
val isStatic: Boolean
override val isStatic: Boolean
}
data class MiniMemberFunDecl(
@@ -319,7 +324,7 @@ class MiniAstBuilder : MiniAstSink {
returnType = attach.returnType,
doc = attach.doc,
nameStart = attach.nameStart,
isStatic = false, // TODO: track static if needed
isStatic = attach.isStatic,
isExtern = attach.isExtern,
body = attach.body
)
@@ -359,7 +364,7 @@ class MiniAstBuilder : MiniAstSink {
initRange = attach.initRange,
doc = attach.doc,
nameStart = attach.nameStart,
isStatic = false, // TODO: track static if needed
isStatic = attach.isStatic,
isExtern = attach.isExtern
)
// Duplicates for vals are rare but possible if Compiler calls it twice
@@ -106,6 +106,34 @@ open class ObjClass(
val classId: Long = ClassIdGen.nextId()
var layoutVersion: Int = 0
private val mangledNameCache = mutableMapOf<String, String>()
fun mangledName(name: String): String = mangledNameCache.getOrPut(name) { "$className::$name" }
/**
* Map of public member names to their effective storage keys in instanceScope.objects.
* This is pre-calculated to avoid MRO traversal and string concatenation during common access.
*/
val publicMemberResolution: Map<String, String> by lazy {
val res = mutableMapOf<String, String>()
// Traverse MRO in REVERSED order so that child classes override parent classes in the map.
for (cls in mro.reversed()) {
if (cls.className == "Obj") continue
for ((name, rec) in cls.members) {
if (rec.visibility == Visibility.Public) {
val key = if (rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.Delegated) cls.mangledName(name) else name
res[name] = key
}
}
cls.classScope?.objects?.forEach { (name, rec) ->
if (rec.visibility == Visibility.Public && (rec.value is Statement || rec.type == ObjRecord.Type.Delegated)) {
val key = if (rec.type == ObjRecord.Type.Delegated) cls.mangledName(name) else name
res[name] = key
}
}
}
res
}
val classNameObj by lazy { ObjString(className) }
var constructorMeta: ArgsDeclaration? = null
@@ -242,6 +270,43 @@ open class ObjClass(
return instance
}
/** Pre-calculated template for instanceScope.objects. */
private val instanceObjectsTemplate: Map<String, ObjRecord> by lazy {
val res = mutableMapOf<String, ObjRecord>()
for (cls in mro) {
// 1) members-defined methods and fields
for ((k, v) in cls.members) {
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.mangledName(k) else k
if (!res.containsKey(key)) {
res[key] = v
}
}
}
// 2) class-scope members registered during class-body execution
cls.classScope?.objects?.forEach { (k, rec) ->
// 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.mangledName(k) else k
// if not already present, copy reference for dispatch
if (!res.containsKey(key)) {
res[key] = rec
}
}
}
}
res
}
private val templateMethods: Map<String, ObjRecord> by lazy {
instanceObjectsTemplate.filter { it.value.type == ObjRecord.Type.Fun }
}
private val templateOthers: List<Pair<String, ObjRecord>> by lazy {
instanceObjectsTemplate.filter { it.value.type != ObjRecord.Type.Fun }.toList()
}
/**
* Create an instance of this class and initialize its [ObjInstance.instanceScope] with
* methods. Does NOT run initializers or constructors.
@@ -257,28 +322,9 @@ open class ObjClass(
// 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 and fields
for ((k, v) in cls.members) {
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.Fun) v else v.copy()
}
}
}
// 2) class-scope members registered during class-body execution
cls.classScope?.objects?.forEach { (k, rec) ->
// 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.Fun) rec else rec.copy()
}
}
}
instance.instanceScope.objects.putAll(templateMethods)
for (p in templateOthers) {
instance.instanceScope.objects[p.first] = p.second.copy()
}
return instance
}
@@ -327,7 +373,7 @@ open class ObjClass(
for (p in meta.params) {
val rec = instance.instanceScope.objects[p.name]
if (rec != null) {
val mangled = "${c.className}::${p.name}"
val mangled = c.mangledName(p.name)
// Always point the mangled name to the current record to keep writes consistent
// across re-bindings
instance.instanceScope.objects[mangled] = rec
@@ -361,7 +407,7 @@ open class ObjClass(
for (p in meta.params) {
val rec = instance.instanceScope.objects[p.name]
if (rec != null) {
val mangled = "${c.className}::${p.name}"
val mangled = c.mangledName(p.name)
instance.instanceScope.objects[mangled] = rec
}
}
@@ -34,6 +34,18 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
override suspend fun readField(scope: Scope, name: String): ObjRecord {
val caller = scope.currentClassCtx
// Fast path for public members when outside any class context
if (caller == null) {
objClass.publicMemberResolution[name]?.let { key ->
instanceScope.objects[key]?.let { rec ->
// Directly return fields to bypass resolveRecord overhead
if ((rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField) && !rec.isAbstract)
return rec
return resolveRecord(scope, rec, name, rec.declaringClass)
}
}
}
// 0. Private mangled of current class context
caller?.let { c ->
// Check for private methods/properties
@@ -43,7 +55,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
}
}
// Check for private fields (stored in instanceScope)
val mangled = "${c.className}::$name"
val mangled = c.mangledName(name)
instanceScope.objects[mangled]?.let { rec ->
if (rec.visibility == Visibility.Private) {
return resolveRecord(scope, rec, name, c)
@@ -54,7 +66,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
// 1. MRO mangled storage
for (cls in objClass.mro) {
if (cls.className == "Obj") break
val mangled = "${cls.className}::$name"
val mangled = cls.mangledName(name)
instanceScope.objects[mangled]?.let { rec ->
if ((scope.thisObj === this && caller != null) || canAccessMember(rec.visibility, cls, caller)) {
return resolveRecord(scope, rec, name, cls)
@@ -79,11 +91,11 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
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"
val storageName = d?.mangledName(name) ?: name
var del = instanceScope[storageName]?.delegate ?: obj.delegate
if (del == null) {
for (c in objClass.mro) {
del = instanceScope["${c.className}::$name"]?.delegate
del = instanceScope[c.mangledName(name)]?.delegate
if (del != null) break
}
}
@@ -97,7 +109,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
var targetRec = obj
val d = decl ?: obj.declaringClass
if (d != null) {
val mangled = "${d.className}::$name"
val mangled = d.mangledName(name)
instanceScope.objects[mangled]?.let {
targetRec = it
}
@@ -120,6 +132,24 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
willMutate(scope)
val caller = scope.currentClassCtx
// Fast path for public members when outside any class context
if (caller == null) {
objClass.publicMemberResolution[name]?.let { key ->
instanceScope.objects[key]?.let { rec ->
if (rec.effectiveWriteVisibility == Visibility.Public) {
// Skip property/delegated overhead if it's a plain mutable field
if (rec.type == ObjRecord.Type.Field && rec.isMutable && !rec.isAbstract) {
if (rec.value.assign(scope, newValue) == null)
rec.value = newValue
return
}
updateRecord(scope, rec, name, newValue, rec.declaringClass)
return
}
}
}
}
// 0. Private mangled of current class context
caller?.let { c ->
// Check for private methods/properties
@@ -130,7 +160,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
}
}
// Check for private fields (stored in instanceScope)
val mangled = "${c.className}::$name"
val mangled = c.mangledName(name)
instanceScope.objects[mangled]?.let { rec ->
if (rec.visibility == Visibility.Private) {
updateRecord(scope, rec, name, newValue, c)
@@ -142,7 +172,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
// 1. MRO mangled storage
for (cls in objClass.mro) {
if (cls.className == "Obj") break
val mangled = "${cls.className}::$name"
val mangled = cls.mangledName(name)
instanceScope.objects[mangled]?.let { rec ->
if ((scope.thisObj === this && caller != null) || canAccessMember(rec.effectiveWriteVisibility, cls, caller)) {
updateRecord(scope, rec, name, newValue, cls)
@@ -191,24 +221,42 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
scope: Scope, name: String, args: Arguments,
onNotFoundResult: (suspend () -> Obj?)?
): Obj {
// 0. Prefer private member of current class context
scope.currentClassCtx?.let { caller ->
val mangled = "${caller.className}::$name"
instanceScope.objects[mangled]?.let { rec ->
if (rec.visibility == Visibility.Private && !rec.isAbstract) {
if (rec.type == ObjRecord.Type.Property) {
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, caller)
} else if (rec.type == ObjRecord.Type.Fun) {
return rec.value.invoke(instanceScope, this, args, caller)
val caller = scope.currentClassCtx
// Fast path for public members when outside any class context
if (caller == null) {
objClass.publicMemberResolution[name]?.let { key ->
instanceScope.objects[key]?.let { rec ->
if (rec.visibility == Visibility.Public && !rec.isAbstract) {
val decl = rec.declaringClass
if (rec.type == ObjRecord.Type.Property) {
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, decl)
} else if (rec.type == ObjRecord.Type.Fun) {
return rec.value.invoke(instanceScope, this, args, decl)
}
}
}
}
caller.members[name]?.let { rec ->
}
// 0. Prefer private member of current class context
caller?.let { c ->
val mangled = c.mangledName(name)
instanceScope.objects[mangled]?.let { rec ->
if (rec.visibility == Visibility.Private && !rec.isAbstract) {
if (rec.type == ObjRecord.Type.Property) {
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, caller)
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, c)
} else if (rec.type == ObjRecord.Type.Fun) {
return rec.value.invoke(instanceScope, this, args, caller)
return rec.value.invoke(instanceScope, this, args, c)
}
}
}
c.members[name]?.let { rec ->
if (rec.visibility == Visibility.Private && !rec.isAbstract) {
if (rec.type == ObjRecord.Type.Property) {
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, c)
} else if (rec.type == ObjRecord.Type.Fun) {
return rec.value.invoke(instanceScope, this, args, c)
}
}
}
@@ -220,7 +268,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
val rec = cls.members[name] ?: cls.classScope?.objects?.get(name)
if (rec != null && !rec.isAbstract) {
if (rec.type == ObjRecord.Type.Delegated) {
val storageName = "${cls.className}::$name"
val storageName = cls.mangledName(name)
val del = instanceScope[storageName]?.delegate ?: rec.delegate
?: scope.raiseError("Internal error: delegated member $name has no delegate (tried $storageName)")
@@ -233,8 +281,8 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
})
}
val decl = rec.declaringClass ?: cls
val caller = scope.currentClassCtx ?: if (scope.thisObj === this) objClass else null
if (!canAccessMember(rec.visibility, decl, caller))
val effectiveCaller = caller ?: if (scope.thisObj === this) objClass else null
if (!canAccessMember(rec.visibility, decl, effectiveCaller))
scope.raiseError(
ObjIllegalAccessException(
scope,
File diff suppressed because it is too large. Load diff