Remove interpreter paths and enforce bytecode-only execution
This commit is contained in:
parent
4cd8e5ded2
commit
90826b519d
@ -136,6 +136,38 @@ Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM te
|
|||||||
- [x] 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.
|
- [x] Replace bytecode entry seeding from Scope with frame-only arg/local binding.
|
||||||
|
|
||||||
|
## Interpreter Removal v2 (2026-02-12)
|
||||||
|
|
||||||
|
Goal: remove interpreter execution paths entirely without regressing semantics; all runtime execution must be bytecode + frame slots.
|
||||||
|
|
||||||
|
- [ ] Step A1: Interpreter path audit (map all remaining runtime `Statement.execute` and scope-lookup paths).
|
||||||
|
- [x] Audit results (current hotspots):
|
||||||
|
- `ClassDeclStatement.executeClassDecl`: executes class body/init via `spec.bodyInit?.execute` and `spec.initScope` (Statement list).
|
||||||
|
- `FunctionDeclStatement.executeFunctionDecl`: class-body delegated function init uses `Statement.execute` in initializer thunk.
|
||||||
|
- `EnumDeclStatement.execute`: direct execution path exists (bytecode also emits DECL_ENUM).
|
||||||
|
- `BlockStatement.execute`: creates child scope, applies slot plan/captures, then executes `Script` (which may interpret).
|
||||||
|
- `Script.execute`: interpreter loop when `moduleBytecode` is null or disabled.
|
||||||
|
- `ObjClass.addFn/addProperty` wrappers use `ObjNativeCallable` calling into `Statement.execute` for declared bodies.
|
||||||
|
- Object expressions: class body executed via `executeClassDecl` path (same as above).
|
||||||
|
- Extension wrappers: `ObjExtensionMethodCallable` uses callable that executes `Statement`.
|
||||||
|
|
||||||
|
- [ ] Step A2: Bytecode-backed class + init.
|
||||||
|
- Replace class-body and instance init execution with bytecode functions (per-class + per-instance).
|
||||||
|
- Remove all class init `Statement.execute` calls.
|
||||||
|
- [x] Introduce `ClassStaticFieldInitStatement` + bytecode ops `DECL_CLASS_FIELD`/`DECL_CLASS_DELEGATED` for static class field init.
|
||||||
|
|
||||||
|
- [ ] Step A3: Bytecode-safe delegated properties/functions and object expressions.
|
||||||
|
- Use explicit `object : Statement()` where needed.
|
||||||
|
- No inline suspend lambdas in hot paths.
|
||||||
|
- Remove interpreter fallbacks.
|
||||||
|
|
||||||
|
- [ ] Step A4: Bytecode for all blocks/lambdas (including class bodies).
|
||||||
|
- Compile non-module blocks/lambdas to bytecode; eliminate interpreter gate flags.
|
||||||
|
|
||||||
|
- [ ] Step A5: Delete interpreter execution path and dead code.
|
||||||
|
- Remove interpreter ops/constants and any runtime name-lookup fallbacks.
|
||||||
|
- Full test suite green.
|
||||||
|
|
||||||
## 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.
|
||||||
|
|||||||
@ -217,7 +217,7 @@ private suspend inline fun Scope.processGuard(crossinline block: suspend () -> O
|
|||||||
|
|
||||||
private fun Flow<String>.toLyngFlow(flowScope: Scope): ObjFlow {
|
private fun Flow<String>.toLyngFlow(flowScope: Scope): ObjFlow {
|
||||||
val producer = statement {
|
val producer = statement {
|
||||||
val builder = (this as? net.sergeych.lyng.ClosureScope)?.callScope?.thisObj as? ObjFlowBuilder
|
val builder = (this as? net.sergeych.lyng.BytecodeClosureScope)?.callScope?.thisObj as? ObjFlowBuilder
|
||||||
?: this.thisObj as? ObjFlowBuilder
|
?: this.thisObj as? ObjFlowBuilder
|
||||||
|
|
||||||
this@toLyngFlow.collect {
|
this@toLyngFlow.collect {
|
||||||
|
|||||||
@ -113,7 +113,7 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun missingValue(a: Item, error: String): Obj {
|
suspend fun missingValue(a: Item, error: String): Obj {
|
||||||
return a.defaultValue?.execute(scope)
|
return a.defaultValue?.callOn(scope)
|
||||||
?: if (a.type.isNullable) ObjNull else scope.raiseIllegalArgument(error)
|
?: if (a.type.isNullable) ObjNull else scope.raiseIllegalArgument(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,17 +252,14 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Assign arguments directly into frame slots using [paramSlotPlan] without creating scope locals.
|
* Assign arguments directly into frame slots using [paramSlotPlan] without creating scope locals.
|
||||||
* Still allows default expressions to evaluate by exposing FrameSlotRef facades in [scope].
|
* Default expressions must resolve through frame slots (no scope mirroring).
|
||||||
*/
|
*/
|
||||||
suspend fun assignToFrame(
|
suspend fun assignToFrame(
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
arguments: Arguments = scope.args,
|
arguments: Arguments = scope.args,
|
||||||
paramSlotPlan: Map<String, Int>,
|
paramSlotPlan: Map<String, Int>,
|
||||||
frame: FrameAccess,
|
frame: FrameAccess,
|
||||||
slotOffset: Int = 0,
|
slotOffset: Int = 0
|
||||||
defaultAccessType: AccessType = AccessType.Var,
|
|
||||||
defaultVisibility: Visibility = Visibility.Public,
|
|
||||||
declaringClass: net.sergeych.lyng.obj.ObjClass? = scope.currentClassCtx
|
|
||||||
) {
|
) {
|
||||||
fun slotFor(name: String): Int {
|
fun slotFor(name: String): Int {
|
||||||
val full = paramSlotPlan[name] ?: scope.raiseIllegalState("parameter slot for '$name' is missing")
|
val full = paramSlotPlan[name] ?: scope.raiseIllegalState("parameter slot for '$name' is missing")
|
||||||
@ -271,19 +268,6 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
return slot
|
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) {
|
fun setFrameValue(slot: Int, value: Obj) {
|
||||||
when (value) {
|
when (value) {
|
||||||
is net.sergeych.lyng.obj.ObjInt -> frame.setInt(slot, value.value)
|
is net.sergeych.lyng.obj.ObjInt -> frame.setInt(slot, value.value)
|
||||||
@ -294,18 +278,12 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun assign(a: Item, value: Obj) {
|
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)
|
val slot = slotFor(a.name)
|
||||||
setFrameValue(slot, value.byValueCopy())
|
setFrameValue(slot, value.byValueCopy())
|
||||||
ensureScopeRef(a, slot, recordType)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun missingValue(a: Item, error: String): Obj {
|
suspend fun missingValue(a: Item, error: String): Obj {
|
||||||
return a.defaultValue?.execute(scope)
|
return a.defaultValue?.callOn(scope)
|
||||||
?: if (a.type.isNullable) ObjNull else scope.raiseIllegalArgument(error)
|
?: if (a.type.isNullable) ObjNull else scope.raiseIllegalArgument(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -459,7 +437,7 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
/**
|
/**
|
||||||
* Single argument declaration descriptor.
|
* Single argument declaration descriptor.
|
||||||
*
|
*
|
||||||
* @param defaultValue default value, if set, can't be an [Obj] as it can depend on the call site, call args, etc.
|
* @param defaultValue default value, callable evaluated at call site.
|
||||||
* If not null, could be executed on __caller context__ only.
|
* If not null, could be executed on __caller context__ only.
|
||||||
*/
|
*/
|
||||||
data class Item(
|
data class Item(
|
||||||
@ -472,7 +450,7 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
* Default value, if set, can't be an [Obj] as it can depend on the call site, call args, etc.
|
* Default value, if set, can't be an [Obj] as it can depend on the call site, call args, etc.
|
||||||
* So it is a [Statement] that must be executed on __caller context__.
|
* So it is a [Statement] that must be executed on __caller context__.
|
||||||
*/
|
*/
|
||||||
val defaultValue: Statement? = null,
|
val defaultValue: Obj? = null,
|
||||||
val accessType: AccessType? = null,
|
val accessType: AccessType? = null,
|
||||||
val visibility: Visibility? = null,
|
val visibility: Visibility? = null,
|
||||||
val isTransient: Boolean = false,
|
val isTransient: Boolean = false,
|
||||||
|
|||||||
@ -20,7 +20,7 @@ package net.sergeych.lyng
|
|||||||
import net.sergeych.lyng.obj.*
|
import net.sergeych.lyng.obj.*
|
||||||
|
|
||||||
data class ParsedArgument(
|
data class ParsedArgument(
|
||||||
val value: Statement,
|
val value: Obj,
|
||||||
val pos: Pos,
|
val pos: Pos,
|
||||||
val isSplat: Boolean = false,
|
val isSplat: Boolean = false,
|
||||||
val name: String? = null,
|
val name: String? = null,
|
||||||
@ -40,115 +40,115 @@ data class ParsedArgument(
|
|||||||
if (!hasSplatOrNamed && count == this.size) {
|
if (!hasSplatOrNamed && count == this.size) {
|
||||||
val quick = when (count) {
|
val quick = when (count) {
|
||||||
0 -> Arguments.EMPTY
|
0 -> Arguments.EMPTY
|
||||||
1 -> Arguments(listOf(this.elementAt(0).value.execute(scope)), tailBlockMode)
|
1 -> Arguments(listOf(this.elementAt(0).value.callOn(scope)), tailBlockMode)
|
||||||
2 -> {
|
2 -> {
|
||||||
val a0 = this.elementAt(0).value.execute(scope)
|
val a0 = this.elementAt(0).value.callOn(scope)
|
||||||
val a1 = this.elementAt(1).value.execute(scope)
|
val a1 = this.elementAt(1).value.callOn(scope)
|
||||||
Arguments(listOf(a0, a1), tailBlockMode)
|
Arguments(listOf(a0, a1), tailBlockMode)
|
||||||
}
|
}
|
||||||
3 -> {
|
3 -> {
|
||||||
val a0 = this.elementAt(0).value.execute(scope)
|
val a0 = this.elementAt(0).value.callOn(scope)
|
||||||
val a1 = this.elementAt(1).value.execute(scope)
|
val a1 = this.elementAt(1).value.callOn(scope)
|
||||||
val a2 = this.elementAt(2).value.execute(scope)
|
val a2 = this.elementAt(2).value.callOn(scope)
|
||||||
Arguments(listOf(a0, a1, a2), tailBlockMode)
|
Arguments(listOf(a0, a1, a2), tailBlockMode)
|
||||||
}
|
}
|
||||||
4 -> {
|
4 -> {
|
||||||
val a0 = this.elementAt(0).value.execute(scope)
|
val a0 = this.elementAt(0).value.callOn(scope)
|
||||||
val a1 = this.elementAt(1).value.execute(scope)
|
val a1 = this.elementAt(1).value.callOn(scope)
|
||||||
val a2 = this.elementAt(2).value.execute(scope)
|
val a2 = this.elementAt(2).value.callOn(scope)
|
||||||
val a3 = this.elementAt(3).value.execute(scope)
|
val a3 = this.elementAt(3).value.callOn(scope)
|
||||||
Arguments(listOf(a0, a1, a2, a3), tailBlockMode)
|
Arguments(listOf(a0, a1, a2, a3), tailBlockMode)
|
||||||
}
|
}
|
||||||
5 -> {
|
5 -> {
|
||||||
val a0 = this.elementAt(0).value.execute(scope)
|
val a0 = this.elementAt(0).value.callOn(scope)
|
||||||
val a1 = this.elementAt(1).value.execute(scope)
|
val a1 = this.elementAt(1).value.callOn(scope)
|
||||||
val a2 = this.elementAt(2).value.execute(scope)
|
val a2 = this.elementAt(2).value.callOn(scope)
|
||||||
val a3 = this.elementAt(3).value.execute(scope)
|
val a3 = this.elementAt(3).value.callOn(scope)
|
||||||
val a4 = this.elementAt(4).value.execute(scope)
|
val a4 = this.elementAt(4).value.callOn(scope)
|
||||||
Arguments(listOf(a0, a1, a2, a3, a4), tailBlockMode)
|
Arguments(listOf(a0, a1, a2, a3, a4), tailBlockMode)
|
||||||
}
|
}
|
||||||
6 -> {
|
6 -> {
|
||||||
val a0 = this.elementAt(0).value.execute(scope)
|
val a0 = this.elementAt(0).value.callOn(scope)
|
||||||
val a1 = this.elementAt(1).value.execute(scope)
|
val a1 = this.elementAt(1).value.callOn(scope)
|
||||||
val a2 = this.elementAt(2).value.execute(scope)
|
val a2 = this.elementAt(2).value.callOn(scope)
|
||||||
val a3 = this.elementAt(3).value.execute(scope)
|
val a3 = this.elementAt(3).value.callOn(scope)
|
||||||
val a4 = this.elementAt(4).value.execute(scope)
|
val a4 = this.elementAt(4).value.callOn(scope)
|
||||||
val a5 = this.elementAt(5).value.execute(scope)
|
val a5 = this.elementAt(5).value.callOn(scope)
|
||||||
Arguments(listOf(a0, a1, a2, a3, a4, a5), tailBlockMode)
|
Arguments(listOf(a0, a1, a2, a3, a4, a5), tailBlockMode)
|
||||||
}
|
}
|
||||||
7 -> {
|
7 -> {
|
||||||
val a0 = this.elementAt(0).value.execute(scope)
|
val a0 = this.elementAt(0).value.callOn(scope)
|
||||||
val a1 = this.elementAt(1).value.execute(scope)
|
val a1 = this.elementAt(1).value.callOn(scope)
|
||||||
val a2 = this.elementAt(2).value.execute(scope)
|
val a2 = this.elementAt(2).value.callOn(scope)
|
||||||
val a3 = this.elementAt(3).value.execute(scope)
|
val a3 = this.elementAt(3).value.callOn(scope)
|
||||||
val a4 = this.elementAt(4).value.execute(scope)
|
val a4 = this.elementAt(4).value.callOn(scope)
|
||||||
val a5 = this.elementAt(5).value.execute(scope)
|
val a5 = this.elementAt(5).value.callOn(scope)
|
||||||
val a6 = this.elementAt(6).value.execute(scope)
|
val a6 = this.elementAt(6).value.callOn(scope)
|
||||||
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6), tailBlockMode)
|
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6), tailBlockMode)
|
||||||
}
|
}
|
||||||
8 -> {
|
8 -> {
|
||||||
val a0 = this.elementAt(0).value.execute(scope)
|
val a0 = this.elementAt(0).value.callOn(scope)
|
||||||
val a1 = this.elementAt(1).value.execute(scope)
|
val a1 = this.elementAt(1).value.callOn(scope)
|
||||||
val a2 = this.elementAt(2).value.execute(scope)
|
val a2 = this.elementAt(2).value.callOn(scope)
|
||||||
val a3 = this.elementAt(3).value.execute(scope)
|
val a3 = this.elementAt(3).value.callOn(scope)
|
||||||
val a4 = this.elementAt(4).value.execute(scope)
|
val a4 = this.elementAt(4).value.callOn(scope)
|
||||||
val a5 = this.elementAt(5).value.execute(scope)
|
val a5 = this.elementAt(5).value.callOn(scope)
|
||||||
val a6 = this.elementAt(6).value.execute(scope)
|
val a6 = this.elementAt(6).value.callOn(scope)
|
||||||
val a7 = this.elementAt(7).value.execute(scope)
|
val a7 = this.elementAt(7).value.callOn(scope)
|
||||||
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7), tailBlockMode)
|
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7), tailBlockMode)
|
||||||
}
|
}
|
||||||
9 -> if (PerfFlags.ARG_SMALL_ARITY_12) {
|
9 -> if (PerfFlags.ARG_SMALL_ARITY_12) {
|
||||||
val a0 = this.elementAt(0).value.execute(scope)
|
val a0 = this.elementAt(0).value.callOn(scope)
|
||||||
val a1 = this.elementAt(1).value.execute(scope)
|
val a1 = this.elementAt(1).value.callOn(scope)
|
||||||
val a2 = this.elementAt(2).value.execute(scope)
|
val a2 = this.elementAt(2).value.callOn(scope)
|
||||||
val a3 = this.elementAt(3).value.execute(scope)
|
val a3 = this.elementAt(3).value.callOn(scope)
|
||||||
val a4 = this.elementAt(4).value.execute(scope)
|
val a4 = this.elementAt(4).value.callOn(scope)
|
||||||
val a5 = this.elementAt(5).value.execute(scope)
|
val a5 = this.elementAt(5).value.callOn(scope)
|
||||||
val a6 = this.elementAt(6).value.execute(scope)
|
val a6 = this.elementAt(6).value.callOn(scope)
|
||||||
val a7 = this.elementAt(7).value.execute(scope)
|
val a7 = this.elementAt(7).value.callOn(scope)
|
||||||
val a8 = this.elementAt(8).value.execute(scope)
|
val a8 = this.elementAt(8).value.callOn(scope)
|
||||||
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8), tailBlockMode)
|
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8), tailBlockMode)
|
||||||
} else null
|
} else null
|
||||||
10 -> if (PerfFlags.ARG_SMALL_ARITY_12) {
|
10 -> if (PerfFlags.ARG_SMALL_ARITY_12) {
|
||||||
val a0 = this.elementAt(0).value.execute(scope)
|
val a0 = this.elementAt(0).value.callOn(scope)
|
||||||
val a1 = this.elementAt(1).value.execute(scope)
|
val a1 = this.elementAt(1).value.callOn(scope)
|
||||||
val a2 = this.elementAt(2).value.execute(scope)
|
val a2 = this.elementAt(2).value.callOn(scope)
|
||||||
val a3 = this.elementAt(3).value.execute(scope)
|
val a3 = this.elementAt(3).value.callOn(scope)
|
||||||
val a4 = this.elementAt(4).value.execute(scope)
|
val a4 = this.elementAt(4).value.callOn(scope)
|
||||||
val a5 = this.elementAt(5).value.execute(scope)
|
val a5 = this.elementAt(5).value.callOn(scope)
|
||||||
val a6 = this.elementAt(6).value.execute(scope)
|
val a6 = this.elementAt(6).value.callOn(scope)
|
||||||
val a7 = this.elementAt(7).value.execute(scope)
|
val a7 = this.elementAt(7).value.callOn(scope)
|
||||||
val a8 = this.elementAt(8).value.execute(scope)
|
val a8 = this.elementAt(8).value.callOn(scope)
|
||||||
val a9 = this.elementAt(9).value.execute(scope)
|
val a9 = this.elementAt(9).value.callOn(scope)
|
||||||
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9), tailBlockMode)
|
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9), tailBlockMode)
|
||||||
} else null
|
} else null
|
||||||
11 -> if (PerfFlags.ARG_SMALL_ARITY_12) {
|
11 -> if (PerfFlags.ARG_SMALL_ARITY_12) {
|
||||||
val a0 = this.elementAt(0).value.execute(scope)
|
val a0 = this.elementAt(0).value.callOn(scope)
|
||||||
val a1 = this.elementAt(1).value.execute(scope)
|
val a1 = this.elementAt(1).value.callOn(scope)
|
||||||
val a2 = this.elementAt(2).value.execute(scope)
|
val a2 = this.elementAt(2).value.callOn(scope)
|
||||||
val a3 = this.elementAt(3).value.execute(scope)
|
val a3 = this.elementAt(3).value.callOn(scope)
|
||||||
val a4 = this.elementAt(4).value.execute(scope)
|
val a4 = this.elementAt(4).value.callOn(scope)
|
||||||
val a5 = this.elementAt(5).value.execute(scope)
|
val a5 = this.elementAt(5).value.callOn(scope)
|
||||||
val a6 = this.elementAt(6).value.execute(scope)
|
val a6 = this.elementAt(6).value.callOn(scope)
|
||||||
val a7 = this.elementAt(7).value.execute(scope)
|
val a7 = this.elementAt(7).value.callOn(scope)
|
||||||
val a8 = this.elementAt(8).value.execute(scope)
|
val a8 = this.elementAt(8).value.callOn(scope)
|
||||||
val a9 = this.elementAt(9).value.execute(scope)
|
val a9 = this.elementAt(9).value.callOn(scope)
|
||||||
val a10 = this.elementAt(10).value.execute(scope)
|
val a10 = this.elementAt(10).value.callOn(scope)
|
||||||
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10), tailBlockMode)
|
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10), tailBlockMode)
|
||||||
} else null
|
} else null
|
||||||
12 -> if (PerfFlags.ARG_SMALL_ARITY_12) {
|
12 -> if (PerfFlags.ARG_SMALL_ARITY_12) {
|
||||||
val a0 = this.elementAt(0).value.execute(scope)
|
val a0 = this.elementAt(0).value.callOn(scope)
|
||||||
val a1 = this.elementAt(1).value.execute(scope)
|
val a1 = this.elementAt(1).value.callOn(scope)
|
||||||
val a2 = this.elementAt(2).value.execute(scope)
|
val a2 = this.elementAt(2).value.callOn(scope)
|
||||||
val a3 = this.elementAt(3).value.execute(scope)
|
val a3 = this.elementAt(3).value.callOn(scope)
|
||||||
val a4 = this.elementAt(4).value.execute(scope)
|
val a4 = this.elementAt(4).value.callOn(scope)
|
||||||
val a5 = this.elementAt(5).value.execute(scope)
|
val a5 = this.elementAt(5).value.callOn(scope)
|
||||||
val a6 = this.elementAt(6).value.execute(scope)
|
val a6 = this.elementAt(6).value.callOn(scope)
|
||||||
val a7 = this.elementAt(7).value.execute(scope)
|
val a7 = this.elementAt(7).value.callOn(scope)
|
||||||
val a8 = this.elementAt(8).value.execute(scope)
|
val a8 = this.elementAt(8).value.callOn(scope)
|
||||||
val a9 = this.elementAt(9).value.execute(scope)
|
val a9 = this.elementAt(9).value.callOn(scope)
|
||||||
val a10 = this.elementAt(10).value.execute(scope)
|
val a10 = this.elementAt(10).value.callOn(scope)
|
||||||
val a11 = this.elementAt(11).value.execute(scope)
|
val a11 = this.elementAt(11).value.callOn(scope)
|
||||||
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11), tailBlockMode)
|
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11), tailBlockMode)
|
||||||
} else null
|
} else null
|
||||||
else -> null
|
else -> null
|
||||||
@ -166,12 +166,12 @@ data class ParsedArgument(
|
|||||||
// Named argument
|
// Named argument
|
||||||
if (named == null) named = linkedMapOf()
|
if (named == null) named = linkedMapOf()
|
||||||
if (named.containsKey(x.name)) scope.raiseIllegalArgument("argument '${x.name}' is already set")
|
if (named.containsKey(x.name)) scope.raiseIllegalArgument("argument '${x.name}' is already set")
|
||||||
val v = x.value.execute(scope)
|
val v = x.value.callOn(scope)
|
||||||
named[x.name] = v
|
named[x.name] = v
|
||||||
namedSeen = true
|
namedSeen = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val value = x.value.execute(scope)
|
val value = x.value.callOn(scope)
|
||||||
if (x.isSplat) {
|
if (x.isSplat) {
|
||||||
when {
|
when {
|
||||||
// IMPORTANT: handle ObjMap BEFORE generic Iterable to ensure map splats
|
// IMPORTANT: handle ObjMap BEFORE generic Iterable to ensure map splats
|
||||||
|
|||||||
@ -32,32 +32,14 @@ class BlockStatement(
|
|||||||
if (slotPlan.isNotEmpty()) target.applySlotPlan(slotPlan)
|
if (slotPlan.isNotEmpty()) target.applySlotPlan(slotPlan)
|
||||||
if (captureSlots.isNotEmpty()) {
|
if (captureSlots.isNotEmpty()) {
|
||||||
val captureRecords = scope.captureRecords
|
val captureRecords = scope.captureRecords
|
||||||
if (captureRecords != null) {
|
if (captureRecords == null) {
|
||||||
for (i in captureSlots.indices) {
|
scope.raiseIllegalState("missing bytecode capture records")
|
||||||
val capture = captureSlots[i]
|
}
|
||||||
val rec = captureRecords.getOrNull(i)
|
for (i in captureSlots.indices) {
|
||||||
?: scope.raiseSymbolNotFound("capture ${capture.name} not found")
|
val capture = captureSlots[i]
|
||||||
target.updateSlotFor(capture.name, rec)
|
val rec = captureRecords.getOrNull(i)
|
||||||
}
|
?: scope.raiseSymbolNotFound("capture ${capture.name} not found")
|
||||||
} else {
|
target.updateSlotFor(capture.name, rec)
|
||||||
val applyScope = scope as? ApplyScope
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
if (rec == null) {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return block.execute(target)
|
return block.execute(target)
|
||||||
|
|||||||
@ -19,4 +19,6 @@ package net.sergeych.lyng
|
|||||||
|
|
||||||
data class CaptureSlot(
|
data class CaptureSlot(
|
||||||
val name: String,
|
val name: String,
|
||||||
|
val ownerScopeId: Int? = null,
|
||||||
|
val ownerSlot: Int? = null,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -45,7 +45,12 @@ data class ClassDeclSpec(
|
|||||||
val initScope: List<Statement>,
|
val initScope: List<Statement>,
|
||||||
)
|
)
|
||||||
|
|
||||||
internal suspend fun executeClassDecl(scope: Scope, spec: ClassDeclSpec): Obj {
|
internal suspend fun executeClassDecl(
|
||||||
|
scope: Scope,
|
||||||
|
spec: ClassDeclSpec,
|
||||||
|
bodyCaptureRecords: List<ObjRecord>? = null,
|
||||||
|
bodyCaptureNames: List<String>? = null
|
||||||
|
): Obj {
|
||||||
if (spec.isObject) {
|
if (spec.isObject) {
|
||||||
val parentClasses = spec.baseSpecs.map { baseSpec ->
|
val parentClasses = spec.baseSpecs.map { baseSpec ->
|
||||||
val rec = scope[baseSpec.name] ?: throw ScriptError(spec.startPos, "unknown base class: ${baseSpec.name}")
|
val rec = scope[baseSpec.name] ?: throw ScriptError(spec.startPos, "unknown base class: ${baseSpec.name}")
|
||||||
@ -61,6 +66,10 @@ internal suspend fun executeClassDecl(scope: Scope, spec: ClassDeclSpec): Obj {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val classScope = scope.createChildScope(newThisObj = newClass)
|
val classScope = scope.createChildScope(newThisObj = newClass)
|
||||||
|
if (!bodyCaptureRecords.isNullOrEmpty() && !bodyCaptureNames.isNullOrEmpty()) {
|
||||||
|
classScope.captureRecords = bodyCaptureRecords
|
||||||
|
classScope.captureNames = bodyCaptureNames
|
||||||
|
}
|
||||||
classScope.currentClassCtx = newClass
|
classScope.currentClassCtx = newClass
|
||||||
newClass.classScope = classScope
|
newClass.classScope = classScope
|
||||||
classScope.addConst("object", newClass)
|
classScope.addConst("object", newClass)
|
||||||
@ -133,6 +142,10 @@ internal suspend fun executeClassDecl(scope: Scope, spec: ClassDeclSpec): Obj {
|
|||||||
|
|
||||||
spec.declaredName?.let { scope.addItem(it, false, newClass) }
|
spec.declaredName?.let { scope.addItem(it, false, newClass) }
|
||||||
val classScope = scope.createChildScope(newThisObj = newClass)
|
val classScope = scope.createChildScope(newThisObj = newClass)
|
||||||
|
if (!bodyCaptureRecords.isNullOrEmpty() && !bodyCaptureNames.isNullOrEmpty()) {
|
||||||
|
classScope.captureRecords = bodyCaptureRecords
|
||||||
|
classScope.captureNames = bodyCaptureNames
|
||||||
|
}
|
||||||
classScope.currentClassCtx = newClass
|
classScope.currentClassCtx = newClass
|
||||||
newClass.classScope = classScope
|
newClass.classScope = classScope
|
||||||
spec.bodyInit?.execute(classScope)
|
spec.bodyInit?.execute(classScope)
|
||||||
|
|||||||
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Sergey S. Chernov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sergeych.lyng
|
||||||
|
|
||||||
|
import net.sergeych.lyng.obj.Obj
|
||||||
|
import net.sergeych.lyng.obj.ObjClass
|
||||||
|
import net.sergeych.lyng.obj.ObjProperty
|
||||||
|
import net.sergeych.lyng.obj.ObjRecord
|
||||||
|
import net.sergeych.lyng.obj.ObjVoid
|
||||||
|
|
||||||
|
class ClassInstanceInitDeclStatement(
|
||||||
|
val initStatement: Statement,
|
||||||
|
override val pos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val cls = scope.thisObj as? ObjClass
|
||||||
|
?: scope.raiseIllegalState("instance init declaration requires class scope")
|
||||||
|
cls.instanceInitializers += initStatement
|
||||||
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClassInstanceFieldDeclStatement(
|
||||||
|
val name: String,
|
||||||
|
val isMutable: Boolean,
|
||||||
|
val visibility: Visibility,
|
||||||
|
val writeVisibility: Visibility?,
|
||||||
|
val isAbstract: Boolean,
|
||||||
|
val isClosed: Boolean,
|
||||||
|
val isOverride: Boolean,
|
||||||
|
val isTransient: Boolean,
|
||||||
|
val fieldId: Int?,
|
||||||
|
val initStatement: Statement?,
|
||||||
|
override val pos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val cls = scope.thisObj as? ObjClass
|
||||||
|
?: scope.raiseIllegalState("instance field declaration requires class scope")
|
||||||
|
cls.createField(
|
||||||
|
name,
|
||||||
|
net.sergeych.lyng.obj.ObjNull,
|
||||||
|
isMutable = isMutable,
|
||||||
|
visibility = visibility,
|
||||||
|
writeVisibility = writeVisibility,
|
||||||
|
isAbstract = isAbstract,
|
||||||
|
isClosed = isClosed,
|
||||||
|
isOverride = isOverride,
|
||||||
|
isTransient = isTransient,
|
||||||
|
declaringClass = cls,
|
||||||
|
type = ObjRecord.Type.Field,
|
||||||
|
fieldId = fieldId
|
||||||
|
)
|
||||||
|
if (!isAbstract) initStatement?.let { cls.instanceInitializers += it }
|
||||||
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClassInstancePropertyDeclStatement(
|
||||||
|
val name: String,
|
||||||
|
val isMutable: Boolean,
|
||||||
|
val visibility: Visibility,
|
||||||
|
val writeVisibility: Visibility?,
|
||||||
|
val isAbstract: Boolean,
|
||||||
|
val isClosed: Boolean,
|
||||||
|
val isOverride: Boolean,
|
||||||
|
val isTransient: Boolean,
|
||||||
|
val prop: ObjProperty,
|
||||||
|
val methodId: Int?,
|
||||||
|
val initStatement: Statement?,
|
||||||
|
override val pos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val cls = scope.thisObj as? ObjClass
|
||||||
|
?: scope.raiseIllegalState("instance property declaration requires class scope")
|
||||||
|
cls.addProperty(
|
||||||
|
name = name,
|
||||||
|
visibility = visibility,
|
||||||
|
writeVisibility = writeVisibility,
|
||||||
|
declaringClass = cls,
|
||||||
|
isAbstract = isAbstract,
|
||||||
|
isClosed = isClosed,
|
||||||
|
isOverride = isOverride,
|
||||||
|
pos = pos,
|
||||||
|
prop = prop,
|
||||||
|
methodId = methodId
|
||||||
|
)
|
||||||
|
if (!isAbstract) initStatement?.let { cls.instanceInitializers += it }
|
||||||
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClassInstanceDelegatedDeclStatement(
|
||||||
|
val name: String,
|
||||||
|
val isMutable: Boolean,
|
||||||
|
val visibility: Visibility,
|
||||||
|
val writeVisibility: Visibility?,
|
||||||
|
val isAbstract: Boolean,
|
||||||
|
val isClosed: Boolean,
|
||||||
|
val isOverride: Boolean,
|
||||||
|
val isTransient: Boolean,
|
||||||
|
val methodId: Int?,
|
||||||
|
val initStatement: Statement?,
|
||||||
|
override val pos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val cls = scope.thisObj as? ObjClass
|
||||||
|
?: scope.raiseIllegalState("instance delegated declaration requires class scope")
|
||||||
|
cls.createField(
|
||||||
|
name,
|
||||||
|
net.sergeych.lyng.obj.ObjUnset,
|
||||||
|
isMutable = isMutable,
|
||||||
|
visibility = visibility,
|
||||||
|
writeVisibility = writeVisibility,
|
||||||
|
isAbstract = isAbstract,
|
||||||
|
isClosed = isClosed,
|
||||||
|
isOverride = isOverride,
|
||||||
|
isTransient = isTransient,
|
||||||
|
declaringClass = cls,
|
||||||
|
type = ObjRecord.Type.Delegated,
|
||||||
|
methodId = methodId
|
||||||
|
)
|
||||||
|
if (!isAbstract) initStatement?.let { cls.instanceInitializers += it }
|
||||||
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Sergey S. Chernov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sergeych.lyng
|
||||||
|
|
||||||
|
import net.sergeych.lyng.obj.Obj
|
||||||
|
import net.sergeych.lyng.obj.ObjClass
|
||||||
|
import net.sergeych.lyng.obj.ObjNull
|
||||||
|
import net.sergeych.lyng.obj.ObjRecord
|
||||||
|
import net.sergeych.lyng.obj.ObjString
|
||||||
|
import net.sergeych.lyng.obj.ObjUnset
|
||||||
|
import net.sergeych.lyng.obj.ObjVoid
|
||||||
|
|
||||||
|
class ClassStaticFieldInitStatement(
|
||||||
|
val name: String,
|
||||||
|
val isMutable: Boolean,
|
||||||
|
val visibility: Visibility,
|
||||||
|
val writeVisibility: Visibility?,
|
||||||
|
val initializer: Statement?,
|
||||||
|
val isDelegated: Boolean,
|
||||||
|
val isTransient: Boolean,
|
||||||
|
private val startPos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override val pos: Pos = startPos
|
||||||
|
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val initValue = initializer?.execute(scope)?.byValueCopy() ?: ObjNull
|
||||||
|
val cls = scope.thisObj as? ObjClass
|
||||||
|
?: scope.raiseIllegalState("static field init requires class scope")
|
||||||
|
return if (isDelegated) {
|
||||||
|
val accessTypeStr = if (isMutable) "Var" else "Val"
|
||||||
|
val accessType = ObjString(accessTypeStr)
|
||||||
|
val finalDelegate = try {
|
||||||
|
initValue.invokeInstanceMethod(
|
||||||
|
scope,
|
||||||
|
"bind",
|
||||||
|
Arguments(ObjString(name), accessType, scope.thisObj)
|
||||||
|
)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
initValue
|
||||||
|
}
|
||||||
|
cls.createClassField(
|
||||||
|
name,
|
||||||
|
ObjUnset,
|
||||||
|
isMutable,
|
||||||
|
visibility,
|
||||||
|
writeVisibility,
|
||||||
|
startPos,
|
||||||
|
isTransient = isTransient,
|
||||||
|
type = ObjRecord.Type.Delegated
|
||||||
|
).apply {
|
||||||
|
delegate = finalDelegate
|
||||||
|
}
|
||||||
|
scope.addItem(
|
||||||
|
name,
|
||||||
|
isMutable,
|
||||||
|
ObjUnset,
|
||||||
|
visibility,
|
||||||
|
writeVisibility,
|
||||||
|
recordType = ObjRecord.Type.Delegated,
|
||||||
|
isTransient = isTransient
|
||||||
|
).apply {
|
||||||
|
delegate = finalDelegate
|
||||||
|
}
|
||||||
|
finalDelegate
|
||||||
|
} else {
|
||||||
|
cls.createClassField(
|
||||||
|
name,
|
||||||
|
initValue,
|
||||||
|
isMutable,
|
||||||
|
visibility,
|
||||||
|
writeVisibility,
|
||||||
|
startPos,
|
||||||
|
isTransient = isTransient
|
||||||
|
)
|
||||||
|
scope.addItem(
|
||||||
|
name,
|
||||||
|
isMutable,
|
||||||
|
initValue,
|
||||||
|
visibility,
|
||||||
|
writeVisibility,
|
||||||
|
recordType = ObjRecord.Type.Field,
|
||||||
|
isTransient = isTransient
|
||||||
|
)
|
||||||
|
initValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,76 +18,12 @@
|
|||||||
package net.sergeych.lyng
|
package net.sergeych.lyng
|
||||||
|
|
||||||
import net.sergeych.lyng.obj.Obj
|
import net.sergeych.lyng.obj.Obj
|
||||||
import net.sergeych.lyng.obj.ObjClass
|
|
||||||
import net.sergeych.lyng.obj.ObjRecord
|
import net.sergeych.lyng.obj.ObjRecord
|
||||||
|
|
||||||
/**
|
|
||||||
* Scope that adds a "closure" to caller; most often it is used to apply class instance to caller scope.
|
|
||||||
* Inherits [Scope.args] and [Scope.thisObj] from [callScope] and adds lookup for symbols
|
|
||||||
* from [closureScope] with proper precedence
|
|
||||||
*/
|
|
||||||
class ClosureScope(
|
|
||||||
val callScope: Scope,
|
|
||||||
val closureScope: Scope,
|
|
||||||
private val preferredThisType: String? = null
|
|
||||||
) :
|
|
||||||
// Important: use closureScope.thisObj so unqualified members (e.g., fields) resolve to the instance
|
|
||||||
// we captured, not to the caller's `this` (e.g., FlowBuilder).
|
|
||||||
Scope(callScope, callScope.args, thisObj = closureScope.thisObj) {
|
|
||||||
|
|
||||||
init {
|
|
||||||
val desired = preferredThisType?.let { typeName ->
|
|
||||||
callScope.thisVariants.firstOrNull { it.objClass.className == typeName }
|
|
||||||
}
|
|
||||||
val primaryThis = closureScope.thisObj
|
|
||||||
val merged = ArrayList<Obj>(callScope.thisVariants.size + closureScope.thisVariants.size + 1)
|
|
||||||
desired?.let { merged.add(it) }
|
|
||||||
merged.addAll(callScope.thisVariants)
|
|
||||||
merged.addAll(closureScope.thisVariants)
|
|
||||||
setThisVariants(primaryThis, merged)
|
|
||||||
// Preserve the lexical class context of the closure by default. This ensures that lambdas
|
|
||||||
// created inside a class method keep access to that class's private/protected members even
|
|
||||||
// when executed from within another object's method (e.g., Mutex.withLock), which may set
|
|
||||||
// its own currentClassCtx temporarily. If the closure has no class context, inherit caller's.
|
|
||||||
this.currentClassCtx = closureScope.currentClassCtx ?: callScope.currentClassCtx
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(name: String): ObjRecord? {
|
|
||||||
if (name == "this") return thisObj.asReadonly
|
|
||||||
|
|
||||||
// 1. Current frame locals (parameters, local variables)
|
|
||||||
tryGetLocalRecord(this, name, currentClassCtx)?.let { return it }
|
|
||||||
|
|
||||||
// 2. Lexical environment (captured locals from entire ancestry)
|
|
||||||
closureScope.chainLookupIgnoreClosure(name, followClosure = true, caller = currentClassCtx)?.let { return it }
|
|
||||||
|
|
||||||
// 3. Lexical this members (captured receiver)
|
|
||||||
val receiver = thisObj
|
|
||||||
val effectiveClass = receiver as? ObjClass ?: receiver.objClass
|
|
||||||
for (cls in effectiveClass.mro) {
|
|
||||||
val rec = cls.members[name] ?: cls.classScope?.objects?.get(name)
|
|
||||||
if (rec != null && !rec.isAbstract) {
|
|
||||||
if (canAccessMember(rec.visibility, rec.declaringClass ?: cls, currentClassCtx, name)) {
|
|
||||||
return rec.copy(receiver = receiver)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Finally, root object fallback
|
|
||||||
Obj.rootObjectType.members[name]?.let { rec ->
|
|
||||||
if (canAccessMember(rec.visibility, rec.declaringClass, currentClassCtx, name)) {
|
|
||||||
return rec.copy(receiver = receiver)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Call environment (caller locals, caller this, and global fallback)
|
|
||||||
return callScope.get(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bytecode-oriented closure scope that keeps the call scope parent chain for stack traces
|
* Bytecode-oriented closure scope that keeps the call scope parent chain for stack traces
|
||||||
* while carrying the lexical closure for `this` variants and module resolution.
|
* while carrying the lexical closure for `this` variants and module resolution.
|
||||||
* Unlike [ClosureScope], it does not override name lookup.
|
* Unlike interpreter closure scopes, it does not override name lookup.
|
||||||
*/
|
*/
|
||||||
class BytecodeClosureScope(
|
class BytecodeClosureScope(
|
||||||
val callScope: Scope,
|
val callScope: Scope,
|
||||||
@ -118,7 +54,7 @@ class ApplyScope(val callScope: Scope, val applied: Scope) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun applyClosure(closure: Scope, preferredThisType: String?): Scope {
|
override fun applyClosure(closure: Scope, preferredThisType: String?): Scope {
|
||||||
return ClosureScope(this, closure, preferredThisType)
|
return BytecodeClosureScope(this, closure, preferredThisType)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -28,6 +28,7 @@ import net.sergeych.lyng.obj.ObjVoid
|
|||||||
class FunctionClosureBox(
|
class FunctionClosureBox(
|
||||||
var closure: Scope? = null,
|
var closure: Scope? = null,
|
||||||
var captureContext: Scope? = null,
|
var captureContext: Scope? = null,
|
||||||
|
var captureRecords: List<ObjRecord>? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class FunctionDeclSpec(
|
data class FunctionDeclSpec(
|
||||||
@ -40,6 +41,7 @@ data class FunctionDeclSpec(
|
|||||||
val isTransient: Boolean,
|
val isTransient: Boolean,
|
||||||
val isDelegated: Boolean,
|
val isDelegated: Boolean,
|
||||||
val delegateExpression: Statement?,
|
val delegateExpression: Statement?,
|
||||||
|
val delegateInitStatement: Statement?,
|
||||||
val extTypeName: String?,
|
val extTypeName: String?,
|
||||||
val extensionWrapperName: String?,
|
val extensionWrapperName: String?,
|
||||||
val memberMethodId: Int?,
|
val memberMethodId: Int?,
|
||||||
@ -50,10 +52,17 @@ data class FunctionDeclSpec(
|
|||||||
val fnBody: Statement,
|
val fnBody: Statement,
|
||||||
val closureBox: FunctionClosureBox,
|
val closureBox: FunctionClosureBox,
|
||||||
val captureSlots: List<CaptureSlot>,
|
val captureSlots: List<CaptureSlot>,
|
||||||
|
val slotIndex: Int?,
|
||||||
|
val scopeId: Int?,
|
||||||
val startPos: Pos,
|
val startPos: Pos,
|
||||||
)
|
)
|
||||||
|
|
||||||
internal suspend fun executeFunctionDecl(scope: Scope, spec: FunctionDeclSpec): Obj {
|
internal suspend fun executeFunctionDecl(
|
||||||
|
scope: Scope,
|
||||||
|
spec: FunctionDeclSpec,
|
||||||
|
captureRecords: List<ObjRecord>? = null
|
||||||
|
): Obj {
|
||||||
|
spec.closureBox.captureRecords = captureRecords
|
||||||
if (spec.actualExtern && spec.extTypeName == null && !spec.parentIsClassBody) {
|
if (spec.actualExtern && spec.extTypeName == null && !spec.parentIsClassBody) {
|
||||||
val existing = scope.get(spec.name)
|
val existing = scope.get(spec.name)
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
@ -88,8 +97,8 @@ internal suspend fun executeFunctionDecl(scope: Scope, spec: FunctionDeclSpec):
|
|||||||
delegate = finalDelegate
|
delegate = finalDelegate
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return ObjVoid
|
return ObjVoid
|
||||||
}
|
}
|
||||||
|
|
||||||
val th = scope.thisObj
|
val th = scope.thisObj
|
||||||
if (spec.isStatic) {
|
if (spec.isStatic) {
|
||||||
@ -117,7 +126,6 @@ internal suspend fun executeFunctionDecl(scope: Scope, spec: FunctionDeclSpec):
|
|||||||
}
|
}
|
||||||
} else if (th is ObjClass) {
|
} else if (th is ObjClass) {
|
||||||
val cls: ObjClass = th
|
val cls: ObjClass = th
|
||||||
val storageName = "${cls.className}::${spec.name}"
|
|
||||||
cls.createField(
|
cls.createField(
|
||||||
spec.name,
|
spec.name,
|
||||||
ObjUnset,
|
ObjUnset,
|
||||||
@ -133,33 +141,9 @@ internal suspend fun executeFunctionDecl(scope: Scope, spec: FunctionDeclSpec):
|
|||||||
type = ObjRecord.Type.Delegated,
|
type = ObjRecord.Type.Delegated,
|
||||||
methodId = spec.memberMethodId
|
methodId = spec.memberMethodId
|
||||||
)
|
)
|
||||||
cls.instanceInitializers += object : Statement() {
|
val initStmt = spec.delegateInitStatement
|
||||||
override val pos: Pos = spec.startPos
|
?: scope.raiseIllegalState("missing delegated init statement for ${spec.name}")
|
||||||
override suspend fun execute(scp: Scope): Obj {
|
cls.instanceInitializers += initStmt
|
||||||
val accessType2 = ObjString("Callable")
|
|
||||||
val initValue2 = delegateExpr.execute(scp)
|
|
||||||
val finalDelegate2 = try {
|
|
||||||
initValue2.invokeInstanceMethod(scp, "bind", Arguments(ObjString(spec.name), accessType2, scp.thisObj))
|
|
||||||
} catch (e: Exception) {
|
|
||||||
initValue2
|
|
||||||
}
|
|
||||||
scp.addItem(
|
|
||||||
storageName,
|
|
||||||
false,
|
|
||||||
ObjUnset,
|
|
||||||
spec.visibility,
|
|
||||||
null,
|
|
||||||
recordType = ObjRecord.Type.Delegated,
|
|
||||||
isAbstract = spec.isAbstract,
|
|
||||||
isClosed = spec.isClosed,
|
|
||||||
isOverride = spec.isOverride,
|
|
||||||
isTransient = spec.isTransient
|
|
||||||
).apply {
|
|
||||||
delegate = finalDelegate2
|
|
||||||
}
|
|
||||||
return ObjVoid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
scope.addItem(
|
scope.addItem(
|
||||||
spec.name,
|
spec.name,
|
||||||
@ -178,7 +162,7 @@ internal suspend fun executeFunctionDecl(scope: Scope, spec: FunctionDeclSpec):
|
|||||||
if (spec.isStatic || !spec.parentIsClassBody) {
|
if (spec.isStatic || !spec.parentIsClassBody) {
|
||||||
spec.closureBox.closure = scope
|
spec.closureBox.closure = scope
|
||||||
}
|
}
|
||||||
if (spec.parentIsClassBody && spec.captureSlots.isNotEmpty()) {
|
if (spec.parentIsClassBody) {
|
||||||
spec.closureBox.captureContext = scope
|
spec.closureBox.captureContext = scope
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,25 +172,18 @@ internal suspend fun executeFunctionDecl(scope: Scope, spec: FunctionDeclSpec):
|
|||||||
spec.extTypeName?.let { typeName ->
|
spec.extTypeName?.let { typeName ->
|
||||||
val type = scope[typeName]?.value ?: scope.raiseSymbolNotFound("class $typeName not found")
|
val type = scope[typeName]?.value ?: scope.raiseSymbolNotFound("class $typeName not found")
|
||||||
if (type !is ObjClass) scope.raiseClassCastError("$typeName is not the class instance")
|
if (type !is ObjClass) scope.raiseClassCastError("$typeName is not the class instance")
|
||||||
val stmt = object : Statement() {
|
val callable = net.sergeych.lyng.obj.ObjNativeCallable {
|
||||||
override val pos: Pos = spec.startPos
|
val result = (thisObj as? ObjInstance)?.let { i ->
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
val execScope = applyClosureForBytecode(i.instanceScope).also {
|
||||||
val result = (scope.thisObj as? ObjInstance)?.let { i ->
|
it.args = args
|
||||||
val execScope = if (compiledFnBody is net.sergeych.lyng.bytecode.BytecodeStatement) {
|
}
|
||||||
scope.applyClosureForBytecode(i.instanceScope).also {
|
compiledFnBody.execute(execScope)
|
||||||
it.args = scope.args
|
} ?: compiledFnBody.execute(thisObj.autoInstanceScope(this))
|
||||||
}
|
result
|
||||||
} else {
|
|
||||||
ClosureScope(scope, i.instanceScope)
|
|
||||||
}
|
|
||||||
compiledFnBody.execute(execScope)
|
|
||||||
} ?: compiledFnBody.execute(scope.thisObj.autoInstanceScope(scope))
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
scope.addExtension(type, spec.name, ObjRecord(stmt, isMutable = false, visibility = spec.visibility, declaringClass = null))
|
scope.addExtension(type, spec.name, ObjRecord(callable, isMutable = false, visibility = spec.visibility, declaringClass = null))
|
||||||
val wrapperName = spec.extensionWrapperName ?: extensionCallableName(typeName, spec.name)
|
val wrapperName = spec.extensionWrapperName ?: extensionCallableName(typeName, spec.name)
|
||||||
val wrapper = ObjExtensionMethodCallable(spec.name, stmt)
|
val wrapper = ObjExtensionMethodCallable(spec.name, callable)
|
||||||
scope.addItem(wrapperName, false, wrapper, spec.visibility, recordType = ObjRecord.Type.Fun)
|
scope.addItem(wrapperName, false, wrapper, spec.visibility, recordType = ObjRecord.Type.Fun)
|
||||||
} ?: run {
|
} ?: run {
|
||||||
val th = scope.thisObj
|
val th = scope.thisObj
|
||||||
@ -238,7 +215,8 @@ internal suspend fun executeFunctionDecl(scope: Scope, spec: FunctionDeclSpec):
|
|||||||
this.currentClassCtx = savedCtx
|
this.currentClassCtx = savedCtx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scope.addItem(spec.name, false, compiledFnBody, spec.visibility, callSignature = spec.externCallSignature)
|
val memberValue = cls.members[spec.name]?.value ?: compiledFnBody
|
||||||
|
scope.addItem(spec.name, false, memberValue, spec.visibility, callSignature = spec.externCallSignature)
|
||||||
compiledFnBody
|
compiledFnBody
|
||||||
} else {
|
} else {
|
||||||
scope.addItem(spec.name, false, compiledFnBody, spec.visibility, callSignature = spec.externCallSignature)
|
scope.addItem(spec.name, false, compiledFnBody, spec.visibility, callSignature = spec.externCallSignature)
|
||||||
|
|||||||
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Sergey S. Chernov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sergeych.lyng
|
||||||
|
|
||||||
|
import net.sergeych.lyng.obj.Obj
|
||||||
|
import net.sergeych.lyng.obj.ObjNull
|
||||||
|
import net.sergeych.lyng.obj.ObjProperty
|
||||||
|
import net.sergeych.lyng.obj.ObjRecord
|
||||||
|
import net.sergeych.lyng.obj.ObjString
|
||||||
|
import net.sergeych.lyng.obj.ObjUnset
|
||||||
|
import net.sergeych.lyng.obj.ObjVoid
|
||||||
|
|
||||||
|
class InstanceFieldInitStatement(
|
||||||
|
val storageName: String,
|
||||||
|
val isMutable: Boolean,
|
||||||
|
val visibility: Visibility,
|
||||||
|
val writeVisibility: Visibility?,
|
||||||
|
val isAbstract: Boolean,
|
||||||
|
val isClosed: Boolean,
|
||||||
|
val isOverride: Boolean,
|
||||||
|
val isTransient: Boolean,
|
||||||
|
val isLateInitVal: Boolean,
|
||||||
|
val initializer: Statement?,
|
||||||
|
override val pos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val initValue = initializer?.execute(scope)?.byValueCopy()
|
||||||
|
?: if (isLateInitVal) ObjUnset else ObjNull
|
||||||
|
scope.addItem(
|
||||||
|
storageName,
|
||||||
|
isMutable,
|
||||||
|
initValue,
|
||||||
|
visibility,
|
||||||
|
writeVisibility,
|
||||||
|
recordType = ObjRecord.Type.Field,
|
||||||
|
isAbstract = isAbstract,
|
||||||
|
isClosed = isClosed,
|
||||||
|
isOverride = isOverride,
|
||||||
|
isTransient = isTransient
|
||||||
|
)
|
||||||
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InstancePropertyInitStatement(
|
||||||
|
val storageName: String,
|
||||||
|
val isMutable: Boolean,
|
||||||
|
val visibility: Visibility,
|
||||||
|
val writeVisibility: Visibility?,
|
||||||
|
val isAbstract: Boolean,
|
||||||
|
val isClosed: Boolean,
|
||||||
|
val isOverride: Boolean,
|
||||||
|
val isTransient: Boolean,
|
||||||
|
val prop: ObjProperty,
|
||||||
|
override val pos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
scope.addItem(
|
||||||
|
storageName,
|
||||||
|
isMutable,
|
||||||
|
prop,
|
||||||
|
visibility,
|
||||||
|
writeVisibility,
|
||||||
|
recordType = ObjRecord.Type.Property,
|
||||||
|
isAbstract = isAbstract,
|
||||||
|
isClosed = isClosed,
|
||||||
|
isOverride = isOverride,
|
||||||
|
isTransient = isTransient
|
||||||
|
)
|
||||||
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InstanceDelegatedInitStatement(
|
||||||
|
val storageName: String,
|
||||||
|
val memberName: String,
|
||||||
|
val isMutable: Boolean,
|
||||||
|
val visibility: Visibility,
|
||||||
|
val writeVisibility: Visibility?,
|
||||||
|
val isAbstract: Boolean,
|
||||||
|
val isClosed: Boolean,
|
||||||
|
val isOverride: Boolean,
|
||||||
|
val isTransient: Boolean,
|
||||||
|
val accessTypeLabel: String,
|
||||||
|
val initializer: Statement,
|
||||||
|
override val pos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val initValue = initializer.execute(scope)
|
||||||
|
val accessType = ObjString(accessTypeLabel)
|
||||||
|
val finalDelegate = try {
|
||||||
|
initValue.invokeInstanceMethod(
|
||||||
|
scope,
|
||||||
|
"bind",
|
||||||
|
Arguments(ObjString(memberName), accessType, scope.thisObj)
|
||||||
|
)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
initValue
|
||||||
|
}
|
||||||
|
scope.addItem(
|
||||||
|
storageName,
|
||||||
|
isMutable,
|
||||||
|
ObjUnset,
|
||||||
|
visibility,
|
||||||
|
writeVisibility,
|
||||||
|
recordType = ObjRecord.Type.Delegated,
|
||||||
|
isAbstract = isAbstract,
|
||||||
|
isClosed = isClosed,
|
||||||
|
isOverride = isOverride,
|
||||||
|
isTransient = isTransient
|
||||||
|
).apply {
|
||||||
|
delegate = finalDelegate
|
||||||
|
}
|
||||||
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Sergey S. Chernov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sergeych.lyng
|
||||||
|
|
||||||
|
import net.sergeych.lyng.bytecode.CmdFrame
|
||||||
|
import net.sergeych.lyng.bytecode.CmdVm
|
||||||
|
import net.sergeych.lyng.obj.Obj
|
||||||
|
import net.sergeych.lyng.obj.ObjNull
|
||||||
|
import net.sergeych.lyng.obj.ObjRecord
|
||||||
|
|
||||||
|
class PropertyAccessorStatement(
|
||||||
|
val body: Statement,
|
||||||
|
val argName: String?,
|
||||||
|
override val pos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
if (argName != null) {
|
||||||
|
val value = scope.args.list.firstOrNull() ?: ObjNull
|
||||||
|
val prev = scope.skipScopeCreation
|
||||||
|
scope.skipScopeCreation = true
|
||||||
|
return try {
|
||||||
|
if (body is net.sergeych.lyng.bytecode.BytecodeStatement) {
|
||||||
|
val fn = body.bytecodeFunction()
|
||||||
|
val binder: suspend (CmdFrame, Arguments) -> Unit = { frame, arguments ->
|
||||||
|
val slotPlan = fn.localSlotPlanByName()
|
||||||
|
val slotIndex = slotPlan[argName]
|
||||||
|
val argValue = arguments.list.firstOrNull() ?: ObjNull
|
||||||
|
if (slotIndex != null) {
|
||||||
|
frame.frame.setObj(slotIndex, argValue)
|
||||||
|
} else if (scope.getLocalRecordDirect(argName) == null) {
|
||||||
|
scope.addItem(argName, true, argValue, recordType = ObjRecord.Type.Argument)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scope.pos = pos
|
||||||
|
CmdVm().execute(fn, scope, scope.args, binder)
|
||||||
|
} else {
|
||||||
|
scope.addItem(argName, true, value, recordType = ObjRecord.Type.Argument)
|
||||||
|
body.execute(scope)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
scope.skipScopeCreation = prev
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return body.execute(scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -39,7 +39,7 @@ fun nextFrameId(): Long = FrameIdGen.nextId()
|
|||||||
*
|
*
|
||||||
* There are special types of scopes:
|
* There are special types of scopes:
|
||||||
*
|
*
|
||||||
* - [ClosureScope] - scope used to apply a closure to some thisObj scope
|
* - [BytecodeClosureScope] - scope used to apply a closure to some thisObj scope
|
||||||
*/
|
*/
|
||||||
open class Scope(
|
open class Scope(
|
||||||
var parent: Scope?,
|
var parent: Scope?,
|
||||||
@ -76,11 +76,19 @@ open class Scope(
|
|||||||
|
|
||||||
internal fun setThisVariants(primary: Obj, extras: List<Obj>) {
|
internal fun setThisVariants(primary: Obj, extras: List<Obj>) {
|
||||||
thisObj = primary
|
thisObj = primary
|
||||||
thisVariants.clear()
|
val extrasSnapshot = when {
|
||||||
thisVariants.add(primary)
|
extras.isEmpty() -> emptyList()
|
||||||
for (obj in extras) {
|
extras === thisVariants -> extras.toList()
|
||||||
if (obj !== primary && !thisVariants.contains(obj)) {
|
extras is MutableList<*> -> synchronized(extras) { extras.toList() }
|
||||||
thisVariants.add(obj)
|
else -> extras.toList()
|
||||||
|
}
|
||||||
|
synchronized(thisVariants) {
|
||||||
|
thisVariants.clear()
|
||||||
|
thisVariants.add(primary)
|
||||||
|
for (obj in extrasSnapshot) {
|
||||||
|
if (obj !== primary && !thisVariants.contains(obj)) {
|
||||||
|
thisVariants.add(obj)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,7 +106,7 @@ open class Scope(
|
|||||||
for (cls in receiverClass.mro) {
|
for (cls in receiverClass.mro) {
|
||||||
s.extensions[cls]?.get(name)?.let { return it }
|
s.extensions[cls]?.get(name)?.let { return it }
|
||||||
}
|
}
|
||||||
if (s is ClosureScope) {
|
if (s is BytecodeClosureScope) {
|
||||||
s.closureScope.findExtension(receiverClass, name)?.let { return it }
|
s.closureScope.findExtension(receiverClass, name)?.let { return it }
|
||||||
}
|
}
|
||||||
s = s.parent
|
s = s.parent
|
||||||
@ -122,7 +130,7 @@ open class Scope(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal lookup helpers that deliberately avoid invoking overridden `get` implementations
|
* Internal lookup helpers that deliberately avoid invoking overridden `get` implementations
|
||||||
* (notably in ClosureScope) to prevent accidental ping-pong and infinite recursion across
|
* (notably in BytecodeClosureScope) to prevent accidental ping-pong and infinite recursion across
|
||||||
* intertwined closure frames. They traverse the plain parent chain and consult only locals
|
* intertwined closure frames. They traverse the plain parent chain and consult only locals
|
||||||
* and bindings of each frame. Instance/class member fallback must be decided by the caller.
|
* and bindings of each frame. Instance/class member fallback must be decided by the caller.
|
||||||
*/
|
*/
|
||||||
@ -165,23 +173,16 @@ open class Scope(
|
|||||||
val effectiveCaller = caller ?: currentClassCtx
|
val effectiveCaller = caller ?: currentClassCtx
|
||||||
while (s != null && hops++ < 1024) {
|
while (s != null && hops++ < 1024) {
|
||||||
tryGetLocalRecord(s, name, effectiveCaller)?.let { return it }
|
tryGetLocalRecord(s, name, effectiveCaller)?.let { return it }
|
||||||
s = if (followClosure && s is ClosureScope) s.closureScope else s.parent
|
s = if (followClosure && s is BytecodeClosureScope) s.closureScope else s.parent
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun resolveCaptureRecord(name: String): ObjRecord? {
|
|
||||||
if (captureRecords != null) {
|
|
||||||
raiseIllegalState("resolveCaptureRecord is interpreter-only; bytecode captures use captureRecords")
|
|
||||||
}
|
|
||||||
return chainLookupIgnoreClosure(name, followClosure = true, caller = currentClassCtx)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform base Scope.get semantics for this frame without delegating into parent.get
|
* Perform base Scope.get semantics for this frame without delegating into parent.get
|
||||||
* virtual dispatch. This checks:
|
* virtual dispatch. This checks:
|
||||||
* - locals/bindings in this frame
|
* - locals/bindings in this frame
|
||||||
* - walks raw parent chain for locals/bindings (ignoring ClosureScope-specific overrides)
|
* - walks raw parent chain for locals/bindings (ignoring BytecodeClosureScope-specific overrides)
|
||||||
* - finally falls back to this frame's `thisObj` instance/class members
|
* - finally falls back to this frame's `thisObj` instance/class members
|
||||||
*/
|
*/
|
||||||
internal fun baseGetIgnoreClosure(name: String): ObjRecord? {
|
internal fun baseGetIgnoreClosure(name: String): ObjRecord? {
|
||||||
@ -211,7 +212,7 @@ open class Scope(
|
|||||||
* - locals/bindings of each frame
|
* - locals/bindings of each frame
|
||||||
* - then instance/class members of each frame's `thisObj`.
|
* - then instance/class members of each frame's `thisObj`.
|
||||||
* This completely avoids invoking overridden `get` implementations, preventing
|
* This completely avoids invoking overridden `get` implementations, preventing
|
||||||
* ping-pong recursion between `ClosureScope` frames.
|
* ping-pong recursion between `BytecodeClosureScope` frames.
|
||||||
*/
|
*/
|
||||||
internal fun chainLookupWithMembers(name: String, caller: net.sergeych.lyng.obj.ObjClass? = currentClassCtx, followClosure: Boolean = false): ObjRecord? {
|
internal fun chainLookupWithMembers(name: String, caller: net.sergeych.lyng.obj.ObjClass? = currentClassCtx, followClosure: Boolean = false): ObjRecord? {
|
||||||
var s: Scope? = this
|
var s: Scope? = this
|
||||||
@ -228,7 +229,7 @@ open class Scope(
|
|||||||
} else return rec
|
} else return rec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s = if (followClosure && s is ClosureScope) s.closureScope else s.parent
|
s = if (followClosure && s is BytecodeClosureScope) s.closureScope else s.parent
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -635,11 +636,6 @@ open class Scope(
|
|||||||
it.value = value
|
it.value = value
|
||||||
// keep local binding index consistent within the frame
|
// keep local binding index consistent within the frame
|
||||||
localBindings[name] = it
|
localBindings[name] = it
|
||||||
// If we are a ClosureScope, mirror binding into the caller frame to keep it discoverable
|
|
||||||
// across suspension when resumed on the call frame
|
|
||||||
if (this is ClosureScope) {
|
|
||||||
callScope.localBindings[name] = it
|
|
||||||
}
|
|
||||||
bumpClassLayoutIfNeeded(name, value, recordType)
|
bumpClassLayoutIfNeeded(name, value, recordType)
|
||||||
it
|
it
|
||||||
} ?: addItem(name, true, value, visibility, writeVisibility, recordType, isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride)
|
} ?: addItem(name, true, value, visibility, writeVisibility, recordType, isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride)
|
||||||
@ -694,19 +690,6 @@ open class Scope(
|
|||||||
}
|
}
|
||||||
// Index this binding within the current frame to help resolve locals across suspension
|
// Index this binding within the current frame to help resolve locals across suspension
|
||||||
localBindings[name] = rec
|
localBindings[name] = rec
|
||||||
// If we are a ClosureScope, mirror binding into the caller frame to keep it discoverable
|
|
||||||
// across suspension when resumed on the call frame
|
|
||||||
if (this is ClosureScope) {
|
|
||||||
callScope.localBindings[name] = rec
|
|
||||||
// Additionally, expose the binding in caller's objects and slot map so identifier
|
|
||||||
// resolution after suspension can still find it even if the active scope is a child
|
|
||||||
// of the callScope (e.g., due to internal withChildFrame usage).
|
|
||||||
// This keeps visibility within the method body but prevents leaking outside the caller frame.
|
|
||||||
callScope.objects[name] = rec
|
|
||||||
if (callScope.getSlotIndexOf(name) == null) {
|
|
||||||
callScope.allocateSlotFor(name, rec)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Map to a slot for fast local access (ensure consistency)
|
// Map to a slot for fast local access (ensure consistency)
|
||||||
if (nameToSlot.isEmpty()) {
|
if (nameToSlot.isEmpty()) {
|
||||||
allocateSlotFor(name, rec)
|
allocateSlotFor(name, rec)
|
||||||
@ -751,12 +734,7 @@ open class Scope(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun addFn(vararg names: String, callSignature: CallSignature? = null, fn: suspend Scope.() -> Obj) {
|
fun addFn(vararg names: String, callSignature: CallSignature? = null, fn: suspend Scope.() -> Obj) {
|
||||||
val newFn = object : Statement() {
|
val newFn = net.sergeych.lyng.obj.ObjNativeCallable { fn() }
|
||||||
override val pos: Pos = Pos.builtIn
|
|
||||||
|
|
||||||
override suspend fun execute(scope: Scope): Obj = scope.fn()
|
|
||||||
|
|
||||||
}
|
|
||||||
for (name in names) {
|
for (name in names) {
|
||||||
addItem(
|
addItem(
|
||||||
name,
|
name,
|
||||||
@ -834,7 +812,7 @@ open class Scope(
|
|||||||
}
|
}
|
||||||
|
|
||||||
open fun applyClosure(closure: Scope, preferredThisType: String? = null): Scope =
|
open fun applyClosure(closure: Scope, preferredThisType: String? = null): Scope =
|
||||||
ClosureScope(this, closure, preferredThisType)
|
BytecodeClosureScope(this, closure, preferredThisType)
|
||||||
|
|
||||||
internal fun applyClosureForBytecode(closure: Scope, preferredThisType: String? = null): Scope {
|
internal fun applyClosureForBytecode(closure: Scope, preferredThisType: String? = null): Scope {
|
||||||
return BytecodeClosureScope(this, closure, preferredThisType)
|
return BytecodeClosureScope(this, closure, preferredThisType)
|
||||||
|
|||||||
@ -40,6 +40,7 @@ class Script(
|
|||||||
private val moduleBytecode: CmdFunction? = null,
|
private val moduleBytecode: CmdFunction? = null,
|
||||||
// private val catchReturn: Boolean = false,
|
// private val catchReturn: Boolean = false,
|
||||||
) : Statement() {
|
) : Statement() {
|
||||||
|
fun statements(): List<Statement> = statements
|
||||||
|
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
scope.pos = pos
|
scope.pos = pos
|
||||||
@ -86,11 +87,10 @@ class Script(
|
|||||||
moduleBytecode?.let { fn ->
|
moduleBytecode?.let { fn ->
|
||||||
return CmdVm().execute(fn, scope, scope.args)
|
return CmdVm().execute(fn, scope, scope.args)
|
||||||
}
|
}
|
||||||
var lastResult: Obj = ObjVoid
|
if (statements.isNotEmpty()) {
|
||||||
for (s in statements) {
|
scope.raiseIllegalState("interpreter execution is not supported; missing module bytecode")
|
||||||
lastResult = s.execute(scope)
|
|
||||||
}
|
}
|
||||||
return lastResult
|
return ObjVoid
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun seedModuleSlots(scope: Scope) {
|
private suspend fun seedModuleSlots(scope: Scope) {
|
||||||
@ -332,7 +332,7 @@ class Script(
|
|||||||
addVoidFn("assert") {
|
addVoidFn("assert") {
|
||||||
val cond = requiredArg<ObjBool>(0)
|
val cond = requiredArg<ObjBool>(0)
|
||||||
val message = if (args.size > 1)
|
val message = if (args.size > 1)
|
||||||
": " + (args[1] as Statement).execute(this).toString(this).value
|
": " + (args[1] as Obj).callOn(this).toString(this).value
|
||||||
else ""
|
else ""
|
||||||
if (!cond.value == true)
|
if (!cond.value == true)
|
||||||
raiseError(ObjAssertionFailedException(this, "Assertion failed$message"))
|
raiseError(ObjAssertionFailedException(this, "Assertion failed$message"))
|
||||||
@ -385,23 +385,23 @@ class Script(
|
|||||||
will be accepted.
|
will be accepted.
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
) {
|
) {
|
||||||
val code: Statement
|
val code: Obj
|
||||||
val expectedClass: ObjClass?
|
val expectedClass: ObjClass?
|
||||||
when (args.size) {
|
when (args.size) {
|
||||||
1 -> {
|
1 -> {
|
||||||
code = requiredArg<Statement>(0)
|
code = requiredArg<Obj>(0)
|
||||||
expectedClass = null
|
expectedClass = null
|
||||||
}
|
}
|
||||||
|
|
||||||
2 -> {
|
2 -> {
|
||||||
code = requiredArg<Statement>(1)
|
code = requiredArg<Obj>(1)
|
||||||
expectedClass = requiredArg<ObjClass>(0)
|
expectedClass = requiredArg<ObjClass>(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> raiseIllegalArgument("Expected 1 or 2 arguments, got ${args.size}")
|
else -> raiseIllegalArgument("Expected 1 or 2 arguments, got ${args.size}")
|
||||||
}
|
}
|
||||||
val result = try {
|
val result = try {
|
||||||
code.execute(this)
|
code.callOn(this)
|
||||||
null
|
null
|
||||||
} catch (e: ExecutionError) {
|
} catch (e: ExecutionError) {
|
||||||
e.errorObject
|
e.errorObject
|
||||||
@ -441,7 +441,7 @@ class Script(
|
|||||||
val condition = requiredArg<ObjBool>(0)
|
val condition = requiredArg<ObjBool>(0)
|
||||||
if (!condition.value) {
|
if (!condition.value) {
|
||||||
var message = args.list.getOrNull(1)
|
var message = args.list.getOrNull(1)
|
||||||
if (message is Statement) message = message.execute(this)
|
if (message is Obj && message.objClass == Statement.type) message = message.callOn(this)
|
||||||
raiseIllegalArgument(message?.toString() ?: "requirement not met")
|
raiseIllegalArgument(message?.toString() ?: "requirement not met")
|
||||||
}
|
}
|
||||||
ObjVoid
|
ObjVoid
|
||||||
@ -450,7 +450,7 @@ class Script(
|
|||||||
val condition = requiredArg<ObjBool>(0)
|
val condition = requiredArg<ObjBool>(0)
|
||||||
if (!condition.value) {
|
if (!condition.value) {
|
||||||
var message = args.list.getOrNull(1)
|
var message = args.list.getOrNull(1)
|
||||||
if (message is Statement) message = message.execute(this)
|
if (message is Obj && message.objClass == Statement.type) message = message.callOn(this)
|
||||||
raiseIllegalState(message?.toString() ?: "check failed")
|
raiseIllegalState(message?.toString() ?: "check failed")
|
||||||
}
|
}
|
||||||
ObjVoid
|
ObjVoid
|
||||||
@ -460,27 +460,23 @@ class Script(
|
|||||||
ObjVoid
|
ObjVoid
|
||||||
}
|
}
|
||||||
addFn("run") {
|
addFn("run") {
|
||||||
requireOnlyArg<Statement>().execute(this)
|
requireOnlyArg<Obj>().callOn(this)
|
||||||
}
|
}
|
||||||
addFn("cached") {
|
addFn("cached") {
|
||||||
val builder = requireOnlyArg<Statement>()
|
val builder = requireOnlyArg<Obj>()
|
||||||
val capturedScope = this
|
val capturedScope = this
|
||||||
var calculated = false
|
var calculated = false
|
||||||
var cachedValue: Obj = ObjVoid
|
var cachedValue: Obj = ObjVoid
|
||||||
val thunk = object : Statement() {
|
ObjNativeCallable {
|
||||||
override val pos: Pos = Pos.builtIn
|
if (!calculated) {
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
cachedValue = builder.callOn(capturedScope)
|
||||||
if (!calculated) {
|
calculated = true
|
||||||
cachedValue = builder.execute(capturedScope)
|
|
||||||
calculated = true
|
|
||||||
}
|
|
||||||
return cachedValue
|
|
||||||
}
|
}
|
||||||
|
cachedValue
|
||||||
}
|
}
|
||||||
thunk
|
|
||||||
}
|
}
|
||||||
addFn("lazy") {
|
addFn("lazy") {
|
||||||
val builder = requireOnlyArg<Statement>()
|
val builder = requireOnlyArg<Obj>()
|
||||||
ObjLazyDelegate(builder, this)
|
ObjLazyDelegate(builder, this)
|
||||||
}
|
}
|
||||||
addVoidFn("delay") {
|
addVoidFn("delay") {
|
||||||
@ -527,9 +523,9 @@ class Script(
|
|||||||
addConst("MapEntry", ObjMapEntry.type)
|
addConst("MapEntry", ObjMapEntry.type)
|
||||||
|
|
||||||
addFn("launch") {
|
addFn("launch") {
|
||||||
val callable = requireOnlyArg<Statement>()
|
val callable = requireOnlyArg<Obj>()
|
||||||
ObjDeferred(globalDefer {
|
ObjDeferred(globalDefer {
|
||||||
callable.execute(this@addFn)
|
callable.callOn(this@addFn)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -541,7 +537,7 @@ class Script(
|
|||||||
addFn("flow", callSignature = CallSignature(tailBlockReceiverType = "FlowBuilder")) {
|
addFn("flow", callSignature = CallSignature(tailBlockReceiverType = "FlowBuilder")) {
|
||||||
// important is: current context contains closure often used in call;
|
// important is: current context contains closure often used in call;
|
||||||
// we'll need it for the producer
|
// we'll need it for the producer
|
||||||
ObjFlow(requireOnlyArg<Statement>(), this)
|
ObjFlow(requireOnlyArg<Obj>(), this)
|
||||||
}
|
}
|
||||||
|
|
||||||
val pi = ObjReal(PI)
|
val pi = ObjReal(PI)
|
||||||
|
|||||||
@ -113,6 +113,96 @@ class BytecodeCompiler(
|
|||||||
is BlockStatement -> compileBlock(name, stmt)
|
is BlockStatement -> compileBlock(name, stmt)
|
||||||
is net.sergeych.lyng.InlineBlockStatement -> compileInlineBlock(name, stmt)
|
is net.sergeych.lyng.InlineBlockStatement -> compileInlineBlock(name, stmt)
|
||||||
is VarDeclStatement -> compileVarDecl(name, stmt)
|
is VarDeclStatement -> compileVarDecl(name, stmt)
|
||||||
|
is net.sergeych.lyng.ClassStaticFieldInitStatement -> {
|
||||||
|
val value = emitClassStaticFieldInit(stmt) ?: return null
|
||||||
|
builder.emit(Opcode.RET, value.slot)
|
||||||
|
val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount
|
||||||
|
builder.build(
|
||||||
|
name,
|
||||||
|
localCount,
|
||||||
|
addrCount = nextAddrSlot,
|
||||||
|
returnLabels = returnLabels,
|
||||||
|
scopeSlotIndices,
|
||||||
|
scopeSlotNames,
|
||||||
|
scopeSlotIsModule,
|
||||||
|
localSlotNames,
|
||||||
|
localSlotMutables,
|
||||||
|
localSlotDelegated,
|
||||||
|
localSlotCaptures
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.ClassInstanceInitDeclStatement -> {
|
||||||
|
val value = emitClassInstanceInitDecl(stmt) ?: return null
|
||||||
|
builder.emit(Opcode.RET, value.slot)
|
||||||
|
val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount
|
||||||
|
builder.build(
|
||||||
|
name,
|
||||||
|
localCount,
|
||||||
|
addrCount = nextAddrSlot,
|
||||||
|
returnLabels = returnLabels,
|
||||||
|
scopeSlotIndices,
|
||||||
|
scopeSlotNames,
|
||||||
|
scopeSlotIsModule,
|
||||||
|
localSlotNames,
|
||||||
|
localSlotMutables,
|
||||||
|
localSlotDelegated,
|
||||||
|
localSlotCaptures
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.ClassInstanceFieldDeclStatement -> {
|
||||||
|
val value = emitClassInstanceFieldDecl(stmt) ?: return null
|
||||||
|
builder.emit(Opcode.RET, value.slot)
|
||||||
|
val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount
|
||||||
|
builder.build(
|
||||||
|
name,
|
||||||
|
localCount,
|
||||||
|
addrCount = nextAddrSlot,
|
||||||
|
returnLabels = returnLabels,
|
||||||
|
scopeSlotIndices,
|
||||||
|
scopeSlotNames,
|
||||||
|
scopeSlotIsModule,
|
||||||
|
localSlotNames,
|
||||||
|
localSlotMutables,
|
||||||
|
localSlotDelegated,
|
||||||
|
localSlotCaptures
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.ClassInstancePropertyDeclStatement -> {
|
||||||
|
val value = emitClassInstancePropertyDecl(stmt) ?: return null
|
||||||
|
builder.emit(Opcode.RET, value.slot)
|
||||||
|
val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount
|
||||||
|
builder.build(
|
||||||
|
name,
|
||||||
|
localCount,
|
||||||
|
addrCount = nextAddrSlot,
|
||||||
|
returnLabels = returnLabels,
|
||||||
|
scopeSlotIndices,
|
||||||
|
scopeSlotNames,
|
||||||
|
scopeSlotIsModule,
|
||||||
|
localSlotNames,
|
||||||
|
localSlotMutables,
|
||||||
|
localSlotDelegated,
|
||||||
|
localSlotCaptures
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.ClassInstanceDelegatedDeclStatement -> {
|
||||||
|
val value = emitClassInstanceDelegatedDecl(stmt) ?: return null
|
||||||
|
builder.emit(Opcode.RET, value.slot)
|
||||||
|
val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount
|
||||||
|
builder.build(
|
||||||
|
name,
|
||||||
|
localCount,
|
||||||
|
addrCount = nextAddrSlot,
|
||||||
|
returnLabels = returnLabels,
|
||||||
|
scopeSlotIndices,
|
||||||
|
scopeSlotNames,
|
||||||
|
scopeSlotIsModule,
|
||||||
|
localSlotNames,
|
||||||
|
localSlotMutables,
|
||||||
|
localSlotDelegated,
|
||||||
|
localSlotCaptures
|
||||||
|
)
|
||||||
|
}
|
||||||
is DelegatedVarDeclStatement -> {
|
is DelegatedVarDeclStatement -> {
|
||||||
val value = emitDelegatedVarDecl(stmt) ?: return null
|
val value = emitDelegatedVarDecl(stmt) ?: return null
|
||||||
builder.emit(Opcode.RET, value.slot)
|
builder.emit(Opcode.RET, value.slot)
|
||||||
@ -131,6 +221,60 @@ class BytecodeCompiler(
|
|||||||
localSlotCaptures
|
localSlotCaptures
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
is net.sergeych.lyng.InstanceFieldInitStatement -> {
|
||||||
|
val value = emitInstanceFieldInit(stmt) ?: return null
|
||||||
|
builder.emit(Opcode.RET, value.slot)
|
||||||
|
val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount
|
||||||
|
builder.build(
|
||||||
|
name,
|
||||||
|
localCount,
|
||||||
|
addrCount = nextAddrSlot,
|
||||||
|
returnLabels = returnLabels,
|
||||||
|
scopeSlotIndices,
|
||||||
|
scopeSlotNames,
|
||||||
|
scopeSlotIsModule,
|
||||||
|
localSlotNames,
|
||||||
|
localSlotMutables,
|
||||||
|
localSlotDelegated,
|
||||||
|
localSlotCaptures
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.InstancePropertyInitStatement -> {
|
||||||
|
val value = emitInstancePropertyInit(stmt) ?: return null
|
||||||
|
builder.emit(Opcode.RET, value.slot)
|
||||||
|
val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount
|
||||||
|
builder.build(
|
||||||
|
name,
|
||||||
|
localCount,
|
||||||
|
addrCount = nextAddrSlot,
|
||||||
|
returnLabels = returnLabels,
|
||||||
|
scopeSlotIndices,
|
||||||
|
scopeSlotNames,
|
||||||
|
scopeSlotIsModule,
|
||||||
|
localSlotNames,
|
||||||
|
localSlotMutables,
|
||||||
|
localSlotDelegated,
|
||||||
|
localSlotCaptures
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.InstanceDelegatedInitStatement -> {
|
||||||
|
val value = emitInstanceDelegatedInit(stmt) ?: return null
|
||||||
|
builder.emit(Opcode.RET, value.slot)
|
||||||
|
val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount
|
||||||
|
builder.build(
|
||||||
|
name,
|
||||||
|
localCount,
|
||||||
|
addrCount = nextAddrSlot,
|
||||||
|
returnLabels = returnLabels,
|
||||||
|
scopeSlotIndices,
|
||||||
|
scopeSlotNames,
|
||||||
|
scopeSlotIsModule,
|
||||||
|
localSlotNames,
|
||||||
|
localSlotMutables,
|
||||||
|
localSlotDelegated,
|
||||||
|
localSlotCaptures
|
||||||
|
)
|
||||||
|
}
|
||||||
is DestructuringVarDeclStatement -> {
|
is DestructuringVarDeclStatement -> {
|
||||||
val value = emitDestructuringVarDecl(stmt) ?: return null
|
val value = emitDestructuringVarDecl(stmt) ?: return null
|
||||||
builder.emit(Opcode.RET, value.slot)
|
builder.emit(Opcode.RET, value.slot)
|
||||||
@ -317,6 +461,17 @@ class BytecodeCompiler(
|
|||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun missingMemberMessage(receiverClass: ObjClass, name: String): String {
|
||||||
|
return "no such member: $name on ${receiverClass.className}. " +
|
||||||
|
"Considered order: ${receiverClass.renderLinearization(true)}. " +
|
||||||
|
"Tip: try this@Base.$name(...) or (obj as Base).$name(...) if ambiguous"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun missingFieldMessage(receiverClass: ObjClass, name: String): String {
|
||||||
|
return "no such field: $name on ${receiverClass.className}. " +
|
||||||
|
"Considered order: ${receiverClass.renderLinearization(true)}"
|
||||||
|
}
|
||||||
|
|
||||||
private fun compileRef(ref: ObjRef): CompiledValue? {
|
private fun compileRef(ref: ObjRef): CompiledValue? {
|
||||||
return when (ref) {
|
return when (ref) {
|
||||||
is ConstRef -> compileConst(ref.constValue)
|
is ConstRef -> compileConst(ref.constValue)
|
||||||
@ -2535,15 +2690,7 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun compileFieldRef(ref: FieldRef): CompiledValue? {
|
private fun compileFieldRef(ref: FieldRef): CompiledValue? {
|
||||||
val receiverClass = resolveReceiverClass(ref.target)
|
val receiverClass = resolveReceiverClass(ref.target) ?: ObjDynamic.type
|
||||||
?: if (isAllowedObjectMember(ref.name)) {
|
|
||||||
Obj.rootObjectType
|
|
||||||
} else {
|
|
||||||
throw BytecodeCompileException(
|
|
||||||
"Member access requires compile-time receiver type: ${ref.name}",
|
|
||||||
Pos.builtIn
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (receiverClass == ObjDynamic.type) {
|
if (receiverClass == ObjDynamic.type) {
|
||||||
val receiver = compileRefWithFallback(ref.target, null, Pos.builtIn) ?: return null
|
val receiver = compileRefWithFallback(ref.target, null, Pos.builtIn) ?: return null
|
||||||
val dst = allocSlot()
|
val dst = allocSlot()
|
||||||
@ -2625,7 +2772,7 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
val extSlot = resolveExtensionGetterSlot(receiverClass, ref.name)
|
val extSlot = resolveExtensionGetterSlot(receiverClass, ref.name)
|
||||||
?: throw BytecodeCompileException(
|
?: throw BytecodeCompileException(
|
||||||
"Unknown member ${ref.name} on ${receiverClass.className}",
|
missingFieldMessage(receiverClass, ref.name),
|
||||||
Pos.builtIn
|
Pos.builtIn
|
||||||
)
|
)
|
||||||
val callee = ensureObjSlot(extSlot)
|
val callee = ensureObjSlot(extSlot)
|
||||||
@ -3627,15 +3774,7 @@ class BytecodeCompiler(
|
|||||||
|
|
||||||
private fun compileMethodCall(ref: MethodCallRef): CompiledValue? {
|
private fun compileMethodCall(ref: MethodCallRef): CompiledValue? {
|
||||||
val callPos = callSitePos()
|
val callPos = callSitePos()
|
||||||
val receiverClass = resolveReceiverClass(ref.receiver)
|
val receiverClass = resolveReceiverClass(ref.receiver) ?: ObjDynamic.type
|
||||||
?: if (isAllowedObjectMember(ref.name)) {
|
|
||||||
Obj.rootObjectType
|
|
||||||
} else {
|
|
||||||
throw BytecodeCompileException(
|
|
||||||
"Member call requires compile-time receiver type: ${ref.name}",
|
|
||||||
Pos.builtIn
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val receiver = compileRefWithFallback(ref.receiver, null, refPosOrCurrent(ref.receiver)) ?: return null
|
val receiver = compileRefWithFallback(ref.receiver, null, refPosOrCurrent(ref.receiver)) ?: return null
|
||||||
val dst = allocSlot()
|
val dst = allocSlot()
|
||||||
if (receiverClass == ObjDynamic.type) {
|
if (receiverClass == ObjDynamic.type) {
|
||||||
@ -3695,6 +3834,41 @@ class BytecodeCompiler(
|
|||||||
builder.mark(endLabel)
|
builder.mark(endLabel)
|
||||||
return CompiledValue(dst, SlotType.OBJ)
|
return CompiledValue(dst, SlotType.OBJ)
|
||||||
}
|
}
|
||||||
|
val fieldId = receiverClass.instanceFieldIdMap()[ref.name]
|
||||||
|
if (fieldId != null) {
|
||||||
|
val encodedFieldId = encodeMemberId(receiverClass, fieldId) ?: fieldId
|
||||||
|
val calleeSlot = allocSlot()
|
||||||
|
if (!ref.isOptional) {
|
||||||
|
builder.emit(Opcode.GET_MEMBER_SLOT, receiver.slot, encodedFieldId, -1, calleeSlot)
|
||||||
|
}
|
||||||
|
if (!ref.isOptional) {
|
||||||
|
val args = compileCallArgs(ref.args, ref.tailBlock) ?: return null
|
||||||
|
val encodedCount = encodeCallArgCount(args) ?: return null
|
||||||
|
setPos(callPos)
|
||||||
|
builder.emit(Opcode.CALL_SLOT, calleeSlot, args.base, encodedCount, dst)
|
||||||
|
} else {
|
||||||
|
val nullSlot = allocSlot()
|
||||||
|
builder.emit(Opcode.CONST_NULL, nullSlot)
|
||||||
|
val cmpSlot = allocSlot()
|
||||||
|
builder.emit(Opcode.CMP_REF_EQ_OBJ, receiver.slot, nullSlot, cmpSlot)
|
||||||
|
val nullLabel = builder.label()
|
||||||
|
val endLabel = builder.label()
|
||||||
|
builder.emit(
|
||||||
|
Opcode.JMP_IF_TRUE,
|
||||||
|
listOf(CmdBuilder.Operand.IntVal(cmpSlot), CmdBuilder.Operand.LabelRef(nullLabel))
|
||||||
|
)
|
||||||
|
builder.emit(Opcode.GET_MEMBER_SLOT, receiver.slot, encodedFieldId, -1, calleeSlot)
|
||||||
|
val args = compileCallArgs(ref.args, ref.tailBlock) ?: return null
|
||||||
|
val encodedCount = encodeCallArgCount(args) ?: return null
|
||||||
|
setPos(callPos)
|
||||||
|
builder.emit(Opcode.CALL_SLOT, calleeSlot, args.base, encodedCount, dst)
|
||||||
|
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
|
||||||
|
builder.mark(nullLabel)
|
||||||
|
builder.emit(Opcode.CONST_NULL, dst)
|
||||||
|
builder.mark(endLabel)
|
||||||
|
}
|
||||||
|
return CompiledValue(dst, SlotType.OBJ)
|
||||||
|
}
|
||||||
if (isKnownClassReceiver(ref.receiver)) {
|
if (isKnownClassReceiver(ref.receiver)) {
|
||||||
val nameId = builder.addConst(BytecodeConst.StringVal(ref.name))
|
val nameId = builder.addConst(BytecodeConst.StringVal(ref.name))
|
||||||
val memberSlot = allocSlot()
|
val memberSlot = allocSlot()
|
||||||
@ -3725,7 +3899,7 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
val extSlot = resolveExtensionCallableSlot(receiverClass, ref.name)
|
val extSlot = resolveExtensionCallableSlot(receiverClass, ref.name)
|
||||||
?: throw BytecodeCompileException(
|
?: throw BytecodeCompileException(
|
||||||
"Unknown member ${ref.name} on ${receiverClass.className}",
|
missingMemberMessage(receiverClass, ref.name),
|
||||||
Pos.builtIn
|
Pos.builtIn
|
||||||
)
|
)
|
||||||
val callee = ensureObjSlot(extSlot)
|
val callee = ensureObjSlot(extSlot)
|
||||||
@ -3932,15 +4106,16 @@ class BytecodeCompiler(
|
|||||||
return CallArgs(base = argSlots[0], count = argSlots.size, planId = planId)
|
return CallArgs(base = argSlots[0], count = argSlots.size, planId = planId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compileArgValue(stmt: Statement): CompiledValue? {
|
private fun compileArgValue(value: Obj): CompiledValue? {
|
||||||
return when (stmt) {
|
return when (value) {
|
||||||
is ExpressionStatement -> compileRefWithFallback(stmt.ref, null, stmt.pos)
|
is ExpressionStatement -> compileRefWithFallback(value.ref, null, value.pos)
|
||||||
else -> {
|
is Statement -> {
|
||||||
throw BytecodeCompileException(
|
throw BytecodeCompileException(
|
||||||
"Bytecode compile error: unsupported argument expression",
|
"Bytecode compile error: unsupported argument expression",
|
||||||
stmt.pos
|
value.pos
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
else -> compileConst(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4137,7 +4312,12 @@ class BytecodeCompiler(
|
|||||||
|
|
||||||
private fun emitDeclFunction(stmt: net.sergeych.lyng.FunctionDeclStatement): CompiledValue {
|
private fun emitDeclFunction(stmt: net.sergeych.lyng.FunctionDeclStatement): CompiledValue {
|
||||||
val constId = builder.addConst(BytecodeConst.FunctionDecl(stmt.spec))
|
val constId = builder.addConst(BytecodeConst.FunctionDecl(stmt.spec))
|
||||||
val dst = allocSlot()
|
val dst = stmt.spec.slotIndex?.let { slotIndex ->
|
||||||
|
val scopeId = stmt.spec.scopeId ?: 0
|
||||||
|
val key = ScopeSlotKey(scopeId, slotIndex)
|
||||||
|
localSlotIndexByKey[key]?.let { scopeSlotCount + it }
|
||||||
|
?: scopeSlotMap[key]
|
||||||
|
} ?: allocSlot()
|
||||||
builder.emit(Opcode.DECL_FUNCTION, constId, dst)
|
builder.emit(Opcode.DECL_FUNCTION, constId, dst)
|
||||||
updateSlotType(dst, SlotType.OBJ)
|
updateSlotType(dst, SlotType.OBJ)
|
||||||
return CompiledValue(dst, SlotType.OBJ)
|
return CompiledValue(dst, SlotType.OBJ)
|
||||||
@ -4151,6 +4331,190 @@ class BytecodeCompiler(
|
|||||||
return CompiledValue(dst, SlotType.OBJ)
|
return CompiledValue(dst, SlotType.OBJ)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun emitClassStaticFieldInit(stmt: net.sergeych.lyng.ClassStaticFieldInitStatement): CompiledValue? {
|
||||||
|
val initValue = if (stmt.initializer != null) {
|
||||||
|
val compiled = compileStatementValueOrFallback(stmt.initializer) ?: return null
|
||||||
|
ensureObjSlot(compiled)
|
||||||
|
} else {
|
||||||
|
val slot = allocSlot()
|
||||||
|
builder.emit(Opcode.CONST_NULL, slot)
|
||||||
|
updateSlotType(slot, SlotType.OBJ)
|
||||||
|
CompiledValue(slot, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
val constId = if (stmt.isDelegated) {
|
||||||
|
builder.addConst(
|
||||||
|
BytecodeConst.ClassDelegatedDecl(
|
||||||
|
name = stmt.name,
|
||||||
|
isMutable = stmt.isMutable,
|
||||||
|
visibility = stmt.visibility,
|
||||||
|
writeVisibility = stmt.writeVisibility,
|
||||||
|
isTransient = stmt.isTransient
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
builder.addConst(
|
||||||
|
BytecodeConst.ClassFieldDecl(
|
||||||
|
name = stmt.name,
|
||||||
|
isMutable = stmt.isMutable,
|
||||||
|
visibility = stmt.visibility,
|
||||||
|
writeVisibility = stmt.writeVisibility,
|
||||||
|
isTransient = stmt.isTransient
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val opcode = if (stmt.isDelegated) Opcode.DECL_CLASS_DELEGATED else Opcode.DECL_CLASS_FIELD
|
||||||
|
builder.emit(opcode, constId, initValue.slot)
|
||||||
|
updateSlotType(initValue.slot, SlotType.OBJ)
|
||||||
|
return initValue
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun emitClassInstanceInitDecl(stmt: net.sergeych.lyng.ClassInstanceInitDeclStatement): CompiledValue? {
|
||||||
|
val constId = builder.addConst(BytecodeConst.ClassInstanceInitDecl(stmt.initStatement))
|
||||||
|
val slot = allocSlot()
|
||||||
|
builder.emit(Opcode.DECL_CLASS_INSTANCE_INIT, constId, slot)
|
||||||
|
updateSlotType(slot, SlotType.OBJ)
|
||||||
|
return CompiledValue(slot, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun emitClassInstanceFieldDecl(stmt: net.sergeych.lyng.ClassInstanceFieldDeclStatement): CompiledValue? {
|
||||||
|
val constId = builder.addConst(
|
||||||
|
BytecodeConst.ClassInstanceFieldDecl(
|
||||||
|
name = stmt.name,
|
||||||
|
isMutable = stmt.isMutable,
|
||||||
|
visibility = stmt.visibility,
|
||||||
|
writeVisibility = stmt.writeVisibility,
|
||||||
|
isTransient = stmt.isTransient,
|
||||||
|
isAbstract = stmt.isAbstract,
|
||||||
|
isClosed = stmt.isClosed,
|
||||||
|
isOverride = stmt.isOverride,
|
||||||
|
fieldId = stmt.fieldId,
|
||||||
|
initStatement = stmt.initStatement,
|
||||||
|
pos = stmt.pos
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val slot = allocSlot()
|
||||||
|
builder.emit(Opcode.DECL_CLASS_INSTANCE_FIELD, constId, slot)
|
||||||
|
updateSlotType(slot, SlotType.OBJ)
|
||||||
|
return CompiledValue(slot, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun emitClassInstancePropertyDecl(stmt: net.sergeych.lyng.ClassInstancePropertyDeclStatement): CompiledValue? {
|
||||||
|
val constId = builder.addConst(
|
||||||
|
BytecodeConst.ClassInstancePropertyDecl(
|
||||||
|
name = stmt.name,
|
||||||
|
isMutable = stmt.isMutable,
|
||||||
|
visibility = stmt.visibility,
|
||||||
|
writeVisibility = stmt.writeVisibility,
|
||||||
|
isTransient = stmt.isTransient,
|
||||||
|
isAbstract = stmt.isAbstract,
|
||||||
|
isClosed = stmt.isClosed,
|
||||||
|
isOverride = stmt.isOverride,
|
||||||
|
prop = stmt.prop,
|
||||||
|
methodId = stmt.methodId,
|
||||||
|
initStatement = stmt.initStatement,
|
||||||
|
pos = stmt.pos
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val slot = allocSlot()
|
||||||
|
builder.emit(Opcode.DECL_CLASS_INSTANCE_PROPERTY, constId, slot)
|
||||||
|
updateSlotType(slot, SlotType.OBJ)
|
||||||
|
return CompiledValue(slot, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun emitClassInstanceDelegatedDecl(stmt: net.sergeych.lyng.ClassInstanceDelegatedDeclStatement): CompiledValue? {
|
||||||
|
val constId = builder.addConst(
|
||||||
|
BytecodeConst.ClassInstanceDelegatedDecl(
|
||||||
|
name = stmt.name,
|
||||||
|
isMutable = stmt.isMutable,
|
||||||
|
visibility = stmt.visibility,
|
||||||
|
writeVisibility = stmt.writeVisibility,
|
||||||
|
isTransient = stmt.isTransient,
|
||||||
|
isAbstract = stmt.isAbstract,
|
||||||
|
isClosed = stmt.isClosed,
|
||||||
|
isOverride = stmt.isOverride,
|
||||||
|
methodId = stmt.methodId,
|
||||||
|
initStatement = stmt.initStatement,
|
||||||
|
pos = stmt.pos
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val slot = allocSlot()
|
||||||
|
builder.emit(Opcode.DECL_CLASS_INSTANCE_DELEGATED, constId, slot)
|
||||||
|
updateSlotType(slot, SlotType.OBJ)
|
||||||
|
return CompiledValue(slot, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun emitInstanceFieldInit(stmt: net.sergeych.lyng.InstanceFieldInitStatement): CompiledValue? {
|
||||||
|
val value = stmt.initializer?.let { compileStatementValueOrFallback(it) } ?: run {
|
||||||
|
val slot = allocSlot()
|
||||||
|
val constId = if (stmt.isLateInitVal) {
|
||||||
|
builder.addConst(BytecodeConst.ObjRef(ObjUnset))
|
||||||
|
} else {
|
||||||
|
builder.addConst(BytecodeConst.ObjRef(ObjNull))
|
||||||
|
}
|
||||||
|
builder.emit(Opcode.CONST_OBJ, constId, slot)
|
||||||
|
updateSlotType(slot, SlotType.OBJ)
|
||||||
|
CompiledValue(slot, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
val declId = builder.addConst(
|
||||||
|
BytecodeConst.InstanceFieldDecl(
|
||||||
|
name = stmt.storageName,
|
||||||
|
isMutable = stmt.isMutable,
|
||||||
|
visibility = stmt.visibility,
|
||||||
|
writeVisibility = stmt.writeVisibility,
|
||||||
|
isTransient = stmt.isTransient,
|
||||||
|
isAbstract = stmt.isAbstract,
|
||||||
|
isClosed = stmt.isClosed,
|
||||||
|
isOverride = stmt.isOverride
|
||||||
|
)
|
||||||
|
)
|
||||||
|
builder.emit(Opcode.DECL_INSTANCE_FIELD, declId, value.slot)
|
||||||
|
updateSlotType(value.slot, SlotType.OBJ)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun emitInstancePropertyInit(stmt: net.sergeych.lyng.InstancePropertyInitStatement): CompiledValue? {
|
||||||
|
val slot = allocSlot()
|
||||||
|
val constId = builder.addConst(BytecodeConst.ObjRef(stmt.prop))
|
||||||
|
builder.emit(Opcode.CONST_OBJ, constId, slot)
|
||||||
|
updateSlotType(slot, SlotType.OBJ)
|
||||||
|
val declId = builder.addConst(
|
||||||
|
BytecodeConst.InstancePropertyDecl(
|
||||||
|
name = stmt.storageName,
|
||||||
|
isMutable = stmt.isMutable,
|
||||||
|
visibility = stmt.visibility,
|
||||||
|
writeVisibility = stmt.writeVisibility,
|
||||||
|
isTransient = stmt.isTransient,
|
||||||
|
isAbstract = stmt.isAbstract,
|
||||||
|
isClosed = stmt.isClosed,
|
||||||
|
isOverride = stmt.isOverride
|
||||||
|
)
|
||||||
|
)
|
||||||
|
builder.emit(Opcode.DECL_INSTANCE_PROPERTY, declId, slot)
|
||||||
|
updateSlotType(slot, SlotType.OBJ)
|
||||||
|
return CompiledValue(slot, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun emitInstanceDelegatedInit(stmt: net.sergeych.lyng.InstanceDelegatedInitStatement): CompiledValue? {
|
||||||
|
val value = compileStatementValueOrFallback(stmt.initializer) ?: return null
|
||||||
|
val declId = builder.addConst(
|
||||||
|
BytecodeConst.InstanceDelegatedDecl(
|
||||||
|
storageName = stmt.storageName,
|
||||||
|
memberName = stmt.memberName,
|
||||||
|
isMutable = stmt.isMutable,
|
||||||
|
visibility = stmt.visibility,
|
||||||
|
writeVisibility = stmt.writeVisibility,
|
||||||
|
isTransient = stmt.isTransient,
|
||||||
|
isAbstract = stmt.isAbstract,
|
||||||
|
isClosed = stmt.isClosed,
|
||||||
|
isOverride = stmt.isOverride,
|
||||||
|
accessTypeLabel = stmt.accessTypeLabel
|
||||||
|
)
|
||||||
|
)
|
||||||
|
builder.emit(Opcode.DECL_INSTANCE_DELEGATED, declId, value.slot)
|
||||||
|
updateSlotType(value.slot, SlotType.OBJ)
|
||||||
|
return CompiledValue(value.slot, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
|
||||||
private fun compileStatementValueOrFallback(stmt: Statement, needResult: Boolean = true): CompiledValue? {
|
private fun compileStatementValueOrFallback(stmt: Statement, needResult: Boolean = true): CompiledValue? {
|
||||||
val target = if (stmt is BytecodeStatement) stmt.original else stmt
|
val target = if (stmt is BytecodeStatement) stmt.original else stmt
|
||||||
setPos(target.pos)
|
setPos(target.pos)
|
||||||
@ -4181,8 +4545,16 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
is BlockStatement -> emitBlock(target, true)
|
is BlockStatement -> emitBlock(target, true)
|
||||||
is VarDeclStatement -> emitVarDecl(target)
|
is VarDeclStatement -> emitVarDecl(target)
|
||||||
|
is net.sergeych.lyng.ClassStaticFieldInitStatement -> emitClassStaticFieldInit(target)
|
||||||
|
is net.sergeych.lyng.ClassInstanceInitDeclStatement -> emitClassInstanceInitDecl(target)
|
||||||
|
is net.sergeych.lyng.ClassInstanceFieldDeclStatement -> emitClassInstanceFieldDecl(target)
|
||||||
|
is net.sergeych.lyng.ClassInstancePropertyDeclStatement -> emitClassInstancePropertyDecl(target)
|
||||||
|
is net.sergeych.lyng.ClassInstanceDelegatedDeclStatement -> emitClassInstanceDelegatedDecl(target)
|
||||||
is DelegatedVarDeclStatement -> emitDelegatedVarDecl(target)
|
is DelegatedVarDeclStatement -> emitDelegatedVarDecl(target)
|
||||||
is DestructuringVarDeclStatement -> emitDestructuringVarDecl(target)
|
is DestructuringVarDeclStatement -> emitDestructuringVarDecl(target)
|
||||||
|
is net.sergeych.lyng.InstanceFieldInitStatement -> emitInstanceFieldInit(target)
|
||||||
|
is net.sergeych.lyng.InstancePropertyInitStatement -> emitInstancePropertyInit(target)
|
||||||
|
is net.sergeych.lyng.InstanceDelegatedInitStatement -> emitInstanceDelegatedInit(target)
|
||||||
is net.sergeych.lyng.ExtensionPropertyDeclStatement -> emitExtensionPropertyDecl(target)
|
is net.sergeych.lyng.ExtensionPropertyDeclStatement -> emitExtensionPropertyDecl(target)
|
||||||
is net.sergeych.lyng.ClassDeclStatement -> emitDeclClass(target)
|
is net.sergeych.lyng.ClassDeclStatement -> emitDeclClass(target)
|
||||||
is net.sergeych.lyng.FunctionDeclStatement -> emitDeclFunction(target)
|
is net.sergeych.lyng.FunctionDeclStatement -> emitDeclFunction(target)
|
||||||
@ -4215,7 +4587,15 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is VarDeclStatement -> emitVarDecl(target)
|
is VarDeclStatement -> emitVarDecl(target)
|
||||||
|
is net.sergeych.lyng.ClassStaticFieldInitStatement -> emitClassStaticFieldInit(target)
|
||||||
|
is net.sergeych.lyng.ClassInstanceInitDeclStatement -> emitClassInstanceInitDecl(target)
|
||||||
|
is net.sergeych.lyng.ClassInstanceFieldDeclStatement -> emitClassInstanceFieldDecl(target)
|
||||||
|
is net.sergeych.lyng.ClassInstancePropertyDeclStatement -> emitClassInstancePropertyDecl(target)
|
||||||
|
is net.sergeych.lyng.ClassInstanceDelegatedDeclStatement -> emitClassInstanceDelegatedDecl(target)
|
||||||
is DelegatedVarDeclStatement -> emitDelegatedVarDecl(target)
|
is DelegatedVarDeclStatement -> emitDelegatedVarDecl(target)
|
||||||
|
is net.sergeych.lyng.InstanceFieldInitStatement -> emitInstanceFieldInit(target)
|
||||||
|
is net.sergeych.lyng.InstancePropertyInitStatement -> emitInstancePropertyInit(target)
|
||||||
|
is net.sergeych.lyng.InstanceDelegatedInitStatement -> emitInstanceDelegatedInit(target)
|
||||||
is IfStatement -> compileIfStatement(target)
|
is IfStatement -> compileIfStatement(target)
|
||||||
is net.sergeych.lyng.ClassDeclStatement -> emitDeclClass(target)
|
is net.sergeych.lyng.ClassDeclStatement -> emitDeclClass(target)
|
||||||
is net.sergeych.lyng.FunctionDeclStatement -> emitDeclFunction(target)
|
is net.sergeych.lyng.FunctionDeclStatement -> emitDeclFunction(target)
|
||||||
@ -5858,6 +6238,11 @@ class BytecodeCompiler(
|
|||||||
private fun resolveTypeRefClass(ref: ObjRef): ObjClass? {
|
private fun resolveTypeRefClass(ref: ObjRef): ObjClass? {
|
||||||
return when (ref) {
|
return when (ref) {
|
||||||
is ConstRef -> ref.constValue as? ObjClass
|
is ConstRef -> ref.constValue as? ObjClass
|
||||||
|
is TypeDeclRef -> when (val decl = ref.decl()) {
|
||||||
|
is TypeDecl.Simple -> resolveTypeNameClass(decl.name) ?: nameObjClass[decl.name]
|
||||||
|
is TypeDecl.Generic -> resolveTypeNameClass(decl.name) ?: nameObjClass[decl.name]
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
is LocalSlotRef -> resolveTypeNameClass(ref.name) ?: nameObjClass[ref.name]
|
is LocalSlotRef -> resolveTypeNameClass(ref.name) ?: nameObjClass[ref.name]
|
||||||
is LocalVarRef -> resolveTypeNameClass(ref.name) ?: nameObjClass[ref.name]
|
is LocalVarRef -> resolveTypeNameClass(ref.name) ?: nameObjClass[ref.name]
|
||||||
is FastLocalVarRef -> resolveTypeNameClass(ref.name) ?: nameObjClass[ref.name]
|
is FastLocalVarRef -> resolveTypeNameClass(ref.name) ?: nameObjClass[ref.name]
|
||||||
@ -6336,6 +6721,25 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
stmt.initializer?.let { collectScopeSlots(it) }
|
stmt.initializer?.let { collectScopeSlots(it) }
|
||||||
}
|
}
|
||||||
|
is net.sergeych.lyng.FunctionDeclStatement -> {
|
||||||
|
val slotIndex = stmt.spec.slotIndex
|
||||||
|
val scopeId = stmt.spec.scopeId ?: 0
|
||||||
|
if (slotIndex != null) {
|
||||||
|
val key = ScopeSlotKey(scopeId, slotIndex)
|
||||||
|
if (allowLocalSlots) {
|
||||||
|
if (!localSlotInfoMap.containsKey(key)) {
|
||||||
|
localSlotInfoMap[key] = LocalSlotInfo(stmt.spec.name, isMutable = false, isDelegated = false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!scopeSlotMap.containsKey(key)) {
|
||||||
|
scopeSlotMap[key] = scopeSlotMap.size
|
||||||
|
}
|
||||||
|
if (!scopeSlotNameMap.containsKey(key)) {
|
||||||
|
scopeSlotNameMap[key] = stmt.spec.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
is DelegatedVarDeclStatement -> {
|
is DelegatedVarDeclStatement -> {
|
||||||
val slotIndex = stmt.slotIndex
|
val slotIndex = stmt.slotIndex
|
||||||
val scopeId = stmt.scopeId ?: 0
|
val scopeId = stmt.scopeId ?: 0
|
||||||
@ -6399,6 +6803,15 @@ class BytecodeCompiler(
|
|||||||
is net.sergeych.lyng.ReturnStatement -> {
|
is net.sergeych.lyng.ReturnStatement -> {
|
||||||
stmt.resultExpr?.let { collectScopeSlots(it) }
|
stmt.resultExpr?.let { collectScopeSlots(it) }
|
||||||
}
|
}
|
||||||
|
is net.sergeych.lyng.ClassStaticFieldInitStatement -> {
|
||||||
|
stmt.initializer?.let { collectScopeSlots(it) }
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.InstanceFieldInitStatement -> {
|
||||||
|
stmt.initializer?.let { collectScopeSlots(it) }
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.InstanceDelegatedInitStatement -> {
|
||||||
|
collectScopeSlots(stmt.initializer)
|
||||||
|
}
|
||||||
is net.sergeych.lyng.ThrowStatement -> {
|
is net.sergeych.lyng.ThrowStatement -> {
|
||||||
collectScopeSlots(stmt.throwExpr)
|
collectScopeSlots(stmt.throwExpr)
|
||||||
}
|
}
|
||||||
@ -6466,7 +6879,11 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun isModuleSlot(scopeId: Int, name: String?): Boolean {
|
private fun isModuleSlot(scopeId: Int, name: String?): Boolean {
|
||||||
val moduleId = moduleScopeId ?: 0
|
val moduleId = moduleScopeId
|
||||||
|
if (moduleId == null) {
|
||||||
|
if (allowedScopeNames == null || name == null) return false
|
||||||
|
return allowedScopeNames.contains(name)
|
||||||
|
}
|
||||||
if (scopeId != moduleId) return false
|
if (scopeId != moduleId) return false
|
||||||
if (allowedScopeNames == null || name == null) return true
|
if (allowedScopeNames == null || name == null) return true
|
||||||
return allowedScopeNames.contains(name)
|
return allowedScopeNames.contains(name)
|
||||||
|
|||||||
@ -75,6 +75,95 @@ sealed class BytecodeConst {
|
|||||||
val visibility: Visibility,
|
val visibility: Visibility,
|
||||||
val isTransient: Boolean,
|
val isTransient: Boolean,
|
||||||
) : BytecodeConst()
|
) : BytecodeConst()
|
||||||
|
data class ClassFieldDecl(
|
||||||
|
val name: String,
|
||||||
|
val isMutable: Boolean,
|
||||||
|
val visibility: Visibility,
|
||||||
|
val writeVisibility: Visibility?,
|
||||||
|
val isTransient: Boolean,
|
||||||
|
) : BytecodeConst()
|
||||||
|
data class ClassDelegatedDecl(
|
||||||
|
val name: String,
|
||||||
|
val isMutable: Boolean,
|
||||||
|
val visibility: Visibility,
|
||||||
|
val writeVisibility: Visibility?,
|
||||||
|
val isTransient: Boolean,
|
||||||
|
) : BytecodeConst()
|
||||||
|
data class ClassInstanceInitDecl(
|
||||||
|
val initStatement: Obj,
|
||||||
|
) : BytecodeConst()
|
||||||
|
data class ClassInstanceFieldDecl(
|
||||||
|
val name: String,
|
||||||
|
val isMutable: Boolean,
|
||||||
|
val visibility: Visibility,
|
||||||
|
val writeVisibility: Visibility?,
|
||||||
|
val isTransient: Boolean,
|
||||||
|
val isAbstract: Boolean,
|
||||||
|
val isClosed: Boolean,
|
||||||
|
val isOverride: Boolean,
|
||||||
|
val fieldId: Int?,
|
||||||
|
val initStatement: Obj?,
|
||||||
|
val pos: Pos,
|
||||||
|
) : BytecodeConst()
|
||||||
|
data class ClassInstancePropertyDecl(
|
||||||
|
val name: String,
|
||||||
|
val isMutable: Boolean,
|
||||||
|
val visibility: Visibility,
|
||||||
|
val writeVisibility: Visibility?,
|
||||||
|
val isTransient: Boolean,
|
||||||
|
val isAbstract: Boolean,
|
||||||
|
val isClosed: Boolean,
|
||||||
|
val isOverride: Boolean,
|
||||||
|
val prop: ObjProperty,
|
||||||
|
val methodId: Int?,
|
||||||
|
val initStatement: Obj?,
|
||||||
|
val pos: Pos,
|
||||||
|
) : BytecodeConst()
|
||||||
|
data class ClassInstanceDelegatedDecl(
|
||||||
|
val name: String,
|
||||||
|
val isMutable: Boolean,
|
||||||
|
val visibility: Visibility,
|
||||||
|
val writeVisibility: Visibility?,
|
||||||
|
val isTransient: Boolean,
|
||||||
|
val isAbstract: Boolean,
|
||||||
|
val isClosed: Boolean,
|
||||||
|
val isOverride: Boolean,
|
||||||
|
val methodId: Int?,
|
||||||
|
val initStatement: Obj?,
|
||||||
|
val pos: Pos,
|
||||||
|
) : BytecodeConst()
|
||||||
|
data class InstanceFieldDecl(
|
||||||
|
val name: String,
|
||||||
|
val isMutable: Boolean,
|
||||||
|
val visibility: Visibility,
|
||||||
|
val writeVisibility: Visibility?,
|
||||||
|
val isTransient: Boolean,
|
||||||
|
val isAbstract: Boolean,
|
||||||
|
val isClosed: Boolean,
|
||||||
|
val isOverride: Boolean,
|
||||||
|
) : BytecodeConst()
|
||||||
|
data class InstancePropertyDecl(
|
||||||
|
val name: String,
|
||||||
|
val isMutable: Boolean,
|
||||||
|
val visibility: Visibility,
|
||||||
|
val writeVisibility: Visibility?,
|
||||||
|
val isTransient: Boolean,
|
||||||
|
val isAbstract: Boolean,
|
||||||
|
val isClosed: Boolean,
|
||||||
|
val isOverride: Boolean,
|
||||||
|
) : BytecodeConst()
|
||||||
|
data class InstanceDelegatedDecl(
|
||||||
|
val storageName: String,
|
||||||
|
val memberName: String,
|
||||||
|
val isMutable: Boolean,
|
||||||
|
val visibility: Visibility,
|
||||||
|
val writeVisibility: Visibility?,
|
||||||
|
val isTransient: Boolean,
|
||||||
|
val isAbstract: Boolean,
|
||||||
|
val isClosed: Boolean,
|
||||||
|
val isOverride: Boolean,
|
||||||
|
val accessTypeLabel: String,
|
||||||
|
) : BytecodeConst()
|
||||||
data class DestructureDecl(
|
data class DestructureDecl(
|
||||||
val pattern: ListLiteralRef,
|
val pattern: ListLiteralRef,
|
||||||
val names: List<String>,
|
val names: List<String>,
|
||||||
|
|||||||
@ -144,6 +144,21 @@ class BytecodeStatement private constructor(
|
|||||||
is net.sergeych.lyng.ClassDeclStatement -> false
|
is net.sergeych.lyng.ClassDeclStatement -> false
|
||||||
is net.sergeych.lyng.FunctionDeclStatement -> false
|
is net.sergeych.lyng.FunctionDeclStatement -> false
|
||||||
is net.sergeych.lyng.EnumDeclStatement -> false
|
is net.sergeych.lyng.EnumDeclStatement -> false
|
||||||
|
is net.sergeych.lyng.ClassStaticFieldInitStatement ->
|
||||||
|
target.initializer?.let { containsUnsupportedStatement(it) } ?: false
|
||||||
|
is net.sergeych.lyng.ClassInstanceInitDeclStatement ->
|
||||||
|
containsUnsupportedStatement(target.initStatement)
|
||||||
|
is net.sergeych.lyng.ClassInstanceFieldDeclStatement ->
|
||||||
|
target.initStatement?.let { containsUnsupportedStatement(it) } ?: false
|
||||||
|
is net.sergeych.lyng.ClassInstancePropertyDeclStatement ->
|
||||||
|
target.initStatement?.let { containsUnsupportedStatement(it) } ?: false
|
||||||
|
is net.sergeych.lyng.ClassInstanceDelegatedDeclStatement ->
|
||||||
|
target.initStatement?.let { containsUnsupportedStatement(it) } ?: false
|
||||||
|
is net.sergeych.lyng.InstanceFieldInitStatement ->
|
||||||
|
target.initializer?.let { containsUnsupportedStatement(it) } ?: false
|
||||||
|
is net.sergeych.lyng.InstancePropertyInitStatement -> false
|
||||||
|
is net.sergeych.lyng.InstanceDelegatedInitStatement ->
|
||||||
|
containsUnsupportedStatement(target.initializer)
|
||||||
is net.sergeych.lyng.TryStatement -> {
|
is net.sergeych.lyng.TryStatement -> {
|
||||||
containsUnsupportedStatement(target.body) ||
|
containsUnsupportedStatement(target.body) ||
|
||||||
target.catches.any { containsUnsupportedStatement(it.block) } ||
|
target.catches.any { containsUnsupportedStatement(it.block) } ||
|
||||||
@ -266,6 +281,115 @@ class BytecodeStatement private constructor(
|
|||||||
stmt.pos
|
stmt.pos
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
is net.sergeych.lyng.ClassStaticFieldInitStatement -> {
|
||||||
|
net.sergeych.lyng.ClassStaticFieldInitStatement(
|
||||||
|
stmt.name,
|
||||||
|
stmt.isMutable,
|
||||||
|
stmt.visibility,
|
||||||
|
stmt.writeVisibility,
|
||||||
|
stmt.initializer?.let { unwrapDeep(it) },
|
||||||
|
stmt.isDelegated,
|
||||||
|
stmt.isTransient,
|
||||||
|
stmt.pos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.ClassInstanceInitDeclStatement -> {
|
||||||
|
net.sergeych.lyng.ClassInstanceInitDeclStatement(
|
||||||
|
unwrapDeep(stmt.initStatement),
|
||||||
|
stmt.pos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.ClassInstanceFieldDeclStatement -> {
|
||||||
|
net.sergeych.lyng.ClassInstanceFieldDeclStatement(
|
||||||
|
stmt.name,
|
||||||
|
stmt.isMutable,
|
||||||
|
stmt.visibility,
|
||||||
|
stmt.writeVisibility,
|
||||||
|
stmt.isAbstract,
|
||||||
|
stmt.isClosed,
|
||||||
|
stmt.isOverride,
|
||||||
|
stmt.isTransient,
|
||||||
|
stmt.fieldId,
|
||||||
|
stmt.initStatement?.let { unwrapDeep(it) },
|
||||||
|
stmt.pos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.ClassInstancePropertyDeclStatement -> {
|
||||||
|
net.sergeych.lyng.ClassInstancePropertyDeclStatement(
|
||||||
|
stmt.name,
|
||||||
|
stmt.isMutable,
|
||||||
|
stmt.visibility,
|
||||||
|
stmt.writeVisibility,
|
||||||
|
stmt.isAbstract,
|
||||||
|
stmt.isClosed,
|
||||||
|
stmt.isOverride,
|
||||||
|
stmt.isTransient,
|
||||||
|
stmt.prop,
|
||||||
|
stmt.methodId,
|
||||||
|
stmt.initStatement?.let { unwrapDeep(it) },
|
||||||
|
stmt.pos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.ClassInstanceDelegatedDeclStatement -> {
|
||||||
|
net.sergeych.lyng.ClassInstanceDelegatedDeclStatement(
|
||||||
|
stmt.name,
|
||||||
|
stmt.isMutable,
|
||||||
|
stmt.visibility,
|
||||||
|
stmt.writeVisibility,
|
||||||
|
stmt.isAbstract,
|
||||||
|
stmt.isClosed,
|
||||||
|
stmt.isOverride,
|
||||||
|
stmt.isTransient,
|
||||||
|
stmt.methodId,
|
||||||
|
stmt.initStatement?.let { unwrapDeep(it) },
|
||||||
|
stmt.pos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.InstanceFieldInitStatement -> {
|
||||||
|
net.sergeych.lyng.InstanceFieldInitStatement(
|
||||||
|
stmt.storageName,
|
||||||
|
stmt.isMutable,
|
||||||
|
stmt.visibility,
|
||||||
|
stmt.writeVisibility,
|
||||||
|
stmt.isAbstract,
|
||||||
|
stmt.isClosed,
|
||||||
|
stmt.isOverride,
|
||||||
|
stmt.isTransient,
|
||||||
|
stmt.isLateInitVal,
|
||||||
|
stmt.initializer?.let { unwrapDeep(it) },
|
||||||
|
stmt.pos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.InstancePropertyInitStatement -> {
|
||||||
|
net.sergeych.lyng.InstancePropertyInitStatement(
|
||||||
|
stmt.storageName,
|
||||||
|
stmt.isMutable,
|
||||||
|
stmt.visibility,
|
||||||
|
stmt.writeVisibility,
|
||||||
|
stmt.isAbstract,
|
||||||
|
stmt.isClosed,
|
||||||
|
stmt.isOverride,
|
||||||
|
stmt.isTransient,
|
||||||
|
stmt.prop,
|
||||||
|
stmt.pos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.InstanceDelegatedInitStatement -> {
|
||||||
|
net.sergeych.lyng.InstanceDelegatedInitStatement(
|
||||||
|
stmt.storageName,
|
||||||
|
stmt.memberName,
|
||||||
|
stmt.isMutable,
|
||||||
|
stmt.visibility,
|
||||||
|
stmt.writeVisibility,
|
||||||
|
stmt.isAbstract,
|
||||||
|
stmt.isClosed,
|
||||||
|
stmt.isOverride,
|
||||||
|
stmt.isTransient,
|
||||||
|
stmt.accessTypeLabel,
|
||||||
|
unwrapDeep(stmt.initializer),
|
||||||
|
stmt.pos
|
||||||
|
)
|
||||||
|
}
|
||||||
else -> stmt
|
else -> stmt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -161,7 +161,10 @@ class CmdBuilder {
|
|||||||
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_ENUM, Opcode.DECL_FUNCTION, Opcode.DECL_CLASS,
|
Opcode.DECL_ENUM, Opcode.DECL_FUNCTION, Opcode.DECL_CLASS, Opcode.DECL_CLASS_FIELD,
|
||||||
|
Opcode.DECL_CLASS_DELEGATED, Opcode.DECL_CLASS_INSTANCE_INIT, Opcode.DECL_CLASS_INSTANCE_FIELD,
|
||||||
|
Opcode.DECL_CLASS_INSTANCE_PROPERTY, Opcode.DECL_CLASS_INSTANCE_DELEGATED, Opcode.DECL_INSTANCE_FIELD,
|
||||||
|
Opcode.DECL_INSTANCE_PROPERTY, Opcode.DECL_INSTANCE_DELEGATED,
|
||||||
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,
|
||||||
@ -418,6 +421,15 @@ class CmdBuilder {
|
|||||||
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])
|
||||||
|
Opcode.DECL_CLASS_FIELD -> CmdDeclClassField(operands[0], operands[1])
|
||||||
|
Opcode.DECL_CLASS_DELEGATED -> CmdDeclClassDelegated(operands[0], operands[1])
|
||||||
|
Opcode.DECL_CLASS_INSTANCE_INIT -> CmdDeclClassInstanceInit(operands[0], operands[1])
|
||||||
|
Opcode.DECL_CLASS_INSTANCE_FIELD -> CmdDeclClassInstanceField(operands[0], operands[1])
|
||||||
|
Opcode.DECL_CLASS_INSTANCE_PROPERTY -> CmdDeclClassInstanceProperty(operands[0], operands[1])
|
||||||
|
Opcode.DECL_CLASS_INSTANCE_DELEGATED -> CmdDeclClassInstanceDelegated(operands[0], operands[1])
|
||||||
|
Opcode.DECL_INSTANCE_FIELD -> CmdDeclInstanceField(operands[0], operands[1])
|
||||||
|
Opcode.DECL_INSTANCE_PROPERTY -> CmdDeclInstanceProperty(operands[0], operands[1])
|
||||||
|
Opcode.DECL_INSTANCE_DELEGATED -> CmdDeclInstanceDelegated(operands[0], operands[1])
|
||||||
Opcode.DECL_EXT_PROPERTY -> CmdDeclExtProperty(operands[0], operands[1])
|
Opcode.DECL_EXT_PROPERTY -> CmdDeclExtProperty(operands[0], operands[1])
|
||||||
Opcode.CALL_DIRECT -> CmdCallDirect(operands[0], operands[1], operands[2], operands[3])
|
Opcode.CALL_DIRECT -> CmdCallDirect(operands[0], operands[1], operands[2], operands[3])
|
||||||
Opcode.ASSIGN_DESTRUCTURE -> CmdAssignDestructure(operands[0], operands[1])
|
Opcode.ASSIGN_DESTRUCTURE -> CmdAssignDestructure(operands[0], operands[1])
|
||||||
|
|||||||
@ -215,6 +215,15 @@ object CmdDisassembler {
|
|||||||
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)
|
||||||
|
is CmdDeclClassField -> Opcode.DECL_CLASS_FIELD to intArrayOf(cmd.constId, cmd.slot)
|
||||||
|
is CmdDeclClassDelegated -> Opcode.DECL_CLASS_DELEGATED to intArrayOf(cmd.constId, cmd.slot)
|
||||||
|
is CmdDeclClassInstanceInit -> Opcode.DECL_CLASS_INSTANCE_INIT to intArrayOf(cmd.constId, cmd.slot)
|
||||||
|
is CmdDeclClassInstanceField -> Opcode.DECL_CLASS_INSTANCE_FIELD to intArrayOf(cmd.constId, cmd.slot)
|
||||||
|
is CmdDeclClassInstanceProperty -> Opcode.DECL_CLASS_INSTANCE_PROPERTY to intArrayOf(cmd.constId, cmd.slot)
|
||||||
|
is CmdDeclClassInstanceDelegated -> Opcode.DECL_CLASS_INSTANCE_DELEGATED to intArrayOf(cmd.constId, cmd.slot)
|
||||||
|
is CmdDeclInstanceField -> Opcode.DECL_INSTANCE_FIELD to intArrayOf(cmd.constId, cmd.slot)
|
||||||
|
is CmdDeclInstanceProperty -> Opcode.DECL_INSTANCE_PROPERTY to intArrayOf(cmd.constId, cmd.slot)
|
||||||
|
is CmdDeclInstanceDelegated -> Opcode.DECL_INSTANCE_DELEGATED to intArrayOf(cmd.constId, cmd.slot)
|
||||||
is CmdDeclExtProperty -> Opcode.DECL_EXT_PROPERTY to intArrayOf(cmd.constId, cmd.slot)
|
is CmdDeclExtProperty -> Opcode.DECL_EXT_PROPERTY to intArrayOf(cmd.constId, cmd.slot)
|
||||||
is CmdCallDirect -> Opcode.CALL_DIRECT to intArrayOf(cmd.id, cmd.argBase, cmd.argCount, cmd.dst)
|
is CmdCallDirect -> Opcode.CALL_DIRECT to intArrayOf(cmd.id, cmd.argBase, cmd.argCount, cmd.dst)
|
||||||
is CmdAssignDestructure -> Opcode.ASSIGN_DESTRUCTURE to intArrayOf(cmd.constId, cmd.slot)
|
is CmdAssignDestructure -> Opcode.ASSIGN_DESTRUCTURE to intArrayOf(cmd.constId, cmd.slot)
|
||||||
@ -287,7 +296,10 @@ object CmdDisassembler {
|
|||||||
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_ENUM, Opcode.DECL_FUNCTION, Opcode.DECL_CLASS,
|
Opcode.DECL_ENUM, Opcode.DECL_FUNCTION, Opcode.DECL_CLASS, Opcode.DECL_CLASS_FIELD,
|
||||||
|
Opcode.DECL_CLASS_DELEGATED, Opcode.DECL_CLASS_INSTANCE_INIT, Opcode.DECL_CLASS_INSTANCE_FIELD,
|
||||||
|
Opcode.DECL_CLASS_INSTANCE_PROPERTY, Opcode.DECL_CLASS_INSTANCE_DELEGATED, Opcode.DECL_INSTANCE_FIELD,
|
||||||
|
Opcode.DECL_INSTANCE_PROPERTY, Opcode.DECL_INSTANCE_DELEGATED,
|
||||||
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,
|
||||||
|
|||||||
@ -72,14 +72,24 @@ class CmdNop : Cmd() {
|
|||||||
|
|
||||||
class CmdMoveObj(internal val src: Int, internal val dst: Int) : Cmd() {
|
class CmdMoveObj(internal val src: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setObj(dst, frame.slotToObj(src))
|
val value = frame.slotToObj(src)
|
||||||
|
if (frame.shouldBypassImmutableWrite(dst)) {
|
||||||
|
frame.setObjUnchecked(dst, value)
|
||||||
|
} else {
|
||||||
|
frame.setObj(dst, value)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdMoveInt(internal val src: Int, internal val dst: Int) : Cmd() {
|
class CmdMoveInt(internal val src: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setInt(dst, frame.getInt(src))
|
val value = frame.getInt(src)
|
||||||
|
if (frame.shouldBypassImmutableWrite(dst)) {
|
||||||
|
frame.setIntUnchecked(dst, value)
|
||||||
|
} else {
|
||||||
|
frame.setInt(dst, value)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,14 +103,24 @@ class CmdMoveIntLocal(internal val src: Int, internal val dst: Int) : Cmd() {
|
|||||||
|
|
||||||
class CmdMoveReal(internal val src: Int, internal val dst: Int) : Cmd() {
|
class CmdMoveReal(internal val src: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setReal(dst, frame.getReal(src))
|
val value = frame.getReal(src)
|
||||||
|
if (frame.shouldBypassImmutableWrite(dst)) {
|
||||||
|
frame.setRealUnchecked(dst, value)
|
||||||
|
} else {
|
||||||
|
frame.setReal(dst, value)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdMoveBool(internal val src: Int, internal val dst: Int) : Cmd() {
|
class CmdMoveBool(internal val src: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setBool(dst, frame.getBool(src))
|
val value = frame.getBool(src)
|
||||||
|
if (frame.shouldBypassImmutableWrite(dst)) {
|
||||||
|
frame.setBoolUnchecked(dst, value)
|
||||||
|
} else {
|
||||||
|
frame.setBool(dst, value)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,7 +356,12 @@ class CmdResolveScopeSlot(internal val scopeSlot: Int, internal val addrSlot: In
|
|||||||
|
|
||||||
class CmdLoadObjAddr(internal val addrSlot: Int, internal val dst: Int) : Cmd() {
|
class CmdLoadObjAddr(internal val addrSlot: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setObj(dst, frame.getAddrObj(addrSlot))
|
val value = frame.getAddrObj(addrSlot)
|
||||||
|
if (frame.shouldBypassImmutableWrite(dst)) {
|
||||||
|
frame.setObjUnchecked(dst, value)
|
||||||
|
} else {
|
||||||
|
frame.setObj(dst, value)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,7 +375,12 @@ class CmdStoreObjAddr(internal val src: Int, internal val addrSlot: Int) : Cmd()
|
|||||||
|
|
||||||
class CmdLoadIntAddr(internal val addrSlot: Int, internal val dst: Int) : Cmd() {
|
class CmdLoadIntAddr(internal val addrSlot: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setInt(dst, frame.getAddrInt(addrSlot))
|
val value = frame.getAddrInt(addrSlot)
|
||||||
|
if (frame.shouldBypassImmutableWrite(dst)) {
|
||||||
|
frame.setIntUnchecked(dst, value)
|
||||||
|
} else {
|
||||||
|
frame.setInt(dst, value)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -364,7 +394,12 @@ class CmdStoreIntAddr(internal val src: Int, internal val addrSlot: Int) : Cmd()
|
|||||||
|
|
||||||
class CmdLoadRealAddr(internal val addrSlot: Int, internal val dst: Int) : Cmd() {
|
class CmdLoadRealAddr(internal val addrSlot: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setReal(dst, frame.getAddrReal(addrSlot))
|
val value = frame.getAddrReal(addrSlot)
|
||||||
|
if (frame.shouldBypassImmutableWrite(dst)) {
|
||||||
|
frame.setRealUnchecked(dst, value)
|
||||||
|
} else {
|
||||||
|
frame.setReal(dst, value)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -378,7 +413,12 @@ class CmdStoreRealAddr(internal val src: Int, internal val addrSlot: Int) : Cmd(
|
|||||||
|
|
||||||
class CmdLoadBoolAddr(internal val addrSlot: Int, internal val dst: Int) : Cmd() {
|
class CmdLoadBoolAddr(internal val addrSlot: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setBool(dst, frame.getAddrBool(addrSlot))
|
val value = frame.getAddrBool(addrSlot)
|
||||||
|
if (frame.shouldBypassImmutableWrite(dst)) {
|
||||||
|
frame.setBoolUnchecked(dst, value)
|
||||||
|
} else {
|
||||||
|
frame.setBool(dst, value)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1302,17 +1342,23 @@ class CmdClearPendingThrowable : Cmd() {
|
|||||||
|
|
||||||
class CmdDeclLocal(internal val constId: Int, internal val slot: Int) : Cmd() {
|
class CmdDeclLocal(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.LocalDecl
|
if (slot < frame.fn.scopeSlotCount) {
|
||||||
?: error("DECL_LOCAL expects LocalDecl at $constId")
|
val decl = frame.fn.constants[constId] as? BytecodeConst.LocalDecl
|
||||||
val value = frame.slotToObj(slot).byValueCopy()
|
?: error("DECL_LOCAL expects LocalDecl at $constId")
|
||||||
frame.ensureScope().addItem(
|
val target = frame.scopeTarget(slot)
|
||||||
decl.name,
|
frame.ensureScopeSlot(target, slot)
|
||||||
decl.isMutable,
|
val value = frame.slotToObj(slot).byValueCopy()
|
||||||
value,
|
target.updateSlotFor(
|
||||||
decl.visibility,
|
decl.name,
|
||||||
recordType = ObjRecord.Type.Other,
|
ObjRecord(
|
||||||
isTransient = decl.isTransient
|
value,
|
||||||
)
|
decl.isMutable,
|
||||||
|
decl.visibility,
|
||||||
|
isTransient = decl.isTransient,
|
||||||
|
type = ObjRecord.Type.Other
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1332,16 +1378,21 @@ class CmdDeclDelegated(internal val constId: Int, internal val slot: Int) : Cmd(
|
|||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
initValue
|
initValue
|
||||||
}
|
}
|
||||||
val rec = frame.ensureScope().addItem(
|
if (slot < frame.fn.scopeSlotCount) {
|
||||||
decl.name,
|
val target = frame.scopeTarget(slot)
|
||||||
decl.isMutable,
|
frame.ensureScopeSlot(target, slot)
|
||||||
ObjNull,
|
target.updateSlotFor(
|
||||||
decl.visibility,
|
decl.name,
|
||||||
recordType = ObjRecord.Type.Delegated,
|
ObjRecord(
|
||||||
isTransient = decl.isTransient
|
ObjNull,
|
||||||
)
|
decl.isMutable,
|
||||||
rec.delegate = finalDelegate
|
decl.visibility,
|
||||||
frame.storeObjResult(slot, finalDelegate)
|
isTransient = decl.isTransient,
|
||||||
|
type = ObjRecord.Type.Delegated
|
||||||
|
).also { it.delegate = finalDelegate }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
frame.setObjUnchecked(slot, finalDelegate)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1361,7 +1412,7 @@ class CmdDeclEnum(internal val constId: Int, internal val slot: Int) : Cmd() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
frame.storeObjResult(slot, enumClass)
|
frame.setObjUnchecked(slot, enumClass)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1370,18 +1421,387 @@ class CmdDeclFunction(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.FunctionDecl
|
val decl = frame.fn.constants[constId] as? BytecodeConst.FunctionDecl
|
||||||
?: error("DECL_FUNCTION expects FunctionDecl at $constId")
|
?: error("DECL_FUNCTION expects FunctionDecl at $constId")
|
||||||
val result = executeFunctionDecl(frame.ensureScope(), decl.spec)
|
val captureNames = captureNamesForFunctionDecl(decl.spec)
|
||||||
frame.storeObjResult(slot, result)
|
val captureRecords = buildFunctionCaptureRecords(frame, captureNames)
|
||||||
|
val result = executeFunctionDecl(frame.ensureScope(), decl.spec, captureRecords)
|
||||||
|
frame.setObjUnchecked(slot, result)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun captureNamesForFunctionDecl(spec: net.sergeych.lyng.FunctionDeclSpec): List<String> {
|
||||||
|
if (spec.captureSlots.isNotEmpty()) {
|
||||||
|
return spec.captureSlots.map { it.name }
|
||||||
|
}
|
||||||
|
return captureNamesForStatement(spec.fnBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun captureNamesForStatement(stmt: Statement?): List<String> {
|
||||||
|
if (stmt == null) return emptyList()
|
||||||
|
val bytecode = when (stmt) {
|
||||||
|
is BytecodeStatement -> stmt.bytecodeFunction()
|
||||||
|
is BytecodeBodyProvider -> stmt.bytecodeBody()?.bytecodeFunction()
|
||||||
|
else -> null
|
||||||
|
} ?: return emptyList()
|
||||||
|
val names = bytecode.localSlotNames
|
||||||
|
val captures = bytecode.localSlotCaptures
|
||||||
|
val ordered = LinkedHashSet<String>()
|
||||||
|
for (i in names.indices) {
|
||||||
|
if (captures.getOrNull(i) != true) continue
|
||||||
|
val name = names[i] ?: continue
|
||||||
|
ordered.add(name)
|
||||||
|
}
|
||||||
|
return ordered.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildFunctionCaptureRecords(frame: CmdFrame, captureNames: List<String>): List<ObjRecord>? {
|
||||||
|
if (captureNames.isEmpty()) return null
|
||||||
|
val records = ArrayList<ObjRecord>(captureNames.size)
|
||||||
|
for (name in captureNames) {
|
||||||
|
val localIndex = resolveLocalSlotIndex(frame.fn, name, preferCapture = true)
|
||||||
|
if (localIndex != null) {
|
||||||
|
val isMutable = frame.fn.localSlotMutables.getOrNull(localIndex) ?: false
|
||||||
|
val isDelegated = frame.fn.localSlotDelegated.getOrNull(localIndex) ?: false
|
||||||
|
if (isDelegated) {
|
||||||
|
val delegate = frame.frame.getObj(localIndex)
|
||||||
|
records += ObjRecord(ObjNull, isMutable, type = ObjRecord.Type.Delegated).also {
|
||||||
|
it.delegate = delegate
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
records += ObjRecord(FrameSlotRef(frame.frame, localIndex), isMutable)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val scopeSlot = frame.fn.scopeSlotNames.indexOfFirst { it == name }
|
||||||
|
if (scopeSlot >= 0) {
|
||||||
|
val target = frame.scopeTarget(scopeSlot)
|
||||||
|
val index = frame.fn.scopeSlotIndices[scopeSlot]
|
||||||
|
records += target.getSlotRecord(index)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val scopeCaptures = frame.scope.captureRecords
|
||||||
|
val scopeCaptureNames = frame.scope.captureNames
|
||||||
|
if (scopeCaptures != null && scopeCaptureNames != null) {
|
||||||
|
val idx = scopeCaptureNames.indexOf(name)
|
||||||
|
if (idx >= 0) {
|
||||||
|
val rec = scopeCaptures.getOrNull(idx)
|
||||||
|
if (rec != null) {
|
||||||
|
records += rec
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame.ensureScope().raiseSymbolNotFound("capture $name not found")
|
||||||
|
}
|
||||||
|
return records
|
||||||
|
}
|
||||||
|
|
||||||
class CmdDeclClass(internal val constId: Int, internal val slot: Int) : Cmd() {
|
class CmdDeclClass(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.ClassDecl
|
val decl = frame.fn.constants[constId] as? BytecodeConst.ClassDecl
|
||||||
?: error("DECL_CLASS expects ClassDecl at $constId")
|
?: error("DECL_CLASS expects ClassDecl at $constId")
|
||||||
val result = executeClassDecl(frame.ensureScope(), decl.spec)
|
val bodyCaptureNames = mergeCaptureNames(
|
||||||
frame.storeObjResult(slot, result)
|
captureNamesForStatement(decl.spec.bodyInit),
|
||||||
|
captureNamesFromFrame(frame)
|
||||||
|
)
|
||||||
|
val bodyCaptureRecords = buildFunctionCaptureRecords(frame, bodyCaptureNames)
|
||||||
|
val result = executeClassDecl(frame.ensureScope(), decl.spec, bodyCaptureRecords, bodyCaptureNames)
|
||||||
|
frame.setObjUnchecked(slot, result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mergeCaptureNames(primary: List<String>, fallback: List<String>): List<String> {
|
||||||
|
if (fallback.isEmpty()) return primary
|
||||||
|
if (primary.isEmpty()) return fallback
|
||||||
|
val ordered = LinkedHashSet<String>(primary.size + fallback.size)
|
||||||
|
ordered.addAll(primary)
|
||||||
|
ordered.addAll(fallback)
|
||||||
|
return ordered.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun captureNamesFromFrame(frame: CmdFrame): List<String> {
|
||||||
|
val ordered = LinkedHashSet<String>()
|
||||||
|
for (name in frame.fn.localSlotNames) {
|
||||||
|
if (name != null) ordered.add(name)
|
||||||
|
}
|
||||||
|
return ordered.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
class CmdDeclClassField(internal val constId: Int, internal val slot: Int) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
val decl = frame.fn.constants[constId] as? BytecodeConst.ClassFieldDecl
|
||||||
|
?: error("DECL_CLASS_FIELD expects ClassFieldDecl at $constId")
|
||||||
|
val scope = frame.ensureScope()
|
||||||
|
val cls = scope.thisObj as? ObjClass
|
||||||
|
?: scope.raiseIllegalState("class field init requires class scope")
|
||||||
|
val value = frame.slotToObj(slot).byValueCopy()
|
||||||
|
cls.createClassField(
|
||||||
|
decl.name,
|
||||||
|
value,
|
||||||
|
decl.isMutable,
|
||||||
|
decl.visibility,
|
||||||
|
decl.writeVisibility,
|
||||||
|
Pos.builtIn,
|
||||||
|
isTransient = decl.isTransient
|
||||||
|
)
|
||||||
|
scope.addItem(
|
||||||
|
decl.name,
|
||||||
|
decl.isMutable,
|
||||||
|
value,
|
||||||
|
decl.visibility,
|
||||||
|
decl.writeVisibility,
|
||||||
|
recordType = ObjRecord.Type.Field,
|
||||||
|
isTransient = decl.isTransient
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CmdDeclClassDelegated(internal val constId: Int, internal val slot: Int) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
val decl = frame.fn.constants[constId] as? BytecodeConst.ClassDelegatedDecl
|
||||||
|
?: error("DECL_CLASS_DELEGATED expects ClassDelegatedDecl at $constId")
|
||||||
|
val scope = frame.ensureScope()
|
||||||
|
val cls = scope.thisObj as? ObjClass
|
||||||
|
?: scope.raiseIllegalState("class delegated init requires class scope")
|
||||||
|
val initValue = frame.slotToObj(slot)
|
||||||
|
val accessTypeStr = if (decl.isMutable) "Var" else "Val"
|
||||||
|
val accessType = ObjString(accessTypeStr)
|
||||||
|
val finalDelegate = try {
|
||||||
|
initValue.invokeInstanceMethod(
|
||||||
|
scope,
|
||||||
|
"bind",
|
||||||
|
Arguments(ObjString(decl.name), accessType, scope.thisObj)
|
||||||
|
)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
initValue
|
||||||
|
}
|
||||||
|
cls.createClassField(
|
||||||
|
decl.name,
|
||||||
|
ObjUnset,
|
||||||
|
decl.isMutable,
|
||||||
|
decl.visibility,
|
||||||
|
decl.writeVisibility,
|
||||||
|
Pos.builtIn,
|
||||||
|
isTransient = decl.isTransient,
|
||||||
|
type = ObjRecord.Type.Delegated
|
||||||
|
).apply {
|
||||||
|
delegate = finalDelegate
|
||||||
|
}
|
||||||
|
scope.addItem(
|
||||||
|
decl.name,
|
||||||
|
decl.isMutable,
|
||||||
|
ObjUnset,
|
||||||
|
decl.visibility,
|
||||||
|
decl.writeVisibility,
|
||||||
|
recordType = ObjRecord.Type.Delegated,
|
||||||
|
isTransient = decl.isTransient
|
||||||
|
).apply {
|
||||||
|
delegate = finalDelegate
|
||||||
|
}
|
||||||
|
frame.storeObjResult(slot, finalDelegate)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CmdDeclClassInstanceInit(internal val constId: Int, internal val slot: Int) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
val decl = frame.fn.constants[constId] as? BytecodeConst.ClassInstanceInitDecl
|
||||||
|
?: error("DECL_CLASS_INSTANCE_INIT expects ClassInstanceInitDecl at $constId")
|
||||||
|
val scope = frame.ensureScope()
|
||||||
|
val cls = scope.thisObj as? ObjClass
|
||||||
|
?: scope.raiseIllegalState("class instance init requires class scope")
|
||||||
|
cls.instanceInitializers += decl.initStatement
|
||||||
|
frame.storeObjResult(slot, ObjVoid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CmdDeclClassInstanceField(internal val constId: Int, internal val slot: Int) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
val decl = frame.fn.constants[constId] as? BytecodeConst.ClassInstanceFieldDecl
|
||||||
|
?: error("DECL_CLASS_INSTANCE_FIELD expects ClassInstanceFieldDecl at $constId")
|
||||||
|
val scope = frame.ensureScope()
|
||||||
|
val cls = scope.thisObj as? ObjClass
|
||||||
|
?: scope.raiseIllegalState("class instance field requires class scope")
|
||||||
|
cls.createField(
|
||||||
|
decl.name,
|
||||||
|
ObjNull,
|
||||||
|
isMutable = decl.isMutable,
|
||||||
|
visibility = decl.visibility,
|
||||||
|
writeVisibility = decl.writeVisibility,
|
||||||
|
pos = decl.pos,
|
||||||
|
declaringClass = cls,
|
||||||
|
isAbstract = decl.isAbstract,
|
||||||
|
isClosed = decl.isClosed,
|
||||||
|
isOverride = decl.isOverride,
|
||||||
|
isTransient = decl.isTransient,
|
||||||
|
type = ObjRecord.Type.Field,
|
||||||
|
fieldId = decl.fieldId
|
||||||
|
)
|
||||||
|
if (!decl.isAbstract) {
|
||||||
|
decl.initStatement?.let { cls.instanceInitializers += it }
|
||||||
|
}
|
||||||
|
frame.storeObjResult(slot, ObjVoid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CmdDeclClassInstanceProperty(internal val constId: Int, internal val slot: Int) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
val decl = frame.fn.constants[constId] as? BytecodeConst.ClassInstancePropertyDecl
|
||||||
|
?: error("DECL_CLASS_INSTANCE_PROPERTY expects ClassInstancePropertyDecl at $constId")
|
||||||
|
val scope = frame.ensureScope()
|
||||||
|
val cls = scope.thisObj as? ObjClass
|
||||||
|
?: scope.raiseIllegalState("class instance property requires class scope")
|
||||||
|
cls.addProperty(
|
||||||
|
name = decl.name,
|
||||||
|
visibility = decl.visibility,
|
||||||
|
writeVisibility = decl.writeVisibility,
|
||||||
|
declaringClass = cls,
|
||||||
|
isAbstract = decl.isAbstract,
|
||||||
|
isClosed = decl.isClosed,
|
||||||
|
isOverride = decl.isOverride,
|
||||||
|
pos = decl.pos,
|
||||||
|
prop = decl.prop,
|
||||||
|
methodId = decl.methodId
|
||||||
|
)
|
||||||
|
if (!decl.isAbstract) {
|
||||||
|
decl.initStatement?.let { cls.instanceInitializers += it }
|
||||||
|
}
|
||||||
|
frame.storeObjResult(slot, ObjVoid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CmdDeclClassInstanceDelegated(internal val constId: Int, internal val slot: Int) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
val decl = frame.fn.constants[constId] as? BytecodeConst.ClassInstanceDelegatedDecl
|
||||||
|
?: error("DECL_CLASS_INSTANCE_DELEGATED expects ClassInstanceDelegatedDecl at $constId")
|
||||||
|
val scope = frame.ensureScope()
|
||||||
|
val cls = scope.thisObj as? ObjClass
|
||||||
|
?: scope.raiseIllegalState("class instance delegated requires class scope")
|
||||||
|
cls.createField(
|
||||||
|
decl.name,
|
||||||
|
ObjUnset,
|
||||||
|
isMutable = decl.isMutable,
|
||||||
|
visibility = decl.visibility,
|
||||||
|
writeVisibility = decl.writeVisibility,
|
||||||
|
pos = decl.pos,
|
||||||
|
declaringClass = cls,
|
||||||
|
isAbstract = decl.isAbstract,
|
||||||
|
isClosed = decl.isClosed,
|
||||||
|
isOverride = decl.isOverride,
|
||||||
|
isTransient = decl.isTransient,
|
||||||
|
type = ObjRecord.Type.Delegated,
|
||||||
|
methodId = decl.methodId
|
||||||
|
)
|
||||||
|
if (!decl.isAbstract) {
|
||||||
|
decl.initStatement?.let { cls.instanceInitializers += it }
|
||||||
|
}
|
||||||
|
frame.storeObjResult(slot, ObjVoid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CmdDeclInstanceField(internal val constId: Int, internal val slot: Int) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
val decl = frame.fn.constants[constId] as? BytecodeConst.InstanceFieldDecl
|
||||||
|
?: error("DECL_INSTANCE_FIELD expects InstanceFieldDecl at $constId")
|
||||||
|
val scope = frame.ensureScope()
|
||||||
|
val value = frame.slotToObj(slot).byValueCopy()
|
||||||
|
scope.addItem(
|
||||||
|
decl.name,
|
||||||
|
decl.isMutable,
|
||||||
|
value,
|
||||||
|
decl.visibility,
|
||||||
|
decl.writeVisibility,
|
||||||
|
recordType = ObjRecord.Type.Field,
|
||||||
|
isAbstract = decl.isAbstract,
|
||||||
|
isClosed = decl.isClosed,
|
||||||
|
isOverride = decl.isOverride,
|
||||||
|
isTransient = decl.isTransient
|
||||||
|
)
|
||||||
|
if (slot >= frame.fn.scopeSlotCount) {
|
||||||
|
frame.storeObjResult(slot, ObjVoid)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CmdDeclInstanceProperty(internal val constId: Int, internal val slot: Int) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
val decl = frame.fn.constants[constId] as? BytecodeConst.InstancePropertyDecl
|
||||||
|
?: error("DECL_INSTANCE_PROPERTY expects InstancePropertyDecl at $constId")
|
||||||
|
val scope = frame.ensureScope()
|
||||||
|
val prop = frame.slotToObj(slot)
|
||||||
|
scope.addItem(
|
||||||
|
decl.name,
|
||||||
|
decl.isMutable,
|
||||||
|
prop,
|
||||||
|
decl.visibility,
|
||||||
|
decl.writeVisibility,
|
||||||
|
recordType = ObjRecord.Type.Property,
|
||||||
|
isAbstract = decl.isAbstract,
|
||||||
|
isClosed = decl.isClosed,
|
||||||
|
isOverride = decl.isOverride,
|
||||||
|
isTransient = decl.isTransient
|
||||||
|
)
|
||||||
|
if (slot >= frame.fn.scopeSlotCount) {
|
||||||
|
frame.storeObjResult(slot, ObjVoid)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CmdDeclInstanceDelegated(internal val constId: Int, internal val slot: Int) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
val decl = frame.fn.constants[constId] as? BytecodeConst.InstanceDelegatedDecl
|
||||||
|
?: error("DECL_INSTANCE_DELEGATED expects InstanceDelegatedDecl at $constId")
|
||||||
|
val scope = frame.ensureScope()
|
||||||
|
var initValue = frame.slotToObj(slot)
|
||||||
|
val debugName = if (slot < frame.fn.scopeSlotCount) {
|
||||||
|
frame.fn.scopeSlotNames.getOrNull(slot)
|
||||||
|
} else {
|
||||||
|
val localIndex = slot - frame.fn.scopeSlotCount
|
||||||
|
frame.fn.localSlotNames.getOrNull(localIndex)
|
||||||
|
}
|
||||||
|
if (initValue === ObjUnset) {
|
||||||
|
if (debugName != null) {
|
||||||
|
val resolved = scope.get(debugName)
|
||||||
|
if (resolved != null && resolved.value !== ObjUnset) {
|
||||||
|
initValue = resolved.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val accessType = ObjString(decl.accessTypeLabel)
|
||||||
|
val finalDelegate = try {
|
||||||
|
initValue.invokeInstanceMethod(
|
||||||
|
scope,
|
||||||
|
"bind",
|
||||||
|
Arguments(ObjString(decl.memberName), accessType, scope.thisObj)
|
||||||
|
)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
initValue
|
||||||
|
}
|
||||||
|
scope.addItem(
|
||||||
|
decl.storageName,
|
||||||
|
decl.isMutable,
|
||||||
|
ObjUnset,
|
||||||
|
decl.visibility,
|
||||||
|
decl.writeVisibility,
|
||||||
|
recordType = ObjRecord.Type.Delegated,
|
||||||
|
isAbstract = decl.isAbstract,
|
||||||
|
isClosed = decl.isClosed,
|
||||||
|
isOverride = decl.isOverride,
|
||||||
|
isTransient = decl.isTransient
|
||||||
|
).apply {
|
||||||
|
delegate = finalDelegate
|
||||||
|
}
|
||||||
|
if (slot >= frame.fn.scopeSlotCount) {
|
||||||
|
frame.storeObjResult(slot, ObjVoid)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1504,33 +1924,6 @@ private fun resolveLocalSlotIndex(fn: CmdFunction, name: String, preferCapture:
|
|||||||
return null
|
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
|
||||||
@ -1578,7 +1971,7 @@ class CmdCallDirect(
|
|||||||
val args = frame.buildArguments(argBase, argCount)
|
val args = frame.buildArguments(argBase, argCount)
|
||||||
if (callee is Statement) {
|
if (callee is Statement) {
|
||||||
val bytecodeBody = (callee as? BytecodeBodyProvider)?.bytecodeBody()
|
val bytecodeBody = (callee as? BytecodeBodyProvider)?.bytecodeBody()
|
||||||
if (callee !is BytecodeStatement && callee !is BytecodeCallable && bytecodeBody == null && isAstStatement(callee)) {
|
if (callee !is BytecodeStatement && callee !is BytecodeCallable && bytecodeBody == null) {
|
||||||
frame.ensureScope().raiseIllegalState("bytecode runtime cannot call non-bytecode Statement")
|
frame.ensureScope().raiseIllegalState("bytecode runtime cannot call non-bytecode Statement")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1619,7 +2012,7 @@ class CmdCallSlot(
|
|||||||
val scope = frame.ensureScope()
|
val scope = frame.ensureScope()
|
||||||
if (callee is Statement) {
|
if (callee is Statement) {
|
||||||
val bytecodeBody = (callee as? BytecodeBodyProvider)?.bytecodeBody()
|
val bytecodeBody = (callee as? BytecodeBodyProvider)?.bytecodeBody()
|
||||||
if (callee !is BytecodeStatement && callee !is BytecodeCallable && bytecodeBody == null && isAstStatement(callee)) {
|
if (callee !is BytecodeStatement && callee !is BytecodeCallable && bytecodeBody == null) {
|
||||||
scope.raiseIllegalState("bytecode runtime cannot call non-bytecode Statement")
|
scope.raiseIllegalState("bytecode runtime cannot call non-bytecode Statement")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1897,18 +2290,26 @@ class CmdCallMemberSlot(
|
|||||||
frame.storeObjResult(dst, result)
|
frame.storeObjResult(dst, result)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
val scope = frame.ensureScope()
|
||||||
val decl = rec.declaringClass ?: receiver.objClass
|
val decl = rec.declaringClass ?: receiver.objClass
|
||||||
|
if (!canAccessMember(rec.visibility, decl, scope.currentClassCtx, name)) {
|
||||||
|
scope.raiseError(
|
||||||
|
ObjIllegalAccessException(
|
||||||
|
scope,
|
||||||
|
"can't invoke ${name}: not visible (declared in ${decl.className}, caller ${scope.currentClassCtx?.className ?: "?"})"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
val result = when (rec.type) {
|
val result = when (rec.type) {
|
||||||
ObjRecord.Type.Property -> {
|
ObjRecord.Type.Property -> {
|
||||||
if (callArgs.isEmpty()) (rec.value as ObjProperty).callGetter(frame.ensureScope(), receiver, decl)
|
if (callArgs.isEmpty()) (rec.value as ObjProperty).callGetter(scope, receiver, decl)
|
||||||
else frame.ensureScope().raiseError("property $name cannot be called with arguments")
|
else scope.raiseError("property $name cannot be called with arguments")
|
||||||
}
|
}
|
||||||
ObjRecord.Type.Fun -> {
|
ObjRecord.Type.Fun -> {
|
||||||
val callScope = inst?.instanceScope ?: frame.ensureScope()
|
val callScope = inst?.instanceScope ?: scope
|
||||||
rec.value.invoke(callScope, receiver, callArgs, decl)
|
rec.value.invoke(callScope, receiver, callArgs, decl)
|
||||||
}
|
}
|
||||||
ObjRecord.Type.Delegated -> {
|
ObjRecord.Type.Delegated -> {
|
||||||
val scope = frame.ensureScope()
|
|
||||||
val delegate = when (receiver) {
|
val delegate = when (receiver) {
|
||||||
is ObjInstance -> {
|
is ObjInstance -> {
|
||||||
val storageName = decl.mangledName(name)
|
val storageName = decl.mangledName(name)
|
||||||
@ -2002,7 +2403,6 @@ class BytecodeLambdaCallable(
|
|||||||
val context = scope.applyClosureForBytecode(closureScope, preferredThisType).also {
|
val context = scope.applyClosureForBytecode(closureScope, preferredThisType).also {
|
||||||
it.args = scope.args
|
it.args = scope.args
|
||||||
}
|
}
|
||||||
if (paramSlotPlan.isNotEmpty()) context.applySlotPlan(paramSlotPlan)
|
|
||||||
if (captureRecords != null) {
|
if (captureRecords != null) {
|
||||||
context.captureRecords = captureRecords
|
context.captureRecords = captureRecords
|
||||||
context.captureNames = captureNames
|
context.captureNames = captureNames
|
||||||
@ -2027,19 +2427,13 @@ class BytecodeLambdaCallable(
|
|||||||
val itSlot = slotPlan["it"]
|
val itSlot = slotPlan["it"]
|
||||||
if (itSlot != null) {
|
if (itSlot != null) {
|
||||||
frame.frame.setObj(itSlot, itValue)
|
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 {
|
} else {
|
||||||
argsDeclaration.assignToFrame(
|
argsDeclaration.assignToFrame(
|
||||||
context,
|
context,
|
||||||
arguments,
|
arguments,
|
||||||
slotPlan,
|
slotPlan,
|
||||||
frame.frame,
|
frame.frame
|
||||||
defaultAccessType = AccessType.Val
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2235,9 +2629,7 @@ class CmdFrame(
|
|||||||
if (!visited.add(current)) continue
|
if (!visited.add(current)) continue
|
||||||
if (current.getSlotIndexOf(slotName) != null) return current
|
if (current.getSlotIndexOf(slotName) != null) return current
|
||||||
current.parent?.let { queue.add(it) }
|
current.parent?.let { queue.add(it) }
|
||||||
if (current is ClosureScope) {
|
if (current is BytecodeClosureScope) {
|
||||||
queue.add(current.closureScope)
|
|
||||||
} else if (current is BytecodeClosureScope) {
|
|
||||||
queue.add(current.closureScope)
|
queue.add(current.closureScope)
|
||||||
} else if (current is ApplyScope) {
|
} else if (current is ApplyScope) {
|
||||||
queue.add(current.applied)
|
queue.add(current.applied)
|
||||||
@ -2255,9 +2647,7 @@ class CmdFrame(
|
|||||||
if (!visited.add(current)) continue
|
if (!visited.add(current)) continue
|
||||||
if (current.getLocalRecordDirect(name) != null) return current
|
if (current.getLocalRecordDirect(name) != null) return current
|
||||||
current.parent?.let { queue.add(it) }
|
current.parent?.let { queue.add(it) }
|
||||||
if (current is ClosureScope) {
|
if (current is BytecodeClosureScope) {
|
||||||
queue.add(current.closureScope)
|
|
||||||
} else if (current is BytecodeClosureScope) {
|
|
||||||
queue.add(current.closureScope)
|
queue.add(current.closureScope)
|
||||||
} else if (current is ApplyScope) {
|
} else if (current is ApplyScope) {
|
||||||
queue.add(current.applied)
|
queue.add(current.applied)
|
||||||
@ -2276,9 +2666,7 @@ class CmdFrame(
|
|||||||
if (current is ModuleScope) return current
|
if (current is ModuleScope) return current
|
||||||
if (current.parent 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 BytecodeClosureScope) {
|
||||||
queue.add(current.closureScope)
|
|
||||||
} else if (current is BytecodeClosureScope) {
|
|
||||||
queue.add(current.closureScope)
|
queue.add(current.closureScope)
|
||||||
} else if (current is ApplyScope) {
|
} else if (current is ApplyScope) {
|
||||||
queue.add(current.applied)
|
queue.add(current.applied)
|
||||||
@ -2360,7 +2748,7 @@ class CmdFrame(
|
|||||||
|
|
||||||
fun pushScope(plan: Map<String, Int>, captures: List<String>) {
|
fun pushScope(plan: Map<String, Int>, captures: List<String>) {
|
||||||
if (scope.skipScopeCreation) {
|
if (scope.skipScopeCreation) {
|
||||||
val snapshot = scope.applySlotPlanWithSnapshot(plan)
|
val snapshot = emptyMap<String, Int?>()
|
||||||
slotPlanStack.addLast(snapshot)
|
slotPlanStack.addLast(snapshot)
|
||||||
virtualDepth += 1
|
virtualDepth += 1
|
||||||
scopeStack.addLast(scope)
|
scopeStack.addLast(scope)
|
||||||
@ -2369,9 +2757,6 @@ class CmdFrame(
|
|||||||
scopeStack.addLast(scope)
|
scopeStack.addLast(scope)
|
||||||
scopeVirtualStack.addLast(false)
|
scopeVirtualStack.addLast(false)
|
||||||
scope = scope.createChildScope()
|
scope = scope.createChildScope()
|
||||||
if (plan.isNotEmpty()) {
|
|
||||||
scope.applySlotPlan(plan)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
captureStack.addLast(captures)
|
captureStack.addLast(captures)
|
||||||
scopeDepth += 1
|
scopeDepth += 1
|
||||||
@ -2417,11 +2802,8 @@ class CmdFrame(
|
|||||||
scopeStack.addLast(scope)
|
scopeStack.addLast(scope)
|
||||||
slotPlanScopeStack.addLast(true)
|
slotPlanScopeStack.addLast(true)
|
||||||
scope = scope.createChildScope()
|
scope = scope.createChildScope()
|
||||||
if (plan.isNotEmpty()) {
|
|
||||||
scope.applySlotPlan(plan)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
val snapshot = scope.applySlotPlanWithSnapshot(plan)
|
val snapshot = emptyMap<String, Int?>()
|
||||||
slotPlanStack.addLast(snapshot)
|
slotPlanStack.addLast(snapshot)
|
||||||
slotPlanScopeStack.addLast(false)
|
slotPlanScopeStack.addLast(false)
|
||||||
virtualDepth += 1
|
virtualDepth += 1
|
||||||
@ -2456,9 +2838,15 @@ class CmdFrame(
|
|||||||
if (slot < fn.scopeSlotCount) {
|
if (slot < fn.scopeSlotCount) {
|
||||||
val target = scopeTarget(slot)
|
val target = scopeTarget(slot)
|
||||||
val index = ensureScopeSlot(target, slot)
|
val index = ensureScopeSlot(target, slot)
|
||||||
|
val record = target.getSlotRecord(index)
|
||||||
|
if (!record.isMutable) {
|
||||||
|
val name = fn.scopeSlotNames[slot] ?: "slot#$index"
|
||||||
|
ensureScope().raiseError("can't assign to read-only variable: $name")
|
||||||
|
}
|
||||||
target.setSlotValue(index, value)
|
target.setSlotValue(index, value)
|
||||||
} else {
|
} else {
|
||||||
val localIndex = slot - fn.scopeSlotCount
|
val localIndex = slot - fn.scopeSlotCount
|
||||||
|
ensureLocalMutable(localIndex)
|
||||||
when (val existing = frame.getRawObj(localIndex)) {
|
when (val existing = frame.getRawObj(localIndex)) {
|
||||||
is FrameSlotRef -> {
|
is FrameSlotRef -> {
|
||||||
existing.write(value)
|
existing.write(value)
|
||||||
@ -2474,6 +2862,15 @@ class CmdFrame(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun shouldBypassImmutableWrite(slot: Int): Boolean {
|
||||||
|
val next = fn.cmds.getOrNull(ip) ?: return false
|
||||||
|
return when (next) {
|
||||||
|
is CmdDeclLocal -> next.slot == slot
|
||||||
|
is CmdDeclDelegated -> next.slot == slot
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getInt(slot: Int): Long {
|
suspend fun getInt(slot: Int): Long {
|
||||||
return if (slot < fn.scopeSlotCount) {
|
return if (slot < fn.scopeSlotCount) {
|
||||||
getScopeSlotValue(slot).toLong()
|
getScopeSlotValue(slot).toLong()
|
||||||
@ -2498,7 +2895,7 @@ class CmdFrame(
|
|||||||
|
|
||||||
fun getLocalInt(local: Int): Long = frame.getInt(local)
|
fun getLocalInt(local: Int): Long = frame.getInt(local)
|
||||||
|
|
||||||
fun setInt(slot: Int, value: Long) {
|
fun setIntUnchecked(slot: Int, value: Long) {
|
||||||
if (slot < fn.scopeSlotCount) {
|
if (slot < fn.scopeSlotCount) {
|
||||||
val target = scopeTarget(slot)
|
val target = scopeTarget(slot)
|
||||||
val index = ensureScopeSlot(target, slot)
|
val index = ensureScopeSlot(target, slot)
|
||||||
@ -2520,6 +2917,34 @@ class CmdFrame(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setInt(slot: Int, value: Long) {
|
||||||
|
if (slot < fn.scopeSlotCount) {
|
||||||
|
val target = scopeTarget(slot)
|
||||||
|
val index = ensureScopeSlot(target, slot)
|
||||||
|
val record = target.getSlotRecord(index)
|
||||||
|
if (!record.isMutable) {
|
||||||
|
val name = fn.scopeSlotNames[slot] ?: "slot#$index"
|
||||||
|
ensureScope().raiseError("can't assign to read-only variable: $name")
|
||||||
|
}
|
||||||
|
target.setSlotValue(index, ObjInt.of(value))
|
||||||
|
} else {
|
||||||
|
val localIndex = slot - fn.scopeSlotCount
|
||||||
|
ensureLocalMutable(localIndex)
|
||||||
|
when (val existing = frame.getRawObj(localIndex)) {
|
||||||
|
is FrameSlotRef -> {
|
||||||
|
existing.write(ObjInt.of(value))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
is RecordSlotRef -> {
|
||||||
|
existing.write(ObjInt.of(value))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
frame.setInt(localIndex, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun setLocalInt(local: Int, value: Long) {
|
fun setLocalInt(local: Int, value: Long) {
|
||||||
frame.setInt(local, value)
|
frame.setInt(local, value)
|
||||||
}
|
}
|
||||||
@ -2547,6 +2972,34 @@ class CmdFrame(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setReal(slot: Int, value: Double) {
|
fun setReal(slot: Int, value: Double) {
|
||||||
|
if (slot < fn.scopeSlotCount) {
|
||||||
|
val target = scopeTarget(slot)
|
||||||
|
val index = ensureScopeSlot(target, slot)
|
||||||
|
val record = target.getSlotRecord(index)
|
||||||
|
if (!record.isMutable) {
|
||||||
|
val name = fn.scopeSlotNames[slot] ?: "slot#$index"
|
||||||
|
ensureScope().raiseError("can't assign to read-only variable: $name")
|
||||||
|
}
|
||||||
|
target.setSlotValue(index, ObjReal.of(value))
|
||||||
|
} else {
|
||||||
|
val localIndex = slot - fn.scopeSlotCount
|
||||||
|
ensureLocalMutable(localIndex)
|
||||||
|
when (val existing = frame.getRawObj(localIndex)) {
|
||||||
|
is FrameSlotRef -> {
|
||||||
|
existing.write(ObjReal.of(value))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
is RecordSlotRef -> {
|
||||||
|
existing.write(ObjReal.of(value))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
frame.setReal(localIndex, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setRealUnchecked(slot: Int, value: Double) {
|
||||||
if (slot < fn.scopeSlotCount) {
|
if (slot < fn.scopeSlotCount) {
|
||||||
val target = scopeTarget(slot)
|
val target = scopeTarget(slot)
|
||||||
val index = ensureScopeSlot(target, slot)
|
val index = ensureScopeSlot(target, slot)
|
||||||
@ -2593,6 +3046,34 @@ class CmdFrame(
|
|||||||
fun getLocalBool(local: Int): Boolean = frame.getBool(local)
|
fun getLocalBool(local: Int): Boolean = frame.getBool(local)
|
||||||
|
|
||||||
fun setBool(slot: Int, value: Boolean) {
|
fun setBool(slot: Int, value: Boolean) {
|
||||||
|
if (slot < fn.scopeSlotCount) {
|
||||||
|
val target = scopeTarget(slot)
|
||||||
|
val index = ensureScopeSlot(target, slot)
|
||||||
|
val record = target.getSlotRecord(index)
|
||||||
|
if (!record.isMutable) {
|
||||||
|
val name = fn.scopeSlotNames[slot] ?: "slot#$index"
|
||||||
|
ensureScope().raiseError("can't assign to read-only variable: $name")
|
||||||
|
}
|
||||||
|
target.setSlotValue(index, if (value) ObjTrue else ObjFalse)
|
||||||
|
} else {
|
||||||
|
val localIndex = slot - fn.scopeSlotCount
|
||||||
|
ensureLocalMutable(localIndex)
|
||||||
|
when (val existing = frame.getRawObj(localIndex)) {
|
||||||
|
is FrameSlotRef -> {
|
||||||
|
existing.write(if (value) ObjTrue else ObjFalse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
is RecordSlotRef -> {
|
||||||
|
existing.write(if (value) ObjTrue else ObjFalse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
frame.setBool(localIndex, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBoolUnchecked(slot: Int, value: Boolean) {
|
||||||
if (slot < fn.scopeSlotCount) {
|
if (slot < fn.scopeSlotCount) {
|
||||||
val target = scopeTarget(slot)
|
val target = scopeTarget(slot)
|
||||||
val index = ensureScopeSlot(target, slot)
|
val index = ensureScopeSlot(target, slot)
|
||||||
@ -2687,6 +3168,16 @@ class CmdFrame(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setObjUnchecked(slot: Int, value: Obj) {
|
||||||
|
if (slot < fn.scopeSlotCount) {
|
||||||
|
val target = scopeTarget(slot)
|
||||||
|
val index = ensureScopeSlot(target, slot)
|
||||||
|
target.setSlotValue(index, value)
|
||||||
|
} else {
|
||||||
|
frame.setObj(slot - fn.scopeSlotCount, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun throwObj(pos: Pos, value: Obj) {
|
suspend fun throwObj(pos: Pos, value: Obj) {
|
||||||
var errorObject = value
|
var errorObject = value
|
||||||
val throwScope = ensureScope().createChildScope(pos = pos)
|
val throwScope = ensureScope().createChildScope(pos = pos)
|
||||||
@ -2889,5 +3380,17 @@ class CmdFrame(
|
|||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ensureLocalMutable(localIndex: Int) {
|
||||||
|
val name = fn.localSlotNames.getOrNull(localIndex) ?: return
|
||||||
|
val isMutable = fn.localSlotMutables.getOrNull(localIndex) ?: true
|
||||||
|
if (!isMutable) {
|
||||||
|
val typeCode = frame.getSlotTypeCode(localIndex)
|
||||||
|
if (typeCode == SlotType.UNKNOWN.code) return
|
||||||
|
val rawObj = frame.getRawObj(localIndex)
|
||||||
|
if (rawObj === ObjUnset) return
|
||||||
|
ensureScope().raiseError("can't assign to read-only variable: $name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Scope depth resolution is no longer used; all scope slots are resolved against the current frame.
|
// Scope depth resolution is no longer used; all scope slots are resolved against the current frame.
|
||||||
}
|
}
|
||||||
|
|||||||
@ -168,6 +168,15 @@ enum class Opcode(val code: Int) {
|
|||||||
BIND_DELEGATE_LOCAL(0xC4),
|
BIND_DELEGATE_LOCAL(0xC4),
|
||||||
DECL_FUNCTION(0xC5),
|
DECL_FUNCTION(0xC5),
|
||||||
DECL_CLASS(0xC6),
|
DECL_CLASS(0xC6),
|
||||||
|
DECL_CLASS_FIELD(0xC7),
|
||||||
|
DECL_CLASS_DELEGATED(0xC8),
|
||||||
|
DECL_CLASS_INSTANCE_INIT(0xC9),
|
||||||
|
DECL_CLASS_INSTANCE_FIELD(0xCA),
|
||||||
|
DECL_CLASS_INSTANCE_PROPERTY(0xCB),
|
||||||
|
DECL_CLASS_INSTANCE_DELEGATED(0xCC),
|
||||||
|
DECL_INSTANCE_FIELD(0xCD),
|
||||||
|
DECL_INSTANCE_PROPERTY(0xCE),
|
||||||
|
DECL_INSTANCE_DELEGATED(0xCF),
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@ -447,7 +447,7 @@ open class Obj {
|
|||||||
caller.members[name]?.let { rec ->
|
caller.members[name]?.let { rec ->
|
||||||
if (rec.visibility == Visibility.Private && !rec.isAbstract) {
|
if (rec.visibility == Visibility.Private && !rec.isAbstract) {
|
||||||
val resolved = resolveRecord(scope, rec, name, caller)
|
val resolved = resolveRecord(scope, rec, name, caller)
|
||||||
if (resolved.type == ObjRecord.Type.Fun && resolved.value is Statement)
|
if (resolved.type == ObjRecord.Type.Fun)
|
||||||
return resolved.copy(value = resolved.value.invoke(scope, this, Arguments.EMPTY, caller))
|
return resolved.copy(value = resolved.value.invoke(scope, this, Arguments.EMPTY, caller))
|
||||||
return resolved
|
return resolved
|
||||||
}
|
}
|
||||||
@ -461,7 +461,7 @@ open class Obj {
|
|||||||
if (rec != null && !rec.isAbstract) {
|
if (rec != null && !rec.isAbstract) {
|
||||||
val decl = rec.declaringClass ?: cls
|
val decl = rec.declaringClass ?: cls
|
||||||
val resolved = resolveRecord(scope, rec, name, decl)
|
val resolved = resolveRecord(scope, rec, name, decl)
|
||||||
if (resolved.type == ObjRecord.Type.Fun && resolved.value is Statement)
|
if (resolved.type == ObjRecord.Type.Fun)
|
||||||
return resolved.copy(value = resolved.value.invoke(scope, this, Arguments.EMPTY, decl))
|
return resolved.copy(value = resolved.value.invoke(scope, this, Arguments.EMPTY, decl))
|
||||||
return resolved
|
return resolved
|
||||||
}
|
}
|
||||||
@ -476,7 +476,7 @@ open class Obj {
|
|||||||
if (!canAccessMember(rec.visibility, decl, caller, name))
|
if (!canAccessMember(rec.visibility, decl, caller, name))
|
||||||
scope.raiseError(ObjIllegalAccessException(scope, "can't access field ${name}: not visible (declared in ${decl.className}, caller ${caller?.className ?: "?"})"))
|
scope.raiseError(ObjIllegalAccessException(scope, "can't access field ${name}: not visible (declared in ${decl.className}, caller ${caller?.className ?: "?"})"))
|
||||||
val resolved = resolveRecord(scope, rec, name, decl)
|
val resolved = resolveRecord(scope, rec, name, decl)
|
||||||
if (resolved.type == ObjRecord.Type.Fun && resolved.value is Statement)
|
if (resolved.type == ObjRecord.Type.Fun)
|
||||||
return resolved.copy(value = resolved.value.invoke(scope, this, Arguments.EMPTY, decl))
|
return resolved.copy(value = resolved.value.invoke(scope, this, Arguments.EMPTY, decl))
|
||||||
return resolved
|
return resolved
|
||||||
}
|
}
|
||||||
@ -500,16 +500,17 @@ open class Obj {
|
|||||||
if (obj.type == ObjRecord.Type.Delegated) {
|
if (obj.type == ObjRecord.Type.Delegated) {
|
||||||
val del = obj.delegate ?: scope.raiseError("Internal error: delegated property $name has no delegate")
|
val del = obj.delegate ?: scope.raiseError("Internal error: delegated property $name has no delegate")
|
||||||
val th = if (this === ObjVoid) ObjNull else this
|
val th = if (this === ObjVoid) ObjNull else this
|
||||||
val getValueRec = del.objClass.getInstanceMemberOrNull("getValue")
|
val getValueRec = when (del) {
|
||||||
|
is ObjInstance -> del.methodRecordForKey("getValue")
|
||||||
|
?: del.instanceScope.objects["getValue"]
|
||||||
|
?: del.objClass.getInstanceMemberOrNull("getValue")
|
||||||
|
else -> del.objClass.getInstanceMemberOrNull("getValue")
|
||||||
|
}
|
||||||
if (getValueRec == null || getValueRec.declaringClass?.className == "Delegate") {
|
if (getValueRec == null || getValueRec.declaringClass?.className == "Delegate") {
|
||||||
val wrapper = object : Statement() {
|
val wrapper = ObjNativeCallable {
|
||||||
override val pos: Pos = Pos.builtIn
|
val th2 = if (thisObj === ObjVoid) ObjNull else thisObj
|
||||||
|
val allArgs = (listOf(th2, ObjString(name)) + args.list).toTypedArray()
|
||||||
override suspend fun execute(s: Scope): Obj {
|
del.invokeInstanceMethod(this, "invoke", Arguments(*allArgs))
|
||||||
val th2 = if (s.thisObj === ObjVoid) ObjNull else s.thisObj
|
|
||||||
val allArgs = (listOf(th2, ObjString(name)) + s.args.list).toTypedArray()
|
|
||||||
return del.invokeInstanceMethod(s, "invoke", Arguments(*allArgs))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return obj.copy(
|
return obj.copy(
|
||||||
value = wrapper,
|
value = wrapper,
|
||||||
@ -517,14 +518,11 @@ open class Obj {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
val res = del.invokeInstanceMethod(scope, "getValue", Arguments(th, ObjString(name)))
|
val res = del.invokeInstanceMethod(scope, "getValue", Arguments(th, ObjString(name)))
|
||||||
return obj.copy(
|
return obj.copy(value = res, type = ObjRecord.Type.Other)
|
||||||
value = res,
|
|
||||||
type = ObjRecord.Type.Other
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
val value = obj.value
|
val value = obj.value
|
||||||
if (value is ObjProperty || obj.type == ObjRecord.Type.Property) {
|
if (value is ObjProperty || obj.type == ObjRecord.Type.Property) {
|
||||||
val prop = if (value is ObjProperty) value else (value as? Statement)?.execute(scope.createChildScope(scope.pos, newThisObj = this)) as? ObjProperty
|
val prop = (value as? ObjProperty)
|
||||||
?: scope.raiseError("Expected ObjProperty for property member $name, got ${value::class}")
|
?: scope.raiseError("Expected ObjProperty for property member $name, got ${value::class}")
|
||||||
val res = prop.callGetter(scope, this, decl)
|
val res = prop.callGetter(scope, this, decl)
|
||||||
return ObjRecord(res, obj.isMutable)
|
return ObjRecord(res, obj.isMutable)
|
||||||
|
|||||||
@ -61,7 +61,7 @@ val ObjClassType by lazy {
|
|||||||
val names = mutableListOf<Obj>()
|
val names = mutableListOf<Obj>()
|
||||||
for (c in cls.mro) {
|
for (c in cls.mro) {
|
||||||
for ((n, rec) in c.members) {
|
for ((n, rec) in c.members) {
|
||||||
if (rec.value !is Statement && seen.add(n)) names += ObjString(n)
|
if (rec.type != ObjRecord.Type.Fun && seen.add(n)) names += ObjString(n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ObjList(names.toMutableList())
|
ObjList(names.toMutableList())
|
||||||
@ -79,7 +79,7 @@ val ObjClassType by lazy {
|
|||||||
val names = mutableListOf<Obj>()
|
val names = mutableListOf<Obj>()
|
||||||
for (c in cls.mro) {
|
for (c in cls.mro) {
|
||||||
for ((n, rec) in c.members) {
|
for ((n, rec) in c.members) {
|
||||||
if (rec.value is Statement && seen.add(n)) names += ObjString(n)
|
if (rec.type == ObjRecord.Type.Fun && seen.add(n)) names += ObjString(n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ObjList(names.toMutableList())
|
ObjList(names.toMutableList())
|
||||||
@ -136,7 +136,7 @@ open class ObjClass(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
cls.classScope?.objects?.forEach { (name, rec) ->
|
cls.classScope?.objects?.forEach { (name, rec) ->
|
||||||
if (rec.visibility == Visibility.Public && (rec.value is Statement || rec.type == ObjRecord.Type.Delegated)) {
|
if (rec.visibility == Visibility.Public && (rec.type == ObjRecord.Type.Fun || rec.type == ObjRecord.Type.Delegated)) {
|
||||||
val key = if (rec.type == ObjRecord.Type.Delegated) cls.mangledName(name) else name
|
val key = if (rec.type == ObjRecord.Type.Delegated) cls.mangledName(name) else name
|
||||||
res[name] = key
|
res[name] = key
|
||||||
}
|
}
|
||||||
@ -150,13 +150,13 @@ open class ObjClass(
|
|||||||
val classNameObj by lazy { ObjString(className) }
|
val classNameObj by lazy { ObjString(className) }
|
||||||
|
|
||||||
var constructorMeta: ArgsDeclaration? = null
|
var constructorMeta: ArgsDeclaration? = null
|
||||||
var instanceConstructor: Statement? = null
|
var instanceConstructor: Obj? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per-instance initializers collected from class body (for instance fields). These are executed
|
* Per-instance initializers collected from class body (for instance fields). These are executed
|
||||||
* during construction in the instance scope of the object, once per class in the hierarchy.
|
* during construction in the instance scope of the object, once per class in the hierarchy.
|
||||||
*/
|
*/
|
||||||
val instanceInitializers: MutableList<Statement> = mutableListOf()
|
val instanceInitializers: MutableList<Obj> = mutableListOf()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the scope for class methods, initialize class vars, etc.
|
* the scope for class methods, initialize class vars, etc.
|
||||||
@ -349,8 +349,7 @@ open class ObjClass(
|
|||||||
if (cls.className == "Obj") break
|
if (cls.className == "Obj") break
|
||||||
for ((name, rec) in cls.members) {
|
for ((name, rec) in cls.members) {
|
||||||
if (rec.isAbstract) continue
|
if (rec.isAbstract) continue
|
||||||
if (rec.value !is Statement &&
|
if (rec.type != ObjRecord.Type.Delegated &&
|
||||||
rec.type != ObjRecord.Type.Delegated &&
|
|
||||||
rec.type != ObjRecord.Type.Fun &&
|
rec.type != ObjRecord.Type.Fun &&
|
||||||
rec.type != ObjRecord.Type.Property) {
|
rec.type != ObjRecord.Type.Property) {
|
||||||
continue
|
continue
|
||||||
@ -363,9 +362,9 @@ open class ObjClass(
|
|||||||
}
|
}
|
||||||
cls.classScope?.objects?.forEach { (name, rec) ->
|
cls.classScope?.objects?.forEach { (name, rec) ->
|
||||||
if (rec.isAbstract) return@forEach
|
if (rec.isAbstract) return@forEach
|
||||||
if (rec.value !is Statement &&
|
if (rec.type != ObjRecord.Type.Delegated &&
|
||||||
rec.type != ObjRecord.Type.Delegated &&
|
rec.type != ObjRecord.Type.Property &&
|
||||||
rec.type != ObjRecord.Type.Property) return@forEach
|
rec.type != ObjRecord.Type.Fun) return@forEach
|
||||||
val key = if (rec.visibility == Visibility.Private || rec.type == ObjRecord.Type.Delegated) cls.mangledName(name) else name
|
val key = if (rec.visibility == Visibility.Private || rec.type == ObjRecord.Type.Delegated) cls.mangledName(name) else name
|
||||||
if (res.containsKey(key)) return@forEach
|
if (res.containsKey(key)) return@forEach
|
||||||
val methodId = rec.methodId ?: cls.assignMethodId(name, rec)
|
val methodId = rec.methodId ?: cls.assignMethodId(name, rec)
|
||||||
@ -533,7 +532,7 @@ open class ObjClass(
|
|||||||
for (cls in mro) {
|
for (cls in mro) {
|
||||||
// 1) members-defined methods and fields
|
// 1) members-defined methods and fields
|
||||||
for ((k, v) in cls.members) {
|
for ((k, v) in cls.members) {
|
||||||
if (!v.isAbstract && (v.value is Statement || v.type == ObjRecord.Type.Delegated || v.type == ObjRecord.Type.Field || v.type == ObjRecord.Type.ConstructorField)) {
|
if (!v.isAbstract && (v.type == ObjRecord.Type.Fun || v.type == ObjRecord.Type.Property || v.type == ObjRecord.Type.Delegated || v.type == ObjRecord.Type.Field || v.type == ObjRecord.Type.ConstructorField)) {
|
||||||
val key = if (v.visibility == Visibility.Private || v.type == ObjRecord.Type.Field || v.type == ObjRecord.Type.ConstructorField || v.type == ObjRecord.Type.Delegated) cls.mangledName(k) else k
|
val key = if (v.visibility == Visibility.Private || v.type == ObjRecord.Type.Field || v.type == ObjRecord.Type.ConstructorField || v.type == ObjRecord.Type.Delegated) cls.mangledName(k) else k
|
||||||
if (!res.containsKey(key)) {
|
if (!res.containsKey(key)) {
|
||||||
res[key] = v
|
res[key] = v
|
||||||
@ -544,7 +543,7 @@ open class ObjClass(
|
|||||||
cls.classScope?.objects?.forEach { (k, rec) ->
|
cls.classScope?.objects?.forEach { (k, rec) ->
|
||||||
// ONLY copy methods and delegated members from class scope to instance scope.
|
// ONLY copy methods and delegated members from class scope to instance scope.
|
||||||
// Fields in class scope are static fields and must NOT be per-instance.
|
// Fields in class scope are static fields and must NOT be per-instance.
|
||||||
if (!rec.isAbstract && (rec.value is Statement || rec.type == ObjRecord.Type.Delegated)) {
|
if (!rec.isAbstract && (rec.type == ObjRecord.Type.Fun || rec.type == ObjRecord.Type.Property || rec.type == ObjRecord.Type.Delegated)) {
|
||||||
val key = if (rec.visibility == Visibility.Private || rec.type == ObjRecord.Type.Delegated) cls.mangledName(k) else k
|
val key = if (rec.visibility == Visibility.Private || rec.type == ObjRecord.Type.Delegated) cls.mangledName(k) else k
|
||||||
// if not already present, copy reference for dispatch
|
// if not already present, copy reference for dispatch
|
||||||
if (!res.containsKey(key)) {
|
if (!res.containsKey(key)) {
|
||||||
@ -715,7 +714,7 @@ open class ObjClass(
|
|||||||
instance.instanceScope.currentClassCtx = c
|
instance.instanceScope.currentClassCtx = c
|
||||||
try {
|
try {
|
||||||
for (initStmt in c.instanceInitializers) {
|
for (initStmt in c.instanceInitializers) {
|
||||||
initStmt.execute(instance.instanceScope)
|
initStmt.callOn(instance.instanceScope)
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
instance.instanceScope.currentClassCtx = savedCtx
|
instance.instanceScope.currentClassCtx = savedCtx
|
||||||
@ -726,7 +725,7 @@ open class ObjClass(
|
|||||||
c.instanceConstructor?.let { ctor ->
|
c.instanceConstructor?.let { ctor ->
|
||||||
val execScope =
|
val execScope =
|
||||||
instance.instanceScope.createChildScope(args = argsForThis ?: Arguments.EMPTY, newThisObj = instance)
|
instance.instanceScope.createChildScope(args = argsForThis ?: Arguments.EMPTY, newThisObj = instance)
|
||||||
ctor.execute(execScope)
|
ctor.callOn(execScope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -933,7 +932,7 @@ open class ObjClass(
|
|||||||
methodId: Int? = null,
|
methodId: Int? = null,
|
||||||
code: (suspend Scope.() -> Obj)? = null
|
code: (suspend Scope.() -> Obj)? = null
|
||||||
) {
|
) {
|
||||||
val stmt = code?.let { statement { it() } } ?: ObjNull
|
val stmt = code?.let { ObjNativeCallable { it() } } ?: ObjNull
|
||||||
createField(
|
createField(
|
||||||
name, stmt, isMutable, visibility, writeVisibility, pos, declaringClass,
|
name, stmt, isMutable, visibility, writeVisibility, pos, declaringClass,
|
||||||
isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride,
|
isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride,
|
||||||
@ -958,8 +957,8 @@ open class ObjClass(
|
|||||||
prop: ObjProperty? = null,
|
prop: ObjProperty? = null,
|
||||||
methodId: Int? = null
|
methodId: Int? = null
|
||||||
) {
|
) {
|
||||||
val g = getter?.let { statement { it() } }
|
val g = getter?.let { ObjNativeCallable { it() } }
|
||||||
val s = setter?.let { statement { it(requiredArg(0)); ObjVoid } }
|
val s = setter?.let { ObjNativeCallable { it(requiredArg(0)); ObjVoid } }
|
||||||
val finalProp = prop ?: if (isAbstract) ObjNull else ObjProperty(name, g, s)
|
val finalProp = prop ?: if (isAbstract) ObjNull else ObjProperty(name, g, s)
|
||||||
createField(
|
createField(
|
||||||
name, finalProp, false, visibility, writeVisibility, pos, declaringClass,
|
name, finalProp, false, visibility, writeVisibility, pos, declaringClass,
|
||||||
@ -971,7 +970,7 @@ open class ObjClass(
|
|||||||
|
|
||||||
fun addClassConst(name: String, value: Obj) = createClassField(name, value)
|
fun addClassConst(name: String, value: Obj) = createClassField(name, value)
|
||||||
fun addClassFn(name: String, isOpen: Boolean = false, code: suspend Scope.() -> Obj) {
|
fun addClassFn(name: String, isOpen: Boolean = false, code: suspend Scope.() -> Obj) {
|
||||||
createClassField(name, statement { code() }, isOpen, type = ObjRecord.Type.Fun)
|
createClassField(name, ObjNativeCallable { code() }, isOpen, type = ObjRecord.Type.Fun)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,6 @@ import kotlinx.datetime.*
|
|||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import kotlinx.serialization.json.JsonPrimitive
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.Statement
|
|
||||||
import net.sergeych.lyng.miniast.addClassFnDoc
|
import net.sergeych.lyng.miniast.addClassFnDoc
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
import net.sergeych.lyng.miniast.addFnDoc
|
||||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
import net.sergeych.lyng.miniast.addPropertyDoc
|
||||||
@ -47,14 +46,19 @@ class ObjDateTime(val instant: Instant, val timeZone: TimeZone) : Obj() {
|
|||||||
if (rec != null) {
|
if (rec != null) {
|
||||||
if (rec.type == ObjRecord.Type.Property) {
|
if (rec.type == ObjRecord.Type.Property) {
|
||||||
val prop = rec.value as? ObjProperty
|
val prop = rec.value as? ObjProperty
|
||||||
?: (rec.value as? Statement)?.execute(scope) as? ObjProperty
|
|
||||||
if (prop != null) {
|
if (prop != null) {
|
||||||
return ObjRecord(prop.callGetter(scope, this, rec.declaringClass ?: cls), rec.isMutable)
|
return ObjRecord(prop.callGetter(scope, this, rec.declaringClass ?: cls), rec.isMutable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rec.type == ObjRecord.Type.Fun || rec.value is Statement) {
|
if (rec.type == ObjRecord.Type.Fun) {
|
||||||
val s = rec.value as Statement
|
val target = rec.value
|
||||||
return ObjRecord(net.sergeych.lyng.statement { s.execute(this.createChildScope(newThisObj = this@ObjDateTime)) }, rec.isMutable)
|
return ObjRecord(
|
||||||
|
ObjNativeCallable {
|
||||||
|
val callScope = createChildScope(args = args, newThisObj = this@ObjDateTime)
|
||||||
|
target.callOn(callScope)
|
||||||
|
},
|
||||||
|
rec.isMutable
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return resolveRecord(scope, rec, name, rec.declaringClass ?: cls)
|
return resolveRecord(scope, rec, name, rec.declaringClass ?: cls)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,6 @@ package net.sergeych.lyng.obj
|
|||||||
|
|
||||||
import net.sergeych.lyng.Arguments
|
import net.sergeych.lyng.Arguments
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.Statement
|
|
||||||
|
|
||||||
class ObjDynamicContext(val delegate: ObjDynamic) : Obj() {
|
class ObjDynamicContext(val delegate: ObjDynamic) : Obj() {
|
||||||
override val objClass: ObjClass get() = type
|
override val objClass: ObjClass get() = type
|
||||||
@ -51,7 +50,7 @@ class ObjDynamicContext(val delegate: ObjDynamic) : Obj() {
|
|||||||
* Object that delegates all its field access/invocation operations to a callback. It is used to implement dynamic
|
* Object that delegates all its field access/invocation operations to a callback. It is used to implement dynamic
|
||||||
* objects using "dynamic" keyword.
|
* objects using "dynamic" keyword.
|
||||||
*/
|
*/
|
||||||
open class ObjDynamic(var readCallback: Statement? = null, var writeCallback: Statement? = null) : Obj() {
|
open class ObjDynamic(var readCallback: Obj? = null, var writeCallback: Obj? = null) : Obj() {
|
||||||
|
|
||||||
override val objClass: ObjClass get() = type
|
override val objClass: ObjClass get() = type
|
||||||
// Capture the lexical scope used to build this dynamic so callbacks can see outer locals
|
// Capture the lexical scope used to build this dynamic so callbacks can see outer locals
|
||||||
@ -63,7 +62,7 @@ open class ObjDynamic(var readCallback: Statement? = null, var writeCallback: St
|
|||||||
*/
|
*/
|
||||||
override suspend fun readField(scope: Scope, name: String): ObjRecord {
|
override suspend fun readField(scope: Scope, name: String): ObjRecord {
|
||||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
||||||
return readCallback?.execute(execBase.createChildScope(Arguments(ObjString(name))))?.let {
|
return readCallback?.callOn(execBase.createChildScope(Arguments(ObjString(name))))?.let {
|
||||||
if (writeCallback != null)
|
if (writeCallback != null)
|
||||||
it.asMutable
|
it.asMutable
|
||||||
else
|
else
|
||||||
@ -83,32 +82,32 @@ open class ObjDynamic(var readCallback: Statement? = null, var writeCallback: St
|
|||||||
onNotFoundResult: (suspend () -> Obj?)?
|
onNotFoundResult: (suspend () -> Obj?)?
|
||||||
): Obj {
|
): Obj {
|
||||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
||||||
val over = readCallback?.execute(execBase.createChildScope(Arguments(ObjString(name))))
|
val over = readCallback?.callOn(execBase.createChildScope(Arguments(ObjString(name))))
|
||||||
return over?.invoke(scope, scope.thisObj, args)
|
return over?.invoke(scope, scope.thisObj, args)
|
||||||
?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
|
override suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
|
||||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
||||||
writeCallback?.execute(execBase.createChildScope(Arguments(ObjString(name), newValue)))
|
writeCallback?.callOn(execBase.createChildScope(Arguments(ObjString(name), newValue)))
|
||||||
?: super.writeField(scope, name, newValue)
|
?: super.writeField(scope, name, newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getAt(scope: Scope, index: Obj): Obj {
|
override suspend fun getAt(scope: Scope, index: Obj): Obj {
|
||||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
||||||
return readCallback?.execute(execBase.createChildScope(Arguments(index)))
|
return readCallback?.callOn(execBase.createChildScope(Arguments(index)))
|
||||||
?: super.getAt(scope, index)
|
?: super.getAt(scope, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun putAt(scope: Scope, index: Obj, newValue: Obj) {
|
override suspend fun putAt(scope: Scope, index: Obj, newValue: Obj) {
|
||||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
||||||
writeCallback?.execute(execBase.createChildScope(Arguments(index, newValue)))
|
writeCallback?.callOn(execBase.createChildScope(Arguments(index, newValue)))
|
||||||
?: super.putAt(scope, index, newValue)
|
?: super.putAt(scope, index, newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
suspend fun create(scope: Scope, builder: Statement): ObjDynamic {
|
suspend fun create(scope: Scope, builder: Obj): ObjDynamic {
|
||||||
val delegate = ObjDynamic()
|
val delegate = ObjDynamic()
|
||||||
val context = ObjDynamicContext(delegate)
|
val context = ObjDynamicContext(delegate)
|
||||||
// Capture the function's lexical scope (scope) so callbacks can see outer locals like parameters.
|
// Capture the function's lexical scope (scope) so callbacks can see outer locals like parameters.
|
||||||
@ -116,7 +115,7 @@ open class ObjDynamic(var readCallback: Statement? = null, var writeCallback: St
|
|||||||
val buildScope = scope.createChildScope(newThisObj = context)
|
val buildScope = scope.createChildScope(newThisObj = context)
|
||||||
// Snapshot the caller scope to capture locals/args even if the runtime pools/reuses frames
|
// Snapshot the caller scope to capture locals/args even if the runtime pools/reuses frames
|
||||||
delegate.builderScope = scope.snapshotForClosure()
|
delegate.builderScope = scope.snapshotForClosure()
|
||||||
builder.execute(buildScope)
|
builder.callOn(buildScope)
|
||||||
return delegate
|
return delegate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -139,7 +139,7 @@ open class ObjException(
|
|||||||
class ExceptionClass(val name: String, vararg parents: ObjClass) : ObjClass(name, *parents) {
|
class ExceptionClass(val name: String, vararg parents: ObjClass) : ObjClass(name, *parents) {
|
||||||
init {
|
init {
|
||||||
constructorMeta = ArgsDeclaration(
|
constructorMeta = ArgsDeclaration(
|
||||||
listOf(ArgsDeclaration.Item("message", defaultValue = statement { ObjString(name) })),
|
listOf(ArgsDeclaration.Item("message", defaultValue = ObjNativeCallable { ObjString(name) })),
|
||||||
Token.Type.RPAREN
|
Token.Type.RPAREN
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -177,7 +177,7 @@ open class ObjException(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val Root = ExceptionClass("Exception").apply {
|
val Root = ExceptionClass("Exception").apply {
|
||||||
instanceInitializers.add(statement {
|
instanceInitializers.add(ObjNativeCallable {
|
||||||
if (thisObj is ObjInstance) {
|
if (thisObj is ObjInstance) {
|
||||||
val msg = get("message")?.value ?: ObjString("Exception")
|
val msg = get("message")?.value ?: ObjString("Exception")
|
||||||
(thisObj as ObjInstance).instanceScope.addItem("Exception::message", false, msg)
|
(thisObj as ObjInstance).instanceScope.addItem("Exception::message", false, msg)
|
||||||
@ -187,7 +187,7 @@ open class ObjException(
|
|||||||
}
|
}
|
||||||
ObjVoid
|
ObjVoid
|
||||||
})
|
})
|
||||||
instanceConstructor = statement { ObjVoid }
|
instanceConstructor = ObjNativeCallable { ObjVoid }
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "message",
|
name = "message",
|
||||||
doc = "Human‑readable error message.",
|
doc = "Human‑readable error message.",
|
||||||
|
|||||||
@ -71,13 +71,13 @@ class ObjFlowBuilder(val output: SendChannel<Obj>) : Obj() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createLyngFlowInput(scope: Scope, producer: Statement): ReceiveChannel<Obj> {
|
private fun createLyngFlowInput(scope: Scope, producer: Obj): ReceiveChannel<Obj> {
|
||||||
val channel = Channel<Obj>(Channel.RENDEZVOUS)
|
val channel = Channel<Obj>(Channel.RENDEZVOUS)
|
||||||
val builder = ObjFlowBuilder(channel)
|
val builder = ObjFlowBuilder(channel)
|
||||||
val builderScope = scope.createChildScope(newThisObj = builder)
|
val builderScope = scope.createChildScope(newThisObj = builder)
|
||||||
globalLaunch {
|
globalLaunch {
|
||||||
try {
|
try {
|
||||||
producer.execute(builderScope)
|
producer.callOn(builderScope)
|
||||||
} catch (x: ScriptFlowIsNoMoreCollected) {
|
} catch (x: ScriptFlowIsNoMoreCollected) {
|
||||||
// premature flow closing, OK
|
// premature flow closing, OK
|
||||||
} catch (x: Exception) {
|
} catch (x: Exception) {
|
||||||
@ -89,7 +89,7 @@ private fun createLyngFlowInput(scope: Scope, producer: Statement): ReceiveChann
|
|||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
class ObjFlow(val producer: Statement, val scope: Scope) : Obj() {
|
class ObjFlow(val producer: Obj, val scope: Scope) : Obj() {
|
||||||
|
|
||||||
override val objClass get() = type
|
override val objClass get() = type
|
||||||
|
|
||||||
@ -106,8 +106,8 @@ class ObjFlow(val producer: Statement, val scope: Scope) : Obj() {
|
|||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib"
|
||||||
) {
|
) {
|
||||||
val objFlow = thisAs<ObjFlow>()
|
val objFlow = thisAs<ObjFlow>()
|
||||||
ObjFlowIterator(statement {
|
ObjFlowIterator(ObjNativeCallable {
|
||||||
objFlow.producer.execute(this)
|
objFlow.producer.callOn(this)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,7 +115,7 @@ class ObjFlow(val producer: Statement, val scope: Scope) : Obj() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ObjFlowIterator(val producer: Statement) : Obj() {
|
class ObjFlowIterator(val producer: Obj) : Obj() {
|
||||||
|
|
||||||
override val objClass: ObjClass get() = type
|
override val objClass: ObjClass get() = type
|
||||||
|
|
||||||
|
|||||||
@ -170,16 +170,17 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
del = del ?: scope.raiseError("Internal error: delegated property $name has no delegate")
|
del = del ?: scope.raiseError("Internal error: delegated property $name has no delegate")
|
||||||
val getValueRec = del.objClass.getInstanceMemberOrNull("getValue")
|
val getValueRec = when (del) {
|
||||||
|
is ObjInstance -> del.methodRecordForKey("getValue")
|
||||||
|
?: del.instanceScope.objects["getValue"]
|
||||||
|
?: del.objClass.getInstanceMemberOrNull("getValue")
|
||||||
|
else -> del.objClass.getInstanceMemberOrNull("getValue")
|
||||||
|
}
|
||||||
if (getValueRec == null || getValueRec.declaringClass?.className == "Delegate") {
|
if (getValueRec == null || getValueRec.declaringClass?.className == "Delegate") {
|
||||||
val wrapper = object : Statement() {
|
val wrapper = ObjNativeCallable {
|
||||||
override val pos: Pos = Pos.builtIn
|
val th2 = if (thisObj === ObjVoid) ObjNull else thisObj
|
||||||
|
val allArgs = (listOf(th2, ObjString(name)) + args.list).toTypedArray()
|
||||||
override suspend fun execute(s: Scope): Obj {
|
del.invokeInstanceMethod(this, "invoke", Arguments(*allArgs))
|
||||||
val th2 = if (s.thisObj === ObjVoid) ObjNull else s.thisObj
|
|
||||||
val allArgs = (listOf(th2, ObjString(name)) + s.args.list).toTypedArray()
|
|
||||||
return del.invokeInstanceMethod(s, "invoke", Arguments(*allArgs))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return obj.copy(value = wrapper, type = ObjRecord.Type.Other)
|
return obj.copy(value = wrapper, type = ObjRecord.Type.Other)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,6 @@
|
|||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import net.sergeych.lyng.Arguments
|
import net.sergeych.lyng.Arguments
|
||||||
import net.sergeych.lyng.Statement
|
|
||||||
import net.sergeych.lyng.miniast.ParamDoc
|
import net.sergeych.lyng.miniast.ParamDoc
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
import net.sergeych.lyng.miniast.addFnDoc
|
||||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
import net.sergeych.lyng.miniast.addPropertyDoc
|
||||||
@ -131,10 +130,11 @@ val ObjIterable by lazy {
|
|||||||
returns = type("lyng.Map"),
|
returns = type("lyng.Map"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib"
|
||||||
) {
|
) {
|
||||||
val association = requireOnlyArg<Statement>()
|
val association = requireOnlyArg<Obj>()
|
||||||
val result = ObjMap()
|
val result = ObjMap()
|
||||||
thisObj.toFlow(this).collect {
|
thisObj.toFlow(this).collect {
|
||||||
result.map[association.call(this, it)] = it
|
val callScope = createChildScope(args = Arguments(it))
|
||||||
|
result.map[association.callOn(callScope)] = it
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@ -147,10 +147,10 @@ val ObjIterable by lazy {
|
|||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib"
|
||||||
) {
|
) {
|
||||||
val it = thisObj.invokeInstanceMethod(this, "iterator")
|
val it = thisObj.invokeInstanceMethod(this, "iterator")
|
||||||
val fn = requiredArg<Statement>(0)
|
val fn = requiredArg<Obj>(0)
|
||||||
while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
|
while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
|
||||||
val x = it.invokeInstanceMethod(this, "next")
|
val x = it.invokeInstanceMethod(this, "next")
|
||||||
fn.execute(this.createChildScope(Arguments(listOf(x))))
|
fn.callOn(this.createChildScope(Arguments(listOf(x))))
|
||||||
}
|
}
|
||||||
ObjVoid
|
ObjVoid
|
||||||
}
|
}
|
||||||
@ -163,10 +163,11 @@ val ObjIterable by lazy {
|
|||||||
isOpen = true,
|
isOpen = true,
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib"
|
||||||
) {
|
) {
|
||||||
val fn = requiredArg<Statement>(0)
|
val fn = requiredArg<Obj>(0)
|
||||||
val result = mutableListOf<Obj>()
|
val result = mutableListOf<Obj>()
|
||||||
thisObj.toFlow(this).collect {
|
thisObj.toFlow(this).collect {
|
||||||
result.add(fn.call(this, it))
|
val callScope = createChildScope(args = Arguments(it))
|
||||||
|
result.add(fn.callOn(callScope))
|
||||||
}
|
}
|
||||||
ObjList(result)
|
ObjList(result)
|
||||||
}
|
}
|
||||||
@ -179,10 +180,11 @@ val ObjIterable by lazy {
|
|||||||
isOpen = true,
|
isOpen = true,
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib"
|
||||||
) {
|
) {
|
||||||
val fn = requiredArg<Statement>(0)
|
val fn = requiredArg<Obj>(0)
|
||||||
val result = mutableListOf<Obj>()
|
val result = mutableListOf<Obj>()
|
||||||
thisObj.toFlow(this).collect {
|
thisObj.toFlow(this).collect {
|
||||||
val transformed = fn.call(this, it)
|
val callScope = createChildScope(args = Arguments(it))
|
||||||
|
val transformed = fn.callOn(callScope)
|
||||||
if( transformed != ObjNull) result.add(transformed)
|
if( transformed != ObjNull) result.add(transformed)
|
||||||
}
|
}
|
||||||
ObjList(result)
|
ObjList(result)
|
||||||
@ -228,9 +230,10 @@ val ObjIterable by lazy {
|
|||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib"
|
||||||
) {
|
) {
|
||||||
val list = thisObj.callMethod<ObjList>(this, "toList")
|
val list = thisObj.callMethod<ObjList>(this, "toList")
|
||||||
val comparator = requireOnlyArg<Statement>()
|
val comparator = requireOnlyArg<Obj>()
|
||||||
list.quicksort { a, b ->
|
list.quicksort { a, b ->
|
||||||
comparator.call(this, a, b).toInt()
|
val callScope = createChildScope(args = Arguments(a, b))
|
||||||
|
comparator.callOn(callScope).toInt()
|
||||||
}
|
}
|
||||||
list
|
list
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import net.sergeych.lyng.*
|
|||||||
* Lazy delegate used by `val x by lazy { ... }`.
|
* Lazy delegate used by `val x by lazy { ... }`.
|
||||||
*/
|
*/
|
||||||
class ObjLazyDelegate(
|
class ObjLazyDelegate(
|
||||||
private val builder: Statement,
|
private val builder: Obj,
|
||||||
private val capturedScope: Scope,
|
private val capturedScope: Scope,
|
||||||
) : Obj() {
|
) : Obj() {
|
||||||
override val objClass: ObjClass = type
|
override val objClass: ObjClass = type
|
||||||
|
|||||||
@ -20,7 +20,7 @@ package net.sergeych.lyng.obj
|
|||||||
import kotlinx.serialization.json.JsonArray
|
import kotlinx.serialization.json.JsonArray
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.Statement
|
import net.sergeych.lyng.Arguments
|
||||||
import net.sergeych.lyng.miniast.ParamDoc
|
import net.sergeych.lyng.miniast.ParamDoc
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
import net.sergeych.lyng.miniast.addFnDoc
|
||||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
import net.sergeych.lyng.miniast.addPropertyDoc
|
||||||
@ -371,8 +371,11 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
|||||||
params = listOf(ParamDoc("comparator")),
|
params = listOf(ParamDoc("comparator")),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib"
|
||||||
) {
|
) {
|
||||||
val comparator = requireOnlyArg<Statement>()
|
val comparator = requireOnlyArg<Obj>()
|
||||||
thisAs<ObjList>().quicksort { a, b -> comparator.call(this, a, b).toInt() }
|
thisAs<ObjList>().quicksort { a, b ->
|
||||||
|
val callScope = createChildScope(args = Arguments(a, b))
|
||||||
|
comparator.callOn(callScope).toInt()
|
||||||
|
}
|
||||||
ObjVoid
|
ObjVoid
|
||||||
}
|
}
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
@ -522,4 +525,3 @@ fun <T>MutableList<T>.swap(i: Int, j: Int) {
|
|||||||
this[j] = temp
|
this[j] = temp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,6 @@ package net.sergeych.lyng.obj
|
|||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.Statement
|
|
||||||
import net.sergeych.lyng.miniast.*
|
import net.sergeych.lyng.miniast.*
|
||||||
import net.sergeych.lynon.LynonDecoder
|
import net.sergeych.lynon.LynonDecoder
|
||||||
import net.sergeych.lynon.LynonEncoder
|
import net.sergeych.lynon.LynonEncoder
|
||||||
@ -261,8 +260,8 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
|
|||||||
) {
|
) {
|
||||||
val key = requiredArg<Obj>(0)
|
val key = requiredArg<Obj>(0)
|
||||||
thisAs<ObjMap>().map.getOrPut(key) {
|
thisAs<ObjMap>().map.getOrPut(key) {
|
||||||
val lambda = requiredArg<Statement>(1)
|
val lambda = requiredArg<Obj>(1)
|
||||||
lambda.execute(this)
|
lambda.callOn(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
|
|||||||
@ -20,7 +20,6 @@ package net.sergeych.lyng.obj
|
|||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.Statement
|
|
||||||
import net.sergeych.lyng.miniast.ParamDoc
|
import net.sergeych.lyng.miniast.ParamDoc
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
import net.sergeych.lyng.miniast.addFnDoc
|
||||||
import net.sergeych.lyng.miniast.type
|
import net.sergeych.lyng.miniast.type
|
||||||
@ -41,11 +40,11 @@ class ObjMutex(val mutex: Mutex): Obj() {
|
|||||||
returns = type("lyng.Any"),
|
returns = type("lyng.Any"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib"
|
||||||
) {
|
) {
|
||||||
val f = requiredArg<Statement>(0)
|
val f = requiredArg<Obj>(0)
|
||||||
// Execute user lambda directly in the current scope to preserve the active scope
|
// Execute user lambda directly in the current scope to preserve the active scope
|
||||||
// ancestry across suspension points. The lambda still constructs a ClosureScope
|
// ancestry across suspension points. The lambda still constructs a closure scope
|
||||||
// on top of this frame, and parseLambdaExpression sets skipScopeCreation for its body.
|
// on top of this frame, and parseLambdaExpression sets skipScopeCreation for its body.
|
||||||
thisAs<ObjMutex>().mutex.withLock { f.execute(this) }
|
thisAs<ObjMutex>().mutex.withLock { f.callOn(this) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,16 +12,22 @@
|
|||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package net.sergeych.lyng
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import net.sergeych.lyng.obj.Obj
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.Statement
|
||||||
|
|
||||||
interface DeclExecutable {
|
class ObjNativeCallable(
|
||||||
suspend fun execute(scope: Scope): Obj
|
private val fn: suspend Scope.() -> Obj
|
||||||
}
|
) : Obj() {
|
||||||
|
|
||||||
class StatementDeclExecutable(private val delegate: Statement) : DeclExecutable {
|
override val objClass: ObjClass
|
||||||
override suspend fun execute(scope: Scope): Obj = delegate.execute(scope)
|
get() = Statement.type
|
||||||
|
|
||||||
|
override suspend fun callOn(scope: Scope): Obj = scope.fn()
|
||||||
|
|
||||||
|
override fun toString(): String = "NativeCallable@${hashCode()}"
|
||||||
}
|
}
|
||||||
@ -19,7 +19,6 @@ package net.sergeych.lyng.obj
|
|||||||
|
|
||||||
import net.sergeych.lyng.Arguments
|
import net.sergeych.lyng.Arguments
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.Statement
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Property accessor storage. Per instructions, properties do NOT have
|
* Property accessor storage. Per instructions, properties do NOT have
|
||||||
@ -27,8 +26,8 @@ import net.sergeych.lyng.Statement
|
|||||||
*/
|
*/
|
||||||
class ObjProperty(
|
class ObjProperty(
|
||||||
val name: String,
|
val name: String,
|
||||||
val getter: Statement?,
|
val getter: Obj?,
|
||||||
val setter: Statement?
|
val setter: Obj?
|
||||||
) : Obj() {
|
) : Obj() {
|
||||||
|
|
||||||
suspend fun callGetter(scope: Scope, instance: Obj, declaringClass: ObjClass? = null): Obj {
|
suspend fun callGetter(scope: Scope, instance: Obj, declaringClass: ObjClass? = null): Obj {
|
||||||
@ -38,7 +37,7 @@ class ObjProperty(
|
|||||||
val instanceScope = (instance as? ObjInstance)?.instanceScope ?: instance.autoInstanceScope(scope)
|
val instanceScope = (instance as? ObjInstance)?.instanceScope ?: instance.autoInstanceScope(scope)
|
||||||
val execScope = scope.applyClosure(instanceScope).createChildScope(newThisObj = instance)
|
val execScope = scope.applyClosure(instanceScope).createChildScope(newThisObj = instance)
|
||||||
execScope.currentClassCtx = declaringClass
|
execScope.currentClassCtx = declaringClass
|
||||||
return g.execute(execScope)
|
return g.callOn(execScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun callSetter(scope: Scope, instance: Obj, value: Obj, declaringClass: ObjClass? = null) {
|
suspend fun callSetter(scope: Scope, instance: Obj, value: Obj, declaringClass: ObjClass? = null) {
|
||||||
@ -48,7 +47,7 @@ class ObjProperty(
|
|||||||
val instanceScope = (instance as? ObjInstance)?.instanceScope ?: instance.autoInstanceScope(scope)
|
val instanceScope = (instance as? ObjInstance)?.instanceScope ?: instance.autoInstanceScope(scope)
|
||||||
val execScope = scope.applyClosure(instanceScope).createChildScope(args = Arguments(value), newThisObj = instance)
|
val execScope = scope.applyClosure(instanceScope).createChildScope(args = Arguments(value), newThisObj = instance)
|
||||||
execScope.currentClassCtx = declaringClass
|
execScope.currentClassCtx = declaringClass
|
||||||
s.execute(execScope)
|
s.callOn(execScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String = "Property($name)"
|
override fun toString(): String = "Property($name)"
|
||||||
|
|||||||
@ -126,8 +126,8 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
|
|||||||
// roundToInt: number rounded to the nearest integer
|
// roundToInt: number rounded to the nearest integer
|
||||||
addConstDoc(
|
addConstDoc(
|
||||||
name = "roundToInt",
|
name = "roundToInt",
|
||||||
value = statement(Pos.builtIn) {
|
value = ObjNativeCallable {
|
||||||
(it.thisObj as ObjReal).value.roundToLong().toObj()
|
(thisObj as ObjReal).value.roundToLong().toObj()
|
||||||
},
|
},
|
||||||
doc = "This real number rounded to the nearest integer.",
|
doc = "This real number rounded to the nearest integer.",
|
||||||
type = type("lyng.Int"),
|
type = type("lyng.Int"),
|
||||||
|
|||||||
@ -21,7 +21,6 @@ import net.sergeych.lyng.PerfFlags
|
|||||||
import net.sergeych.lyng.Pos
|
import net.sergeych.lyng.Pos
|
||||||
import net.sergeych.lyng.RegexCache
|
import net.sergeych.lyng.RegexCache
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.Statement
|
|
||||||
import net.sergeych.lyng.miniast.*
|
import net.sergeych.lyng.miniast.*
|
||||||
|
|
||||||
class ObjRegex(val regex: Regex) : Obj() {
|
class ObjRegex(val regex: Regex) : Obj() {
|
||||||
@ -76,14 +75,10 @@ class ObjRegex(val regex: Regex) : Obj() {
|
|||||||
}
|
}
|
||||||
createField(
|
createField(
|
||||||
name = "operatorMatch",
|
name = "operatorMatch",
|
||||||
initialValue = object : Statement() {
|
initialValue = ObjNativeCallable {
|
||||||
override val pos: Pos = Pos.builtIn
|
val other = args.firstAndOnly(Pos.builtIn)
|
||||||
|
val targetScope = parent ?: this
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
(thisObj as ObjRegex).operatorMatch(targetScope, other)
|
||||||
val other = scope.args.firstAndOnly(pos)
|
|
||||||
val targetScope = scope.parent ?: scope
|
|
||||||
return (scope.thisObj as ObjRegex).operatorMatch(targetScope, other)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
type = ObjRecord.Type.Fun
|
type = ObjRecord.Type.Fun
|
||||||
)
|
)
|
||||||
|
|||||||
@ -25,7 +25,6 @@ import net.sergeych.lyng.PerfFlags
|
|||||||
import net.sergeych.lyng.Pos
|
import net.sergeych.lyng.Pos
|
||||||
import net.sergeych.lyng.RegexCache
|
import net.sergeych.lyng.RegexCache
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.Statement
|
|
||||||
import net.sergeych.lyng.miniast.*
|
import net.sergeych.lyng.miniast.*
|
||||||
import net.sergeych.lynon.LynonDecoder
|
import net.sergeych.lynon.LynonDecoder
|
||||||
import net.sergeych.lynon.LynonEncoder
|
import net.sergeych.lynon.LynonEncoder
|
||||||
@ -344,14 +343,10 @@ data class ObjString(val value: String) : Obj() {
|
|||||||
name = "re",
|
name = "re",
|
||||||
initialValue = ObjProperty(
|
initialValue = ObjProperty(
|
||||||
name = "re",
|
name = "re",
|
||||||
getter = object : Statement() {
|
getter = ObjNativeCallable {
|
||||||
override val pos: Pos = Pos.builtIn
|
val pattern = (thisObj as ObjString).value
|
||||||
|
val re = if (PerfFlags.REGEX_CACHE) RegexCache.get(pattern) else pattern.toRegex()
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
ObjRegex(re)
|
||||||
val pattern = (scope.thisObj as ObjString).value
|
|
||||||
val re = if (PerfFlags.REGEX_CACHE) RegexCache.get(pattern) else pattern.toRegex()
|
|
||||||
return ObjRegex(re)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
setter = null
|
setter = null
|
||||||
),
|
),
|
||||||
@ -359,14 +354,10 @@ data class ObjString(val value: String) : Obj() {
|
|||||||
)
|
)
|
||||||
createField(
|
createField(
|
||||||
name = "operatorMatch",
|
name = "operatorMatch",
|
||||||
initialValue = object : Statement() {
|
initialValue = ObjNativeCallable {
|
||||||
override val pos: Pos = Pos.builtIn
|
val other = args.firstAndOnly(Pos.builtIn)
|
||||||
|
val targetScope = parent ?: this
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
(thisObj as ObjString).operatorMatch(targetScope, other)
|
||||||
val other = scope.args.firstAndOnly(pos)
|
|
||||||
val targetScope = scope.parent ?: scope
|
|
||||||
return (scope.thisObj as ObjString).operatorMatch(targetScope, other)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
type = ObjRecord.Type.Fun
|
type = ObjRecord.Type.Fun
|
||||||
)
|
)
|
||||||
|
|||||||
@ -70,7 +70,7 @@ object CompileTimeResolver {
|
|||||||
source,
|
source,
|
||||||
importProvider,
|
importProvider,
|
||||||
resolutionSink = collector,
|
resolutionSink = collector,
|
||||||
useBytecodeStatements = false,
|
compileBytecode = false,
|
||||||
allowUnresolvedRefs = true
|
allowUnresolvedRefs = true
|
||||||
)
|
)
|
||||||
return collector.buildReport()
|
return collector.buildReport()
|
||||||
|
|||||||
@ -106,7 +106,7 @@ object LyngLanguageTools {
|
|||||||
request.importProvider,
|
request.importProvider,
|
||||||
miniSink = miniSink,
|
miniSink = miniSink,
|
||||||
resolutionSink = resolutionCollector,
|
resolutionSink = resolutionCollector,
|
||||||
useBytecodeStatements = false,
|
compileBytecode = false,
|
||||||
allowUnresolvedRefs = true,
|
allowUnresolvedRefs = true,
|
||||||
seedScope = request.seedScope
|
seedScope = request.seedScope
|
||||||
)
|
)
|
||||||
|
|||||||
@ -392,7 +392,6 @@ class BytecodeRecentOpsTest {
|
|||||||
val script = Compiler.compileWithResolution(
|
val script = Compiler.compileWithResolution(
|
||||||
Source("<fast-local>", code),
|
Source("<fast-local>", code),
|
||||||
Script.defaultImportManager,
|
Script.defaultImportManager,
|
||||||
useBytecodeStatements = true,
|
|
||||||
useFastLocalRefs = true
|
useFastLocalRefs = true
|
||||||
)
|
)
|
||||||
val result = script.execute(Script.defaultImportManager.newStdScope())
|
val result = script.execute(Script.defaultImportManager.newStdScope())
|
||||||
|
|||||||
@ -38,7 +38,6 @@ class CompileTimeResolutionRuntimeTest {
|
|||||||
val script = Compiler.compileWithResolution(
|
val script = Compiler.compileWithResolution(
|
||||||
Source("<strict-slot>", code),
|
Source("<strict-slot>", code),
|
||||||
Script.defaultImportManager,
|
Script.defaultImportManager,
|
||||||
useBytecodeStatements = false,
|
|
||||||
strictSlotRefs = true
|
strictSlotRefs = true
|
||||||
)
|
)
|
||||||
val result = script.execute(Script.defaultImportManager.newStdScope())
|
val result = script.execute(Script.defaultImportManager.newStdScope())
|
||||||
@ -61,7 +60,6 @@ class CompileTimeResolutionRuntimeTest {
|
|||||||
val script = Compiler.compileWithResolution(
|
val script = Compiler.compileWithResolution(
|
||||||
Source("<shadow-slot>", code),
|
Source("<shadow-slot>", code),
|
||||||
Script.defaultImportManager,
|
Script.defaultImportManager,
|
||||||
useBytecodeStatements = true,
|
|
||||||
strictSlotRefs = true
|
strictSlotRefs = true
|
||||||
)
|
)
|
||||||
val result = script.execute(Script.defaultImportManager.newStdScope())
|
val result = script.execute(Script.defaultImportManager.newStdScope())
|
||||||
@ -85,7 +83,6 @@ class CompileTimeResolutionRuntimeTest {
|
|||||||
val script = Compiler.compileWithResolution(
|
val script = Compiler.compileWithResolution(
|
||||||
Source("<shadow-block>", code),
|
Source("<shadow-block>", code),
|
||||||
Script.defaultImportManager,
|
Script.defaultImportManager,
|
||||||
useBytecodeStatements = true,
|
|
||||||
strictSlotRefs = true
|
strictSlotRefs = true
|
||||||
)
|
)
|
||||||
val result = script.execute(Script.defaultImportManager.newStdScope())
|
val result = script.execute(Script.defaultImportManager.newStdScope())
|
||||||
|
|||||||
@ -45,8 +45,7 @@ class ParamTypeInferenceTest {
|
|||||||
Compiler.compileWithResolution(
|
Compiler.compileWithResolution(
|
||||||
Source("<eval>", code.trimIndent()),
|
Source("<eval>", code.trimIndent()),
|
||||||
Script.defaultImportManager,
|
Script.defaultImportManager,
|
||||||
miniSink = sink,
|
miniSink = sink
|
||||||
useBytecodeStatements = false
|
|
||||||
)
|
)
|
||||||
val mini = sink.build()!!
|
val mini = sink.build()!!
|
||||||
val binding = Binder.bind(code, mini)
|
val binding = Binder.bind(code, mini)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user