Optimize member access via slots
This commit is contained in:
parent
9b580bafb6
commit
74d73540c6
@ -44,6 +44,10 @@ class Compiler(
|
|||||||
private val currentLocalNames: MutableSet<String>?
|
private val currentLocalNames: MutableSet<String>?
|
||||||
get() = localNamesStack.lastOrNull()
|
get() = localNamesStack.lastOrNull()
|
||||||
|
|
||||||
|
private data class SlotPlan(val slots: MutableMap<String, Int>, var nextIndex: Int)
|
||||||
|
private data class SlotLocation(val slot: Int, val depth: Int)
|
||||||
|
private val slotPlanStack = mutableListOf<SlotPlan>()
|
||||||
|
|
||||||
// Track declared local variables count per function for precise capacity hints
|
// Track declared local variables count per function for precise capacity hints
|
||||||
private val localDeclCountStack = mutableListOf<Int>()
|
private val localDeclCountStack = mutableListOf<Int>()
|
||||||
private val currentLocalDeclCount: Int
|
private val currentLocalDeclCount: Int
|
||||||
@ -64,6 +68,35 @@ class Compiler(
|
|||||||
if (added && localDeclCountStack.isNotEmpty()) {
|
if (added && localDeclCountStack.isNotEmpty()) {
|
||||||
localDeclCountStack[localDeclCountStack.lastIndex] = currentLocalDeclCount + 1
|
localDeclCountStack[localDeclCountStack.lastIndex] = currentLocalDeclCount + 1
|
||||||
}
|
}
|
||||||
|
declareSlotName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun declareSlotName(name: String) {
|
||||||
|
val plan = slotPlanStack.lastOrNull() ?: return
|
||||||
|
if (plan.slots.containsKey(name)) return
|
||||||
|
plan.slots[name] = plan.nextIndex
|
||||||
|
plan.nextIndex += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildParamSlotPlan(names: List<String>): SlotPlan {
|
||||||
|
val map = mutableMapOf<String, Int>()
|
||||||
|
var idx = 0
|
||||||
|
for (name in names) {
|
||||||
|
if (!map.containsKey(name)) {
|
||||||
|
map[name] = idx
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SlotPlan(map, idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun lookupSlotLocation(name: String): SlotLocation? {
|
||||||
|
for (i in slotPlanStack.indices.reversed()) {
|
||||||
|
val slot = slotPlanStack[i].slots[name] ?: continue
|
||||||
|
val depth = slotPlanStack.size - 1 - i
|
||||||
|
return SlotLocation(slot, depth)
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
var packageName: String? = null
|
var packageName: String? = null
|
||||||
@ -243,9 +276,12 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val module = importManager.prepareImport(pos, name, null)
|
val module = importManager.prepareImport(pos, name, null)
|
||||||
statements += statement {
|
statements += object : Statement() {
|
||||||
module.importInto(this, null)
|
override val pos: Pos = pos
|
||||||
ObjVoid
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
module.importInto(scope, null)
|
||||||
|
return ObjVoid
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -376,7 +412,13 @@ class Compiler(
|
|||||||
|
|
||||||
private suspend fun parseExpression(): Statement? {
|
private suspend fun parseExpression(): Statement? {
|
||||||
val pos = cc.currentPos()
|
val pos = cc.currentPos()
|
||||||
return parseExpressionLevel()?.let { a -> statement(pos) { a.evalValue(it) } }
|
return parseExpressionLevel()?.let { ref ->
|
||||||
|
val stmtPos = pos
|
||||||
|
object : Statement() {
|
||||||
|
override val pos: Pos = stmtPos
|
||||||
|
override suspend fun execute(scope: Scope): Obj = ref.evalValue(scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun parseExpressionLevel(level: Int = 0): ObjRef? {
|
private suspend fun parseExpressionLevel(level: Int = 0): ObjRef? {
|
||||||
@ -414,6 +456,10 @@ class Compiler(
|
|||||||
val name = when (target) {
|
val name = when (target) {
|
||||||
is LocalVarRef -> target.name
|
is LocalVarRef -> target.name
|
||||||
is FastLocalVarRef -> target.name
|
is FastLocalVarRef -> target.name
|
||||||
|
is LocalSlotRef -> target.name
|
||||||
|
is ImplicitThisMemberRef -> target.name
|
||||||
|
is ThisFieldSlotRef -> target.name
|
||||||
|
is QualifiedThisFieldSlotRef -> target.name
|
||||||
is FieldRef -> if (target.target is LocalVarRef && target.target.name == "this") target.name else null
|
is FieldRef -> if (target.target is LocalVarRef && target.target.name == "this") target.name else null
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
@ -487,7 +533,16 @@ class Compiler(
|
|||||||
val args = parsed.first
|
val args = parsed.first
|
||||||
val tailBlock = parsed.second
|
val tailBlock = parsed.second
|
||||||
isCall = true
|
isCall = true
|
||||||
operand = MethodCallRef(left, next.value, args, tailBlock, isOptional)
|
operand = when (left) {
|
||||||
|
is LocalVarRef -> if (left.name == "this") {
|
||||||
|
ThisMethodSlotCallRef(next.value, args, tailBlock, isOptional)
|
||||||
|
} else {
|
||||||
|
MethodCallRef(left, next.value, args, tailBlock, isOptional)
|
||||||
|
}
|
||||||
|
is QualifiedThisRef ->
|
||||||
|
QualifiedThisMethodSlotCallRef(left.typeName, next.value, args, tailBlock, isOptional)
|
||||||
|
else -> MethodCallRef(left, next.value, args, tailBlock, isOptional)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -496,16 +551,37 @@ class Compiler(
|
|||||||
cc.next()
|
cc.next()
|
||||||
isCall = true
|
isCall = true
|
||||||
val lambda = parseLambdaExpression()
|
val lambda = parseLambdaExpression()
|
||||||
val argStmt = statement { lambda.get(this).value }
|
val argPos = next.pos
|
||||||
|
val argStmt = object : Statement() {
|
||||||
|
override val pos: Pos = argPos
|
||||||
|
override suspend fun execute(scope: Scope): Obj = lambda.get(scope).value
|
||||||
|
}
|
||||||
val args = listOf(ParsedArgument(argStmt, next.pos))
|
val args = listOf(ParsedArgument(argStmt, next.pos))
|
||||||
operand = MethodCallRef(left, next.value, args, true, isOptional)
|
operand = when (left) {
|
||||||
|
is LocalVarRef -> if (left.name == "this") {
|
||||||
|
ThisMethodSlotCallRef(next.value, args, true, isOptional)
|
||||||
|
} else {
|
||||||
|
MethodCallRef(left, next.value, args, true, isOptional)
|
||||||
|
}
|
||||||
|
is QualifiedThisRef ->
|
||||||
|
QualifiedThisMethodSlotCallRef(left.typeName, next.value, args, true, isOptional)
|
||||||
|
else -> MethodCallRef(left, next.value, args, true, isOptional)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isCall) {
|
if (!isCall) {
|
||||||
operand = FieldRef(left, next.value, isOptional)
|
operand = when (left) {
|
||||||
|
is LocalVarRef -> if (left.name == "this") {
|
||||||
|
ThisFieldSlotRef(next.value, isOptional)
|
||||||
|
} else {
|
||||||
|
FieldRef(left, next.value, isOptional)
|
||||||
|
}
|
||||||
|
is QualifiedThisRef -> QualifiedThisFieldSlotRef(left.typeName, next.value, isOptional)
|
||||||
|
else -> FieldRef(left, next.value, isOptional)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -597,7 +673,15 @@ class Compiler(
|
|||||||
// selector: <lvalue>, '.' , <id>
|
// selector: <lvalue>, '.' , <id>
|
||||||
// we replace operand with selector code, that
|
// we replace operand with selector code, that
|
||||||
// is RW:
|
// is RW:
|
||||||
operand = FieldRef(left, t.value, false)
|
operand = when (left) {
|
||||||
|
is LocalVarRef -> if (left.name == "this") {
|
||||||
|
ThisFieldSlotRef(t.value, false)
|
||||||
|
} else {
|
||||||
|
FieldRef(left, t.value, false)
|
||||||
|
}
|
||||||
|
is QualifiedThisRef -> QualifiedThisFieldSlotRef(left.typeName, t.value, false)
|
||||||
|
else -> FieldRef(left, t.value, false)
|
||||||
|
}
|
||||||
} ?: run {
|
} ?: run {
|
||||||
// variable to read or like
|
// variable to read or like
|
||||||
cc.previous()
|
cc.previous()
|
||||||
@ -696,19 +780,31 @@ class Compiler(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val paramNames = argsDeclaration?.params?.map { it.name } ?: emptyList()
|
val paramNames = argsDeclaration?.params?.map { it.name } ?: emptyList()
|
||||||
|
val hasImplicitIt = argsDeclaration == null
|
||||||
|
val slotParamNames = if (hasImplicitIt) paramNames + "it" else paramNames
|
||||||
|
val paramSlotPlan = buildParamSlotPlan(slotParamNames)
|
||||||
|
|
||||||
label?.let { cc.labels.add(it) }
|
label?.let { cc.labels.add(it) }
|
||||||
val body = inCodeContext(CodeContext.Function("<lambda>")) {
|
slotPlanStack.add(paramSlotPlan)
|
||||||
withLocalNames(paramNames.toSet()) {
|
val body = try {
|
||||||
|
inCodeContext(CodeContext.Function("<lambda>")) {
|
||||||
|
withLocalNames(slotParamNames.toSet()) {
|
||||||
parseBlock(skipLeadingBrace = true)
|
parseBlock(skipLeadingBrace = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
slotPlanStack.removeLast()
|
||||||
|
}
|
||||||
label?.let { cc.labels.remove(it) }
|
label?.let { cc.labels.remove(it) }
|
||||||
|
|
||||||
|
val paramSlotPlanSnapshot = if (paramSlotPlan.slots.isEmpty()) emptyMap() else paramSlotPlan.slots.toMap()
|
||||||
return ValueFnRef { closureScope ->
|
return ValueFnRef { closureScope ->
|
||||||
statement(body.pos) { scope ->
|
val stmt = object : Statement() {
|
||||||
|
override val pos: Pos = body.pos
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
// and the source closure of the lambda which might have other thisObj.
|
// and the source closure of the lambda which might have other thisObj.
|
||||||
val context = scope.applyClosure(closureScope)
|
val context = scope.applyClosure(closureScope)
|
||||||
|
if (paramSlotPlanSnapshot.isNotEmpty()) context.applySlotPlan(paramSlotPlanSnapshot)
|
||||||
// Execute lambda body in a closure-aware context. Blocks inside the lambda
|
// Execute lambda body in a closure-aware context. Blocks inside the lambda
|
||||||
// will create child scopes as usual, so re-declarations inside loops work.
|
// will create child scopes as usual, so re-declarations inside loops work.
|
||||||
if (argsDeclaration == null) {
|
if (argsDeclaration == null) {
|
||||||
@ -727,13 +823,15 @@ class Compiler(
|
|||||||
// assign vars as declared the standard way
|
// assign vars as declared the standard way
|
||||||
argsDeclaration.assignToContext(context, defaultAccessType = AccessType.Val)
|
argsDeclaration.assignToContext(context, defaultAccessType = AccessType.Val)
|
||||||
}
|
}
|
||||||
try {
|
return try {
|
||||||
body.execute(context)
|
body.execute(context)
|
||||||
} catch (e: ReturnException) {
|
} catch (e: ReturnException) {
|
||||||
if (e.label == null || e.label == label) e.result
|
if (e.label == null || e.label == label) e.result
|
||||||
else throw e
|
else throw e
|
||||||
}
|
}
|
||||||
}.asReadonly
|
}
|
||||||
|
}
|
||||||
|
stmt.asReadonly
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1122,7 +1220,12 @@ class Compiler(
|
|||||||
val next = cc.peekNextNonWhitespace()
|
val next = cc.peekNextNonWhitespace()
|
||||||
if (next.type == Token.Type.COMMA || next.type == Token.Type.RPAREN) {
|
if (next.type == Token.Type.COMMA || next.type == Token.Type.RPAREN) {
|
||||||
val localVar = LocalVarRef(name, t1.pos)
|
val localVar = LocalVarRef(name, t1.pos)
|
||||||
return ParsedArgument(statement(t1.pos) { localVar.evalValue(it) }, t1.pos, isSplat = false, name = name)
|
val argPos = t1.pos
|
||||||
|
val argStmt = object : Statement() {
|
||||||
|
override val pos: Pos = argPos
|
||||||
|
override suspend fun execute(scope: Scope): Obj = localVar.evalValue(scope)
|
||||||
|
}
|
||||||
|
return ParsedArgument(argStmt, t1.pos, isSplat = false, name = name)
|
||||||
}
|
}
|
||||||
val rhs = parseExpression() ?: t2.raiseSyntax("expected expression after named argument '${name}:'")
|
val rhs = parseExpression() ?: t2.raiseSyntax("expected expression after named argument '${name}:'")
|
||||||
return ParsedArgument(rhs, t1.pos, isSplat = false, name = name)
|
return ParsedArgument(rhs, t1.pos, isSplat = false, name = name)
|
||||||
@ -1166,8 +1269,9 @@ class Compiler(
|
|||||||
val callableAccessor = parseLambdaExpression()
|
val callableAccessor = parseLambdaExpression()
|
||||||
args += ParsedArgument(
|
args += ParsedArgument(
|
||||||
// transform ObjRef to the callable value
|
// transform ObjRef to the callable value
|
||||||
statement {
|
object : Statement() {
|
||||||
callableAccessor.get(this).value
|
override val pos: Pos = end.pos
|
||||||
|
override suspend fun execute(scope: Scope): Obj = callableAccessor.get(scope).value
|
||||||
},
|
},
|
||||||
end.pos
|
end.pos
|
||||||
)
|
)
|
||||||
@ -1194,7 +1298,12 @@ class Compiler(
|
|||||||
val next = cc.peekNextNonWhitespace()
|
val next = cc.peekNextNonWhitespace()
|
||||||
if (next.type == Token.Type.COMMA || next.type == Token.Type.RPAREN) {
|
if (next.type == Token.Type.COMMA || next.type == Token.Type.RPAREN) {
|
||||||
val localVar = LocalVarRef(name, t1.pos)
|
val localVar = LocalVarRef(name, t1.pos)
|
||||||
return ParsedArgument(statement(t1.pos) { localVar.evalValue(it) }, t1.pos, isSplat = false, name = name)
|
val argPos = t1.pos
|
||||||
|
val argStmt = object : Statement() {
|
||||||
|
override val pos: Pos = argPos
|
||||||
|
override suspend fun execute(scope: Scope): Obj = localVar.evalValue(scope)
|
||||||
|
}
|
||||||
|
return ParsedArgument(argStmt, t1.pos, isSplat = false, name = name)
|
||||||
}
|
}
|
||||||
val rhs = parseExpression() ?: t2.raiseSyntax("expected expression after named argument '${name}:'")
|
val rhs = parseExpression() ?: t2.raiseSyntax("expected expression after named argument '${name}:'")
|
||||||
return ParsedArgument(rhs, t1.pos, isSplat = false, name = name)
|
return ParsedArgument(rhs, t1.pos, isSplat = false, name = name)
|
||||||
@ -1246,14 +1355,21 @@ class Compiler(
|
|||||||
// into the lambda body. This ensures expected order:
|
// into the lambda body. This ensures expected order:
|
||||||
// foo { ... }.bar() == (foo { ... }).bar()
|
// foo { ... }.bar() == (foo { ... }).bar()
|
||||||
val callableAccessor = parseLambdaExpression()
|
val callableAccessor = parseLambdaExpression()
|
||||||
val argStmt = statement { callableAccessor.get(this).value }
|
val argStmt = object : Statement() {
|
||||||
|
override val pos: Pos = cc.currentPos()
|
||||||
|
override suspend fun execute(scope: Scope): Obj = callableAccessor.get(scope).value
|
||||||
|
}
|
||||||
listOf(ParsedArgument(argStmt, cc.currentPos()))
|
listOf(ParsedArgument(argStmt, cc.currentPos()))
|
||||||
} else {
|
} else {
|
||||||
val r = parseArgs()
|
val r = parseArgs()
|
||||||
detectedBlockArgument = r.second
|
detectedBlockArgument = r.second
|
||||||
r.first
|
r.first
|
||||||
}
|
}
|
||||||
return CallRef(left, args, detectedBlockArgument, isOptional)
|
return when (left) {
|
||||||
|
is ImplicitThisMemberRef ->
|
||||||
|
ImplicitThisMethodCallRef(left.name, args, detectedBlockArgument, isOptional, left.atPos)
|
||||||
|
else -> CallRef(left, args, detectedBlockArgument, isOptional)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun parseAccessor(): ObjRef? {
|
private suspend fun parseAccessor(): ObjRef? {
|
||||||
@ -1301,9 +1417,17 @@ class Compiler(
|
|||||||
"null" -> ConstRef(ObjNull.asReadonly)
|
"null" -> ConstRef(ObjNull.asReadonly)
|
||||||
"true" -> ConstRef(ObjTrue.asReadonly)
|
"true" -> ConstRef(ObjTrue.asReadonly)
|
||||||
"false" -> ConstRef(ObjFalse.asReadonly)
|
"false" -> ConstRef(ObjFalse.asReadonly)
|
||||||
else -> if (PerfFlags.EMIT_FAST_LOCAL_REFS && (currentLocalNames?.contains(t.value) == true))
|
else -> {
|
||||||
|
val slotLoc = lookupSlotLocation(t.value)
|
||||||
|
val inClassCtx = codeContexts.any { it is CodeContext.ClassBody }
|
||||||
|
when {
|
||||||
|
slotLoc != null -> LocalSlotRef(t.value, slotLoc.slot, slotLoc.depth, t.pos)
|
||||||
|
PerfFlags.EMIT_FAST_LOCAL_REFS && (currentLocalNames?.contains(t.value) == true) ->
|
||||||
FastLocalVarRef(t.value, t.pos)
|
FastLocalVarRef(t.value, t.pos)
|
||||||
else LocalVarRef(t.value, t.pos)
|
inClassCtx -> ImplicitThisMemberRef(t.value, t.pos)
|
||||||
|
else -> LocalVarRef(t.value, t.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1526,20 +1650,27 @@ class Compiler(
|
|||||||
lastParsedBlockRange?.let { range ->
|
lastParsedBlockRange?.let { range ->
|
||||||
miniSink?.onInitDecl(MiniInitDecl(MiniRange(id.pos, range.end), id.pos))
|
miniSink?.onInitDecl(MiniInitDecl(MiniRange(id.pos, range.end), id.pos))
|
||||||
}
|
}
|
||||||
val initStmt = statement(id.pos) { scp ->
|
val initPos = id.pos
|
||||||
val cls = scp.thisObj.objClass
|
val initStmt = object : Statement() {
|
||||||
val saved = scp.currentClassCtx
|
override val pos: Pos = initPos
|
||||||
scp.currentClassCtx = cls
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val cls = scope.thisObj.objClass
|
||||||
|
val saved = scope.currentClassCtx
|
||||||
|
scope.currentClassCtx = cls
|
||||||
try {
|
try {
|
||||||
block.execute(scp)
|
block.execute(scope)
|
||||||
} finally {
|
} finally {
|
||||||
scp.currentClassCtx = saved
|
scope.currentClassCtx = saved
|
||||||
}
|
}
|
||||||
ObjVoid
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object : Statement() {
|
||||||
|
override val pos: Pos = id.pos
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
scope.currentClassCtx?.instanceInitializers?.add(initStmt)
|
||||||
|
return ObjVoid
|
||||||
}
|
}
|
||||||
statement {
|
|
||||||
currentClassCtx?.instanceInitializers?.add(initStmt)
|
|
||||||
ObjVoid
|
|
||||||
}
|
}
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
@ -1695,9 +1826,13 @@ class Compiler(
|
|||||||
// we need a copy in the closure:
|
// we need a copy in the closure:
|
||||||
val isIn = t.type == Token.Type.IN
|
val isIn = t.type == Token.Type.IN
|
||||||
val container = parseExpression() ?: throw ScriptError(cc.currentPos(), "type expected")
|
val container = parseExpression() ?: throw ScriptError(cc.currentPos(), "type expected")
|
||||||
currentCondition += statement {
|
val condPos = t.pos
|
||||||
val r = container.execute(this).contains(this, whenValue)
|
currentCondition += object : Statement() {
|
||||||
ObjBool(if (isIn) r else !r)
|
override val pos: Pos = condPos
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val r = container.execute(scope).contains(scope, whenValue)
|
||||||
|
return ObjBool(if (isIn) r else !r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1705,9 +1840,13 @@ class Compiler(
|
|||||||
// we need a copy in the closure:
|
// we need a copy in the closure:
|
||||||
val isIn = t.type == Token.Type.IS
|
val isIn = t.type == Token.Type.IS
|
||||||
val caseType = parseExpression() ?: throw ScriptError(cc.currentPos(), "type expected")
|
val caseType = parseExpression() ?: throw ScriptError(cc.currentPos(), "type expected")
|
||||||
currentCondition += statement {
|
val condPos = t.pos
|
||||||
val r = whenValue.isInstanceOf(caseType.execute(this))
|
currentCondition += object : Statement() {
|
||||||
ObjBool(if (isIn) r else !r)
|
override val pos: Pos = condPos
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val r = whenValue.isInstanceOf(caseType.execute(scope))
|
||||||
|
return ObjBool(if (isIn) r else !r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1737,8 +1876,12 @@ class Compiler(
|
|||||||
cc.previous()
|
cc.previous()
|
||||||
val x = parseExpression()
|
val x = parseExpression()
|
||||||
?: throw ScriptError(cc.currentPos(), "when case condition expected")
|
?: throw ScriptError(cc.currentPos(), "when case condition expected")
|
||||||
currentCondition += statement {
|
val condPos = t.pos
|
||||||
ObjBool(x.execute(this).compareTo(this, whenValue) == 0)
|
currentCondition += object : Statement() {
|
||||||
|
override val pos: Pos = condPos
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
return ObjBool(x.execute(scope).compareTo(scope, whenValue) == 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1750,19 +1893,24 @@ class Compiler(
|
|||||||
for (c in currentCondition) cases += WhenCase(c, block)
|
for (c in currentCondition) cases += WhenCase(c, block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
statement {
|
val whenPos = t.pos
|
||||||
|
object : Statement() {
|
||||||
|
override val pos: Pos = whenPos
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
var result: Obj = ObjVoid
|
var result: Obj = ObjVoid
|
||||||
// in / is and like uses whenValue from closure:
|
// in / is and like uses whenValue from closure:
|
||||||
whenValue = value.execute(this)
|
whenValue = value.execute(scope)
|
||||||
var found = false
|
var found = false
|
||||||
for (c in cases)
|
for (c in cases) {
|
||||||
if (c.condition.execute(this).toBool()) {
|
if (c.condition.execute(scope).toBool()) {
|
||||||
result = c.block.execute(this)
|
result = c.block.execute(scope)
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if (!found && elseCase != null) result = elseCase.execute(this)
|
}
|
||||||
result
|
if (!found && elseCase != null) result = elseCase.execute(scope)
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// when { cond -> ... }
|
// when { cond -> ... }
|
||||||
@ -1774,10 +1922,12 @@ class Compiler(
|
|||||||
val throwStatement = parseStatement() ?: throw ScriptError(cc.currentPos(), "throw object expected")
|
val throwStatement = parseStatement() ?: throw ScriptError(cc.currentPos(), "throw object expected")
|
||||||
// Important: bind the created statement to the position of the `throw` keyword so that
|
// Important: bind the created statement to the position of the `throw` keyword so that
|
||||||
// any raised error reports the correct source location.
|
// any raised error reports the correct source location.
|
||||||
return statement(start) { sc ->
|
return object : Statement() {
|
||||||
var errorObject = throwStatement.execute(sc)
|
override val pos: Pos = start
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
var errorObject = throwStatement.execute(scope)
|
||||||
// Rebind error scope to the throw-site position so ScriptError.pos is accurate
|
// Rebind error scope to the throw-site position so ScriptError.pos is accurate
|
||||||
val throwScope = sc.createChildScope(pos = start)
|
val throwScope = scope.createChildScope(pos = start)
|
||||||
if (errorObject is ObjString) {
|
if (errorObject is ObjString) {
|
||||||
errorObject = ObjException(throwScope, errorObject.value).apply { getStackTrace() }
|
errorObject = ObjException(throwScope, errorObject.value).apply { getStackTrace() }
|
||||||
}
|
}
|
||||||
@ -1794,9 +1944,11 @@ class Compiler(
|
|||||||
).apply { getStackTrace() }
|
).apply { getStackTrace() }
|
||||||
throwScope.raiseError(errorObject)
|
throwScope.raiseError(errorObject)
|
||||||
} else {
|
} else {
|
||||||
val msg = errorObject.invokeInstanceMethod(sc, "message").toString(sc).value
|
val msg = errorObject.invokeInstanceMethod(scope, "message").toString(scope).value
|
||||||
throwScope.raiseError(errorObject, start, msg)
|
throwScope.raiseError(errorObject, start, msg)
|
||||||
}
|
}
|
||||||
|
return ObjVoid
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1868,11 +2020,14 @@ class Compiler(
|
|||||||
if (catches.isEmpty() && finallyClause == null)
|
if (catches.isEmpty() && finallyClause == null)
|
||||||
throw ScriptError(cc.currentPos(), "try block must have either catch or finally clause or both")
|
throw ScriptError(cc.currentPos(), "try block must have either catch or finally clause or both")
|
||||||
|
|
||||||
return statement {
|
val stmtPos = body.pos
|
||||||
|
return object : Statement() {
|
||||||
|
override val pos: Pos = stmtPos
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
var result: Obj = ObjVoid
|
var result: Obj = ObjVoid
|
||||||
try {
|
try {
|
||||||
// body is a parsed block, it already has separate context
|
// body is a parsed block, it already has separate context
|
||||||
result = body.execute(this)
|
result = body.execute(scope)
|
||||||
} catch (e: ReturnException) {
|
} catch (e: ReturnException) {
|
||||||
throw e
|
throw e
|
||||||
} catch (e: LoopBreakContinueException) {
|
} catch (e: LoopBreakContinueException) {
|
||||||
@ -1881,22 +2036,22 @@ class Compiler(
|
|||||||
// convert to appropriate exception
|
// convert to appropriate exception
|
||||||
val caughtObj = when (e) {
|
val caughtObj = when (e) {
|
||||||
is ExecutionError -> e.errorObject
|
is ExecutionError -> e.errorObject
|
||||||
else -> ObjUnknownException(this, e.message ?: e.toString())
|
else -> ObjUnknownException(scope, e.message ?: e.toString())
|
||||||
}
|
}
|
||||||
// let's see if we should catch it:
|
// let's see if we should catch it:
|
||||||
var isCaught = false
|
var isCaught = false
|
||||||
for (cdata in catches) {
|
for (cdata in catches) {
|
||||||
var match: Obj? = null
|
var match: Obj? = null
|
||||||
for (exceptionClassName in cdata.classNames) {
|
for (exceptionClassName in cdata.classNames) {
|
||||||
val exObj = this[exceptionClassName]?.value as? ObjClass
|
val exObj = scope[exceptionClassName]?.value as? ObjClass
|
||||||
?: raiseSymbolNotFound("error class does not exist or is not a class: $exceptionClassName")
|
?: scope.raiseSymbolNotFound("error class does not exist or is not a class: $exceptionClassName")
|
||||||
if (caughtObj.isInstanceOf(exObj)) {
|
if (caughtObj.isInstanceOf(exObj)) {
|
||||||
match = caughtObj
|
match = caughtObj
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
val catchContext = this.createChildScope(pos = cdata.catchVar.pos)
|
val catchContext = scope.createChildScope(pos = cdata.catchVar.pos)
|
||||||
catchContext.addItem(cdata.catchVar.value, false, caughtObj)
|
catchContext.addItem(cdata.catchVar.value, false, caughtObj)
|
||||||
result = cdata.block.execute(catchContext)
|
result = cdata.block.execute(catchContext)
|
||||||
isCaught = true
|
isCaught = true
|
||||||
@ -1908,9 +2063,10 @@ class Compiler(
|
|||||||
throw e
|
throw e
|
||||||
} finally {
|
} finally {
|
||||||
// finally clause does not alter result!
|
// finally clause does not alter result!
|
||||||
finallyClause?.execute(this)
|
finallyClause?.execute(scope)
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1964,9 +2120,13 @@ class Compiler(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return statement {
|
val stmtPos = startPos
|
||||||
ObjEnumClass.createSimpleEnum(nameToken.value, names).also {
|
return object : Statement() {
|
||||||
addItem(nameToken.value, false, it, recordType = ObjRecord.Type.Enum)
|
override val pos: Pos = stmtPos
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val enumClass = ObjEnumClass.createSimpleEnum(nameToken.value, names)
|
||||||
|
scope.addItem(nameToken.value, false, enumClass, recordType = ObjRecord.Type.Enum)
|
||||||
|
return enumClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2052,9 +2212,11 @@ class Compiler(
|
|||||||
|
|
||||||
val initScope = popInitScope()
|
val initScope = popInitScope()
|
||||||
|
|
||||||
return statement(startPos) { context ->
|
return object : Statement() {
|
||||||
|
override val pos: Pos = startPos
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
val parentClasses = baseSpecs.map { baseSpec ->
|
val parentClasses = baseSpecs.map { baseSpec ->
|
||||||
val rec = context[baseSpec.name] ?: throw ScriptError(startPos, "unknown base class: ${baseSpec.name}")
|
val rec = scope[baseSpec.name] ?: throw ScriptError(startPos, "unknown base class: ${baseSpec.name}")
|
||||||
(rec.value as? ObjClass) ?: throw ScriptError(startPos, "${baseSpec.name} is not a class")
|
(rec.value as? ObjClass) ?: throw ScriptError(startPos, "${baseSpec.name} is not a class")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2067,7 +2229,7 @@ class Compiler(
|
|||||||
if (argsList != null) newClass.directParentArgs[parentClasses[i]] = argsList
|
if (argsList != null) newClass.directParentArgs[parentClasses[i]] = argsList
|
||||||
}
|
}
|
||||||
|
|
||||||
val classScope = context.createChildScope(newThisObj = newClass)
|
val classScope = scope.createChildScope(newThisObj = newClass)
|
||||||
classScope.currentClassCtx = newClass
|
classScope.currentClassCtx = newClass
|
||||||
newClass.classScope = classScope
|
newClass.classScope = classScope
|
||||||
classScope.addConst("object", newClass)
|
classScope.addConst("object", newClass)
|
||||||
@ -2075,10 +2237,11 @@ class Compiler(
|
|||||||
bodyInit?.execute(classScope)
|
bodyInit?.execute(classScope)
|
||||||
|
|
||||||
// Create instance (singleton)
|
// Create instance (singleton)
|
||||||
val instance = newClass.callOn(context.createChildScope(Arguments.EMPTY))
|
val instance = newClass.callOn(scope.createChildScope(Arguments.EMPTY))
|
||||||
if (nameToken != null)
|
if (nameToken != null)
|
||||||
context.addItem(className, false, instance)
|
scope.addItem(className, false, instance)
|
||||||
instance
|
return instance
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2198,24 +2361,29 @@ class Compiler(
|
|||||||
// create instance constructor
|
// create instance constructor
|
||||||
// create custom objClass with all fields and instance constructor
|
// create custom objClass with all fields and instance constructor
|
||||||
|
|
||||||
val constructorCode = statement {
|
val constructorCode = object : Statement() {
|
||||||
|
override val pos: Pos = startPos
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
// constructor code is registered with class instance and is called over
|
// constructor code is registered with class instance and is called over
|
||||||
// new `thisObj` already set by class to ObjInstance.instanceContext
|
// new `thisObj` already set by class to ObjInstance.instanceContext
|
||||||
val instance = thisObj as ObjInstance
|
val instance = scope.thisObj as ObjInstance
|
||||||
// Constructor parameters have been assigned to instance scope by ObjClass.callOn before
|
// Constructor parameters have been assigned to instance scope by ObjClass.callOn before
|
||||||
// invoking parent/child constructors.
|
// invoking parent/child constructors.
|
||||||
// IMPORTANT: do not execute class body here; class body was executed once in the class scope
|
// IMPORTANT: do not execute class body here; class body was executed once in the class scope
|
||||||
// to register methods and prepare initializers. Instance constructor should be empty unless
|
// to register methods and prepare initializers. Instance constructor should be empty unless
|
||||||
// we later add explicit constructor body syntax.
|
// we later add explicit constructor body syntax.
|
||||||
instance
|
return instance
|
||||||
}
|
}
|
||||||
statement {
|
}
|
||||||
|
object : Statement() {
|
||||||
|
override val pos: Pos = startPos
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
// the main statement should create custom ObjClass instance with field
|
// the main statement should create custom ObjClass instance with field
|
||||||
// accessors, constructor registration, etc.
|
// accessors, constructor registration, etc.
|
||||||
// Resolve parent classes by name at execution time
|
// Resolve parent classes by name at execution time
|
||||||
val parentClasses = baseSpecs.map { baseSpec ->
|
val parentClasses = baseSpecs.map { baseSpec ->
|
||||||
val rec =
|
val rec =
|
||||||
this[baseSpec.name] ?: throw ScriptError(nameToken.pos, "unknown base class: ${baseSpec.name}")
|
scope[baseSpec.name] ?: throw ScriptError(nameToken.pos, "unknown base class: ${baseSpec.name}")
|
||||||
(rec.value as? ObjClass) ?: throw ScriptError(nameToken.pos, "${baseSpec.name} is not a class")
|
(rec.value as? ObjClass) ?: throw ScriptError(nameToken.pos, "${baseSpec.name} is not a class")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2247,9 +2415,9 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addItem(className, false, newClass)
|
scope.addItem(className, false, newClass)
|
||||||
// Prepare class scope for class-scope members (static) and future registrations
|
// Prepare class scope for class-scope members (static) and future registrations
|
||||||
val classScope = createChildScope(newThisObj = newClass)
|
val classScope = scope.createChildScope(newThisObj = newClass)
|
||||||
// Set lexical class context for visibility tagging inside class body
|
// Set lexical class context for visibility tagging inside class body
|
||||||
classScope.currentClassCtx = newClass
|
classScope.currentClassCtx = newClass
|
||||||
newClass.classScope = classScope
|
newClass.classScope = classScope
|
||||||
@ -2261,7 +2429,8 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
newClass.checkAbstractSatisfaction(nameToken.pos)
|
newClass.checkAbstractSatisfaction(nameToken.pos)
|
||||||
// Debug summary: list registered instance methods and class-scope functions for this class
|
// Debug summary: list registered instance methods and class-scope functions for this class
|
||||||
newClass
|
return newClass
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2319,8 +2488,10 @@ class Compiler(
|
|||||||
Triple(loopParsed.first, loopParsed.second, elseStmt)
|
Triple(loopParsed.first, loopParsed.second, elseStmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
return statement(body.pos) { cxt ->
|
return object : Statement() {
|
||||||
val forContext = cxt.createChildScope(start)
|
override val pos: Pos = body.pos
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val forContext = scope.createChildScope(start)
|
||||||
|
|
||||||
// loop var: StoredObject
|
// loop var: StoredObject
|
||||||
val loopSO = forContext.addItem(tVar.value, true, ObjNull)
|
val loopSO = forContext.addItem(tVar.value, true, ObjNull)
|
||||||
@ -2329,7 +2500,7 @@ class Compiler(
|
|||||||
val sourceObj = source.execute(forContext)
|
val sourceObj = source.execute(forContext)
|
||||||
|
|
||||||
if (sourceObj is ObjRange && sourceObj.isIntRange && PerfFlags.PRIMITIVE_FASTOPS) {
|
if (sourceObj is ObjRange && sourceObj.isIntRange && PerfFlags.PRIMITIVE_FASTOPS) {
|
||||||
loopIntRange(
|
return loopIntRange(
|
||||||
forContext,
|
forContext,
|
||||||
sourceObj.start!!.toLong(),
|
sourceObj.start!!.toLong(),
|
||||||
if (sourceObj.isEndInclusive)
|
if (sourceObj.isEndInclusive)
|
||||||
@ -2343,7 +2514,7 @@ class Compiler(
|
|||||||
canBreak
|
canBreak
|
||||||
)
|
)
|
||||||
} else if (sourceObj.isInstanceOf(ObjIterable)) {
|
} else if (sourceObj.isInstanceOf(ObjIterable)) {
|
||||||
loopIterable(forContext, sourceObj, loopSO, body, elseStatement, label, canBreak)
|
return loopIterable(forContext, sourceObj, loopSO, body, elseStatement, label, canBreak)
|
||||||
} else {
|
} else {
|
||||||
val size = runCatching { sourceObj.readField(forContext, "size").value.toInt() }
|
val size = runCatching { sourceObj.readField(forContext, "size").value.toInt() }
|
||||||
.getOrElse {
|
.getOrElse {
|
||||||
@ -2362,7 +2533,7 @@ class Compiler(
|
|||||||
.getOrElse {
|
.getOrElse {
|
||||||
throw ScriptError(
|
throw ScriptError(
|
||||||
tOp.pos,
|
tOp.pos,
|
||||||
"object is not enumerable: no index access for ${sourceObj.inspect(cxt)}",
|
"object is not enumerable: no index access for ${sourceObj.inspect(scope)}",
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -2387,9 +2558,10 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!breakCaught && elseStatement != null) {
|
if (!breakCaught && elseStatement != null) {
|
||||||
result = elseStatement.execute(cxt)
|
result = elseStatement.execute(scope)
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -2484,11 +2656,13 @@ class Compiler(
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
return statement(body.pos) {
|
return object : Statement() {
|
||||||
|
override val pos: Pos = body.pos
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
var wasBroken = false
|
var wasBroken = false
|
||||||
var result: Obj = ObjVoid
|
var result: Obj = ObjVoid
|
||||||
while (true) {
|
while (true) {
|
||||||
val doScope = it.createChildScope().apply { skipScopeCreation = true }
|
val doScope = scope.createChildScope().apply { skipScopeCreation = true }
|
||||||
try {
|
try {
|
||||||
result = body.execute(doScope)
|
result = body.execute(doScope)
|
||||||
} catch (e: LoopBreakContinueException) {
|
} catch (e: LoopBreakContinueException) {
|
||||||
@ -2507,8 +2681,9 @@ class Compiler(
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!wasBroken) elseStatement?.let { s -> result = s.execute(it) }
|
if (!wasBroken) elseStatement?.let { s -> result = s.execute(scope) }
|
||||||
result
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2532,11 +2707,13 @@ class Compiler(
|
|||||||
cc.previous()
|
cc.previous()
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
return statement(body.pos) {
|
return object : Statement() {
|
||||||
|
override val pos: Pos = body.pos
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
var result: Obj = ObjVoid
|
var result: Obj = ObjVoid
|
||||||
var wasBroken = false
|
var wasBroken = false
|
||||||
while (condition.execute(it).toBool()) {
|
while (condition.execute(scope).toBool()) {
|
||||||
val loopScope = it.createChildScope()
|
val loopScope = scope.createChildScope()
|
||||||
if (canBreak) {
|
if (canBreak) {
|
||||||
try {
|
try {
|
||||||
result = body.execute(loopScope)
|
result = body.execute(loopScope)
|
||||||
@ -2554,8 +2731,9 @@ class Compiler(
|
|||||||
} else
|
} else
|
||||||
result = body.execute(loopScope)
|
result = body.execute(loopScope)
|
||||||
}
|
}
|
||||||
if (!wasBroken) elseStatement?.let { s -> result = s.execute(it) }
|
if (!wasBroken) elseStatement?.let { s -> result = s.execute(scope) }
|
||||||
result
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2590,8 +2768,10 @@ class Compiler(
|
|||||||
|
|
||||||
cc.addBreak()
|
cc.addBreak()
|
||||||
|
|
||||||
return statement(start) {
|
return object : Statement() {
|
||||||
val returnValue = resultExpr?.execute(it)// ?: ObjVoid
|
override val pos: Pos = start
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val returnValue = resultExpr?.execute(scope)// ?: ObjVoid
|
||||||
throw LoopBreakContinueException(
|
throw LoopBreakContinueException(
|
||||||
doContinue = false,
|
doContinue = false,
|
||||||
label = label,
|
label = label,
|
||||||
@ -2599,6 +2779,7 @@ class Compiler(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun parseContinueStatement(start: Pos): Statement {
|
private fun parseContinueStatement(start: Pos): Statement {
|
||||||
val t = cc.next()
|
val t = cc.next()
|
||||||
@ -2614,13 +2795,16 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
cc.addBreak()
|
cc.addBreak()
|
||||||
|
|
||||||
return statement(start) {
|
return object : Statement() {
|
||||||
|
override val pos: Pos = start
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
throw LoopBreakContinueException(
|
throw LoopBreakContinueException(
|
||||||
doContinue = true,
|
doContinue = true,
|
||||||
label = label,
|
label = label,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun parseReturnStatement(start: Pos): Statement {
|
private suspend fun parseReturnStatement(start: Pos): Statement {
|
||||||
var t = cc.next()
|
var t = cc.next()
|
||||||
@ -2648,11 +2832,14 @@ class Compiler(
|
|||||||
parseExpression()
|
parseExpression()
|
||||||
} else null
|
} else null
|
||||||
|
|
||||||
return statement(start) {
|
return object : Statement() {
|
||||||
val returnValue = resultExpr?.execute(it) ?: ObjVoid
|
override val pos: Pos = start
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val returnValue = resultExpr?.execute(scope) ?: ObjVoid
|
||||||
throw ReturnException(returnValue, label)
|
throw ReturnException(returnValue, label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun ensureRparen(): Pos {
|
private fun ensureRparen(): Pos {
|
||||||
val t = cc.next()
|
val t = cc.next()
|
||||||
@ -2686,19 +2873,24 @@ class Compiler(
|
|||||||
return if (t2.type == Token.Type.ID && t2.value == "else") {
|
return if (t2.type == Token.Type.ID && t2.value == "else") {
|
||||||
val elseBody =
|
val elseBody =
|
||||||
parseStatement() ?: throw ScriptError(pos, "Bad else statement: expected statement")
|
parseStatement() ?: throw ScriptError(pos, "Bad else statement: expected statement")
|
||||||
return statement(start) {
|
return object : Statement() {
|
||||||
if (condition.execute(it).toBool())
|
override val pos: Pos = start
|
||||||
ifBody.execute(it)
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
return if (condition.execute(scope).toBool())
|
||||||
|
ifBody.execute(scope)
|
||||||
else
|
else
|
||||||
elseBody.execute(it)
|
elseBody.execute(scope)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cc.previous()
|
cc.previous()
|
||||||
statement(start) {
|
object : Statement() {
|
||||||
if (condition.execute(it).toBool())
|
override val pos: Pos = start
|
||||||
ifBody.execute(it)
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
else
|
if (condition.execute(scope).toBool())
|
||||||
ObjVoid
|
return ifBody.execute(scope)
|
||||||
|
return ObjVoid
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2803,13 +2995,22 @@ class Compiler(
|
|||||||
cc.labels.add(name)
|
cc.labels.add(name)
|
||||||
outerLabel?.let { cc.labels.add(it) }
|
outerLabel?.let { cc.labels.add(it) }
|
||||||
|
|
||||||
val paramNames: Set<String> = argsDeclaration.params.map { it.name }.toSet()
|
val paramNamesList = argsDeclaration.params.map { it.name }
|
||||||
|
val paramNames: Set<String> = paramNamesList.toSet()
|
||||||
|
val paramSlotPlan = buildParamSlotPlan(paramNamesList)
|
||||||
|
|
||||||
// Parse function body while tracking declared locals to compute precise capacity hints
|
// Parse function body while tracking declared locals to compute precise capacity hints
|
||||||
currentLocalDeclCount
|
currentLocalDeclCount
|
||||||
localDeclCountStack.add(0)
|
localDeclCountStack.add(0)
|
||||||
val fnStatements = if (actualExtern)
|
slotPlanStack.add(paramSlotPlan)
|
||||||
statement { raiseError("extern function not provided: $name") }
|
val fnStatements = try {
|
||||||
|
if (actualExtern)
|
||||||
|
object : Statement() {
|
||||||
|
override val pos: Pos = start
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
scope.raiseError("extern function not provided: $name")
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (isAbstract || isDelegated) {
|
else if (isAbstract || isDelegated) {
|
||||||
null
|
null
|
||||||
} else
|
} else
|
||||||
@ -2821,19 +3022,26 @@ class Compiler(
|
|||||||
throw ScriptError(cc.currentPos(), "return is not allowed in shorthand function")
|
throw ScriptError(cc.currentPos(), "return is not allowed in shorthand function")
|
||||||
val expr = parseExpression() ?: throw ScriptError(cc.currentPos(), "Expected function body expression")
|
val expr = parseExpression() ?: throw ScriptError(cc.currentPos(), "Expected function body expression")
|
||||||
// Shorthand function returns the expression value
|
// Shorthand function returns the expression value
|
||||||
statement(expr.pos) { scope ->
|
object : Statement() {
|
||||||
expr.execute(scope)
|
override val pos: Pos = expr.pos
|
||||||
|
override suspend fun execute(scope: Scope): Obj = expr.execute(scope)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
parseBlock()
|
parseBlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
slotPlanStack.removeLast()
|
||||||
|
}
|
||||||
// Capture and pop the local declarations count for this function
|
// Capture and pop the local declarations count for this function
|
||||||
val fnLocalDecls = localDeclCountStack.removeLastOrNull() ?: 0
|
val fnLocalDecls = localDeclCountStack.removeLastOrNull() ?: 0
|
||||||
|
|
||||||
var closure: Scope? = null
|
var closure: Scope? = null
|
||||||
|
|
||||||
val fnBody = statement(t.pos) { callerContext ->
|
val paramSlotPlanSnapshot = if (paramSlotPlan.slots.isEmpty()) emptyMap() else paramSlotPlan.slots.toMap()
|
||||||
|
val fnBody = object : Statement() {
|
||||||
|
override val pos: Pos = t.pos
|
||||||
|
override suspend fun execute(callerContext: Scope): Obj {
|
||||||
callerContext.pos = start
|
callerContext.pos = start
|
||||||
|
|
||||||
// restore closure where the function was defined, and making a copy of it
|
// restore closure where the function was defined, and making a copy of it
|
||||||
@ -2845,23 +3053,27 @@ class Compiler(
|
|||||||
// Capacity hint: parameters + declared locals + small overhead
|
// Capacity hint: parameters + declared locals + small overhead
|
||||||
val capacityHint = paramNames.size + fnLocalDecls + 4
|
val capacityHint = paramNames.size + fnLocalDecls + 4
|
||||||
context.hintLocalCapacity(capacityHint)
|
context.hintLocalCapacity(capacityHint)
|
||||||
|
if (paramSlotPlanSnapshot.isNotEmpty()) context.applySlotPlan(paramSlotPlanSnapshot)
|
||||||
|
|
||||||
// load params from caller context
|
// load params from caller context
|
||||||
argsDeclaration.assignToContext(context, callerContext.args, defaultAccessType = AccessType.Val)
|
argsDeclaration.assignToContext(context, callerContext.args, defaultAccessType = AccessType.Val)
|
||||||
if (extTypeName != null) {
|
if (extTypeName != null) {
|
||||||
context.thisObj = callerContext.thisObj
|
context.thisObj = callerContext.thisObj
|
||||||
}
|
}
|
||||||
try {
|
return try {
|
||||||
fnStatements?.execute(context) ?: ObjVoid
|
fnStatements?.execute(context) ?: ObjVoid
|
||||||
} catch (e: ReturnException) {
|
} catch (e: ReturnException) {
|
||||||
if (e.label == null || e.label == name || e.label == outerLabel) e.result
|
if (e.label == null || e.label == name || e.label == outerLabel) e.result
|
||||||
else throw e
|
else throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
cc.labels.remove(name)
|
cc.labels.remove(name)
|
||||||
outerLabel?.let { cc.labels.remove(it) }
|
outerLabel?.let { cc.labels.remove(it) }
|
||||||
// parentContext
|
// parentContext
|
||||||
val fnCreateStatement = statement(start) { context ->
|
val fnCreateStatement = object : Statement() {
|
||||||
|
override val pos: Pos = start
|
||||||
|
override suspend fun execute(context: Scope): Obj {
|
||||||
if (isDelegated) {
|
if (isDelegated) {
|
||||||
val accessType = context.resolveQualifiedIdentifier("DelegateAccess.Callable")
|
val accessType = context.resolveQualifiedIdentifier("DelegateAccess.Callable")
|
||||||
val initValue = delegateExpression!!.execute(context)
|
val initValue = delegateExpression!!.execute(context)
|
||||||
@ -2877,7 +3089,7 @@ class Compiler(
|
|||||||
context.addExtension(type, name, ObjRecord(ObjUnset, isMutable = false, visibility = visibility, declaringClass = null, type = ObjRecord.Type.Delegated).apply {
|
context.addExtension(type, name, ObjRecord(ObjUnset, isMutable = false, visibility = visibility, declaringClass = null, type = ObjRecord.Type.Delegated).apply {
|
||||||
delegate = finalDelegate
|
delegate = finalDelegate
|
||||||
})
|
})
|
||||||
return@statement ObjVoid
|
return ObjVoid
|
||||||
}
|
}
|
||||||
|
|
||||||
val th = context.thisObj
|
val th = context.thisObj
|
||||||
@ -2892,7 +3104,9 @@ class Compiler(
|
|||||||
val cls: ObjClass = th
|
val cls: ObjClass = th
|
||||||
val storageName = "${cls.className}::$name"
|
val storageName = "${cls.className}::$name"
|
||||||
cls.createField(name, ObjUnset, false, visibility, null, start, declaringClass = cls, isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride, isTransient = isTransient, 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 ->
|
cls.instanceInitializers += object : Statement() {
|
||||||
|
override val pos: Pos = start
|
||||||
|
override suspend fun execute(scp: Scope): Obj {
|
||||||
val accessType2 = scp.resolveQualifiedIdentifier("DelegateAccess.Callable")
|
val accessType2 = scp.resolveQualifiedIdentifier("DelegateAccess.Callable")
|
||||||
val initValue2 = delegateExpression.execute(scp)
|
val initValue2 = delegateExpression.execute(scp)
|
||||||
val finalDelegate2 = try {
|
val finalDelegate2 = try {
|
||||||
@ -2903,14 +3117,15 @@ class Compiler(
|
|||||||
scp.addItem(storageName, false, ObjUnset, visibility, null, recordType = ObjRecord.Type.Delegated, isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride, isTransient = isTransient).apply {
|
scp.addItem(storageName, false, ObjUnset, visibility, null, recordType = ObjRecord.Type.Delegated, isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride, isTransient = isTransient).apply {
|
||||||
delegate = finalDelegate2
|
delegate = finalDelegate2
|
||||||
}
|
}
|
||||||
ObjVoid
|
return ObjVoid
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
context.addItem(name, false, ObjUnset, visibility, recordType = ObjRecord.Type.Delegated, isTransient = isTransient).apply {
|
context.addItem(name, false, ObjUnset, visibility, recordType = ObjRecord.Type.Delegated, isTransient = isTransient).apply {
|
||||||
delegate = finalDelegate
|
delegate = finalDelegate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return@statement ObjVoid
|
return ObjVoid
|
||||||
}
|
}
|
||||||
|
|
||||||
// we added fn in the context. now we must save closure
|
// we added fn in the context. now we must save closure
|
||||||
@ -2925,13 +3140,17 @@ class Compiler(
|
|||||||
// class extension method
|
// class extension method
|
||||||
val type = context[typeName]?.value ?: context.raiseSymbolNotFound("class $typeName not found")
|
val type = context[typeName]?.value ?: context.raiseSymbolNotFound("class $typeName not found")
|
||||||
if (type !is ObjClass) context.raiseClassCastError("$typeName is not the class instance")
|
if (type !is ObjClass) context.raiseClassCastError("$typeName is not the class instance")
|
||||||
val stmt = statement {
|
val stmt = object : Statement() {
|
||||||
|
override val pos: Pos = start
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
// ObjInstance has a fixed instance scope, so we need to build a closure
|
// ObjInstance has a fixed instance scope, so we need to build a closure
|
||||||
(thisObj as? ObjInstance)?.let { i ->
|
val result = (scope.thisObj as? ObjInstance)?.let { i ->
|
||||||
annotatedFnBody.execute(ClosureScope(this, i.instanceScope))
|
annotatedFnBody.execute(ClosureScope(scope, i.instanceScope))
|
||||||
}
|
}
|
||||||
// other classes can create one-time scope for this rare case:
|
// other classes can create one-time scope for this rare case:
|
||||||
?: annotatedFnBody.execute(thisObj.autoInstanceScope(this))
|
?: annotatedFnBody.execute(scope.thisObj.autoInstanceScope(scope))
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
context.addExtension(type, name, ObjRecord(stmt, isMutable = false, visibility = visibility, declaringClass = null))
|
context.addExtension(type, name, ObjRecord(stmt, isMutable = false, visibility = visibility, declaringClass = null))
|
||||||
}
|
}
|
||||||
@ -2971,7 +3190,8 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
// as the function can be called from anywhere, we have
|
// as the function can be called from anywhere, we have
|
||||||
// saved the proper context in the closure
|
// saved the proper context in the closure
|
||||||
annotatedFnBody
|
return annotatedFnBody
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (isStatic) {
|
if (isStatic) {
|
||||||
currentInitScope += fnCreateStatement
|
currentInitScope += fnCreateStatement
|
||||||
@ -3013,11 +3233,24 @@ class Compiler(
|
|||||||
if (t.type != Token.Type.LBRACE)
|
if (t.type != Token.Type.LBRACE)
|
||||||
throw ScriptError(t.pos, "Expected block body start: {")
|
throw ScriptError(t.pos, "Expected block body start: {")
|
||||||
}
|
}
|
||||||
val block = parseScript()
|
val blockSlotPlan = SlotPlan(mutableMapOf(), 0)
|
||||||
return statement(startPos) {
|
slotPlanStack.add(blockSlotPlan)
|
||||||
|
val block = try {
|
||||||
|
parseScript()
|
||||||
|
} finally {
|
||||||
|
slotPlanStack.removeLast()
|
||||||
|
}
|
||||||
|
val planSnapshot = if (blockSlotPlan.slots.isEmpty()) emptyMap() else blockSlotPlan.slots.toMap()
|
||||||
|
val stmt = object : Statement() {
|
||||||
|
override val pos: Pos = startPos
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
// block run on inner context:
|
// block run on inner context:
|
||||||
block.execute(if (it.skipScopeCreation) it else it.createChildScope(startPos))
|
val target = if (scope.skipScopeCreation) scope else scope.createChildScope(startPos)
|
||||||
}.also {
|
if (planSnapshot.isNotEmpty()) target.applySlotPlan(planSnapshot)
|
||||||
|
return block.execute(target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stmt.also {
|
||||||
val t1 = cc.next()
|
val t1 = cc.next()
|
||||||
if (t1.type != Token.Type.RBRACE)
|
if (t1.type != Token.Type.RBRACE)
|
||||||
throw ScriptError(t1.pos, "unbalanced braces: expected block body end: }")
|
throw ScriptError(t1.pos, "unbalanced braces: expected block body end: }")
|
||||||
@ -3079,7 +3312,9 @@ class Compiler(
|
|||||||
val names = mutableListOf<String>()
|
val names = mutableListOf<String>()
|
||||||
pattern.forEachVariable { names.add(it) }
|
pattern.forEachVariable { names.add(it) }
|
||||||
|
|
||||||
return statement(start) { context ->
|
return object : Statement() {
|
||||||
|
override val pos: Pos = start
|
||||||
|
override suspend fun execute(context: Scope): Obj {
|
||||||
val value = initialExpression.execute(context)
|
val value = initialExpression.execute(context)
|
||||||
for (name in names) {
|
for (name in names) {
|
||||||
context.addItem(name, true, ObjVoid, visibility, isTransient = isTransient)
|
context.addItem(name, true, ObjVoid, visibility, isTransient = isTransient)
|
||||||
@ -3094,7 +3329,8 @@ class Compiler(
|
|||||||
context.updateSlotFor(name, immutableRec)
|
context.updateSlotFor(name, immutableRec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ObjVoid
|
return ObjVoid
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -392,6 +392,24 @@ open class Scope(
|
|||||||
nameToSlot[name]?.let { slots[it] = record }
|
nameToSlot[name]?.let { slots[it] = record }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a precomputed slot plan (name -> slot index) for this scope.
|
||||||
|
* This enables direct slot references to bypass name-based lookup.
|
||||||
|
*/
|
||||||
|
fun applySlotPlan(plan: Map<String, Int>) {
|
||||||
|
if (plan.isEmpty()) return
|
||||||
|
val maxIndex = plan.values.maxOrNull() ?: return
|
||||||
|
if (slots.size <= maxIndex) {
|
||||||
|
val targetSize = maxIndex + 1
|
||||||
|
while (slots.size < targetSize) {
|
||||||
|
slots.add(ObjRecord(ObjUnset, isMutable = true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ((name, idx) in plan) {
|
||||||
|
nameToSlot[name] = idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear all references and maps to prevent memory leaks when pooled.
|
* Clear all references and maps to prevent memory leaks when pooled.
|
||||||
*/
|
*/
|
||||||
@ -503,6 +521,7 @@ open class Scope(
|
|||||||
if (this is ClosureScope) {
|
if (this is ClosureScope) {
|
||||||
callScope.localBindings[name] = it
|
callScope.localBindings[name] = it
|
||||||
}
|
}
|
||||||
|
bumpClassLayoutIfNeeded(name, value, recordType)
|
||||||
it
|
it
|
||||||
} ?: addItem(name, true, value, visibility, writeVisibility, recordType, isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride)
|
} ?: addItem(name, true, value, visibility, writeVisibility, recordType, isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride)
|
||||||
|
|
||||||
@ -529,6 +548,24 @@ open class Scope(
|
|||||||
isTransient = isTransient
|
isTransient = isTransient
|
||||||
)
|
)
|
||||||
objects[name] = rec
|
objects[name] = rec
|
||||||
|
bumpClassLayoutIfNeeded(name, value, recordType)
|
||||||
|
if (recordType == ObjRecord.Type.Field || recordType == ObjRecord.Type.ConstructorField) {
|
||||||
|
val inst = thisObj as? net.sergeych.lyng.obj.ObjInstance
|
||||||
|
if (inst != null) {
|
||||||
|
val slot = inst.objClass.fieldSlotForKey(name)
|
||||||
|
if (slot != null) inst.setFieldSlotRecord(slot.slot, rec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value is Statement ||
|
||||||
|
recordType == ObjRecord.Type.Fun ||
|
||||||
|
recordType == ObjRecord.Type.Delegated ||
|
||||||
|
recordType == ObjRecord.Type.Property) {
|
||||||
|
val inst = thisObj as? net.sergeych.lyng.obj.ObjInstance
|
||||||
|
if (inst != null) {
|
||||||
|
val slot = inst.objClass.methodSlotForKey(name)
|
||||||
|
if (slot != null) inst.setMethodSlotRecord(slot.slot, rec)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Index this binding within the current frame to help resolve locals across suspension
|
// Index this binding within the current frame to help resolve locals across suspension
|
||||||
localBindings[name] = rec
|
localBindings[name] = rec
|
||||||
// If we are a ClosureScope, mirror binding into the caller frame to keep it discoverable
|
// If we are a ClosureScope, mirror binding into the caller frame to keep it discoverable
|
||||||
@ -558,6 +595,14 @@ open class Scope(
|
|||||||
return rec
|
return rec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun bumpClassLayoutIfNeeded(name: String, value: Obj, recordType: ObjRecord.Type) {
|
||||||
|
val cls = thisObj as? net.sergeych.lyng.obj.ObjClass ?: return
|
||||||
|
if (cls.classScope !== this) return
|
||||||
|
if (!(value is Statement || recordType == ObjRecord.Type.Fun || recordType == ObjRecord.Type.Delegated)) return
|
||||||
|
if (cls.members.containsKey(name)) return
|
||||||
|
cls.layoutVersion += 1
|
||||||
|
}
|
||||||
|
|
||||||
fun getOrCreateNamespace(name: String): ObjClass {
|
fun getOrCreateNamespace(name: String): ObjClass {
|
||||||
val ns = objects.getOrPut(name) { ObjRecord(ObjNamespace(name), isMutable = false) }.value
|
val ns = objects.getOrPut(name) { ObjRecord(ObjNamespace(name), isMutable = false) }.value
|
||||||
return ns.objClass
|
return ns.objClass
|
||||||
|
|||||||
@ -114,8 +114,7 @@ open class ObjClass(
|
|||||||
val classId: Long = ClassIdGen.nextId()
|
val classId: Long = ClassIdGen.nextId()
|
||||||
var layoutVersion: Int = 0
|
var layoutVersion: Int = 0
|
||||||
|
|
||||||
private val mangledNameCache = mutableMapOf<String, String>()
|
fun mangledName(name: String): String = "$className::$name"
|
||||||
fun mangledName(name: String): String = mangledNameCache.getOrPut(name) { "$className::$name" }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map of public member names to their effective storage keys in instanceScope.objects.
|
* Map of public member names to their effective storage keys in instanceScope.objects.
|
||||||
@ -128,7 +127,7 @@ open class ObjClass(
|
|||||||
if (cls.className == "Obj") continue
|
if (cls.className == "Obj") continue
|
||||||
for ((name, rec) in cls.members) {
|
for ((name, rec) in cls.members) {
|
||||||
if (rec.visibility == Visibility.Public) {
|
if (rec.visibility == Visibility.Public) {
|
||||||
val key = if (rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.Delegated) cls.mangledName(name) else name
|
val key = if (rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField || rec.type == ObjRecord.Type.Delegated) cls.mangledName(name) else name
|
||||||
res[name] = key
|
res[name] = key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -267,6 +266,119 @@ open class ObjClass(
|
|||||||
*/
|
*/
|
||||||
internal val members = mutableMapOf<String, ObjRecord>()
|
internal val members = mutableMapOf<String, ObjRecord>()
|
||||||
|
|
||||||
|
internal data class FieldSlot(val slot: Int, val record: ObjRecord)
|
||||||
|
internal data class ResolvedMember(val record: ObjRecord, val declaringClass: ObjClass)
|
||||||
|
internal data class MethodSlot(val slot: Int, val record: ObjRecord)
|
||||||
|
private var fieldSlotLayoutVersion: Int = -1
|
||||||
|
private var fieldSlotMap: Map<String, FieldSlot> = emptyMap()
|
||||||
|
private var fieldSlotCount: Int = 0
|
||||||
|
private var instanceMemberLayoutVersion: Int = -1
|
||||||
|
private var instanceMemberCache: Map<String, ResolvedMember> = emptyMap()
|
||||||
|
private var methodSlotLayoutVersion: Int = -1
|
||||||
|
private var methodSlotMap: Map<String, MethodSlot> = emptyMap()
|
||||||
|
private var methodSlotCount: Int = 0
|
||||||
|
|
||||||
|
private fun ensureFieldSlots(): Map<String, FieldSlot> {
|
||||||
|
if (fieldSlotLayoutVersion == layoutVersion) return fieldSlotMap
|
||||||
|
val res = mutableMapOf<String, FieldSlot>()
|
||||||
|
var idx = 0
|
||||||
|
for (cls in mro) {
|
||||||
|
for ((name, rec) in cls.members) {
|
||||||
|
if (rec.isAbstract) continue
|
||||||
|
if (rec.type != ObjRecord.Type.Field && rec.type != ObjRecord.Type.ConstructorField) continue
|
||||||
|
val key = cls.mangledName(name)
|
||||||
|
if (res.containsKey(key)) continue
|
||||||
|
res[key] = FieldSlot(idx, rec)
|
||||||
|
idx += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldSlotMap = res
|
||||||
|
fieldSlotCount = idx
|
||||||
|
fieldSlotLayoutVersion = layoutVersion
|
||||||
|
return fieldSlotMap
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ensureInstanceMemberCache(): Map<String, ResolvedMember> {
|
||||||
|
if (instanceMemberLayoutVersion == layoutVersion) return instanceMemberCache
|
||||||
|
val res = mutableMapOf<String, ResolvedMember>()
|
||||||
|
for (cls in mro) {
|
||||||
|
if (cls.className == "Obj") break
|
||||||
|
for ((name, rec) in cls.members) {
|
||||||
|
if (rec.isAbstract) continue
|
||||||
|
if (res.containsKey(name)) continue
|
||||||
|
val decl = rec.declaringClass ?: cls
|
||||||
|
res[name] = ResolvedMember(rec, decl)
|
||||||
|
}
|
||||||
|
cls.classScope?.objects?.forEach { (name, rec) ->
|
||||||
|
if (rec.isAbstract) return@forEach
|
||||||
|
if (res.containsKey(name)) return@forEach
|
||||||
|
val decl = rec.declaringClass ?: cls
|
||||||
|
res[name] = ResolvedMember(rec, decl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instanceMemberCache = res
|
||||||
|
instanceMemberLayoutVersion = layoutVersion
|
||||||
|
return instanceMemberCache
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ensureMethodSlots(): Map<String, MethodSlot> {
|
||||||
|
if (methodSlotLayoutVersion == layoutVersion) return methodSlotMap
|
||||||
|
val res = mutableMapOf<String, MethodSlot>()
|
||||||
|
var idx = 0
|
||||||
|
for (cls in mro) {
|
||||||
|
if (cls.className == "Obj") break
|
||||||
|
for ((name, rec) in cls.members) {
|
||||||
|
if (rec.isAbstract) continue
|
||||||
|
if (rec.value !is Statement &&
|
||||||
|
rec.type != ObjRecord.Type.Delegated &&
|
||||||
|
rec.type != ObjRecord.Type.Fun &&
|
||||||
|
rec.type != ObjRecord.Type.Property) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val key = if (rec.visibility == Visibility.Private || rec.type == ObjRecord.Type.Delegated) cls.mangledName(name) else name
|
||||||
|
if (res.containsKey(key)) continue
|
||||||
|
res[key] = MethodSlot(idx, rec)
|
||||||
|
idx += 1
|
||||||
|
}
|
||||||
|
cls.classScope?.objects?.forEach { (name, rec) ->
|
||||||
|
if (rec.isAbstract) return@forEach
|
||||||
|
if (rec.value !is Statement &&
|
||||||
|
rec.type != ObjRecord.Type.Delegated &&
|
||||||
|
rec.type != ObjRecord.Type.Property) return@forEach
|
||||||
|
val key = if (rec.visibility == Visibility.Private || rec.type == ObjRecord.Type.Delegated) cls.mangledName(name) else name
|
||||||
|
if (res.containsKey(key)) return@forEach
|
||||||
|
res[key] = MethodSlot(idx, rec)
|
||||||
|
idx += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
methodSlotMap = res
|
||||||
|
methodSlotCount = idx
|
||||||
|
methodSlotLayoutVersion = layoutVersion
|
||||||
|
return methodSlotMap
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun fieldSlotCount(): Int {
|
||||||
|
ensureFieldSlots()
|
||||||
|
return fieldSlotCount
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun fieldSlotForKey(key: String): FieldSlot? {
|
||||||
|
ensureFieldSlots()
|
||||||
|
return fieldSlotMap[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun fieldSlotMap(): Map<String, FieldSlot> = ensureFieldSlots()
|
||||||
|
internal fun resolveInstanceMember(name: String): ResolvedMember? = ensureInstanceMemberCache()[name]
|
||||||
|
internal fun methodSlotCount(): Int {
|
||||||
|
ensureMethodSlots()
|
||||||
|
return methodSlotCount
|
||||||
|
}
|
||||||
|
internal fun methodSlotForKey(key: String): MethodSlot? {
|
||||||
|
ensureMethodSlots()
|
||||||
|
return methodSlotMap[key]
|
||||||
|
}
|
||||||
|
internal fun methodSlotMap(): Map<String, MethodSlot> = ensureMethodSlots()
|
||||||
|
|
||||||
override fun toString(): String = className
|
override fun toString(): String = className
|
||||||
|
|
||||||
override suspend fun compareTo(scope: Scope, other: Obj): Int = if (other === this) 0 else -1
|
override suspend fun compareTo(scope: Scope, other: Obj): Int = if (other === this) 0 else -1
|
||||||
@ -284,8 +396,8 @@ open class ObjClass(
|
|||||||
for (cls in mro) {
|
for (cls in mro) {
|
||||||
// 1) members-defined methods and fields
|
// 1) members-defined methods and fields
|
||||||
for ((k, v) in cls.members) {
|
for ((k, v) in cls.members) {
|
||||||
if (!v.isAbstract && (v.value is Statement || v.type == ObjRecord.Type.Delegated || v.type == ObjRecord.Type.Field)) {
|
if (!v.isAbstract && (v.value is Statement || v.type == ObjRecord.Type.Delegated || v.type == ObjRecord.Type.Field || v.type == ObjRecord.Type.ConstructorField)) {
|
||||||
val key = if (v.visibility == Visibility.Private || v.type == ObjRecord.Type.Field || v.type == ObjRecord.Type.Delegated) cls.mangledName(k) else k
|
val key = if (v.visibility == Visibility.Private || v.type == ObjRecord.Type.Field || v.type == ObjRecord.Type.ConstructorField || v.type == ObjRecord.Type.Delegated) cls.mangledName(k) else k
|
||||||
if (!res.containsKey(key)) {
|
if (!res.containsKey(key)) {
|
||||||
res[key] = v
|
res[key] = v
|
||||||
}
|
}
|
||||||
@ -327,12 +439,47 @@ open class ObjClass(
|
|||||||
val stableParent = classScope ?: scope.parent
|
val stableParent = classScope ?: scope.parent
|
||||||
instance.instanceScope = Scope(stableParent, scope.args, scope.pos, instance)
|
instance.instanceScope = Scope(stableParent, scope.args, scope.pos, instance)
|
||||||
instance.instanceScope.currentClassCtx = null
|
instance.instanceScope.currentClassCtx = null
|
||||||
|
val fieldSlots = fieldSlotMap()
|
||||||
|
if (fieldSlots.isNotEmpty()) {
|
||||||
|
instance.initFieldSlots(fieldSlotCount())
|
||||||
|
}
|
||||||
|
val methodSlots = methodSlotMap()
|
||||||
|
if (methodSlots.isNotEmpty()) {
|
||||||
|
instance.initMethodSlots(methodSlotCount())
|
||||||
|
}
|
||||||
// Expose instance methods (and other callable members) directly in the instance scope for fast lookup
|
// 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
|
// This mirrors Obj.autoInstanceScope behavior for ad-hoc scopes and makes fb.method() resolution robust
|
||||||
|
|
||||||
instance.instanceScope.objects.putAll(templateMethods)
|
instance.instanceScope.objects.putAll(templateMethods)
|
||||||
|
if (methodSlots.isNotEmpty()) {
|
||||||
|
for ((key, rec) in templateMethods) {
|
||||||
|
val slot = methodSlots[key]
|
||||||
|
if (slot != null) {
|
||||||
|
instance.setMethodSlotRecord(slot.slot, rec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for (p in templateOthers) {
|
for (p in templateOthers) {
|
||||||
instance.instanceScope.objects[p.first] = p.second.copy()
|
val rec = p.second.copy()
|
||||||
|
instance.instanceScope.objects[p.first] = rec
|
||||||
|
val slot = fieldSlots[p.first]
|
||||||
|
if (slot != null) {
|
||||||
|
instance.setFieldSlotRecord(slot.slot, rec)
|
||||||
|
}
|
||||||
|
if (methodSlots.isNotEmpty()) {
|
||||||
|
val mSlot = methodSlots[p.first]
|
||||||
|
if (mSlot != null) {
|
||||||
|
instance.setMethodSlotRecord(mSlot.slot, rec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (methodSlots.isNotEmpty()) {
|
||||||
|
for ((_, mSlot) in methodSlots) {
|
||||||
|
val idx = mSlot.slot
|
||||||
|
if (idx >= 0 && idx < instance.methodSlots.size && instance.methodSlots[idx] == null) {
|
||||||
|
instance.setMethodSlotRecord(idx, mSlot.record)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
@ -417,6 +564,10 @@ open class ObjClass(
|
|||||||
if (rec != null) {
|
if (rec != null) {
|
||||||
val mangled = c.mangledName(p.name)
|
val mangled = c.mangledName(p.name)
|
||||||
instance.instanceScope.objects[mangled] = rec
|
instance.instanceScope.objects[mangled] = rec
|
||||||
|
val slot = instance.objClass.fieldSlotForKey(mangled)
|
||||||
|
if (slot != null) {
|
||||||
|
instance.setFieldSlotRecord(slot.slot, rec)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -738,5 +889,3 @@ open class ObjClass(
|
|||||||
scope.raiseNotImplemented()
|
scope.raiseNotImplemented()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -30,6 +30,36 @@ import net.sergeych.lynon.LynonType
|
|||||||
class ObjInstance(override val objClass: ObjClass) : Obj() {
|
class ObjInstance(override val objClass: ObjClass) : Obj() {
|
||||||
|
|
||||||
internal lateinit var instanceScope: Scope
|
internal lateinit var instanceScope: Scope
|
||||||
|
internal var fieldSlots: Array<ObjRecord?> = emptyArray()
|
||||||
|
internal var methodSlots: Array<ObjRecord?> = emptyArray()
|
||||||
|
|
||||||
|
internal fun initFieldSlots(size: Int) {
|
||||||
|
fieldSlots = arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun setFieldSlotRecord(slot: Int, rec: ObjRecord) {
|
||||||
|
if (slot >= 0 && slot < fieldSlots.size) fieldSlots[slot] = rec
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun initMethodSlots(size: Int) {
|
||||||
|
methodSlots = arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun setMethodSlotRecord(slot: Int, rec: ObjRecord) {
|
||||||
|
if (slot >= 0 && slot < methodSlots.size) methodSlots[slot] = rec
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun fieldRecordForKey(key: String): ObjRecord? {
|
||||||
|
val slot = objClass.fieldSlotForKey(key) ?: return null
|
||||||
|
val idx = slot.slot
|
||||||
|
return if (idx >= 0 && idx < fieldSlots.size) fieldSlots[idx] else null
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun methodRecordForKey(key: String): ObjRecord? {
|
||||||
|
val slot = objClass.methodSlotForKey(key) ?: return null
|
||||||
|
val idx = slot.slot
|
||||||
|
return if (idx >= 0 && idx < methodSlots.size) methodSlots[idx] else null
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun readField(scope: Scope, name: String): ObjRecord {
|
override suspend fun readField(scope: Scope, name: String): ObjRecord {
|
||||||
val caller = scope.currentClassCtx
|
val caller = scope.currentClassCtx
|
||||||
@ -37,6 +67,16 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
// Fast path for public members when outside any class context
|
// Fast path for public members when outside any class context
|
||||||
if (caller == null) {
|
if (caller == null) {
|
||||||
objClass.publicMemberResolution[name]?.let { key ->
|
objClass.publicMemberResolution[name]?.let { key ->
|
||||||
|
fieldRecordForKey(key)?.let { rec ->
|
||||||
|
if ((rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField) && !rec.isAbstract)
|
||||||
|
return rec
|
||||||
|
}
|
||||||
|
methodRecordForKey(key)?.let { rec ->
|
||||||
|
if (!rec.isAbstract) {
|
||||||
|
val decl = rec.declaringClass ?: objClass.findDeclaringClassOf(name) ?: objClass
|
||||||
|
return resolveRecord(scope, rec, name, decl)
|
||||||
|
}
|
||||||
|
}
|
||||||
instanceScope.objects[key]?.let { rec ->
|
instanceScope.objects[key]?.let { rec ->
|
||||||
// Directly return fields to bypass resolveRecord overhead
|
// Directly return fields to bypass resolveRecord overhead
|
||||||
if ((rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField) && !rec.isAbstract)
|
if ((rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField) && !rec.isAbstract)
|
||||||
@ -56,6 +96,16 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
}
|
}
|
||||||
// Check for private fields (stored in instanceScope)
|
// Check for private fields (stored in instanceScope)
|
||||||
val mangled = c.mangledName(name)
|
val mangled = c.mangledName(name)
|
||||||
|
fieldRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (rec.visibility == Visibility.Private) {
|
||||||
|
return resolveRecord(scope, rec, name, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
methodRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (rec.visibility == Visibility.Private) {
|
||||||
|
return resolveRecord(scope, rec, name, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
instanceScope.objects[mangled]?.let { rec ->
|
instanceScope.objects[mangled]?.let { rec ->
|
||||||
if (rec.visibility == Visibility.Private) {
|
if (rec.visibility == Visibility.Private) {
|
||||||
return resolveRecord(scope, rec, name, c)
|
return resolveRecord(scope, rec, name, c)
|
||||||
@ -67,6 +117,16 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
for (cls in objClass.mro) {
|
for (cls in objClass.mro) {
|
||||||
if (cls.className == "Obj") break
|
if (cls.className == "Obj") break
|
||||||
val mangled = cls.mangledName(name)
|
val mangled = cls.mangledName(name)
|
||||||
|
fieldRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (canAccessMember(rec.visibility, cls, caller, name)) {
|
||||||
|
return resolveRecord(scope, rec, name, cls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
methodRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (canAccessMember(rec.visibility, cls, caller, name)) {
|
||||||
|
return resolveRecord(scope, rec, name, cls)
|
||||||
|
}
|
||||||
|
}
|
||||||
instanceScope.objects[mangled]?.let { rec ->
|
instanceScope.objects[mangled]?.let { rec ->
|
||||||
if (canAccessMember(rec.visibility, cls, caller, name)) {
|
if (canAccessMember(rec.visibility, cls, caller, name)) {
|
||||||
return resolveRecord(scope, rec, name, cls)
|
return resolveRecord(scope, rec, name, cls)
|
||||||
@ -81,6 +141,12 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
return resolveRecord(scope, rec, name, decl)
|
return resolveRecord(scope, rec, name, decl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
methodRecordForKey(name)?.let { rec ->
|
||||||
|
val decl = rec.declaringClass ?: objClass.findDeclaringClassOf(name)
|
||||||
|
if (canAccessMember(rec.visibility, decl, caller, name)) {
|
||||||
|
return resolveRecord(scope, rec, name, decl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 3. Fall back to super (handles class members and extensions)
|
// 3. Fall back to super (handles class members and extensions)
|
||||||
return super.readField(scope, name)
|
return super.readField(scope, name)
|
||||||
@ -109,10 +175,15 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
val d = decl ?: obj.declaringClass
|
val d = decl ?: obj.declaringClass
|
||||||
if (d != null) {
|
if (d != null) {
|
||||||
val mangled = d.mangledName(name)
|
val mangled = d.mangledName(name)
|
||||||
|
fieldRecordForKey(mangled)?.let {
|
||||||
|
targetRec = it
|
||||||
|
}
|
||||||
|
if (targetRec === obj) {
|
||||||
instanceScope.objects[mangled]?.let {
|
instanceScope.objects[mangled]?.let {
|
||||||
targetRec = it
|
targetRec = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (targetRec === obj) {
|
if (targetRec === obj) {
|
||||||
instanceScope.objects[name]?.let { rec ->
|
instanceScope.objects[name]?.let { rec ->
|
||||||
// Check if this record in instanceScope is the one we want.
|
// Check if this record in instanceScope is the one we want.
|
||||||
@ -134,10 +205,29 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
// Fast path for public members when outside any class context
|
// Fast path for public members when outside any class context
|
||||||
if (caller == null) {
|
if (caller == null) {
|
||||||
objClass.publicMemberResolution[name]?.let { key ->
|
objClass.publicMemberResolution[name]?.let { key ->
|
||||||
|
fieldRecordForKey(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.type == ObjRecord.Type.ConstructorField) && rec.isMutable && !rec.isAbstract) {
|
||||||
|
if (rec.value.assign(scope, newValue) == null)
|
||||||
|
rec.value = newValue
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updateRecord(scope, rec, name, newValue, rec.declaringClass)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
methodRecordForKey(key)?.let { rec ->
|
||||||
|
if (rec.effectiveWriteVisibility == Visibility.Public &&
|
||||||
|
(rec.type == ObjRecord.Type.Property || rec.type == ObjRecord.Type.Delegated)) {
|
||||||
|
updateRecord(scope, rec, name, newValue, rec.declaringClass)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
instanceScope.objects[key]?.let { rec ->
|
instanceScope.objects[key]?.let { rec ->
|
||||||
if (rec.effectiveWriteVisibility == Visibility.Public) {
|
if (rec.effectiveWriteVisibility == Visibility.Public) {
|
||||||
// Skip property/delegated overhead if it's a plain mutable field
|
// Skip property/delegated overhead if it's a plain mutable field
|
||||||
if (rec.type == ObjRecord.Type.Field && rec.isMutable && !rec.isAbstract) {
|
if ((rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField) && rec.isMutable && !rec.isAbstract) {
|
||||||
if (rec.value.assign(scope, newValue) == null)
|
if (rec.value.assign(scope, newValue) == null)
|
||||||
rec.value = newValue
|
rec.value = newValue
|
||||||
return
|
return
|
||||||
@ -160,6 +250,19 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
}
|
}
|
||||||
// Check for private fields (stored in instanceScope)
|
// Check for private fields (stored in instanceScope)
|
||||||
val mangled = c.mangledName(name)
|
val mangled = c.mangledName(name)
|
||||||
|
fieldRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (rec.visibility == Visibility.Private) {
|
||||||
|
updateRecord(scope, rec, name, newValue, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
methodRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (rec.visibility == Visibility.Private &&
|
||||||
|
(rec.type == ObjRecord.Type.Property || rec.type == ObjRecord.Type.Delegated)) {
|
||||||
|
updateRecord(scope, rec, name, newValue, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
instanceScope.objects[mangled]?.let { rec ->
|
instanceScope.objects[mangled]?.let { rec ->
|
||||||
if (rec.visibility == Visibility.Private) {
|
if (rec.visibility == Visibility.Private) {
|
||||||
updateRecord(scope, rec, name, newValue, c)
|
updateRecord(scope, rec, name, newValue, c)
|
||||||
@ -172,6 +275,19 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
for (cls in objClass.mro) {
|
for (cls in objClass.mro) {
|
||||||
if (cls.className == "Obj") break
|
if (cls.className == "Obj") break
|
||||||
val mangled = cls.mangledName(name)
|
val mangled = cls.mangledName(name)
|
||||||
|
fieldRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (canAccessMember(rec.effectiveWriteVisibility, cls, caller, name)) {
|
||||||
|
updateRecord(scope, rec, name, newValue, cls)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
methodRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (canAccessMember(rec.effectiveWriteVisibility, cls, caller, name) &&
|
||||||
|
(rec.type == ObjRecord.Type.Property || rec.type == ObjRecord.Type.Delegated)) {
|
||||||
|
updateRecord(scope, rec, name, newValue, cls)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
instanceScope.objects[mangled]?.let { rec ->
|
instanceScope.objects[mangled]?.let { rec ->
|
||||||
if (canAccessMember(rec.effectiveWriteVisibility, cls, caller, name)) {
|
if (canAccessMember(rec.effectiveWriteVisibility, cls, caller, name)) {
|
||||||
updateRecord(scope, rec, name, newValue, cls)
|
updateRecord(scope, rec, name, newValue, cls)
|
||||||
@ -188,6 +304,14 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
methodRecordForKey(name)?.let { rec ->
|
||||||
|
val decl = rec.declaringClass ?: objClass.findDeclaringClassOf(name)
|
||||||
|
if (canAccessMember(rec.effectiveWriteVisibility, decl, caller, name) &&
|
||||||
|
(rec.type == ObjRecord.Type.Property || rec.type == ObjRecord.Type.Delegated)) {
|
||||||
|
updateRecord(scope, rec, name, newValue, decl)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
super.writeField(scope, name, newValue)
|
super.writeField(scope, name, newValue)
|
||||||
}
|
}
|
||||||
@ -225,6 +349,16 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
// Fast path for public members when outside any class context
|
// Fast path for public members when outside any class context
|
||||||
if (caller == null) {
|
if (caller == null) {
|
||||||
objClass.publicMemberResolution[name]?.let { key ->
|
objClass.publicMemberResolution[name]?.let { key ->
|
||||||
|
methodRecordForKey(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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
instanceScope.objects[key]?.let { rec ->
|
instanceScope.objects[key]?.let { rec ->
|
||||||
if (rec.visibility == Visibility.Public && !rec.isAbstract) {
|
if (rec.visibility == Visibility.Public && !rec.isAbstract) {
|
||||||
val decl = rec.declaringClass
|
val decl = rec.declaringClass
|
||||||
@ -241,6 +375,15 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
// 0. Prefer private member of current class context
|
// 0. Prefer private member of current class context
|
||||||
caller?.let { c ->
|
caller?.let { c ->
|
||||||
val mangled = c.mangledName(name)
|
val mangled = c.mangledName(name)
|
||||||
|
methodRecordForKey(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, c)
|
||||||
|
} else if (rec.type == ObjRecord.Type.Fun) {
|
||||||
|
return rec.value.invoke(instanceScope, this, args, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
instanceScope.objects[mangled]?.let { rec ->
|
instanceScope.objects[mangled]?.let { rec ->
|
||||||
if (rec.visibility == Visibility.Private && !rec.isAbstract) {
|
if (rec.visibility == Visibility.Private && !rec.isAbstract) {
|
||||||
if (rec.type == ObjRecord.Type.Property) {
|
if (rec.type == ObjRecord.Type.Property) {
|
||||||
@ -261,13 +404,23 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Walk MRO to find member, handling delegation
|
// Fast path for non-delegated instance methods in class context
|
||||||
for (cls in objClass.mro) {
|
methodRecordForKey(name)?.let { rec ->
|
||||||
if (cls.className == "Obj") break
|
if (!rec.isAbstract && rec.type == ObjRecord.Type.Fun) {
|
||||||
val rec = cls.members[name] ?: cls.classScope?.objects?.get(name)
|
val decl = rec.declaringClass ?: objClass.findDeclaringClassOf(name) ?: objClass
|
||||||
if (rec != null && !rec.isAbstract) {
|
val effectiveCaller = caller ?: if (scope.thisObj === this) objClass else null
|
||||||
|
if (canAccessMember(rec.visibility, decl, effectiveCaller, name)) {
|
||||||
|
return rec.value.invoke(instanceScope, this, args, decl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Resolve instance member via cached MRO lookup, handling delegation
|
||||||
|
objClass.resolveInstanceMember(name)?.let { resolvedMember ->
|
||||||
|
val rec = resolvedMember.record
|
||||||
|
val decl = resolvedMember.declaringClass
|
||||||
if (rec.type == ObjRecord.Type.Delegated) {
|
if (rec.type == ObjRecord.Type.Delegated) {
|
||||||
val storageName = cls.mangledName(name)
|
val storageName = decl.mangledName(name)
|
||||||
val del = instanceScope[storageName]?.delegate ?: rec.delegate
|
val del = instanceScope[storageName]?.delegate ?: rec.delegate
|
||||||
?: scope.raiseError("Internal error: delegated member $name has no delegate (tried $storageName)")
|
?: scope.raiseError("Internal error: delegated member $name has no delegate (tried $storageName)")
|
||||||
|
|
||||||
@ -276,10 +429,9 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
return del.invokeInstanceMethod(scope, "invoke", Arguments(*allArgs), onNotFoundResult = {
|
return del.invokeInstanceMethod(scope, "invoke", Arguments(*allArgs), onNotFoundResult = {
|
||||||
// Fallback: property delegation (getValue then call result)
|
// Fallback: property delegation (getValue then call result)
|
||||||
val propVal = del.invokeInstanceMethod(scope, "getValue", Arguments(this, ObjString(name)))
|
val propVal = del.invokeInstanceMethod(scope, "getValue", Arguments(this, ObjString(name)))
|
||||||
propVal.invoke(scope, this, args, rec.declaringClass ?: cls)
|
propVal.invoke(scope, this, args, rec.declaringClass ?: decl)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
val decl = rec.declaringClass ?: cls
|
|
||||||
val effectiveCaller = caller ?: if (scope.thisObj === this) objClass else null
|
val effectiveCaller = caller ?: if (scope.thisObj === this) objClass else null
|
||||||
if (!canAccessMember(rec.visibility, decl, effectiveCaller, name))
|
if (!canAccessMember(rec.visibility, decl, effectiveCaller, name))
|
||||||
scope.raiseError(
|
scope.raiseError(
|
||||||
@ -303,7 +455,6 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
return resolved.value.invoke(scope, this, args, resolved.declaringClass)
|
return resolved.value.invoke(scope, this, args, resolved.declaringClass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Fall back to super (handles extensions and root fallback)
|
// 2. Fall back to super (handles extensions and root fallback)
|
||||||
return super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
return super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
||||||
@ -431,6 +582,14 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
|
|||||||
override suspend fun readField(scope: Scope, name: String): ObjRecord {
|
override suspend fun readField(scope: Scope, name: String): ObjRecord {
|
||||||
// Qualified field access: prefer mangled storage for the qualified ancestor
|
// Qualified field access: prefer mangled storage for the qualified ancestor
|
||||||
val mangled = "${startClass.className}::$name"
|
val mangled = "${startClass.className}::$name"
|
||||||
|
instance.fieldRecordForKey(mangled)?.let { rec ->
|
||||||
|
// Visibility: declaring class is the qualified ancestor for mangled storage
|
||||||
|
val decl = rec.declaringClass ?: startClass
|
||||||
|
val caller = scope.currentClassCtx
|
||||||
|
if (!canAccessMember(rec.visibility, decl, caller, name))
|
||||||
|
scope.raiseError(ObjIllegalAccessException(scope, "can't access field $name (declared in ${decl.className})"))
|
||||||
|
return instance.resolveRecord(scope, rec, name, decl)
|
||||||
|
}
|
||||||
instance.instanceScope.objects[mangled]?.let { rec ->
|
instance.instanceScope.objects[mangled]?.let { rec ->
|
||||||
// Visibility: declaring class is the qualified ancestor for mangled storage
|
// Visibility: declaring class is the qualified ancestor for mangled storage
|
||||||
val decl = rec.declaringClass ?: startClass
|
val decl = rec.declaringClass ?: startClass
|
||||||
@ -467,6 +626,18 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
|
|||||||
override suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
|
override suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
|
||||||
// Qualified write: target mangled storage for the ancestor
|
// Qualified write: target mangled storage for the ancestor
|
||||||
val mangled = "${startClass.className}::$name"
|
val mangled = "${startClass.className}::$name"
|
||||||
|
instance.fieldRecordForKey(mangled)?.let { f ->
|
||||||
|
val decl = f.declaringClass ?: startClass
|
||||||
|
val caller = scope.currentClassCtx
|
||||||
|
if (!canAccessMember(f.effectiveWriteVisibility, decl, caller, name))
|
||||||
|
ObjIllegalAccessException(
|
||||||
|
scope,
|
||||||
|
"can't assign to field $name (declared in ${decl.className})"
|
||||||
|
).raise()
|
||||||
|
if (!f.isMutable && f.value !== ObjUnset) ObjIllegalAssignmentException(scope, "can't reassign val $name").raise()
|
||||||
|
if (f.value.assign(scope, newValue) == null) f.value = newValue
|
||||||
|
return
|
||||||
|
}
|
||||||
instance.instanceScope.objects[mangled]?.let { f ->
|
instance.instanceScope.objects[mangled]?.let { f ->
|
||||||
val decl = f.declaringClass ?: startClass
|
val decl = f.declaringClass ?: startClass
|
||||||
val caller = scope.currentClassCtx
|
val caller = scope.currentClassCtx
|
||||||
|
|||||||
@ -381,7 +381,7 @@ class CastRef(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Qualified `this@Type`: resolves to a view of current `this` starting dispatch from the ancestor Type. */
|
/** Qualified `this@Type`: resolves to a view of current `this` starting dispatch from the ancestor Type. */
|
||||||
class QualifiedThisRef(private val typeName: String, private val atPos: Pos) : ObjRef {
|
class QualifiedThisRef(val typeName: String, private val atPos: Pos) : ObjRef {
|
||||||
override suspend fun get(scope: Scope): ObjRecord {
|
override suspend fun get(scope: Scope): ObjRecord {
|
||||||
val t = scope[typeName]?.value as? ObjClass
|
val t = scope[typeName]?.value as? ObjClass
|
||||||
?: scope.raiseError("unknown type $typeName")
|
?: scope.raiseError("unknown type $typeName")
|
||||||
@ -403,6 +403,188 @@ class QualifiedThisRef(private val typeName: String, private val atPos: Pos) : O
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun resolveQualifiedThisInstance(scope: Scope, typeName: String): Pair<ObjInstance, ObjClass> {
|
||||||
|
val t = scope[typeName]?.value as? ObjClass
|
||||||
|
?: scope.raiseError("unknown type $typeName")
|
||||||
|
var s: Scope? = scope
|
||||||
|
while (s != null) {
|
||||||
|
val inst = s.thisObj as? ObjInstance
|
||||||
|
if (inst != null && (inst.objClass === t || inst.objClass.allParentsSet.contains(t))) {
|
||||||
|
return inst to t
|
||||||
|
}
|
||||||
|
s = s.parent
|
||||||
|
}
|
||||||
|
scope.raiseClassCastError(
|
||||||
|
"No instance of type ${t.className} found in the scope chain"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fast path for direct `this@Type.name` access using slot maps when possible.
|
||||||
|
*/
|
||||||
|
class QualifiedThisFieldSlotRef(
|
||||||
|
private val typeName: String,
|
||||||
|
val name: String,
|
||||||
|
private val isOptional: Boolean
|
||||||
|
) : ObjRef {
|
||||||
|
override suspend fun get(scope: Scope): ObjRecord {
|
||||||
|
val (inst, startClass) = resolveQualifiedThisInstance(scope, typeName)
|
||||||
|
if (isOptional && inst == ObjNull) return ObjNull.asMutable
|
||||||
|
|
||||||
|
if (startClass !== inst.objClass) {
|
||||||
|
return ObjQualifiedView(inst, startClass).readField(scope, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val caller = scope.currentClassCtx
|
||||||
|
if (caller != null) {
|
||||||
|
val mangled = caller.mangledName(name)
|
||||||
|
inst.fieldRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (rec.visibility == Visibility.Private) {
|
||||||
|
return inst.resolveRecord(scope, rec, name, caller)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inst.methodRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (rec.visibility == Visibility.Private) {
|
||||||
|
return inst.resolveRecord(scope, rec, name, caller)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val key = inst.objClass.publicMemberResolution[name] ?: name
|
||||||
|
inst.fieldRecordForKey(key)?.let { rec ->
|
||||||
|
if ((rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField) && !rec.isAbstract)
|
||||||
|
return rec
|
||||||
|
}
|
||||||
|
inst.methodRecordForKey(key)?.let { rec ->
|
||||||
|
if (!rec.isAbstract) {
|
||||||
|
val decl = rec.declaringClass ?: inst.objClass.findDeclaringClassOf(name) ?: inst.objClass
|
||||||
|
return inst.resolveRecord(scope, rec, name, decl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return inst.readField(scope, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setAt(pos: Pos, scope: Scope, newValue: Obj) {
|
||||||
|
val (inst, startClass) = resolveQualifiedThisInstance(scope, typeName)
|
||||||
|
if (isOptional && inst == ObjNull) return
|
||||||
|
|
||||||
|
if (startClass !== inst.objClass) {
|
||||||
|
ObjQualifiedView(inst, startClass).writeField(scope, name, newValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val caller = scope.currentClassCtx
|
||||||
|
if (caller != null) {
|
||||||
|
val mangled = caller.mangledName(name)
|
||||||
|
inst.fieldRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (rec.visibility == Visibility.Private) {
|
||||||
|
writeDirectOrFallback(scope, inst, rec, name, newValue, caller)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inst.methodRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (rec.visibility == Visibility.Private &&
|
||||||
|
(rec.type == ObjRecord.Type.Property || rec.type == ObjRecord.Type.Delegated)) {
|
||||||
|
inst.writeField(scope, name, newValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val key = inst.objClass.publicMemberResolution[name] ?: name
|
||||||
|
inst.fieldRecordForKey(key)?.let { rec ->
|
||||||
|
val decl = rec.declaringClass ?: inst.objClass.findDeclaringClassOf(name)
|
||||||
|
if (canAccessMember(rec.effectiveWriteVisibility, decl, caller, name)) {
|
||||||
|
writeDirectOrFallback(scope, inst, rec, name, newValue, decl)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inst.methodRecordForKey(key)?.let { rec ->
|
||||||
|
if (rec.effectiveWriteVisibility == Visibility.Public &&
|
||||||
|
(rec.type == ObjRecord.Type.Property || rec.type == ObjRecord.Type.Delegated)) {
|
||||||
|
inst.writeField(scope, name, newValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inst.writeField(scope, name, newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun writeDirectOrFallback(
|
||||||
|
scope: Scope,
|
||||||
|
inst: ObjInstance,
|
||||||
|
rec: ObjRecord,
|
||||||
|
name: String,
|
||||||
|
newValue: Obj,
|
||||||
|
decl: ObjClass?
|
||||||
|
) {
|
||||||
|
if ((rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField) && !rec.isAbstract) {
|
||||||
|
if (!rec.isMutable && rec.value !== ObjUnset) {
|
||||||
|
ObjIllegalAssignmentException(scope, "can't reassign val $name").raise()
|
||||||
|
}
|
||||||
|
if (rec.value.assign(scope, newValue) == null) rec.value = newValue
|
||||||
|
} else {
|
||||||
|
inst.writeField(scope, name, newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fast path for direct `this@Type.method(...)` calls using slots when the qualifier is the
|
||||||
|
* dynamic class. Otherwise falls back to a qualified view dispatch.
|
||||||
|
*/
|
||||||
|
class QualifiedThisMethodSlotCallRef(
|
||||||
|
private val typeName: String,
|
||||||
|
private val name: String,
|
||||||
|
private val args: List<ParsedArgument>,
|
||||||
|
private val tailBlock: Boolean,
|
||||||
|
private val isOptional: Boolean
|
||||||
|
) : ObjRef {
|
||||||
|
override suspend fun get(scope: Scope): ObjRecord = evalValue(scope).asReadonly
|
||||||
|
|
||||||
|
override suspend fun evalValue(scope: Scope): Obj {
|
||||||
|
val (inst, startClass) = resolveQualifiedThisInstance(scope, typeName)
|
||||||
|
if (isOptional && inst == ObjNull) return ObjNull
|
||||||
|
val callArgs = args.toArguments(scope, tailBlock)
|
||||||
|
|
||||||
|
if (startClass !== inst.objClass) {
|
||||||
|
return ObjQualifiedView(inst, startClass).invokeInstanceMethod(scope, name, callArgs, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
val caller = scope.currentClassCtx
|
||||||
|
if (caller != null) {
|
||||||
|
val mangled = caller.mangledName(name)
|
||||||
|
inst.methodRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (rec.visibility == Visibility.Private && !rec.isAbstract) {
|
||||||
|
if (rec.type == ObjRecord.Type.Property) {
|
||||||
|
if (callArgs.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, inst, caller)
|
||||||
|
} else if (rec.type == ObjRecord.Type.Fun) {
|
||||||
|
return rec.value.invoke(inst.instanceScope, inst, callArgs, caller)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val key = inst.objClass.publicMemberResolution[name] ?: name
|
||||||
|
inst.methodRecordForKey(key)?.let { rec ->
|
||||||
|
if (!rec.isAbstract) {
|
||||||
|
val decl = rec.declaringClass ?: inst.objClass.findDeclaringClassOf(name) ?: inst.objClass
|
||||||
|
val effectiveCaller = caller ?: if (scope.thisObj === inst) inst.objClass else null
|
||||||
|
if (!canAccessMember(rec.visibility, decl, effectiveCaller, name))
|
||||||
|
scope.raiseError(ObjIllegalAccessException(scope, "can't invoke method $name (declared in ${decl.className})"))
|
||||||
|
if (rec.type == ObjRecord.Type.Property) {
|
||||||
|
if (callArgs.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, inst, decl)
|
||||||
|
} else if (rec.type == ObjRecord.Type.Fun) {
|
||||||
|
return rec.value.invoke(inst.instanceScope, inst, callArgs, decl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return inst.invokeInstanceMethod(scope, name, callArgs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Assignment compound op: target op= value */
|
/** Assignment compound op: target op= value */
|
||||||
class AssignOpRef(
|
class AssignOpRef(
|
||||||
private val op: BinOp,
|
private val op: BinOp,
|
||||||
@ -691,9 +873,20 @@ class FieldRef(
|
|||||||
if (effectiveKey != null) {
|
if (effectiveKey != null) {
|
||||||
rKey1 = key; rVer1 = ver; rGetter1 = { obj, sc ->
|
rKey1 = key; rVer1 = ver; rGetter1 = { obj, sc ->
|
||||||
if (obj is ObjInstance && obj.objClass === cls) {
|
if (obj is ObjInstance && obj.objClass === cls) {
|
||||||
val rec = obj.instanceScope.objects[effectiveKey]
|
val slot = cls.fieldSlotForKey(effectiveKey)
|
||||||
|
if (slot != null) {
|
||||||
|
val idx = slot.slot
|
||||||
|
val rec = if (idx >= 0 && idx < obj.fieldSlots.size) obj.fieldSlots[idx] else null
|
||||||
|
if (rec != null &&
|
||||||
|
(rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField) &&
|
||||||
|
!rec.isAbstract) {
|
||||||
|
rec
|
||||||
|
} else obj.readField(sc, name)
|
||||||
|
} else {
|
||||||
|
val rec = obj.fieldRecordForKey(effectiveKey) ?: obj.instanceScope.objects[effectiveKey]
|
||||||
if (rec != null && rec.type != ObjRecord.Type.Delegated) rec
|
if (rec != null && rec.type != ObjRecord.Type.Delegated) rec
|
||||||
else obj.readField(sc, name)
|
else obj.readField(sc, name)
|
||||||
|
}
|
||||||
} else obj.readField(sc, name)
|
} else obj.readField(sc, name)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -809,10 +1002,24 @@ class FieldRef(
|
|||||||
if (effectiveKey != null) {
|
if (effectiveKey != null) {
|
||||||
wKey1 = key; wVer1 = ver; wSetter1 = { obj, sc, nv ->
|
wKey1 = key; wVer1 = ver; wSetter1 = { obj, sc, nv ->
|
||||||
if (obj is ObjInstance && obj.objClass === cls) {
|
if (obj is ObjInstance && obj.objClass === cls) {
|
||||||
val rec = obj.instanceScope.objects[effectiveKey]
|
val slot = cls.fieldSlotForKey(effectiveKey)
|
||||||
if (rec != null && rec.effectiveWriteVisibility == Visibility.Public && rec.isMutable && rec.type == ObjRecord.Type.Field) {
|
if (slot != null) {
|
||||||
|
val idx = slot.slot
|
||||||
|
val rec = if (idx >= 0 && idx < obj.fieldSlots.size) obj.fieldSlots[idx] else null
|
||||||
|
if (rec != null &&
|
||||||
|
rec.effectiveWriteVisibility == Visibility.Public &&
|
||||||
|
rec.isMutable &&
|
||||||
|
(rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField) &&
|
||||||
|
!rec.isAbstract) {
|
||||||
if (rec.value.assign(sc, nv) == null) rec.value = nv
|
if (rec.value.assign(sc, nv) == null) rec.value = nv
|
||||||
} else obj.writeField(sc, name, nv)
|
} else obj.writeField(sc, name, nv)
|
||||||
|
} else {
|
||||||
|
val rec = obj.fieldRecordForKey(effectiveKey) ?: obj.instanceScope.objects[effectiveKey]
|
||||||
|
if (rec != null && rec.effectiveWriteVisibility == Visibility.Public && rec.isMutable &&
|
||||||
|
(rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField)) {
|
||||||
|
if (rec.value.assign(sc, nv) == null) rec.value = nv
|
||||||
|
} else obj.writeField(sc, name, nv)
|
||||||
|
}
|
||||||
} else obj.writeField(sc, name, nv)
|
} else obj.writeField(sc, name, nv)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -890,6 +1097,113 @@ class FieldRef(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fast path for direct `this.name` access using slot maps.
|
||||||
|
* Falls back to normal member resolution when needed.
|
||||||
|
*/
|
||||||
|
class ThisFieldSlotRef(
|
||||||
|
val name: String,
|
||||||
|
private val isOptional: Boolean
|
||||||
|
) : ObjRef {
|
||||||
|
override suspend fun get(scope: Scope): ObjRecord {
|
||||||
|
val th = scope.thisObj
|
||||||
|
if (th == ObjNull && isOptional) return ObjNull.asMutable
|
||||||
|
if (th !is ObjInstance) return th.readField(scope, name)
|
||||||
|
|
||||||
|
val caller = scope.currentClassCtx
|
||||||
|
if (caller != null) {
|
||||||
|
val mangled = caller.mangledName(name)
|
||||||
|
th.fieldRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (rec.visibility == Visibility.Private) {
|
||||||
|
return th.resolveRecord(scope, rec, name, caller)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
th.methodRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (rec.visibility == Visibility.Private) {
|
||||||
|
return th.resolveRecord(scope, rec, name, caller)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val key = th.objClass.publicMemberResolution[name] ?: name
|
||||||
|
th.fieldRecordForKey(key)?.let { rec ->
|
||||||
|
if ((rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField) && !rec.isAbstract)
|
||||||
|
return rec
|
||||||
|
}
|
||||||
|
th.methodRecordForKey(key)?.let { rec ->
|
||||||
|
if (!rec.isAbstract) {
|
||||||
|
val decl = rec.declaringClass ?: th.objClass.findDeclaringClassOf(name) ?: th.objClass
|
||||||
|
return th.resolveRecord(scope, rec, name, decl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return th.readField(scope, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setAt(pos: Pos, scope: Scope, newValue: Obj) {
|
||||||
|
val th = scope.thisObj
|
||||||
|
if (th == ObjNull && isOptional) return
|
||||||
|
if (th !is ObjInstance) {
|
||||||
|
th.writeField(scope, name, newValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val caller = scope.currentClassCtx
|
||||||
|
if (caller != null) {
|
||||||
|
val mangled = caller.mangledName(name)
|
||||||
|
th.fieldRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (rec.visibility == Visibility.Private) {
|
||||||
|
writeDirectOrFallback(scope, th, rec, name, newValue, caller)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
th.methodRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (rec.visibility == Visibility.Private &&
|
||||||
|
(rec.type == ObjRecord.Type.Property || rec.type == ObjRecord.Type.Delegated)) {
|
||||||
|
th.writeField(scope, name, newValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val key = th.objClass.publicMemberResolution[name] ?: name
|
||||||
|
th.fieldRecordForKey(key)?.let { rec ->
|
||||||
|
val decl = rec.declaringClass ?: th.objClass.findDeclaringClassOf(name)
|
||||||
|
if (canAccessMember(rec.effectiveWriteVisibility, decl, caller, name)) {
|
||||||
|
writeDirectOrFallback(scope, th, rec, name, newValue, decl)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
th.methodRecordForKey(key)?.let { rec ->
|
||||||
|
if (rec.effectiveWriteVisibility == Visibility.Public &&
|
||||||
|
(rec.type == ObjRecord.Type.Property || rec.type == ObjRecord.Type.Delegated)) {
|
||||||
|
th.writeField(scope, name, newValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
th.writeField(scope, name, newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun writeDirectOrFallback(
|
||||||
|
scope: Scope,
|
||||||
|
inst: ObjInstance,
|
||||||
|
rec: ObjRecord,
|
||||||
|
name: String,
|
||||||
|
newValue: Obj,
|
||||||
|
decl: ObjClass?
|
||||||
|
) {
|
||||||
|
if ((rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField) && !rec.isAbstract) {
|
||||||
|
if (!rec.isMutable && rec.value !== ObjUnset) {
|
||||||
|
ObjIllegalAssignmentException(scope, "can't reassign val $name").raise()
|
||||||
|
}
|
||||||
|
if (rec.value.assign(scope, newValue) == null) rec.value = newValue
|
||||||
|
} else {
|
||||||
|
inst.writeField(scope, name, newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to index access (a[i]) with optional chaining.
|
* Reference to index access (a[i]) with optional chaining.
|
||||||
*/
|
*/
|
||||||
@ -1336,37 +1650,51 @@ class MethodCallRef(
|
|||||||
is ObjInstance -> {
|
is ObjInstance -> {
|
||||||
// Prefer resolved class member to avoid per-call lookup on hit
|
// Prefer resolved class member to avoid per-call lookup on hit
|
||||||
// BUT only if it's NOT a root object member (which can be shadowed by extensions)
|
// BUT only if it's NOT a root object member (which can be shadowed by extensions)
|
||||||
var hierarchyMember: ObjRecord? = null
|
|
||||||
val cls0 = base.objClass
|
val cls0 = base.objClass
|
||||||
val keyInScope = cls0.publicMemberResolution[name]
|
val keyInScope = cls0.publicMemberResolution[name]
|
||||||
if (keyInScope != null) {
|
val methodSlot = if (keyInScope != null) cls0.methodSlotForKey(keyInScope) else null
|
||||||
val rec = base.instanceScope.objects[keyInScope]
|
val fastRec = if (methodSlot != null) {
|
||||||
if (rec != null && rec.type == ObjRecord.Type.Fun) {
|
val idx = methodSlot.slot
|
||||||
hierarchyMember = rec
|
if (idx >= 0 && idx < base.methodSlots.size) base.methodSlots[idx] else null
|
||||||
}
|
} else if (keyInScope != null) {
|
||||||
}
|
base.methodRecordForKey(keyInScope) ?: base.instanceScope.objects[keyInScope]
|
||||||
|
} else null
|
||||||
|
val resolved = if (fastRec != null) null else cls0.resolveInstanceMember(name)
|
||||||
|
|
||||||
if (hierarchyMember == null) {
|
val targetRec = when {
|
||||||
for (cls in base.objClass.mro) {
|
fastRec != null && fastRec.type == ObjRecord.Type.Fun -> fastRec
|
||||||
if (cls.className == "Obj") break
|
resolved != null && resolved.record.type == ObjRecord.Type.Fun && !resolved.record.isAbstract -> resolved.record
|
||||||
val rec = cls.members[name] ?: cls.classScope?.objects?.get(name)
|
else -> null
|
||||||
if (rec != null && !rec.isAbstract && rec.type != ObjRecord.Type.Field) {
|
}
|
||||||
hierarchyMember = rec
|
if (targetRec != null) {
|
||||||
break
|
val visibility = targetRec.visibility
|
||||||
|
val decl = targetRec.declaringClass ?: (resolved?.declaringClass ?: cls0)
|
||||||
|
if (methodSlot != null && targetRec.type == ObjRecord.Type.Fun) {
|
||||||
|
val slotIndex = methodSlot.slot
|
||||||
|
mKey1 = key; mVer1 = ver; mInvoker1 = { obj, sc, a ->
|
||||||
|
val inst = obj as ObjInstance
|
||||||
|
if (inst.objClass === cls0) {
|
||||||
|
val rec = if (slotIndex >= 0 && slotIndex < inst.methodSlots.size) inst.methodSlots[slotIndex] else null
|
||||||
|
if (rec != null && rec.type == ObjRecord.Type.Fun && !rec.isAbstract) {
|
||||||
|
if (!visibility.isPublic && !canAccessMember(visibility, decl, sc.currentClassCtx, name))
|
||||||
|
sc.raiseError(ObjIllegalAccessException(sc, "can't invoke non-public method $name"))
|
||||||
|
rec.value.invoke(inst.instanceScope, inst, a, decl)
|
||||||
|
} else {
|
||||||
|
obj.invokeInstanceMethod(sc, name, a)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
obj.invokeInstanceMethod(sc, name, a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
val callable = targetRec.value
|
||||||
if (hierarchyMember != null) {
|
|
||||||
val visibility = hierarchyMember.visibility
|
|
||||||
val callable = hierarchyMember.value
|
|
||||||
val decl = hierarchyMember.declaringClass ?: base.objClass
|
|
||||||
mKey1 = key; mVer1 = ver; mInvoker1 = { obj, sc, a ->
|
mKey1 = key; mVer1 = ver; mInvoker1 = { obj, sc, a ->
|
||||||
val inst = obj as ObjInstance
|
val inst = obj as ObjInstance
|
||||||
if (!visibility.isPublic && !canAccessMember(visibility, decl, sc.currentClassCtx, name))
|
if (!visibility.isPublic && !canAccessMember(visibility, decl, sc.currentClassCtx, name))
|
||||||
sc.raiseError(ObjIllegalAccessException(sc, "can't invoke non-public method $name"))
|
sc.raiseError(ObjIllegalAccessException(sc, "can't invoke non-public method $name"))
|
||||||
callable.invoke(inst.instanceScope, inst, a)
|
callable.invoke(inst.instanceScope, inst, a)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fallback to name-based lookup per call (handles extensions and root members)
|
// Fallback to name-based lookup per call (handles extensions and root members)
|
||||||
mKey1 = key; mVer1 = ver; mInvoker1 = { obj, sc, a -> obj.invokeInstanceMethod(sc, name, a) }
|
mKey1 = key; mVer1 = ver; mInvoker1 = { obj, sc, a -> obj.invokeInstanceMethod(sc, name, a) }
|
||||||
@ -1399,6 +1727,57 @@ class MethodCallRef(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fast path for direct `this.method(...)` calls using slot maps.
|
||||||
|
* Falls back to normal invoke semantics when needed.
|
||||||
|
*/
|
||||||
|
class ThisMethodSlotCallRef(
|
||||||
|
private val name: String,
|
||||||
|
private val args: List<ParsedArgument>,
|
||||||
|
private val tailBlock: Boolean,
|
||||||
|
private val isOptional: Boolean
|
||||||
|
) : ObjRef {
|
||||||
|
override suspend fun get(scope: Scope): ObjRecord = evalValue(scope).asReadonly
|
||||||
|
|
||||||
|
override suspend fun evalValue(scope: Scope): Obj {
|
||||||
|
val base = scope.thisObj
|
||||||
|
if (base == ObjNull && isOptional) return ObjNull
|
||||||
|
val callArgs = args.toArguments(scope, tailBlock)
|
||||||
|
if (base !is ObjInstance) return base.invokeInstanceMethod(scope, name, callArgs)
|
||||||
|
|
||||||
|
val caller = scope.currentClassCtx
|
||||||
|
if (caller != null) {
|
||||||
|
val mangled = caller.mangledName(name)
|
||||||
|
base.methodRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (rec.visibility == Visibility.Private && !rec.isAbstract) {
|
||||||
|
if (rec.type == ObjRecord.Type.Property) {
|
||||||
|
if (callArgs.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, base, caller)
|
||||||
|
} else if (rec.type == ObjRecord.Type.Fun) {
|
||||||
|
return rec.value.invoke(base.instanceScope, base, callArgs, caller)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val key = base.objClass.publicMemberResolution[name] ?: name
|
||||||
|
base.methodRecordForKey(key)?.let { rec ->
|
||||||
|
if (!rec.isAbstract) {
|
||||||
|
val decl = rec.declaringClass ?: base.objClass.findDeclaringClassOf(name) ?: base.objClass
|
||||||
|
val effectiveCaller = caller ?: if (scope.thisObj === base) base.objClass else null
|
||||||
|
if (!canAccessMember(rec.visibility, decl, effectiveCaller, name))
|
||||||
|
scope.raiseError(ObjIllegalAccessException(scope, "can't invoke method $name (declared in ${decl.className})"))
|
||||||
|
if (rec.type == ObjRecord.Type.Property) {
|
||||||
|
if (callArgs.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, base, decl)
|
||||||
|
} else if (rec.type == ObjRecord.Type.Fun) {
|
||||||
|
return rec.value.invoke(base.instanceScope, base, callArgs, decl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.invokeInstanceMethod(scope, name, callArgs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to a local/visible variable by name (Phase A: scope lookup).
|
* Reference to a local/visible variable by name (Phase A: scope lookup).
|
||||||
*/
|
*/
|
||||||
@ -1729,6 +2108,240 @@ class FastLocalVarRef(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier reference in class context that prefers member slots on `this` after local lookup.
|
||||||
|
* Falls back to normal scope lookup for globals/outer scopes.
|
||||||
|
*/
|
||||||
|
class ImplicitThisMemberRef(
|
||||||
|
val name: String,
|
||||||
|
val atPos: Pos
|
||||||
|
) : ObjRef {
|
||||||
|
override fun forEachVariable(block: (String) -> Unit) {
|
||||||
|
block(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun forEachVariableWithPos(block: (String, Pos) -> Unit) {
|
||||||
|
block(name, atPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun get(scope: Scope): ObjRecord {
|
||||||
|
scope.pos = atPos
|
||||||
|
val caller = scope.currentClassCtx
|
||||||
|
val th = scope.thisObj
|
||||||
|
|
||||||
|
// 1) locals in the same `this` chain
|
||||||
|
var s: Scope? = scope
|
||||||
|
while (s != null && s.thisObj === th) {
|
||||||
|
scope.tryGetLocalRecord(s, name, caller)?.let { return it }
|
||||||
|
s = s.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) member slots on this instance
|
||||||
|
if (th is ObjInstance) {
|
||||||
|
// private member access for current class context
|
||||||
|
caller?.let { c ->
|
||||||
|
val mangled = c.mangledName(name)
|
||||||
|
th.fieldRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (rec.visibility == Visibility.Private) {
|
||||||
|
return th.resolveRecord(scope, rec, name, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
th.methodRecordForKey(mangled)?.let { rec ->
|
||||||
|
if (rec.visibility == Visibility.Private) {
|
||||||
|
return th.resolveRecord(scope, rec, name, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val key = th.objClass.publicMemberResolution[name] ?: name
|
||||||
|
th.fieldRecordForKey(key)?.let { rec ->
|
||||||
|
if ((rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField) && !rec.isAbstract)
|
||||||
|
return rec
|
||||||
|
}
|
||||||
|
th.methodRecordForKey(key)?.let { rec ->
|
||||||
|
if (!rec.isAbstract) {
|
||||||
|
val decl = rec.declaringClass ?: th.objClass.findDeclaringClassOf(name) ?: th.objClass
|
||||||
|
return th.resolveRecord(scope, rec, name, decl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) fallback to normal scope resolution (globals/outer scopes)
|
||||||
|
scope[name]?.let { return it }
|
||||||
|
try {
|
||||||
|
return th.readField(scope, name)
|
||||||
|
} catch (e: ExecutionError) {
|
||||||
|
if ((e.message ?: "").contains("no such field: $name")) scope.raiseSymbolNotFound(name)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun evalValue(scope: Scope): Obj {
|
||||||
|
val rec = get(scope)
|
||||||
|
return scope.resolve(rec, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setAt(pos: Pos, scope: Scope, newValue: Obj) {
|
||||||
|
scope.pos = atPos
|
||||||
|
val caller = scope.currentClassCtx
|
||||||
|
val th = scope.thisObj
|
||||||
|
|
||||||
|
// 1) locals in the same `this` chain
|
||||||
|
var s: Scope? = scope
|
||||||
|
while (s != null && s.thisObj === th) {
|
||||||
|
val rec = scope.tryGetLocalRecord(s, name, caller)
|
||||||
|
if (rec != null) {
|
||||||
|
scope.assign(rec, name, newValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s = s.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) member slots on this instance
|
||||||
|
if (th is ObjInstance) {
|
||||||
|
val key = th.objClass.publicMemberResolution[name] ?: name
|
||||||
|
th.fieldRecordForKey(key)?.let { rec ->
|
||||||
|
val decl = rec.declaringClass ?: th.objClass.findDeclaringClassOf(name)
|
||||||
|
if (canAccessMember(rec.effectiveWriteVisibility, decl, caller, name)) {
|
||||||
|
if ((rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField) && !rec.isAbstract) {
|
||||||
|
if (!rec.isMutable && rec.value !== ObjUnset) {
|
||||||
|
ObjIllegalAssignmentException(scope, "can't reassign val $name").raise()
|
||||||
|
}
|
||||||
|
if (rec.value.assign(scope, newValue) == null) rec.value = newValue
|
||||||
|
} else {
|
||||||
|
th.writeField(scope, name, newValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
th.methodRecordForKey(key)?.let { rec ->
|
||||||
|
if (rec.effectiveWriteVisibility == Visibility.Public &&
|
||||||
|
(rec.type == ObjRecord.Type.Property || rec.type == ObjRecord.Type.Delegated)) {
|
||||||
|
th.writeField(scope, name, newValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) fallback to normal scope resolution
|
||||||
|
scope[name]?.let { stored ->
|
||||||
|
scope.assign(stored, name, newValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
th.writeField(scope, name, newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fast path for implicit member calls in class bodies: `foo(...)` resolves locals first,
|
||||||
|
* then falls back to member lookup on `this`.
|
||||||
|
*/
|
||||||
|
class ImplicitThisMethodCallRef(
|
||||||
|
private val name: String,
|
||||||
|
private val args: List<ParsedArgument>,
|
||||||
|
private val tailBlock: Boolean,
|
||||||
|
private val isOptional: Boolean,
|
||||||
|
private val atPos: Pos
|
||||||
|
) : ObjRef {
|
||||||
|
private val memberRef = ImplicitThisMemberRef(name, atPos)
|
||||||
|
|
||||||
|
override suspend fun get(scope: Scope): ObjRecord = evalValue(scope).asReadonly
|
||||||
|
|
||||||
|
override suspend fun evalValue(scope: Scope): Obj {
|
||||||
|
scope.pos = atPos
|
||||||
|
val callee = memberRef.evalValue(scope)
|
||||||
|
if (callee == ObjNull && isOptional) return ObjNull
|
||||||
|
val callArgs = args.toArguments(scope, tailBlock)
|
||||||
|
val usePool = PerfFlags.SCOPE_POOL
|
||||||
|
return if (usePool) {
|
||||||
|
scope.withChildFrame(callArgs) { child ->
|
||||||
|
callee.callOn(child)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callee.callOn(scope.createChildScope(scope.pos, callArgs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Direct local slot reference with known slot index and lexical depth.
|
||||||
|
* Depth=0 means current scope, depth=1 means parent scope, etc.
|
||||||
|
*/
|
||||||
|
class LocalSlotRef(
|
||||||
|
val name: String,
|
||||||
|
private val slot: Int,
|
||||||
|
private val depth: Int,
|
||||||
|
private val atPos: Pos,
|
||||||
|
) : ObjRef {
|
||||||
|
override fun forEachVariable(block: (String) -> Unit) {
|
||||||
|
block(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val fallbackRef = LocalVarRef(name, atPos)
|
||||||
|
private var cachedFrameId: Long = 0L
|
||||||
|
private var cachedOwner: Scope? = null
|
||||||
|
private var cachedOwnerVerified: Boolean = false
|
||||||
|
|
||||||
|
private fun resolveOwner(scope: Scope): Scope? {
|
||||||
|
if (cachedOwner != null && cachedFrameId == scope.frameId && cachedOwnerVerified) return cachedOwner
|
||||||
|
var s: Scope? = scope
|
||||||
|
var remaining = depth
|
||||||
|
while (s != null && remaining > 0) {
|
||||||
|
s = s.parent
|
||||||
|
remaining--
|
||||||
|
}
|
||||||
|
if (s == null || s.getSlotIndexOf(name) != slot) {
|
||||||
|
cachedOwner = null
|
||||||
|
cachedOwnerVerified = false
|
||||||
|
cachedFrameId = scope.frameId
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
cachedOwner = s
|
||||||
|
cachedOwnerVerified = true
|
||||||
|
cachedFrameId = scope.frameId
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun get(scope: Scope): ObjRecord {
|
||||||
|
scope.pos = atPos
|
||||||
|
val owner = resolveOwner(scope) ?: return fallbackRef.get(scope)
|
||||||
|
if (slot < 0 || slot >= owner.slotCount()) return fallbackRef.get(scope)
|
||||||
|
val rec = owner.getSlotRecord(slot)
|
||||||
|
if (rec.declaringClass != null && !canAccessMember(rec.visibility, rec.declaringClass, scope.currentClassCtx, name)) {
|
||||||
|
scope.raiseError(ObjIllegalAccessException(scope, "private field access"))
|
||||||
|
}
|
||||||
|
return rec
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun evalValue(scope: Scope): Obj {
|
||||||
|
scope.pos = atPos
|
||||||
|
val owner = resolveOwner(scope) ?: return fallbackRef.evalValue(scope)
|
||||||
|
if (slot < 0 || slot >= owner.slotCount()) return fallbackRef.evalValue(scope)
|
||||||
|
val rec = owner.getSlotRecord(slot)
|
||||||
|
if (rec.declaringClass != null && !canAccessMember(rec.visibility, rec.declaringClass, scope.currentClassCtx, name)) {
|
||||||
|
scope.raiseError(ObjIllegalAccessException(scope, "private field access"))
|
||||||
|
}
|
||||||
|
return scope.resolve(rec, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setAt(pos: Pos, scope: Scope, newValue: Obj) {
|
||||||
|
scope.pos = atPos
|
||||||
|
val owner = resolveOwner(scope) ?: run {
|
||||||
|
fallbackRef.setAt(pos, scope, newValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (slot < 0 || slot >= owner.slotCount()) {
|
||||||
|
fallbackRef.setAt(pos, scope, newValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val rec = owner.getSlotRecord(slot)
|
||||||
|
if (rec.declaringClass != null && !canAccessMember(rec.visibility, rec.declaringClass, scope.currentClassCtx, name)) {
|
||||||
|
scope.raiseError(ObjIllegalAccessException(scope, "private field access"))
|
||||||
|
}
|
||||||
|
scope.assign(rec, name, newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ListLiteralRef(private val entries: List<ListEntry>) : ObjRef {
|
class ListLiteralRef(private val entries: List<ListEntry>) : ObjRef {
|
||||||
override fun forEachVariable(block: (String) -> Unit) {
|
override fun forEachVariable(block: (String) -> Unit) {
|
||||||
for (e in entries) {
|
for (e in entries) {
|
||||||
@ -1910,7 +2523,15 @@ class AssignRef(
|
|||||||
val v = value.evalValue(scope)
|
val v = value.evalValue(scope)
|
||||||
// For properties, we should not call get() on target because it invokes the getter.
|
// For properties, we should not call get() on target because it invokes the getter.
|
||||||
// Instead, we call setAt directly.
|
// Instead, we call setAt directly.
|
||||||
if (target is FieldRef || target is IndexRef || target is LocalVarRef || target is FastLocalVarRef || target is BoundLocalVarRef) {
|
if (target is FieldRef ||
|
||||||
|
target is IndexRef ||
|
||||||
|
target is LocalVarRef ||
|
||||||
|
target is FastLocalVarRef ||
|
||||||
|
target is BoundLocalVarRef ||
|
||||||
|
target is LocalSlotRef ||
|
||||||
|
target is ThisFieldSlotRef ||
|
||||||
|
target is QualifiedThisFieldSlotRef ||
|
||||||
|
target is ImplicitThisMemberRef) {
|
||||||
target.setAt(atPos, scope, v)
|
target.setAt(atPos, scope, v)
|
||||||
} else {
|
} else {
|
||||||
val rec = target.get(scope)
|
val rec = target.get(scope)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user