Step 24E: frame capture refs
This commit is contained in:
parent
c14c7d43d9
commit
99ca15d20f
@ -106,9 +106,9 @@ Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM te
|
|||||||
- [x] Keep interpreter path using `ClosureScope` until interpreter removal.
|
- [x] Keep interpreter path using `ClosureScope` until interpreter removal.
|
||||||
- [x] JVM tests must be green before commit.
|
- [x] JVM tests must be green before commit.
|
||||||
- [x] Step 24E: Isolate interpreter-only capture logic.
|
- [x] Step 24E: Isolate interpreter-only capture logic.
|
||||||
- [ ] Mark `resolveCaptureRecord` paths as interpreter-only.
|
- [x] Mark `resolveCaptureRecord` paths as interpreter-only.
|
||||||
- [ ] Guard or delete any bytecode path that tries to sync captures into scopes.
|
- [x] Guard or delete any bytecode path that tries to sync captures into scopes.
|
||||||
- [ ] JVM tests must be green before commit.
|
- [x] JVM tests must be green before commit.
|
||||||
|
|
||||||
## Interpreter Removal (next)
|
## Interpreter Removal (next)
|
||||||
|
|
||||||
|
|||||||
@ -31,23 +31,33 @@ class BlockStatement(
|
|||||||
val target = if (scope.skipScopeCreation) scope else scope.createChildScope(startPos)
|
val target = if (scope.skipScopeCreation) scope else scope.createChildScope(startPos)
|
||||||
if (slotPlan.isNotEmpty()) target.applySlotPlan(slotPlan)
|
if (slotPlan.isNotEmpty()) target.applySlotPlan(slotPlan)
|
||||||
if (captureSlots.isNotEmpty()) {
|
if (captureSlots.isNotEmpty()) {
|
||||||
val applyScope = scope as? ApplyScope
|
val captureRecords = scope.captureRecords
|
||||||
for (capture in captureSlots) {
|
if (captureRecords != null) {
|
||||||
// Interpreter-only capture resolution; bytecode paths must use captureRecords instead.
|
for (i in captureSlots.indices) {
|
||||||
val rec = if (applyScope != null) {
|
val capture = captureSlots[i]
|
||||||
applyScope.resolveCaptureRecord(capture.name)
|
val rec = captureRecords.getOrNull(i)
|
||||||
?: applyScope.callScope.resolveCaptureRecord(capture.name)
|
?: scope.raiseSymbolNotFound("capture ${capture.name} not found")
|
||||||
} else {
|
target.updateSlotFor(capture.name, rec)
|
||||||
scope.resolveCaptureRecord(capture.name)
|
|
||||||
}
|
}
|
||||||
if (rec == null) {
|
} else {
|
||||||
if (scope.getSlotIndexOf(capture.name) == null && scope.getLocalRecordDirect(capture.name) == null) {
|
val applyScope = scope as? ApplyScope
|
||||||
continue
|
for (capture in captureSlots) {
|
||||||
|
// Interpreter-only capture resolution; bytecode paths must use captureRecords instead.
|
||||||
|
val rec = if (applyScope != null) {
|
||||||
|
applyScope.resolveCaptureRecord(capture.name)
|
||||||
|
?: applyScope.callScope.resolveCaptureRecord(capture.name)
|
||||||
|
} else {
|
||||||
|
scope.resolveCaptureRecord(capture.name)
|
||||||
}
|
}
|
||||||
(applyScope?.callScope ?: scope)
|
if (rec == null) {
|
||||||
.raiseSymbolNotFound("symbol ${capture.name} not found")
|
if (scope.getSlotIndexOf(capture.name) == null && scope.getLocalRecordDirect(capture.name) == null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
(applyScope?.callScope ?: scope)
|
||||||
|
.raiseSymbolNotFound("symbol ${capture.name} not found")
|
||||||
|
}
|
||||||
|
target.updateSlotFor(capture.name, rec)
|
||||||
}
|
}
|
||||||
target.updateSlotFor(capture.name, rec)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return block.execute(target)
|
return block.execute(target)
|
||||||
|
|||||||
@ -1705,9 +1705,12 @@ class Compiler(
|
|||||||
private val currentRangeParamNames: Set<String>
|
private val currentRangeParamNames: Set<String>
|
||||||
get() = rangeParamNamesStack.lastOrNull() ?: emptySet()
|
get() = rangeParamNamesStack.lastOrNull() ?: emptySet()
|
||||||
private val capturePlanStack = mutableListOf<CapturePlan>()
|
private val capturePlanStack = mutableListOf<CapturePlan>()
|
||||||
|
private var lambdaDepth = 0
|
||||||
|
|
||||||
private data class CapturePlan(
|
private data class CapturePlan(
|
||||||
val slotPlan: SlotPlan,
|
val slotPlan: SlotPlan,
|
||||||
|
val isFunction: Boolean,
|
||||||
|
val propagateToParentFunction: Boolean,
|
||||||
val captures: MutableList<CaptureSlot> = mutableListOf(),
|
val captures: MutableList<CaptureSlot> = mutableListOf(),
|
||||||
val captureMap: MutableMap<String, CaptureSlot> = mutableMapOf(),
|
val captureMap: MutableMap<String, CaptureSlot> = mutableMapOf(),
|
||||||
val captureOwners: MutableMap<String, SlotLocation> = mutableMapOf()
|
val captureOwners: MutableMap<String, SlotLocation> = mutableMapOf()
|
||||||
@ -1715,6 +1718,19 @@ class Compiler(
|
|||||||
|
|
||||||
private fun recordCaptureSlot(name: String, slotLoc: SlotLocation) {
|
private fun recordCaptureSlot(name: String, slotLoc: SlotLocation) {
|
||||||
val plan = capturePlanStack.lastOrNull() ?: return
|
val plan = capturePlanStack.lastOrNull() ?: return
|
||||||
|
recordCaptureSlotInto(plan, name, slotLoc)
|
||||||
|
if (plan.propagateToParentFunction) {
|
||||||
|
for (i in capturePlanStack.size - 2 downTo 0) {
|
||||||
|
val parent = capturePlanStack[i]
|
||||||
|
if (parent.isFunction) {
|
||||||
|
recordCaptureSlotInto(parent, name, slotLoc)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun recordCaptureSlotInto(plan: CapturePlan, name: String, slotLoc: SlotLocation) {
|
||||||
if (plan.captureMap.containsKey(name)) return
|
if (plan.captureMap.containsKey(name)) return
|
||||||
val capture = CaptureSlot(
|
val capture = CaptureSlot(
|
||||||
name = name,
|
name = name,
|
||||||
@ -1734,6 +1750,12 @@ class Compiler(
|
|||||||
|
|
||||||
private fun captureLocalRef(name: String, slotLoc: SlotLocation, pos: Pos): LocalSlotRef? {
|
private fun captureLocalRef(name: String, slotLoc: SlotLocation, pos: Pos): LocalSlotRef? {
|
||||||
if (capturePlanStack.isEmpty() || slotLoc.depth == 0) return null
|
if (capturePlanStack.isEmpty() || slotLoc.depth == 0) return null
|
||||||
|
val functionPlan = capturePlanStack.asReversed().firstOrNull { it.isFunction } ?: return null
|
||||||
|
val functionIndex = functionPlan?.let { plan ->
|
||||||
|
slotPlanStack.indexOfLast { it.id == plan.slotPlan.id }
|
||||||
|
} ?: -1
|
||||||
|
val scopeIndex = slotPlanStack.indexOfLast { it.id == slotLoc.scopeId }
|
||||||
|
if (functionIndex >= 0 && scopeIndex >= functionIndex) return null
|
||||||
val moduleId = moduleSlotPlan()?.id
|
val moduleId = moduleSlotPlan()?.id
|
||||||
if (moduleId != null && slotLoc.scopeId == moduleId) return null
|
if (moduleId != null && slotLoc.scopeId == moduleId) return null
|
||||||
recordCaptureSlot(name, slotLoc)
|
recordCaptureSlot(name, slotLoc)
|
||||||
@ -2158,6 +2180,7 @@ class Compiler(
|
|||||||
stmt.label,
|
stmt.label,
|
||||||
stmt.canBreak,
|
stmt.canBreak,
|
||||||
stmt.loopSlotPlan,
|
stmt.loopSlotPlan,
|
||||||
|
stmt.loopScopeId,
|
||||||
stmt.pos
|
stmt.pos
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -2843,7 +2866,7 @@ class Compiler(
|
|||||||
|
|
||||||
label?.let { cc.labels.add(it) }
|
label?.let { cc.labels.add(it) }
|
||||||
slotPlanStack.add(paramSlotPlan)
|
slotPlanStack.add(paramSlotPlan)
|
||||||
val capturePlan = CapturePlan(paramSlotPlan)
|
val capturePlan = CapturePlan(paramSlotPlan, isFunction = true, propagateToParentFunction = false)
|
||||||
capturePlanStack.add(capturePlan)
|
capturePlanStack.add(capturePlan)
|
||||||
val parsedBody = try {
|
val parsedBody = try {
|
||||||
inCodeContext(CodeContext.Function("<lambda>", implicitThisMembers = true, implicitThisTypeName = expectedReceiverType)) {
|
inCodeContext(CodeContext.Function("<lambda>", implicitThisMembers = true, implicitThisTypeName = expectedReceiverType)) {
|
||||||
@ -2854,8 +2877,13 @@ class Compiler(
|
|||||||
for (param in slotParamNames) {
|
for (param in slotParamNames) {
|
||||||
resolutionSink?.declareSymbol(param, SymbolKind.PARAM, isMutable = false, pos = startPos)
|
resolutionSink?.declareSymbol(param, SymbolKind.PARAM, isMutable = false, pos = startPos)
|
||||||
}
|
}
|
||||||
withLocalNames(slotParamNames.toSet()) {
|
lambdaDepth += 1
|
||||||
parseBlock(skipLeadingBrace = true)
|
try {
|
||||||
|
withLocalNames(slotParamNames.toSet()) {
|
||||||
|
parseBlock(skipLeadingBrace = true)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lambdaDepth -= 1
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
resolutionSink?.exitScope(cc.currentPos())
|
resolutionSink?.exitScope(cc.currentPos())
|
||||||
@ -2872,25 +2900,56 @@ class Compiler(
|
|||||||
val paramSlotPlanSnapshot = slotPlanIndices(paramSlotPlan)
|
val paramSlotPlanSnapshot = slotPlanIndices(paramSlotPlan)
|
||||||
val captureSlots = capturePlan.captures.toList()
|
val captureSlots = capturePlan.captures.toList()
|
||||||
val returnClass = inferReturnClassFromStatement(body)
|
val returnClass = inferReturnClassFromStatement(body)
|
||||||
|
val paramKnownClasses = mutableMapOf<String, ObjClass>()
|
||||||
|
argsDeclaration?.params?.forEach { param ->
|
||||||
|
val cls = resolveTypeDeclObjClass(param.type) ?: return@forEach
|
||||||
|
paramKnownClasses[param.name] = cls
|
||||||
|
}
|
||||||
|
val returnLabels = label?.let { setOf(it) } ?: emptySet()
|
||||||
|
val fnStatements = if (useBytecodeStatements && !containsUnsupportedForBytecode(body)) {
|
||||||
|
returnLabelStack.addLast(returnLabels)
|
||||||
|
try {
|
||||||
|
wrapFunctionBytecode(body, "<lambda>", paramKnownClasses)
|
||||||
|
} catch (e: net.sergeych.lyng.bytecode.BytecodeCompileException) {
|
||||||
|
body
|
||||||
|
} finally {
|
||||||
|
returnLabelStack.removeLast()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
body
|
||||||
|
}
|
||||||
val ref = ValueFnRef { closureScope ->
|
val ref = ValueFnRef { closureScope ->
|
||||||
val stmt = object : Statement() {
|
val captureRecords = closureScope.captureRecords
|
||||||
override val pos: Pos = body.pos
|
val stmt = object : Statement(), BytecodeBodyProvider {
|
||||||
|
override val pos: Pos = fnStatements.pos
|
||||||
|
|
||||||
|
override fun bytecodeBody(): BytecodeStatement? = fnStatements as? BytecodeStatement
|
||||||
|
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
// and the source closure of the lambda which might have other thisObj.
|
// TODO(bytecode): remove this fallback once lambdas are fully bytecode-backed (Step 26).
|
||||||
val useBytecodeClosure = closureScope.captureRecords != null
|
val usesBytecodeBody = fnStatements is BytecodeStatement &&
|
||||||
val context = if (useBytecodeClosure) {
|
(captureSlots.isEmpty() || captureRecords != null)
|
||||||
scope.applyClosureForBytecode(closureScope, preferredThisType = expectedReceiverType)
|
val useBytecodeClosure = captureRecords != null && usesBytecodeBody
|
||||||
|
val context = if (usesBytecodeBody) {
|
||||||
|
scope.applyClosureForBytecode(closureScope, preferredThisType = expectedReceiverType).also {
|
||||||
|
it.args = scope.args
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
scope.applyClosure(closureScope, preferredThisType = expectedReceiverType)
|
scope.applyClosure(closureScope, preferredThisType = expectedReceiverType)
|
||||||
}
|
}
|
||||||
if (paramSlotPlanSnapshot.isNotEmpty()) context.applySlotPlan(paramSlotPlanSnapshot)
|
if (paramSlotPlanSnapshot.isNotEmpty()) context.applySlotPlan(paramSlotPlanSnapshot)
|
||||||
if (captureSlots.isNotEmpty()) {
|
if (captureSlots.isNotEmpty()) {
|
||||||
val captureRecords = closureScope.captureRecords
|
|
||||||
if (captureRecords != null) {
|
if (captureRecords != null) {
|
||||||
for (i in captureSlots.indices) {
|
val records = captureRecords as List<ObjRecord>
|
||||||
val rec = captureRecords.getOrNull(i)
|
if (useBytecodeClosure) {
|
||||||
?: closureScope.raiseSymbolNotFound("capture ${captureSlots[i].name} not found")
|
context.captureRecords = records
|
||||||
context.updateSlotFor(captureSlots[i].name, rec)
|
context.captureNames = captureSlots.map { it.name }
|
||||||
|
} else {
|
||||||
|
for (i in captureSlots.indices) {
|
||||||
|
val rec = records.getOrNull(i)
|
||||||
|
?: closureScope.raiseSymbolNotFound("capture ${captureSlots[i].name} not found")
|
||||||
|
context.updateSlotFor(captureSlots[i].name, rec)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val moduleScope = if (context is ApplyScope) {
|
val moduleScope = if (context is ApplyScope) {
|
||||||
@ -2902,38 +2961,44 @@ class Compiler(
|
|||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
val usesBytecodeBody = fnStatements is BytecodeStatement
|
||||||
|
val resolvedRecords = if (usesBytecodeBody) ArrayList<ObjRecord>() else null
|
||||||
|
val resolvedNames = if (usesBytecodeBody) ArrayList<String>() else null
|
||||||
for (capture in captureSlots) {
|
for (capture in captureSlots) {
|
||||||
if (moduleScope != null && moduleScope.getLocalRecordDirect(capture.name) != null) {
|
if (moduleScope != null && moduleScope.getLocalRecordDirect(capture.name) != null) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val rec = closureScope.resolveCaptureRecord(capture.name)
|
val rec = closureScope.resolveCaptureRecord(capture.name)
|
||||||
?: closureScope.raiseSymbolNotFound("symbol ${capture.name} not found")
|
?: closureScope.raiseSymbolNotFound("symbol ${capture.name} not found")
|
||||||
context.updateSlotFor(capture.name, rec)
|
if (usesBytecodeBody) {
|
||||||
|
resolvedRecords?.add(rec)
|
||||||
|
resolvedNames?.add(capture.name)
|
||||||
|
} else {
|
||||||
|
context.updateSlotFor(capture.name, rec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (usesBytecodeBody) {
|
||||||
|
context.captureRecords = resolvedRecords
|
||||||
|
context.captureNames = resolvedNames
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Execute lambda body in a closure-aware context. Blocks inside the lambda
|
|
||||||
// will create child scopes as usual, so re-declarations inside loops work.
|
|
||||||
if (argsDeclaration == null) {
|
if (argsDeclaration == null) {
|
||||||
// no args: automatic var 'it'
|
|
||||||
val l = scope.args.list
|
val l = scope.args.list
|
||||||
val itValue: Obj = when (l.size) {
|
val itValue: Obj = when (l.size) {
|
||||||
// no args: it == void
|
|
||||||
0 -> ObjVoid
|
0 -> ObjVoid
|
||||||
// one args: it is this arg
|
|
||||||
1 -> l[0]
|
1 -> l[0]
|
||||||
// more args: it is a list of args
|
|
||||||
else -> ObjList(l.toMutableList())
|
else -> ObjList(l.toMutableList())
|
||||||
}
|
}
|
||||||
context.addItem("it", false, itValue, recordType = ObjRecord.Type.Argument)
|
context.addItem("it", false, itValue, recordType = ObjRecord.Type.Argument)
|
||||||
} else {
|
} else {
|
||||||
// assign vars as declared the standard way
|
argsDeclaration.assignToContext(context, scope.args, defaultAccessType = AccessType.Val)
|
||||||
argsDeclaration.assignToContext(context, defaultAccessType = AccessType.Val)
|
|
||||||
}
|
}
|
||||||
|
val effectiveStatements = if (usesBytecodeBody) fnStatements else body
|
||||||
return try {
|
return try {
|
||||||
body.execute(context)
|
effectiveStatements.execute(context)
|
||||||
} catch (e: ReturnException) {
|
} catch (e: ReturnException) {
|
||||||
if (e.label == null || e.label == label) e.result
|
if (e.label == null || returnLabels.contains(e.label)) e.result
|
||||||
else throw e
|
else throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2948,22 +3013,27 @@ class Compiler(
|
|||||||
if (returnClass != null) {
|
if (returnClass != null) {
|
||||||
lambdaReturnTypeByRef[ref] = returnClass
|
lambdaReturnTypeByRef[ref] = returnClass
|
||||||
}
|
}
|
||||||
val moduleScopeId = moduleSlotPlan()?.id
|
if (captureSlots.isNotEmpty()) {
|
||||||
val captureEntries = captureSlots.map { capture ->
|
val moduleScopeId = moduleSlotPlan()?.id
|
||||||
val owner = capturePlan.captureOwners[capture.name]
|
val captureEntries = captureSlots.map { capture ->
|
||||||
?: error("Missing capture owner for ${capture.name}")
|
val owner = capturePlan.captureOwners[capture.name]
|
||||||
val kind = if (moduleScopeId != null && owner.scopeId == moduleScopeId) {
|
?: error("Missing capture owner for ${capture.name}")
|
||||||
net.sergeych.lyng.bytecode.CaptureOwnerFrameKind.MODULE
|
val kind = if (moduleScopeId != null && owner.scopeId == moduleScopeId) {
|
||||||
} else {
|
net.sergeych.lyng.bytecode.CaptureOwnerFrameKind.MODULE
|
||||||
net.sergeych.lyng.bytecode.CaptureOwnerFrameKind.LOCAL
|
} else {
|
||||||
|
net.sergeych.lyng.bytecode.CaptureOwnerFrameKind.LOCAL
|
||||||
|
}
|
||||||
|
net.sergeych.lyng.bytecode.LambdaCaptureEntry(
|
||||||
|
ownerKind = kind,
|
||||||
|
ownerScopeId = owner.scopeId,
|
||||||
|
ownerSlotId = owner.slot,
|
||||||
|
ownerName = capture.name,
|
||||||
|
ownerIsMutable = owner.isMutable,
|
||||||
|
ownerIsDelegated = owner.isDelegated
|
||||||
|
)
|
||||||
}
|
}
|
||||||
net.sergeych.lyng.bytecode.LambdaCaptureEntry(
|
lambdaCaptureEntriesByRef[ref] = captureEntries
|
||||||
ownerKind = kind,
|
|
||||||
ownerScopeId = owner.scopeId,
|
|
||||||
ownerSlotId = owner.slot
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
lambdaCaptureEntriesByRef[ref] = captureEntries
|
|
||||||
return ref
|
return ref
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3039,7 +3109,7 @@ class Compiler(
|
|||||||
return when (t.value) {
|
return when (t.value) {
|
||||||
"class" -> {
|
"class" -> {
|
||||||
val ref = ValueFnRef { scope ->
|
val ref = ValueFnRef { scope ->
|
||||||
operand.get(scope).value.objClass.asReadonly
|
operand.evalValue(scope).objClass.asReadonly
|
||||||
}
|
}
|
||||||
lambdaReturnTypeByRef[ref] = ObjClassType
|
lambdaReturnTypeByRef[ref] = ObjClassType
|
||||||
ref
|
ref
|
||||||
@ -4162,6 +4232,7 @@ class Compiler(
|
|||||||
val payload = inferEncodedPayloadClass(ref.args)
|
val payload = inferEncodedPayloadClass(ref.args)
|
||||||
if (payload != null) return payload
|
if (payload != null) return payload
|
||||||
}
|
}
|
||||||
|
val receiverClass = resolveReceiverClassForMember(ref.receiver)
|
||||||
return inferMethodCallReturnClass(ref.name)
|
return inferMethodCallReturnClass(ref.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6393,6 +6464,7 @@ class Compiler(
|
|||||||
label = label,
|
label = label,
|
||||||
canBreak = canBreak,
|
canBreak = canBreak,
|
||||||
loopSlotPlan = loopSlotPlanSnapshot,
|
loopSlotPlan = loopSlotPlanSnapshot,
|
||||||
|
loopScopeId = loopSlotPlan.id,
|
||||||
pos = body.pos
|
pos = body.pos
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -6818,7 +6890,7 @@ class Compiler(
|
|||||||
val typeParamNames = mergedTypeParamDecls.map { it.name }
|
val typeParamNames = mergedTypeParamDecls.map { it.name }
|
||||||
val paramNames: Set<String> = paramNamesList.toSet()
|
val paramNames: Set<String> = paramNamesList.toSet()
|
||||||
val paramSlotPlan = buildParamSlotPlan(paramNamesList + typeParamNames)
|
val paramSlotPlan = buildParamSlotPlan(paramNamesList + typeParamNames)
|
||||||
val capturePlan = CapturePlan(paramSlotPlan)
|
val capturePlan = CapturePlan(paramSlotPlan, isFunction = true, propagateToParentFunction = false)
|
||||||
val rangeParamNames = argsDeclaration.params
|
val rangeParamNames = argsDeclaration.params
|
||||||
.filter { isRangeType(it.type) }
|
.filter { isRangeType(it.type) }
|
||||||
.map { it.name }
|
.map { it.name }
|
||||||
@ -6945,11 +7017,17 @@ class Compiler(
|
|||||||
if (paramSlotPlanSnapshot.isNotEmpty()) context.applySlotPlan(paramSlotPlanSnapshot)
|
if (paramSlotPlanSnapshot.isNotEmpty()) context.applySlotPlan(paramSlotPlanSnapshot)
|
||||||
val captureBase = closureBox.captureContext ?: closureBox.closure
|
val captureBase = closureBox.captureContext ?: closureBox.closure
|
||||||
if (captureBase != null && captureSlots.isNotEmpty()) {
|
if (captureBase != null && captureSlots.isNotEmpty()) {
|
||||||
for (capture in captureSlots) {
|
if (fnStatements is BytecodeStatement) {
|
||||||
// Interpreter-only capture resolution; bytecode functions do not use resolveCaptureRecord.
|
captureBase.raiseIllegalState(
|
||||||
val rec = captureBase.resolveCaptureRecord(capture.name)
|
"bytecode function captures require frame slots; scope capture resolution disabled"
|
||||||
?: captureBase.raiseSymbolNotFound("symbol ${capture.name} not found")
|
)
|
||||||
context.updateSlotFor(capture.name, rec)
|
} else {
|
||||||
|
for (capture in captureSlots) {
|
||||||
|
// Interpreter-only capture resolution; bytecode functions do not use resolveCaptureRecord.
|
||||||
|
val rec = captureBase.resolveCaptureRecord(capture.name)
|
||||||
|
?: captureBase.raiseSymbolNotFound("symbol ${capture.name} not found")
|
||||||
|
context.updateSlotFor(capture.name, rec)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7040,7 +7118,7 @@ class Compiler(
|
|||||||
resolutionSink?.declareSymbol(name, SymbolKind.LOCAL, isMutable, startPos, isOverride = false)
|
resolutionSink?.declareSymbol(name, SymbolKind.LOCAL, isMutable, startPos, isOverride = false)
|
||||||
}
|
}
|
||||||
slotPlanStack.add(exprSlotPlan)
|
slotPlanStack.add(exprSlotPlan)
|
||||||
val capturePlan = CapturePlan(exprSlotPlan)
|
val capturePlan = CapturePlan(exprSlotPlan, isFunction = false, propagateToParentFunction = lambdaDepth > 0)
|
||||||
capturePlanStack.add(capturePlan)
|
capturePlanStack.add(capturePlan)
|
||||||
val expr = try {
|
val expr = try {
|
||||||
parseExpression() ?: throw ScriptError(cc.current().pos, "Expected expression")
|
parseExpression() ?: throw ScriptError(cc.current().pos, "Expected expression")
|
||||||
@ -7062,7 +7140,7 @@ class Compiler(
|
|||||||
declareSlotNameIn(blockSlotPlan, name, isMutable, isDelegated = false)
|
declareSlotNameIn(blockSlotPlan, name, isMutable, isDelegated = false)
|
||||||
}
|
}
|
||||||
slotPlanStack.add(blockSlotPlan)
|
slotPlanStack.add(blockSlotPlan)
|
||||||
val capturePlan = CapturePlan(blockSlotPlan)
|
val capturePlan = CapturePlan(blockSlotPlan, isFunction = false, propagateToParentFunction = lambdaDepth > 0)
|
||||||
capturePlanStack.add(capturePlan)
|
capturePlanStack.add(capturePlan)
|
||||||
val expr = try {
|
val expr = try {
|
||||||
parseExpression() ?: throw ScriptError(cc.current().pos, "Expected expression")
|
parseExpression() ?: throw ScriptError(cc.current().pos, "Expected expression")
|
||||||
@ -7379,7 +7457,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
slotPlanStack.add(blockSlotPlan)
|
slotPlanStack.add(blockSlotPlan)
|
||||||
val capturePlan = CapturePlan(blockSlotPlan)
|
val capturePlan = CapturePlan(blockSlotPlan, isFunction = false, propagateToParentFunction = lambdaDepth > 0)
|
||||||
capturePlanStack.add(capturePlan)
|
capturePlanStack.add(capturePlan)
|
||||||
val block = try {
|
val block = try {
|
||||||
parseScript()
|
parseScript()
|
||||||
@ -7410,7 +7488,7 @@ class Compiler(
|
|||||||
resolutionSink?.enterScope(ScopeKind.BLOCK, startPos, null)
|
resolutionSink?.enterScope(ScopeKind.BLOCK, startPos, null)
|
||||||
val blockSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++)
|
val blockSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++)
|
||||||
slotPlanStack.add(blockSlotPlan)
|
slotPlanStack.add(blockSlotPlan)
|
||||||
val capturePlan = CapturePlan(blockSlotPlan)
|
val capturePlan = CapturePlan(blockSlotPlan, isFunction = false, propagateToParentFunction = lambdaDepth > 0)
|
||||||
capturePlanStack.add(capturePlan)
|
capturePlanStack.add(capturePlan)
|
||||||
val block = try {
|
val block = try {
|
||||||
parseScript()
|
parseScript()
|
||||||
@ -7440,7 +7518,7 @@ class Compiler(
|
|||||||
resolutionSink?.enterScope(ScopeKind.BLOCK, startPos, null)
|
resolutionSink?.enterScope(ScopeKind.BLOCK, startPos, null)
|
||||||
val blockSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++)
|
val blockSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++)
|
||||||
slotPlanStack.add(blockSlotPlan)
|
slotPlanStack.add(blockSlotPlan)
|
||||||
val capturePlan = CapturePlan(blockSlotPlan)
|
val capturePlan = CapturePlan(blockSlotPlan, isFunction = false, propagateToParentFunction = lambdaDepth > 0)
|
||||||
capturePlanStack.add(capturePlan)
|
capturePlanStack.add(capturePlan)
|
||||||
val block = try {
|
val block = try {
|
||||||
parseScript()
|
parseScript()
|
||||||
|
|||||||
@ -55,3 +55,16 @@ class FrameSlotRef(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RecordSlotRef(
|
||||||
|
private val record: ObjRecord,
|
||||||
|
) : net.sergeych.lyng.obj.Obj() {
|
||||||
|
fun read(): Obj {
|
||||||
|
val direct = record.value
|
||||||
|
return if (direct is FrameSlotRef) direct.read() else direct
|
||||||
|
}
|
||||||
|
|
||||||
|
fun write(value: Obj) {
|
||||||
|
record.value = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -60,6 +60,7 @@ open class Scope(
|
|||||||
private val slots: MutableList<ObjRecord> = mutableListOf()
|
private val slots: MutableList<ObjRecord> = mutableListOf()
|
||||||
private val nameToSlot: MutableMap<String, Int> = mutableMapOf()
|
private val nameToSlot: MutableMap<String, Int> = mutableMapOf()
|
||||||
internal var captureRecords: List<ObjRecord>? = null
|
internal var captureRecords: List<ObjRecord>? = null
|
||||||
|
internal var captureNames: List<String>? = null
|
||||||
/**
|
/**
|
||||||
* Auxiliary per-frame map of local bindings (locals declared in this frame).
|
* Auxiliary per-frame map of local bindings (locals declared in this frame).
|
||||||
* This helps resolving locals across suspension when slot ownership isn't
|
* This helps resolving locals across suspension when slot ownership isn't
|
||||||
|
|||||||
@ -53,10 +53,13 @@ class BytecodeCompiler(
|
|||||||
private val localSlotInfoMap = LinkedHashMap<ScopeSlotKey, LocalSlotInfo>()
|
private val localSlotInfoMap = LinkedHashMap<ScopeSlotKey, LocalSlotInfo>()
|
||||||
private val localSlotIndexByKey = LinkedHashMap<ScopeSlotKey, Int>()
|
private val localSlotIndexByKey = LinkedHashMap<ScopeSlotKey, Int>()
|
||||||
private val localSlotIndexByName = LinkedHashMap<String, Int>()
|
private val localSlotIndexByName = LinkedHashMap<String, Int>()
|
||||||
|
private val captureSlotKeys = LinkedHashSet<ScopeSlotKey>()
|
||||||
|
private val forcedObjSlots = LinkedHashSet<Int>()
|
||||||
private val loopSlotOverrides = LinkedHashMap<String, Int>()
|
private val loopSlotOverrides = LinkedHashMap<String, Int>()
|
||||||
private var localSlotNames = emptyArray<String?>()
|
private var localSlotNames = emptyArray<String?>()
|
||||||
private var localSlotMutables = BooleanArray(0)
|
private var localSlotMutables = BooleanArray(0)
|
||||||
private var localSlotDelegated = BooleanArray(0)
|
private var localSlotDelegated = BooleanArray(0)
|
||||||
|
private var localSlotCaptures = BooleanArray(0)
|
||||||
private val declaredLocalKeys = LinkedHashSet<ScopeSlotKey>()
|
private val declaredLocalKeys = LinkedHashSet<ScopeSlotKey>()
|
||||||
private val localRangeRefs = LinkedHashMap<ScopeSlotKey, RangeRef>()
|
private val localRangeRefs = LinkedHashMap<ScopeSlotKey, RangeRef>()
|
||||||
private val slotTypes = mutableMapOf<Int, SlotType>()
|
private val slotTypes = mutableMapOf<Int, SlotType>()
|
||||||
@ -65,6 +68,7 @@ class BytecodeCompiler(
|
|||||||
private val knownClassNames = knownNameObjClass.keys.toSet()
|
private val knownClassNames = knownNameObjClass.keys.toSet()
|
||||||
private val slotInitClassByKey = mutableMapOf<ScopeSlotKey, ObjClass>()
|
private val slotInitClassByKey = mutableMapOf<ScopeSlotKey, ObjClass>()
|
||||||
private val intLoopVarNames = LinkedHashSet<String>()
|
private val intLoopVarNames = LinkedHashSet<String>()
|
||||||
|
private val valueFnRefs = LinkedHashSet<ValueFnRef>()
|
||||||
private val loopStack = ArrayDeque<LoopContext>()
|
private val loopStack = ArrayDeque<LoopContext>()
|
||||||
private var forceScopeSlots = false
|
private var forceScopeSlots = false
|
||||||
private var currentPos: Pos? = null
|
private var currentPos: Pos? = null
|
||||||
@ -100,8 +104,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotNames,
|
scopeSlotNames,
|
||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
|
localSlotCaptures
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is BlockStatement -> compileBlock(name, stmt)
|
is BlockStatement -> compileBlock(name, stmt)
|
||||||
@ -120,8 +125,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotNames,
|
scopeSlotNames,
|
||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
|
localSlotCaptures
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is DestructuringVarDeclStatement -> {
|
is DestructuringVarDeclStatement -> {
|
||||||
@ -137,8 +143,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotNames,
|
scopeSlotNames,
|
||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
|
localSlotCaptures
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is net.sergeych.lyng.ThrowStatement -> compileThrowStatement(name, stmt)
|
is net.sergeych.lyng.ThrowStatement -> compileThrowStatement(name, stmt)
|
||||||
@ -156,8 +163,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotNames,
|
scopeSlotNames,
|
||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
|
localSlotCaptures
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is net.sergeych.lyng.ClassDeclStatement -> {
|
is net.sergeych.lyng.ClassDeclStatement -> {
|
||||||
@ -173,8 +181,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotNames,
|
scopeSlotNames,
|
||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
|
localSlotCaptures
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is net.sergeych.lyng.FunctionDeclStatement -> {
|
is net.sergeych.lyng.FunctionDeclStatement -> {
|
||||||
@ -190,8 +199,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotNames,
|
scopeSlotNames,
|
||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
|
localSlotCaptures
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is net.sergeych.lyng.EnumDeclStatement -> {
|
is net.sergeych.lyng.EnumDeclStatement -> {
|
||||||
@ -207,8 +217,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotNames,
|
scopeSlotNames,
|
||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
|
localSlotCaptures
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is net.sergeych.lyng.NopStatement -> {
|
is net.sergeych.lyng.NopStatement -> {
|
||||||
@ -225,8 +236,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotNames,
|
scopeSlotNames,
|
||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
|
localSlotCaptures
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else -> null
|
else -> null
|
||||||
@ -246,8 +258,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
)
|
localSlotCaptures
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compileExtensionPropertyDecl(
|
private fun compileExtensionPropertyDecl(
|
||||||
@ -268,8 +281,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
)
|
localSlotCaptures
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun compileExpression(name: String, stmt: ExpressionStatement): CmdFunction? {
|
fun compileExpression(name: String, stmt: ExpressionStatement): CmdFunction? {
|
||||||
@ -287,8 +301,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
)
|
localSlotCaptures
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class CompiledValue(val slot: Int, val type: SlotType)
|
private data class CompiledValue(val slot: Int, val type: SlotType)
|
||||||
@ -358,16 +373,16 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
if (allowLocalSlots) {
|
if (allowLocalSlots) {
|
||||||
if (!forceScopeSlots) {
|
if (!forceScopeSlots) {
|
||||||
scopeSlotIndexByName[ref.name]?.let { slot ->
|
|
||||||
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
|
|
||||||
return CompiledValue(slot, resolved)
|
|
||||||
}
|
|
||||||
val localIndex = localSlotIndexByName[ref.name]
|
val localIndex = localSlotIndexByName[ref.name]
|
||||||
if (localIndex != null) {
|
if (localIndex != null) {
|
||||||
val slot = scopeSlotCount + localIndex
|
val slot = scopeSlotCount + localIndex
|
||||||
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
|
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
|
||||||
return CompiledValue(slot, resolved)
|
return CompiledValue(slot, resolved)
|
||||||
}
|
}
|
||||||
|
scopeSlotIndexByName[ref.name]?.let { slot ->
|
||||||
|
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
|
||||||
|
return CompiledValue(slot, resolved)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (forceScopeSlots) {
|
if (forceScopeSlots) {
|
||||||
scopeSlotIndexByName[ref.name]?.let { slot ->
|
scopeSlotIndexByName[ref.name]?.let { slot ->
|
||||||
@ -388,16 +403,16 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
if (allowLocalSlots) {
|
if (allowLocalSlots) {
|
||||||
if (!forceScopeSlots) {
|
if (!forceScopeSlots) {
|
||||||
scopeSlotIndexByName[ref.name]?.let { slot ->
|
|
||||||
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
|
|
||||||
return CompiledValue(slot, resolved)
|
|
||||||
}
|
|
||||||
val localIndex = localSlotIndexByName[ref.name]
|
val localIndex = localSlotIndexByName[ref.name]
|
||||||
if (localIndex != null) {
|
if (localIndex != null) {
|
||||||
val slot = scopeSlotCount + localIndex
|
val slot = scopeSlotCount + localIndex
|
||||||
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
|
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
|
||||||
return CompiledValue(slot, resolved)
|
return CompiledValue(slot, resolved)
|
||||||
}
|
}
|
||||||
|
scopeSlotIndexByName[ref.name]?.let { slot ->
|
||||||
|
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
|
||||||
|
return CompiledValue(slot, resolved)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (forceScopeSlots) {
|
if (forceScopeSlots) {
|
||||||
scopeSlotIndexByName[ref.name]?.let { slot ->
|
scopeSlotIndexByName[ref.name]?.let { slot ->
|
||||||
@ -3970,8 +3985,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
)
|
localSlotCaptures
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compileForIn(name: String, stmt: net.sergeych.lyng.ForInStatement): CmdFunction? {
|
private fun compileForIn(name: String, stmt: net.sergeych.lyng.ForInStatement): CmdFunction? {
|
||||||
@ -3988,8 +4004,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
)
|
localSlotCaptures
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compileWhile(name: String, stmt: net.sergeych.lyng.WhileStatement): CmdFunction? {
|
private fun compileWhile(name: String, stmt: net.sergeych.lyng.WhileStatement): CmdFunction? {
|
||||||
@ -4007,8 +4024,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
)
|
localSlotCaptures
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compileDoWhile(name: String, stmt: net.sergeych.lyng.DoWhileStatement): CmdFunction? {
|
private fun compileDoWhile(name: String, stmt: net.sergeych.lyng.DoWhileStatement): CmdFunction? {
|
||||||
@ -4026,8 +4044,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
)
|
localSlotCaptures
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compileBlock(name: String, stmt: BlockStatement): CmdFunction? {
|
private fun compileBlock(name: String, stmt: BlockStatement): CmdFunction? {
|
||||||
@ -4048,8 +4067,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
)
|
localSlotCaptures
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compileVarDecl(name: String, stmt: VarDeclStatement): CmdFunction? {
|
private fun compileVarDecl(name: String, stmt: VarDeclStatement): CmdFunction? {
|
||||||
@ -4066,8 +4086,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
)
|
localSlotCaptures
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compileStatementValue(stmt: Statement): CompiledValue? {
|
private fun compileStatementValue(stmt: Statement): CompiledValue? {
|
||||||
@ -4520,8 +4541,9 @@ class BytecodeCompiler(
|
|||||||
scopeSlotIsModule,
|
scopeSlotIsModule,
|
||||||
localSlotNames,
|
localSlotNames,
|
||||||
localSlotMutables,
|
localSlotMutables,
|
||||||
localSlotDelegated
|
localSlotDelegated,
|
||||||
)
|
localSlotCaptures
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compileLoopBody(stmt: Statement, needResult: Boolean): CompiledValue? {
|
private fun compileLoopBody(stmt: Statement, needResult: Boolean): CompiledValue? {
|
||||||
@ -4788,7 +4810,9 @@ class BytecodeCompiler(
|
|||||||
val typedRangeLocal = if (range == null && rangeRef == null) extractTypedRangeLocal(stmt.source) else null
|
val typedRangeLocal = if (range == null && rangeRef == null) extractTypedRangeLocal(stmt.source) else null
|
||||||
val loopSlotPlan = stmt.loopSlotPlan
|
val loopSlotPlan = stmt.loopSlotPlan
|
||||||
var useLoopScope = loopSlotPlan.isNotEmpty()
|
var useLoopScope = loopSlotPlan.isNotEmpty()
|
||||||
val loopLocalIndex = localSlotIndexByName[stmt.loopVarName]
|
val loopSlotIndex = stmt.loopSlotPlan[stmt.loopVarName]
|
||||||
|
val loopKey = loopSlotIndex?.let { ScopeSlotKey(stmt.loopScopeId, it) }
|
||||||
|
val loopLocalIndex = loopKey?.let { localSlotIndexByKey[it] } ?: localSlotIndexByName[stmt.loopVarName]
|
||||||
var usedOverride = false
|
var usedOverride = false
|
||||||
var loopSlotId = when {
|
var loopSlotId = when {
|
||||||
loopLocalIndex != null -> scopeSlotCount + loopLocalIndex
|
loopLocalIndex != null -> scopeSlotCount + loopLocalIndex
|
||||||
@ -4905,7 +4929,7 @@ class BytecodeCompiler(
|
|||||||
val nextSlot = allocSlot()
|
val nextSlot = allocSlot()
|
||||||
builder.emit(Opcode.CALL_MEMBER_SLOT, iterSlot, nextMethodId, 0, 0, nextSlot)
|
builder.emit(Opcode.CALL_MEMBER_SLOT, iterSlot, nextMethodId, 0, 0, nextSlot)
|
||||||
val nextObj = ensureObjSlot(CompiledValue(nextSlot, SlotType.UNKNOWN))
|
val nextObj = ensureObjSlot(CompiledValue(nextSlot, SlotType.UNKNOWN))
|
||||||
builder.emit(Opcode.MOVE_OBJ, nextObj.slot, loopSlotId)
|
emitMove(CompiledValue(nextObj.slot, SlotType.OBJ), loopSlotId)
|
||||||
updateSlotType(loopSlotId, SlotType.OBJ)
|
updateSlotType(loopSlotId, SlotType.OBJ)
|
||||||
updateSlotTypeByName(stmt.loopVarName, SlotType.OBJ)
|
updateSlotTypeByName(stmt.loopVarName, SlotType.OBJ)
|
||||||
if (emitDeclLocal) {
|
if (emitDeclLocal) {
|
||||||
@ -5012,7 +5036,7 @@ class BytecodeCompiler(
|
|||||||
Opcode.JMP_IF_TRUE,
|
Opcode.JMP_IF_TRUE,
|
||||||
listOf(CmdBuilder.Operand.IntVal(cmpSlot), CmdBuilder.Operand.LabelRef(endLabel))
|
listOf(CmdBuilder.Operand.IntVal(cmpSlot), CmdBuilder.Operand.LabelRef(endLabel))
|
||||||
)
|
)
|
||||||
builder.emit(Opcode.MOVE_INT, iSlot, loopSlotId)
|
emitMove(CompiledValue(iSlot, SlotType.INT), loopSlotId)
|
||||||
updateSlotType(loopSlotId, SlotType.INT)
|
updateSlotType(loopSlotId, SlotType.INT)
|
||||||
updateSlotTypeByName(stmt.loopVarName, SlotType.INT)
|
updateSlotTypeByName(stmt.loopVarName, SlotType.INT)
|
||||||
if (emitDeclLocal) {
|
if (emitDeclLocal) {
|
||||||
@ -6084,16 +6108,22 @@ class BytecodeCompiler(
|
|||||||
scopeSlotIndexByName[ref.name]?.let { return it }
|
scopeSlotIndexByName[ref.name]?.let { return it }
|
||||||
}
|
}
|
||||||
if (ref.captureOwnerScopeId != null) {
|
if (ref.captureOwnerScopeId != null) {
|
||||||
val ownerKey = ScopeSlotKey(ref.captureOwnerScopeId, ref.captureOwnerSlot ?: refSlot(ref))
|
val scopeKey = ScopeSlotKey(refScopeId(ref), refSlot(ref))
|
||||||
val ownerLocal = localSlotIndexByKey[ownerKey]
|
val localIndex = localSlotIndexByKey[scopeKey]
|
||||||
if (ownerLocal != null) {
|
if (localIndex != null) {
|
||||||
return scopeSlotCount + ownerLocal
|
if (localSlotCaptures.getOrNull(localIndex) == true) {
|
||||||
|
return scopeSlotCount + localIndex
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val nameLocal = localSlotIndexByName[ref.name]
|
val nameLocal = localSlotIndexByName[ref.name]
|
||||||
if (nameLocal != null) {
|
if (nameLocal != null && localSlotCaptures.getOrNull(nameLocal) == true) {
|
||||||
return scopeSlotCount + nameLocal
|
return scopeSlotCount + nameLocal
|
||||||
}
|
}
|
||||||
val scopeKey = ScopeSlotKey(refScopeId(ref), refSlot(ref))
|
for (idx in localSlotNames.indices) {
|
||||||
|
if (localSlotNames[idx] != ref.name) continue
|
||||||
|
if (localSlotCaptures.getOrNull(idx) != true) continue
|
||||||
|
return scopeSlotCount + idx
|
||||||
|
}
|
||||||
return scopeSlotMap[scopeKey]
|
return scopeSlotMap[scopeKey]
|
||||||
}
|
}
|
||||||
if (ref.isDelegated) {
|
if (ref.isDelegated) {
|
||||||
@ -6117,6 +6147,7 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateSlotType(slot: Int, type: SlotType) {
|
private fun updateSlotType(slot: Int, type: SlotType) {
|
||||||
|
if (forcedObjSlots.contains(slot) && type != SlotType.OBJ) return
|
||||||
if (type == SlotType.UNKNOWN) {
|
if (type == SlotType.UNKNOWN) {
|
||||||
slotTypes.remove(slot)
|
slotTypes.remove(slot)
|
||||||
} else {
|
} else {
|
||||||
@ -6141,18 +6172,22 @@ class BytecodeCompiler(
|
|||||||
localSlotInfoMap.clear()
|
localSlotInfoMap.clear()
|
||||||
localSlotIndexByKey.clear()
|
localSlotIndexByKey.clear()
|
||||||
localSlotIndexByName.clear()
|
localSlotIndexByName.clear()
|
||||||
|
captureSlotKeys.clear()
|
||||||
|
forcedObjSlots.clear()
|
||||||
loopSlotOverrides.clear()
|
loopSlotOverrides.clear()
|
||||||
scopeSlotIndexByName.clear()
|
scopeSlotIndexByName.clear()
|
||||||
pendingScopeNameRefs.clear()
|
pendingScopeNameRefs.clear()
|
||||||
localSlotNames = emptyArray()
|
localSlotNames = emptyArray()
|
||||||
localSlotMutables = BooleanArray(0)
|
localSlotMutables = BooleanArray(0)
|
||||||
localSlotDelegated = BooleanArray(0)
|
localSlotDelegated = BooleanArray(0)
|
||||||
|
localSlotCaptures = BooleanArray(0)
|
||||||
declaredLocalKeys.clear()
|
declaredLocalKeys.clear()
|
||||||
localRangeRefs.clear()
|
localRangeRefs.clear()
|
||||||
intLoopVarNames.clear()
|
intLoopVarNames.clear()
|
||||||
|
valueFnRefs.clear()
|
||||||
addrSlotByScopeSlot.clear()
|
addrSlotByScopeSlot.clear()
|
||||||
loopStack.clear()
|
loopStack.clear()
|
||||||
forceScopeSlots = allowLocalSlots && containsValueFnRef(stmt)
|
forceScopeSlots = false
|
||||||
if (slotTypeByScopeId.isNotEmpty()) {
|
if (slotTypeByScopeId.isNotEmpty()) {
|
||||||
for ((scopeId, slots) in slotTypeByScopeId) {
|
for ((scopeId, slots) in slotTypeByScopeId) {
|
||||||
for ((slotIndex, cls) in slots) {
|
for ((slotIndex, cls) in slots) {
|
||||||
@ -6167,6 +6202,22 @@ class BytecodeCompiler(
|
|||||||
if (allowLocalSlots) {
|
if (allowLocalSlots) {
|
||||||
collectLoopSlotPlans(stmt, 0)
|
collectLoopSlotPlans(stmt, 0)
|
||||||
}
|
}
|
||||||
|
if (allowLocalSlots && valueFnRefs.isNotEmpty() && lambdaCaptureEntriesByRef.isNotEmpty()) {
|
||||||
|
for (ref in valueFnRefs) {
|
||||||
|
val entries = lambdaCaptureEntriesByRef[ref] ?: continue
|
||||||
|
for (entry in entries) {
|
||||||
|
if (entry.ownerKind != CaptureOwnerFrameKind.LOCAL) continue
|
||||||
|
val key = ScopeSlotKey(entry.ownerScopeId, entry.ownerSlotId)
|
||||||
|
if (!localSlotInfoMap.containsKey(key)) {
|
||||||
|
localSlotInfoMap[key] = LocalSlotInfo(
|
||||||
|
entry.ownerName,
|
||||||
|
entry.ownerIsMutable,
|
||||||
|
entry.ownerIsDelegated
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (pendingScopeNameRefs.isNotEmpty()) {
|
if (pendingScopeNameRefs.isNotEmpty()) {
|
||||||
val existingNames = HashSet<String>(scopeSlotNameMap.values)
|
val existingNames = HashSet<String>(scopeSlotNameMap.values)
|
||||||
var maxSlotIndex = scopeSlotMap.keys.maxOfOrNull { it.slot } ?: -1
|
var maxSlotIndex = scopeSlotMap.keys.maxOfOrNull { it.slot } ?: -1
|
||||||
@ -6209,6 +6260,21 @@ class BytecodeCompiler(
|
|||||||
localSlotMutables = mutables
|
localSlotMutables = mutables
|
||||||
localSlotDelegated = delegated
|
localSlotDelegated = delegated
|
||||||
}
|
}
|
||||||
|
localSlotCaptures = BooleanArray(localSlotNames.size)
|
||||||
|
if (captureSlotKeys.isNotEmpty()) {
|
||||||
|
for (key in captureSlotKeys) {
|
||||||
|
val localIndex = localSlotIndexByKey[key] ?: continue
|
||||||
|
val slot = scopeSlotCount + localIndex
|
||||||
|
localSlotCaptures[localIndex] = true
|
||||||
|
forcedObjSlots.add(slot)
|
||||||
|
slotTypes[slot] = SlotType.OBJ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i in localSlotNames.indices) {
|
||||||
|
if (localSlotCaptures.getOrNull(i) != true) continue
|
||||||
|
val name = localSlotNames[i] ?: continue
|
||||||
|
localSlotIndexByName[name] = i
|
||||||
|
}
|
||||||
if (scopeSlotCount > 0) {
|
if (scopeSlotCount > 0) {
|
||||||
for ((key, index) in scopeSlotMap) {
|
for ((key, index) in scopeSlotMap) {
|
||||||
val name = scopeSlotNameMap[key] ?: continue
|
val name = scopeSlotNameMap[key] ?: continue
|
||||||
@ -6572,12 +6638,12 @@ class BytecodeCompiler(
|
|||||||
val scopeId = refScopeId(ref)
|
val scopeId = refScopeId(ref)
|
||||||
val key = ScopeSlotKey(scopeId, refSlot(ref))
|
val key = ScopeSlotKey(scopeId, refSlot(ref))
|
||||||
if (ref.captureOwnerScopeId != null) {
|
if (ref.captureOwnerScopeId != null) {
|
||||||
if (!scopeSlotMap.containsKey(key)) {
|
if (allowLocalSlots) {
|
||||||
scopeSlotMap[key] = scopeSlotMap.size
|
if (!localSlotInfoMap.containsKey(key)) {
|
||||||
}
|
localSlotInfoMap[key] = LocalSlotInfo(ref.name, ref.isMutable, ref.isDelegated)
|
||||||
if (!scopeSlotNameMap.containsKey(key)) {
|
}
|
||||||
scopeSlotNameMap[key] = ref.name
|
|
||||||
}
|
}
|
||||||
|
captureSlotKeys.add(key)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val shouldLocalize = ref.isDelegated || !forceScopeSlots || intLoopVarNames.contains(ref.name)
|
val shouldLocalize = ref.isDelegated || !forceScopeSlots || intLoopVarNames.contains(ref.name)
|
||||||
@ -6622,12 +6688,12 @@ class BytecodeCompiler(
|
|||||||
val scopeId = refScopeId(target)
|
val scopeId = refScopeId(target)
|
||||||
val key = ScopeSlotKey(scopeId, refSlot(target))
|
val key = ScopeSlotKey(scopeId, refSlot(target))
|
||||||
if (target.captureOwnerScopeId != null) {
|
if (target.captureOwnerScopeId != null) {
|
||||||
if (!scopeSlotMap.containsKey(key)) {
|
if (allowLocalSlots) {
|
||||||
scopeSlotMap[key] = scopeSlotMap.size
|
if (!localSlotInfoMap.containsKey(key)) {
|
||||||
}
|
localSlotInfoMap[key] = LocalSlotInfo(target.name, target.isMutable, target.isDelegated)
|
||||||
if (!scopeSlotNameMap.containsKey(key)) {
|
}
|
||||||
scopeSlotNameMap[key] = target.name
|
|
||||||
}
|
}
|
||||||
|
captureSlotKeys.add(key)
|
||||||
} else {
|
} else {
|
||||||
val shouldLocalize = target.isDelegated || !forceScopeSlots || intLoopVarNames.contains(target.name)
|
val shouldLocalize = target.isDelegated || !forceScopeSlots || intLoopVarNames.contains(target.name)
|
||||||
val isModuleSlot = if (target.isDelegated) false else isModuleSlot(scopeId, target.name)
|
val isModuleSlot = if (target.isDelegated) false else isModuleSlot(scopeId, target.name)
|
||||||
@ -6656,6 +6722,9 @@ class BytecodeCompiler(
|
|||||||
collectScopeSlotsRef(ref.target)
|
collectScopeSlotsRef(ref.target)
|
||||||
collectScopeSlotsRef(ref.value)
|
collectScopeSlotsRef(ref.value)
|
||||||
}
|
}
|
||||||
|
is ValueFnRef -> {
|
||||||
|
valueFnRefs.add(ref)
|
||||||
|
}
|
||||||
is AssignIfNullRef -> {
|
is AssignIfNullRef -> {
|
||||||
collectScopeSlotsRef(ref.target)
|
collectScopeSlotsRef(ref.target)
|
||||||
collectScopeSlotsRef(ref.value)
|
collectScopeSlotsRef(ref.value)
|
||||||
|
|||||||
@ -212,6 +212,7 @@ class BytecodeStatement private constructor(
|
|||||||
stmt.label,
|
stmt.label,
|
||||||
stmt.canBreak,
|
stmt.canBreak,
|
||||||
stmt.loopSlotPlan,
|
stmt.loopSlotPlan,
|
||||||
|
stmt.loopScopeId,
|
||||||
stmt.pos
|
stmt.pos
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,7 +69,8 @@ class CmdBuilder {
|
|||||||
scopeSlotIsModule: BooleanArray = BooleanArray(0),
|
scopeSlotIsModule: BooleanArray = BooleanArray(0),
|
||||||
localSlotNames: Array<String?> = emptyArray(),
|
localSlotNames: Array<String?> = emptyArray(),
|
||||||
localSlotMutables: BooleanArray = BooleanArray(0),
|
localSlotMutables: BooleanArray = BooleanArray(0),
|
||||||
localSlotDelegated: BooleanArray = BooleanArray(0)
|
localSlotDelegated: BooleanArray = BooleanArray(0),
|
||||||
|
localSlotCaptures: BooleanArray = BooleanArray(0)
|
||||||
): CmdFunction {
|
): CmdFunction {
|
||||||
val scopeSlotCount = scopeSlotIndices.size
|
val scopeSlotCount = scopeSlotIndices.size
|
||||||
require(scopeSlotNames.isEmpty() || scopeSlotNames.size == scopeSlotCount) {
|
require(scopeSlotNames.isEmpty() || scopeSlotNames.size == scopeSlotCount) {
|
||||||
@ -80,6 +81,7 @@ class CmdBuilder {
|
|||||||
}
|
}
|
||||||
require(localSlotNames.size == localSlotMutables.size) { "local slot metadata size mismatch" }
|
require(localSlotNames.size == localSlotMutables.size) { "local slot metadata size mismatch" }
|
||||||
require(localSlotNames.size == localSlotDelegated.size) { "local slot delegation size mismatch" }
|
require(localSlotNames.size == localSlotDelegated.size) { "local slot delegation size mismatch" }
|
||||||
|
require(localSlotNames.size == localSlotCaptures.size) { "local slot capture size mismatch" }
|
||||||
val labelIps = mutableMapOf<Label, Int>()
|
val labelIps = mutableMapOf<Label, Int>()
|
||||||
for ((label, idx) in labelPositions) {
|
for ((label, idx) in labelPositions) {
|
||||||
labelIps[label] = idx
|
labelIps[label] = idx
|
||||||
@ -114,6 +116,7 @@ class CmdBuilder {
|
|||||||
localSlotNames = localSlotNames,
|
localSlotNames = localSlotNames,
|
||||||
localSlotMutables = localSlotMutables,
|
localSlotMutables = localSlotMutables,
|
||||||
localSlotDelegated = localSlotDelegated,
|
localSlotDelegated = localSlotDelegated,
|
||||||
|
localSlotCaptures = localSlotCaptures,
|
||||||
constants = constPool.toList(),
|
constants = constPool.toList(),
|
||||||
cmds = cmds.toTypedArray(),
|
cmds = cmds.toTypedArray(),
|
||||||
posByIp = posByInstr.toTypedArray()
|
posByIp = posByInstr.toTypedArray()
|
||||||
@ -150,7 +153,8 @@ class CmdBuilder {
|
|||||||
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
||||||
Opcode.CONST_NULL ->
|
Opcode.CONST_NULL ->
|
||||||
listOf(OperandKind.SLOT)
|
listOf(OperandKind.SLOT)
|
||||||
Opcode.CONST_OBJ, Opcode.CONST_INT, Opcode.CONST_REAL, Opcode.CONST_BOOL, Opcode.MAKE_VALUE_FN ->
|
Opcode.CONST_OBJ, Opcode.CONST_INT, Opcode.CONST_REAL, Opcode.CONST_BOOL,
|
||||||
|
Opcode.MAKE_VALUE_FN ->
|
||||||
listOf(OperandKind.CONST, OperandKind.SLOT)
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.PUSH_SCOPE, Opcode.PUSH_SLOT_PLAN ->
|
Opcode.PUSH_SCOPE, Opcode.PUSH_SLOT_PLAN ->
|
||||||
listOf(OperandKind.CONST)
|
listOf(OperandKind.CONST)
|
||||||
|
|||||||
@ -279,7 +279,8 @@ object CmdDisassembler {
|
|||||||
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
||||||
Opcode.CONST_NULL ->
|
Opcode.CONST_NULL ->
|
||||||
listOf(OperandKind.SLOT)
|
listOf(OperandKind.SLOT)
|
||||||
Opcode.CONST_OBJ, Opcode.CONST_INT, Opcode.CONST_REAL, Opcode.CONST_BOOL, Opcode.MAKE_VALUE_FN ->
|
Opcode.CONST_OBJ, Opcode.CONST_INT, Opcode.CONST_REAL, Opcode.CONST_BOOL,
|
||||||
|
Opcode.MAKE_VALUE_FN ->
|
||||||
listOf(OperandKind.CONST, OperandKind.SLOT)
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.PUSH_SCOPE, Opcode.PUSH_SLOT_PLAN ->
|
Opcode.PUSH_SCOPE, Opcode.PUSH_SLOT_PLAN ->
|
||||||
listOf(OperandKind.CONST)
|
listOf(OperandKind.CONST)
|
||||||
|
|||||||
@ -29,6 +29,7 @@ data class CmdFunction(
|
|||||||
val localSlotNames: Array<String?>,
|
val localSlotNames: Array<String?>,
|
||||||
val localSlotMutables: BooleanArray,
|
val localSlotMutables: BooleanArray,
|
||||||
val localSlotDelegated: BooleanArray,
|
val localSlotDelegated: BooleanArray,
|
||||||
|
val localSlotCaptures: BooleanArray,
|
||||||
val constants: List<BytecodeConst>,
|
val constants: List<BytecodeConst>,
|
||||||
val cmds: Array<Cmd>,
|
val cmds: Array<Cmd>,
|
||||||
val posByIp: Array<net.sergeych.lyng.Pos?>,
|
val posByIp: Array<net.sergeych.lyng.Pos?>,
|
||||||
@ -39,6 +40,7 @@ data class CmdFunction(
|
|||||||
require(scopeSlotIsModule.size == scopeSlotCount) { "scopeSlotIsModule size mismatch" }
|
require(scopeSlotIsModule.size == scopeSlotCount) { "scopeSlotIsModule size mismatch" }
|
||||||
require(localSlotNames.size == localSlotMutables.size) { "localSlot metadata size mismatch" }
|
require(localSlotNames.size == localSlotMutables.size) { "localSlot metadata size mismatch" }
|
||||||
require(localSlotNames.size == localSlotDelegated.size) { "localSlot delegation size mismatch" }
|
require(localSlotNames.size == localSlotDelegated.size) { "localSlot delegation size mismatch" }
|
||||||
|
require(localSlotNames.size == localSlotCaptures.size) { "localSlot capture size mismatch" }
|
||||||
require(localSlotNames.size <= localCount) { "localSlotNames exceed localCount" }
|
require(localSlotNames.size <= localCount) { "localSlotNames exceed localCount" }
|
||||||
require(addrCount >= 0) { "addrCount must be non-negative" }
|
require(addrCount >= 0) { "addrCount must be non-negative" }
|
||||||
if (posByIp.isNotEmpty()) {
|
if (posByIp.isNotEmpty()) {
|
||||||
|
|||||||
@ -26,6 +26,7 @@ class CmdVm {
|
|||||||
suspend fun execute(fn: CmdFunction, scope0: Scope, args: List<Obj>): Obj {
|
suspend fun execute(fn: CmdFunction, scope0: Scope, args: List<Obj>): Obj {
|
||||||
result = null
|
result = null
|
||||||
val frame = CmdFrame(this, fn, scope0, args)
|
val frame = CmdFrame(this, fn, scope0, args)
|
||||||
|
frame.applyCaptureRecords()
|
||||||
val cmds = fn.cmds
|
val cmds = fn.cmds
|
||||||
if (fn.localSlotNames.isNotEmpty()) {
|
if (fn.localSlotNames.isNotEmpty()) {
|
||||||
frame.syncScopeToFrame()
|
frame.syncScopeToFrame()
|
||||||
@ -236,12 +237,23 @@ class CmdAssertIs(internal val objSlot: Int, internal val typeSlot: Int) : Cmd()
|
|||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
val obj = frame.slotToObj(objSlot)
|
val obj = frame.slotToObj(objSlot)
|
||||||
val typeObj = frame.slotToObj(typeSlot)
|
val typeObj = frame.slotToObj(typeSlot)
|
||||||
val clazz = typeObj as? ObjClass ?: frame.ensureScope().raiseClassCastError(
|
when (typeObj) {
|
||||||
"${typeObj.inspect(frame.ensureScope())} is not the class instance"
|
is ObjClass -> {
|
||||||
)
|
if (!obj.isInstanceOf(typeObj)) {
|
||||||
if (!obj.isInstanceOf(clazz)) {
|
frame.ensureScope().raiseClassCastError(
|
||||||
frame.ensureScope().raiseClassCastError(
|
"Cannot cast ${obj.objClass.className} to ${typeObj.className}"
|
||||||
"Cannot cast ${obj.objClass.className} to ${clazz.className}"
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is ObjTypeExpr -> {
|
||||||
|
if (!matchesTypeDecl(frame.ensureScope(), obj, typeObj.typeDecl)) {
|
||||||
|
frame.ensureScope().raiseClassCastError(
|
||||||
|
"Cannot cast ${obj.objClass.className} to ${typeObj.typeDecl}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> frame.ensureScope().raiseClassCastError(
|
||||||
|
"${typeObj.inspect(frame.ensureScope())} is not the class instance"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -256,17 +268,22 @@ class CmdMakeQualifiedView(
|
|||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
val obj0 = frame.slotToObj(objSlot)
|
val obj0 = frame.slotToObj(objSlot)
|
||||||
val typeObj = frame.slotToObj(typeSlot)
|
val typeObj = frame.slotToObj(typeSlot)
|
||||||
val clazz = typeObj as? ObjClass ?: frame.ensureScope().raiseClassCastError(
|
|
||||||
"${typeObj.inspect(frame.ensureScope())} is not the class instance"
|
|
||||||
)
|
|
||||||
val base = when (obj0) {
|
val base = when (obj0) {
|
||||||
is ObjQualifiedView -> obj0.instance
|
is ObjQualifiedView -> obj0.instance
|
||||||
else -> obj0
|
else -> obj0
|
||||||
}
|
}
|
||||||
val result = if (base is ObjInstance && base.isInstanceOf(clazz)) {
|
val result = when (typeObj) {
|
||||||
ObjQualifiedView(base, clazz)
|
is ObjClass -> {
|
||||||
} else {
|
if (base is ObjInstance && base.isInstanceOf(typeObj)) {
|
||||||
base
|
ObjQualifiedView(base, typeObj)
|
||||||
|
} else {
|
||||||
|
base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is ObjTypeExpr -> base
|
||||||
|
else -> frame.ensureScope().raiseClassCastError(
|
||||||
|
"${typeObj.inspect(frame.ensureScope())} is not the class instance"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
frame.storeObjResult(dst, result)
|
frame.storeObjResult(dst, result)
|
||||||
return
|
return
|
||||||
@ -1488,6 +1505,9 @@ class CmdCallSlot(
|
|||||||
} else {
|
} else {
|
||||||
// Pooling for Statement-based callables (lambdas) can still alter closure semantics; keep safe path for now.
|
// Pooling for Statement-based callables (lambdas) can still alter closure semantics; keep safe path for now.
|
||||||
val scope = frame.ensureScope()
|
val scope = frame.ensureScope()
|
||||||
|
if (callee is Statement && callee !is BytecodeStatement) {
|
||||||
|
frame.syncFrameToScope(useRefs = true)
|
||||||
|
}
|
||||||
callee.callOn(scope.createChildScope(scope.pos, args = args))
|
callee.callOn(scope.createChildScope(scope.pos, args = args))
|
||||||
}
|
}
|
||||||
frame.storeObjResult(dst, result)
|
frame.storeObjResult(dst, result)
|
||||||
@ -1938,6 +1958,57 @@ class CmdFrame(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun applyCaptureRecords() {
|
||||||
|
val captureRecords = scope.captureRecords ?: return
|
||||||
|
val captureNames = scope.captureNames ?: return
|
||||||
|
val localNames = fn.localSlotNames
|
||||||
|
if (localNames.isEmpty()) return
|
||||||
|
for (i in captureNames.indices) {
|
||||||
|
val name = captureNames[i]
|
||||||
|
val record = captureRecords.getOrNull(i) ?: continue
|
||||||
|
var localIndex = -1
|
||||||
|
for (idx in localNames.indices) {
|
||||||
|
if (localNames[idx] != name) continue
|
||||||
|
if (fn.localSlotCaptures.getOrNull(idx) != true) continue
|
||||||
|
localIndex = idx
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (localIndex < 0) {
|
||||||
|
for (idx in localNames.indices) {
|
||||||
|
if (localNames[idx] != name) continue
|
||||||
|
localIndex = idx
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (localIndex < 0) continue
|
||||||
|
if (record.type == ObjRecord.Type.Delegated) {
|
||||||
|
frame.setObj(localIndex, record.delegate ?: ObjNull)
|
||||||
|
} else {
|
||||||
|
val value = record.value
|
||||||
|
if (value is FrameSlotRef) {
|
||||||
|
frame.setObj(localIndex, value)
|
||||||
|
} else {
|
||||||
|
frame.setObj(localIndex, RecordSlotRef(record))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (idx in localNames.indices) {
|
||||||
|
if (idx == localIndex) continue
|
||||||
|
if (localNames[idx] != name) continue
|
||||||
|
if (fn.localSlotCaptures.getOrNull(idx) == true) continue
|
||||||
|
if (record.type == ObjRecord.Type.Delegated) {
|
||||||
|
frame.setObj(idx, record.delegate ?: ObjNull)
|
||||||
|
} else {
|
||||||
|
val value = record.value
|
||||||
|
if (value is FrameSlotRef) {
|
||||||
|
frame.setObj(idx, value)
|
||||||
|
} else {
|
||||||
|
frame.setObj(idx, RecordSlotRef(record))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal fun buildCaptureRecords(captureTableId: Int): List<ObjRecord> {
|
internal fun buildCaptureRecords(captureTableId: Int): List<ObjRecord> {
|
||||||
val table = fn.constants.getOrNull(captureTableId) as? BytecodeConst.CaptureTable
|
val table = fn.constants.getOrNull(captureTableId) as? BytecodeConst.CaptureTable
|
||||||
?: error("Capture table $captureTableId missing")
|
?: error("Capture table $captureTableId missing")
|
||||||
@ -1987,7 +2058,11 @@ class CmdFrame(
|
|||||||
.firstOrNull { fn.scopeSlotIsModule.getOrNull(it) == true }
|
.firstOrNull { fn.scopeSlotIsModule.getOrNull(it) == true }
|
||||||
?.let { fn.scopeSlotNames[it] }
|
?.let { fn.scopeSlotNames[it] }
|
||||||
if (moduleSlotName != null) {
|
if (moduleSlotName != null) {
|
||||||
findScopeWithSlot(scope, moduleSlotName)?.let { return it }
|
val bySlot = findScopeWithSlot(scope, moduleSlotName)
|
||||||
|
bySlot?.let { return it }
|
||||||
|
val byRecord = findScopeWithRecord(scope, moduleSlotName)
|
||||||
|
byRecord?.let { return it }
|
||||||
|
return scope
|
||||||
}
|
}
|
||||||
findModuleScope(scope)?.let { return it }
|
findModuleScope(scope)?.let { return it }
|
||||||
return scope
|
return scope
|
||||||
@ -2013,15 +2088,14 @@ class CmdFrame(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findModuleScope(scope: Scope): Scope? {
|
private fun findScopeWithRecord(scope: Scope, name: String): Scope? {
|
||||||
val visited = HashSet<Scope>(16)
|
val visited = HashSet<Scope>(16)
|
||||||
val queue = ArrayDeque<Scope>()
|
val queue = ArrayDeque<Scope>()
|
||||||
queue.add(scope)
|
queue.add(scope)
|
||||||
while (queue.isNotEmpty()) {
|
while (queue.isNotEmpty()) {
|
||||||
val current = queue.removeFirst()
|
val current = queue.removeFirst()
|
||||||
if (!visited.add(current)) continue
|
if (!visited.add(current)) continue
|
||||||
if (current is ModuleScope) return current
|
if (current.getLocalRecordDirect(name) != null) return current
|
||||||
if (current.parent is ModuleScope) return current
|
|
||||||
current.parent?.let { queue.add(it) }
|
current.parent?.let { queue.add(it) }
|
||||||
if (current is ClosureScope) {
|
if (current is ClosureScope) {
|
||||||
queue.add(current.closureScope)
|
queue.add(current.closureScope)
|
||||||
@ -2034,14 +2108,15 @@ class CmdFrame(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findScopeWithRecord(scope: Scope, name: String): Scope? {
|
private fun findModuleScope(scope: Scope): Scope? {
|
||||||
val visited = HashSet<Scope>(16)
|
val visited = HashSet<Scope>(16)
|
||||||
val queue = ArrayDeque<Scope>()
|
val queue = ArrayDeque<Scope>()
|
||||||
queue.add(scope)
|
queue.add(scope)
|
||||||
while (queue.isNotEmpty()) {
|
while (queue.isNotEmpty()) {
|
||||||
val current = queue.removeFirst()
|
val current = queue.removeFirst()
|
||||||
if (!visited.add(current)) continue
|
if (!visited.add(current)) continue
|
||||||
if (current.getLocalRecordDirect(name) != null) return current
|
if (current is ModuleScope) return current
|
||||||
|
if (current.parent is ModuleScope) return current
|
||||||
current.parent?.let { queue.add(it) }
|
current.parent?.let { queue.add(it) }
|
||||||
if (current is ClosureScope) {
|
if (current is ClosureScope) {
|
||||||
queue.add(current.closureScope)
|
queue.add(current.closureScope)
|
||||||
@ -2215,7 +2290,7 @@ class CmdFrame(
|
|||||||
return if (slot < fn.scopeSlotCount) {
|
return if (slot < fn.scopeSlotCount) {
|
||||||
getScopeSlotValue(slot)
|
getScopeSlotValue(slot)
|
||||||
} else {
|
} else {
|
||||||
frame.getObj(slot - fn.scopeSlotCount)
|
localSlotToObj(slot - fn.scopeSlotCount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2225,7 +2300,17 @@ class CmdFrame(
|
|||||||
val index = ensureScopeSlot(target, slot)
|
val index = ensureScopeSlot(target, slot)
|
||||||
target.setSlotValue(index, value)
|
target.setSlotValue(index, value)
|
||||||
} else {
|
} else {
|
||||||
frame.setObj(slot - fn.scopeSlotCount, value)
|
val localIndex = slot - fn.scopeSlotCount
|
||||||
|
val existing = frame.getObj(localIndex)
|
||||||
|
if (existing is FrameSlotRef) {
|
||||||
|
existing.write(value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (existing is RecordSlotRef) {
|
||||||
|
existing.write(value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
frame.setObj(localIndex, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2238,7 +2323,14 @@ class CmdFrame(
|
|||||||
SlotType.INT.code -> frame.getInt(local)
|
SlotType.INT.code -> frame.getInt(local)
|
||||||
SlotType.REAL.code -> frame.getReal(local).toLong()
|
SlotType.REAL.code -> frame.getReal(local).toLong()
|
||||||
SlotType.BOOL.code -> if (frame.getBool(local)) 1L else 0L
|
SlotType.BOOL.code -> if (frame.getBool(local)) 1L else 0L
|
||||||
SlotType.OBJ.code -> frame.getObj(local).toLong()
|
SlotType.OBJ.code -> {
|
||||||
|
val obj = frame.getObj(local)
|
||||||
|
when (obj) {
|
||||||
|
is FrameSlotRef -> obj.read().toLong()
|
||||||
|
is RecordSlotRef -> obj.read().toLong()
|
||||||
|
else -> obj.toLong()
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> 0L
|
else -> 0L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2252,7 +2344,17 @@ class CmdFrame(
|
|||||||
val index = ensureScopeSlot(target, slot)
|
val index = ensureScopeSlot(target, slot)
|
||||||
target.setSlotValue(index, ObjInt.of(value))
|
target.setSlotValue(index, ObjInt.of(value))
|
||||||
} else {
|
} else {
|
||||||
frame.setInt(slot - fn.scopeSlotCount, value)
|
val localIndex = slot - fn.scopeSlotCount
|
||||||
|
val existing = frame.getObj(localIndex)
|
||||||
|
if (existing is FrameSlotRef) {
|
||||||
|
existing.write(ObjInt.of(value))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (existing is RecordSlotRef) {
|
||||||
|
existing.write(ObjInt.of(value))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
frame.setInt(localIndex, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2269,7 +2371,14 @@ class CmdFrame(
|
|||||||
SlotType.REAL.code -> frame.getReal(local)
|
SlotType.REAL.code -> frame.getReal(local)
|
||||||
SlotType.INT.code -> frame.getInt(local).toDouble()
|
SlotType.INT.code -> frame.getInt(local).toDouble()
|
||||||
SlotType.BOOL.code -> if (frame.getBool(local)) 1.0 else 0.0
|
SlotType.BOOL.code -> if (frame.getBool(local)) 1.0 else 0.0
|
||||||
SlotType.OBJ.code -> frame.getObj(local).toDouble()
|
SlotType.OBJ.code -> {
|
||||||
|
val obj = frame.getObj(local)
|
||||||
|
when (obj) {
|
||||||
|
is FrameSlotRef -> obj.read().toDouble()
|
||||||
|
is RecordSlotRef -> obj.read().toDouble()
|
||||||
|
else -> obj.toDouble()
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> 0.0
|
else -> 0.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2281,7 +2390,17 @@ class CmdFrame(
|
|||||||
val index = ensureScopeSlot(target, slot)
|
val index = ensureScopeSlot(target, slot)
|
||||||
target.setSlotValue(index, ObjReal.of(value))
|
target.setSlotValue(index, ObjReal.of(value))
|
||||||
} else {
|
} else {
|
||||||
frame.setReal(slot - fn.scopeSlotCount, value)
|
val localIndex = slot - fn.scopeSlotCount
|
||||||
|
val existing = frame.getObj(localIndex)
|
||||||
|
if (existing is FrameSlotRef) {
|
||||||
|
existing.write(ObjReal.of(value))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (existing is RecordSlotRef) {
|
||||||
|
existing.write(ObjReal.of(value))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
frame.setReal(localIndex, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2294,7 +2413,14 @@ class CmdFrame(
|
|||||||
SlotType.BOOL.code -> frame.getBool(local)
|
SlotType.BOOL.code -> frame.getBool(local)
|
||||||
SlotType.INT.code -> frame.getInt(local) != 0L
|
SlotType.INT.code -> frame.getInt(local) != 0L
|
||||||
SlotType.REAL.code -> frame.getReal(local) != 0.0
|
SlotType.REAL.code -> frame.getReal(local) != 0.0
|
||||||
SlotType.OBJ.code -> frame.getObj(local).toBool()
|
SlotType.OBJ.code -> {
|
||||||
|
val obj = frame.getObj(local)
|
||||||
|
when (obj) {
|
||||||
|
is FrameSlotRef -> obj.read().toBool()
|
||||||
|
is RecordSlotRef -> obj.read().toBool()
|
||||||
|
else -> obj.toBool()
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2308,7 +2434,17 @@ class CmdFrame(
|
|||||||
val index = ensureScopeSlot(target, slot)
|
val index = ensureScopeSlot(target, slot)
|
||||||
target.setSlotValue(index, if (value) ObjTrue else ObjFalse)
|
target.setSlotValue(index, if (value) ObjTrue else ObjFalse)
|
||||||
} else {
|
} else {
|
||||||
frame.setBool(slot - fn.scopeSlotCount, value)
|
val localIndex = slot - fn.scopeSlotCount
|
||||||
|
val existing = frame.getObj(localIndex)
|
||||||
|
if (existing is FrameSlotRef) {
|
||||||
|
existing.write(if (value) ObjTrue else ObjFalse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (existing is RecordSlotRef) {
|
||||||
|
existing.write(if (value) ObjTrue else ObjFalse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
frame.setBool(localIndex, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2361,6 +2497,9 @@ class CmdFrame(
|
|||||||
return getScopeSlotValue(slot)
|
return getScopeSlotValue(slot)
|
||||||
}
|
}
|
||||||
val local = slot - fn.scopeSlotCount
|
val local = slot - fn.scopeSlotCount
|
||||||
|
if (fn.localSlotCaptures.getOrNull(local) == true) {
|
||||||
|
return localSlotToObj(local)
|
||||||
|
}
|
||||||
val localName = fn.localSlotNames.getOrNull(local)
|
val localName = fn.localSlotNames.getOrNull(local)
|
||||||
if (localName != null && fn.localSlotDelegated.getOrNull(local) != true) {
|
if (localName != null && fn.localSlotDelegated.getOrNull(local) != true) {
|
||||||
val rec = scope.getLocalRecordDirect(localName) ?: scope.localBindings[localName]
|
val rec = scope.getLocalRecordDirect(localName) ?: scope.localBindings[localName]
|
||||||
@ -2372,7 +2511,10 @@ class CmdFrame(
|
|||||||
SlotType.INT.code -> ObjInt.of(frame.getInt(local))
|
SlotType.INT.code -> ObjInt.of(frame.getInt(local))
|
||||||
SlotType.REAL.code -> ObjReal.of(frame.getReal(local))
|
SlotType.REAL.code -> ObjReal.of(frame.getReal(local))
|
||||||
SlotType.BOOL.code -> if (frame.getBool(local)) ObjTrue else ObjFalse
|
SlotType.BOOL.code -> if (frame.getBool(local)) ObjTrue else ObjFalse
|
||||||
SlotType.OBJ.code -> frame.getObj(local)
|
SlotType.OBJ.code -> {
|
||||||
|
val obj = frame.getObj(local)
|
||||||
|
if (obj is FrameSlotRef) obj.read() else obj
|
||||||
|
}
|
||||||
else -> ObjVoid
|
else -> ObjVoid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2415,6 +2557,7 @@ class CmdFrame(
|
|||||||
if (names.isEmpty()) return
|
if (names.isEmpty()) return
|
||||||
for (i in names.indices) {
|
for (i in names.indices) {
|
||||||
val name = names[i] ?: continue
|
val name = names[i] ?: continue
|
||||||
|
if (fn.localSlotCaptures.getOrNull(i) == true) continue
|
||||||
if (scopeSlotNames.contains(name)) continue
|
if (scopeSlotNames.contains(name)) continue
|
||||||
val target = resolveLocalScope(i) ?: continue
|
val target = resolveLocalScope(i) ?: continue
|
||||||
val isDelegated = fn.localSlotDelegated.getOrNull(i) == true
|
val isDelegated = fn.localSlotDelegated.getOrNull(i) == true
|
||||||
@ -2450,6 +2593,7 @@ class CmdFrame(
|
|||||||
if (names.isEmpty()) return
|
if (names.isEmpty()) return
|
||||||
for (i in names.indices) {
|
for (i in names.indices) {
|
||||||
val name = names[i] ?: continue
|
val name = names[i] ?: continue
|
||||||
|
if (fn.localSlotCaptures.getOrNull(i) == true) continue
|
||||||
val target = resolveLocalScope(i) ?: continue
|
val target = resolveLocalScope(i) ?: continue
|
||||||
val rec = target.getLocalRecordDirect(name) ?: continue
|
val rec = target.getLocalRecordDirect(name) ?: continue
|
||||||
if (fn.localSlotDelegated.getOrNull(i) == true && rec.type == ObjRecord.Type.Delegated) {
|
if (fn.localSlotDelegated.getOrNull(i) == true && rec.type == ObjRecord.Type.Delegated) {
|
||||||
@ -2459,13 +2603,6 @@ class CmdFrame(
|
|||||||
}
|
}
|
||||||
val value = rec.value
|
val value = rec.value
|
||||||
if (value is FrameSlotRef) {
|
if (value is FrameSlotRef) {
|
||||||
val resolved = value.read()
|
|
||||||
when (resolved) {
|
|
||||||
is ObjInt -> frame.setInt(i, resolved.value)
|
|
||||||
is ObjReal -> frame.setReal(i, resolved.value)
|
|
||||||
is ObjBool -> frame.setBool(i, resolved.value)
|
|
||||||
else -> frame.setObj(i, resolved)
|
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
when (value) {
|
when (value) {
|
||||||
@ -2563,8 +2700,22 @@ class CmdFrame(
|
|||||||
SlotType.INT.code -> ObjInt.of(frame.getInt(localIndex))
|
SlotType.INT.code -> ObjInt.of(frame.getInt(localIndex))
|
||||||
SlotType.REAL.code -> ObjReal.of(frame.getReal(localIndex))
|
SlotType.REAL.code -> ObjReal.of(frame.getReal(localIndex))
|
||||||
SlotType.BOOL.code -> if (frame.getBool(localIndex)) ObjTrue else ObjFalse
|
SlotType.BOOL.code -> if (frame.getBool(localIndex)) ObjTrue else ObjFalse
|
||||||
SlotType.OBJ.code -> frame.getObj(localIndex)
|
SlotType.OBJ.code -> {
|
||||||
else -> ObjNull
|
val obj = frame.getObj(localIndex)
|
||||||
|
when (obj) {
|
||||||
|
is FrameSlotRef -> obj.read()
|
||||||
|
is RecordSlotRef -> obj.read()
|
||||||
|
else -> obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val obj = frame.getObj(localIndex)
|
||||||
|
when (obj) {
|
||||||
|
is FrameSlotRef -> obj.read()
|
||||||
|
is RecordSlotRef -> obj.read()
|
||||||
|
else -> obj
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,9 @@ data class LambdaCaptureEntry(
|
|||||||
val ownerKind: CaptureOwnerFrameKind,
|
val ownerKind: CaptureOwnerFrameKind,
|
||||||
val ownerScopeId: Int,
|
val ownerScopeId: Int,
|
||||||
val ownerSlotId: Int,
|
val ownerSlotId: Int,
|
||||||
|
val ownerName: String,
|
||||||
|
val ownerIsMutable: Boolean,
|
||||||
|
val ownerIsDelegated: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class BytecodeCaptureEntry(
|
data class BytecodeCaptureEntry(
|
||||||
|
|||||||
@ -20,6 +20,8 @@
|
|||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import net.sergeych.lyng.*
|
import net.sergeych.lyng.*
|
||||||
|
import net.sergeych.lyng.FrameSlotRef
|
||||||
|
import net.sergeych.lyng.RecordSlotRef
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reference to a value with optional write-back path.
|
* A reference to a value with optional write-back path.
|
||||||
@ -43,7 +45,12 @@ sealed interface ObjRef {
|
|||||||
if (rec.receiver != null && rec.declaringClass != null) {
|
if (rec.receiver != null && rec.declaringClass != null) {
|
||||||
return rec.receiver!!.resolveRecord(scope, rec, "unknown", rec.declaringClass).value
|
return rec.receiver!!.resolveRecord(scope, rec, "unknown", rec.declaringClass).value
|
||||||
}
|
}
|
||||||
return rec.value
|
val value = rec.value
|
||||||
|
return when (value) {
|
||||||
|
is FrameSlotRef -> value.read()
|
||||||
|
is RecordSlotRef -> value.read()
|
||||||
|
else -> value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
suspend fun setAt(pos: Pos, scope: Scope, newValue: Obj) {
|
suspend fun setAt(pos: Pos, scope: Scope, newValue: Obj) {
|
||||||
throw ScriptError(pos, "can't assign value")
|
throw ScriptError(pos, "can't assign value")
|
||||||
|
|||||||
@ -99,6 +99,7 @@ class ForInStatement(
|
|||||||
val label: String?,
|
val label: String?,
|
||||||
val canBreak: Boolean,
|
val canBreak: Boolean,
|
||||||
val loopSlotPlan: Map<String, Int>,
|
val loopSlotPlan: Map<String, Int>,
|
||||||
|
val loopScopeId: Int,
|
||||||
override val pos: Pos,
|
override val pos: Pos,
|
||||||
) : Statement() {
|
) : Statement() {
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
|||||||
@ -180,6 +180,39 @@ class BytecodeRecentOpsTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun lambdaCapturesLocalByReference() = runTest {
|
||||||
|
eval(
|
||||||
|
"""
|
||||||
|
fun make() {
|
||||||
|
var base = 3
|
||||||
|
val f = { x -> x + base }
|
||||||
|
base = 7
|
||||||
|
return f(1)
|
||||||
|
}
|
||||||
|
assertEquals(8, make())
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun lambdaCapturesDelegatedLocal() = runTest {
|
||||||
|
eval(
|
||||||
|
"""
|
||||||
|
class BoxDelegate(var v) : Delegate {
|
||||||
|
override fun getValue(thisRef: Object, name: String): Object = v
|
||||||
|
override fun setValue(thisRef: Object, name: String, value: Object) { v = value }
|
||||||
|
}
|
||||||
|
fun make() {
|
||||||
|
var x by BoxDelegate(1)
|
||||||
|
val f = { y -> x += y; return x }
|
||||||
|
return f(2)
|
||||||
|
}
|
||||||
|
assertEquals(3, make())
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun delegatedMemberAccessAndCall() = runTest {
|
fun delegatedMemberAccessAndCall() = runTest {
|
||||||
eval(
|
eval(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user