Compare commits
No commits in common. "db4f7d0973584958450143e38dd112fe26460380" and "473a5dd6ed94ec13763d9702c11531ebbfa44ddb" have entirely different histories.
db4f7d0973
...
473a5dd6ed
@ -43,98 +43,6 @@ Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM te
|
|||||||
- [x] Fix post-inc return value for object slots stored in scope frames.
|
- [x] Fix post-inc return value for object slots stored in scope frames.
|
||||||
- [x] Handle optional receivers for member assign-ops and inc/dec without evaluating operands on null.
|
- [x] Handle optional receivers for member assign-ops and inc/dec without evaluating operands on null.
|
||||||
- [x] Support class-scope and index optional inc/dec paths in bytecode.
|
- [x] Support class-scope and index optional inc/dec paths in bytecode.
|
||||||
- [x] Step 13: Qualified `this` value refs in bytecode.
|
|
||||||
- [x] Compile `QualifiedThisRef` (`this@Type`) via `LOAD_THIS_VARIANT`.
|
|
||||||
- [x] Add a JVM test that evaluates `this@Type` as a value inside nested classes.
|
|
||||||
- [x] Step 14: Fast local ref reads in bytecode.
|
|
||||||
- [x] Support `FastLocalVarRef` reads with the same slot resolution as `LocalVarRef`.
|
|
||||||
- [x] If `BoundLocalVarRef` is still emitted, map it to a direct slot read instead of failing.
|
|
||||||
- [x] Add a JVM test that exercises fast-local reads in a bytecode-compiled function.
|
|
||||||
- [x] Step 15: Class-scope `?=` in bytecode.
|
|
||||||
- [x] Handle `C.x ?= v` and `C?.x ?= v` for class-scope members without falling back.
|
|
||||||
- [x] Add a JVM test for class-scope `?=` on static vars.
|
|
||||||
- [x] Step 16: Remove dead `ToBoolStatement`.
|
|
||||||
- [x] Confirm no parser/compiler paths construct `ToBoolStatement` and delete it plus interpreter hooks.
|
|
||||||
- [x] Keep JVM tests green after removal.
|
|
||||||
- [x] Step 17: Callable property calls in bytecode.
|
|
||||||
- [x] Support `CallRef` where the target is a `FieldRef` (e.g., `(obj.fn)()`), keeping compile-time resolution.
|
|
||||||
- [x] Add a JVM test for a callable property call compiled to bytecode.
|
|
||||||
- [x] Step 18: Delegated member access in bytecode.
|
|
||||||
- [x] Remove `containsDelegatedRefs` guard once bytecode emits delegated get/set/call correctly.
|
|
||||||
- [x] Add JVM coverage for delegated member get/set/call in bytecode.
|
|
||||||
- [x] Step 19: Unknown receiver member access in bytecode.
|
|
||||||
- [x] Reject Object/unknown receiver member calls without explicit cast or Dynamic.
|
|
||||||
- [x] Add union-member dispatch with ordered type checks and runtime mismatch error.
|
|
||||||
- [x] Add JVM tests for unknown receiver and union member access.
|
|
||||||
- [x] Step 20: Bytecode support for `NopStatement`.
|
|
||||||
- [x] Allow `NopStatement` in `containsUnsupportedForBytecode`.
|
|
||||||
- [x] Emit `ObjVoid` directly in bytecode for `NopStatement` in statement/value contexts.
|
|
||||||
- [x] Add a JVM test that exercises a code path returning `NopStatement` in bytecode (e.g., static class member decl in class body).
|
|
||||||
- [x] Step 21: Union mismatch path in bytecode.
|
|
||||||
- [x] Replace `UnionTypeMismatchStatement` branch with a bytecode-compilable throw path (no custom `StatementRef` that blocks bytecode).
|
|
||||||
- [x] Add a JVM test that forces the union mismatch at runtime and asserts the error message.
|
|
||||||
- [x] Step 22: Delegated local slots in bytecode.
|
|
||||||
- [x] Support reads/writes/assign-ops/inc/dec for delegated locals (`LocalSlotRef.isDelegated`) in `BytecodeCompiler`.
|
|
||||||
- [x] Remove `containsDelegatedRefs` guard once delegated locals are bytecode-safe.
|
|
||||||
- [x] Add JVM tests that use delegated locals inside bytecode-compiled functions.
|
|
||||||
- [x] Step 23: Refactor delegated locals to keep delegate objects in frame slots.
|
|
||||||
- [x] Add bytecode ops to bind/get/set delegated locals without scope storage.
|
|
||||||
- [x] Store delegated locals in frame slots and compile get/set/assign ops with new ops.
|
|
||||||
- [x] Preserve reflection facade by syncing delegated locals into scope only when needed.
|
|
||||||
- [x] Step 24: Remove `ASSIGN_SCOPE_SLOT` now that delegated locals are always frame-backed.
|
|
||||||
- [x] Force delegated locals into local slots (even module) and avoid scope-slot resolution.
|
|
||||||
- [x] Drop opcode/runtime support for `ASSIGN_SCOPE_SLOT`.
|
|
||||||
|
|
||||||
## Frame-Only Execution (new, before interpreter removal)
|
|
||||||
|
|
||||||
- [x] Step 24A: Bytecode capture tables for lambdas (compiler only).
|
|
||||||
- [x] Emit per-lambda capture tables containing (ownerFrameKind, ownerSlotId).
|
|
||||||
- [x] Create captures only when detected; do not allocate scope slots.
|
|
||||||
- [x] Add disassembler output for capture tables.
|
|
||||||
- [x] JVM tests must be green before commit.
|
|
||||||
- [x] Step 24B: Frame-slot captures in bytecode runtime.
|
|
||||||
- [x] Build lambdas from bytecode + capture table (no capture names).
|
|
||||||
- [x] Read captured values via `FrameSlotRef` only.
|
|
||||||
- [x] Forbid `resolveCaptureRecord` in bytecode paths; keep only in interpreter.
|
|
||||||
- [x] JVM tests must be green before commit.
|
|
||||||
- [x] Step 24C: Remove scope local mirroring in bytecode execution.
|
|
||||||
- [x] Remove/disable any bytecode runtime code that writes locals into Scope for execution.
|
|
||||||
- [x] Keep Scope creation only for reflection/Kotlin interop paths.
|
|
||||||
- [x] JVM tests must be green before commit.
|
|
||||||
- [x] Step 24D: Eliminate `ClosureScope` usage on bytecode execution paths.
|
|
||||||
- [x] Avoid `ClosureScope` in bytecode-related call paths (Block/Lambda/ObjDynamic/ObjProperty).
|
|
||||||
- [x] Keep interpreter path using `ClosureScope` until interpreter removal.
|
|
||||||
- [x] JVM tests must be green before commit.
|
|
||||||
- [x] Step 24E: Isolate interpreter-only capture logic.
|
|
||||||
- [x] Mark `resolveCaptureRecord` paths as interpreter-only.
|
|
||||||
- [x] Guard or delete any bytecode path that tries to sync captures into scopes.
|
|
||||||
- [x] JVM tests must be green before commit.
|
|
||||||
|
|
||||||
## Interpreter Removal (next)
|
|
||||||
|
|
||||||
- [ ] Step 25: Replace Statement-based declaration calls in bytecode.
|
|
||||||
- [x] Add bytecode const/op for enum declarations (no `Statement` objects in constants).
|
|
||||||
- [x] Add bytecode const/op for class declarations (no `Statement` objects in constants).
|
|
||||||
- [x] Add bytecode const/op for function declarations (no `Statement` objects in constants).
|
|
||||||
- [x] Replace `emitStatementCall` usage for `EnumDeclStatement`.
|
|
||||||
- [x] Replace `emitStatementCall` usage for `ClassDeclStatement`.
|
|
||||||
- [x] Replace `emitStatementCall` usage for `FunctionDeclStatement`.
|
|
||||||
- [x] Add JVM disasm coverage to ensure module init has no `CALL_SLOT` to `Callable@...` for declarations.
|
|
||||||
- [ ] Step 26: Bytecode-backed lambdas (remove `ValueFnRef` runtime execution).
|
|
||||||
- [x] Compile lambda bodies to bytecode and emit an opcode to create a callable from bytecode + capture plan.
|
|
||||||
- [x] Remove `containsValueFnRef` helper now that lambdas are bytecode-backed.
|
|
||||||
- [x] Remove `forceScopeSlots` branches once no bytecode paths depend on scope slots.
|
|
||||||
- [x] Add JVM tests for captured locals and delegated locals inside lambdas on the bytecode path.
|
|
||||||
- [x] Step 27: Remove interpreter opcodes and constants from bytecode runtime.
|
|
||||||
- [x] Delete `BytecodeConst.ValueFn`, `CmdMakeValueFn`, and `MAKE_VALUE_FN`.
|
|
||||||
- [x] Delete `BytecodeConst.StatementVal`, `CmdEvalStmt`, and `EVAL_STMT`.
|
|
||||||
- [x] Add bytecode-backed `::class` via `ClassOperatorRef` + `GET_OBJ_CLASS` to avoid ValueFn for class operator.
|
|
||||||
- [x] Add a bytecode fallback reporter hook for lambdas to locate remaining non-bytecode cases.
|
|
||||||
- [x] Remove `emitStatementCall`/`emitStatementEval` once unused.
|
|
||||||
- [ ] Step 28: Scope as facade only.
|
|
||||||
- [x] Audit bytecode execution paths for `Statement.execute` usage and remove remaining calls.
|
|
||||||
- [x] Keep scope sync only for reflection/Kotlin interop, not for execution.
|
|
||||||
- [x] Replace bytecode entry seeding from Scope with frame-only arg/local binding.
|
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
|||||||
@ -27,9 +27,7 @@ import com.github.ajalt.clikt.parameters.arguments.optional
|
|||||||
import com.github.ajalt.clikt.parameters.options.flag
|
import com.github.ajalt.clikt.parameters.options.flag
|
||||||
import com.github.ajalt.clikt.parameters.options.option
|
import com.github.ajalt.clikt.parameters.options.option
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import net.sergeych.lyng.Compiler
|
|
||||||
import net.sergeych.lyng.LyngVersion
|
import net.sergeych.lyng.LyngVersion
|
||||||
import net.sergeych.lyng.Pos
|
|
||||||
import net.sergeych.lyng.Script
|
import net.sergeych.lyng.Script
|
||||||
import net.sergeych.lyng.ScriptError
|
import net.sergeych.lyng.ScriptError
|
||||||
import net.sergeych.lyng.Source
|
import net.sergeych.lyng.Source
|
||||||
@ -158,7 +156,6 @@ private class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand()
|
|||||||
|
|
||||||
val version by option("-v", "--version", help = "Print version and exit").flag()
|
val version by option("-v", "--version", help = "Print version and exit").flag()
|
||||||
val benchmark by option("--benchmark", help = "Run JVM microbenchmarks and exit").flag()
|
val benchmark by option("--benchmark", help = "Run JVM microbenchmarks and exit").flag()
|
||||||
val bytecodeFallbacks by option("--bytecode-fallbacks", help = "Report lambdas that fall back to interpreter").flag()
|
|
||||||
val script by argument(help = "one or more scripts to execute").optional()
|
val script by argument(help = "one or more scripts to execute").optional()
|
||||||
val execute: String? by option(
|
val execute: String? by option(
|
||||||
"-x", "--execute", help = """
|
"-x", "--execute", help = """
|
||||||
@ -201,15 +198,7 @@ private class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand()
|
|||||||
launcher {
|
launcher {
|
||||||
// there is no script name, it is a first argument instead:
|
// there is no script name, it is a first argument instead:
|
||||||
processErrors {
|
processErrors {
|
||||||
val reporter = bytecodeFallbackReporter(bytecodeFallbacks)
|
baseScope.eval(execute!!)
|
||||||
val script = Compiler.compileWithResolution(
|
|
||||||
Source("<eval>", execute!!),
|
|
||||||
baseScope.currentImportProvider,
|
|
||||||
seedScope = baseScope,
|
|
||||||
bytecodeFallbackReporter = reporter
|
|
||||||
)
|
|
||||||
script.execute(baseScope)
|
|
||||||
flushBytecodeFallbacks(reporter)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -220,7 +209,7 @@ private class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand()
|
|||||||
echoFormattedHelp()
|
echoFormattedHelp()
|
||||||
} else {
|
} else {
|
||||||
baseScope.addConst("ARGV", ObjList(args.map { ObjString(it) }.toMutableList()))
|
baseScope.addConst("ARGV", ObjList(args.map { ObjString(it) }.toMutableList()))
|
||||||
launcher { executeFile(script!!, bytecodeFallbacks) }
|
launcher { executeFile(script!!) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -228,14 +217,14 @@ private class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun executeFileWithArgs(fileName: String, args: List<String>, reportBytecodeFallbacks: Boolean = false) {
|
fun executeFileWithArgs(fileName: String, args: List<String>) {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
baseScopeDefer.await().addConst("ARGV", ObjList(args.map { ObjString(it) }.toMutableList()))
|
baseScopeDefer.await().addConst("ARGV", ObjList(args.map { ObjString(it) }.toMutableList()))
|
||||||
executeFile(fileName, reportBytecodeFallbacks)
|
executeFile(fileName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun executeFile(fileName: String, reportBytecodeFallbacks: Boolean = false) {
|
suspend fun executeFile(fileName: String) {
|
||||||
var text = FileSystem.SYSTEM.source(fileName.toPath()).use { fileSource ->
|
var text = FileSystem.SYSTEM.source(fileName.toPath()).use { fileSource ->
|
||||||
fileSource.buffer().use { bs ->
|
fileSource.buffer().use { bs ->
|
||||||
bs.readUtf8()
|
bs.readUtf8()
|
||||||
@ -247,16 +236,7 @@ suspend fun executeFile(fileName: String, reportBytecodeFallbacks: Boolean = fal
|
|||||||
text = text.substring(pos + 1)
|
text = text.substring(pos + 1)
|
||||||
}
|
}
|
||||||
processErrors {
|
processErrors {
|
||||||
val scope = baseScopeDefer.await()
|
baseScopeDefer.await().eval(Source(fileName, text))
|
||||||
val reporter = bytecodeFallbackReporter(reportBytecodeFallbacks)
|
|
||||||
val script = Compiler.compileWithResolution(
|
|
||||||
Source(fileName, text),
|
|
||||||
scope.currentImportProvider,
|
|
||||||
seedScope = scope,
|
|
||||||
bytecodeFallbackReporter = reporter
|
|
||||||
)
|
|
||||||
script.execute(scope)
|
|
||||||
flushBytecodeFallbacks(reporter)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,22 +248,3 @@ suspend fun processErrors(block: suspend () -> Unit) {
|
|||||||
println("\nError executing the script:\n$e\n")
|
println("\nError executing the script:\n$e\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bytecodeFallbackReporter(enabled: Boolean): ((Pos, String) -> Unit)? {
|
|
||||||
if (!enabled) return null
|
|
||||||
val reports = ArrayList<String>()
|
|
||||||
val reporter: (Pos, String) -> Unit = { pos, msg ->
|
|
||||||
reports.add("$pos: $msg")
|
|
||||||
}
|
|
||||||
return object : (Pos, String) -> Unit by reporter {
|
|
||||||
override fun invoke(pos: Pos, msg: String) = reporter(pos, msg)
|
|
||||||
override fun toString(): String = reports.joinToString("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun flushBytecodeFallbacks(reporter: ((Pos, String) -> Unit)?) {
|
|
||||||
val text = reporter?.toString().orEmpty()
|
|
||||||
if (text.isBlank()) return
|
|
||||||
println("Bytecode lambda fallbacks:")
|
|
||||||
println(text)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -250,212 +250,6 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Assign arguments directly into frame slots using [paramSlotPlan] without creating scope locals.
|
|
||||||
* Still allows default expressions to evaluate by exposing FrameSlotRef facades in [scope].
|
|
||||||
*/
|
|
||||||
suspend fun assignToFrame(
|
|
||||||
scope: Scope,
|
|
||||||
arguments: Arguments = scope.args,
|
|
||||||
paramSlotPlan: Map<String, Int>,
|
|
||||||
frame: FrameAccess,
|
|
||||||
slotOffset: Int = 0,
|
|
||||||
defaultAccessType: AccessType = AccessType.Var,
|
|
||||||
defaultVisibility: Visibility = Visibility.Public,
|
|
||||||
declaringClass: net.sergeych.lyng.obj.ObjClass? = scope.currentClassCtx
|
|
||||||
) {
|
|
||||||
fun slotFor(name: String): Int {
|
|
||||||
val full = paramSlotPlan[name] ?: scope.raiseIllegalState("parameter slot for '$name' is missing")
|
|
||||||
val slot = full - slotOffset
|
|
||||||
if (slot < 0) scope.raiseIllegalState("parameter slot for '$name' is out of range")
|
|
||||||
return slot
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ensureScopeRef(a: Item, slot: Int, recordType: ObjRecord.Type) {
|
|
||||||
if (scope.getLocalRecordDirect(a.name) != null) return
|
|
||||||
scope.addItem(
|
|
||||||
a.name,
|
|
||||||
(a.accessType ?: defaultAccessType).isMutable,
|
|
||||||
FrameSlotRef(frame, slot),
|
|
||||||
a.visibility ?: defaultVisibility,
|
|
||||||
recordType = recordType,
|
|
||||||
declaringClass = declaringClass,
|
|
||||||
isTransient = a.isTransient
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setFrameValue(slot: Int, value: Obj) {
|
|
||||||
when (value) {
|
|
||||||
is net.sergeych.lyng.obj.ObjInt -> frame.setInt(slot, value.value)
|
|
||||||
is net.sergeych.lyng.obj.ObjReal -> frame.setReal(slot, value.value)
|
|
||||||
is net.sergeych.lyng.obj.ObjBool -> frame.setBool(slot, value.value)
|
|
||||||
else -> frame.setObj(slot, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun assign(a: Item, value: Obj) {
|
|
||||||
val recordType = if (declaringClass != null && a.accessType != null) {
|
|
||||||
ObjRecord.Type.ConstructorField
|
|
||||||
} else {
|
|
||||||
ObjRecord.Type.Argument
|
|
||||||
}
|
|
||||||
val slot = slotFor(a.name)
|
|
||||||
setFrameValue(slot, value.byValueCopy())
|
|
||||||
ensureScopeRef(a, slot, recordType)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun missingValue(a: Item, error: String): Obj {
|
|
||||||
return a.defaultValue?.execute(scope)
|
|
||||||
?: if (a.type.isNullable) ObjNull else scope.raiseIllegalArgument(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fast path for simple positional-only calls with no ellipsis and no defaults
|
|
||||||
if (arguments.named.isEmpty() && !arguments.tailBlockMode) {
|
|
||||||
var hasComplex = false
|
|
||||||
for (p in params) {
|
|
||||||
if (p.isEllipsis || p.defaultValue != null) {
|
|
||||||
hasComplex = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!hasComplex) {
|
|
||||||
if (arguments.list.size > params.size)
|
|
||||||
scope.raiseIllegalArgument("expected ${params.size} arguments, got ${arguments.list.size}")
|
|
||||||
if (arguments.list.size < params.size) {
|
|
||||||
for (i in arguments.list.size until params.size) {
|
|
||||||
val a = params[i]
|
|
||||||
if (!a.type.isNullable) {
|
|
||||||
scope.raiseIllegalArgument("expected ${params.size} arguments, got ${arguments.list.size}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i in params.indices) {
|
|
||||||
val a = params[i]
|
|
||||||
val value = if (i < arguments.list.size) arguments.list[i] else ObjNull
|
|
||||||
assign(a, value)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare positional args and parameter count, handle tail-block binding
|
|
||||||
val callArgs: List<Obj>
|
|
||||||
val paramsSize: Int
|
|
||||||
if (arguments.tailBlockMode) {
|
|
||||||
val lastParam = params.last()
|
|
||||||
if (arguments.named.containsKey(lastParam.name))
|
|
||||||
scope.raiseIllegalArgument("trailing block cannot be used when the last parameter is already assigned by a named argument")
|
|
||||||
paramsSize = params.size - 1
|
|
||||||
assign(lastParam, arguments.list.last())
|
|
||||||
callArgs = arguments.list.dropLast(1)
|
|
||||||
} else {
|
|
||||||
paramsSize = params.size
|
|
||||||
callArgs = arguments.list
|
|
||||||
}
|
|
||||||
|
|
||||||
val coveredByPositional = BooleanArray(paramsSize)
|
|
||||||
run {
|
|
||||||
var headRequired = 0
|
|
||||||
var tailRequired = 0
|
|
||||||
val ellipsisIdx = params.subList(0, paramsSize).indexOfFirst { it.isEllipsis }
|
|
||||||
if (ellipsisIdx >= 0) {
|
|
||||||
for (i in 0 until ellipsisIdx) if (!params[i].isEllipsis && params[i].defaultValue == null) headRequired++
|
|
||||||
for (i in paramsSize - 1 downTo ellipsisIdx + 1) if (params[i].defaultValue == null) tailRequired++
|
|
||||||
} else {
|
|
||||||
for (i in 0 until paramsSize) if (params[i].defaultValue == null) headRequired++
|
|
||||||
}
|
|
||||||
val P = callArgs.size
|
|
||||||
if (ellipsisIdx < 0) {
|
|
||||||
val k = minOf(P, paramsSize)
|
|
||||||
for (i in 0 until k) coveredByPositional[i] = true
|
|
||||||
} else {
|
|
||||||
val headTake = minOf(P, headRequired)
|
|
||||||
for (i in 0 until headTake) coveredByPositional[i] = true
|
|
||||||
val remaining = P - headTake
|
|
||||||
val tailTake = minOf(remaining, tailRequired)
|
|
||||||
var j = paramsSize - 1
|
|
||||||
var taken = 0
|
|
||||||
while (j > ellipsisIdx && taken < tailTake) {
|
|
||||||
coveredByPositional[j] = true
|
|
||||||
j--
|
|
||||||
taken++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val assignedByName = BooleanArray(paramsSize)
|
|
||||||
val namedValues = arrayOfNulls<Obj>(paramsSize)
|
|
||||||
if (arguments.named.isNotEmpty()) {
|
|
||||||
for ((k, v) in arguments.named) {
|
|
||||||
val idx = params.subList(0, paramsSize).indexOfFirst { it.name == k }
|
|
||||||
if (idx < 0) scope.raiseIllegalArgument("unknown parameter '$k'")
|
|
||||||
if (params[idx].isEllipsis) scope.raiseIllegalArgument("ellipsis (variadic) parameter cannot be assigned by name: '$k'")
|
|
||||||
if (coveredByPositional[idx]) scope.raiseIllegalArgument("argument '$k' is already set by positional argument")
|
|
||||||
if (assignedByName[idx]) scope.raiseIllegalArgument("argument '$k' is already set")
|
|
||||||
assignedByName[idx] = true
|
|
||||||
namedValues[idx] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun processHead(index: Int, headPos: Int): Pair<Int, Int> {
|
|
||||||
var i = index
|
|
||||||
var hp = headPos
|
|
||||||
while (i < paramsSize) {
|
|
||||||
val a = params[i]
|
|
||||||
if (a.isEllipsis) break
|
|
||||||
if (assignedByName[i]) {
|
|
||||||
assign(a, namedValues[i]!!)
|
|
||||||
} else {
|
|
||||||
val value = if (hp < callArgs.size) callArgs[hp++]
|
|
||||||
else missingValue(a, "too few arguments for the call (missing ${a.name})")
|
|
||||||
assign(a, value)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return i to hp
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun processTail(startExclusive: Int, tailStart: Int, headPosBound: Int): Int {
|
|
||||||
var i = paramsSize - 1
|
|
||||||
var tp = tailStart
|
|
||||||
while (i > startExclusive) {
|
|
||||||
val a = params[i]
|
|
||||||
if (a.isEllipsis) break
|
|
||||||
if (i < assignedByName.size && assignedByName[i]) {
|
|
||||||
assign(a, namedValues[i]!!)
|
|
||||||
} else {
|
|
||||||
val value = if (tp >= headPosBound) callArgs[tp--]
|
|
||||||
else missingValue(a, "too few arguments for the call")
|
|
||||||
assign(a, value)
|
|
||||||
}
|
|
||||||
i--
|
|
||||||
}
|
|
||||||
return tp
|
|
||||||
}
|
|
||||||
|
|
||||||
fun processEllipsis(index: Int, headPos: Int, tailPos: Int) {
|
|
||||||
val a = params[index]
|
|
||||||
val from = headPos
|
|
||||||
val to = tailPos
|
|
||||||
val l = if (from > to) ObjList()
|
|
||||||
else ObjList(callArgs.subList(from, to + 1).toMutableList())
|
|
||||||
assign(a, l)
|
|
||||||
}
|
|
||||||
|
|
||||||
val ellipsisIndex = params.subList(0, paramsSize).indexOfFirst { it.isEllipsis }
|
|
||||||
|
|
||||||
if (ellipsisIndex >= 0) {
|
|
||||||
val (_, headConsumedTo) = processHead(0, 0)
|
|
||||||
val tailConsumedFrom = processTail(ellipsisIndex, callArgs.size - 1, headConsumedTo)
|
|
||||||
processEllipsis(ellipsisIndex, headConsumedTo, tailConsumedFrom)
|
|
||||||
} else {
|
|
||||||
val (_, headConsumedTo) = processHead(0, 0)
|
|
||||||
if (headConsumedTo != callArgs.size)
|
|
||||||
scope.raiseIllegalArgument("too many arguments for the call")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Single argument declaration descriptor.
|
* Single argument declaration descriptor.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -31,33 +31,22 @@ class BlockStatement(
|
|||||||
val target = if (scope.skipScopeCreation) scope else scope.createChildScope(startPos)
|
val target = if (scope.skipScopeCreation) scope else scope.createChildScope(startPos)
|
||||||
if (slotPlan.isNotEmpty()) target.applySlotPlan(slotPlan)
|
if (slotPlan.isNotEmpty()) target.applySlotPlan(slotPlan)
|
||||||
if (captureSlots.isNotEmpty()) {
|
if (captureSlots.isNotEmpty()) {
|
||||||
val captureRecords = scope.captureRecords
|
val applyScope = scope as? ApplyScope
|
||||||
if (captureRecords != null) {
|
for (capture in captureSlots) {
|
||||||
for (i in captureSlots.indices) {
|
val rec = if (applyScope != null) {
|
||||||
val capture = captureSlots[i]
|
applyScope.resolveCaptureRecord(capture.name)
|
||||||
val rec = captureRecords.getOrNull(i)
|
?: applyScope.callScope.resolveCaptureRecord(capture.name)
|
||||||
?: scope.raiseSymbolNotFound("capture ${capture.name} not found")
|
} else {
|
||||||
target.updateSlotFor(capture.name, rec)
|
scope.resolveCaptureRecord(capture.name)
|
||||||
}
|
}
|
||||||
} else {
|
if (rec == null) {
|
||||||
val applyScope = scope as? ApplyScope
|
if (scope.getSlotIndexOf(capture.name) == null && scope.getLocalRecordDirect(capture.name) == null) {
|
||||||
for (capture in captureSlots) {
|
continue
|
||||||
// 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) {
|
(applyScope?.callScope ?: scope)
|
||||||
if (scope.getSlotIndexOf(capture.name) == null && scope.getLocalRecordDirect(capture.name) == null) {
|
.raiseSymbolNotFound("symbol ${capture.name} not found")
|
||||||
continue
|
|
||||||
}
|
|
||||||
(applyScope?.callScope ?: scope)
|
|
||||||
.raiseSymbolNotFound("symbol ${capture.name} not found")
|
|
||||||
}
|
|
||||||
target.updateSlotFor(capture.name, rec)
|
|
||||||
}
|
}
|
||||||
|
target.updateSlotFor(capture.name, rec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return block.execute(target)
|
return block.execute(target)
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
|
|
||||||
interface BytecodeCallable
|
|
||||||
@ -17,147 +17,20 @@
|
|||||||
package net.sergeych.lyng
|
package net.sergeych.lyng
|
||||||
|
|
||||||
import net.sergeych.lyng.obj.Obj
|
import net.sergeych.lyng.obj.Obj
|
||||||
import net.sergeych.lyng.obj.ObjClass
|
|
||||||
import net.sergeych.lyng.obj.ObjException
|
|
||||||
import net.sergeych.lyng.obj.ObjInstance
|
|
||||||
import net.sergeych.lyng.obj.ObjInstanceClass
|
|
||||||
import net.sergeych.lyng.obj.ObjNull
|
|
||||||
import net.sergeych.lyng.obj.ObjRecord
|
|
||||||
|
|
||||||
data class ClassDeclBaseSpec(
|
|
||||||
val name: String,
|
|
||||||
val args: List<ParsedArgument>?
|
|
||||||
)
|
|
||||||
|
|
||||||
data class ClassDeclSpec(
|
|
||||||
val declaredName: String?,
|
|
||||||
val className: String,
|
|
||||||
val typeName: String,
|
|
||||||
val startPos: Pos,
|
|
||||||
val isExtern: Boolean,
|
|
||||||
val isAbstract: Boolean,
|
|
||||||
val isObject: Boolean,
|
|
||||||
val isAnonymous: Boolean,
|
|
||||||
val baseSpecs: List<ClassDeclBaseSpec>,
|
|
||||||
val constructorArgs: ArgsDeclaration?,
|
|
||||||
val constructorFieldIds: Map<String, Int>?,
|
|
||||||
val bodyInit: Statement?,
|
|
||||||
val initScope: List<Statement>,
|
|
||||||
)
|
|
||||||
|
|
||||||
internal suspend fun executeClassDecl(scope: Scope, spec: ClassDeclSpec): Obj {
|
|
||||||
if (spec.isObject) {
|
|
||||||
val parentClasses = spec.baseSpecs.map { baseSpec ->
|
|
||||||
val rec = scope[baseSpec.name] ?: throw ScriptError(spec.startPos, "unknown base class: ${baseSpec.name}")
|
|
||||||
(rec.value as? ObjClass) ?: throw ScriptError(spec.startPos, "${baseSpec.name} is not a class")
|
|
||||||
}
|
|
||||||
|
|
||||||
val newClass = ObjInstanceClass(spec.className, *parentClasses.toTypedArray())
|
|
||||||
newClass.isAnonymous = spec.isAnonymous
|
|
||||||
newClass.constructorMeta = ArgsDeclaration(emptyList(), Token.Type.RPAREN)
|
|
||||||
for (i in parentClasses.indices) {
|
|
||||||
val argsList = spec.baseSpecs[i].args
|
|
||||||
if (argsList != null) newClass.directParentArgs[parentClasses[i]] = argsList
|
|
||||||
}
|
|
||||||
|
|
||||||
val classScope = scope.createChildScope(newThisObj = newClass)
|
|
||||||
classScope.currentClassCtx = newClass
|
|
||||||
newClass.classScope = classScope
|
|
||||||
classScope.addConst("object", newClass)
|
|
||||||
|
|
||||||
spec.bodyInit?.execute(classScope)
|
|
||||||
|
|
||||||
val instance = newClass.callOn(scope.createChildScope(Arguments.EMPTY))
|
|
||||||
if (spec.declaredName != null) {
|
|
||||||
scope.addItem(spec.declaredName, false, instance)
|
|
||||||
}
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spec.isExtern) {
|
|
||||||
val rec = scope[spec.className]
|
|
||||||
val existing = rec?.value as? ObjClass
|
|
||||||
val resolved = if (existing != null) {
|
|
||||||
existing
|
|
||||||
} else if (spec.className.contains('.')) {
|
|
||||||
scope.resolveQualifiedIdentifier(spec.className) as? ObjClass
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
val stub = resolved ?: ObjInstanceClass(spec.className).apply { this.isAbstract = true }
|
|
||||||
spec.declaredName?.let { scope.addItem(it, false, stub) }
|
|
||||||
return stub
|
|
||||||
}
|
|
||||||
|
|
||||||
val parentClasses = spec.baseSpecs.map { baseSpec ->
|
|
||||||
val rec = scope[baseSpec.name]
|
|
||||||
val cls = rec?.value as? ObjClass
|
|
||||||
if (cls != null) return@map cls
|
|
||||||
if (baseSpec.name == "Exception") return@map ObjException.Root
|
|
||||||
if (rec == null) throw ScriptError(spec.startPos, "unknown base class: ${baseSpec.name}")
|
|
||||||
throw ScriptError(spec.startPos, "${baseSpec.name} is not a class")
|
|
||||||
}
|
|
||||||
|
|
||||||
val constructorCode = object : Statement() {
|
|
||||||
override val pos: Pos = spec.startPos
|
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
|
||||||
val instance = scope.thisObj as ObjInstance
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val newClass = ObjInstanceClass(spec.className, *parentClasses.toTypedArray()).also {
|
|
||||||
it.isAbstract = spec.isAbstract
|
|
||||||
it.instanceConstructor = constructorCode
|
|
||||||
it.constructorMeta = spec.constructorArgs
|
|
||||||
for (i in parentClasses.indices) {
|
|
||||||
val argsList = spec.baseSpecs[i].args
|
|
||||||
if (argsList != null) it.directParentArgs[parentClasses[i]] = argsList
|
|
||||||
}
|
|
||||||
spec.constructorArgs?.params?.forEach { p ->
|
|
||||||
if (p.accessType != null) {
|
|
||||||
it.createField(
|
|
||||||
p.name,
|
|
||||||
ObjNull,
|
|
||||||
isMutable = p.accessType == AccessType.Var,
|
|
||||||
visibility = p.visibility ?: Visibility.Public,
|
|
||||||
declaringClass = it,
|
|
||||||
pos = Pos.builtIn,
|
|
||||||
isTransient = p.isTransient,
|
|
||||||
type = ObjRecord.Type.ConstructorField,
|
|
||||||
fieldId = spec.constructorFieldIds?.get(p.name)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spec.declaredName?.let { scope.addItem(it, false, newClass) }
|
|
||||||
val classScope = scope.createChildScope(newThisObj = newClass)
|
|
||||||
classScope.currentClassCtx = newClass
|
|
||||||
newClass.classScope = classScope
|
|
||||||
spec.bodyInit?.execute(classScope)
|
|
||||||
if (spec.initScope.isNotEmpty()) {
|
|
||||||
for (s in spec.initScope) {
|
|
||||||
s.execute(classScope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newClass.checkAbstractSatisfaction(spec.startPos)
|
|
||||||
return newClass
|
|
||||||
}
|
|
||||||
|
|
||||||
class ClassDeclStatement(
|
class ClassDeclStatement(
|
||||||
val spec: ClassDeclSpec,
|
private val delegate: Statement,
|
||||||
|
private val startPos: Pos,
|
||||||
|
val declaredName: String?,
|
||||||
) : Statement() {
|
) : Statement() {
|
||||||
override val pos: Pos = spec.startPos
|
override val pos: Pos = startPos
|
||||||
val declaredName: String? get() = spec.declaredName
|
|
||||||
val typeName: String get() = spec.typeName
|
|
||||||
|
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
return executeClassDecl(scope, spec)
|
return delegate.execute(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun callOn(scope: Scope): Obj {
|
override suspend fun callOn(scope: Scope): Obj {
|
||||||
val target = scope.parent ?: scope
|
val target = scope.parent ?: scope
|
||||||
return executeClassDecl(target, spec)
|
return delegate.execute(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,32 +84,6 @@ class ClosureScope(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
class BytecodeClosureScope(
|
|
||||||
val callScope: Scope,
|
|
||||||
val closureScope: Scope,
|
|
||||||
private val preferredThisType: String? = null
|
|
||||||
) :
|
|
||||||
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)
|
|
||||||
this.currentClassCtx = closureScope.currentClassCtx ?: callScope.currentClassCtx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ApplyScope(val callScope: Scope, val applied: Scope) :
|
class ApplyScope(val callScope: Scope, val applied: Scope) :
|
||||||
Scope(callScope, thisObj = applied.thisObj) {
|
Scope(callScope, thisObj = applied.thisObj) {
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
@ -28,8 +28,6 @@ class DelegatedVarDeclStatement(
|
|||||||
val visibility: Visibility,
|
val visibility: Visibility,
|
||||||
val initializer: Statement,
|
val initializer: Statement,
|
||||||
val isTransient: Boolean,
|
val isTransient: Boolean,
|
||||||
val slotIndex: Int?,
|
|
||||||
val scopeId: Int?,
|
|
||||||
private val startPos: Pos,
|
private val startPos: Pos,
|
||||||
) : Statement() {
|
) : Statement() {
|
||||||
override val pos: Pos = startPos
|
override val pos: Pos = startPos
|
||||||
|
|||||||
@ -17,34 +17,19 @@
|
|||||||
package net.sergeych.lyng
|
package net.sergeych.lyng
|
||||||
|
|
||||||
import net.sergeych.lyng.obj.Obj
|
import net.sergeych.lyng.obj.Obj
|
||||||
import net.sergeych.lyng.obj.ObjEnumClass
|
|
||||||
import net.sergeych.lyng.obj.ObjRecord
|
|
||||||
|
|
||||||
class EnumDeclStatement(
|
class EnumDeclStatement(
|
||||||
val declaredName: String,
|
private val delegate: Statement,
|
||||||
val qualifiedName: String,
|
|
||||||
val entries: List<String>,
|
|
||||||
val lifted: Boolean,
|
|
||||||
private val startPos: Pos,
|
private val startPos: Pos,
|
||||||
) : Statement() {
|
) : Statement() {
|
||||||
override val pos: Pos = startPos
|
override val pos: Pos = startPos
|
||||||
|
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
val enumClass = ObjEnumClass.createSimpleEnum(qualifiedName, entries)
|
return delegate.execute(scope)
|
||||||
scope.addItem(declaredName, false, enumClass, recordType = ObjRecord.Type.Enum)
|
|
||||||
if (lifted) {
|
|
||||||
for (entry in entries) {
|
|
||||||
val rec = enumClass.getInstanceMemberOrNull(entry, includeAbstract = false, includeStatic = true)
|
|
||||||
if (rec != null) {
|
|
||||||
scope.addItem(entry, false, rec.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return enumClass
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun callOn(scope: Scope): Obj {
|
override suspend fun callOn(scope: Scope): Obj {
|
||||||
val target = scope.parent ?: scope
|
val target = scope.parent ?: scope
|
||||||
return execute(target)
|
return delegate.execute(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,16 +55,3 @@ class FrameSlotRef(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RecordSlotRef(
|
|
||||||
private val record: ObjRecord,
|
|
||||||
) : net.sergeych.lyng.obj.Obj() {
|
|
||||||
fun read(): Obj {
|
|
||||||
val direct = record.value
|
|
||||||
return if (direct is FrameSlotRef) direct.read() else direct
|
|
||||||
}
|
|
||||||
|
|
||||||
fun write(value: Obj) {
|
|
||||||
record.value = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -17,247 +17,19 @@
|
|||||||
package net.sergeych.lyng
|
package net.sergeych.lyng
|
||||||
|
|
||||||
import net.sergeych.lyng.obj.Obj
|
import net.sergeych.lyng.obj.Obj
|
||||||
import net.sergeych.lyng.obj.ObjClass
|
|
||||||
import net.sergeych.lyng.obj.ObjExtensionMethodCallable
|
|
||||||
import net.sergeych.lyng.obj.ObjInstance
|
|
||||||
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 FunctionClosureBox(
|
|
||||||
var closure: Scope? = null,
|
|
||||||
var captureContext: Scope? = null,
|
|
||||||
)
|
|
||||||
|
|
||||||
data class FunctionDeclSpec(
|
|
||||||
val name: String,
|
|
||||||
val visibility: Visibility,
|
|
||||||
val isAbstract: Boolean,
|
|
||||||
val isClosed: Boolean,
|
|
||||||
val isOverride: Boolean,
|
|
||||||
val isStatic: Boolean,
|
|
||||||
val isTransient: Boolean,
|
|
||||||
val isDelegated: Boolean,
|
|
||||||
val delegateExpression: Statement?,
|
|
||||||
val extTypeName: String?,
|
|
||||||
val extensionWrapperName: String?,
|
|
||||||
val memberMethodId: Int?,
|
|
||||||
val actualExtern: Boolean,
|
|
||||||
val parentIsClassBody: Boolean,
|
|
||||||
val externCallSignature: CallSignature?,
|
|
||||||
val annotation: (suspend (Scope, ObjString, Statement) -> Statement)?,
|
|
||||||
val fnBody: Statement,
|
|
||||||
val closureBox: FunctionClosureBox,
|
|
||||||
val captureSlots: List<CaptureSlot>,
|
|
||||||
val startPos: Pos,
|
|
||||||
)
|
|
||||||
|
|
||||||
internal suspend fun executeFunctionDecl(scope: Scope, spec: FunctionDeclSpec): Obj {
|
|
||||||
if (spec.actualExtern && spec.extTypeName == null && !spec.parentIsClassBody) {
|
|
||||||
val existing = scope.get(spec.name)
|
|
||||||
if (existing != null) {
|
|
||||||
scope.addItem(
|
|
||||||
spec.name,
|
|
||||||
false,
|
|
||||||
existing.value,
|
|
||||||
spec.visibility,
|
|
||||||
callSignature = existing.callSignature
|
|
||||||
)
|
|
||||||
return existing.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spec.isDelegated) {
|
|
||||||
val delegateExpr = spec.delegateExpression ?: scope.raiseError("delegated function missing delegate")
|
|
||||||
val accessType = ObjString("Callable")
|
|
||||||
val initValue = delegateExpr.execute(scope)
|
|
||||||
val finalDelegate = try {
|
|
||||||
initValue.invokeInstanceMethod(scope, "bind", Arguments(ObjString(spec.name), accessType, scope.thisObj))
|
|
||||||
} catch (e: Exception) {
|
|
||||||
initValue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spec.extTypeName != null) {
|
|
||||||
val type = scope[spec.extTypeName]?.value ?: scope.raiseSymbolNotFound("class ${spec.extTypeName} not found")
|
|
||||||
if (type !is ObjClass) scope.raiseClassCastError("${spec.extTypeName} is not the class instance")
|
|
||||||
scope.addExtension(
|
|
||||||
type,
|
|
||||||
spec.name,
|
|
||||||
ObjRecord(ObjUnset, isMutable = false, visibility = spec.visibility, declaringClass = null, type = ObjRecord.Type.Delegated).apply {
|
|
||||||
delegate = finalDelegate
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return ObjVoid
|
|
||||||
}
|
|
||||||
|
|
||||||
val th = scope.thisObj
|
|
||||||
if (spec.isStatic) {
|
|
||||||
(th as ObjClass).createClassField(
|
|
||||||
spec.name,
|
|
||||||
ObjUnset,
|
|
||||||
false,
|
|
||||||
spec.visibility,
|
|
||||||
null,
|
|
||||||
spec.startPos,
|
|
||||||
isTransient = spec.isTransient,
|
|
||||||
type = ObjRecord.Type.Delegated
|
|
||||||
).apply {
|
|
||||||
delegate = finalDelegate
|
|
||||||
}
|
|
||||||
scope.addItem(
|
|
||||||
spec.name,
|
|
||||||
false,
|
|
||||||
ObjUnset,
|
|
||||||
spec.visibility,
|
|
||||||
recordType = ObjRecord.Type.Delegated,
|
|
||||||
isTransient = spec.isTransient
|
|
||||||
).apply {
|
|
||||||
delegate = finalDelegate
|
|
||||||
}
|
|
||||||
} else if (th is ObjClass) {
|
|
||||||
val cls: ObjClass = th
|
|
||||||
val storageName = "${cls.className}::${spec.name}"
|
|
||||||
cls.createField(
|
|
||||||
spec.name,
|
|
||||||
ObjUnset,
|
|
||||||
false,
|
|
||||||
spec.visibility,
|
|
||||||
null,
|
|
||||||
spec.startPos,
|
|
||||||
declaringClass = cls,
|
|
||||||
isAbstract = spec.isAbstract,
|
|
||||||
isClosed = spec.isClosed,
|
|
||||||
isOverride = spec.isOverride,
|
|
||||||
isTransient = spec.isTransient,
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
scope.addItem(
|
|
||||||
spec.name,
|
|
||||||
false,
|
|
||||||
ObjUnset,
|
|
||||||
spec.visibility,
|
|
||||||
recordType = ObjRecord.Type.Delegated,
|
|
||||||
isTransient = spec.isTransient
|
|
||||||
).apply {
|
|
||||||
delegate = finalDelegate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ObjVoid
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spec.isStatic || !spec.parentIsClassBody) {
|
|
||||||
spec.closureBox.closure = scope
|
|
||||||
}
|
|
||||||
if (spec.parentIsClassBody && spec.captureSlots.isNotEmpty()) {
|
|
||||||
spec.closureBox.captureContext = scope
|
|
||||||
}
|
|
||||||
|
|
||||||
val annotatedFnBody = spec.annotation?.invoke(scope, ObjString(spec.name), spec.fnBody) ?: spec.fnBody
|
|
||||||
val compiledFnBody = annotatedFnBody
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scope.addExtension(type, spec.name, ObjRecord(stmt, isMutable = false, visibility = spec.visibility, declaringClass = null))
|
|
||||||
val wrapperName = spec.extensionWrapperName ?: extensionCallableName(typeName, spec.name)
|
|
||||||
val wrapper = ObjExtensionMethodCallable(spec.name, stmt)
|
|
||||||
scope.addItem(wrapperName, false, wrapper, spec.visibility, recordType = ObjRecord.Type.Fun)
|
|
||||||
} ?: run {
|
|
||||||
val th = scope.thisObj
|
|
||||||
if (!spec.isStatic && th is ObjClass) {
|
|
||||||
val cls: ObjClass = th
|
|
||||||
cls.addFn(
|
|
||||||
spec.name,
|
|
||||||
isMutable = true,
|
|
||||||
visibility = spec.visibility,
|
|
||||||
isAbstract = spec.isAbstract,
|
|
||||||
isClosed = spec.isClosed,
|
|
||||||
isOverride = spec.isOverride,
|
|
||||||
pos = spec.startPos,
|
|
||||||
methodId = spec.memberMethodId
|
|
||||||
) {
|
|
||||||
val savedCtx = this.currentClassCtx
|
|
||||||
this.currentClassCtx = cls
|
|
||||||
try {
|
|
||||||
(thisObj as? ObjInstance)?.let { i ->
|
|
||||||
val execScope = i.instanceScope.createChildScope(
|
|
||||||
pos = this.pos,
|
|
||||||
args = this.args,
|
|
||||||
newThisObj = i
|
|
||||||
)
|
|
||||||
execScope.currentClassCtx = cls
|
|
||||||
compiledFnBody.execute(execScope)
|
|
||||||
} ?: compiledFnBody.execute(thisObj.autoInstanceScope(this))
|
|
||||||
} finally {
|
|
||||||
this.currentClassCtx = savedCtx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scope.addItem(spec.name, false, compiledFnBody, spec.visibility, callSignature = spec.externCallSignature)
|
|
||||||
compiledFnBody
|
|
||||||
} else {
|
|
||||||
scope.addItem(spec.name, false, compiledFnBody, spec.visibility, callSignature = spec.externCallSignature)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return annotatedFnBody
|
|
||||||
}
|
|
||||||
|
|
||||||
class FunctionDeclStatement(
|
class FunctionDeclStatement(
|
||||||
val spec: FunctionDeclSpec,
|
private val delegate: Statement,
|
||||||
|
private val startPos: Pos,
|
||||||
) : Statement() {
|
) : Statement() {
|
||||||
override val pos: Pos = spec.startPos
|
override val pos: Pos = startPos
|
||||||
|
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
return executeFunctionDecl(scope, spec)
|
return delegate.execute(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun callOn(scope: Scope): Obj {
|
override suspend fun callOn(scope: Scope): Obj {
|
||||||
val target = scope.parent ?: scope
|
val target = scope.parent ?: scope
|
||||||
return executeFunctionDecl(target, spec)
|
return delegate.execute(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,8 +59,6 @@ open class Scope(
|
|||||||
// Enabled by default for child scopes; module/class scopes can ignore it.
|
// Enabled by default for child scopes; module/class scopes can ignore it.
|
||||||
private val slots: MutableList<ObjRecord> = mutableListOf()
|
private val slots: MutableList<ObjRecord> = mutableListOf()
|
||||||
private val nameToSlot: MutableMap<String, Int> = mutableMapOf()
|
private val nameToSlot: MutableMap<String, Int> = mutableMapOf()
|
||||||
internal var captureRecords: List<ObjRecord>? = null
|
|
||||||
internal var captureNames: List<String>? = null
|
|
||||||
/**
|
/**
|
||||||
* Auxiliary per-frame map of local bindings (locals declared in this frame).
|
* Auxiliary per-frame map of local bindings (locals declared in this frame).
|
||||||
* This helps resolving locals across suspension when slot ownership isn't
|
* This helps resolving locals across suspension when slot ownership isn't
|
||||||
@ -171,9 +169,6 @@ open class Scope(
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun resolveCaptureRecord(name: String): ObjRecord? {
|
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)
|
return chainLookupIgnoreClosure(name, followClosure = true, caller = currentClassCtx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -836,10 +831,6 @@ open class Scope(
|
|||||||
open fun applyClosure(closure: Scope, preferredThisType: String? = null): Scope =
|
open fun applyClosure(closure: Scope, preferredThisType: String? = null): Scope =
|
||||||
ClosureScope(this, closure, preferredThisType)
|
ClosureScope(this, closure, preferredThisType)
|
||||||
|
|
||||||
internal fun applyClosureForBytecode(closure: Scope, preferredThisType: String? = null): Scope {
|
|
||||||
return BytecodeClosureScope(this, closure, preferredThisType)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve and evaluate a qualified identifier exactly as compiled code would.
|
* Resolve and evaluate a qualified identifier exactly as compiled code would.
|
||||||
* For input like `A.B.C`, it builds the same ObjRef chain the compiler emits:
|
* For input like `A.B.C`, it builds the same ObjRef chain the compiler emits:
|
||||||
|
|||||||
@ -84,7 +84,7 @@ class Script(
|
|||||||
seedModuleSlots(moduleTarget)
|
seedModuleSlots(moduleTarget)
|
||||||
}
|
}
|
||||||
moduleBytecode?.let { fn ->
|
moduleBytecode?.let { fn ->
|
||||||
return CmdVm().execute(fn, scope, scope.args)
|
return CmdVm().execute(fn, scope, scope.args.list)
|
||||||
}
|
}
|
||||||
var lastResult: Obj = ObjVoid
|
var lastResult: Obj = ObjVoid
|
||||||
for (s in statements) {
|
for (s in statements) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package net.sergeych.lyng.bytecode
|
package net.sergeych.lyng.bytecode
|
||||||
|
|
||||||
import net.sergeych.lyng.ArgsDeclaration
|
|
||||||
import net.sergeych.lyng.Pos
|
import net.sergeych.lyng.Pos
|
||||||
import net.sergeych.lyng.Visibility
|
import net.sergeych.lyng.Visibility
|
||||||
import net.sergeych.lyng.obj.ListLiteralRef
|
import net.sergeych.lyng.obj.ListLiteralRef
|
||||||
@ -31,32 +30,11 @@ sealed class BytecodeConst {
|
|||||||
data class StringVal(val value: String) : BytecodeConst()
|
data class StringVal(val value: String) : BytecodeConst()
|
||||||
data class PosVal(val pos: Pos) : BytecodeConst()
|
data class PosVal(val pos: Pos) : BytecodeConst()
|
||||||
data class ObjRef(val value: Obj) : BytecodeConst()
|
data class ObjRef(val value: Obj) : BytecodeConst()
|
||||||
|
data class Ref(val value: net.sergeych.lyng.obj.ObjRef) : BytecodeConst()
|
||||||
|
data class StatementVal(val statement: net.sergeych.lyng.Statement) : BytecodeConst()
|
||||||
data class ListLiteralPlan(val spreads: List<Boolean>) : BytecodeConst()
|
data class ListLiteralPlan(val spreads: List<Boolean>) : BytecodeConst()
|
||||||
data class LambdaFn(
|
data class ValueFn(val fn: suspend (net.sergeych.lyng.Scope) -> net.sergeych.lyng.obj.ObjRecord) : BytecodeConst()
|
||||||
val fn: CmdFunction,
|
|
||||||
val captureTableId: Int?,
|
|
||||||
val captureNames: List<String>,
|
|
||||||
val paramSlotPlan: Map<String, Int>,
|
|
||||||
val argsDeclaration: ArgsDeclaration?,
|
|
||||||
val preferredThisType: String?,
|
|
||||||
val wrapAsExtensionCallable: Boolean,
|
|
||||||
val returnLabels: Set<String>,
|
|
||||||
val pos: Pos,
|
|
||||||
) : BytecodeConst()
|
|
||||||
data class EnumDecl(
|
|
||||||
val declaredName: String,
|
|
||||||
val qualifiedName: String,
|
|
||||||
val entries: List<String>,
|
|
||||||
val lifted: Boolean,
|
|
||||||
) : BytecodeConst()
|
|
||||||
data class FunctionDecl(
|
|
||||||
val spec: net.sergeych.lyng.FunctionDeclSpec,
|
|
||||||
) : BytecodeConst()
|
|
||||||
data class ClassDecl(
|
|
||||||
val spec: net.sergeych.lyng.ClassDeclSpec,
|
|
||||||
) : BytecodeConst()
|
|
||||||
data class SlotPlan(val plan: Map<String, Int>, val captures: List<String> = emptyList()) : BytecodeConst()
|
data class SlotPlan(val plan: Map<String, Int>, val captures: List<String> = emptyList()) : BytecodeConst()
|
||||||
data class CaptureTable(val entries: List<BytecodeCaptureEntry>) : BytecodeConst()
|
|
||||||
data class ExtensionPropertyDecl(
|
data class ExtensionPropertyDecl(
|
||||||
val extTypeName: String,
|
val extTypeName: String,
|
||||||
val property: ObjProperty,
|
val property: ObjProperty,
|
||||||
|
|||||||
@ -40,23 +40,9 @@ class BytecodeFrame(
|
|||||||
slotTypes[slot] = type.code
|
slotTypes[slot] = type.code
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getObj(slot: Int): Obj {
|
override fun getObj(slot: Int): Obj = objSlots[slot] ?: ObjNull
|
||||||
val value = objSlots[slot] ?: return ObjNull
|
|
||||||
return when (value) {
|
|
||||||
is net.sergeych.lyng.FrameSlotRef -> value.read()
|
|
||||||
is net.sergeych.lyng.RecordSlotRef -> value.read()
|
|
||||||
else -> value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun getRawObj(slot: Int): Obj? = objSlots[slot]
|
|
||||||
|
|
||||||
override fun setObj(slot: Int, value: Obj) {
|
override fun setObj(slot: Int, value: Obj) {
|
||||||
when (val current = objSlots[slot]) {
|
objSlots[slot] = value
|
||||||
is net.sergeych.lyng.FrameSlotRef -> current.write(value)
|
|
||||||
is net.sergeych.lyng.RecordSlotRef -> current.write(value)
|
|
||||||
else -> objSlots[slot] = value
|
|
||||||
}
|
|
||||||
slotTypes[slot] = SlotType.OBJ.code
|
slotTypes[slot] = SlotType.OBJ.code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,6 @@ package net.sergeych.lyng.bytecode
|
|||||||
import net.sergeych.lyng.*
|
import net.sergeych.lyng.*
|
||||||
import net.sergeych.lyng.obj.Obj
|
import net.sergeych.lyng.obj.Obj
|
||||||
import net.sergeych.lyng.obj.ObjClass
|
import net.sergeych.lyng.obj.ObjClass
|
||||||
import net.sergeych.lyng.obj.ValueFnRef
|
|
||||||
|
|
||||||
class BytecodeStatement private constructor(
|
class BytecodeStatement private constructor(
|
||||||
val original: Statement,
|
val original: Statement,
|
||||||
@ -30,7 +29,7 @@ class BytecodeStatement private constructor(
|
|||||||
|
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
scope.pos = pos
|
scope.pos = pos
|
||||||
return CmdVm().execute(function, scope, scope.args)
|
return CmdVm().execute(function, scope, scope.args.list)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun bytecodeFunction(): CmdFunction = function
|
internal fun bytecodeFunction(): CmdFunction = function
|
||||||
@ -44,8 +43,6 @@ class BytecodeStatement private constructor(
|
|||||||
rangeLocalNames: Set<String> = emptySet(),
|
rangeLocalNames: Set<String> = emptySet(),
|
||||||
allowedScopeNames: Set<String>? = null,
|
allowedScopeNames: Set<String>? = null,
|
||||||
moduleScopeId: Int? = null,
|
moduleScopeId: Int? = null,
|
||||||
forcedLocalSlots: Map<String, Int> = emptyMap(),
|
|
||||||
forcedLocalScopeId: Int? = null,
|
|
||||||
slotTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
|
slotTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
|
||||||
knownNameObjClass: Map<String, ObjClass> = emptyMap(),
|
knownNameObjClass: Map<String, ObjClass> = emptyMap(),
|
||||||
knownObjectNames: Set<String> = emptySet(),
|
knownObjectNames: Set<String> = emptySet(),
|
||||||
@ -53,7 +50,6 @@ class BytecodeStatement private constructor(
|
|||||||
enumEntriesByName: Map<String, List<String>> = emptyMap(),
|
enumEntriesByName: Map<String, List<String>> = emptyMap(),
|
||||||
callableReturnTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
|
callableReturnTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
|
||||||
callableReturnTypeByName: Map<String, ObjClass> = emptyMap(),
|
callableReturnTypeByName: Map<String, ObjClass> = emptyMap(),
|
||||||
lambdaCaptureEntriesByRef: Map<ValueFnRef, List<LambdaCaptureEntry>> = emptyMap(),
|
|
||||||
): Statement {
|
): Statement {
|
||||||
if (statement is BytecodeStatement) return statement
|
if (statement is BytecodeStatement) return statement
|
||||||
val hasUnsupported = containsUnsupportedStatement(statement)
|
val hasUnsupported = containsUnsupportedStatement(statement)
|
||||||
@ -71,16 +67,13 @@ class BytecodeStatement private constructor(
|
|||||||
rangeLocalNames = rangeLocalNames,
|
rangeLocalNames = rangeLocalNames,
|
||||||
allowedScopeNames = allowedScopeNames,
|
allowedScopeNames = allowedScopeNames,
|
||||||
moduleScopeId = moduleScopeId,
|
moduleScopeId = moduleScopeId,
|
||||||
forcedLocalSlots = forcedLocalSlots,
|
|
||||||
forcedLocalScopeId = forcedLocalScopeId,
|
|
||||||
slotTypeByScopeId = slotTypeByScopeId,
|
slotTypeByScopeId = slotTypeByScopeId,
|
||||||
knownNameObjClass = knownNameObjClass,
|
knownNameObjClass = knownNameObjClass,
|
||||||
knownObjectNames = knownObjectNames,
|
knownObjectNames = knownObjectNames,
|
||||||
classFieldTypesByName = classFieldTypesByName,
|
classFieldTypesByName = classFieldTypesByName,
|
||||||
enumEntriesByName = enumEntriesByName,
|
enumEntriesByName = enumEntriesByName,
|
||||||
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
||||||
callableReturnTypeByName = callableReturnTypeByName,
|
callableReturnTypeByName = callableReturnTypeByName
|
||||||
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
|
|
||||||
)
|
)
|
||||||
val compiled = compiler.compileStatement(nameHint, statement)
|
val compiled = compiler.compileStatement(nameHint, statement)
|
||||||
val fn = compiled ?: throw BytecodeCompileException(
|
val fn = compiled ?: throw BytecodeCompileException(
|
||||||
@ -139,7 +132,6 @@ class BytecodeStatement private constructor(
|
|||||||
target.resultExpr?.let { containsUnsupportedStatement(it) } ?: false
|
target.resultExpr?.let { containsUnsupportedStatement(it) } ?: false
|
||||||
is net.sergeych.lyng.ThrowStatement ->
|
is net.sergeych.lyng.ThrowStatement ->
|
||||||
containsUnsupportedStatement(target.throwExpr)
|
containsUnsupportedStatement(target.throwExpr)
|
||||||
is net.sergeych.lyng.NopStatement -> false
|
|
||||||
is net.sergeych.lyng.ExtensionPropertyDeclStatement -> false
|
is net.sergeych.lyng.ExtensionPropertyDeclStatement -> false
|
||||||
is net.sergeych.lyng.ClassDeclStatement -> false
|
is net.sergeych.lyng.ClassDeclStatement -> false
|
||||||
is net.sergeych.lyng.FunctionDeclStatement -> false
|
is net.sergeych.lyng.FunctionDeclStatement -> false
|
||||||
@ -216,7 +208,6 @@ class BytecodeStatement private constructor(
|
|||||||
stmt.label,
|
stmt.label,
|
||||||
stmt.canBreak,
|
stmt.canBreak,
|
||||||
stmt.loopSlotPlan,
|
stmt.loopSlotPlan,
|
||||||
stmt.loopScopeId,
|
|
||||||
stmt.pos
|
stmt.pos
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,9 +68,7 @@ class CmdBuilder {
|
|||||||
scopeSlotNames: Array<String?> = emptyArray(),
|
scopeSlotNames: Array<String?> = emptyArray(),
|
||||||
scopeSlotIsModule: BooleanArray = BooleanArray(0),
|
scopeSlotIsModule: BooleanArray = BooleanArray(0),
|
||||||
localSlotNames: Array<String?> = emptyArray(),
|
localSlotNames: Array<String?> = emptyArray(),
|
||||||
localSlotMutables: BooleanArray = BooleanArray(0),
|
localSlotMutables: BooleanArray = BooleanArray(0)
|
||||||
localSlotDelegated: BooleanArray = BooleanArray(0),
|
|
||||||
localSlotCaptures: BooleanArray = BooleanArray(0)
|
|
||||||
): CmdFunction {
|
): CmdFunction {
|
||||||
val scopeSlotCount = scopeSlotIndices.size
|
val scopeSlotCount = scopeSlotIndices.size
|
||||||
require(scopeSlotNames.isEmpty() || scopeSlotNames.size == scopeSlotCount) {
|
require(scopeSlotNames.isEmpty() || scopeSlotNames.size == scopeSlotCount) {
|
||||||
@ -80,8 +78,6 @@ class CmdBuilder {
|
|||||||
"scope slot module mapping size mismatch"
|
"scope slot module mapping size mismatch"
|
||||||
}
|
}
|
||||||
require(localSlotNames.size == localSlotMutables.size) { "local slot metadata size mismatch" }
|
require(localSlotNames.size == localSlotMutables.size) { "local slot metadata size mismatch" }
|
||||||
require(localSlotNames.size == localSlotDelegated.size) { "local slot delegation size mismatch" }
|
|
||||||
require(localSlotNames.size == localSlotCaptures.size) { "local slot capture size mismatch" }
|
|
||||||
val labelIps = mutableMapOf<Label, Int>()
|
val labelIps = mutableMapOf<Label, Int>()
|
||||||
for ((label, idx) in labelPositions) {
|
for ((label, idx) in labelPositions) {
|
||||||
labelIps[label] = idx
|
labelIps[label] = idx
|
||||||
@ -115,8 +111,6 @@ class CmdBuilder {
|
|||||||
scopeSlotIsModule = if (scopeSlotIsModule.isEmpty()) BooleanArray(scopeSlotCount) else scopeSlotIsModule,
|
scopeSlotIsModule = if (scopeSlotIsModule.isEmpty()) BooleanArray(scopeSlotCount) else scopeSlotIsModule,
|
||||||
localSlotNames = localSlotNames,
|
localSlotNames = localSlotNames,
|
||||||
localSlotMutables = localSlotMutables,
|
localSlotMutables = localSlotMutables,
|
||||||
localSlotDelegated = localSlotDelegated,
|
|
||||||
localSlotCaptures = localSlotCaptures,
|
|
||||||
constants = constPool.toList(),
|
constants = constPool.toList(),
|
||||||
cmds = cmds.toTypedArray(),
|
cmds = cmds.toTypedArray(),
|
||||||
posByIp = posByInstr.toTypedArray()
|
posByIp = posByInstr.toTypedArray()
|
||||||
@ -129,7 +123,7 @@ class CmdBuilder {
|
|||||||
Opcode.CLEAR_PENDING_THROWABLE, Opcode.RETHROW_PENDING -> emptyList()
|
Opcode.CLEAR_PENDING_THROWABLE, Opcode.RETHROW_PENDING -> emptyList()
|
||||||
Opcode.MOVE_OBJ, Opcode.MOVE_INT, Opcode.MOVE_REAL, Opcode.MOVE_BOOL, Opcode.BOX_OBJ,
|
Opcode.MOVE_OBJ, Opcode.MOVE_INT, Opcode.MOVE_REAL, Opcode.MOVE_BOOL, Opcode.BOX_OBJ,
|
||||||
Opcode.INT_TO_REAL, Opcode.REAL_TO_INT, Opcode.BOOL_TO_INT, Opcode.INT_TO_BOOL,
|
Opcode.INT_TO_REAL, Opcode.REAL_TO_INT, Opcode.BOOL_TO_INT, Opcode.INT_TO_BOOL,
|
||||||
Opcode.OBJ_TO_BOOL, Opcode.GET_OBJ_CLASS,
|
Opcode.OBJ_TO_BOOL,
|
||||||
Opcode.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT,
|
Opcode.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT,
|
||||||
Opcode.ASSERT_IS ->
|
Opcode.ASSERT_IS ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT)
|
||||||
@ -141,27 +135,19 @@ class CmdBuilder {
|
|||||||
listOf(OperandKind.CONST, OperandKind.SLOT)
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.RESOLVE_SCOPE_SLOT ->
|
Opcode.RESOLVE_SCOPE_SLOT ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
||||||
Opcode.DELEGATED_GET_LOCAL ->
|
|
||||||
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT)
|
|
||||||
Opcode.DELEGATED_SET_LOCAL ->
|
|
||||||
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT)
|
|
||||||
Opcode.BIND_DELEGATE_LOCAL ->
|
|
||||||
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.CONST, OperandKind.SLOT)
|
|
||||||
Opcode.LOAD_OBJ_ADDR, Opcode.LOAD_INT_ADDR, Opcode.LOAD_REAL_ADDR, Opcode.LOAD_BOOL_ADDR ->
|
Opcode.LOAD_OBJ_ADDR, Opcode.LOAD_INT_ADDR, Opcode.LOAD_REAL_ADDR, Opcode.LOAD_BOOL_ADDR ->
|
||||||
listOf(OperandKind.ADDR, OperandKind.SLOT)
|
listOf(OperandKind.ADDR, OperandKind.SLOT)
|
||||||
Opcode.STORE_OBJ_ADDR, Opcode.STORE_INT_ADDR, Opcode.STORE_REAL_ADDR, Opcode.STORE_BOOL_ADDR ->
|
Opcode.STORE_OBJ_ADDR, Opcode.STORE_INT_ADDR, Opcode.STORE_REAL_ADDR, Opcode.STORE_BOOL_ADDR ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
||||||
Opcode.CONST_NULL ->
|
Opcode.CONST_NULL ->
|
||||||
listOf(OperandKind.SLOT)
|
listOf(OperandKind.SLOT)
|
||||||
Opcode.CONST_OBJ, Opcode.CONST_INT, Opcode.CONST_REAL, Opcode.CONST_BOOL,
|
Opcode.CONST_OBJ, Opcode.CONST_INT, Opcode.CONST_REAL, Opcode.CONST_BOOL, Opcode.MAKE_VALUE_FN ->
|
||||||
Opcode.MAKE_LAMBDA_FN ->
|
|
||||||
listOf(OperandKind.CONST, OperandKind.SLOT)
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.PUSH_SCOPE, Opcode.PUSH_SLOT_PLAN ->
|
Opcode.PUSH_SCOPE, Opcode.PUSH_SLOT_PLAN ->
|
||||||
listOf(OperandKind.CONST)
|
listOf(OperandKind.CONST)
|
||||||
Opcode.PUSH_TRY ->
|
Opcode.PUSH_TRY ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP)
|
listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP)
|
||||||
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE,
|
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE,
|
||||||
Opcode.DECL_ENUM, Opcode.DECL_FUNCTION, Opcode.DECL_CLASS,
|
|
||||||
Opcode.ASSIGN_DESTRUCTURE ->
|
Opcode.ASSIGN_DESTRUCTURE ->
|
||||||
listOf(OperandKind.CONST, OperandKind.SLOT)
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
|
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
|
||||||
@ -254,10 +240,9 @@ class CmdBuilder {
|
|||||||
Opcode.CONST_REAL -> CmdConstReal(operands[0], operands[1])
|
Opcode.CONST_REAL -> CmdConstReal(operands[0], operands[1])
|
||||||
Opcode.CONST_BOOL -> CmdConstBool(operands[0], operands[1])
|
Opcode.CONST_BOOL -> CmdConstBool(operands[0], operands[1])
|
||||||
Opcode.CONST_NULL -> CmdConstNull(operands[0])
|
Opcode.CONST_NULL -> CmdConstNull(operands[0])
|
||||||
Opcode.MAKE_LAMBDA_FN -> CmdMakeLambda(operands[0], operands[1])
|
Opcode.MAKE_VALUE_FN -> CmdMakeValueFn(operands[0], operands[1])
|
||||||
Opcode.BOX_OBJ -> CmdBoxObj(operands[0], operands[1])
|
Opcode.BOX_OBJ -> CmdBoxObj(operands[0], operands[1])
|
||||||
Opcode.OBJ_TO_BOOL -> CmdObjToBool(operands[0], operands[1])
|
Opcode.OBJ_TO_BOOL -> CmdObjToBool(operands[0], operands[1])
|
||||||
Opcode.GET_OBJ_CLASS -> CmdGetObjClass(operands[0], operands[1])
|
|
||||||
Opcode.RANGE_INT_BOUNDS -> CmdRangeIntBounds(operands[0], operands[1], operands[2], operands[3])
|
Opcode.RANGE_INT_BOUNDS -> CmdRangeIntBounds(operands[0], operands[1], operands[2], operands[3])
|
||||||
Opcode.LOAD_THIS -> CmdLoadThis(operands[0])
|
Opcode.LOAD_THIS -> CmdLoadThis(operands[0])
|
||||||
Opcode.LOAD_THIS_VARIANT -> CmdLoadThisVariant(operands[0], operands[1])
|
Opcode.LOAD_THIS_VARIANT -> CmdLoadThisVariant(operands[0], operands[1])
|
||||||
@ -269,9 +254,6 @@ class CmdBuilder {
|
|||||||
Opcode.THROW -> CmdThrow(operands[0], operands[1])
|
Opcode.THROW -> CmdThrow(operands[0], operands[1])
|
||||||
Opcode.RETHROW_PENDING -> CmdRethrowPending()
|
Opcode.RETHROW_PENDING -> CmdRethrowPending()
|
||||||
Opcode.RESOLVE_SCOPE_SLOT -> CmdResolveScopeSlot(operands[0], operands[1])
|
Opcode.RESOLVE_SCOPE_SLOT -> CmdResolveScopeSlot(operands[0], operands[1])
|
||||||
Opcode.DELEGATED_GET_LOCAL -> CmdDelegatedGetLocal(operands[0], operands[1], operands[2])
|
|
||||||
Opcode.DELEGATED_SET_LOCAL -> CmdDelegatedSetLocal(operands[0], operands[1], operands[2])
|
|
||||||
Opcode.BIND_DELEGATE_LOCAL -> CmdBindDelegateLocal(operands[0], operands[1], operands[2], operands[3])
|
|
||||||
Opcode.LOAD_OBJ_ADDR -> CmdLoadObjAddr(operands[0], operands[1])
|
Opcode.LOAD_OBJ_ADDR -> CmdLoadObjAddr(operands[0], operands[1])
|
||||||
Opcode.STORE_OBJ_ADDR -> CmdStoreObjAddr(operands[0], operands[1])
|
Opcode.STORE_OBJ_ADDR -> CmdStoreObjAddr(operands[0], operands[1])
|
||||||
Opcode.LOAD_INT_ADDR -> CmdLoadIntAddr(operands[0], operands[1])
|
Opcode.LOAD_INT_ADDR -> CmdLoadIntAddr(operands[0], operands[1])
|
||||||
@ -415,9 +397,6 @@ class CmdBuilder {
|
|||||||
Opcode.DECL_LOCAL -> CmdDeclLocal(operands[0], operands[1])
|
Opcode.DECL_LOCAL -> CmdDeclLocal(operands[0], operands[1])
|
||||||
Opcode.DECL_DELEGATED -> CmdDeclDelegated(operands[0], operands[1])
|
Opcode.DECL_DELEGATED -> CmdDeclDelegated(operands[0], operands[1])
|
||||||
Opcode.DECL_DESTRUCTURE -> CmdDeclDestructure(operands[0], operands[1])
|
Opcode.DECL_DESTRUCTURE -> CmdDeclDestructure(operands[0], operands[1])
|
||||||
Opcode.DECL_ENUM -> CmdDeclEnum(operands[0], operands[1])
|
|
||||||
Opcode.DECL_FUNCTION -> CmdDeclFunction(operands[0], operands[1])
|
|
||||||
Opcode.DECL_CLASS -> CmdDeclClass(operands[0], operands[1])
|
|
||||||
Opcode.DECL_EXT_PROPERTY -> CmdDeclExtProperty(operands[0], operands[1])
|
Opcode.DECL_EXT_PROPERTY -> CmdDeclExtProperty(operands[0], operands[1])
|
||||||
Opcode.CALL_DIRECT -> CmdCallDirect(operands[0], operands[1], operands[2], operands[3])
|
Opcode.CALL_DIRECT -> CmdCallDirect(operands[0], operands[1], operands[2], operands[3])
|
||||||
Opcode.ASSIGN_DESTRUCTURE -> CmdAssignDestructure(operands[0], operands[1])
|
Opcode.ASSIGN_DESTRUCTURE -> CmdAssignDestructure(operands[0], operands[1])
|
||||||
|
|||||||
@ -50,23 +50,6 @@ object CmdDisassembler {
|
|||||||
}
|
}
|
||||||
out.append('\n')
|
out.append('\n')
|
||||||
}
|
}
|
||||||
val captureConsts = fn.constants.withIndex().mapNotNull { (idx, constVal) ->
|
|
||||||
val table = constVal as? BytecodeConst.CaptureTable ?: return@mapNotNull null
|
|
||||||
idx to table
|
|
||||||
}
|
|
||||||
if (captureConsts.isNotEmpty()) {
|
|
||||||
out.append("consts:\n")
|
|
||||||
for ((idx, table) in captureConsts) {
|
|
||||||
val entries = if (table.entries.isEmpty()) {
|
|
||||||
"[]"
|
|
||||||
} else {
|
|
||||||
table.entries.joinToString(prefix = "[", postfix = "]") { entry ->
|
|
||||||
"${entry.ownerKind}#${entry.ownerScopeId}:${entry.ownerSlotId}@s${entry.slotIndex}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.append("k").append(idx).append(" CAPTURE_TABLE ").append(entries).append('\n')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out.toString()
|
return out.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,10 +69,9 @@ object CmdDisassembler {
|
|||||||
is CmdLoadThis -> Opcode.LOAD_THIS to intArrayOf(cmd.dst)
|
is CmdLoadThis -> Opcode.LOAD_THIS to intArrayOf(cmd.dst)
|
||||||
is CmdLoadThisVariant -> Opcode.LOAD_THIS_VARIANT to intArrayOf(cmd.typeId, cmd.dst)
|
is CmdLoadThisVariant -> Opcode.LOAD_THIS_VARIANT to intArrayOf(cmd.typeId, cmd.dst)
|
||||||
is CmdConstNull -> Opcode.CONST_NULL to intArrayOf(cmd.dst)
|
is CmdConstNull -> Opcode.CONST_NULL to intArrayOf(cmd.dst)
|
||||||
is CmdMakeLambda -> Opcode.MAKE_LAMBDA_FN to intArrayOf(cmd.id, cmd.dst)
|
is CmdMakeValueFn -> Opcode.MAKE_VALUE_FN to intArrayOf(cmd.id, cmd.dst)
|
||||||
is CmdBoxObj -> Opcode.BOX_OBJ to intArrayOf(cmd.src, cmd.dst)
|
is CmdBoxObj -> Opcode.BOX_OBJ to intArrayOf(cmd.src, cmd.dst)
|
||||||
is CmdObjToBool -> Opcode.OBJ_TO_BOOL to intArrayOf(cmd.src, cmd.dst)
|
is CmdObjToBool -> Opcode.OBJ_TO_BOOL to intArrayOf(cmd.src, cmd.dst)
|
||||||
is CmdGetObjClass -> Opcode.GET_OBJ_CLASS to intArrayOf(cmd.src, cmd.dst)
|
|
||||||
is CmdCheckIs -> Opcode.CHECK_IS to intArrayOf(cmd.objSlot, cmd.typeSlot, cmd.dst)
|
is CmdCheckIs -> Opcode.CHECK_IS to intArrayOf(cmd.objSlot, cmd.typeSlot, cmd.dst)
|
||||||
is CmdAssertIs -> Opcode.ASSERT_IS to intArrayOf(cmd.objSlot, cmd.typeSlot)
|
is CmdAssertIs -> Opcode.ASSERT_IS to intArrayOf(cmd.objSlot, cmd.typeSlot)
|
||||||
is CmdMakeQualifiedView -> Opcode.MAKE_QUALIFIED_VIEW to intArrayOf(cmd.objSlot, cmd.typeSlot, cmd.dst)
|
is CmdMakeQualifiedView -> Opcode.MAKE_QUALIFIED_VIEW to intArrayOf(cmd.objSlot, cmd.typeSlot, cmd.dst)
|
||||||
@ -102,9 +84,6 @@ object CmdDisassembler {
|
|||||||
cmd.dst
|
cmd.dst
|
||||||
)
|
)
|
||||||
is CmdResolveScopeSlot -> Opcode.RESOLVE_SCOPE_SLOT to intArrayOf(cmd.scopeSlot, cmd.addrSlot)
|
is CmdResolveScopeSlot -> Opcode.RESOLVE_SCOPE_SLOT to intArrayOf(cmd.scopeSlot, cmd.addrSlot)
|
||||||
is CmdDelegatedGetLocal -> Opcode.DELEGATED_GET_LOCAL to intArrayOf(cmd.delegateSlot, cmd.nameId, cmd.dst)
|
|
||||||
is CmdDelegatedSetLocal -> Opcode.DELEGATED_SET_LOCAL to intArrayOf(cmd.delegateSlot, cmd.nameId, cmd.valueSlot)
|
|
||||||
is CmdBindDelegateLocal -> Opcode.BIND_DELEGATE_LOCAL to intArrayOf(cmd.delegateSlot, cmd.nameId, cmd.accessId, cmd.dst)
|
|
||||||
is CmdLoadObjAddr -> Opcode.LOAD_OBJ_ADDR to intArrayOf(cmd.addrSlot, cmd.dst)
|
is CmdLoadObjAddr -> Opcode.LOAD_OBJ_ADDR to intArrayOf(cmd.addrSlot, cmd.dst)
|
||||||
is CmdStoreObjAddr -> Opcode.STORE_OBJ_ADDR to intArrayOf(cmd.src, cmd.addrSlot)
|
is CmdStoreObjAddr -> Opcode.STORE_OBJ_ADDR to intArrayOf(cmd.src, cmd.addrSlot)
|
||||||
is CmdLoadIntAddr -> Opcode.LOAD_INT_ADDR to intArrayOf(cmd.addrSlot, cmd.dst)
|
is CmdLoadIntAddr -> Opcode.LOAD_INT_ADDR to intArrayOf(cmd.addrSlot, cmd.dst)
|
||||||
@ -212,9 +191,6 @@ object CmdDisassembler {
|
|||||||
is CmdDeclLocal -> Opcode.DECL_LOCAL to intArrayOf(cmd.constId, cmd.slot)
|
is CmdDeclLocal -> Opcode.DECL_LOCAL to intArrayOf(cmd.constId, cmd.slot)
|
||||||
is CmdDeclDelegated -> Opcode.DECL_DELEGATED to intArrayOf(cmd.constId, cmd.slot)
|
is CmdDeclDelegated -> Opcode.DECL_DELEGATED to intArrayOf(cmd.constId, cmd.slot)
|
||||||
is CmdDeclDestructure -> Opcode.DECL_DESTRUCTURE to intArrayOf(cmd.constId, cmd.slot)
|
is CmdDeclDestructure -> Opcode.DECL_DESTRUCTURE to intArrayOf(cmd.constId, cmd.slot)
|
||||||
is 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 CmdDeclExtProperty -> Opcode.DECL_EXT_PROPERTY to intArrayOf(cmd.constId, cmd.slot)
|
is CmdDeclExtProperty -> Opcode.DECL_EXT_PROPERTY to intArrayOf(cmd.constId, cmd.slot)
|
||||||
is CmdCallDirect -> Opcode.CALL_DIRECT to intArrayOf(cmd.id, cmd.argBase, cmd.argCount, cmd.dst)
|
is CmdCallDirect -> Opcode.CALL_DIRECT to intArrayOf(cmd.id, cmd.argBase, cmd.argCount, cmd.dst)
|
||||||
is CmdAssignDestructure -> Opcode.ASSIGN_DESTRUCTURE to intArrayOf(cmd.constId, cmd.slot)
|
is CmdAssignDestructure -> Opcode.ASSIGN_DESTRUCTURE to intArrayOf(cmd.constId, cmd.slot)
|
||||||
@ -255,7 +231,7 @@ object CmdDisassembler {
|
|||||||
Opcode.INT_TO_REAL, Opcode.REAL_TO_INT, Opcode.BOOL_TO_INT, Opcode.INT_TO_BOOL,
|
Opcode.INT_TO_REAL, Opcode.REAL_TO_INT, Opcode.BOOL_TO_INT, Opcode.INT_TO_BOOL,
|
||||||
Opcode.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT ->
|
Opcode.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT)
|
||||||
Opcode.OBJ_TO_BOOL, Opcode.GET_OBJ_CLASS, Opcode.ASSERT_IS ->
|
Opcode.OBJ_TO_BOOL, Opcode.ASSERT_IS ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT)
|
||||||
Opcode.CHECK_IS, Opcode.MAKE_QUALIFIED_VIEW ->
|
Opcode.CHECK_IS, Opcode.MAKE_QUALIFIED_VIEW ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||||
@ -267,27 +243,19 @@ object CmdDisassembler {
|
|||||||
listOf(OperandKind.CONST, OperandKind.SLOT)
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.RESOLVE_SCOPE_SLOT ->
|
Opcode.RESOLVE_SCOPE_SLOT ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
||||||
Opcode.DELEGATED_GET_LOCAL ->
|
|
||||||
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT)
|
|
||||||
Opcode.DELEGATED_SET_LOCAL ->
|
|
||||||
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT)
|
|
||||||
Opcode.BIND_DELEGATE_LOCAL ->
|
|
||||||
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.CONST, OperandKind.SLOT)
|
|
||||||
Opcode.LOAD_OBJ_ADDR, Opcode.LOAD_INT_ADDR, Opcode.LOAD_REAL_ADDR, Opcode.LOAD_BOOL_ADDR ->
|
Opcode.LOAD_OBJ_ADDR, Opcode.LOAD_INT_ADDR, Opcode.LOAD_REAL_ADDR, Opcode.LOAD_BOOL_ADDR ->
|
||||||
listOf(OperandKind.ADDR, OperandKind.SLOT)
|
listOf(OperandKind.ADDR, OperandKind.SLOT)
|
||||||
Opcode.STORE_OBJ_ADDR, Opcode.STORE_INT_ADDR, Opcode.STORE_REAL_ADDR, Opcode.STORE_BOOL_ADDR ->
|
Opcode.STORE_OBJ_ADDR, Opcode.STORE_INT_ADDR, Opcode.STORE_REAL_ADDR, Opcode.STORE_BOOL_ADDR ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
||||||
Opcode.CONST_NULL ->
|
Opcode.CONST_NULL ->
|
||||||
listOf(OperandKind.SLOT)
|
listOf(OperandKind.SLOT)
|
||||||
Opcode.CONST_OBJ, Opcode.CONST_INT, Opcode.CONST_REAL, Opcode.CONST_BOOL,
|
Opcode.CONST_OBJ, Opcode.CONST_INT, Opcode.CONST_REAL, Opcode.CONST_BOOL, Opcode.MAKE_VALUE_FN ->
|
||||||
Opcode.MAKE_LAMBDA_FN ->
|
|
||||||
listOf(OperandKind.CONST, OperandKind.SLOT)
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.PUSH_SCOPE, Opcode.PUSH_SLOT_PLAN ->
|
Opcode.PUSH_SCOPE, Opcode.PUSH_SLOT_PLAN ->
|
||||||
listOf(OperandKind.CONST)
|
listOf(OperandKind.CONST)
|
||||||
Opcode.PUSH_TRY ->
|
Opcode.PUSH_TRY ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP)
|
listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP)
|
||||||
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE,
|
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE,
|
||||||
Opcode.DECL_ENUM, Opcode.DECL_FUNCTION, Opcode.DECL_CLASS,
|
|
||||||
Opcode.ASSIGN_DESTRUCTURE ->
|
Opcode.ASSIGN_DESTRUCTURE ->
|
||||||
listOf(OperandKind.CONST, OperandKind.SLOT)
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
|
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
|
||||||
|
|||||||
@ -28,8 +28,6 @@ data class CmdFunction(
|
|||||||
val scopeSlotIsModule: BooleanArray,
|
val scopeSlotIsModule: BooleanArray,
|
||||||
val localSlotNames: Array<String?>,
|
val localSlotNames: Array<String?>,
|
||||||
val localSlotMutables: BooleanArray,
|
val localSlotMutables: BooleanArray,
|
||||||
val localSlotDelegated: BooleanArray,
|
|
||||||
val localSlotCaptures: BooleanArray,
|
|
||||||
val constants: List<BytecodeConst>,
|
val constants: List<BytecodeConst>,
|
||||||
val cmds: Array<Cmd>,
|
val cmds: Array<Cmd>,
|
||||||
val posByIp: Array<net.sergeych.lyng.Pos?>,
|
val posByIp: Array<net.sergeych.lyng.Pos?>,
|
||||||
@ -39,31 +37,10 @@ data class CmdFunction(
|
|||||||
require(scopeSlotNames.size == scopeSlotCount) { "scopeSlotNames size mismatch" }
|
require(scopeSlotNames.size == scopeSlotCount) { "scopeSlotNames size mismatch" }
|
||||||
require(scopeSlotIsModule.size == scopeSlotCount) { "scopeSlotIsModule size mismatch" }
|
require(scopeSlotIsModule.size == scopeSlotCount) { "scopeSlotIsModule size mismatch" }
|
||||||
require(localSlotNames.size == localSlotMutables.size) { "localSlot metadata size mismatch" }
|
require(localSlotNames.size == localSlotMutables.size) { "localSlot metadata size mismatch" }
|
||||||
require(localSlotNames.size == localSlotDelegated.size) { "localSlot delegation size mismatch" }
|
|
||||||
require(localSlotNames.size == localSlotCaptures.size) { "localSlot capture size mismatch" }
|
|
||||||
require(localSlotNames.size <= localCount) { "localSlotNames exceed localCount" }
|
require(localSlotNames.size <= localCount) { "localSlotNames exceed localCount" }
|
||||||
require(addrCount >= 0) { "addrCount must be non-negative" }
|
require(addrCount >= 0) { "addrCount must be non-negative" }
|
||||||
if (posByIp.isNotEmpty()) {
|
if (posByIp.isNotEmpty()) {
|
||||||
require(posByIp.size == cmds.size) { "posByIp size mismatch" }
|
require(posByIp.size == cmds.size) { "posByIp size mismatch" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun localSlotPlanByName(): Map<String, Int> {
|
|
||||||
val result = LinkedHashMap<String, Int>()
|
|
||||||
for (i in localSlotNames.indices) {
|
|
||||||
val name = localSlotNames[i] ?: continue
|
|
||||||
val existing = result[name]
|
|
||||||
if (existing == null) {
|
|
||||||
result[name] = i
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val existingIsCapture = localSlotCaptures.getOrNull(existing) == true
|
|
||||||
val currentIsCapture = localSlotCaptures.getOrNull(i) == true
|
|
||||||
if (existingIsCapture && !currentIsCapture) {
|
|
||||||
result[name] = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.bytecode
|
|
||||||
|
|
||||||
enum class CaptureOwnerFrameKind { MODULE, LOCAL }
|
|
||||||
|
|
||||||
data class LambdaCaptureEntry(
|
|
||||||
val ownerKind: CaptureOwnerFrameKind,
|
|
||||||
val ownerScopeId: Int,
|
|
||||||
val ownerSlotId: Int,
|
|
||||||
val ownerName: String,
|
|
||||||
val ownerIsMutable: Boolean,
|
|
||||||
val ownerIsDelegated: Boolean,
|
|
||||||
)
|
|
||||||
|
|
||||||
data class BytecodeCaptureEntry(
|
|
||||||
val ownerKind: CaptureOwnerFrameKind,
|
|
||||||
val ownerScopeId: Int,
|
|
||||||
val ownerSlotId: Int,
|
|
||||||
val slotIndex: Int,
|
|
||||||
)
|
|
||||||
@ -31,6 +31,7 @@ enum class Opcode(val code: Int) {
|
|||||||
RANGE_INT_BOUNDS(0x0B),
|
RANGE_INT_BOUNDS(0x0B),
|
||||||
MAKE_RANGE(0x0C),
|
MAKE_RANGE(0x0C),
|
||||||
LOAD_THIS(0x0D),
|
LOAD_THIS(0x0D),
|
||||||
|
MAKE_VALUE_FN(0x0E),
|
||||||
LOAD_THIS_VARIANT(0x0F),
|
LOAD_THIS_VARIANT(0x0F),
|
||||||
|
|
||||||
INT_TO_REAL(0x10),
|
INT_TO_REAL(0x10),
|
||||||
@ -41,8 +42,6 @@ enum class Opcode(val code: Int) {
|
|||||||
CHECK_IS(0x15),
|
CHECK_IS(0x15),
|
||||||
ASSERT_IS(0x16),
|
ASSERT_IS(0x16),
|
||||||
MAKE_QUALIFIED_VIEW(0x17),
|
MAKE_QUALIFIED_VIEW(0x17),
|
||||||
MAKE_LAMBDA_FN(0x18),
|
|
||||||
GET_OBJ_CLASS(0x19),
|
|
||||||
|
|
||||||
ADD_INT(0x20),
|
ADD_INT(0x20),
|
||||||
SUB_INT(0x21),
|
SUB_INT(0x21),
|
||||||
@ -159,15 +158,9 @@ enum class Opcode(val code: Int) {
|
|||||||
STORE_BOOL_ADDR(0xB9),
|
STORE_BOOL_ADDR(0xB9),
|
||||||
THROW(0xBB),
|
THROW(0xBB),
|
||||||
RETHROW_PENDING(0xBC),
|
RETHROW_PENDING(0xBC),
|
||||||
DECL_ENUM(0xBE),
|
|
||||||
ITER_PUSH(0xBF),
|
ITER_PUSH(0xBF),
|
||||||
ITER_POP(0xC0),
|
ITER_POP(0xC0),
|
||||||
ITER_CANCEL(0xC1),
|
ITER_CANCEL(0xC1),
|
||||||
DELEGATED_GET_LOCAL(0xC2),
|
|
||||||
DELEGATED_SET_LOCAL(0xC3),
|
|
||||||
BIND_DELEGATE_LOCAL(0xC4),
|
|
||||||
DECL_FUNCTION(0xC5),
|
|
||||||
DECL_CLASS(0xC6),
|
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
|
|
||||||
*
|
|
||||||
* 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.obj
|
|
||||||
|
|
||||||
import net.sergeych.lyng.ArgsDeclaration
|
|
||||||
import net.sergeych.lyng.Pos
|
|
||||||
import net.sergeych.lyng.Scope
|
|
||||||
import net.sergeych.lyng.bytecode.CmdFunction
|
|
||||||
|
|
||||||
class LambdaFnRef(
|
|
||||||
valueFn: suspend (Scope) -> ObjRecord,
|
|
||||||
val bytecodeFn: CmdFunction?,
|
|
||||||
val paramSlotPlan: Map<String, Int>,
|
|
||||||
val argsDeclaration: ArgsDeclaration?,
|
|
||||||
val preferredThisType: String?,
|
|
||||||
val wrapAsExtensionCallable: Boolean,
|
|
||||||
val returnLabels: Set<String>,
|
|
||||||
val pos: Pos,
|
|
||||||
) : ValueFnRef(valueFn)
|
|
||||||
@ -18,6 +18,7 @@
|
|||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import net.sergeych.lyng.Arguments
|
import net.sergeych.lyng.Arguments
|
||||||
|
import net.sergeych.lyng.ClosureScope
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.Statement
|
import net.sergeych.lyng.Statement
|
||||||
|
|
||||||
@ -62,7 +63,7 @@ open class ObjDynamic(var readCallback: Statement? = null, var writeCallback: St
|
|||||||
* with method invocation which is implemented separately in [invokeInstanceMethod] below.
|
* with method invocation which is implemented separately in [invokeInstanceMethod] below.
|
||||||
*/
|
*/
|
||||||
override suspend fun readField(scope: Scope, name: String): ObjRecord {
|
override suspend fun readField(scope: Scope, name: String): ObjRecord {
|
||||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
val execBase = builderScope?.let { ClosureScope(scope, it) } ?: scope
|
||||||
return readCallback?.execute(execBase.createChildScope(Arguments(ObjString(name))))?.let {
|
return readCallback?.execute(execBase.createChildScope(Arguments(ObjString(name))))?.let {
|
||||||
if (writeCallback != null)
|
if (writeCallback != null)
|
||||||
it.asMutable
|
it.asMutable
|
||||||
@ -82,26 +83,26 @@ open class ObjDynamic(var readCallback: Statement? = null, var writeCallback: St
|
|||||||
args: Arguments,
|
args: Arguments,
|
||||||
onNotFoundResult: (suspend () -> Obj?)?
|
onNotFoundResult: (suspend () -> Obj?)?
|
||||||
): Obj {
|
): Obj {
|
||||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
val execBase = builderScope?.let { ClosureScope(scope, it) } ?: scope
|
||||||
val over = readCallback?.execute(execBase.createChildScope(Arguments(ObjString(name))))
|
val over = readCallback?.execute(execBase.createChildScope(Arguments(ObjString(name))))
|
||||||
return over?.invoke(scope, scope.thisObj, args)
|
return over?.invoke(scope, scope.thisObj, args)
|
||||||
?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
|
override suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
|
||||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
val execBase = builderScope?.let { ClosureScope(scope, it) } ?: scope
|
||||||
writeCallback?.execute(execBase.createChildScope(Arguments(ObjString(name), newValue)))
|
writeCallback?.execute(execBase.createChildScope(Arguments(ObjString(name), newValue)))
|
||||||
?: super.writeField(scope, name, newValue)
|
?: super.writeField(scope, name, newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getAt(scope: Scope, index: Obj): Obj {
|
override suspend fun getAt(scope: Scope, index: Obj): Obj {
|
||||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
val execBase = builderScope?.let { ClosureScope(scope, it) } ?: scope
|
||||||
return readCallback?.execute(execBase.createChildScope(Arguments(index)))
|
return readCallback?.execute(execBase.createChildScope(Arguments(index)))
|
||||||
?: super.getAt(scope, index)
|
?: super.getAt(scope, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun putAt(scope: Scope, index: Obj, newValue: Obj) {
|
override suspend fun putAt(scope: Scope, index: Obj, newValue: Obj) {
|
||||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
val execBase = builderScope?.let { ClosureScope(scope, it) } ?: scope
|
||||||
writeCallback?.execute(execBase.createChildScope(Arguments(index, newValue)))
|
writeCallback?.execute(execBase.createChildScope(Arguments(index, newValue)))
|
||||||
?: super.putAt(scope, index, newValue)
|
?: super.putAt(scope, index, newValue)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import net.sergeych.lyng.Arguments
|
import net.sergeych.lyng.Arguments
|
||||||
|
import net.sergeych.lyng.ClosureScope
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.Statement
|
import net.sergeych.lyng.Statement
|
||||||
|
|
||||||
@ -34,9 +35,9 @@ class ObjProperty(
|
|||||||
suspend fun callGetter(scope: Scope, instance: Obj, declaringClass: ObjClass? = null): Obj {
|
suspend fun callGetter(scope: Scope, instance: Obj, declaringClass: ObjClass? = null): Obj {
|
||||||
val g = getter ?: scope.raiseError("property $name has no getter")
|
val g = getter ?: scope.raiseError("property $name has no getter")
|
||||||
// Execute getter in a child scope of the instance with 'this' properly set
|
// Execute getter in a child scope of the instance with 'this' properly set
|
||||||
// Match extension function behavior (access to instance scope + call scope).
|
// Use ClosureScope to match extension function behavior (access to instance scope + call scope)
|
||||||
val instanceScope = (instance as? ObjInstance)?.instanceScope ?: instance.autoInstanceScope(scope)
|
val instanceScope = (instance as? ObjInstance)?.instanceScope ?: instance.autoInstanceScope(scope)
|
||||||
val execScope = scope.applyClosure(instanceScope).createChildScope(newThisObj = instance)
|
val execScope = ClosureScope(scope, instanceScope).createChildScope(newThisObj = instance)
|
||||||
execScope.currentClassCtx = declaringClass
|
execScope.currentClassCtx = declaringClass
|
||||||
return g.execute(execScope)
|
return g.execute(execScope)
|
||||||
}
|
}
|
||||||
@ -44,9 +45,9 @@ class ObjProperty(
|
|||||||
suspend fun callSetter(scope: Scope, instance: Obj, value: Obj, declaringClass: ObjClass? = null) {
|
suspend fun callSetter(scope: Scope, instance: Obj, value: Obj, declaringClass: ObjClass? = null) {
|
||||||
val s = setter ?: scope.raiseError("property $name has no setter")
|
val s = setter ?: scope.raiseError("property $name has no setter")
|
||||||
// Execute setter in a child scope of the instance with 'this' properly set and the value as an argument
|
// Execute setter in a child scope of the instance with 'this' properly set and the value as an argument
|
||||||
// Match extension function behavior (access to instance scope + call scope).
|
// Use ClosureScope to match extension function behavior
|
||||||
val instanceScope = (instance as? ObjInstance)?.instanceScope ?: instance.autoInstanceScope(scope)
|
val instanceScope = (instance as? ObjInstance)?.instanceScope ?: instance.autoInstanceScope(scope)
|
||||||
val execScope = scope.applyClosure(instanceScope).createChildScope(args = Arguments(value), newThisObj = instance)
|
val execScope = ClosureScope(scope, instanceScope).createChildScope(args = Arguments(value), newThisObj = instance)
|
||||||
execScope.currentClassCtx = declaringClass
|
execScope.currentClassCtx = declaringClass
|
||||||
s.execute(execScope)
|
s.execute(execScope)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,8 +20,6 @@
|
|||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import net.sergeych.lyng.*
|
import net.sergeych.lyng.*
|
||||||
import net.sergeych.lyng.FrameSlotRef
|
|
||||||
import net.sergeych.lyng.RecordSlotRef
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reference to a value with optional write-back path.
|
* A reference to a value with optional write-back path.
|
||||||
@ -45,12 +43,7 @@ sealed interface ObjRef {
|
|||||||
if (rec.receiver != null && rec.declaringClass != null) {
|
if (rec.receiver != null && rec.declaringClass != null) {
|
||||||
return rec.receiver!!.resolveRecord(scope, rec, "unknown", rec.declaringClass).value
|
return rec.receiver!!.resolveRecord(scope, rec, "unknown", rec.declaringClass).value
|
||||||
}
|
}
|
||||||
val value = rec.value
|
return rec.value
|
||||||
return when (value) {
|
|
||||||
is FrameSlotRef -> value.read()
|
|
||||||
is RecordSlotRef -> value.read()
|
|
||||||
else -> value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
suspend fun setAt(pos: Pos, scope: Scope, newValue: Obj) {
|
suspend fun setAt(pos: Pos, scope: Scope, newValue: Obj) {
|
||||||
throw ScriptError(pos, "can't assign value")
|
throw ScriptError(pos, "can't assign value")
|
||||||
@ -72,19 +65,12 @@ sealed interface ObjRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Runtime-computed read-only reference backed by a lambda. */
|
/** Runtime-computed read-only reference backed by a lambda. */
|
||||||
open class ValueFnRef(private val fn: suspend (Scope) -> ObjRecord) : ObjRef {
|
class ValueFnRef(private val fn: suspend (Scope) -> ObjRecord) : ObjRef {
|
||||||
internal fun valueFn(): suspend (Scope) -> ObjRecord = fn
|
internal fun valueFn(): suspend (Scope) -> ObjRecord = fn
|
||||||
|
|
||||||
override suspend fun get(scope: Scope): ObjRecord = fn(scope)
|
override suspend fun get(scope: Scope): ObjRecord = fn(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Compile-time supported ::class operator reference. */
|
|
||||||
class ClassOperatorRef(val target: ObjRef, val pos: Pos) : ObjRef {
|
|
||||||
override suspend fun get(scope: Scope): ObjRecord {
|
|
||||||
return target.evalValue(scope).objClass.asReadonly
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Unary operations supported by ObjRef. */
|
/** Unary operations supported by ObjRef. */
|
||||||
enum class UnaryOp { NOT, NEGATE, BITNOT }
|
enum class UnaryOp { NOT, NEGATE, BITNOT }
|
||||||
|
|
||||||
@ -1985,7 +1971,6 @@ class BoundLocalVarRef(
|
|||||||
private val slot: Int,
|
private val slot: Int,
|
||||||
private val atPos: Pos,
|
private val atPos: Pos,
|
||||||
) : ObjRef {
|
) : ObjRef {
|
||||||
internal fun slotIndex(): Int = slot
|
|
||||||
override suspend fun get(scope: Scope): ObjRecord {
|
override suspend fun get(scope: Scope): ObjRecord {
|
||||||
scope.pos = atPos
|
scope.pos = atPos
|
||||||
val rec = scope.getSlotRecord(slot)
|
val rec = scope.getSlotRecord(slot)
|
||||||
|
|||||||
@ -99,7 +99,6 @@ class ForInStatement(
|
|||||||
val label: String?,
|
val label: String?,
|
||||||
val canBreak: Boolean,
|
val canBreak: Boolean,
|
||||||
val loopSlotPlan: Map<String, Int>,
|
val loopSlotPlan: Map<String, Int>,
|
||||||
val loopScopeId: Int,
|
|
||||||
override val pos: Pos,
|
override val pos: Pos,
|
||||||
) : Statement() {
|
) : Statement() {
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
@ -440,6 +439,15 @@ class ThrowStatement(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ToBoolStatement(
|
||||||
|
val expr: Statement,
|
||||||
|
override val pos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
return if (expr.execute(scope).toBool()) net.sergeych.lyng.obj.ObjTrue else net.sergeych.lyng.obj.ObjFalse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ExpressionStatement(
|
class ExpressionStatement(
|
||||||
val ref: net.sergeych.lyng.obj.ObjRef,
|
val ref: net.sergeych.lyng.obj.ObjRef,
|
||||||
override val pos: Pos
|
override val pos: Pos
|
||||||
|
|||||||
@ -16,22 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import net.sergeych.lyng.Compiler
|
|
||||||
import net.sergeych.lyng.ExecutionError
|
|
||||||
import net.sergeych.lyng.Pos
|
|
||||||
import net.sergeych.lyng.Script
|
|
||||||
import net.sergeych.lyng.ScriptError
|
|
||||||
import net.sergeych.lyng.Source
|
|
||||||
import net.sergeych.lyng.eval
|
import net.sergeych.lyng.eval
|
||||||
import net.sergeych.lyng.toSource
|
|
||||||
import net.sergeych.lyng.bytecode.CmdDisassembler
|
|
||||||
import net.sergeych.lyng.bytecode.CmdFunction
|
|
||||||
import net.sergeych.lyng.obj.toInt
|
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
|
||||||
import kotlin.test.assertFailsWith
|
|
||||||
import kotlin.test.assertNotNull
|
|
||||||
import kotlin.test.assertTrue
|
|
||||||
|
|
||||||
class BytecodeRecentOpsTest {
|
class BytecodeRecentOpsTest {
|
||||||
|
|
||||||
@ -102,300 +88,4 @@ class BytecodeRecentOpsTest {
|
|||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun optionalIndexIncDecSkipsOnNullReceiver() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
var count = 0
|
|
||||||
fun idx() { count = count + 1; return 1 }
|
|
||||||
var a: List<Int>? = null
|
|
||||||
val r = a?[idx()]++
|
|
||||||
assertEquals(null, r)
|
|
||||||
assertEquals(0, count)
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun optionalIndexIncDecUpdatesOnNonNullReceiver() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
var a = [1, 2, 3]
|
|
||||||
val r = a?[1]++
|
|
||||||
assertEquals(2, r)
|
|
||||||
assertEquals(3, a[1])
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun optionalIndexPreIncSkipsOnNullReceiver() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
var count = 0
|
|
||||||
fun idx() { count = count + 1; return 1 }
|
|
||||||
var a: List<Int>? = null
|
|
||||||
val r = ++a?[idx()]
|
|
||||||
assertEquals(null, r)
|
|
||||||
assertEquals(0, count)
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun optionalClassScopeIncDec() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
class C { static var x = 1 }
|
|
||||||
val r = C?.x++
|
|
||||||
assertEquals(1, r)
|
|
||||||
assertEquals(2, C.x)
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun classScopeIfNullAssign() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
class C { static var x: Object? = null }
|
|
||||||
C.x ?= 7
|
|
||||||
assertEquals(7, C.x)
|
|
||||||
C.x ?= 9
|
|
||||||
assertEquals(7, C.x)
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun callablePropertyCall() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
class C { var f = { x -> x + 1 } }
|
|
||||||
val c = C()
|
|
||||||
val r = (c.f)(2)
|
|
||||||
assertEquals(3, r)
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun lambdaCapturesLocalByReference() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
fun make() {
|
|
||||||
var base = 3
|
|
||||||
val f = { x -> x + base }
|
|
||||||
base = 7
|
|
||||||
return f(1)
|
|
||||||
}
|
|
||||||
assertEquals(8, make())
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun lambdaCapturesDelegatedLocal() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
class BoxDelegate(var v) : Delegate {
|
|
||||||
override fun getValue(thisRef: Object, name: String): Object = v
|
|
||||||
override fun setValue(thisRef: Object, name: String, value: Object) { v = value }
|
|
||||||
}
|
|
||||||
fun make() {
|
|
||||||
var x by BoxDelegate(1)
|
|
||||||
val f = { y -> x += y; return x }
|
|
||||||
return f(2)
|
|
||||||
}
|
|
||||||
assertEquals(3, make())
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun delegatedMemberAccessAndCall() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
class ConstDelegate(val v) : Delegate {
|
|
||||||
override fun getValue(thisRef: Object, name: String): Object = v
|
|
||||||
}
|
|
||||||
class ActionDelegate : Delegate {
|
|
||||||
override fun invoke(thisRef: Object, name: String, args...) {
|
|
||||||
val list: List = args as List
|
|
||||||
"Called %s with %d args: %s"(name, list.size, list.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class C {
|
|
||||||
val a by ConstDelegate(7)
|
|
||||||
fun greet by ActionDelegate()
|
|
||||||
}
|
|
||||||
val c = C()
|
|
||||||
assertEquals(7, c.a)
|
|
||||||
assertEquals("Called greet with 2 args: [hi,world]", c.greet("hi", "world"))
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun delegatedLocalAssignAndIncDec() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
class BoxDelegate(var v) : Delegate {
|
|
||||||
override fun getValue(thisRef: Object, name: String): Object = v
|
|
||||||
override fun setValue(thisRef: Object, name: String, value: Object) { v = value }
|
|
||||||
}
|
|
||||||
fun calc() {
|
|
||||||
var x by BoxDelegate(1)
|
|
||||||
x += 2
|
|
||||||
x++
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
assertEquals(4, calc())
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun delegatedLocalDisasmUsesDelegateOps() = runTest {
|
|
||||||
val script = """
|
|
||||||
class BoxDelegate(var v) : Delegate {
|
|
||||||
override fun getValue(thisRef: Object, name: String): Object = v
|
|
||||||
override fun setValue(thisRef: Object, name: String, value: Object) { v = value }
|
|
||||||
}
|
|
||||||
fun calc() {
|
|
||||||
var x by BoxDelegate(1)
|
|
||||||
x += 2
|
|
||||||
x++
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
""".trimIndent()
|
|
||||||
val compiled = Compiler.compile(script.toSource(), Script.defaultImportManager)
|
|
||||||
val scope = Script.defaultImportManager.newModuleAt(Pos.builtIn)
|
|
||||||
compiled.execute(scope)
|
|
||||||
val disasm = scope.disassembleSymbol("calc")
|
|
||||||
assertTrue(disasm.contains("DELEGATED_GET_LOCAL"), disasm)
|
|
||||||
assertTrue(disasm.contains("DELEGATED_SET_LOCAL"), disasm)
|
|
||||||
assertTrue(disasm.contains("DECL_DELEGATED"), disasm)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun moduleDeclsAvoidCallableCallSlots() = runTest {
|
|
||||||
val script = """
|
|
||||||
class A {}
|
|
||||||
fun f() { 1 }
|
|
||||||
enum E { one }
|
|
||||||
""".trimIndent()
|
|
||||||
val compiled = Compiler.compile(script.toSource(), Script.defaultImportManager)
|
|
||||||
val field = Script::class.java.getDeclaredField("moduleBytecode")
|
|
||||||
field.isAccessible = true
|
|
||||||
val moduleFn = field.get(compiled) as? CmdFunction
|
|
||||||
assertNotNull(moduleFn, "module bytecode missing")
|
|
||||||
val disasm = CmdDisassembler.disassemble(moduleFn)
|
|
||||||
assertTrue(!disasm.contains("CALL_SLOT"), disasm)
|
|
||||||
assertTrue(!disasm.contains("Callable@"), disasm)
|
|
||||||
assertTrue(disasm.contains("DECL_CLASS"), disasm)
|
|
||||||
assertTrue(disasm.contains("DECL_FUNCTION"), disasm)
|
|
||||||
assertTrue(disasm.contains("DECL_ENUM"), disasm)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun unionMemberDispatchSubtype() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
class A { fun who() = "A" }
|
|
||||||
class B : A { override fun who() = "B" }
|
|
||||||
fun pick(x: A | B) { x.who() }
|
|
||||||
assertEquals("B", pick(B()))
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun staticMemberDeclNopStatement() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
class C {
|
|
||||||
static fun ping() { 7 }
|
|
||||||
}
|
|
||||||
assertEquals(7, C.ping())
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun unionMemberDispatchMismatch() = runTest {
|
|
||||||
val err = assertFailsWith<ExecutionError> {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
class A { fun who() = "A" }
|
|
||||||
class B { fun who() = "B" }
|
|
||||||
val x: A | B = 1
|
|
||||||
x.who()
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
assertTrue(err.message?.contains("value is not A | B") == true)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun objectReceiverMemberError() = runTest {
|
|
||||||
val failed = try {
|
|
||||||
eval("fun bad(x) { x.missing() }")
|
|
||||||
false
|
|
||||||
} catch (_: ScriptError) {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
assertTrue(failed)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun unionMissingMemberError() = runTest {
|
|
||||||
val failed = try {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
class A { fun who() = "A" }
|
|
||||||
class B { fun other() = "B" }
|
|
||||||
fun pick(x: A | B) { x.who() }
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
false
|
|
||||||
} catch (_: ScriptError) {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
assertTrue(failed)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun qualifiedThisValueRef() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
class T(val v) {
|
|
||||||
fun get() {
|
|
||||||
this@T.v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assertEquals(7, T(7).get())
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun fastLocalVarRefRead() = runTest {
|
|
||||||
val code = """
|
|
||||||
fun addOne(x) {
|
|
||||||
val y = x + 1
|
|
||||||
y
|
|
||||||
}
|
|
||||||
addOne(1)
|
|
||||||
""".trimIndent()
|
|
||||||
val script = Compiler.compileWithResolution(
|
|
||||||
Source("<fast-local>", code),
|
|
||||||
Script.defaultImportManager,
|
|
||||||
useBytecodeStatements = true,
|
|
||||||
useFastLocalRefs = true
|
|
||||||
)
|
|
||||||
val result = script.execute(Script.defaultImportManager.newStdScope())
|
|
||||||
assertEquals(2, result.toInt())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,12 +31,6 @@ extern class List<T> : Array<T> {
|
|||||||
fun add(value: T, more...): Void
|
fun add(value: T, more...): Void
|
||||||
}
|
}
|
||||||
|
|
||||||
extern class RingBuffer<T> : Iterable<T> {
|
|
||||||
val size: Int
|
|
||||||
fun first(): T
|
|
||||||
fun add(value: T): Void
|
|
||||||
}
|
|
||||||
|
|
||||||
extern class Set<T> : Collection<T> {
|
extern class Set<T> : Collection<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,10 +270,8 @@ fun Iterable<T>.shuffled(): List<T> {
|
|||||||
*/
|
*/
|
||||||
fun Iterable<Iterable<T>>.flatten(): List<T> {
|
fun Iterable<Iterable<T>>.flatten(): List<T> {
|
||||||
var result: List<T> = List()
|
var result: List<T> = List()
|
||||||
for (i in this) {
|
forEach { i ->
|
||||||
for (item in i) {
|
i.forEach { result += it }
|
||||||
result += item
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user