Finalize bytecode-only lambdas and frame binding
This commit is contained in:
parent
a481371349
commit
db4f7d0973
@ -125,15 +125,16 @@ Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM te
|
|||||||
- [x] Remove `containsValueFnRef` helper now that lambdas are bytecode-backed.
|
- [x] Remove `containsValueFnRef` helper now that lambdas are bytecode-backed.
|
||||||
- [x] Remove `forceScopeSlots` branches once no bytecode paths depend on scope slots.
|
- [x] Remove `forceScopeSlots` branches once no bytecode paths depend on scope slots.
|
||||||
- [x] Add JVM tests for captured locals and delegated locals inside lambdas on the bytecode path.
|
- [x] Add JVM tests for captured locals and delegated locals inside lambdas on the bytecode path.
|
||||||
- [ ] Step 27: Remove interpreter opcodes and constants from bytecode runtime.
|
- [x] Step 27: Remove interpreter opcodes and constants from bytecode runtime.
|
||||||
- [ ] Delete `BytecodeConst.ValueFn`, `CmdMakeValueFn`, and `MAKE_VALUE_FN` (blocked: some lambdas still fall back to non-bytecode bodies).
|
- [x] Delete `BytecodeConst.ValueFn`, `CmdMakeValueFn`, and `MAKE_VALUE_FN`.
|
||||||
- [x] Delete `BytecodeConst.StatementVal`, `CmdEvalStmt`, and `EVAL_STMT`.
|
- [x] Delete `BytecodeConst.StatementVal`, `CmdEvalStmt`, and `EVAL_STMT`.
|
||||||
- [x] Add bytecode-backed `::class` via `ClassOperatorRef` + `GET_OBJ_CLASS` to avoid ValueFn for class operator.
|
- [x] Add bytecode-backed `::class` via `ClassOperatorRef` + `GET_OBJ_CLASS` to avoid ValueFn for class operator.
|
||||||
- [x] Add a bytecode fallback reporter hook for lambdas to locate remaining non-bytecode cases.
|
- [x] Add a bytecode fallback reporter hook for lambdas to locate remaining non-bytecode cases.
|
||||||
- [ ] Remove `emitStatementCall`/`emitStatementEval` once unused.
|
- [x] Remove `emitStatementCall`/`emitStatementEval` once unused.
|
||||||
- [ ] Step 28: Scope as facade only.
|
- [ ] Step 28: Scope as facade only.
|
||||||
- [ ] Audit bytecode execution paths for `Statement.execute` usage and remove remaining calls.
|
- [x] Audit bytecode execution paths for `Statement.execute` usage and remove remaining calls.
|
||||||
- [ ] Keep scope sync only for reflection/Kotlin interop, not for execution.
|
- [x] Keep scope sync only for reflection/Kotlin interop, not for execution.
|
||||||
|
- [x] Replace bytecode entry seeding from Scope with frame-only arg/local binding.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
|||||||
@ -250,6 +250,212 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign arguments directly into frame slots using [paramSlotPlan] without creating scope locals.
|
||||||
|
* Still allows default expressions to evaluate by exposing FrameSlotRef facades in [scope].
|
||||||
|
*/
|
||||||
|
suspend fun assignToFrame(
|
||||||
|
scope: Scope,
|
||||||
|
arguments: Arguments = scope.args,
|
||||||
|
paramSlotPlan: Map<String, Int>,
|
||||||
|
frame: FrameAccess,
|
||||||
|
slotOffset: Int = 0,
|
||||||
|
defaultAccessType: AccessType = AccessType.Var,
|
||||||
|
defaultVisibility: Visibility = Visibility.Public,
|
||||||
|
declaringClass: net.sergeych.lyng.obj.ObjClass? = scope.currentClassCtx
|
||||||
|
) {
|
||||||
|
fun slotFor(name: String): Int {
|
||||||
|
val full = paramSlotPlan[name] ?: scope.raiseIllegalState("parameter slot for '$name' is missing")
|
||||||
|
val slot = full - slotOffset
|
||||||
|
if (slot < 0) scope.raiseIllegalState("parameter slot for '$name' is out of range")
|
||||||
|
return slot
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ensureScopeRef(a: Item, slot: Int, recordType: ObjRecord.Type) {
|
||||||
|
if (scope.getLocalRecordDirect(a.name) != null) return
|
||||||
|
scope.addItem(
|
||||||
|
a.name,
|
||||||
|
(a.accessType ?: defaultAccessType).isMutable,
|
||||||
|
FrameSlotRef(frame, slot),
|
||||||
|
a.visibility ?: defaultVisibility,
|
||||||
|
recordType = recordType,
|
||||||
|
declaringClass = declaringClass,
|
||||||
|
isTransient = a.isTransient
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setFrameValue(slot: Int, value: Obj) {
|
||||||
|
when (value) {
|
||||||
|
is net.sergeych.lyng.obj.ObjInt -> frame.setInt(slot, value.value)
|
||||||
|
is net.sergeych.lyng.obj.ObjReal -> frame.setReal(slot, value.value)
|
||||||
|
is net.sergeych.lyng.obj.ObjBool -> frame.setBool(slot, value.value)
|
||||||
|
else -> frame.setObj(slot, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assign(a: Item, value: Obj) {
|
||||||
|
val recordType = if (declaringClass != null && a.accessType != null) {
|
||||||
|
ObjRecord.Type.ConstructorField
|
||||||
|
} else {
|
||||||
|
ObjRecord.Type.Argument
|
||||||
|
}
|
||||||
|
val slot = slotFor(a.name)
|
||||||
|
setFrameValue(slot, value.byValueCopy())
|
||||||
|
ensureScopeRef(a, slot, recordType)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun missingValue(a: Item, error: String): Obj {
|
||||||
|
return a.defaultValue?.execute(scope)
|
||||||
|
?: if (a.type.isNullable) ObjNull else scope.raiseIllegalArgument(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast path for simple positional-only calls with no ellipsis and no defaults
|
||||||
|
if (arguments.named.isEmpty() && !arguments.tailBlockMode) {
|
||||||
|
var hasComplex = false
|
||||||
|
for (p in params) {
|
||||||
|
if (p.isEllipsis || p.defaultValue != null) {
|
||||||
|
hasComplex = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasComplex) {
|
||||||
|
if (arguments.list.size > params.size)
|
||||||
|
scope.raiseIllegalArgument("expected ${params.size} arguments, got ${arguments.list.size}")
|
||||||
|
if (arguments.list.size < params.size) {
|
||||||
|
for (i in arguments.list.size until params.size) {
|
||||||
|
val a = params[i]
|
||||||
|
if (!a.type.isNullable) {
|
||||||
|
scope.raiseIllegalArgument("expected ${params.size} arguments, got ${arguments.list.size}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in params.indices) {
|
||||||
|
val a = params[i]
|
||||||
|
val value = if (i < arguments.list.size) arguments.list[i] else ObjNull
|
||||||
|
assign(a, value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare positional args and parameter count, handle tail-block binding
|
||||||
|
val callArgs: List<Obj>
|
||||||
|
val paramsSize: Int
|
||||||
|
if (arguments.tailBlockMode) {
|
||||||
|
val lastParam = params.last()
|
||||||
|
if (arguments.named.containsKey(lastParam.name))
|
||||||
|
scope.raiseIllegalArgument("trailing block cannot be used when the last parameter is already assigned by a named argument")
|
||||||
|
paramsSize = params.size - 1
|
||||||
|
assign(lastParam, arguments.list.last())
|
||||||
|
callArgs = arguments.list.dropLast(1)
|
||||||
|
} else {
|
||||||
|
paramsSize = params.size
|
||||||
|
callArgs = arguments.list
|
||||||
|
}
|
||||||
|
|
||||||
|
val coveredByPositional = BooleanArray(paramsSize)
|
||||||
|
run {
|
||||||
|
var headRequired = 0
|
||||||
|
var tailRequired = 0
|
||||||
|
val ellipsisIdx = params.subList(0, paramsSize).indexOfFirst { it.isEllipsis }
|
||||||
|
if (ellipsisIdx >= 0) {
|
||||||
|
for (i in 0 until ellipsisIdx) if (!params[i].isEllipsis && params[i].defaultValue == null) headRequired++
|
||||||
|
for (i in paramsSize - 1 downTo ellipsisIdx + 1) if (params[i].defaultValue == null) tailRequired++
|
||||||
|
} else {
|
||||||
|
for (i in 0 until paramsSize) if (params[i].defaultValue == null) headRequired++
|
||||||
|
}
|
||||||
|
val P = callArgs.size
|
||||||
|
if (ellipsisIdx < 0) {
|
||||||
|
val k = minOf(P, paramsSize)
|
||||||
|
for (i in 0 until k) coveredByPositional[i] = true
|
||||||
|
} else {
|
||||||
|
val headTake = minOf(P, headRequired)
|
||||||
|
for (i in 0 until headTake) coveredByPositional[i] = true
|
||||||
|
val remaining = P - headTake
|
||||||
|
val tailTake = minOf(remaining, tailRequired)
|
||||||
|
var j = paramsSize - 1
|
||||||
|
var taken = 0
|
||||||
|
while (j > ellipsisIdx && taken < tailTake) {
|
||||||
|
coveredByPositional[j] = true
|
||||||
|
j--
|
||||||
|
taken++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val assignedByName = BooleanArray(paramsSize)
|
||||||
|
val namedValues = arrayOfNulls<Obj>(paramsSize)
|
||||||
|
if (arguments.named.isNotEmpty()) {
|
||||||
|
for ((k, v) in arguments.named) {
|
||||||
|
val idx = params.subList(0, paramsSize).indexOfFirst { it.name == k }
|
||||||
|
if (idx < 0) scope.raiseIllegalArgument("unknown parameter '$k'")
|
||||||
|
if (params[idx].isEllipsis) scope.raiseIllegalArgument("ellipsis (variadic) parameter cannot be assigned by name: '$k'")
|
||||||
|
if (coveredByPositional[idx]) scope.raiseIllegalArgument("argument '$k' is already set by positional argument")
|
||||||
|
if (assignedByName[idx]) scope.raiseIllegalArgument("argument '$k' is already set")
|
||||||
|
assignedByName[idx] = true
|
||||||
|
namedValues[idx] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun processHead(index: Int, headPos: Int): Pair<Int, Int> {
|
||||||
|
var i = index
|
||||||
|
var hp = headPos
|
||||||
|
while (i < paramsSize) {
|
||||||
|
val a = params[i]
|
||||||
|
if (a.isEllipsis) break
|
||||||
|
if (assignedByName[i]) {
|
||||||
|
assign(a, namedValues[i]!!)
|
||||||
|
} else {
|
||||||
|
val value = if (hp < callArgs.size) callArgs[hp++]
|
||||||
|
else missingValue(a, "too few arguments for the call (missing ${a.name})")
|
||||||
|
assign(a, value)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return i to hp
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun processTail(startExclusive: Int, tailStart: Int, headPosBound: Int): Int {
|
||||||
|
var i = paramsSize - 1
|
||||||
|
var tp = tailStart
|
||||||
|
while (i > startExclusive) {
|
||||||
|
val a = params[i]
|
||||||
|
if (a.isEllipsis) break
|
||||||
|
if (i < assignedByName.size && assignedByName[i]) {
|
||||||
|
assign(a, namedValues[i]!!)
|
||||||
|
} else {
|
||||||
|
val value = if (tp >= headPosBound) callArgs[tp--]
|
||||||
|
else missingValue(a, "too few arguments for the call")
|
||||||
|
assign(a, value)
|
||||||
|
}
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
return tp
|
||||||
|
}
|
||||||
|
|
||||||
|
fun processEllipsis(index: Int, headPos: Int, tailPos: Int) {
|
||||||
|
val a = params[index]
|
||||||
|
val from = headPos
|
||||||
|
val to = tailPos
|
||||||
|
val l = if (from > to) ObjList()
|
||||||
|
else ObjList(callArgs.subList(from, to + 1).toMutableList())
|
||||||
|
assign(a, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
val ellipsisIndex = params.subList(0, paramsSize).indexOfFirst { it.isEllipsis }
|
||||||
|
|
||||||
|
if (ellipsisIndex >= 0) {
|
||||||
|
val (_, headConsumedTo) = processHead(0, 0)
|
||||||
|
val tailConsumedFrom = processTail(ellipsisIndex, callArgs.size - 1, headConsumedTo)
|
||||||
|
processEllipsis(ellipsisIndex, headConsumedTo, tailConsumedFrom)
|
||||||
|
} else {
|
||||||
|
val (_, headConsumedTo) = processHead(0, 0)
|
||||||
|
if (headConsumedTo != callArgs.size)
|
||||||
|
scope.raiseIllegalArgument("too many arguments for the call")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Single argument declaration descriptor.
|
* Single argument declaration descriptor.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -1892,10 +1892,11 @@ class Compiler(
|
|||||||
private fun wrapFunctionBytecode(
|
private fun wrapFunctionBytecode(
|
||||||
stmt: Statement,
|
stmt: Statement,
|
||||||
name: String,
|
name: String,
|
||||||
extraKnownNameObjClass: Map<String, ObjClass> = emptyMap()
|
extraKnownNameObjClass: Map<String, ObjClass> = emptyMap(),
|
||||||
|
forcedLocalSlots: Map<String, Int> = emptyMap(),
|
||||||
|
forcedLocalScopeId: Int? = null
|
||||||
): Statement {
|
): Statement {
|
||||||
if (!useBytecodeStatements) return stmt
|
if (!useBytecodeStatements) return stmt
|
||||||
if (containsUnsupportedForBytecode(stmt)) return stmt
|
|
||||||
val returnLabels = returnLabelStack.lastOrNull() ?: emptySet()
|
val returnLabels = returnLabelStack.lastOrNull() ?: emptySet()
|
||||||
val allowedScopeNames = moduleSlotPlan()?.slots?.keys
|
val allowedScopeNames = moduleSlotPlan()?.slots?.keys
|
||||||
val knownNames = if (extraKnownNameObjClass.isEmpty()) {
|
val knownNames = if (extraKnownNameObjClass.isEmpty()) {
|
||||||
@ -1914,6 +1915,8 @@ class Compiler(
|
|||||||
rangeLocalNames = currentRangeParamNames,
|
rangeLocalNames = currentRangeParamNames,
|
||||||
allowedScopeNames = allowedScopeNames,
|
allowedScopeNames = allowedScopeNames,
|
||||||
moduleScopeId = moduleSlotPlan()?.id,
|
moduleScopeId = moduleSlotPlan()?.id,
|
||||||
|
forcedLocalSlots = forcedLocalSlots,
|
||||||
|
forcedLocalScopeId = forcedLocalScopeId,
|
||||||
slotTypeByScopeId = slotTypeByScopeId,
|
slotTypeByScopeId = slotTypeByScopeId,
|
||||||
knownNameObjClass = knownNames,
|
knownNameObjClass = knownNames,
|
||||||
knownObjectNames = objectDeclNames,
|
knownObjectNames = objectDeclNames,
|
||||||
@ -2910,27 +2913,25 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
val returnLabels = label?.let { setOf(it) } ?: emptySet()
|
val returnLabels = label?.let { setOf(it) } ?: emptySet()
|
||||||
val fnStatements = if (useBytecodeStatements) {
|
val fnStatements = if (useBytecodeStatements) {
|
||||||
if (containsUnsupportedForBytecode(body)) {
|
|
||||||
bytecodeFallbackReporter?.invoke(
|
|
||||||
body.pos,
|
|
||||||
"lambda contains unsupported bytecode statements"
|
|
||||||
)
|
|
||||||
body
|
|
||||||
} else {
|
|
||||||
returnLabelStack.addLast(returnLabels)
|
returnLabelStack.addLast(returnLabels)
|
||||||
try {
|
try {
|
||||||
wrapFunctionBytecode(body, "<lambda>", paramKnownClasses)
|
wrapFunctionBytecode(
|
||||||
|
body,
|
||||||
|
"<lambda>",
|
||||||
|
paramKnownClasses,
|
||||||
|
forcedLocalSlots = paramSlotPlanSnapshot,
|
||||||
|
forcedLocalScopeId = paramSlotPlan.id
|
||||||
|
)
|
||||||
} catch (e: net.sergeych.lyng.bytecode.BytecodeCompileException) {
|
} catch (e: net.sergeych.lyng.bytecode.BytecodeCompileException) {
|
||||||
val pos = e.pos ?: body.pos
|
val pos = e.pos ?: body.pos
|
||||||
bytecodeFallbackReporter?.invoke(
|
bytecodeFallbackReporter?.invoke(
|
||||||
pos,
|
pos,
|
||||||
"lambda bytecode compile failed: ${e.message}"
|
"lambda bytecode compile failed: ${e.message}"
|
||||||
)
|
)
|
||||||
body
|
throw e
|
||||||
} finally {
|
} finally {
|
||||||
returnLabelStack.removeLast()
|
returnLabelStack.removeLast()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
body
|
body
|
||||||
}
|
}
|
||||||
@ -3001,6 +3002,48 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (usesBytecodeBody && fnStatements is BytecodeStatement) {
|
||||||
|
val bytecodeFn = fnStatements.bytecodeFunction()
|
||||||
|
val binder: suspend (net.sergeych.lyng.bytecode.CmdFrame, Arguments) -> Unit = { frame, arguments ->
|
||||||
|
val slotPlan = bytecodeFn.localSlotPlanByName()
|
||||||
|
if (argsDeclaration == null) {
|
||||||
|
val l = arguments.list
|
||||||
|
val itValue: Obj = when (l.size) {
|
||||||
|
0 -> ObjVoid
|
||||||
|
1 -> l[0]
|
||||||
|
else -> ObjList(l.toMutableList())
|
||||||
|
}
|
||||||
|
val itSlot = slotPlan["it"]
|
||||||
|
if (itSlot != null) {
|
||||||
|
frame.frame.setObj(itSlot, itValue)
|
||||||
|
if (context.getLocalRecordDirect("it") == null) {
|
||||||
|
context.addItem(
|
||||||
|
"it",
|
||||||
|
false,
|
||||||
|
FrameSlotRef(frame.frame, itSlot),
|
||||||
|
recordType = ObjRecord.Type.Argument
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (context.getLocalRecordDirect("it") == null) {
|
||||||
|
context.addItem("it", false, itValue, recordType = ObjRecord.Type.Argument)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
argsDeclaration.assignToFrame(
|
||||||
|
context,
|
||||||
|
arguments,
|
||||||
|
slotPlan,
|
||||||
|
frame.frame,
|
||||||
|
defaultAccessType = AccessType.Val
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return try {
|
||||||
|
net.sergeych.lyng.bytecode.CmdVm().execute(bytecodeFn, context, scope.args, binder)
|
||||||
|
} catch (e: ReturnException) {
|
||||||
|
if (e.label == null || returnLabels.contains(e.label)) e.result
|
||||||
|
else throw e
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (argsDeclaration == null) {
|
if (argsDeclaration == null) {
|
||||||
val l = scope.args.list
|
val l = scope.args.list
|
||||||
val itValue: Obj = when (l.size) {
|
val itValue: Obj = when (l.size) {
|
||||||
@ -3012,15 +3055,15 @@ class Compiler(
|
|||||||
} else {
|
} else {
|
||||||
argsDeclaration.assignToContext(context, scope.args, defaultAccessType = AccessType.Val)
|
argsDeclaration.assignToContext(context, scope.args, defaultAccessType = AccessType.Val)
|
||||||
}
|
}
|
||||||
val effectiveStatements = if (usesBytecodeBody) fnStatements else body
|
|
||||||
return try {
|
return try {
|
||||||
effectiveStatements.execute(context)
|
body.execute(context)
|
||||||
} catch (e: ReturnException) {
|
} catch (e: ReturnException) {
|
||||||
if (e.label == null || returnLabels.contains(e.label)) e.result
|
if (e.label == null || returnLabels.contains(e.label)) e.result
|
||||||
else throw e
|
else throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
val callable: Obj = if (wrapAsExtensionCallable) {
|
val callable: Obj = if (wrapAsExtensionCallable) {
|
||||||
ObjExtensionMethodCallable("<lambda>", stmt)
|
ObjExtensionMethodCallable("<lambda>", stmt)
|
||||||
} else {
|
} else {
|
||||||
@ -4716,30 +4759,43 @@ class Compiler(
|
|||||||
context: Scope,
|
context: Scope,
|
||||||
argsDeclaration: ArgsDeclaration,
|
argsDeclaration: ArgsDeclaration,
|
||||||
typeParams: List<TypeDecl.TypeParam>
|
typeParams: List<TypeDecl.TypeParam>
|
||||||
) {
|
): Map<String, Obj> {
|
||||||
if (typeParams.isEmpty()) return
|
if (typeParams.isEmpty()) return emptyMap()
|
||||||
val inferred = mutableMapOf<String, TypeDecl>()
|
val inferred = mutableMapOf<String, TypeDecl>()
|
||||||
for (param in argsDeclaration.params) {
|
for (param in argsDeclaration.params) {
|
||||||
val rec = context.getLocalRecordDirect(param.name) ?: continue
|
val rec = context.getLocalRecordDirect(param.name) ?: continue
|
||||||
val value = rec.value
|
val direct = rec.value
|
||||||
|
val value = when (direct) {
|
||||||
|
is FrameSlotRef -> direct.read()
|
||||||
|
is RecordSlotRef -> direct.read()
|
||||||
|
else -> direct
|
||||||
|
}
|
||||||
if (value is Obj) {
|
if (value is Obj) {
|
||||||
collectRuntimeTypeVarBindings(param.type, value, inferred)
|
collectRuntimeTypeVarBindings(param.type, value, inferred)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val boundValues = LinkedHashMap<String, Obj>(typeParams.size)
|
||||||
for (tp in typeParams) {
|
for (tp in typeParams) {
|
||||||
val inferredType = inferred[tp.name] ?: tp.defaultType ?: TypeDecl.TypeAny
|
val inferredType = inferred[tp.name] ?: tp.defaultType ?: TypeDecl.TypeAny
|
||||||
val normalized = normalizeRuntimeTypeDecl(inferredType)
|
val normalized = normalizeRuntimeTypeDecl(inferredType)
|
||||||
val cls = resolveTypeDeclObjClass(normalized)
|
val cls = resolveTypeDeclObjClass(normalized)
|
||||||
if (cls != null && !normalized.isNullable && normalized !is TypeDecl.Union && normalized !is TypeDecl.Intersection) {
|
val boundValue = if (cls != null &&
|
||||||
context.addConst(tp.name, cls)
|
!normalized.isNullable &&
|
||||||
|
normalized !is TypeDecl.Union &&
|
||||||
|
normalized !is TypeDecl.Intersection
|
||||||
|
) {
|
||||||
|
cls
|
||||||
} else {
|
} else {
|
||||||
context.addConst(tp.name, net.sergeych.lyng.obj.ObjTypeExpr(normalized))
|
net.sergeych.lyng.obj.ObjTypeExpr(normalized)
|
||||||
}
|
}
|
||||||
|
context.addConst(tp.name, boundValue)
|
||||||
|
boundValues[tp.name] = boundValue
|
||||||
val bound = tp.bound ?: continue
|
val bound = tp.bound ?: continue
|
||||||
if (!typeDeclSatisfiesBound(normalized, bound)) {
|
if (!typeDeclSatisfiesBound(normalized, bound)) {
|
||||||
context.raiseError("type argument ${typeDeclName(normalized)} does not satisfy bound ${typeDeclName(bound)}")
|
context.raiseError("type argument ${typeDeclName(normalized)} does not satisfy bound ${typeDeclName(bound)}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return boundValues
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectRuntimeTypeVarBindings(
|
private fun collectRuntimeTypeVarBindings(
|
||||||
@ -6994,6 +7050,12 @@ class Compiler(
|
|||||||
inferredReturnClass
|
inferredReturnClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val paramSlotPlanSnapshot = slotPlanIndices(paramSlotPlan)
|
||||||
|
val forcedLocalSlots = LinkedHashMap<String, Int>()
|
||||||
|
for (name in paramNamesList) {
|
||||||
|
val idx = paramSlotPlanSnapshot[name] ?: continue
|
||||||
|
forcedLocalSlots[name] = idx
|
||||||
|
}
|
||||||
val fnStatements = rawFnStatements?.let { stmt ->
|
val fnStatements = rawFnStatements?.let { stmt ->
|
||||||
if (useBytecodeStatements &&
|
if (useBytecodeStatements &&
|
||||||
parentContext !is CodeContext.ClassBody &&
|
parentContext !is CodeContext.ClassBody &&
|
||||||
@ -7004,7 +7066,13 @@ class Compiler(
|
|||||||
val cls = resolveTypeDeclObjClass(param.type) ?: continue
|
val cls = resolveTypeDeclObjClass(param.type) ?: continue
|
||||||
paramKnownClasses[param.name] = cls
|
paramKnownClasses[param.name] = cls
|
||||||
}
|
}
|
||||||
wrapFunctionBytecode(stmt, name, paramKnownClasses)
|
wrapFunctionBytecode(
|
||||||
|
stmt,
|
||||||
|
name,
|
||||||
|
paramKnownClasses,
|
||||||
|
forcedLocalSlots = forcedLocalSlots,
|
||||||
|
forcedLocalScopeId = paramSlotPlan.id
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
stmt
|
stmt
|
||||||
}
|
}
|
||||||
@ -7014,7 +7082,6 @@ class Compiler(
|
|||||||
|
|
||||||
val closureBox = FunctionClosureBox()
|
val closureBox = FunctionClosureBox()
|
||||||
|
|
||||||
val paramSlotPlanSnapshot = slotPlanIndices(paramSlotPlan)
|
|
||||||
val captureSlots = capturePlan.captures.toList()
|
val captureSlots = capturePlan.captures.toList()
|
||||||
val fnBody = object : Statement(), BytecodeBodyProvider {
|
val fnBody = object : Statement(), BytecodeBodyProvider {
|
||||||
override val pos: Pos = start
|
override val pos: Pos = start
|
||||||
@ -7055,6 +7122,36 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val bytecodeBody = (fnStatements as? BytecodeStatement)
|
||||||
|
if (bytecodeBody != null) {
|
||||||
|
val bytecodeFn = bytecodeBody.bytecodeFunction()
|
||||||
|
val binder: suspend (net.sergeych.lyng.bytecode.CmdFrame, Arguments) -> Unit = { frame, arguments ->
|
||||||
|
val slotPlan = bytecodeFn.localSlotPlanByName()
|
||||||
|
argsDeclaration.assignToFrame(
|
||||||
|
context,
|
||||||
|
arguments,
|
||||||
|
slotPlan,
|
||||||
|
frame.frame,
|
||||||
|
defaultAccessType = AccessType.Val
|
||||||
|
)
|
||||||
|
val typeBindings = bindTypeParamsAtRuntime(context, argsDeclaration, mergedTypeParamDecls)
|
||||||
|
if (typeBindings.isNotEmpty()) {
|
||||||
|
for ((name, bound) in typeBindings) {
|
||||||
|
val slot = slotPlan[name] ?: continue
|
||||||
|
frame.frame.setObj(slot, bound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (extTypeName != null) {
|
||||||
|
context.thisObj = callerContext.thisObj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return try {
|
||||||
|
net.sergeych.lyng.bytecode.CmdVm().execute(bytecodeFn, context, callerContext.args, binder)
|
||||||
|
} catch (e: ReturnException) {
|
||||||
|
if (e.label == null || e.label == name || e.label == outerLabel) e.result
|
||||||
|
else throw e
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// 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)
|
||||||
bindTypeParamsAtRuntime(context, argsDeclaration, mergedTypeParamDecls)
|
bindTypeParamsAtRuntime(context, argsDeclaration, mergedTypeParamDecls)
|
||||||
@ -7069,6 +7166,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
cc.labels.remove(name)
|
cc.labels.remove(name)
|
||||||
outerLabel?.let { cc.labels.remove(it) }
|
outerLabel?.let { cc.labels.remove(it) }
|
||||||
val spec = FunctionDeclSpec(
|
val spec = FunctionDeclSpec(
|
||||||
|
|||||||
@ -84,7 +84,7 @@ class Script(
|
|||||||
seedModuleSlots(moduleTarget)
|
seedModuleSlots(moduleTarget)
|
||||||
}
|
}
|
||||||
moduleBytecode?.let { fn ->
|
moduleBytecode?.let { fn ->
|
||||||
return CmdVm().execute(fn, scope, scope.args.list)
|
return CmdVm().execute(fn, scope, scope.args)
|
||||||
}
|
}
|
||||||
var lastResult: Obj = ObjVoid
|
var lastResult: Obj = ObjVoid
|
||||||
for (s in statements) {
|
for (s in statements) {
|
||||||
|
|||||||
@ -26,6 +26,8 @@ class BytecodeCompiler(
|
|||||||
private val rangeLocalNames: Set<String> = emptySet(),
|
private val rangeLocalNames: Set<String> = emptySet(),
|
||||||
private val allowedScopeNames: Set<String>? = null,
|
private val allowedScopeNames: Set<String>? = null,
|
||||||
private val moduleScopeId: Int? = null,
|
private val moduleScopeId: Int? = null,
|
||||||
|
private val forcedLocalSlots: Map<String, Int> = emptyMap(),
|
||||||
|
private val forcedLocalScopeId: Int? = null,
|
||||||
private val slotTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
|
private val slotTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
|
||||||
private val knownNameObjClass: Map<String, ObjClass> = emptyMap(),
|
private val knownNameObjClass: Map<String, ObjClass> = emptyMap(),
|
||||||
private val knownObjectNames: Set<String> = emptySet(),
|
private val knownObjectNames: Set<String> = emptySet(),
|
||||||
@ -649,25 +651,13 @@ class BytecodeCompiler(
|
|||||||
updateSlotType(slot, SlotType.OBJ)
|
updateSlotType(slot, SlotType.OBJ)
|
||||||
return CompiledValue(slot, SlotType.OBJ)
|
return CompiledValue(slot, SlotType.OBJ)
|
||||||
}
|
}
|
||||||
val captureTableId = lambdaCaptureEntriesByRef[ref]?.let { captures ->
|
val pos = (ref as? LambdaFnRef)?.pos ?: Pos.builtIn
|
||||||
if (captures.isEmpty()) return@let null
|
val refName = ref::class.simpleName ?: "ValueFnRef"
|
||||||
val resolved = captures.map { entry ->
|
throw BytecodeCompileException(
|
||||||
val slotIndex = resolveCaptureSlot(entry)
|
"Bytecode compile error: non-bytecode lambda $refName encountered",
|
||||||
BytecodeCaptureEntry(
|
pos
|
||||||
ownerKind = entry.ownerKind,
|
|
||||||
ownerScopeId = entry.ownerScopeId,
|
|
||||||
ownerSlotId = entry.ownerSlotId,
|
|
||||||
slotIndex = slotIndex
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
builder.addConst(BytecodeConst.CaptureTable(resolved))
|
|
||||||
}
|
|
||||||
val id = builder.addConst(BytecodeConst.ValueFn(ref.valueFn(), captureTableId))
|
|
||||||
val slot = allocSlot()
|
|
||||||
builder.emit(Opcode.MAKE_VALUE_FN, id, slot)
|
|
||||||
updateSlotType(slot, SlotType.OBJ)
|
|
||||||
return CompiledValue(slot, SlotType.OBJ)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun resolveCaptureSlot(entry: LambdaCaptureEntry): Int {
|
private fun resolveCaptureSlot(entry: LambdaCaptureEntry): Int {
|
||||||
val key = ScopeSlotKey(entry.ownerScopeId, entry.ownerSlotId)
|
val key = ScopeSlotKey(entry.ownerScopeId, entry.ownerSlotId)
|
||||||
@ -4130,36 +4120,6 @@ class BytecodeCompiler(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun emitStatementEval(stmt: Statement): CompiledValue {
|
|
||||||
val stmtName = stmt::class.simpleName ?: "UnknownStatement"
|
|
||||||
throw BytecodeCompileException("Unsupported statement in bytecode: $stmtName", stmt.pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun emitStatementCall(stmt: Statement): CompiledValue {
|
|
||||||
val constId = builder.addConst(BytecodeConst.ObjRef(stmt))
|
|
||||||
val calleeSlot = allocSlot()
|
|
||||||
builder.emit(Opcode.CONST_OBJ, constId, calleeSlot)
|
|
||||||
updateSlotType(calleeSlot, SlotType.OBJ)
|
|
||||||
val dst = allocSlot()
|
|
||||||
builder.emit(Opcode.CALL_SLOT, calleeSlot, 0, 0, dst)
|
|
||||||
updateSlotType(dst, SlotType.OBJ)
|
|
||||||
return CompiledValue(dst, SlotType.OBJ)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun emitDeclExec(stmt: Statement): CompiledValue {
|
|
||||||
val executable = when (stmt) {
|
|
||||||
else -> throw BytecodeCompileException(
|
|
||||||
"Bytecode compile error: unsupported declaration ${stmt::class.simpleName}",
|
|
||||||
stmt.pos
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val constId = builder.addConst(BytecodeConst.DeclExec(executable))
|
|
||||||
val dst = allocSlot()
|
|
||||||
builder.emit(Opcode.DECL_EXEC, constId, dst)
|
|
||||||
updateSlotType(dst, SlotType.OBJ)
|
|
||||||
return CompiledValue(dst, SlotType.OBJ)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun emitDeclEnum(stmt: net.sergeych.lyng.EnumDeclStatement): CompiledValue {
|
private fun emitDeclEnum(stmt: net.sergeych.lyng.EnumDeclStatement): CompiledValue {
|
||||||
val constId = builder.addConst(
|
val constId = builder.addConst(
|
||||||
BytecodeConst.EnumDecl(
|
BytecodeConst.EnumDecl(
|
||||||
@ -5746,13 +5706,15 @@ class BytecodeCompiler(
|
|||||||
if (knownObjectNames.contains(ref.name)) {
|
if (knownObjectNames.contains(ref.name)) {
|
||||||
return nameObjClass[ref.name] ?: ObjDynamic.type
|
return nameObjClass[ref.name] ?: ObjDynamic.type
|
||||||
}
|
}
|
||||||
|
val ownerScopeId = ref.captureOwnerScopeId ?: ref.scopeId
|
||||||
|
val ownerSlot = ref.captureOwnerSlot ?: ref.slot
|
||||||
val slot = resolveSlot(ref)
|
val slot = resolveSlot(ref)
|
||||||
val fromSlot = slot?.let { slotObjClass[it] }
|
val fromSlot = slot?.let { slotObjClass[it] }
|
||||||
fromSlot
|
fromSlot
|
||||||
?: slotTypeByScopeId[refScopeId(ref)]?.get(refSlot(ref))
|
?: slotTypeByScopeId[ownerScopeId]?.get(ownerSlot)
|
||||||
?: nameObjClass[ref.name]
|
?: nameObjClass[ref.name]
|
||||||
?: resolveTypeNameClass(ref.name)
|
?: resolveTypeNameClass(ref.name)
|
||||||
?: slotInitClassByKey[ScopeSlotKey(refScopeId(ref), refSlot(ref))]
|
?: slotInitClassByKey[ScopeSlotKey(ownerScopeId, ownerSlot)]
|
||||||
?: run {
|
?: run {
|
||||||
val match = slotInitClassByKey.entries.firstOrNull { (key, _) ->
|
val match = slotInitClassByKey.entries.firstOrNull { (key, _) ->
|
||||||
val name = localSlotInfoMap[key]?.name ?: scopeSlotNameMap[key]
|
val name = localSlotInfoMap[key]?.name ?: scopeSlotNameMap[key]
|
||||||
@ -6216,6 +6178,14 @@ class BytecodeCompiler(
|
|||||||
if (allowLocalSlots) {
|
if (allowLocalSlots) {
|
||||||
collectLoopSlotPlans(stmt, 0)
|
collectLoopSlotPlans(stmt, 0)
|
||||||
}
|
}
|
||||||
|
if (allowLocalSlots && forcedLocalSlots.isNotEmpty() && forcedLocalScopeId != null) {
|
||||||
|
for ((name, slotIndex) in forcedLocalSlots) {
|
||||||
|
val key = ScopeSlotKey(forcedLocalScopeId, slotIndex)
|
||||||
|
if (!localSlotInfoMap.containsKey(key)) {
|
||||||
|
localSlotInfoMap[key] = LocalSlotInfo(name, isMutable = false, isDelegated = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (allowLocalSlots && valueFnRefs.isNotEmpty() && lambdaCaptureEntriesByRef.isNotEmpty()) {
|
if (allowLocalSlots && valueFnRefs.isNotEmpty() && lambdaCaptureEntriesByRef.isNotEmpty()) {
|
||||||
for (ref in valueFnRefs) {
|
for (ref in valueFnRefs) {
|
||||||
val entries = lambdaCaptureEntriesByRef[ref] ?: continue
|
val entries = lambdaCaptureEntriesByRef[ref] ?: continue
|
||||||
|
|||||||
@ -31,12 +31,7 @@ sealed class BytecodeConst {
|
|||||||
data class StringVal(val value: String) : BytecodeConst()
|
data class StringVal(val value: String) : BytecodeConst()
|
||||||
data class PosVal(val pos: Pos) : BytecodeConst()
|
data class PosVal(val pos: Pos) : BytecodeConst()
|
||||||
data class ObjRef(val value: Obj) : BytecodeConst()
|
data class ObjRef(val value: Obj) : BytecodeConst()
|
||||||
data class Ref(val value: net.sergeych.lyng.obj.ObjRef) : BytecodeConst()
|
|
||||||
data class ListLiteralPlan(val spreads: List<Boolean>) : BytecodeConst()
|
data class ListLiteralPlan(val spreads: List<Boolean>) : BytecodeConst()
|
||||||
data class ValueFn(
|
|
||||||
val fn: suspend (net.sergeych.lyng.Scope) -> net.sergeych.lyng.obj.ObjRecord,
|
|
||||||
val captureTableId: Int? = null,
|
|
||||||
) : BytecodeConst()
|
|
||||||
data class LambdaFn(
|
data class LambdaFn(
|
||||||
val fn: CmdFunction,
|
val fn: CmdFunction,
|
||||||
val captureTableId: Int?,
|
val captureTableId: Int?,
|
||||||
@ -48,7 +43,6 @@ sealed class BytecodeConst {
|
|||||||
val returnLabels: Set<String>,
|
val returnLabels: Set<String>,
|
||||||
val pos: Pos,
|
val pos: Pos,
|
||||||
) : BytecodeConst()
|
) : BytecodeConst()
|
||||||
data class DeclExec(val executable: net.sergeych.lyng.DeclExecutable) : BytecodeConst()
|
|
||||||
data class EnumDecl(
|
data class EnumDecl(
|
||||||
val declaredName: String,
|
val declaredName: String,
|
||||||
val qualifiedName: String,
|
val qualifiedName: String,
|
||||||
|
|||||||
@ -40,9 +40,23 @@ class BytecodeFrame(
|
|||||||
slotTypes[slot] = type.code
|
slotTypes[slot] = type.code
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getObj(slot: Int): Obj = objSlots[slot] ?: ObjNull
|
override fun getObj(slot: Int): Obj {
|
||||||
|
val value = objSlots[slot] ?: return ObjNull
|
||||||
|
return when (value) {
|
||||||
|
is net.sergeych.lyng.FrameSlotRef -> value.read()
|
||||||
|
is net.sergeych.lyng.RecordSlotRef -> value.read()
|
||||||
|
else -> value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getRawObj(slot: Int): Obj? = objSlots[slot]
|
||||||
|
|
||||||
override fun setObj(slot: Int, value: Obj) {
|
override fun setObj(slot: Int, value: Obj) {
|
||||||
objSlots[slot] = value
|
when (val current = objSlots[slot]) {
|
||||||
|
is net.sergeych.lyng.FrameSlotRef -> current.write(value)
|
||||||
|
is net.sergeych.lyng.RecordSlotRef -> current.write(value)
|
||||||
|
else -> objSlots[slot] = value
|
||||||
|
}
|
||||||
slotTypes[slot] = SlotType.OBJ.code
|
slotTypes[slot] = SlotType.OBJ.code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@ class BytecodeStatement private constructor(
|
|||||||
|
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
scope.pos = pos
|
scope.pos = pos
|
||||||
return CmdVm().execute(function, scope, scope.args.list)
|
return CmdVm().execute(function, scope, scope.args)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun bytecodeFunction(): CmdFunction = function
|
internal fun bytecodeFunction(): CmdFunction = function
|
||||||
@ -44,6 +44,8 @@ class BytecodeStatement private constructor(
|
|||||||
rangeLocalNames: Set<String> = emptySet(),
|
rangeLocalNames: Set<String> = emptySet(),
|
||||||
allowedScopeNames: Set<String>? = null,
|
allowedScopeNames: Set<String>? = null,
|
||||||
moduleScopeId: Int? = null,
|
moduleScopeId: Int? = null,
|
||||||
|
forcedLocalSlots: Map<String, Int> = emptyMap(),
|
||||||
|
forcedLocalScopeId: Int? = null,
|
||||||
slotTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
|
slotTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
|
||||||
knownNameObjClass: Map<String, ObjClass> = emptyMap(),
|
knownNameObjClass: Map<String, ObjClass> = emptyMap(),
|
||||||
knownObjectNames: Set<String> = emptySet(),
|
knownObjectNames: Set<String> = emptySet(),
|
||||||
@ -69,6 +71,8 @@ class BytecodeStatement private constructor(
|
|||||||
rangeLocalNames = rangeLocalNames,
|
rangeLocalNames = rangeLocalNames,
|
||||||
allowedScopeNames = allowedScopeNames,
|
allowedScopeNames = allowedScopeNames,
|
||||||
moduleScopeId = moduleScopeId,
|
moduleScopeId = moduleScopeId,
|
||||||
|
forcedLocalSlots = forcedLocalSlots,
|
||||||
|
forcedLocalScopeId = forcedLocalScopeId,
|
||||||
slotTypeByScopeId = slotTypeByScopeId,
|
slotTypeByScopeId = slotTypeByScopeId,
|
||||||
knownNameObjClass = knownNameObjClass,
|
knownNameObjClass = knownNameObjClass,
|
||||||
knownObjectNames = knownObjectNames,
|
knownObjectNames = knownObjectNames,
|
||||||
|
|||||||
@ -154,14 +154,14 @@ class CmdBuilder {
|
|||||||
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.CONST_OBJ, Opcode.CONST_INT, Opcode.CONST_REAL, Opcode.CONST_BOOL,
|
||||||
Opcode.MAKE_VALUE_FN, Opcode.MAKE_LAMBDA_FN ->
|
Opcode.MAKE_LAMBDA_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)
|
||||||
Opcode.PUSH_TRY ->
|
Opcode.PUSH_TRY ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP)
|
listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP)
|
||||||
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE,
|
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE,
|
||||||
Opcode.DECL_EXEC, Opcode.DECL_ENUM, Opcode.DECL_FUNCTION, Opcode.DECL_CLASS,
|
Opcode.DECL_ENUM, Opcode.DECL_FUNCTION, Opcode.DECL_CLASS,
|
||||||
Opcode.ASSIGN_DESTRUCTURE ->
|
Opcode.ASSIGN_DESTRUCTURE ->
|
||||||
listOf(OperandKind.CONST, OperandKind.SLOT)
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
|
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
|
||||||
@ -254,7 +254,6 @@ class CmdBuilder {
|
|||||||
Opcode.CONST_REAL -> CmdConstReal(operands[0], operands[1])
|
Opcode.CONST_REAL -> CmdConstReal(operands[0], operands[1])
|
||||||
Opcode.CONST_BOOL -> CmdConstBool(operands[0], operands[1])
|
Opcode.CONST_BOOL -> CmdConstBool(operands[0], operands[1])
|
||||||
Opcode.CONST_NULL -> CmdConstNull(operands[0])
|
Opcode.CONST_NULL -> CmdConstNull(operands[0])
|
||||||
Opcode.MAKE_VALUE_FN -> CmdMakeValueFn(operands[0], operands[1])
|
|
||||||
Opcode.MAKE_LAMBDA_FN -> CmdMakeLambda(operands[0], operands[1])
|
Opcode.MAKE_LAMBDA_FN -> CmdMakeLambda(operands[0], operands[1])
|
||||||
Opcode.BOX_OBJ -> CmdBoxObj(operands[0], operands[1])
|
Opcode.BOX_OBJ -> CmdBoxObj(operands[0], operands[1])
|
||||||
Opcode.OBJ_TO_BOOL -> CmdObjToBool(operands[0], operands[1])
|
Opcode.OBJ_TO_BOOL -> CmdObjToBool(operands[0], operands[1])
|
||||||
@ -416,7 +415,6 @@ class CmdBuilder {
|
|||||||
Opcode.DECL_LOCAL -> CmdDeclLocal(operands[0], operands[1])
|
Opcode.DECL_LOCAL -> CmdDeclLocal(operands[0], operands[1])
|
||||||
Opcode.DECL_DELEGATED -> CmdDeclDelegated(operands[0], operands[1])
|
Opcode.DECL_DELEGATED -> CmdDeclDelegated(operands[0], operands[1])
|
||||||
Opcode.DECL_DESTRUCTURE -> CmdDeclDestructure(operands[0], operands[1])
|
Opcode.DECL_DESTRUCTURE -> CmdDeclDestructure(operands[0], operands[1])
|
||||||
Opcode.DECL_EXEC -> CmdDeclExec(operands[0], operands[1])
|
|
||||||
Opcode.DECL_ENUM -> CmdDeclEnum(operands[0], operands[1])
|
Opcode.DECL_ENUM -> CmdDeclEnum(operands[0], operands[1])
|
||||||
Opcode.DECL_FUNCTION -> CmdDeclFunction(operands[0], operands[1])
|
Opcode.DECL_FUNCTION -> CmdDeclFunction(operands[0], operands[1])
|
||||||
Opcode.DECL_CLASS -> CmdDeclClass(operands[0], operands[1])
|
Opcode.DECL_CLASS -> CmdDeclClass(operands[0], operands[1])
|
||||||
|
|||||||
@ -86,7 +86,6 @@ object CmdDisassembler {
|
|||||||
is CmdLoadThis -> Opcode.LOAD_THIS to intArrayOf(cmd.dst)
|
is CmdLoadThis -> Opcode.LOAD_THIS to intArrayOf(cmd.dst)
|
||||||
is CmdLoadThisVariant -> Opcode.LOAD_THIS_VARIANT to intArrayOf(cmd.typeId, cmd.dst)
|
is CmdLoadThisVariant -> Opcode.LOAD_THIS_VARIANT to intArrayOf(cmd.typeId, cmd.dst)
|
||||||
is CmdConstNull -> Opcode.CONST_NULL to intArrayOf(cmd.dst)
|
is CmdConstNull -> Opcode.CONST_NULL to intArrayOf(cmd.dst)
|
||||||
is CmdMakeValueFn -> Opcode.MAKE_VALUE_FN to intArrayOf(cmd.id, cmd.dst)
|
|
||||||
is CmdMakeLambda -> Opcode.MAKE_LAMBDA_FN to intArrayOf(cmd.id, cmd.dst)
|
is CmdMakeLambda -> Opcode.MAKE_LAMBDA_FN to intArrayOf(cmd.id, cmd.dst)
|
||||||
is CmdBoxObj -> Opcode.BOX_OBJ to intArrayOf(cmd.src, cmd.dst)
|
is CmdBoxObj -> Opcode.BOX_OBJ to intArrayOf(cmd.src, cmd.dst)
|
||||||
is CmdObjToBool -> Opcode.OBJ_TO_BOOL to intArrayOf(cmd.src, cmd.dst)
|
is CmdObjToBool -> Opcode.OBJ_TO_BOOL to intArrayOf(cmd.src, cmd.dst)
|
||||||
@ -213,7 +212,6 @@ object CmdDisassembler {
|
|||||||
is CmdDeclLocal -> Opcode.DECL_LOCAL to intArrayOf(cmd.constId, cmd.slot)
|
is CmdDeclLocal -> Opcode.DECL_LOCAL to intArrayOf(cmd.constId, cmd.slot)
|
||||||
is CmdDeclDelegated -> Opcode.DECL_DELEGATED to intArrayOf(cmd.constId, cmd.slot)
|
is CmdDeclDelegated -> Opcode.DECL_DELEGATED to intArrayOf(cmd.constId, cmd.slot)
|
||||||
is CmdDeclDestructure -> Opcode.DECL_DESTRUCTURE to intArrayOf(cmd.constId, cmd.slot)
|
is CmdDeclDestructure -> Opcode.DECL_DESTRUCTURE to intArrayOf(cmd.constId, cmd.slot)
|
||||||
is CmdDeclExec -> Opcode.DECL_EXEC to intArrayOf(cmd.constId, cmd.slot)
|
|
||||||
is CmdDeclEnum -> Opcode.DECL_ENUM to intArrayOf(cmd.constId, cmd.slot)
|
is CmdDeclEnum -> Opcode.DECL_ENUM to intArrayOf(cmd.constId, cmd.slot)
|
||||||
is CmdDeclFunction -> Opcode.DECL_FUNCTION to intArrayOf(cmd.constId, cmd.slot)
|
is CmdDeclFunction -> Opcode.DECL_FUNCTION to intArrayOf(cmd.constId, cmd.slot)
|
||||||
is CmdDeclClass -> Opcode.DECL_CLASS to intArrayOf(cmd.constId, cmd.slot)
|
is CmdDeclClass -> Opcode.DECL_CLASS to intArrayOf(cmd.constId, cmd.slot)
|
||||||
@ -282,14 +280,14 @@ object CmdDisassembler {
|
|||||||
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.CONST_OBJ, Opcode.CONST_INT, Opcode.CONST_REAL, Opcode.CONST_BOOL,
|
||||||
Opcode.MAKE_VALUE_FN, Opcode.MAKE_LAMBDA_FN ->
|
Opcode.MAKE_LAMBDA_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)
|
||||||
Opcode.PUSH_TRY ->
|
Opcode.PUSH_TRY ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP)
|
listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP)
|
||||||
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE,
|
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE,
|
||||||
Opcode.DECL_EXEC, Opcode.DECL_ENUM, Opcode.DECL_FUNCTION, Opcode.DECL_CLASS,
|
Opcode.DECL_ENUM, Opcode.DECL_FUNCTION, Opcode.DECL_CLASS,
|
||||||
Opcode.ASSIGN_DESTRUCTURE ->
|
Opcode.ASSIGN_DESTRUCTURE ->
|
||||||
listOf(OperandKind.CONST, OperandKind.SLOT)
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
|
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
|
||||||
|
|||||||
@ -47,4 +47,23 @@ data class CmdFunction(
|
|||||||
require(posByIp.size == cmds.size) { "posByIp size mismatch" }
|
require(posByIp.size == cmds.size) { "posByIp size mismatch" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun localSlotPlanByName(): Map<String, Int> {
|
||||||
|
val result = LinkedHashMap<String, Int>()
|
||||||
|
for (i in localSlotNames.indices) {
|
||||||
|
val name = localSlotNames[i] ?: continue
|
||||||
|
val existing = result[name]
|
||||||
|
if (existing == null) {
|
||||||
|
result[name] = i
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val existingIsCapture = localSlotCaptures.getOrNull(existing) == true
|
||||||
|
val currentIsCapture = localSlotCaptures.getOrNull(i) == true
|
||||||
|
if (existingIsCapture && !currentIsCapture) {
|
||||||
|
result[name] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,14 +23,17 @@ import net.sergeych.lyng.obj.*
|
|||||||
class CmdVm {
|
class CmdVm {
|
||||||
var result: Obj? = null
|
var result: Obj? = null
|
||||||
|
|
||||||
suspend fun execute(fn: CmdFunction, scope0: Scope, args: List<Obj>): Obj {
|
suspend fun execute(
|
||||||
|
fn: CmdFunction,
|
||||||
|
scope0: Scope,
|
||||||
|
args: Arguments,
|
||||||
|
binder: (suspend (CmdFrame, Arguments) -> Unit)? = null
|
||||||
|
): Obj {
|
||||||
result = null
|
result = null
|
||||||
val frame = CmdFrame(this, fn, scope0, args)
|
val frame = CmdFrame(this, fn, scope0, args.list)
|
||||||
frame.applyCaptureRecords()
|
frame.applyCaptureRecords()
|
||||||
|
binder?.invoke(frame, args)
|
||||||
val cmds = fn.cmds
|
val cmds = fn.cmds
|
||||||
if (fn.localSlotNames.isNotEmpty()) {
|
|
||||||
frame.syncScopeToFrame()
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
while (result == null) {
|
while (result == null) {
|
||||||
val cmd = cmds[frame.ip]
|
val cmd = cmds[frame.ip]
|
||||||
@ -51,6 +54,10 @@ class CmdVm {
|
|||||||
frame.cancelIterators()
|
frame.cancelIterators()
|
||||||
return result ?: ObjVoid
|
return result ?: ObjVoid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun execute(fn: CmdFunction, scope0: Scope, args: List<Obj>): Obj {
|
||||||
|
return execute(fn, scope0, Arguments.from(args))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class Cmd {
|
sealed class Cmd {
|
||||||
@ -1339,16 +1346,6 @@ class CmdDeclDelegated(internal val constId: Int, internal val slot: Int) : Cmd(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdDeclExec(internal val constId: Int, internal val slot: Int) : Cmd() {
|
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
|
||||||
val decl = frame.fn.constants[constId] as? BytecodeConst.DeclExec
|
|
||||||
?: error("DECL_EXEC expects DeclExec at $constId")
|
|
||||||
val result = decl.executable.execute(frame.ensureScope())
|
|
||||||
frame.storeObjResult(slot, result)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CmdDeclEnum(internal val constId: Int, internal val slot: Int) : Cmd() {
|
class CmdDeclEnum(internal val constId: Int, internal val slot: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
val decl = frame.fn.constants[constId] as? BytecodeConst.EnumDecl
|
val decl = frame.fn.constants[constId] as? BytecodeConst.EnumDecl
|
||||||
@ -1394,20 +1391,7 @@ class CmdDeclDestructure(internal val constId: Int, internal val slot: Int) : Cm
|
|||||||
val decl = frame.fn.constants[constId] as? BytecodeConst.DestructureDecl
|
val decl = frame.fn.constants[constId] as? BytecodeConst.DestructureDecl
|
||||||
?: error("DECL_DESTRUCTURE expects DestructureDecl at $constId")
|
?: error("DECL_DESTRUCTURE expects DestructureDecl at $constId")
|
||||||
val value = frame.slotToObj(slot)
|
val value = frame.slotToObj(slot)
|
||||||
val scope = frame.ensureScope()
|
assignDestructurePattern(frame, decl.pattern, value, decl.pos)
|
||||||
for (name in decl.names) {
|
|
||||||
scope.addItem(name, true, ObjVoid, decl.visibility, isTransient = decl.isTransient)
|
|
||||||
}
|
|
||||||
decl.pattern.setAt(decl.pos, scope, value)
|
|
||||||
if (!decl.isMutable) {
|
|
||||||
for (name in decl.names) {
|
|
||||||
val rec = scope.objects[name] ?: continue
|
|
||||||
val immutableRec = rec.copy(isMutable = false)
|
|
||||||
scope.objects[name] = immutableRec
|
|
||||||
scope.localBindings[name] = immutableRec
|
|
||||||
scope.updateSlotFor(name, immutableRec)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (slot >= frame.fn.scopeSlotCount) {
|
if (slot >= frame.fn.scopeSlotCount) {
|
||||||
frame.storeObjResult(slot, ObjVoid)
|
frame.storeObjResult(slot, ObjVoid)
|
||||||
}
|
}
|
||||||
@ -1417,21 +1401,136 @@ class CmdDeclDestructure(internal val constId: Int, internal val slot: Int) : Cm
|
|||||||
|
|
||||||
class CmdAssignDestructure(internal val constId: Int, internal val slot: Int) : Cmd() {
|
class CmdAssignDestructure(internal val constId: Int, internal val slot: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
if (frame.fn.localSlotNames.isNotEmpty()) {
|
|
||||||
frame.syncFrameToScope(useRefs = true)
|
|
||||||
}
|
|
||||||
val decl = frame.fn.constants[constId] as? BytecodeConst.DestructureAssign
|
val decl = frame.fn.constants[constId] as? BytecodeConst.DestructureAssign
|
||||||
?: error("ASSIGN_DESTRUCTURE expects DestructureAssign at $constId")
|
?: error("ASSIGN_DESTRUCTURE expects DestructureAssign at $constId")
|
||||||
val value = frame.slotToObj(slot)
|
val value = frame.slotToObj(slot)
|
||||||
decl.pattern.setAt(decl.pos, frame.ensureScope(), value)
|
assignDestructurePattern(frame, decl.pattern, value, decl.pos)
|
||||||
if (frame.fn.localSlotNames.isNotEmpty()) {
|
|
||||||
frame.syncScopeToFrame()
|
|
||||||
}
|
|
||||||
frame.storeObjResult(slot, value)
|
frame.storeObjResult(slot, value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun assignDestructurePattern(frame: CmdFrame, pattern: ListLiteralRef, value: Obj, pos: Pos) {
|
||||||
|
val sourceList = (value as? ObjList)?.list
|
||||||
|
?: throw ScriptError(pos, "destructuring assignment requires a list on the right side")
|
||||||
|
|
||||||
|
val entries = pattern.entries()
|
||||||
|
val ellipsisIdx = entries.indexOfFirst { it is ListEntry.Spread }
|
||||||
|
if (entries.count { it is ListEntry.Spread } > 1) {
|
||||||
|
throw ScriptError(pos, "destructuring pattern can have only one splat")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ellipsisIdx < 0) {
|
||||||
|
if (sourceList.size < entries.size) {
|
||||||
|
throw ScriptError(pos, "too few elements for destructuring")
|
||||||
|
}
|
||||||
|
for (i in entries.indices) {
|
||||||
|
val entry = entries[i]
|
||||||
|
if (entry is ListEntry.Element) {
|
||||||
|
assignDestructureTarget(frame, entry.ref, sourceList[i], pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val headCount = ellipsisIdx
|
||||||
|
val tailCount = entries.size - ellipsisIdx - 1
|
||||||
|
if (sourceList.size < headCount + tailCount) {
|
||||||
|
throw ScriptError(pos, "too few elements for destructuring")
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in 0 until headCount) {
|
||||||
|
val entry = entries[i]
|
||||||
|
if (entry is ListEntry.Element) {
|
||||||
|
assignDestructureTarget(frame, entry.ref, sourceList[i], pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in 0 until tailCount) {
|
||||||
|
val entry = entries[entries.size - 1 - i]
|
||||||
|
if (entry is ListEntry.Element) {
|
||||||
|
assignDestructureTarget(frame, entry.ref, sourceList[sourceList.size - 1 - i], pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val spreadEntry = entries[ellipsisIdx] as ListEntry.Spread
|
||||||
|
val spreadList = sourceList.subList(headCount, sourceList.size - tailCount)
|
||||||
|
assignDestructureTarget(frame, spreadEntry.ref, ObjList(spreadList.toMutableList()), pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun assignDestructureTarget(frame: CmdFrame, ref: ObjRef, value: Obj, pos: Pos) {
|
||||||
|
when (ref) {
|
||||||
|
is ListLiteralRef -> assignDestructurePattern(frame, ref, value, pos)
|
||||||
|
is LocalSlotRef -> {
|
||||||
|
val index = resolveLocalSlotIndex(frame.fn, ref.name, preferCapture = ref.captureOwnerScopeId != null)
|
||||||
|
if (index != null) {
|
||||||
|
frame.frame.setObj(index, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is LocalVarRef -> {
|
||||||
|
val index = resolveLocalSlotIndex(frame.fn, ref.name, preferCapture = false)
|
||||||
|
if (index != null) {
|
||||||
|
frame.frame.setObj(index, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is FastLocalVarRef -> {
|
||||||
|
val index = resolveLocalSlotIndex(frame.fn, ref.name, preferCapture = false)
|
||||||
|
if (index != null) {
|
||||||
|
frame.frame.setObj(index, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
ref.setAt(pos, frame.ensureScope(), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveLocalSlotIndex(fn: CmdFunction, name: String, preferCapture: Boolean): Int? {
|
||||||
|
val names = fn.localSlotNames
|
||||||
|
if (preferCapture) {
|
||||||
|
for (i in names.indices) {
|
||||||
|
if (names[i] == name && fn.localSlotCaptures.getOrNull(i) == true) return i
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i in names.indices) {
|
||||||
|
if (names[i] == name && fn.localSlotCaptures.getOrNull(i) != true) return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i in names.indices) {
|
||||||
|
if (names[i] == name) return i
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isAstStatement(stmt: Statement): Boolean {
|
||||||
|
return when (stmt) {
|
||||||
|
is ExpressionStatement,
|
||||||
|
is IfStatement,
|
||||||
|
is ForInStatement,
|
||||||
|
is WhileStatement,
|
||||||
|
is DoWhileStatement,
|
||||||
|
is BlockStatement,
|
||||||
|
is InlineBlockStatement,
|
||||||
|
is VarDeclStatement,
|
||||||
|
is DelegatedVarDeclStatement,
|
||||||
|
is DestructuringVarDeclStatement,
|
||||||
|
is BreakStatement,
|
||||||
|
is ContinueStatement,
|
||||||
|
is ReturnStatement,
|
||||||
|
is ThrowStatement,
|
||||||
|
is net.sergeych.lyng.NopStatement,
|
||||||
|
is ExtensionPropertyDeclStatement,
|
||||||
|
is ClassDeclStatement,
|
||||||
|
is FunctionDeclStatement,
|
||||||
|
is EnumDeclStatement,
|
||||||
|
is TryStatement,
|
||||||
|
is WhenStatement -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CmdDeclExtProperty(internal val constId: Int, internal val slot: Int) : Cmd() {
|
class CmdDeclExtProperty(internal val constId: Int, internal val slot: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
val decl = frame.fn.constants[constId] as? BytecodeConst.ExtensionPropertyDecl
|
val decl = frame.fn.constants[constId] as? BytecodeConst.ExtensionPropertyDecl
|
||||||
@ -1511,10 +1610,12 @@ class CmdCallSlot(
|
|||||||
val result = if (canPool) {
|
val result = if (canPool) {
|
||||||
frame.ensureScope().withChildFrame(args) { child -> callee.callOn(child) }
|
frame.ensureScope().withChildFrame(args) { child -> callee.callOn(child) }
|
||||||
} else {
|
} else {
|
||||||
// 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 && callee !is BytecodeCallable) {
|
if (callee is Statement) {
|
||||||
frame.syncFrameToScope(useRefs = true)
|
val bytecodeBody = (callee as? BytecodeBodyProvider)?.bytecodeBody()
|
||||||
|
if (callee !is BytecodeStatement && callee !is BytecodeCallable && bytecodeBody == null && isAstStatement(callee)) {
|
||||||
|
scope.raiseIllegalState("bytecode runtime cannot call non-bytecode Statement")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
callee.callOn(scope.createChildScope(scope.pos, args = args))
|
callee.callOn(scope.createChildScope(scope.pos, args = args))
|
||||||
}
|
}
|
||||||
@ -1853,37 +1954,6 @@ class CmdSetIndex(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdEvalRef(internal val id: Int, internal val dst: Int) : Cmd() {
|
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
|
||||||
if (frame.fn.localSlotNames.isNotEmpty()) {
|
|
||||||
frame.syncFrameToScope(useRefs = true)
|
|
||||||
}
|
|
||||||
val ref = frame.fn.constants[id] as? BytecodeConst.Ref
|
|
||||||
?: error("EVAL_REF expects Ref at $id")
|
|
||||||
val result = ref.value.evalValue(frame.ensureScope())
|
|
||||||
if (frame.fn.localSlotNames.isNotEmpty()) {
|
|
||||||
frame.syncScopeToFrame()
|
|
||||||
}
|
|
||||||
frame.storeObjResult(dst, result)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CmdMakeValueFn(internal val id: Int, internal val dst: Int) : Cmd() {
|
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
|
||||||
val valueFn = frame.fn.constants.getOrNull(id) as? BytecodeConst.ValueFn
|
|
||||||
?: error("MAKE_VALUE_FN expects ValueFn at $id")
|
|
||||||
val scope = frame.ensureScope()
|
|
||||||
val previousCaptures = scope.captureRecords
|
|
||||||
val captureRecords = valueFn.captureTableId?.let { frame.buildCaptureRecords(it) }
|
|
||||||
scope.captureRecords = captureRecords
|
|
||||||
val result = valueFn.fn(scope).value
|
|
||||||
scope.captureRecords = previousCaptures
|
|
||||||
frame.storeObjResult(dst, result)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CmdMakeLambda(internal val id: Int, internal val dst: Int) : Cmd() {
|
class CmdMakeLambda(internal val id: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
val lambdaConst = frame.fn.constants.getOrNull(id) as? BytecodeConst.LambdaFn
|
val lambdaConst = frame.fn.constants.getOrNull(id) as? BytecodeConst.LambdaFn
|
||||||
@ -1934,18 +2004,40 @@ class BytecodeLambdaCallable(
|
|||||||
closureScope.raiseIllegalState("bytecode lambda capture records missing")
|
closureScope.raiseIllegalState("bytecode lambda capture records missing")
|
||||||
}
|
}
|
||||||
if (argsDeclaration == null) {
|
if (argsDeclaration == null) {
|
||||||
val l = scope.args.list
|
// Bound in the bytecode entry binder.
|
||||||
|
} else {
|
||||||
|
// args bound into frame slots in the bytecode entry binder
|
||||||
|
}
|
||||||
|
return try {
|
||||||
|
val binder: suspend (CmdFrame, Arguments) -> Unit = { frame, arguments ->
|
||||||
|
val slotPlan = fn.localSlotPlanByName()
|
||||||
|
if (argsDeclaration == null) {
|
||||||
|
val l = arguments.list
|
||||||
val itValue: Obj = when (l.size) {
|
val itValue: Obj = when (l.size) {
|
||||||
0 -> ObjVoid
|
0 -> ObjVoid
|
||||||
1 -> l[0]
|
1 -> l[0]
|
||||||
else -> ObjList(l.toMutableList())
|
else -> ObjList(l.toMutableList())
|
||||||
}
|
}
|
||||||
context.addItem("it", false, itValue, recordType = ObjRecord.Type.Argument)
|
val itSlot = slotPlan["it"]
|
||||||
} else {
|
if (itSlot != null) {
|
||||||
argsDeclaration.assignToContext(context, scope.args, defaultAccessType = AccessType.Val)
|
frame.frame.setObj(itSlot, itValue)
|
||||||
|
if (context.getLocalRecordDirect("it") == null) {
|
||||||
|
context.addItem("it", false, FrameSlotRef(frame.frame, itSlot), recordType = ObjRecord.Type.Argument)
|
||||||
}
|
}
|
||||||
return try {
|
} else if (context.getLocalRecordDirect("it") == null) {
|
||||||
CmdVm().execute(fn, context, scope.args.list)
|
context.addItem("it", false, itValue, recordType = ObjRecord.Type.Argument)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
argsDeclaration.assignToFrame(
|
||||||
|
context,
|
||||||
|
arguments,
|
||||||
|
slotPlan,
|
||||||
|
frame.frame,
|
||||||
|
defaultAccessType = AccessType.Val
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CmdVm().execute(fn, context, scope.args, binder)
|
||||||
} catch (e: ReturnException) {
|
} catch (e: ReturnException) {
|
||||||
if (e.label == null || returnLabels.contains(e.label)) e.result else throw e
|
if (e.label == null || returnLabels.contains(e.label)) e.result else throw e
|
||||||
}
|
}
|
||||||
@ -2361,15 +2453,17 @@ class CmdFrame(
|
|||||||
target.setSlotValue(index, value)
|
target.setSlotValue(index, value)
|
||||||
} else {
|
} else {
|
||||||
val localIndex = slot - fn.scopeSlotCount
|
val localIndex = slot - fn.scopeSlotCount
|
||||||
val existing = frame.getObj(localIndex)
|
when (val existing = frame.getRawObj(localIndex)) {
|
||||||
if (existing is FrameSlotRef) {
|
is FrameSlotRef -> {
|
||||||
existing.write(value)
|
existing.write(value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (existing is RecordSlotRef) {
|
is RecordSlotRef -> {
|
||||||
existing.write(value)
|
existing.write(value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
frame.setObj(localIndex, value)
|
frame.setObj(localIndex, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2405,15 +2499,17 @@ class CmdFrame(
|
|||||||
target.setSlotValue(index, ObjInt.of(value))
|
target.setSlotValue(index, ObjInt.of(value))
|
||||||
} else {
|
} else {
|
||||||
val localIndex = slot - fn.scopeSlotCount
|
val localIndex = slot - fn.scopeSlotCount
|
||||||
val existing = frame.getObj(localIndex)
|
when (val existing = frame.getRawObj(localIndex)) {
|
||||||
if (existing is FrameSlotRef) {
|
is FrameSlotRef -> {
|
||||||
existing.write(ObjInt.of(value))
|
existing.write(ObjInt.of(value))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (existing is RecordSlotRef) {
|
is RecordSlotRef -> {
|
||||||
existing.write(ObjInt.of(value))
|
existing.write(ObjInt.of(value))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
frame.setInt(localIndex, value)
|
frame.setInt(localIndex, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2451,15 +2547,17 @@ class CmdFrame(
|
|||||||
target.setSlotValue(index, ObjReal.of(value))
|
target.setSlotValue(index, ObjReal.of(value))
|
||||||
} else {
|
} else {
|
||||||
val localIndex = slot - fn.scopeSlotCount
|
val localIndex = slot - fn.scopeSlotCount
|
||||||
val existing = frame.getObj(localIndex)
|
when (val existing = frame.getRawObj(localIndex)) {
|
||||||
if (existing is FrameSlotRef) {
|
is FrameSlotRef -> {
|
||||||
existing.write(ObjReal.of(value))
|
existing.write(ObjReal.of(value))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (existing is RecordSlotRef) {
|
is RecordSlotRef -> {
|
||||||
existing.write(ObjReal.of(value))
|
existing.write(ObjReal.of(value))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
frame.setReal(localIndex, value)
|
frame.setReal(localIndex, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2495,15 +2593,17 @@ class CmdFrame(
|
|||||||
target.setSlotValue(index, if (value) ObjTrue else ObjFalse)
|
target.setSlotValue(index, if (value) ObjTrue else ObjFalse)
|
||||||
} else {
|
} else {
|
||||||
val localIndex = slot - fn.scopeSlotCount
|
val localIndex = slot - fn.scopeSlotCount
|
||||||
val existing = frame.getObj(localIndex)
|
when (val existing = frame.getRawObj(localIndex)) {
|
||||||
if (existing is FrameSlotRef) {
|
is FrameSlotRef -> {
|
||||||
existing.write(if (value) ObjTrue else ObjFalse)
|
existing.write(if (value) ObjTrue else ObjFalse)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (existing is RecordSlotRef) {
|
is RecordSlotRef -> {
|
||||||
existing.write(if (value) ObjTrue else ObjFalse)
|
existing.write(if (value) ObjTrue else ObjFalse)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
frame.setBool(localIndex, value)
|
frame.setBool(localIndex, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2560,13 +2660,6 @@ class CmdFrame(
|
|||||||
if (fn.localSlotCaptures.getOrNull(local) == true) {
|
if (fn.localSlotCaptures.getOrNull(local) == true) {
|
||||||
return localSlotToObj(local)
|
return localSlotToObj(local)
|
||||||
}
|
}
|
||||||
val localName = fn.localSlotNames.getOrNull(local)
|
|
||||||
if (localName != null && fn.localSlotDelegated.getOrNull(local) != true) {
|
|
||||||
val rec = scope.getLocalRecordDirect(localName) ?: scope.localBindings[localName]
|
|
||||||
if (rec != null && (rec.type == ObjRecord.Type.Delegated || rec.type == ObjRecord.Type.Property || rec.value is ObjProperty)) {
|
|
||||||
return scope.resolve(rec, localName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return when (frame.getSlotTypeCode(local)) {
|
return when (frame.getSlotTypeCode(local)) {
|
||||||
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))
|
||||||
@ -2612,68 +2705,6 @@ class CmdFrame(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun syncFrameToScope(useRefs: Boolean = false) {
|
|
||||||
val names = fn.localSlotNames
|
|
||||||
if (names.isEmpty()) return
|
|
||||||
for (i in names.indices) {
|
|
||||||
val name = names[i] ?: continue
|
|
||||||
if (fn.localSlotCaptures.getOrNull(i) == true) continue
|
|
||||||
if (scopeSlotNames.contains(name)) continue
|
|
||||||
val target = resolveLocalScope(i) ?: continue
|
|
||||||
val isDelegated = fn.localSlotDelegated.getOrNull(i) == true
|
|
||||||
val value = if (useRefs) FrameSlotRef(frame, i) else localSlotToObj(i)
|
|
||||||
val rec = target.getLocalRecordDirect(name)
|
|
||||||
if (rec == null) {
|
|
||||||
val isMutable = fn.localSlotMutables.getOrElse(i) { true }
|
|
||||||
if (isDelegated) {
|
|
||||||
val delegatedRec = target.addItem(
|
|
||||||
name,
|
|
||||||
isMutable,
|
|
||||||
ObjNull,
|
|
||||||
recordType = ObjRecord.Type.Delegated
|
|
||||||
)
|
|
||||||
delegatedRec.delegate = localSlotToObj(i)
|
|
||||||
} else {
|
|
||||||
target.addItem(name, isMutable, value)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (isDelegated && rec.type == ObjRecord.Type.Delegated) {
|
|
||||||
rec.delegate = localSlotToObj(i)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val existing = rec.value
|
|
||||||
if (existing is FrameSlotRef && !useRefs) continue
|
|
||||||
rec.value = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun syncScopeToFrame() {
|
|
||||||
val names = fn.localSlotNames
|
|
||||||
if (names.isEmpty()) return
|
|
||||||
for (i in names.indices) {
|
|
||||||
val name = names[i] ?: continue
|
|
||||||
if (fn.localSlotCaptures.getOrNull(i) == true) continue
|
|
||||||
val target = resolveLocalScope(i) ?: continue
|
|
||||||
val rec = target.getLocalRecordDirect(name) ?: continue
|
|
||||||
if (fn.localSlotDelegated.getOrNull(i) == true && rec.type == ObjRecord.Type.Delegated) {
|
|
||||||
val delegate = rec.delegate ?: ObjNull
|
|
||||||
frame.setObj(i, delegate)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val value = rec.value
|
|
||||||
if (value is FrameSlotRef) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
when (value) {
|
|
||||||
is ObjInt -> frame.setInt(i, value.value)
|
|
||||||
is ObjReal -> frame.setReal(i, value.value)
|
|
||||||
is ObjBool -> frame.setBool(i, value.value)
|
|
||||||
else -> frame.setObj(i, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun buildArguments(argBase: Int, argCount: Int): Arguments {
|
suspend fun buildArguments(argBase: Int, argCount: Int): Arguments {
|
||||||
if (argCount == 0) return Arguments.EMPTY
|
if (argCount == 0) return Arguments.EMPTY
|
||||||
if ((argCount and ARG_PLAN_FLAG) != 0) {
|
if ((argCount and ARG_PLAN_FLAG) != 0) {
|
||||||
@ -2747,7 +2778,7 @@ class CmdFrame(
|
|||||||
return scope
|
return scope
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scopeTarget(slot: Int): Scope {
|
internal fun scopeTarget(slot: Int): Scope {
|
||||||
return if (slot < fn.scopeSlotCount && fn.scopeSlotIsModule.getOrNull(slot) == true) {
|
return if (slot < fn.scopeSlotCount && fn.scopeSlotIsModule.getOrNull(slot) == true) {
|
||||||
moduleScope
|
moduleScope
|
||||||
} else {
|
} else {
|
||||||
@ -2828,7 +2859,7 @@ class CmdFrame(
|
|||||||
target.setSlotValue(index, value)
|
target.setSlotValue(index, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ensureScopeSlot(target: Scope, slot: Int): Int {
|
internal fun ensureScopeSlot(target: Scope, slot: Int): Int {
|
||||||
val name = fn.scopeSlotNames[slot]
|
val name = fn.scopeSlotNames[slot]
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
val existing = target.getSlotIndexOf(name)
|
val existing = target.getSlotIndexOf(name)
|
||||||
|
|||||||
@ -31,7 +31,6 @@ enum class Opcode(val code: Int) {
|
|||||||
RANGE_INT_BOUNDS(0x0B),
|
RANGE_INT_BOUNDS(0x0B),
|
||||||
MAKE_RANGE(0x0C),
|
MAKE_RANGE(0x0C),
|
||||||
LOAD_THIS(0x0D),
|
LOAD_THIS(0x0D),
|
||||||
MAKE_VALUE_FN(0x0E),
|
|
||||||
LOAD_THIS_VARIANT(0x0F),
|
LOAD_THIS_VARIANT(0x0F),
|
||||||
|
|
||||||
INT_TO_REAL(0x10),
|
INT_TO_REAL(0x10),
|
||||||
@ -160,7 +159,6 @@ enum class Opcode(val code: Int) {
|
|||||||
STORE_BOOL_ADDR(0xB9),
|
STORE_BOOL_ADDR(0xB9),
|
||||||
THROW(0xBB),
|
THROW(0xBB),
|
||||||
RETHROW_PENDING(0xBC),
|
RETHROW_PENDING(0xBC),
|
||||||
DECL_EXEC(0xBD),
|
|
||||||
DECL_ENUM(0xBE),
|
DECL_ENUM(0xBE),
|
||||||
ITER_PUSH(0xBF),
|
ITER_PUSH(0xBF),
|
||||||
ITER_POP(0xC0),
|
ITER_POP(0xC0),
|
||||||
|
|||||||
@ -31,6 +31,12 @@ extern class List<T> : Array<T> {
|
|||||||
fun add(value: T, more...): Void
|
fun add(value: T, more...): Void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern class RingBuffer<T> : Iterable<T> {
|
||||||
|
val size: Int
|
||||||
|
fun first(): T
|
||||||
|
fun add(value: T): Void
|
||||||
|
}
|
||||||
|
|
||||||
extern class Set<T> : Collection<T> {
|
extern class Set<T> : Collection<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,8 +276,10 @@ fun Iterable<T>.shuffled(): List<T> {
|
|||||||
*/
|
*/
|
||||||
fun Iterable<Iterable<T>>.flatten(): List<T> {
|
fun Iterable<Iterable<T>>.flatten(): List<T> {
|
||||||
var result: List<T> = List()
|
var result: List<T> = List()
|
||||||
forEach { i ->
|
for (i in this) {
|
||||||
i.forEach { result += it }
|
for (item in i) {
|
||||||
|
result += item
|
||||||
|
}
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user