Compare commits
4 Commits
f5ced02505
...
5fd322a43e
| Author | SHA1 | Date | |
|---|---|---|---|
| 5fd322a43e | |||
| 3a46e59ec8 | |||
| 026b023892 | |||
| f9fe3d1186 |
@ -14,6 +14,25 @@ Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM te
|
|||||||
- [x] Preserve existing error/stack semantics.
|
- [x] Preserve existing error/stack semantics.
|
||||||
- [x] JVM tests must be green before commit.
|
- [x] JVM tests must be green before commit.
|
||||||
|
|
||||||
|
## Remaining Migration (prioritized)
|
||||||
|
|
||||||
|
- [x] Step 4: Allow bytecode wrapping for supported declaration statements.
|
||||||
|
- [x] Enable `DestructuringVarDeclStatement` and `ExtensionPropertyDeclStatement` in `containsUnsupportedForBytecode`.
|
||||||
|
- [x] Keep JVM tests green before commit.
|
||||||
|
- [x] Step 5: Enable bytecode for delegated var declarations.
|
||||||
|
- [x] Revisit `containsDelegatedRefs` guard for `DelegatedVarDeclStatement`.
|
||||||
|
- [x] Ensure delegate binding uses explicit `Statement` objects (no inline suspend lambdas).
|
||||||
|
- [x] Keep JVM tests green before commit.
|
||||||
|
- [x] Step 6: Map literal spread in bytecode.
|
||||||
|
- [x] Replace `MapLiteralEntry.Spread` bytecode exception with runtime `putAll`/merge logic.
|
||||||
|
- [x] Step 7: Class-scope member refs in bytecode.
|
||||||
|
- [x] Support `ClassScopeMemberRef` without scope-map fallback.
|
||||||
|
- [ ] Step 8: ObjDynamic member access in bytecode.
|
||||||
|
- [ ] Allow dynamic receiver field/method lookup without falling back to interpreter.
|
||||||
|
- [ ] Step 9: Module-level bytecode execution.
|
||||||
|
- [ ] Compile `Script` bodies to bytecode instead of interpreting at module scope.
|
||||||
|
- [ ] Keep import/module slot seeding in frame-only flow.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- Keep imports bound to module frame slots; no scope map writes for imports.
|
- Keep imports bound to module frame slots; no scope map writes for imports.
|
||||||
|
|||||||
@ -1893,10 +1893,13 @@ class Compiler(
|
|||||||
is BlockStatement -> target.statements().any { containsUnsupportedForBytecode(it) }
|
is BlockStatement -> target.statements().any { containsUnsupportedForBytecode(it) }
|
||||||
is InlineBlockStatement -> target.statements().any { containsUnsupportedForBytecode(it) }
|
is InlineBlockStatement -> target.statements().any { containsUnsupportedForBytecode(it) }
|
||||||
is VarDeclStatement -> target.initializer?.let { containsUnsupportedForBytecode(it) } ?: false
|
is VarDeclStatement -> target.initializer?.let { containsUnsupportedForBytecode(it) } ?: false
|
||||||
|
is DestructuringVarDeclStatement -> containsUnsupportedForBytecode(target.initializer)
|
||||||
|
is DelegatedVarDeclStatement -> containsUnsupportedForBytecode(target.initializer)
|
||||||
is BreakStatement -> target.resultExpr?.let { containsUnsupportedForBytecode(it) } ?: false
|
is BreakStatement -> target.resultExpr?.let { containsUnsupportedForBytecode(it) } ?: false
|
||||||
is ContinueStatement -> false
|
is ContinueStatement -> false
|
||||||
is ReturnStatement -> target.resultExpr?.let { containsUnsupportedForBytecode(it) } ?: false
|
is ReturnStatement -> target.resultExpr?.let { containsUnsupportedForBytecode(it) } ?: false
|
||||||
is ThrowStatement -> containsUnsupportedForBytecode(target.throwExpr)
|
is ThrowStatement -> containsUnsupportedForBytecode(target.throwExpr)
|
||||||
|
is ExtensionPropertyDeclStatement -> false
|
||||||
is TryStatement -> {
|
is TryStatement -> {
|
||||||
containsUnsupportedForBytecode(target.body) ||
|
containsUnsupportedForBytecode(target.body) ||
|
||||||
target.catches.any { containsUnsupportedForBytecode(it.block) } ||
|
target.catches.any { containsUnsupportedForBytecode(it.block) } ||
|
||||||
@ -1928,6 +1931,7 @@ class Compiler(
|
|||||||
is CastRef -> containsUnsupportedRef(ref.castValueRef()) || containsUnsupportedRef(ref.castTypeRef())
|
is CastRef -> containsUnsupportedRef(ref.castValueRef()) || containsUnsupportedRef(ref.castTypeRef())
|
||||||
is net.sergeych.lyng.obj.TypeDeclRef -> false
|
is net.sergeych.lyng.obj.TypeDeclRef -> false
|
||||||
is AssignRef -> {
|
is AssignRef -> {
|
||||||
|
if (ref.target is ListLiteralRef) return true
|
||||||
val target = ref.target as? LocalSlotRef
|
val target = ref.target as? LocalSlotRef
|
||||||
(target?.isDelegated == true) || containsUnsupportedRef(ref.value)
|
(target?.isDelegated == true) || containsUnsupportedRef(ref.value)
|
||||||
}
|
}
|
||||||
@ -1979,7 +1983,7 @@ class Compiler(
|
|||||||
is ImplicitThisMethodCallRef -> false
|
is ImplicitThisMethodCallRef -> false
|
||||||
is QualifiedThisMethodSlotCallRef -> false
|
is QualifiedThisMethodSlotCallRef -> false
|
||||||
is QualifiedThisFieldSlotRef -> false
|
is QualifiedThisFieldSlotRef -> false
|
||||||
is ClassScopeMemberRef -> true
|
is ClassScopeMemberRef -> false
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1990,7 +1994,7 @@ class Compiler(
|
|||||||
is ExpressionStatement -> containsDelegatedRefs(target.ref)
|
is ExpressionStatement -> containsDelegatedRefs(target.ref)
|
||||||
is BlockStatement -> target.statements().any { containsDelegatedRefs(it) }
|
is BlockStatement -> target.statements().any { containsDelegatedRefs(it) }
|
||||||
is VarDeclStatement -> target.initializer?.let { containsDelegatedRefs(it) } ?: false
|
is VarDeclStatement -> target.initializer?.let { containsDelegatedRefs(it) } ?: false
|
||||||
is DelegatedVarDeclStatement -> true
|
is DelegatedVarDeclStatement -> containsDelegatedRefs(target.initializer)
|
||||||
is DestructuringVarDeclStatement -> containsDelegatedRefs(target.initializer)
|
is DestructuringVarDeclStatement -> containsDelegatedRefs(target.initializer)
|
||||||
is IfStatement -> {
|
is IfStatement -> {
|
||||||
containsDelegatedRefs(target.condition) ||
|
containsDelegatedRefs(target.condition) ||
|
||||||
|
|||||||
@ -310,6 +310,7 @@ class BytecodeCompiler(
|
|||||||
is CallRef -> compileCall(ref)
|
is CallRef -> compileCall(ref)
|
||||||
is MethodCallRef -> compileMethodCall(ref)
|
is MethodCallRef -> compileMethodCall(ref)
|
||||||
is FieldRef -> compileFieldRef(ref)
|
is FieldRef -> compileFieldRef(ref)
|
||||||
|
is ClassScopeMemberRef -> compileClassScopeMemberRef(ref)
|
||||||
is ThisFieldSlotRef -> compileThisFieldSlotRef(ref)
|
is ThisFieldSlotRef -> compileThisFieldSlotRef(ref)
|
||||||
is QualifiedThisFieldSlotRef -> compileQualifiedThisFieldSlotRef(ref)
|
is QualifiedThisFieldSlotRef -> compileQualifiedThisFieldSlotRef(ref)
|
||||||
is ImplicitThisMemberRef -> {
|
is ImplicitThisMemberRef -> {
|
||||||
@ -529,7 +530,35 @@ class BytecodeCompiler(
|
|||||||
builder.emit(Opcode.SET_INDEX, dst, keySlot, value.slot)
|
builder.emit(Opcode.SET_INDEX, dst, keySlot, value.slot)
|
||||||
}
|
}
|
||||||
is net.sergeych.lyng.obj.MapLiteralEntry.Spread -> {
|
is net.sergeych.lyng.obj.MapLiteralEntry.Spread -> {
|
||||||
throw BytecodeCompileException("Map spread is not supported in bytecode", Pos.builtIn)
|
if (entry.ref is ListLiteralRef) {
|
||||||
|
throw BytecodeCompileException(
|
||||||
|
"spread element in map literal must be a Map",
|
||||||
|
Pos.builtIn
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val value = compileRefWithFallback(entry.ref, null, Pos.builtIn) ?: return null
|
||||||
|
val mapClassId = builder.addConst(BytecodeConst.ObjRef(ObjMap.type))
|
||||||
|
val mapClassSlot = allocSlot()
|
||||||
|
builder.emit(Opcode.CONST_OBJ, mapClassId, mapClassSlot)
|
||||||
|
val checkSlot = allocSlot()
|
||||||
|
builder.emit(Opcode.CHECK_IS, value.slot, mapClassSlot, checkSlot)
|
||||||
|
val okLabel = builder.label()
|
||||||
|
val endLabel = builder.label()
|
||||||
|
builder.emit(
|
||||||
|
Opcode.JMP_IF_TRUE,
|
||||||
|
listOf(CmdBuilder.Operand.IntVal(checkSlot), CmdBuilder.Operand.LabelRef(okLabel))
|
||||||
|
)
|
||||||
|
val msgId = builder.addConst(BytecodeConst.StringVal("spread element in map literal must be a Map"))
|
||||||
|
val msgSlot = allocSlot()
|
||||||
|
builder.emit(Opcode.CONST_OBJ, msgId, msgSlot)
|
||||||
|
val posId = builder.addConst(BytecodeConst.PosVal(Pos.builtIn))
|
||||||
|
builder.emit(Opcode.THROW, posId, msgSlot)
|
||||||
|
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
|
||||||
|
builder.mark(okLabel)
|
||||||
|
val mergedSlot = allocSlot()
|
||||||
|
builder.emit(Opcode.ADD_OBJ, dst, value.slot, mergedSlot)
|
||||||
|
builder.emit(Opcode.MOVE_OBJ, mergedSlot, dst)
|
||||||
|
builder.mark(endLabel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1619,6 +1648,21 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
val value = compileRef(assignValue(ref)) ?: return null
|
val value = compileRef(assignValue(ref)) ?: return null
|
||||||
val target = ref.target
|
val target = ref.target
|
||||||
|
if (target is ClassScopeMemberRef) {
|
||||||
|
val className = target.ownerClassName()
|
||||||
|
val classSlot = compileRef(LocalVarRef(className, Pos.builtIn)) ?: run {
|
||||||
|
val cls = resolveTypeNameClass(className) ?: return null
|
||||||
|
val id = builder.addConst(BytecodeConst.ObjRef(cls))
|
||||||
|
val slot = allocSlot()
|
||||||
|
builder.emit(Opcode.CONST_OBJ, id, slot)
|
||||||
|
updateSlotType(slot, SlotType.OBJ)
|
||||||
|
CompiledValue(slot, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
val classObj = ensureObjSlot(classSlot)
|
||||||
|
val nameId = builder.addConst(BytecodeConst.StringVal(target.name))
|
||||||
|
builder.emit(Opcode.SET_CLASS_SCOPE, classObj.slot, nameId, value.slot)
|
||||||
|
return value
|
||||||
|
}
|
||||||
if (target is FieldRef) {
|
if (target is FieldRef) {
|
||||||
val receiverClass = resolveReceiverClass(target.target)
|
val receiverClass = resolveReceiverClass(target.target)
|
||||||
?: throw BytecodeCompileException(
|
?: throw BytecodeCompileException(
|
||||||
@ -2116,6 +2160,24 @@ class BytecodeCompiler(
|
|||||||
return CompiledValue(dst, SlotType.OBJ)
|
return CompiledValue(dst, SlotType.OBJ)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun compileClassScopeMemberRef(ref: ClassScopeMemberRef): CompiledValue? {
|
||||||
|
val className = ref.ownerClassName()
|
||||||
|
val classSlot = compileRef(LocalVarRef(className, Pos.builtIn)) ?: run {
|
||||||
|
val cls = resolveTypeNameClass(className) ?: return null
|
||||||
|
val id = builder.addConst(BytecodeConst.ObjRef(cls))
|
||||||
|
val slot = allocSlot()
|
||||||
|
builder.emit(Opcode.CONST_OBJ, id, slot)
|
||||||
|
updateSlotType(slot, SlotType.OBJ)
|
||||||
|
CompiledValue(slot, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
val classObj = ensureObjSlot(classSlot)
|
||||||
|
val nameId = builder.addConst(BytecodeConst.StringVal(ref.name))
|
||||||
|
val dst = allocSlot()
|
||||||
|
builder.emit(Opcode.GET_CLASS_SCOPE, classObj.slot, nameId, dst)
|
||||||
|
updateSlotType(dst, SlotType.OBJ)
|
||||||
|
return CompiledValue(dst, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
|
||||||
private fun compileThisFieldSlotRef(ref: ThisFieldSlotRef): CompiledValue? {
|
private fun compileThisFieldSlotRef(ref: ThisFieldSlotRef): CompiledValue? {
|
||||||
val receiver = compileThisRef()
|
val receiver = compileThisRef()
|
||||||
val fieldId = ref.fieldId() ?: -1
|
val fieldId = ref.fieldId() ?: -1
|
||||||
|
|||||||
@ -193,6 +193,10 @@ class CmdBuilder {
|
|||||||
listOf(OperandKind.SLOT, OperandKind.ID, OperandKind.ID, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.ID, OperandKind.ID, OperandKind.SLOT)
|
||||||
Opcode.SET_MEMBER_SLOT ->
|
Opcode.SET_MEMBER_SLOT ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.ID, OperandKind.ID, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.ID, OperandKind.ID, OperandKind.SLOT)
|
||||||
|
Opcode.GET_CLASS_SCOPE ->
|
||||||
|
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT)
|
||||||
|
Opcode.SET_CLASS_SCOPE ->
|
||||||
|
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.ITER_PUSH ->
|
Opcode.ITER_PUSH ->
|
||||||
listOf(OperandKind.SLOT)
|
listOf(OperandKind.SLOT)
|
||||||
Opcode.ITER_POP, Opcode.ITER_CANCEL ->
|
Opcode.ITER_POP, Opcode.ITER_CANCEL ->
|
||||||
@ -395,6 +399,8 @@ class CmdBuilder {
|
|||||||
Opcode.LIST_LITERAL -> CmdListLiteral(operands[0], operands[1], operands[2], operands[3])
|
Opcode.LIST_LITERAL -> CmdListLiteral(operands[0], operands[1], operands[2], operands[3])
|
||||||
Opcode.GET_MEMBER_SLOT -> CmdGetMemberSlot(operands[0], operands[1], operands[2], operands[3])
|
Opcode.GET_MEMBER_SLOT -> CmdGetMemberSlot(operands[0], operands[1], operands[2], operands[3])
|
||||||
Opcode.SET_MEMBER_SLOT -> CmdSetMemberSlot(operands[0], operands[1], operands[2], operands[3])
|
Opcode.SET_MEMBER_SLOT -> CmdSetMemberSlot(operands[0], operands[1], operands[2], operands[3])
|
||||||
|
Opcode.GET_CLASS_SCOPE -> CmdGetClassScope(operands[0], operands[1], operands[2])
|
||||||
|
Opcode.SET_CLASS_SCOPE -> CmdSetClassScope(operands[0], operands[1], operands[2])
|
||||||
Opcode.ITER_PUSH -> CmdIterPush(operands[0])
|
Opcode.ITER_PUSH -> CmdIterPush(operands[0])
|
||||||
Opcode.ITER_POP -> CmdIterPop()
|
Opcode.ITER_POP -> CmdIterPop()
|
||||||
Opcode.ITER_CANCEL -> CmdIterCancel()
|
Opcode.ITER_CANCEL -> CmdIterCancel()
|
||||||
|
|||||||
@ -200,6 +200,8 @@ object CmdDisassembler {
|
|||||||
is CmdListLiteral -> Opcode.LIST_LITERAL to intArrayOf(cmd.planId, cmd.baseSlot, cmd.count, cmd.dst)
|
is CmdListLiteral -> Opcode.LIST_LITERAL to intArrayOf(cmd.planId, cmd.baseSlot, cmd.count, cmd.dst)
|
||||||
is CmdGetMemberSlot -> Opcode.GET_MEMBER_SLOT to intArrayOf(cmd.recvSlot, cmd.fieldId, cmd.methodId, cmd.dst)
|
is CmdGetMemberSlot -> Opcode.GET_MEMBER_SLOT to intArrayOf(cmd.recvSlot, cmd.fieldId, cmd.methodId, cmd.dst)
|
||||||
is CmdSetMemberSlot -> Opcode.SET_MEMBER_SLOT to intArrayOf(cmd.recvSlot, cmd.fieldId, cmd.methodId, cmd.valueSlot)
|
is CmdSetMemberSlot -> Opcode.SET_MEMBER_SLOT to intArrayOf(cmd.recvSlot, cmd.fieldId, cmd.methodId, cmd.valueSlot)
|
||||||
|
is CmdGetClassScope -> Opcode.GET_CLASS_SCOPE to intArrayOf(cmd.classSlot, cmd.nameId, cmd.dst)
|
||||||
|
is CmdSetClassScope -> Opcode.SET_CLASS_SCOPE to intArrayOf(cmd.classSlot, cmd.nameId, cmd.valueSlot)
|
||||||
is CmdIterPush -> Opcode.ITER_PUSH to intArrayOf(cmd.iterSlot)
|
is CmdIterPush -> Opcode.ITER_PUSH to intArrayOf(cmd.iterSlot)
|
||||||
is CmdIterPop -> Opcode.ITER_POP to intArrayOf()
|
is CmdIterPop -> Opcode.ITER_POP to intArrayOf()
|
||||||
is CmdIterCancel -> Opcode.ITER_CANCEL to intArrayOf()
|
is CmdIterCancel -> Opcode.ITER_CANCEL to intArrayOf()
|
||||||
@ -293,6 +295,10 @@ object CmdDisassembler {
|
|||||||
listOf(OperandKind.SLOT, OperandKind.ID, OperandKind.ID, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.ID, OperandKind.ID, OperandKind.SLOT)
|
||||||
Opcode.SET_MEMBER_SLOT ->
|
Opcode.SET_MEMBER_SLOT ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.ID, OperandKind.ID, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.ID, OperandKind.ID, OperandKind.SLOT)
|
||||||
|
Opcode.GET_CLASS_SCOPE ->
|
||||||
|
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT)
|
||||||
|
Opcode.SET_CLASS_SCOPE ->
|
||||||
|
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1493,6 +1493,40 @@ class CmdSetMemberSlot(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CmdGetClassScope(
|
||||||
|
internal val classSlot: Int,
|
||||||
|
internal val nameId: Int,
|
||||||
|
internal val dst: Int,
|
||||||
|
) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
val nameConst = frame.fn.constants.getOrNull(nameId) as? BytecodeConst.StringVal
|
||||||
|
?: error("GET_CLASS_SCOPE expects StringVal at $nameId")
|
||||||
|
val scope = frame.ensureScope()
|
||||||
|
val cls = frame.slotToObj(classSlot) as? ObjClass
|
||||||
|
?: scope.raiseSymbolNotFound(nameConst.value)
|
||||||
|
val rec = cls.readField(scope, nameConst.value)
|
||||||
|
val value = scope.resolve(rec, nameConst.value)
|
||||||
|
frame.storeObjResult(dst, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CmdSetClassScope(
|
||||||
|
internal val classSlot: Int,
|
||||||
|
internal val nameId: Int,
|
||||||
|
internal val valueSlot: Int,
|
||||||
|
) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
val nameConst = frame.fn.constants.getOrNull(nameId) as? BytecodeConst.StringVal
|
||||||
|
?: error("SET_CLASS_SCOPE expects StringVal at $nameId")
|
||||||
|
val scope = frame.ensureScope()
|
||||||
|
val cls = frame.slotToObj(classSlot) as? ObjClass
|
||||||
|
?: scope.raiseSymbolNotFound(nameConst.value)
|
||||||
|
cls.writeField(scope, nameConst.value, frame.slotToObj(valueSlot))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CmdCallMemberSlot(
|
class CmdCallMemberSlot(
|
||||||
internal val recvSlot: Int,
|
internal val recvSlot: Int,
|
||||||
internal val methodId: Int,
|
internal val methodId: Int,
|
||||||
|
|||||||
@ -140,6 +140,8 @@ enum class Opcode(val code: Int) {
|
|||||||
LIST_LITERAL(0xA5),
|
LIST_LITERAL(0xA5),
|
||||||
GET_MEMBER_SLOT(0xA8),
|
GET_MEMBER_SLOT(0xA8),
|
||||||
SET_MEMBER_SLOT(0xA9),
|
SET_MEMBER_SLOT(0xA9),
|
||||||
|
GET_CLASS_SCOPE(0xAA),
|
||||||
|
SET_CLASS_SCOPE(0xAB),
|
||||||
|
|
||||||
RESOLVE_SCOPE_SLOT(0xB1),
|
RESOLVE_SCOPE_SLOT(0xB1),
|
||||||
LOAD_OBJ_ADDR(0xB2),
|
LOAD_OBJ_ADDR(0xB2),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user