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] 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
|
||||
|
||||
- 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 {
|
||||
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@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 {
|
||||
return a.defaultValue?.execute(scope)
|
||||
return a.defaultValue?.callOn(scope)
|
||||
?: 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.
|
||||
* 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(
|
||||
scope: Scope,
|
||||
arguments: Arguments = scope.args,
|
||||
paramSlotPlan: Map<String, Int>,
|
||||
frame: FrameAccess,
|
||||
slotOffset: Int = 0,
|
||||
defaultAccessType: AccessType = AccessType.Var,
|
||||
defaultVisibility: Visibility = Visibility.Public,
|
||||
declaringClass: net.sergeych.lyng.obj.ObjClass? = scope.currentClassCtx
|
||||
slotOffset: Int = 0
|
||||
) {
|
||||
fun slotFor(name: String): Int {
|
||||
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
|
||||
}
|
||||
|
||||
fun ensureScopeRef(a: Item, slot: Int, recordType: ObjRecord.Type) {
|
||||
if (scope.getLocalRecordDirect(a.name) != null) return
|
||||
scope.addItem(
|
||||
a.name,
|
||||
(a.accessType ?: defaultAccessType).isMutable,
|
||||
FrameSlotRef(frame, slot),
|
||||
a.visibility ?: defaultVisibility,
|
||||
recordType = recordType,
|
||||
declaringClass = declaringClass,
|
||||
isTransient = a.isTransient
|
||||
)
|
||||
}
|
||||
|
||||
fun setFrameValue(slot: Int, value: Obj) {
|
||||
when (value) {
|
||||
is net.sergeych.lyng.obj.ObjInt -> frame.setInt(slot, value.value)
|
||||
@ -294,18 +278,12 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
||||
}
|
||||
|
||||
fun assign(a: Item, value: Obj) {
|
||||
val recordType = if (declaringClass != null && a.accessType != null) {
|
||||
ObjRecord.Type.ConstructorField
|
||||
} else {
|
||||
ObjRecord.Type.Argument
|
||||
}
|
||||
val slot = slotFor(a.name)
|
||||
setFrameValue(slot, value.byValueCopy())
|
||||
ensureScopeRef(a, slot, recordType)
|
||||
}
|
||||
|
||||
suspend fun missingValue(a: Item, error: String): Obj {
|
||||
return a.defaultValue?.execute(scope)
|
||||
return a.defaultValue?.callOn(scope)
|
||||
?: 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.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
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.
|
||||
* 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 visibility: Visibility? = null,
|
||||
val isTransient: Boolean = false,
|
||||
|
||||
@ -20,7 +20,7 @@ package net.sergeych.lyng
|
||||
import net.sergeych.lyng.obj.*
|
||||
|
||||
data class ParsedArgument(
|
||||
val value: Statement,
|
||||
val value: Obj,
|
||||
val pos: Pos,
|
||||
val isSplat: Boolean = false,
|
||||
val name: String? = null,
|
||||
@ -40,115 +40,115 @@ data class ParsedArgument(
|
||||
if (!hasSplatOrNamed && count == this.size) {
|
||||
val quick = when (count) {
|
||||
0 -> Arguments.EMPTY
|
||||
1 -> Arguments(listOf(this.elementAt(0).value.execute(scope)), tailBlockMode)
|
||||
1 -> Arguments(listOf(this.elementAt(0).value.callOn(scope)), tailBlockMode)
|
||||
2 -> {
|
||||
val a0 = this.elementAt(0).value.execute(scope)
|
||||
val a1 = this.elementAt(1).value.execute(scope)
|
||||
val a0 = this.elementAt(0).value.callOn(scope)
|
||||
val a1 = this.elementAt(1).value.callOn(scope)
|
||||
Arguments(listOf(a0, a1), tailBlockMode)
|
||||
}
|
||||
3 -> {
|
||||
val a0 = this.elementAt(0).value.execute(scope)
|
||||
val a1 = this.elementAt(1).value.execute(scope)
|
||||
val a2 = this.elementAt(2).value.execute(scope)
|
||||
val a0 = this.elementAt(0).value.callOn(scope)
|
||||
val a1 = this.elementAt(1).value.callOn(scope)
|
||||
val a2 = this.elementAt(2).value.callOn(scope)
|
||||
Arguments(listOf(a0, a1, a2), tailBlockMode)
|
||||
}
|
||||
4 -> {
|
||||
val a0 = this.elementAt(0).value.execute(scope)
|
||||
val a1 = this.elementAt(1).value.execute(scope)
|
||||
val a2 = this.elementAt(2).value.execute(scope)
|
||||
val a3 = this.elementAt(3).value.execute(scope)
|
||||
val a0 = this.elementAt(0).value.callOn(scope)
|
||||
val a1 = this.elementAt(1).value.callOn(scope)
|
||||
val a2 = this.elementAt(2).value.callOn(scope)
|
||||
val a3 = this.elementAt(3).value.callOn(scope)
|
||||
Arguments(listOf(a0, a1, a2, a3), tailBlockMode)
|
||||
}
|
||||
5 -> {
|
||||
val a0 = this.elementAt(0).value.execute(scope)
|
||||
val a1 = this.elementAt(1).value.execute(scope)
|
||||
val a2 = this.elementAt(2).value.execute(scope)
|
||||
val a3 = this.elementAt(3).value.execute(scope)
|
||||
val a4 = this.elementAt(4).value.execute(scope)
|
||||
val a0 = this.elementAt(0).value.callOn(scope)
|
||||
val a1 = this.elementAt(1).value.callOn(scope)
|
||||
val a2 = this.elementAt(2).value.callOn(scope)
|
||||
val a3 = this.elementAt(3).value.callOn(scope)
|
||||
val a4 = this.elementAt(4).value.callOn(scope)
|
||||
Arguments(listOf(a0, a1, a2, a3, a4), tailBlockMode)
|
||||
}
|
||||
6 -> {
|
||||
val a0 = this.elementAt(0).value.execute(scope)
|
||||
val a1 = this.elementAt(1).value.execute(scope)
|
||||
val a2 = this.elementAt(2).value.execute(scope)
|
||||
val a3 = this.elementAt(3).value.execute(scope)
|
||||
val a4 = this.elementAt(4).value.execute(scope)
|
||||
val a5 = this.elementAt(5).value.execute(scope)
|
||||
val a0 = this.elementAt(0).value.callOn(scope)
|
||||
val a1 = this.elementAt(1).value.callOn(scope)
|
||||
val a2 = this.elementAt(2).value.callOn(scope)
|
||||
val a3 = this.elementAt(3).value.callOn(scope)
|
||||
val a4 = this.elementAt(4).value.callOn(scope)
|
||||
val a5 = this.elementAt(5).value.callOn(scope)
|
||||
Arguments(listOf(a0, a1, a2, a3, a4, a5), tailBlockMode)
|
||||
}
|
||||
7 -> {
|
||||
val a0 = this.elementAt(0).value.execute(scope)
|
||||
val a1 = this.elementAt(1).value.execute(scope)
|
||||
val a2 = this.elementAt(2).value.execute(scope)
|
||||
val a3 = this.elementAt(3).value.execute(scope)
|
||||
val a4 = this.elementAt(4).value.execute(scope)
|
||||
val a5 = this.elementAt(5).value.execute(scope)
|
||||
val a6 = this.elementAt(6).value.execute(scope)
|
||||
val a0 = this.elementAt(0).value.callOn(scope)
|
||||
val a1 = this.elementAt(1).value.callOn(scope)
|
||||
val a2 = this.elementAt(2).value.callOn(scope)
|
||||
val a3 = this.elementAt(3).value.callOn(scope)
|
||||
val a4 = this.elementAt(4).value.callOn(scope)
|
||||
val a5 = this.elementAt(5).value.callOn(scope)
|
||||
val a6 = this.elementAt(6).value.callOn(scope)
|
||||
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6), tailBlockMode)
|
||||
}
|
||||
8 -> {
|
||||
val a0 = this.elementAt(0).value.execute(scope)
|
||||
val a1 = this.elementAt(1).value.execute(scope)
|
||||
val a2 = this.elementAt(2).value.execute(scope)
|
||||
val a3 = this.elementAt(3).value.execute(scope)
|
||||
val a4 = this.elementAt(4).value.execute(scope)
|
||||
val a5 = this.elementAt(5).value.execute(scope)
|
||||
val a6 = this.elementAt(6).value.execute(scope)
|
||||
val a7 = this.elementAt(7).value.execute(scope)
|
||||
val a0 = this.elementAt(0).value.callOn(scope)
|
||||
val a1 = this.elementAt(1).value.callOn(scope)
|
||||
val a2 = this.elementAt(2).value.callOn(scope)
|
||||
val a3 = this.elementAt(3).value.callOn(scope)
|
||||
val a4 = this.elementAt(4).value.callOn(scope)
|
||||
val a5 = this.elementAt(5).value.callOn(scope)
|
||||
val a6 = this.elementAt(6).value.callOn(scope)
|
||||
val a7 = this.elementAt(7).value.callOn(scope)
|
||||
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7), tailBlockMode)
|
||||
}
|
||||
9 -> if (PerfFlags.ARG_SMALL_ARITY_12) {
|
||||
val a0 = this.elementAt(0).value.execute(scope)
|
||||
val a1 = this.elementAt(1).value.execute(scope)
|
||||
val a2 = this.elementAt(2).value.execute(scope)
|
||||
val a3 = this.elementAt(3).value.execute(scope)
|
||||
val a4 = this.elementAt(4).value.execute(scope)
|
||||
val a5 = this.elementAt(5).value.execute(scope)
|
||||
val a6 = this.elementAt(6).value.execute(scope)
|
||||
val a7 = this.elementAt(7).value.execute(scope)
|
||||
val a8 = this.elementAt(8).value.execute(scope)
|
||||
val a0 = this.elementAt(0).value.callOn(scope)
|
||||
val a1 = this.elementAt(1).value.callOn(scope)
|
||||
val a2 = this.elementAt(2).value.callOn(scope)
|
||||
val a3 = this.elementAt(3).value.callOn(scope)
|
||||
val a4 = this.elementAt(4).value.callOn(scope)
|
||||
val a5 = this.elementAt(5).value.callOn(scope)
|
||||
val a6 = this.elementAt(6).value.callOn(scope)
|
||||
val a7 = this.elementAt(7).value.callOn(scope)
|
||||
val a8 = this.elementAt(8).value.callOn(scope)
|
||||
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8), tailBlockMode)
|
||||
} else null
|
||||
10 -> if (PerfFlags.ARG_SMALL_ARITY_12) {
|
||||
val a0 = this.elementAt(0).value.execute(scope)
|
||||
val a1 = this.elementAt(1).value.execute(scope)
|
||||
val a2 = this.elementAt(2).value.execute(scope)
|
||||
val a3 = this.elementAt(3).value.execute(scope)
|
||||
val a4 = this.elementAt(4).value.execute(scope)
|
||||
val a5 = this.elementAt(5).value.execute(scope)
|
||||
val a6 = this.elementAt(6).value.execute(scope)
|
||||
val a7 = this.elementAt(7).value.execute(scope)
|
||||
val a8 = this.elementAt(8).value.execute(scope)
|
||||
val a9 = this.elementAt(9).value.execute(scope)
|
||||
val a0 = this.elementAt(0).value.callOn(scope)
|
||||
val a1 = this.elementAt(1).value.callOn(scope)
|
||||
val a2 = this.elementAt(2).value.callOn(scope)
|
||||
val a3 = this.elementAt(3).value.callOn(scope)
|
||||
val a4 = this.elementAt(4).value.callOn(scope)
|
||||
val a5 = this.elementAt(5).value.callOn(scope)
|
||||
val a6 = this.elementAt(6).value.callOn(scope)
|
||||
val a7 = this.elementAt(7).value.callOn(scope)
|
||||
val a8 = this.elementAt(8).value.callOn(scope)
|
||||
val a9 = this.elementAt(9).value.callOn(scope)
|
||||
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9), tailBlockMode)
|
||||
} else null
|
||||
11 -> if (PerfFlags.ARG_SMALL_ARITY_12) {
|
||||
val a0 = this.elementAt(0).value.execute(scope)
|
||||
val a1 = this.elementAt(1).value.execute(scope)
|
||||
val a2 = this.elementAt(2).value.execute(scope)
|
||||
val a3 = this.elementAt(3).value.execute(scope)
|
||||
val a4 = this.elementAt(4).value.execute(scope)
|
||||
val a5 = this.elementAt(5).value.execute(scope)
|
||||
val a6 = this.elementAt(6).value.execute(scope)
|
||||
val a7 = this.elementAt(7).value.execute(scope)
|
||||
val a8 = this.elementAt(8).value.execute(scope)
|
||||
val a9 = this.elementAt(9).value.execute(scope)
|
||||
val a10 = this.elementAt(10).value.execute(scope)
|
||||
val a0 = this.elementAt(0).value.callOn(scope)
|
||||
val a1 = this.elementAt(1).value.callOn(scope)
|
||||
val a2 = this.elementAt(2).value.callOn(scope)
|
||||
val a3 = this.elementAt(3).value.callOn(scope)
|
||||
val a4 = this.elementAt(4).value.callOn(scope)
|
||||
val a5 = this.elementAt(5).value.callOn(scope)
|
||||
val a6 = this.elementAt(6).value.callOn(scope)
|
||||
val a7 = this.elementAt(7).value.callOn(scope)
|
||||
val a8 = this.elementAt(8).value.callOn(scope)
|
||||
val a9 = this.elementAt(9).value.callOn(scope)
|
||||
val a10 = this.elementAt(10).value.callOn(scope)
|
||||
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10), tailBlockMode)
|
||||
} else null
|
||||
12 -> if (PerfFlags.ARG_SMALL_ARITY_12) {
|
||||
val a0 = this.elementAt(0).value.execute(scope)
|
||||
val a1 = this.elementAt(1).value.execute(scope)
|
||||
val a2 = this.elementAt(2).value.execute(scope)
|
||||
val a3 = this.elementAt(3).value.execute(scope)
|
||||
val a4 = this.elementAt(4).value.execute(scope)
|
||||
val a5 = this.elementAt(5).value.execute(scope)
|
||||
val a6 = this.elementAt(6).value.execute(scope)
|
||||
val a7 = this.elementAt(7).value.execute(scope)
|
||||
val a8 = this.elementAt(8).value.execute(scope)
|
||||
val a9 = this.elementAt(9).value.execute(scope)
|
||||
val a10 = this.elementAt(10).value.execute(scope)
|
||||
val a11 = this.elementAt(11).value.execute(scope)
|
||||
val a0 = this.elementAt(0).value.callOn(scope)
|
||||
val a1 = this.elementAt(1).value.callOn(scope)
|
||||
val a2 = this.elementAt(2).value.callOn(scope)
|
||||
val a3 = this.elementAt(3).value.callOn(scope)
|
||||
val a4 = this.elementAt(4).value.callOn(scope)
|
||||
val a5 = this.elementAt(5).value.callOn(scope)
|
||||
val a6 = this.elementAt(6).value.callOn(scope)
|
||||
val a7 = this.elementAt(7).value.callOn(scope)
|
||||
val a8 = this.elementAt(8).value.callOn(scope)
|
||||
val a9 = this.elementAt(9).value.callOn(scope)
|
||||
val a10 = this.elementAt(10).value.callOn(scope)
|
||||
val a11 = this.elementAt(11).value.callOn(scope)
|
||||
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11), tailBlockMode)
|
||||
} else null
|
||||
else -> null
|
||||
@ -166,12 +166,12 @@ data class ParsedArgument(
|
||||
// Named argument
|
||||
if (named == null) named = linkedMapOf()
|
||||
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
|
||||
namedSeen = true
|
||||
continue
|
||||
}
|
||||
val value = x.value.execute(scope)
|
||||
val value = x.value.callOn(scope)
|
||||
if (x.isSplat) {
|
||||
when {
|
||||
// IMPORTANT: handle ObjMap BEFORE generic Iterable to ensure map splats
|
||||
|
||||
@ -32,32 +32,14 @@ class BlockStatement(
|
||||
if (slotPlan.isNotEmpty()) target.applySlotPlan(slotPlan)
|
||||
if (captureSlots.isNotEmpty()) {
|
||||
val captureRecords = scope.captureRecords
|
||||
if (captureRecords != null) {
|
||||
for (i in captureSlots.indices) {
|
||||
val capture = captureSlots[i]
|
||||
val rec = captureRecords.getOrNull(i)
|
||||
?: scope.raiseSymbolNotFound("capture ${capture.name} not found")
|
||||
target.updateSlotFor(capture.name, rec)
|
||||
}
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
if (captureRecords == null) {
|
||||
scope.raiseIllegalState("missing bytecode capture records")
|
||||
}
|
||||
for (i in captureSlots.indices) {
|
||||
val capture = captureSlots[i]
|
||||
val rec = captureRecords.getOrNull(i)
|
||||
?: scope.raiseSymbolNotFound("capture ${capture.name} not found")
|
||||
target.updateSlotFor(capture.name, rec)
|
||||
}
|
||||
}
|
||||
return block.execute(target)
|
||||
|
||||
@ -19,4 +19,6 @@ package net.sergeych.lyng
|
||||
|
||||
data class CaptureSlot(
|
||||
val name: String,
|
||||
val ownerScopeId: Int? = null,
|
||||
val ownerSlot: Int? = null,
|
||||
)
|
||||
|
||||
@ -45,7 +45,12 @@ data class ClassDeclSpec(
|
||||
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) {
|
||||
val parentClasses = spec.baseSpecs.map { baseSpec ->
|
||||
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)
|
||||
if (!bodyCaptureRecords.isNullOrEmpty() && !bodyCaptureNames.isNullOrEmpty()) {
|
||||
classScope.captureRecords = bodyCaptureRecords
|
||||
classScope.captureNames = bodyCaptureNames
|
||||
}
|
||||
classScope.currentClassCtx = newClass
|
||||
newClass.classScope = classScope
|
||||
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) }
|
||||
val classScope = scope.createChildScope(newThisObj = newClass)
|
||||
if (!bodyCaptureRecords.isNullOrEmpty() && !bodyCaptureNames.isNullOrEmpty()) {
|
||||
classScope.captureRecords = bodyCaptureRecords
|
||||
classScope.captureNames = bodyCaptureNames
|
||||
}
|
||||
classScope.currentClassCtx = newClass
|
||||
newClass.classScope = 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
|
||||
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjClass
|
||||
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
|
||||
* 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(
|
||||
val callScope: Scope,
|
||||
@ -118,7 +54,7 @@ class ApplyScope(val callScope: Scope, val applied: 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(
|
||||
var closure: Scope? = null,
|
||||
var captureContext: Scope? = null,
|
||||
var captureRecords: List<ObjRecord>? = null,
|
||||
)
|
||||
|
||||
data class FunctionDeclSpec(
|
||||
@ -40,6 +41,7 @@ data class FunctionDeclSpec(
|
||||
val isTransient: Boolean,
|
||||
val isDelegated: Boolean,
|
||||
val delegateExpression: Statement?,
|
||||
val delegateInitStatement: Statement?,
|
||||
val extTypeName: String?,
|
||||
val extensionWrapperName: String?,
|
||||
val memberMethodId: Int?,
|
||||
@ -50,10 +52,17 @@ data class FunctionDeclSpec(
|
||||
val fnBody: Statement,
|
||||
val closureBox: FunctionClosureBox,
|
||||
val captureSlots: List<CaptureSlot>,
|
||||
val slotIndex: Int?,
|
||||
val scopeId: Int?,
|
||||
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) {
|
||||
val existing = scope.get(spec.name)
|
||||
if (existing != null) {
|
||||
@ -88,8 +97,8 @@ internal suspend fun executeFunctionDecl(scope: Scope, spec: FunctionDeclSpec):
|
||||
delegate = finalDelegate
|
||||
}
|
||||
)
|
||||
return ObjVoid
|
||||
}
|
||||
return ObjVoid
|
||||
}
|
||||
|
||||
val th = scope.thisObj
|
||||
if (spec.isStatic) {
|
||||
@ -117,7 +126,6 @@ internal suspend fun executeFunctionDecl(scope: Scope, spec: FunctionDeclSpec):
|
||||
}
|
||||
} else if (th is ObjClass) {
|
||||
val cls: ObjClass = th
|
||||
val storageName = "${cls.className}::${spec.name}"
|
||||
cls.createField(
|
||||
spec.name,
|
||||
ObjUnset,
|
||||
@ -133,33 +141,9 @@ internal suspend fun executeFunctionDecl(scope: Scope, spec: FunctionDeclSpec):
|
||||
type = ObjRecord.Type.Delegated,
|
||||
methodId = spec.memberMethodId
|
||||
)
|
||||
cls.instanceInitializers += object : Statement() {
|
||||
override val pos: Pos = spec.startPos
|
||||
override suspend fun execute(scp: Scope): Obj {
|
||||
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
|
||||
}
|
||||
}
|
||||
val initStmt = spec.delegateInitStatement
|
||||
?: scope.raiseIllegalState("missing delegated init statement for ${spec.name}")
|
||||
cls.instanceInitializers += initStmt
|
||||
} else {
|
||||
scope.addItem(
|
||||
spec.name,
|
||||
@ -178,7 +162,7 @@ internal suspend fun executeFunctionDecl(scope: Scope, spec: FunctionDeclSpec):
|
||||
if (spec.isStatic || !spec.parentIsClassBody) {
|
||||
spec.closureBox.closure = scope
|
||||
}
|
||||
if (spec.parentIsClassBody && spec.captureSlots.isNotEmpty()) {
|
||||
if (spec.parentIsClassBody) {
|
||||
spec.closureBox.captureContext = scope
|
||||
}
|
||||
|
||||
@ -188,25 +172,18 @@ internal suspend fun executeFunctionDecl(scope: Scope, spec: FunctionDeclSpec):
|
||||
spec.extTypeName?.let { typeName ->
|
||||
val type = scope[typeName]?.value ?: scope.raiseSymbolNotFound("class $typeName not found")
|
||||
if (type !is ObjClass) scope.raiseClassCastError("$typeName is not the class instance")
|
||||
val stmt = object : Statement() {
|
||||
override val pos: Pos = spec.startPos
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
val result = (scope.thisObj as? ObjInstance)?.let { i ->
|
||||
val execScope = if (compiledFnBody is net.sergeych.lyng.bytecode.BytecodeStatement) {
|
||||
scope.applyClosureForBytecode(i.instanceScope).also {
|
||||
it.args = scope.args
|
||||
}
|
||||
} else {
|
||||
ClosureScope(scope, i.instanceScope)
|
||||
}
|
||||
compiledFnBody.execute(execScope)
|
||||
} ?: compiledFnBody.execute(scope.thisObj.autoInstanceScope(scope))
|
||||
return result
|
||||
}
|
||||
val callable = net.sergeych.lyng.obj.ObjNativeCallable {
|
||||
val result = (thisObj as? ObjInstance)?.let { i ->
|
||||
val execScope = applyClosureForBytecode(i.instanceScope).also {
|
||||
it.args = args
|
||||
}
|
||||
compiledFnBody.execute(execScope)
|
||||
} ?: compiledFnBody.execute(thisObj.autoInstanceScope(this))
|
||||
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 wrapper = ObjExtensionMethodCallable(spec.name, stmt)
|
||||
val wrapper = ObjExtensionMethodCallable(spec.name, callable)
|
||||
scope.addItem(wrapperName, false, wrapper, spec.visibility, recordType = ObjRecord.Type.Fun)
|
||||
} ?: run {
|
||||
val th = scope.thisObj
|
||||
@ -238,7 +215,8 @@ internal suspend fun executeFunctionDecl(scope: Scope, spec: FunctionDeclSpec):
|
||||
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
|
||||
} else {
|
||||
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:
|
||||
*
|
||||
* - [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(
|
||||
var parent: Scope?,
|
||||
@ -76,11 +76,19 @@ open class Scope(
|
||||
|
||||
internal fun setThisVariants(primary: Obj, extras: List<Obj>) {
|
||||
thisObj = primary
|
||||
thisVariants.clear()
|
||||
thisVariants.add(primary)
|
||||
for (obj in extras) {
|
||||
if (obj !== primary && !thisVariants.contains(obj)) {
|
||||
thisVariants.add(obj)
|
||||
val extrasSnapshot = when {
|
||||
extras.isEmpty() -> emptyList()
|
||||
extras === thisVariants -> extras.toList()
|
||||
extras is MutableList<*> -> synchronized(extras) { extras.toList() }
|
||||
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) {
|
||||
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 = s.parent
|
||||
@ -122,7 +130,7 @@ open class Scope(
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
while (s != null && hops++ < 1024) {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
* virtual dispatch. This checks:
|
||||
* - 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
|
||||
*/
|
||||
internal fun baseGetIgnoreClosure(name: String): ObjRecord? {
|
||||
@ -211,7 +212,7 @@ open class Scope(
|
||||
* - locals/bindings of each frame
|
||||
* - then instance/class members of each frame's `thisObj`.
|
||||
* 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? {
|
||||
var s: Scope? = this
|
||||
@ -228,7 +229,7 @@ open class Scope(
|
||||
} 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
|
||||
}
|
||||
@ -635,11 +636,6 @@ open class Scope(
|
||||
it.value = value
|
||||
// keep local binding index consistent within the frame
|
||||
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)
|
||||
it
|
||||
} ?: 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
|
||||
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)
|
||||
if (nameToSlot.isEmpty()) {
|
||||
allocateSlotFor(name, rec)
|
||||
@ -751,12 +734,7 @@ open class Scope(
|
||||
}
|
||||
|
||||
fun addFn(vararg names: String, callSignature: CallSignature? = null, fn: suspend Scope.() -> Obj) {
|
||||
val newFn = object : Statement() {
|
||||
override val pos: Pos = Pos.builtIn
|
||||
|
||||
override suspend fun execute(scope: Scope): Obj = scope.fn()
|
||||
|
||||
}
|
||||
val newFn = net.sergeych.lyng.obj.ObjNativeCallable { fn() }
|
||||
for (name in names) {
|
||||
addItem(
|
||||
name,
|
||||
@ -834,7 +812,7 @@ open class 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 {
|
||||
return BytecodeClosureScope(this, closure, preferredThisType)
|
||||
|
||||
@ -40,6 +40,7 @@ class Script(
|
||||
private val moduleBytecode: CmdFunction? = null,
|
||||
// private val catchReturn: Boolean = false,
|
||||
) : Statement() {
|
||||
fun statements(): List<Statement> = statements
|
||||
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
scope.pos = pos
|
||||
@ -86,11 +87,10 @@ class Script(
|
||||
moduleBytecode?.let { fn ->
|
||||
return CmdVm().execute(fn, scope, scope.args)
|
||||
}
|
||||
var lastResult: Obj = ObjVoid
|
||||
for (s in statements) {
|
||||
lastResult = s.execute(scope)
|
||||
if (statements.isNotEmpty()) {
|
||||
scope.raiseIllegalState("interpreter execution is not supported; missing module bytecode")
|
||||
}
|
||||
return lastResult
|
||||
return ObjVoid
|
||||
}
|
||||
|
||||
private suspend fun seedModuleSlots(scope: Scope) {
|
||||
@ -332,7 +332,7 @@ class Script(
|
||||
addVoidFn("assert") {
|
||||
val cond = requiredArg<ObjBool>(0)
|
||||
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 ""
|
||||
if (!cond.value == true)
|
||||
raiseError(ObjAssertionFailedException(this, "Assertion failed$message"))
|
||||
@ -385,23 +385,23 @@ class Script(
|
||||
will be accepted.
|
||||
""".trimIndent()
|
||||
) {
|
||||
val code: Statement
|
||||
val code: Obj
|
||||
val expectedClass: ObjClass?
|
||||
when (args.size) {
|
||||
1 -> {
|
||||
code = requiredArg<Statement>(0)
|
||||
code = requiredArg<Obj>(0)
|
||||
expectedClass = null
|
||||
}
|
||||
|
||||
2 -> {
|
||||
code = requiredArg<Statement>(1)
|
||||
code = requiredArg<Obj>(1)
|
||||
expectedClass = requiredArg<ObjClass>(0)
|
||||
}
|
||||
|
||||
else -> raiseIllegalArgument("Expected 1 or 2 arguments, got ${args.size}")
|
||||
}
|
||||
val result = try {
|
||||
code.execute(this)
|
||||
code.callOn(this)
|
||||
null
|
||||
} catch (e: ExecutionError) {
|
||||
e.errorObject
|
||||
@ -441,7 +441,7 @@ class Script(
|
||||
val condition = requiredArg<ObjBool>(0)
|
||||
if (!condition.value) {
|
||||
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")
|
||||
}
|
||||
ObjVoid
|
||||
@ -450,7 +450,7 @@ class Script(
|
||||
val condition = requiredArg<ObjBool>(0)
|
||||
if (!condition.value) {
|
||||
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")
|
||||
}
|
||||
ObjVoid
|
||||
@ -460,27 +460,23 @@ class Script(
|
||||
ObjVoid
|
||||
}
|
||||
addFn("run") {
|
||||
requireOnlyArg<Statement>().execute(this)
|
||||
requireOnlyArg<Obj>().callOn(this)
|
||||
}
|
||||
addFn("cached") {
|
||||
val builder = requireOnlyArg<Statement>()
|
||||
val builder = requireOnlyArg<Obj>()
|
||||
val capturedScope = this
|
||||
var calculated = false
|
||||
var cachedValue: Obj = ObjVoid
|
||||
val thunk = object : Statement() {
|
||||
override val pos: Pos = Pos.builtIn
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
if (!calculated) {
|
||||
cachedValue = builder.execute(capturedScope)
|
||||
calculated = true
|
||||
}
|
||||
return cachedValue
|
||||
ObjNativeCallable {
|
||||
if (!calculated) {
|
||||
cachedValue = builder.callOn(capturedScope)
|
||||
calculated = true
|
||||
}
|
||||
cachedValue
|
||||
}
|
||||
thunk
|
||||
}
|
||||
addFn("lazy") {
|
||||
val builder = requireOnlyArg<Statement>()
|
||||
val builder = requireOnlyArg<Obj>()
|
||||
ObjLazyDelegate(builder, this)
|
||||
}
|
||||
addVoidFn("delay") {
|
||||
@ -527,9 +523,9 @@ class Script(
|
||||
addConst("MapEntry", ObjMapEntry.type)
|
||||
|
||||
addFn("launch") {
|
||||
val callable = requireOnlyArg<Statement>()
|
||||
val callable = requireOnlyArg<Obj>()
|
||||
ObjDeferred(globalDefer {
|
||||
callable.execute(this@addFn)
|
||||
callable.callOn(this@addFn)
|
||||
})
|
||||
}
|
||||
|
||||
@ -541,7 +537,7 @@ class Script(
|
||||
addFn("flow", callSignature = CallSignature(tailBlockReceiverType = "FlowBuilder")) {
|
||||
// important is: current context contains closure often used in call;
|
||||
// we'll need it for the producer
|
||||
ObjFlow(requireOnlyArg<Statement>(), this)
|
||||
ObjFlow(requireOnlyArg<Obj>(), this)
|
||||
}
|
||||
|
||||
val pi = ObjReal(PI)
|
||||
|
||||
@ -113,6 +113,96 @@ class BytecodeCompiler(
|
||||
is BlockStatement -> compileBlock(name, stmt)
|
||||
is net.sergeych.lyng.InlineBlockStatement -> compileInlineBlock(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 -> {
|
||||
val value = emitDelegatedVarDecl(stmt) ?: return null
|
||||
builder.emit(Opcode.RET, value.slot)
|
||||
@ -131,6 +221,60 @@ class BytecodeCompiler(
|
||||
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 -> {
|
||||
val value = emitDestructuringVarDecl(stmt) ?: return null
|
||||
builder.emit(Opcode.RET, value.slot)
|
||||
@ -317,6 +461,17 @@ class BytecodeCompiler(
|
||||
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? {
|
||||
return when (ref) {
|
||||
is ConstRef -> compileConst(ref.constValue)
|
||||
@ -2535,15 +2690,7 @@ class BytecodeCompiler(
|
||||
}
|
||||
|
||||
private fun compileFieldRef(ref: FieldRef): CompiledValue? {
|
||||
val receiverClass = resolveReceiverClass(ref.target)
|
||||
?: if (isAllowedObjectMember(ref.name)) {
|
||||
Obj.rootObjectType
|
||||
} else {
|
||||
throw BytecodeCompileException(
|
||||
"Member access requires compile-time receiver type: ${ref.name}",
|
||||
Pos.builtIn
|
||||
)
|
||||
}
|
||||
val receiverClass = resolveReceiverClass(ref.target) ?: ObjDynamic.type
|
||||
if (receiverClass == ObjDynamic.type) {
|
||||
val receiver = compileRefWithFallback(ref.target, null, Pos.builtIn) ?: return null
|
||||
val dst = allocSlot()
|
||||
@ -2625,7 +2772,7 @@ class BytecodeCompiler(
|
||||
}
|
||||
val extSlot = resolveExtensionGetterSlot(receiverClass, ref.name)
|
||||
?: throw BytecodeCompileException(
|
||||
"Unknown member ${ref.name} on ${receiverClass.className}",
|
||||
missingFieldMessage(receiverClass, ref.name),
|
||||
Pos.builtIn
|
||||
)
|
||||
val callee = ensureObjSlot(extSlot)
|
||||
@ -3627,15 +3774,7 @@ class BytecodeCompiler(
|
||||
|
||||
private fun compileMethodCall(ref: MethodCallRef): CompiledValue? {
|
||||
val callPos = callSitePos()
|
||||
val receiverClass = resolveReceiverClass(ref.receiver)
|
||||
?: if (isAllowedObjectMember(ref.name)) {
|
||||
Obj.rootObjectType
|
||||
} else {
|
||||
throw BytecodeCompileException(
|
||||
"Member call requires compile-time receiver type: ${ref.name}",
|
||||
Pos.builtIn
|
||||
)
|
||||
}
|
||||
val receiverClass = resolveReceiverClass(ref.receiver) ?: ObjDynamic.type
|
||||
val receiver = compileRefWithFallback(ref.receiver, null, refPosOrCurrent(ref.receiver)) ?: return null
|
||||
val dst = allocSlot()
|
||||
if (receiverClass == ObjDynamic.type) {
|
||||
@ -3695,6 +3834,41 @@ class BytecodeCompiler(
|
||||
builder.mark(endLabel)
|
||||
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)) {
|
||||
val nameId = builder.addConst(BytecodeConst.StringVal(ref.name))
|
||||
val memberSlot = allocSlot()
|
||||
@ -3725,7 +3899,7 @@ class BytecodeCompiler(
|
||||
}
|
||||
val extSlot = resolveExtensionCallableSlot(receiverClass, ref.name)
|
||||
?: throw BytecodeCompileException(
|
||||
"Unknown member ${ref.name} on ${receiverClass.className}",
|
||||
missingMemberMessage(receiverClass, ref.name),
|
||||
Pos.builtIn
|
||||
)
|
||||
val callee = ensureObjSlot(extSlot)
|
||||
@ -3932,15 +4106,16 @@ class BytecodeCompiler(
|
||||
return CallArgs(base = argSlots[0], count = argSlots.size, planId = planId)
|
||||
}
|
||||
|
||||
private fun compileArgValue(stmt: Statement): CompiledValue? {
|
||||
return when (stmt) {
|
||||
is ExpressionStatement -> compileRefWithFallback(stmt.ref, null, stmt.pos)
|
||||
else -> {
|
||||
private fun compileArgValue(value: Obj): CompiledValue? {
|
||||
return when (value) {
|
||||
is ExpressionStatement -> compileRefWithFallback(value.ref, null, value.pos)
|
||||
is Statement -> {
|
||||
throw BytecodeCompileException(
|
||||
"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 {
|
||||
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)
|
||||
updateSlotType(dst, SlotType.OBJ)
|
||||
return CompiledValue(dst, SlotType.OBJ)
|
||||
@ -4151,6 +4331,190 @@ class BytecodeCompiler(
|
||||
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? {
|
||||
val target = if (stmt is BytecodeStatement) stmt.original else stmt
|
||||
setPos(target.pos)
|
||||
@ -4181,8 +4545,16 @@ class BytecodeCompiler(
|
||||
}
|
||||
is BlockStatement -> emitBlock(target, true)
|
||||
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 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.ClassDeclStatement -> emitDeclClass(target)
|
||||
is net.sergeych.lyng.FunctionDeclStatement -> emitDeclFunction(target)
|
||||
@ -4215,7 +4587,15 @@ class BytecodeCompiler(
|
||||
}
|
||||
}
|
||||
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 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 net.sergeych.lyng.ClassDeclStatement -> emitDeclClass(target)
|
||||
is net.sergeych.lyng.FunctionDeclStatement -> emitDeclFunction(target)
|
||||
@ -5858,6 +6238,11 @@ class BytecodeCompiler(
|
||||
private fun resolveTypeRefClass(ref: ObjRef): ObjClass? {
|
||||
return when (ref) {
|
||||
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 LocalVarRef -> 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) }
|
||||
}
|
||||
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 -> {
|
||||
val slotIndex = stmt.slotIndex
|
||||
val scopeId = stmt.scopeId ?: 0
|
||||
@ -6399,6 +6803,15 @@ class BytecodeCompiler(
|
||||
is net.sergeych.lyng.ReturnStatement -> {
|
||||
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 -> {
|
||||
collectScopeSlots(stmt.throwExpr)
|
||||
}
|
||||
@ -6466,7 +6879,11 @@ class BytecodeCompiler(
|
||||
}
|
||||
|
||||
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 (allowedScopeNames == null || name == null) return true
|
||||
return allowedScopeNames.contains(name)
|
||||
|
||||
@ -75,6 +75,95 @@ sealed class BytecodeConst {
|
||||
val visibility: Visibility,
|
||||
val isTransient: Boolean,
|
||||
) : 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(
|
||||
val pattern: ListLiteralRef,
|
||||
val names: List<String>,
|
||||
|
||||
@ -144,6 +144,21 @@ class BytecodeStatement private constructor(
|
||||
is net.sergeych.lyng.ClassDeclStatement -> false
|
||||
is net.sergeych.lyng.FunctionDeclStatement -> 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 -> {
|
||||
containsUnsupportedStatement(target.body) ||
|
||||
target.catches.any { containsUnsupportedStatement(it.block) } ||
|
||||
@ -266,6 +281,115 @@ class BytecodeStatement private constructor(
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,7 +161,10 @@ class CmdBuilder {
|
||||
Opcode.PUSH_TRY ->
|
||||
listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP)
|
||||
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 ->
|
||||
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||
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_FUNCTION -> CmdDeclFunction(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.CALL_DIRECT -> CmdCallDirect(operands[0], operands[1], operands[2], operands[3])
|
||||
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 CmdDeclFunction -> Opcode.DECL_FUNCTION 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 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)
|
||||
@ -287,7 +296,10 @@ object CmdDisassembler {
|
||||
Opcode.PUSH_TRY ->
|
||||
listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP)
|
||||
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 ->
|
||||
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||
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() {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
class CmdMoveInt(internal val src: Int, internal val dst: Int) : Cmd() {
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
class CmdMoveBool(internal val src: Int, internal val dst: Int) : Cmd() {
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -1302,17 +1342,23 @@ class CmdClearPendingThrowable : Cmd() {
|
||||
|
||||
class CmdDeclLocal(internal val constId: Int, internal val slot: Int) : Cmd() {
|
||||
override suspend fun perform(frame: CmdFrame) {
|
||||
val decl = frame.fn.constants[constId] as? BytecodeConst.LocalDecl
|
||||
?: error("DECL_LOCAL expects LocalDecl at $constId")
|
||||
val value = frame.slotToObj(slot).byValueCopy()
|
||||
frame.ensureScope().addItem(
|
||||
decl.name,
|
||||
decl.isMutable,
|
||||
value,
|
||||
decl.visibility,
|
||||
recordType = ObjRecord.Type.Other,
|
||||
isTransient = decl.isTransient
|
||||
)
|
||||
if (slot < frame.fn.scopeSlotCount) {
|
||||
val decl = frame.fn.constants[constId] as? BytecodeConst.LocalDecl
|
||||
?: error("DECL_LOCAL expects LocalDecl at $constId")
|
||||
val target = frame.scopeTarget(slot)
|
||||
frame.ensureScopeSlot(target, slot)
|
||||
val value = frame.slotToObj(slot).byValueCopy()
|
||||
target.updateSlotFor(
|
||||
decl.name,
|
||||
ObjRecord(
|
||||
value,
|
||||
decl.isMutable,
|
||||
decl.visibility,
|
||||
isTransient = decl.isTransient,
|
||||
type = ObjRecord.Type.Other
|
||||
)
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -1332,16 +1378,21 @@ class CmdDeclDelegated(internal val constId: Int, internal val slot: Int) : Cmd(
|
||||
} catch (_: Exception) {
|
||||
initValue
|
||||
}
|
||||
val rec = frame.ensureScope().addItem(
|
||||
decl.name,
|
||||
decl.isMutable,
|
||||
ObjNull,
|
||||
decl.visibility,
|
||||
recordType = ObjRecord.Type.Delegated,
|
||||
isTransient = decl.isTransient
|
||||
)
|
||||
rec.delegate = finalDelegate
|
||||
frame.storeObjResult(slot, finalDelegate)
|
||||
if (slot < frame.fn.scopeSlotCount) {
|
||||
val target = frame.scopeTarget(slot)
|
||||
frame.ensureScopeSlot(target, slot)
|
||||
target.updateSlotFor(
|
||||
decl.name,
|
||||
ObjRecord(
|
||||
ObjNull,
|
||||
decl.isMutable,
|
||||
decl.visibility,
|
||||
isTransient = decl.isTransient,
|
||||
type = ObjRecord.Type.Delegated
|
||||
).also { it.delegate = finalDelegate }
|
||||
)
|
||||
}
|
||||
frame.setObjUnchecked(slot, finalDelegate)
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -1370,18 +1421,387 @@ class CmdDeclFunction(internal val constId: Int, internal val slot: Int) : Cmd()
|
||||
override suspend fun perform(frame: CmdFrame) {
|
||||
val decl = frame.fn.constants[constId] as? BytecodeConst.FunctionDecl
|
||||
?: error("DECL_FUNCTION expects FunctionDecl at $constId")
|
||||
val result = executeFunctionDecl(frame.ensureScope(), decl.spec)
|
||||
frame.storeObjResult(slot, result)
|
||||
val captureNames = captureNamesForFunctionDecl(decl.spec)
|
||||
val captureRecords = buildFunctionCaptureRecords(frame, captureNames)
|
||||
val result = executeFunctionDecl(frame.ensureScope(), decl.spec, captureRecords)
|
||||
frame.setObjUnchecked(slot, result)
|
||||
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() {
|
||||
override suspend fun perform(frame: CmdFrame) {
|
||||
val decl = frame.fn.constants[constId] as? BytecodeConst.ClassDecl
|
||||
?: error("DECL_CLASS expects ClassDecl at $constId")
|
||||
val result = executeClassDecl(frame.ensureScope(), decl.spec)
|
||||
frame.storeObjResult(slot, result)
|
||||
val bodyCaptureNames = mergeCaptureNames(
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -1504,33 +1924,6 @@ private fun resolveLocalSlotIndex(fn: CmdFunction, name: String, preferCapture:
|
||||
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() {
|
||||
override suspend fun perform(frame: CmdFrame) {
|
||||
val decl = frame.fn.constants[constId] as? BytecodeConst.ExtensionPropertyDecl
|
||||
@ -1578,7 +1971,7 @@ class CmdCallDirect(
|
||||
val args = frame.buildArguments(argBase, argCount)
|
||||
if (callee is Statement) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
@ -1619,7 +2012,7 @@ class CmdCallSlot(
|
||||
val scope = frame.ensureScope()
|
||||
if (callee is Statement) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
@ -1897,18 +2290,26 @@ class CmdCallMemberSlot(
|
||||
frame.storeObjResult(dst, result)
|
||||
return
|
||||
}
|
||||
val scope = frame.ensureScope()
|
||||
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) {
|
||||
ObjRecord.Type.Property -> {
|
||||
if (callArgs.isEmpty()) (rec.value as ObjProperty).callGetter(frame.ensureScope(), receiver, decl)
|
||||
else frame.ensureScope().raiseError("property $name cannot be called with arguments")
|
||||
if (callArgs.isEmpty()) (rec.value as ObjProperty).callGetter(scope, receiver, decl)
|
||||
else scope.raiseError("property $name cannot be called with arguments")
|
||||
}
|
||||
ObjRecord.Type.Fun -> {
|
||||
val callScope = inst?.instanceScope ?: frame.ensureScope()
|
||||
val callScope = inst?.instanceScope ?: scope
|
||||
rec.value.invoke(callScope, receiver, callArgs, decl)
|
||||
}
|
||||
ObjRecord.Type.Delegated -> {
|
||||
val scope = frame.ensureScope()
|
||||
val delegate = when (receiver) {
|
||||
is ObjInstance -> {
|
||||
val storageName = decl.mangledName(name)
|
||||
@ -2002,7 +2403,6 @@ class BytecodeLambdaCallable(
|
||||
val context = scope.applyClosureForBytecode(closureScope, preferredThisType).also {
|
||||
it.args = scope.args
|
||||
}
|
||||
if (paramSlotPlan.isNotEmpty()) context.applySlotPlan(paramSlotPlan)
|
||||
if (captureRecords != null) {
|
||||
context.captureRecords = captureRecords
|
||||
context.captureNames = captureNames
|
||||
@ -2027,19 +2427,13 @@ class BytecodeLambdaCallable(
|
||||
val itSlot = slotPlan["it"]
|
||||
if (itSlot != null) {
|
||||
frame.frame.setObj(itSlot, itValue)
|
||||
if (context.getLocalRecordDirect("it") == null) {
|
||||
context.addItem("it", false, FrameSlotRef(frame.frame, itSlot), recordType = ObjRecord.Type.Argument)
|
||||
}
|
||||
} else if (context.getLocalRecordDirect("it") == null) {
|
||||
context.addItem("it", false, itValue, recordType = ObjRecord.Type.Argument)
|
||||
}
|
||||
} else {
|
||||
argsDeclaration.assignToFrame(
|
||||
context,
|
||||
arguments,
|
||||
slotPlan,
|
||||
frame.frame,
|
||||
defaultAccessType = AccessType.Val
|
||||
frame.frame
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -2235,9 +2629,7 @@ class CmdFrame(
|
||||
if (!visited.add(current)) continue
|
||||
if (current.getSlotIndexOf(slotName) != null) return current
|
||||
current.parent?.let { queue.add(it) }
|
||||
if (current is ClosureScope) {
|
||||
queue.add(current.closureScope)
|
||||
} else if (current is BytecodeClosureScope) {
|
||||
if (current is BytecodeClosureScope) {
|
||||
queue.add(current.closureScope)
|
||||
} else if (current is ApplyScope) {
|
||||
queue.add(current.applied)
|
||||
@ -2255,9 +2647,7 @@ class CmdFrame(
|
||||
if (!visited.add(current)) continue
|
||||
if (current.getLocalRecordDirect(name) != null) return current
|
||||
current.parent?.let { queue.add(it) }
|
||||
if (current is ClosureScope) {
|
||||
queue.add(current.closureScope)
|
||||
} else if (current is BytecodeClosureScope) {
|
||||
if (current is BytecodeClosureScope) {
|
||||
queue.add(current.closureScope)
|
||||
} else if (current is ApplyScope) {
|
||||
queue.add(current.applied)
|
||||
@ -2276,9 +2666,7 @@ class CmdFrame(
|
||||
if (current is ModuleScope) return current
|
||||
if (current.parent is ModuleScope) return current
|
||||
current.parent?.let { queue.add(it) }
|
||||
if (current is ClosureScope) {
|
||||
queue.add(current.closureScope)
|
||||
} else if (current is BytecodeClosureScope) {
|
||||
if (current is BytecodeClosureScope) {
|
||||
queue.add(current.closureScope)
|
||||
} else if (current is ApplyScope) {
|
||||
queue.add(current.applied)
|
||||
@ -2360,7 +2748,7 @@ class CmdFrame(
|
||||
|
||||
fun pushScope(plan: Map<String, Int>, captures: List<String>) {
|
||||
if (scope.skipScopeCreation) {
|
||||
val snapshot = scope.applySlotPlanWithSnapshot(plan)
|
||||
val snapshot = emptyMap<String, Int?>()
|
||||
slotPlanStack.addLast(snapshot)
|
||||
virtualDepth += 1
|
||||
scopeStack.addLast(scope)
|
||||
@ -2369,9 +2757,6 @@ class CmdFrame(
|
||||
scopeStack.addLast(scope)
|
||||
scopeVirtualStack.addLast(false)
|
||||
scope = scope.createChildScope()
|
||||
if (plan.isNotEmpty()) {
|
||||
scope.applySlotPlan(plan)
|
||||
}
|
||||
}
|
||||
captureStack.addLast(captures)
|
||||
scopeDepth += 1
|
||||
@ -2417,11 +2802,8 @@ class CmdFrame(
|
||||
scopeStack.addLast(scope)
|
||||
slotPlanScopeStack.addLast(true)
|
||||
scope = scope.createChildScope()
|
||||
if (plan.isNotEmpty()) {
|
||||
scope.applySlotPlan(plan)
|
||||
}
|
||||
} else {
|
||||
val snapshot = scope.applySlotPlanWithSnapshot(plan)
|
||||
val snapshot = emptyMap<String, Int?>()
|
||||
slotPlanStack.addLast(snapshot)
|
||||
slotPlanScopeStack.addLast(false)
|
||||
virtualDepth += 1
|
||||
@ -2456,9 +2838,15 @@ class CmdFrame(
|
||||
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, value)
|
||||
} else {
|
||||
val localIndex = slot - fn.scopeSlotCount
|
||||
ensureLocalMutable(localIndex)
|
||||
when (val existing = frame.getRawObj(localIndex)) {
|
||||
is FrameSlotRef -> {
|
||||
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 {
|
||||
return if (slot < fn.scopeSlotCount) {
|
||||
getScopeSlotValue(slot).toLong()
|
||||
@ -2498,7 +2895,7 @@ class CmdFrame(
|
||||
|
||||
fun getLocalInt(local: Int): Long = frame.getInt(local)
|
||||
|
||||
fun setInt(slot: Int, value: Long) {
|
||||
fun setIntUnchecked(slot: Int, value: Long) {
|
||||
if (slot < fn.scopeSlotCount) {
|
||||
val target = scopeTarget(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) {
|
||||
frame.setInt(local, value)
|
||||
}
|
||||
@ -2547,6 +2972,34 @@ class CmdFrame(
|
||||
}
|
||||
|
||||
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) {
|
||||
val target = scopeTarget(slot)
|
||||
val index = ensureScopeSlot(target, slot)
|
||||
@ -2593,6 +3046,34 @@ class CmdFrame(
|
||||
fun getLocalBool(local: Int): Boolean = frame.getBool(local)
|
||||
|
||||
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) {
|
||||
val target = scopeTarget(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) {
|
||||
var errorObject = value
|
||||
val throwScope = ensureScope().createChildScope(pos = pos)
|
||||
@ -2889,5 +3380,17 @@ class CmdFrame(
|
||||
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.
|
||||
}
|
||||
|
||||
@ -168,6 +168,15 @@ enum class Opcode(val code: Int) {
|
||||
BIND_DELEGATE_LOCAL(0xC4),
|
||||
DECL_FUNCTION(0xC5),
|
||||
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 {
|
||||
|
||||
@ -447,7 +447,7 @@ open class Obj {
|
||||
caller.members[name]?.let { rec ->
|
||||
if (rec.visibility == Visibility.Private && !rec.isAbstract) {
|
||||
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
|
||||
}
|
||||
@ -461,7 +461,7 @@ open class Obj {
|
||||
if (rec != null && !rec.isAbstract) {
|
||||
val decl = rec.declaringClass ?: cls
|
||||
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
|
||||
}
|
||||
@ -476,7 +476,7 @@ open class Obj {
|
||||
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 ?: "?"})"))
|
||||
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
|
||||
}
|
||||
@ -500,16 +500,17 @@ open class Obj {
|
||||
if (obj.type == ObjRecord.Type.Delegated) {
|
||||
val del = obj.delegate ?: scope.raiseError("Internal error: delegated property $name has no delegate")
|
||||
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") {
|
||||
val wrapper = object : Statement() {
|
||||
override val pos: Pos = Pos.builtIn
|
||||
|
||||
override suspend fun execute(s: Scope): Obj {
|
||||
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))
|
||||
}
|
||||
val wrapper = ObjNativeCallable {
|
||||
val th2 = if (thisObj === ObjVoid) ObjNull else thisObj
|
||||
val allArgs = (listOf(th2, ObjString(name)) + args.list).toTypedArray()
|
||||
del.invokeInstanceMethod(this, "invoke", Arguments(*allArgs))
|
||||
}
|
||||
return obj.copy(
|
||||
value = wrapper,
|
||||
@ -517,14 +518,11 @@ open class Obj {
|
||||
)
|
||||
}
|
||||
val res = del.invokeInstanceMethod(scope, "getValue", Arguments(th, ObjString(name)))
|
||||
return obj.copy(
|
||||
value = res,
|
||||
type = ObjRecord.Type.Other
|
||||
)
|
||||
return obj.copy(value = res, type = ObjRecord.Type.Other)
|
||||
}
|
||||
val value = obj.value
|
||||
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}")
|
||||
val res = prop.callGetter(scope, this, decl)
|
||||
return ObjRecord(res, obj.isMutable)
|
||||
|
||||
@ -61,7 +61,7 @@ val ObjClassType by lazy {
|
||||
val names = mutableListOf<Obj>()
|
||||
for (c in cls.mro) {
|
||||
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())
|
||||
@ -79,7 +79,7 @@ val ObjClassType by lazy {
|
||||
val names = mutableListOf<Obj>()
|
||||
for (c in cls.mro) {
|
||||
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())
|
||||
@ -136,7 +136,7 @@ open class ObjClass(
|
||||
}
|
||||
}
|
||||
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
|
||||
res[name] = key
|
||||
}
|
||||
@ -150,13 +150,13 @@ open class ObjClass(
|
||||
val classNameObj by lazy { ObjString(className) }
|
||||
|
||||
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
|
||||
* 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.
|
||||
@ -349,8 +349,7 @@ open class ObjClass(
|
||||
if (cls.className == "Obj") break
|
||||
for ((name, rec) in cls.members) {
|
||||
if (rec.isAbstract) continue
|
||||
if (rec.value !is Statement &&
|
||||
rec.type != ObjRecord.Type.Delegated &&
|
||||
if (rec.type != ObjRecord.Type.Delegated &&
|
||||
rec.type != ObjRecord.Type.Fun &&
|
||||
rec.type != ObjRecord.Type.Property) {
|
||||
continue
|
||||
@ -363,9 +362,9 @@ open class ObjClass(
|
||||
}
|
||||
cls.classScope?.objects?.forEach { (name, rec) ->
|
||||
if (rec.isAbstract) return@forEach
|
||||
if (rec.value !is Statement &&
|
||||
rec.type != ObjRecord.Type.Delegated &&
|
||||
rec.type != ObjRecord.Type.Property) return@forEach
|
||||
if (rec.type != ObjRecord.Type.Delegated &&
|
||||
rec.type != ObjRecord.Type.Property &&
|
||||
rec.type != ObjRecord.Type.Fun) return@forEach
|
||||
val key = if (rec.visibility == Visibility.Private || rec.type == ObjRecord.Type.Delegated) cls.mangledName(name) else name
|
||||
if (res.containsKey(key)) return@forEach
|
||||
val methodId = rec.methodId ?: cls.assignMethodId(name, rec)
|
||||
@ -533,7 +532,7 @@ open class ObjClass(
|
||||
for (cls in mro) {
|
||||
// 1) members-defined methods and fields
|
||||
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
|
||||
if (!res.containsKey(key)) {
|
||||
res[key] = v
|
||||
@ -544,7 +543,7 @@ open class ObjClass(
|
||||
cls.classScope?.objects?.forEach { (k, rec) ->
|
||||
// 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.
|
||||
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
|
||||
// if not already present, copy reference for dispatch
|
||||
if (!res.containsKey(key)) {
|
||||
@ -715,7 +714,7 @@ open class ObjClass(
|
||||
instance.instanceScope.currentClassCtx = c
|
||||
try {
|
||||
for (initStmt in c.instanceInitializers) {
|
||||
initStmt.execute(instance.instanceScope)
|
||||
initStmt.callOn(instance.instanceScope)
|
||||
}
|
||||
} finally {
|
||||
instance.instanceScope.currentClassCtx = savedCtx
|
||||
@ -726,7 +725,7 @@ open class ObjClass(
|
||||
c.instanceConstructor?.let { ctor ->
|
||||
val execScope =
|
||||
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,
|
||||
code: (suspend Scope.() -> Obj)? = null
|
||||
) {
|
||||
val stmt = code?.let { statement { it() } } ?: ObjNull
|
||||
val stmt = code?.let { ObjNativeCallable { it() } } ?: ObjNull
|
||||
createField(
|
||||
name, stmt, isMutable, visibility, writeVisibility, pos, declaringClass,
|
||||
isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride,
|
||||
@ -958,8 +957,8 @@ open class ObjClass(
|
||||
prop: ObjProperty? = null,
|
||||
methodId: Int? = null
|
||||
) {
|
||||
val g = getter?.let { statement { it() } }
|
||||
val s = setter?.let { statement { it(requiredArg(0)); ObjVoid } }
|
||||
val g = getter?.let { ObjNativeCallable { it() } }
|
||||
val s = setter?.let { ObjNativeCallable { it(requiredArg(0)); ObjVoid } }
|
||||
val finalProp = prop ?: if (isAbstract) ObjNull else ObjProperty(name, g, s)
|
||||
createField(
|
||||
name, finalProp, false, visibility, writeVisibility, pos, declaringClass,
|
||||
@ -971,7 +970,7 @@ open class ObjClass(
|
||||
|
||||
fun addClassConst(name: String, value: Obj) = createClassField(name, value)
|
||||
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.JsonPrimitive
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.Statement
|
||||
import net.sergeych.lyng.miniast.addClassFnDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
||||
@ -47,14 +46,19 @@ class ObjDateTime(val instant: Instant, val timeZone: TimeZone) : Obj() {
|
||||
if (rec != null) {
|
||||
if (rec.type == ObjRecord.Type.Property) {
|
||||
val prop = rec.value as? ObjProperty
|
||||
?: (rec.value as? Statement)?.execute(scope) as? ObjProperty
|
||||
if (prop != null) {
|
||||
return ObjRecord(prop.callGetter(scope, this, rec.declaringClass ?: cls), rec.isMutable)
|
||||
}
|
||||
}
|
||||
if (rec.type == ObjRecord.Type.Fun || rec.value is Statement) {
|
||||
val s = rec.value as Statement
|
||||
return ObjRecord(net.sergeych.lyng.statement { s.execute(this.createChildScope(newThisObj = this@ObjDateTime)) }, rec.isMutable)
|
||||
if (rec.type == ObjRecord.Type.Fun) {
|
||||
val target = rec.value
|
||||
return ObjRecord(
|
||||
ObjNativeCallable {
|
||||
val callScope = createChildScope(args = args, newThisObj = this@ObjDateTime)
|
||||
target.callOn(callScope)
|
||||
},
|
||||
rec.isMutable
|
||||
)
|
||||
}
|
||||
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.Scope
|
||||
import net.sergeych.lyng.Statement
|
||||
|
||||
class ObjDynamicContext(val delegate: ObjDynamic) : Obj() {
|
||||
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
|
||||
* 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
|
||||
// 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 {
|
||||
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)
|
||||
it.asMutable
|
||||
else
|
||||
@ -83,32 +82,32 @@ open class ObjDynamic(var readCallback: Statement? = null, var writeCallback: St
|
||||
onNotFoundResult: (suspend () -> Obj?)?
|
||||
): Obj {
|
||||
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)
|
||||
?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
||||
}
|
||||
|
||||
override suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
|
||||
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)
|
||||
}
|
||||
|
||||
override suspend fun getAt(scope: Scope, index: Obj): Obj {
|
||||
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)
|
||||
}
|
||||
|
||||
override suspend fun putAt(scope: Scope, index: Obj, newValue: Obj) {
|
||||
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)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
suspend fun create(scope: Scope, builder: Statement): ObjDynamic {
|
||||
suspend fun create(scope: Scope, builder: Obj): ObjDynamic {
|
||||
val delegate = ObjDynamic()
|
||||
val context = ObjDynamicContext(delegate)
|
||||
// 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)
|
||||
// Snapshot the caller scope to capture locals/args even if the runtime pools/reuses frames
|
||||
delegate.builderScope = scope.snapshotForClosure()
|
||||
builder.execute(buildScope)
|
||||
builder.callOn(buildScope)
|
||||
return delegate
|
||||
}
|
||||
|
||||
|
||||
@ -139,7 +139,7 @@ open class ObjException(
|
||||
class ExceptionClass(val name: String, vararg parents: ObjClass) : ObjClass(name, *parents) {
|
||||
init {
|
||||
constructorMeta = ArgsDeclaration(
|
||||
listOf(ArgsDeclaration.Item("message", defaultValue = statement { ObjString(name) })),
|
||||
listOf(ArgsDeclaration.Item("message", defaultValue = ObjNativeCallable { ObjString(name) })),
|
||||
Token.Type.RPAREN
|
||||
)
|
||||
}
|
||||
@ -177,7 +177,7 @@ open class ObjException(
|
||||
}
|
||||
|
||||
val Root = ExceptionClass("Exception").apply {
|
||||
instanceInitializers.add(statement {
|
||||
instanceInitializers.add(ObjNativeCallable {
|
||||
if (thisObj is ObjInstance) {
|
||||
val msg = get("message")?.value ?: ObjString("Exception")
|
||||
(thisObj as ObjInstance).instanceScope.addItem("Exception::message", false, msg)
|
||||
@ -187,7 +187,7 @@ open class ObjException(
|
||||
}
|
||||
ObjVoid
|
||||
})
|
||||
instanceConstructor = statement { ObjVoid }
|
||||
instanceConstructor = ObjNativeCallable { ObjVoid }
|
||||
addPropertyDoc(
|
||||
name = "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 builder = ObjFlowBuilder(channel)
|
||||
val builderScope = scope.createChildScope(newThisObj = builder)
|
||||
globalLaunch {
|
||||
try {
|
||||
producer.execute(builderScope)
|
||||
producer.callOn(builderScope)
|
||||
} catch (x: ScriptFlowIsNoMoreCollected) {
|
||||
// premature flow closing, OK
|
||||
} catch (x: Exception) {
|
||||
@ -89,7 +89,7 @@ private fun createLyngFlowInput(scope: Scope, producer: Statement): ReceiveChann
|
||||
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
|
||||
|
||||
@ -106,8 +106,8 @@ class ObjFlow(val producer: Statement, val scope: Scope) : Obj() {
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val objFlow = thisAs<ObjFlow>()
|
||||
ObjFlowIterator(statement {
|
||||
objFlow.producer.execute(this)
|
||||
ObjFlowIterator(ObjNativeCallable {
|
||||
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
|
||||
|
||||
|
||||
@ -170,16 +170,17 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
||||
}
|
||||
}
|
||||
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") {
|
||||
val wrapper = object : Statement() {
|
||||
override val pos: Pos = Pos.builtIn
|
||||
|
||||
override suspend fun execute(s: Scope): Obj {
|
||||
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))
|
||||
}
|
||||
val wrapper = ObjNativeCallable {
|
||||
val th2 = if (thisObj === ObjVoid) ObjNull else thisObj
|
||||
val allArgs = (listOf(th2, ObjString(name)) + args.list).toTypedArray()
|
||||
del.invokeInstanceMethod(this, "invoke", Arguments(*allArgs))
|
||||
}
|
||||
return obj.copy(value = wrapper, type = ObjRecord.Type.Other)
|
||||
}
|
||||
|
||||
@ -18,7 +18,6 @@
|
||||
package net.sergeych.lyng.obj
|
||||
|
||||
import net.sergeych.lyng.Arguments
|
||||
import net.sergeych.lyng.Statement
|
||||
import net.sergeych.lyng.miniast.ParamDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
||||
@ -131,10 +130,11 @@ val ObjIterable by lazy {
|
||||
returns = type("lyng.Map"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val association = requireOnlyArg<Statement>()
|
||||
val association = requireOnlyArg<Obj>()
|
||||
val result = ObjMap()
|
||||
thisObj.toFlow(this).collect {
|
||||
result.map[association.call(this, it)] = it
|
||||
val callScope = createChildScope(args = Arguments(it))
|
||||
result.map[association.callOn(callScope)] = it
|
||||
}
|
||||
result
|
||||
}
|
||||
@ -147,10 +147,10 @@ val ObjIterable by lazy {
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val it = thisObj.invokeInstanceMethod(this, "iterator")
|
||||
val fn = requiredArg<Statement>(0)
|
||||
val fn = requiredArg<Obj>(0)
|
||||
while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
|
||||
val x = it.invokeInstanceMethod(this, "next")
|
||||
fn.execute(this.createChildScope(Arguments(listOf(x))))
|
||||
fn.callOn(this.createChildScope(Arguments(listOf(x))))
|
||||
}
|
||||
ObjVoid
|
||||
}
|
||||
@ -163,10 +163,11 @@ val ObjIterable by lazy {
|
||||
isOpen = true,
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val fn = requiredArg<Statement>(0)
|
||||
val fn = requiredArg<Obj>(0)
|
||||
val result = mutableListOf<Obj>()
|
||||
thisObj.toFlow(this).collect {
|
||||
result.add(fn.call(this, it))
|
||||
val callScope = createChildScope(args = Arguments(it))
|
||||
result.add(fn.callOn(callScope))
|
||||
}
|
||||
ObjList(result)
|
||||
}
|
||||
@ -179,10 +180,11 @@ val ObjIterable by lazy {
|
||||
isOpen = true,
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val fn = requiredArg<Statement>(0)
|
||||
val fn = requiredArg<Obj>(0)
|
||||
val result = mutableListOf<Obj>()
|
||||
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)
|
||||
}
|
||||
ObjList(result)
|
||||
@ -228,9 +230,10 @@ val ObjIterable by lazy {
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val list = thisObj.callMethod<ObjList>(this, "toList")
|
||||
val comparator = requireOnlyArg<Statement>()
|
||||
val comparator = requireOnlyArg<Obj>()
|
||||
list.quicksort { a, b ->
|
||||
comparator.call(this, a, b).toInt()
|
||||
val callScope = createChildScope(args = Arguments(a, b))
|
||||
comparator.callOn(callScope).toInt()
|
||||
}
|
||||
list
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ import net.sergeych.lyng.*
|
||||
* Lazy delegate used by `val x by lazy { ... }`.
|
||||
*/
|
||||
class ObjLazyDelegate(
|
||||
private val builder: Statement,
|
||||
private val builder: Obj,
|
||||
private val capturedScope: Scope,
|
||||
) : Obj() {
|
||||
override val objClass: ObjClass = type
|
||||
|
||||
@ -20,7 +20,7 @@ package net.sergeych.lyng.obj
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
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.addFnDoc
|
||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
||||
@ -371,8 +371,11 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
||||
params = listOf(ParamDoc("comparator")),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val comparator = requireOnlyArg<Statement>()
|
||||
thisAs<ObjList>().quicksort { a, b -> comparator.call(this, a, b).toInt() }
|
||||
val comparator = requireOnlyArg<Obj>()
|
||||
thisAs<ObjList>().quicksort { a, b ->
|
||||
val callScope = createChildScope(args = Arguments(a, b))
|
||||
comparator.callOn(callScope).toInt()
|
||||
}
|
||||
ObjVoid
|
||||
}
|
||||
addFnDoc(
|
||||
@ -522,4 +525,3 @@ fun <T>MutableList<T>.swap(i: Int, j: Int) {
|
||||
this[j] = temp
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,7 +20,6 @@ package net.sergeych.lyng.obj
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.Statement
|
||||
import net.sergeych.lyng.miniast.*
|
||||
import net.sergeych.lynon.LynonDecoder
|
||||
import net.sergeych.lynon.LynonEncoder
|
||||
@ -261,8 +260,8 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
|
||||
) {
|
||||
val key = requiredArg<Obj>(0)
|
||||
thisAs<ObjMap>().map.getOrPut(key) {
|
||||
val lambda = requiredArg<Statement>(1)
|
||||
lambda.execute(this)
|
||||
val lambda = requiredArg<Obj>(1)
|
||||
lambda.callOn(this)
|
||||
}
|
||||
}
|
||||
addPropertyDoc(
|
||||
|
||||
@ -20,7 +20,6 @@ package net.sergeych.lyng.obj
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.Statement
|
||||
import net.sergeych.lyng.miniast.ParamDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
@ -41,11 +40,11 @@ class ObjMutex(val mutex: Mutex): Obj() {
|
||||
returns = type("lyng.Any"),
|
||||
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
|
||||
// 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.
|
||||
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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* 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 {
|
||||
suspend fun execute(scope: Scope): Obj
|
||||
}
|
||||
|
||||
class StatementDeclExecutable(private val delegate: Statement) : DeclExecutable {
|
||||
override suspend fun execute(scope: Scope): Obj = delegate.execute(scope)
|
||||
class ObjNativeCallable(
|
||||
private val fn: suspend Scope.() -> Obj
|
||||
) : Obj() {
|
||||
|
||||
override val objClass: ObjClass
|
||||
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.Scope
|
||||
import net.sergeych.lyng.Statement
|
||||
|
||||
/**
|
||||
* Property accessor storage. Per instructions, properties do NOT have
|
||||
@ -27,8 +26,8 @@ import net.sergeych.lyng.Statement
|
||||
*/
|
||||
class ObjProperty(
|
||||
val name: String,
|
||||
val getter: Statement?,
|
||||
val setter: Statement?
|
||||
val getter: Obj?,
|
||||
val setter: Obj?
|
||||
) : 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 execScope = scope.applyClosure(instanceScope).createChildScope(newThisObj = instance)
|
||||
execScope.currentClassCtx = declaringClass
|
||||
return g.execute(execScope)
|
||||
return g.callOn(execScope)
|
||||
}
|
||||
|
||||
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 execScope = scope.applyClosure(instanceScope).createChildScope(args = Arguments(value), newThisObj = instance)
|
||||
execScope.currentClassCtx = declaringClass
|
||||
s.execute(execScope)
|
||||
s.callOn(execScope)
|
||||
}
|
||||
|
||||
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
|
||||
addConstDoc(
|
||||
name = "roundToInt",
|
||||
value = statement(Pos.builtIn) {
|
||||
(it.thisObj as ObjReal).value.roundToLong().toObj()
|
||||
value = ObjNativeCallable {
|
||||
(thisObj as ObjReal).value.roundToLong().toObj()
|
||||
},
|
||||
doc = "This real number rounded to the nearest integer.",
|
||||
type = type("lyng.Int"),
|
||||
|
||||
@ -21,7 +21,6 @@ import net.sergeych.lyng.PerfFlags
|
||||
import net.sergeych.lyng.Pos
|
||||
import net.sergeych.lyng.RegexCache
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.Statement
|
||||
import net.sergeych.lyng.miniast.*
|
||||
|
||||
class ObjRegex(val regex: Regex) : Obj() {
|
||||
@ -76,14 +75,10 @@ class ObjRegex(val regex: Regex) : Obj() {
|
||||
}
|
||||
createField(
|
||||
name = "operatorMatch",
|
||||
initialValue = object : Statement() {
|
||||
override val pos: Pos = Pos.builtIn
|
||||
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
val other = scope.args.firstAndOnly(pos)
|
||||
val targetScope = scope.parent ?: scope
|
||||
return (scope.thisObj as ObjRegex).operatorMatch(targetScope, other)
|
||||
}
|
||||
initialValue = ObjNativeCallable {
|
||||
val other = args.firstAndOnly(Pos.builtIn)
|
||||
val targetScope = parent ?: this
|
||||
(thisObj as ObjRegex).operatorMatch(targetScope, other)
|
||||
},
|
||||
type = ObjRecord.Type.Fun
|
||||
)
|
||||
|
||||
@ -25,7 +25,6 @@ import net.sergeych.lyng.PerfFlags
|
||||
import net.sergeych.lyng.Pos
|
||||
import net.sergeych.lyng.RegexCache
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.Statement
|
||||
import net.sergeych.lyng.miniast.*
|
||||
import net.sergeych.lynon.LynonDecoder
|
||||
import net.sergeych.lynon.LynonEncoder
|
||||
@ -344,14 +343,10 @@ data class ObjString(val value: String) : Obj() {
|
||||
name = "re",
|
||||
initialValue = ObjProperty(
|
||||
name = "re",
|
||||
getter = object : Statement() {
|
||||
override val pos: Pos = Pos.builtIn
|
||||
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
val pattern = (scope.thisObj as ObjString).value
|
||||
val re = if (PerfFlags.REGEX_CACHE) RegexCache.get(pattern) else pattern.toRegex()
|
||||
return ObjRegex(re)
|
||||
}
|
||||
getter = ObjNativeCallable {
|
||||
val pattern = (thisObj as ObjString).value
|
||||
val re = if (PerfFlags.REGEX_CACHE) RegexCache.get(pattern) else pattern.toRegex()
|
||||
ObjRegex(re)
|
||||
},
|
||||
setter = null
|
||||
),
|
||||
@ -359,14 +354,10 @@ data class ObjString(val value: String) : Obj() {
|
||||
)
|
||||
createField(
|
||||
name = "operatorMatch",
|
||||
initialValue = object : Statement() {
|
||||
override val pos: Pos = Pos.builtIn
|
||||
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
val other = scope.args.firstAndOnly(pos)
|
||||
val targetScope = scope.parent ?: scope
|
||||
return (scope.thisObj as ObjString).operatorMatch(targetScope, other)
|
||||
}
|
||||
initialValue = ObjNativeCallable {
|
||||
val other = args.firstAndOnly(Pos.builtIn)
|
||||
val targetScope = parent ?: this
|
||||
(thisObj as ObjString).operatorMatch(targetScope, other)
|
||||
},
|
||||
type = ObjRecord.Type.Fun
|
||||
)
|
||||
|
||||
@ -70,7 +70,7 @@ object CompileTimeResolver {
|
||||
source,
|
||||
importProvider,
|
||||
resolutionSink = collector,
|
||||
useBytecodeStatements = false,
|
||||
compileBytecode = false,
|
||||
allowUnresolvedRefs = true
|
||||
)
|
||||
return collector.buildReport()
|
||||
|
||||
@ -106,7 +106,7 @@ object LyngLanguageTools {
|
||||
request.importProvider,
|
||||
miniSink = miniSink,
|
||||
resolutionSink = resolutionCollector,
|
||||
useBytecodeStatements = false,
|
||||
compileBytecode = false,
|
||||
allowUnresolvedRefs = true,
|
||||
seedScope = request.seedScope
|
||||
)
|
||||
|
||||
@ -392,7 +392,6 @@ class BytecodeRecentOpsTest {
|
||||
val script = Compiler.compileWithResolution(
|
||||
Source("<fast-local>", code),
|
||||
Script.defaultImportManager,
|
||||
useBytecodeStatements = true,
|
||||
useFastLocalRefs = true
|
||||
)
|
||||
val result = script.execute(Script.defaultImportManager.newStdScope())
|
||||
|
||||
@ -38,7 +38,6 @@ class CompileTimeResolutionRuntimeTest {
|
||||
val script = Compiler.compileWithResolution(
|
||||
Source("<strict-slot>", code),
|
||||
Script.defaultImportManager,
|
||||
useBytecodeStatements = false,
|
||||
strictSlotRefs = true
|
||||
)
|
||||
val result = script.execute(Script.defaultImportManager.newStdScope())
|
||||
@ -61,7 +60,6 @@ class CompileTimeResolutionRuntimeTest {
|
||||
val script = Compiler.compileWithResolution(
|
||||
Source("<shadow-slot>", code),
|
||||
Script.defaultImportManager,
|
||||
useBytecodeStatements = true,
|
||||
strictSlotRefs = true
|
||||
)
|
||||
val result = script.execute(Script.defaultImportManager.newStdScope())
|
||||
@ -85,7 +83,6 @@ class CompileTimeResolutionRuntimeTest {
|
||||
val script = Compiler.compileWithResolution(
|
||||
Source("<shadow-block>", code),
|
||||
Script.defaultImportManager,
|
||||
useBytecodeStatements = true,
|
||||
strictSlotRefs = true
|
||||
)
|
||||
val result = script.execute(Script.defaultImportManager.newStdScope())
|
||||
|
||||
@ -45,8 +45,7 @@ class ParamTypeInferenceTest {
|
||||
Compiler.compileWithResolution(
|
||||
Source("<eval>", code.trimIndent()),
|
||||
Script.defaultImportManager,
|
||||
miniSink = sink,
|
||||
useBytecodeStatements = false
|
||||
miniSink = sink
|
||||
)
|
||||
val mini = sink.build()!!
|
||||
val binding = Binder.bind(code, mini)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user