Compare commits

..

No commits in common. "fd2da1efd3405856a4d241ce8a35139df844a5f2" and "db4f7d0973584958450143e38dd112fe26460380" have entirely different histories.

114 changed files with 4874 additions and 9967 deletions

View File

@ -1,7 +1,143 @@
# Bytecode Migration Plan (Archived) # Bytecode Migration Plan
Status: completed. Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM tests green after each step.
Historical reference: ## Steps
- `notes/archive/bytecode_migration_plan.md` (full plan)
- `notes/archive/bytecode_migration_plan_completed.md` (summary) - [x] Step 1: Module/import slots seeded into module frame; bytecode module resolution works across closures.
- [x] Step 2: Allow implicit/qualified `this` member refs to compile to bytecode.
- [x] Enable bytecode for `ImplicitThisMethodCallRef`, `QualifiedThisMethodSlotCallRef`, `QualifiedThisFieldSlotRef`.
- [x] Keep unsupported cases blocked: `ClassScopeMemberRef`, dynamic receivers, delegated members.
- [x] JVM tests must be green before commit.
- [x] Step 3: Bytecode support for `try/catch/finally`.
- [x] Implement bytecode emission for try/catch and finally blocks.
- [x] Preserve existing error/stack semantics.
- [x] JVM tests must be green before commit.
## Remaining Migration (prioritized)
- [x] Step 4: Allow bytecode wrapping for supported declaration statements.
- [x] Enable `DestructuringVarDeclStatement` and `ExtensionPropertyDeclStatement` in `containsUnsupportedForBytecode`.
- [x] Keep JVM tests green before commit.
- [x] Step 5: Enable bytecode for delegated var declarations.
- [x] Revisit `containsDelegatedRefs` guard for `DelegatedVarDeclStatement`.
- [x] Ensure delegate binding uses explicit `Statement` objects (no inline suspend lambdas).
- [x] Keep JVM tests green before commit.
- [x] Step 6: Map literal spread in bytecode.
- [x] Replace `MapLiteralEntry.Spread` bytecode exception with runtime `putAll`/merge logic.
- [x] Step 7: Class-scope member refs in bytecode.
- [x] Support `ClassScopeMemberRef` without scope-map fallback.
- [x] Step 8: ObjDynamic member access in bytecode.
- [x] Allow dynamic receiver field/method lookup without falling back to interpreter.
- [x] Step 9: Module-level bytecode execution.
- [x] Compile `Script` bodies to bytecode instead of interpreting at module scope.
- [x] Keep import/module slot seeding in frame-only flow.
- [x] Step 10: Bytecode for declaration statements in module scripts.
- [x] Support `ClassDeclStatement`, `FunctionDeclStatement`, `EnumDeclStatement` in bytecode compilation.
- [x] Keep a mixed execution path for declarations (module bytecode calls statement bodies via `CALL_SLOT`).
- [x] Ensure module object member refs compile as instance access (not class-scope).
- [x] Step 11: Destructuring assignment bytecode.
- [x] Handle `[a, b] = expr` (AssignRef target `ListLiteralRef`) without interpreter fallback.
- [x] Step 12: Optional member assign-ops and inc/dec in bytecode.
- [x] Support `a?.b += 1` and `a?.b++` for `FieldRef` targets.
- [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] 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
- Keep imports bound to module frame slots; no scope map writes for imports.
- Avoid inline suspend lambdas in compiler hot paths; use explicit `object : Statement()`.
- Do not reintroduce bytecode fallback opcodes; all symbol resolution remains compile-time only.

View File

@ -63,8 +63,6 @@ In spite of this you can use ranges in for loops:
>>> 3 >>> 3
>>> void >>> void
The loop variable is read-only inside the loop body (behaves like a `val`).
but but
for( i in 1..<3 ) for( i in 1..<3 )

View File

@ -105,7 +105,6 @@ arguments list in almost arbitrary ways. For example:
var result = "" var result = ""
for( a in args ) result += a for( a in args ) result += a
} }
// loop variables are read-only inside the loop body
assertEquals( assertEquals(
"4231", "4231",

View File

@ -739,7 +739,6 @@ See also: [Testing and Assertions](Testing.md)
var result = [] var result = []
for( x in iterable ) result += transform(x) for( x in iterable ) result += transform(x)
} }
// loop variables are read-only inside the loop body
assert( [11, 21, 31] == mapValues( [1,2,3], { it*10+1 })) assert( [11, 21, 31] == mapValues( [1,2,3], { it*10+1 }))
>>> void >>> void

View File

@ -38,7 +38,6 @@ import kotlinx.coroutines.launch
import net.sergeych.lyng.ExecutionError import net.sergeych.lyng.ExecutionError
import net.sergeych.lyng.Script import net.sergeych.lyng.Script
import net.sergeych.lyng.Source import net.sergeych.lyng.Source
import net.sergeych.lyng.requireScope
import net.sergeych.lyng.idea.LyngIcons import net.sergeych.lyng.idea.LyngIcons
import net.sergeych.lyng.obj.ObjVoid import net.sergeych.lyng.obj.ObjVoid
import net.sergeych.lyng.obj.getLyngExceptionMessageWithStackTrace import net.sergeych.lyng.obj.getLyngExceptionMessageWithStackTrace
@ -82,7 +81,7 @@ class RunLyngScriptAction : AnAction(LyngIcons.FILE) {
val sb = StringBuilder() val sb = StringBuilder()
for ((i, arg) in args.list.withIndex()) { for ((i, arg) in args.list.withIndex()) {
if (i > 0) sb.append(" ") if (i > 0) sb.append(" ")
sb.append(arg.toString(requireScope()).value) sb.append(arg.toString(this).value)
} }
console.print(sb.toString(), ConsoleViewContentType.NORMAL_OUTPUT) console.print(sb.toString(), ConsoleViewContentType.NORMAL_OUTPUT)
ObjVoid ObjVoid
@ -91,7 +90,7 @@ class RunLyngScriptAction : AnAction(LyngIcons.FILE) {
val sb = StringBuilder() val sb = StringBuilder()
for ((i, arg) in args.list.withIndex()) { for ((i, arg) in args.list.withIndex()) {
if (i > 0) sb.append(" ") if (i > 0) sb.append(" ")
sb.append(arg.toString(requireScope()).value) sb.append(arg.toString(this).value)
} }
console.print(sb.toString() + "\n", ConsoleViewContentType.NORMAL_OUTPUT) console.print(sb.toString() + "\n", ConsoleViewContentType.NORMAL_OUTPUT)
ObjVoid ObjVoid

View File

@ -122,7 +122,6 @@ class LyngCompletionContributor : CompletionContributor() {
fromText.forEach { add(it) } fromText.forEach { add(it) }
add("lyng.stdlib") add("lyng.stdlib")
}.toList() }.toList()
val staticOnly = DocLookupUtils.isStaticReceiver(mini, text, memberDotPos, imported, binding)
// Try inferring return/receiver class around the dot // Try inferring return/receiver class around the dot
val inferred = val inferred =
@ -137,7 +136,7 @@ class LyngCompletionContributor : CompletionContributor() {
if (inferred != null) { if (inferred != null) {
if (DEBUG_COMPLETION) log.info("[LYNG_DEBUG] Fallback inferred receiver/return class='$inferred' — offering its members") if (DEBUG_COMPLETION) log.info("[LYNG_DEBUG] Fallback inferred receiver/return class='$inferred' — offering its members")
offerMembers(emit, imported, inferred, staticOnly = staticOnly, sourceText = text, mini = mini) offerMembers(emit, imported, inferred, sourceText = text, mini = mini)
return return
} else { } else {
if (DEBUG_COMPLETION) log.info("[LYNG_DEBUG] Fallback could not infer class; keeping list empty (no globals after dot)") if (DEBUG_COMPLETION) log.info("[LYNG_DEBUG] Fallback could not infer class; keeping list empty (no globals after dot)")
@ -296,9 +295,6 @@ class LyngCompletionContributor : CompletionContributor() {
} }
is MiniEnumDecl -> LookupElementBuilder.create(name) is MiniEnumDecl -> LookupElementBuilder.create(name)
.withIcon(AllIcons.Nodes.Enum) .withIcon(AllIcons.Nodes.Enum)
is MiniTypeAliasDecl -> LookupElementBuilder.create(name)
.withIcon(AllIcons.Nodes.Class)
.withTypeText(typeOf(d.target), true)
} }
emit(builder) emit(builder)
} }
@ -376,7 +372,6 @@ class LyngCompletionContributor : CompletionContributor() {
when (m) { when (m) {
is MiniMemberFunDecl -> if (!m.isStatic) continue is MiniMemberFunDecl -> if (!m.isStatic) continue
is MiniMemberValDecl -> if (!m.isStatic) continue is MiniMemberValDecl -> if (!m.isStatic) continue
is MiniMemberTypeAliasDecl -> if (!m.isStatic) continue
is MiniInitDecl -> continue is MiniInitDecl -> continue
} }
} }
@ -466,16 +461,6 @@ class LyngCompletionContributor : CompletionContributor() {
emit(builder) emit(builder)
} }
} }
is MiniMemberTypeAliasDecl -> {
val builder = LookupElementBuilder.create(name)
.withIcon(AllIcons.Nodes.Class)
.withTypeText(typeOf(rep.target), true)
if (groupPriority != 0.0) {
emit(PrioritizedLookupElement.withPriority(builder, groupPriority))
} else {
emit(builder)
}
}
is MiniInitDecl -> {} is MiniInitDecl -> {}
} }
} }

View File

@ -317,8 +317,7 @@ class LyngDocumentationProvider : AbstractDocumentationProvider() {
} }
if (DEBUG_LOG) log.info("[LYNG_DEBUG] QuickDoc: memberCtx dotPos=${dotPos} chBeforeDot='${if (dotPos > 0) text[dotPos - 1] else ' '}' classGuess=${className} imports=${importedModules}") if (DEBUG_LOG) log.info("[LYNG_DEBUG] QuickDoc: memberCtx dotPos=${dotPos} chBeforeDot='${if (dotPos > 0) text[dotPos - 1] else ' '}' classGuess=${className} imports=${importedModules}")
if (className != null) { if (className != null) {
val staticOnly = DocLookupUtils.isStaticReceiver(mini, text, dotPos, importedModules, analysis.binding) DocLookupUtils.resolveMemberWithInheritance(importedModules, className, ident, mini)?.let { (owner, member) ->
DocLookupUtils.resolveMemberWithInheritance(importedModules, className, ident, mini, staticOnly = staticOnly)?.let { (owner, member) ->
if (DEBUG_INHERITANCE) log.info("[LYNG_DEBUG] QuickDoc: literal/call '$ident' resolved to $owner.${member.name}") if (DEBUG_INHERITANCE) log.info("[LYNG_DEBUG] QuickDoc: literal/call '$ident' resolved to $owner.${member.name}")
return when (member) { return when (member) {
is MiniMemberFunDecl -> renderMemberFunDoc(owner, member) is MiniMemberFunDecl -> renderMemberFunDoc(owner, member)
@ -381,9 +380,7 @@ class LyngDocumentationProvider : AbstractDocumentationProvider() {
val lhs = previousWordBefore(text, idRange.startOffset) val lhs = previousWordBefore(text, idRange.startOffset)
if (lhs != null && hasDotBetween(text, lhs.endOffset, idRange.startOffset)) { if (lhs != null && hasDotBetween(text, lhs.endOffset, idRange.startOffset)) {
val className = text.substring(lhs.startOffset, lhs.endOffset) val className = text.substring(lhs.startOffset, lhs.endOffset)
val dotPos = findDotLeft(text, idRange.startOffset) DocLookupUtils.resolveMemberWithInheritance(importedModules, className, ident, mini)?.let { (owner, member) ->
val staticOnly = dotPos?.let { DocLookupUtils.isStaticReceiver(mini, text, it, importedModules, analysis.binding) } ?: false
DocLookupUtils.resolveMemberWithInheritance(importedModules, className, ident, mini, staticOnly = staticOnly)?.let { (owner, member) ->
if (DEBUG_INHERITANCE) log.info("[LYNG_DEBUG] Inheritance resolved $className.$ident to $owner.${member.name}") if (DEBUG_INHERITANCE) log.info("[LYNG_DEBUG] Inheritance resolved $className.$ident to $owner.${member.name}")
return when (member) { return when (member) {
is MiniMemberFunDecl -> renderMemberFunDoc(owner, member) is MiniMemberFunDecl -> renderMemberFunDoc(owner, member)
@ -408,8 +405,7 @@ class LyngDocumentationProvider : AbstractDocumentationProvider() {
else -> DocLookupUtils.guessClassFromCallBefore(text, dotPos, importedModules, mini) else -> DocLookupUtils.guessClassFromCallBefore(text, dotPos, importedModules, mini)
} }
if (guessed != null) { if (guessed != null) {
val staticOnly = DocLookupUtils.isStaticReceiver(mini, text, dotPos, importedModules, analysis.binding) DocLookupUtils.resolveMemberWithInheritance(importedModules, guessed, ident, mini)?.let { (owner, member) ->
DocLookupUtils.resolveMemberWithInheritance(importedModules, guessed, ident, mini, staticOnly = staticOnly)?.let { (owner, member) ->
if (DEBUG_INHERITANCE) log.info("[LYNG_DEBUG] Heuristic '$guessed.$ident' resolved via inheritance to $owner.${member.name}") if (DEBUG_INHERITANCE) log.info("[LYNG_DEBUG] Heuristic '$guessed.$ident' resolved via inheritance to $owner.${member.name}")
return when (member) { return when (member) {
is MiniMemberFunDecl -> renderMemberFunDoc(owner, member) is MiniMemberFunDecl -> renderMemberFunDoc(owner, member)
@ -428,8 +424,7 @@ class LyngDocumentationProvider : AbstractDocumentationProvider() {
run { run {
val candidates = listOf("String", "Iterable", "Iterator", "List", "Collection", "Array", "Dict", "Regex") val candidates = listOf("String", "Iterable", "Iterator", "List", "Collection", "Array", "Dict", "Regex")
for (c in candidates) { for (c in candidates) {
val staticOnly = DocLookupUtils.isStaticReceiver(mini, text, dotPos, importedModules, analysis.binding) DocLookupUtils.resolveMemberWithInheritance(importedModules, c, ident, mini)?.let { (owner, member) ->
DocLookupUtils.resolveMemberWithInheritance(importedModules, c, ident, mini, staticOnly = staticOnly)?.let { (owner, member) ->
if (DEBUG_INHERITANCE) log.info("[LYNG_DEBUG] Candidate '$c.$ident' resolved via inheritance to $owner.${member.name}") if (DEBUG_INHERITANCE) log.info("[LYNG_DEBUG] Candidate '$c.$ident' resolved via inheritance to $owner.${member.name}")
return when (member) { return when (member) {
is MiniMemberFunDecl -> renderMemberFunDoc(owner, member) is MiniMemberFunDecl -> renderMemberFunDoc(owner, member)
@ -466,13 +461,11 @@ class LyngDocumentationProvider : AbstractDocumentationProvider() {
return when (member) { return when (member) {
is MiniMemberFunDecl -> renderMemberFunDoc(owner, member) is MiniMemberFunDecl -> renderMemberFunDoc(owner, member)
is MiniMemberValDecl -> renderMemberValDoc(owner, member) is MiniMemberValDecl -> renderMemberValDoc(owner, member)
is MiniMemberTypeAliasDecl -> renderMemberTypeAliasDoc(owner, member)
is MiniInitDecl -> null is MiniInitDecl -> null
is MiniFunDecl -> renderDeclDoc(member, text, mini, importedModules) is MiniFunDecl -> renderDeclDoc(member, text, mini, importedModules)
is MiniValDecl -> renderDeclDoc(member, text, mini, importedModules) is MiniValDecl -> renderDeclDoc(member, text, mini, importedModules)
is MiniClassDecl -> renderDeclDoc(member, text, mini, importedModules) is MiniClassDecl -> renderDeclDoc(member, text, mini, importedModules)
is MiniEnumDecl -> renderDeclDoc(member, text, mini, importedModules) is MiniEnumDecl -> renderDeclDoc(member, text, mini, importedModules)
is MiniTypeAliasDecl -> renderDeclDoc(member, text, mini, importedModules)
} }
} }
} }

View File

@ -48,10 +48,9 @@ class LyngPsiReference(element: PsiElement) : PsiPolyVariantReferenceBase<PsiEle
if (dotPos != null) { if (dotPos != null) {
val receiverClass = DocLookupUtils.guessReceiverClassViaMini(mini, text, dotPos, imported.toList(), binding) val receiverClass = DocLookupUtils.guessReceiverClassViaMini(mini, text, dotPos, imported.toList(), binding)
?: DocLookupUtils.guessReceiverClass(text, dotPos, imported.toList(), mini) ?: DocLookupUtils.guessReceiverClass(text, dotPos, imported.toList(), mini)
val staticOnly = DocLookupUtils.isStaticReceiver(mini, text, dotPos, imported.toList(), binding)
if (receiverClass != null) { if (receiverClass != null) {
val resolved = DocLookupUtils.resolveMemberWithInheritance(imported.toList(), receiverClass, name, mini, staticOnly = staticOnly) val resolved = DocLookupUtils.resolveMemberWithInheritance(imported.toList(), receiverClass, name, mini)
if (resolved != null) { if (resolved != null) {
val owner = resolved.first val owner = resolved.first
val member = resolved.second val member = resolved.second

View File

@ -29,6 +29,7 @@ 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.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
@ -157,6 +158,7 @@ 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 = """
@ -168,7 +170,7 @@ private class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand()
override fun help(context: Context): String = override fun help(context: Context): String =
""" """
The Lyng script language runtime, language version is $LyngVersion. The Lyng script language interpreter, language version is $LyngVersion.
Please refer form more information to the project site: Please refer form more information to the project site:
https://gitea.sergeych.net/SergeychWorks/lyng https://gitea.sergeych.net/SergeychWorks/lyng
@ -199,12 +201,15 @@ 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)
val script = Compiler.compileWithResolution( val script = Compiler.compileWithResolution(
Source("<eval>", execute!!), Source("<eval>", execute!!),
baseScope.currentImportProvider, baseScope.currentImportProvider,
seedScope = baseScope seedScope = baseScope,
bytecodeFallbackReporter = reporter
) )
script.execute(baseScope) script.execute(baseScope)
flushBytecodeFallbacks(reporter)
} }
} }
} }
@ -215,7 +220,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!!) } launcher { executeFile(script!!, bytecodeFallbacks) }
} }
} }
} }
@ -223,14 +228,14 @@ private class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand()
} }
} }
fun executeFileWithArgs(fileName: String, args: List<String>) { fun executeFileWithArgs(fileName: String, args: List<String>, reportBytecodeFallbacks: Boolean = false) {
runBlocking { runBlocking {
baseScopeDefer.await().addConst("ARGV", ObjList(args.map { ObjString(it) }.toMutableList())) baseScopeDefer.await().addConst("ARGV", ObjList(args.map { ObjString(it) }.toMutableList()))
executeFile(fileName) executeFile(fileName, reportBytecodeFallbacks)
} }
} }
suspend fun executeFile(fileName: String) { suspend fun executeFile(fileName: String, reportBytecodeFallbacks: Boolean = false) {
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()
@ -243,12 +248,15 @@ suspend fun executeFile(fileName: String) {
} }
processErrors { processErrors {
val scope = baseScopeDefer.await() val scope = baseScopeDefer.await()
val reporter = bytecodeFallbackReporter(reportBytecodeFallbacks)
val script = Compiler.compileWithResolution( val script = Compiler.compileWithResolution(
Source(fileName, text), Source(fileName, text),
scope.currentImportProvider, scope.currentImportProvider,
seedScope = scope seedScope = scope,
bytecodeFallbackReporter = reporter
) )
script.execute(scope) script.execute(scope)
flushBytecodeFallbacks(reporter)
} }
} }
@ -260,3 +268,22 @@ 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)
}

View File

@ -23,8 +23,6 @@ package net.sergeych.lyng.io.fs
import net.sergeych.lyng.ModuleScope import net.sergeych.lyng.ModuleScope
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.ScopeFacade
import net.sergeych.lyng.requireScope
import net.sergeych.lyng.miniast.* import net.sergeych.lyng.miniast.*
import net.sergeych.lyng.obj.* import net.sergeych.lyng.obj.*
import net.sergeych.lyng.pacman.ImportManager import net.sergeych.lyng.pacman.ImportManager
@ -439,7 +437,7 @@ private suspend fun buildFsModule(module: ModuleScope, policy: FsAccessPolicy) {
moduleName = module.packageName moduleName = module.packageName
) { ) {
fsGuard { fsGuard {
val chunkIt = thisObj.invokeInstanceMethod(requireScope(), "readUtf8Chunks") val chunkIt = thisObj.invokeInstanceMethod(this, "readUtf8Chunks")
ObjFsLinesIterator(chunkIt) ObjFsLinesIterator(chunkIt)
} }
} }
@ -465,7 +463,7 @@ private suspend fun buildFsModule(module: ModuleScope, policy: FsAccessPolicy) {
// --- Helper classes and utilities --- // --- Helper classes and utilities ---
private fun parsePathArg(scope: ScopeFacade, self: ObjPath, arg: Obj): LyngPath { private fun parsePathArg(scope: Scope, self: ObjPath, arg: Obj): LyngPath {
return when (arg) { return when (arg) {
is ObjString -> arg.value.toPath() is ObjString -> arg.value.toPath()
is ObjPath -> arg.path is ObjPath -> arg.path
@ -474,11 +472,11 @@ private fun parsePathArg(scope: ScopeFacade, self: ObjPath, arg: Obj): LyngPath
} }
// Map Fs access denials to Lyng runtime exceptions for script-friendly errors // Map Fs access denials to Lyng runtime exceptions for script-friendly errors
private suspend inline fun ScopeFacade.fsGuard(crossinline block: suspend () -> Obj): Obj { private suspend inline fun Scope.fsGuard(crossinline block: suspend () -> Obj): Obj {
return try { return try {
block() block()
} catch (e: AccessDeniedException) { } catch (e: AccessDeniedException) {
raiseError(ObjIllegalOperationException(requireScope(), e.reasonDetail ?: "access denied")) raiseError(ObjIllegalOperationException(this, e.reasonDetail ?: "access denied"))
} }
} }
@ -670,17 +668,16 @@ class ObjFsLinesIterator(
} }
} }
private suspend fun ensureBufferFilled(scope: ScopeFacade) { private suspend fun ensureBufferFilled(scope: Scope) {
if (buffer.contains('\n') || exhausted) return if (buffer.contains('\n') || exhausted) return
val actualScope = scope.requireScope()
// Pull next chunk from the underlying iterator // Pull next chunk from the underlying iterator
val it = chunksIterator.invokeInstanceMethod(actualScope, "iterator") val it = chunksIterator.invokeInstanceMethod(scope, "iterator")
val hasNext = it.invokeInstanceMethod(actualScope, "hasNext").toBool() val hasNext = it.invokeInstanceMethod(scope, "hasNext").toBool()
if (!hasNext) { if (!hasNext) {
exhausted = true exhausted = true
return return
} }
val next = it.invokeInstanceMethod(actualScope, "next") val next = it.invokeInstanceMethod(scope, "next")
buffer += next.toString() buffer += next.toString()
} }
} }

View File

@ -20,11 +20,10 @@ package net.sergeych.lyng.io.process
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import net.sergeych.lyng.ModuleScope import net.sergeych.lyng.ModuleScope
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.ScopeFacade
import net.sergeych.lyng.requireScope
import net.sergeych.lyng.miniast.* import net.sergeych.lyng.miniast.*
import net.sergeych.lyng.obj.* import net.sergeych.lyng.obj.*
import net.sergeych.lyng.pacman.ImportManager import net.sergeych.lyng.pacman.ImportManager
import net.sergeych.lyng.statement
import net.sergeych.lyngio.process.* import net.sergeych.lyngio.process.*
import net.sergeych.lyngio.process.security.ProcessAccessDeniedException import net.sergeych.lyngio.process.security.ProcessAccessDeniedException
import net.sergeych.lyngio.process.security.ProcessAccessPolicy import net.sergeych.lyngio.process.security.ProcessAccessPolicy
@ -206,21 +205,20 @@ class ObjRunningProcess(
override fun toString(): String = "RunningProcess($process)" override fun toString(): String = "RunningProcess($process)"
} }
private suspend inline fun ScopeFacade.processGuard(crossinline block: suspend () -> Obj): Obj { private suspend inline fun Scope.processGuard(crossinline block: suspend () -> Obj): Obj {
return try { return try {
block() block()
} catch (e: ProcessAccessDeniedException) { } catch (e: ProcessAccessDeniedException) {
raiseError(ObjIllegalOperationException(requireScope(), e.reasonDetail ?: "process access denied")) raiseError(ObjIllegalOperationException(this, e.reasonDetail ?: "process access denied"))
} catch (e: Exception) { } catch (e: Exception) {
raiseError(ObjIllegalOperationException(requireScope(), e.message ?: "process error")) raiseError(ObjIllegalOperationException(this, e.message ?: "process error"))
} }
} }
private fun Flow<String>.toLyngFlow(flowScope: ScopeFacade): ObjFlow { private fun Flow<String>.toLyngFlow(flowScope: Scope): ObjFlow {
val producer = net.sergeych.lyng.obj.ObjExternCallable.fromBridge { val producer = statement {
val scope = requireScope() val builder = (this as? net.sergeych.lyng.ClosureScope)?.callScope?.thisObj as? ObjFlowBuilder
val builder = (scope as? net.sergeych.lyng.BytecodeClosureScope)?.callScope?.thisObj as? ObjFlowBuilder ?: this.thisObj as? ObjFlowBuilder
?: scope.thisObj as? ObjFlowBuilder
this@toLyngFlow.collect { this@toLyngFlow.collect {
try { try {
@ -232,5 +230,5 @@ private fun Flow<String>.toLyngFlow(flowScope: ScopeFacade): ObjFlow {
} }
ObjVoid ObjVoid
} }
return ObjFlow(producer, flowScope.requireScope()) return ObjFlow(producer, flowScope)
} }

View File

@ -21,7 +21,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.JvmTarget
group = "net.sergeych" group = "net.sergeych"
version = "1.4.0-SNAPSHOT" version = "1.3.0-SNAPSHOT"
// Removed legacy buildscript classpath declarations; plugins are applied via the plugins DSL below // Removed legacy buildscript classpath declarations; plugins are applied via the plugins DSL below

View File

@ -113,7 +113,7 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
} }
suspend fun missingValue(a: Item, error: String): Obj { suspend fun missingValue(a: Item, error: String): Obj {
return a.defaultValue?.callOn(scope) return a.defaultValue?.execute(scope)
?: if (a.type.isNullable) ObjNull else scope.raiseIllegalArgument(error) ?: if (a.type.isNullable) ObjNull else scope.raiseIllegalArgument(error)
} }
@ -252,14 +252,17 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
/** /**
* Assign arguments directly into frame slots using [paramSlotPlan] without creating scope locals. * Assign arguments directly into frame slots using [paramSlotPlan] without creating scope locals.
* Default expressions must resolve through frame slots (no scope mirroring). * Still allows default expressions to evaluate by exposing FrameSlotRef facades in [scope].
*/ */
suspend fun assignToFrame( suspend fun assignToFrame(
scope: Scope, scope: Scope,
arguments: Arguments = scope.args, arguments: Arguments = scope.args,
paramSlotPlan: Map<String, Int>, paramSlotPlan: Map<String, Int>,
frame: FrameAccess, frame: FrameAccess,
slotOffset: Int = 0 slotOffset: Int = 0,
defaultAccessType: AccessType = AccessType.Var,
defaultVisibility: Visibility = Visibility.Public,
declaringClass: net.sergeych.lyng.obj.ObjClass? = scope.currentClassCtx
) { ) {
fun slotFor(name: String): Int { fun slotFor(name: String): Int {
val full = paramSlotPlan[name] ?: scope.raiseIllegalState("parameter slot for '$name' is missing") val full = paramSlotPlan[name] ?: scope.raiseIllegalState("parameter slot for '$name' is missing")
@ -268,6 +271,19 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
return slot return slot
} }
fun ensureScopeRef(a: Item, slot: Int, recordType: ObjRecord.Type) {
if (scope.getLocalRecordDirect(a.name) != null) return
scope.addItem(
a.name,
(a.accessType ?: defaultAccessType).isMutable,
FrameSlotRef(frame, slot),
a.visibility ?: defaultVisibility,
recordType = recordType,
declaringClass = declaringClass,
isTransient = a.isTransient
)
}
fun setFrameValue(slot: Int, value: Obj) { fun setFrameValue(slot: Int, value: Obj) {
when (value) { when (value) {
is net.sergeych.lyng.obj.ObjInt -> frame.setInt(slot, value.value) is net.sergeych.lyng.obj.ObjInt -> frame.setInt(slot, value.value)
@ -278,12 +294,18 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
} }
fun assign(a: Item, value: Obj) { fun assign(a: Item, value: Obj) {
val recordType = if (declaringClass != null && a.accessType != null) {
ObjRecord.Type.ConstructorField
} else {
ObjRecord.Type.Argument
}
val slot = slotFor(a.name) val slot = slotFor(a.name)
setFrameValue(slot, value.byValueCopy()) setFrameValue(slot, value.byValueCopy())
ensureScopeRef(a, slot, recordType)
} }
suspend fun missingValue(a: Item, error: String): Obj { suspend fun missingValue(a: Item, error: String): Obj {
return a.defaultValue?.callOn(scope) return a.defaultValue?.execute(scope)
?: if (a.type.isNullable) ObjNull else scope.raiseIllegalArgument(error) ?: if (a.type.isNullable) ObjNull else scope.raiseIllegalArgument(error)
} }
@ -437,7 +459,7 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
/** /**
* Single argument declaration descriptor. * Single argument declaration descriptor.
* *
* @param defaultValue default value, callable evaluated at call site. * @param defaultValue default value, if set, can't be an [Obj] as it can depend on the call site, call args, etc.
* If not null, could be executed on __caller context__ only. * If not null, could be executed on __caller context__ only.
*/ */
data class Item( data class Item(
@ -450,7 +472,7 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
* Default value, if set, can't be an [Obj] as it can depend on the call site, call args, etc. * Default value, if set, can't be an [Obj] as it can depend on the call site, call args, etc.
* So it is a [Statement] that must be executed on __caller context__. * So it is a [Statement] that must be executed on __caller context__.
*/ */
val defaultValue: Obj? = null, val defaultValue: Statement? = null,
val accessType: AccessType? = null, val accessType: AccessType? = null,
val visibility: Visibility? = null, val visibility: Visibility? = null,
val isTransient: Boolean = false, val isTransient: Boolean = false,

View File

@ -20,7 +20,7 @@ package net.sergeych.lyng
import net.sergeych.lyng.obj.* import net.sergeych.lyng.obj.*
data class ParsedArgument( data class ParsedArgument(
val value: Obj, val value: Statement,
val pos: Pos, val pos: Pos,
val isSplat: Boolean = false, val isSplat: Boolean = false,
val name: String? = null, val name: String? = null,
@ -40,115 +40,115 @@ data class ParsedArgument(
if (!hasSplatOrNamed && count == this.size) { if (!hasSplatOrNamed && count == this.size) {
val quick = when (count) { val quick = when (count) {
0 -> Arguments.EMPTY 0 -> Arguments.EMPTY
1 -> Arguments(listOf(this.elementAt(0).value.callOn(scope)), tailBlockMode) 1 -> Arguments(listOf(this.elementAt(0).value.execute(scope)), tailBlockMode)
2 -> { 2 -> {
val a0 = this.elementAt(0).value.callOn(scope) val a0 = this.elementAt(0).value.execute(scope)
val a1 = this.elementAt(1).value.callOn(scope) val a1 = this.elementAt(1).value.execute(scope)
Arguments(listOf(a0, a1), tailBlockMode) Arguments(listOf(a0, a1), tailBlockMode)
} }
3 -> { 3 -> {
val a0 = this.elementAt(0).value.callOn(scope) val a0 = this.elementAt(0).value.execute(scope)
val a1 = this.elementAt(1).value.callOn(scope) val a1 = this.elementAt(1).value.execute(scope)
val a2 = this.elementAt(2).value.callOn(scope) val a2 = this.elementAt(2).value.execute(scope)
Arguments(listOf(a0, a1, a2), tailBlockMode) Arguments(listOf(a0, a1, a2), tailBlockMode)
} }
4 -> { 4 -> {
val a0 = this.elementAt(0).value.callOn(scope) val a0 = this.elementAt(0).value.execute(scope)
val a1 = this.elementAt(1).value.callOn(scope) val a1 = this.elementAt(1).value.execute(scope)
val a2 = this.elementAt(2).value.callOn(scope) val a2 = this.elementAt(2).value.execute(scope)
val a3 = this.elementAt(3).value.callOn(scope) val a3 = this.elementAt(3).value.execute(scope)
Arguments(listOf(a0, a1, a2, a3), tailBlockMode) Arguments(listOf(a0, a1, a2, a3), tailBlockMode)
} }
5 -> { 5 -> {
val a0 = this.elementAt(0).value.callOn(scope) val a0 = this.elementAt(0).value.execute(scope)
val a1 = this.elementAt(1).value.callOn(scope) val a1 = this.elementAt(1).value.execute(scope)
val a2 = this.elementAt(2).value.callOn(scope) val a2 = this.elementAt(2).value.execute(scope)
val a3 = this.elementAt(3).value.callOn(scope) val a3 = this.elementAt(3).value.execute(scope)
val a4 = this.elementAt(4).value.callOn(scope) val a4 = this.elementAt(4).value.execute(scope)
Arguments(listOf(a0, a1, a2, a3, a4), tailBlockMode) Arguments(listOf(a0, a1, a2, a3, a4), tailBlockMode)
} }
6 -> { 6 -> {
val a0 = this.elementAt(0).value.callOn(scope) val a0 = this.elementAt(0).value.execute(scope)
val a1 = this.elementAt(1).value.callOn(scope) val a1 = this.elementAt(1).value.execute(scope)
val a2 = this.elementAt(2).value.callOn(scope) val a2 = this.elementAt(2).value.execute(scope)
val a3 = this.elementAt(3).value.callOn(scope) val a3 = this.elementAt(3).value.execute(scope)
val a4 = this.elementAt(4).value.callOn(scope) val a4 = this.elementAt(4).value.execute(scope)
val a5 = this.elementAt(5).value.callOn(scope) val a5 = this.elementAt(5).value.execute(scope)
Arguments(listOf(a0, a1, a2, a3, a4, a5), tailBlockMode) Arguments(listOf(a0, a1, a2, a3, a4, a5), tailBlockMode)
} }
7 -> { 7 -> {
val a0 = this.elementAt(0).value.callOn(scope) val a0 = this.elementAt(0).value.execute(scope)
val a1 = this.elementAt(1).value.callOn(scope) val a1 = this.elementAt(1).value.execute(scope)
val a2 = this.elementAt(2).value.callOn(scope) val a2 = this.elementAt(2).value.execute(scope)
val a3 = this.elementAt(3).value.callOn(scope) val a3 = this.elementAt(3).value.execute(scope)
val a4 = this.elementAt(4).value.callOn(scope) val a4 = this.elementAt(4).value.execute(scope)
val a5 = this.elementAt(5).value.callOn(scope) val a5 = this.elementAt(5).value.execute(scope)
val a6 = this.elementAt(6).value.callOn(scope) val a6 = this.elementAt(6).value.execute(scope)
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6), tailBlockMode) Arguments(listOf(a0, a1, a2, a3, a4, a5, a6), tailBlockMode)
} }
8 -> { 8 -> {
val a0 = this.elementAt(0).value.callOn(scope) val a0 = this.elementAt(0).value.execute(scope)
val a1 = this.elementAt(1).value.callOn(scope) val a1 = this.elementAt(1).value.execute(scope)
val a2 = this.elementAt(2).value.callOn(scope) val a2 = this.elementAt(2).value.execute(scope)
val a3 = this.elementAt(3).value.callOn(scope) val a3 = this.elementAt(3).value.execute(scope)
val a4 = this.elementAt(4).value.callOn(scope) val a4 = this.elementAt(4).value.execute(scope)
val a5 = this.elementAt(5).value.callOn(scope) val a5 = this.elementAt(5).value.execute(scope)
val a6 = this.elementAt(6).value.callOn(scope) val a6 = this.elementAt(6).value.execute(scope)
val a7 = this.elementAt(7).value.callOn(scope) val a7 = this.elementAt(7).value.execute(scope)
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7), tailBlockMode) Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7), tailBlockMode)
} }
9 -> if (PerfFlags.ARG_SMALL_ARITY_12) { 9 -> if (PerfFlags.ARG_SMALL_ARITY_12) {
val a0 = this.elementAt(0).value.callOn(scope) val a0 = this.elementAt(0).value.execute(scope)
val a1 = this.elementAt(1).value.callOn(scope) val a1 = this.elementAt(1).value.execute(scope)
val a2 = this.elementAt(2).value.callOn(scope) val a2 = this.elementAt(2).value.execute(scope)
val a3 = this.elementAt(3).value.callOn(scope) val a3 = this.elementAt(3).value.execute(scope)
val a4 = this.elementAt(4).value.callOn(scope) val a4 = this.elementAt(4).value.execute(scope)
val a5 = this.elementAt(5).value.callOn(scope) val a5 = this.elementAt(5).value.execute(scope)
val a6 = this.elementAt(6).value.callOn(scope) val a6 = this.elementAt(6).value.execute(scope)
val a7 = this.elementAt(7).value.callOn(scope) val a7 = this.elementAt(7).value.execute(scope)
val a8 = this.elementAt(8).value.callOn(scope) val a8 = this.elementAt(8).value.execute(scope)
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8), tailBlockMode) Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8), tailBlockMode)
} else null } else null
10 -> if (PerfFlags.ARG_SMALL_ARITY_12) { 10 -> if (PerfFlags.ARG_SMALL_ARITY_12) {
val a0 = this.elementAt(0).value.callOn(scope) val a0 = this.elementAt(0).value.execute(scope)
val a1 = this.elementAt(1).value.callOn(scope) val a1 = this.elementAt(1).value.execute(scope)
val a2 = this.elementAt(2).value.callOn(scope) val a2 = this.elementAt(2).value.execute(scope)
val a3 = this.elementAt(3).value.callOn(scope) val a3 = this.elementAt(3).value.execute(scope)
val a4 = this.elementAt(4).value.callOn(scope) val a4 = this.elementAt(4).value.execute(scope)
val a5 = this.elementAt(5).value.callOn(scope) val a5 = this.elementAt(5).value.execute(scope)
val a6 = this.elementAt(6).value.callOn(scope) val a6 = this.elementAt(6).value.execute(scope)
val a7 = this.elementAt(7).value.callOn(scope) val a7 = this.elementAt(7).value.execute(scope)
val a8 = this.elementAt(8).value.callOn(scope) val a8 = this.elementAt(8).value.execute(scope)
val a9 = this.elementAt(9).value.callOn(scope) val a9 = this.elementAt(9).value.execute(scope)
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9), tailBlockMode) Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9), tailBlockMode)
} else null } else null
11 -> if (PerfFlags.ARG_SMALL_ARITY_12) { 11 -> if (PerfFlags.ARG_SMALL_ARITY_12) {
val a0 = this.elementAt(0).value.callOn(scope) val a0 = this.elementAt(0).value.execute(scope)
val a1 = this.elementAt(1).value.callOn(scope) val a1 = this.elementAt(1).value.execute(scope)
val a2 = this.elementAt(2).value.callOn(scope) val a2 = this.elementAt(2).value.execute(scope)
val a3 = this.elementAt(3).value.callOn(scope) val a3 = this.elementAt(3).value.execute(scope)
val a4 = this.elementAt(4).value.callOn(scope) val a4 = this.elementAt(4).value.execute(scope)
val a5 = this.elementAt(5).value.callOn(scope) val a5 = this.elementAt(5).value.execute(scope)
val a6 = this.elementAt(6).value.callOn(scope) val a6 = this.elementAt(6).value.execute(scope)
val a7 = this.elementAt(7).value.callOn(scope) val a7 = this.elementAt(7).value.execute(scope)
val a8 = this.elementAt(8).value.callOn(scope) val a8 = this.elementAt(8).value.execute(scope)
val a9 = this.elementAt(9).value.callOn(scope) val a9 = this.elementAt(9).value.execute(scope)
val a10 = this.elementAt(10).value.callOn(scope) val a10 = this.elementAt(10).value.execute(scope)
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10), tailBlockMode) Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10), tailBlockMode)
} else null } else null
12 -> if (PerfFlags.ARG_SMALL_ARITY_12) { 12 -> if (PerfFlags.ARG_SMALL_ARITY_12) {
val a0 = this.elementAt(0).value.callOn(scope) val a0 = this.elementAt(0).value.execute(scope)
val a1 = this.elementAt(1).value.callOn(scope) val a1 = this.elementAt(1).value.execute(scope)
val a2 = this.elementAt(2).value.callOn(scope) val a2 = this.elementAt(2).value.execute(scope)
val a3 = this.elementAt(3).value.callOn(scope) val a3 = this.elementAt(3).value.execute(scope)
val a4 = this.elementAt(4).value.callOn(scope) val a4 = this.elementAt(4).value.execute(scope)
val a5 = this.elementAt(5).value.callOn(scope) val a5 = this.elementAt(5).value.execute(scope)
val a6 = this.elementAt(6).value.callOn(scope) val a6 = this.elementAt(6).value.execute(scope)
val a7 = this.elementAt(7).value.callOn(scope) val a7 = this.elementAt(7).value.execute(scope)
val a8 = this.elementAt(8).value.callOn(scope) val a8 = this.elementAt(8).value.execute(scope)
val a9 = this.elementAt(9).value.callOn(scope) val a9 = this.elementAt(9).value.execute(scope)
val a10 = this.elementAt(10).value.callOn(scope) val a10 = this.elementAt(10).value.execute(scope)
val a11 = this.elementAt(11).value.callOn(scope) val a11 = this.elementAt(11).value.execute(scope)
Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11), tailBlockMode) Arguments(listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11), tailBlockMode)
} else null } else null
else -> null else -> null
@ -166,12 +166,12 @@ data class ParsedArgument(
// Named argument // Named argument
if (named == null) named = linkedMapOf() if (named == null) named = linkedMapOf()
if (named.containsKey(x.name)) scope.raiseIllegalArgument("argument '${x.name}' is already set") if (named.containsKey(x.name)) scope.raiseIllegalArgument("argument '${x.name}' is already set")
val v = x.value.callOn(scope) val v = x.value.execute(scope)
named[x.name] = v named[x.name] = v
namedSeen = true namedSeen = true
continue continue
} }
val value = x.value.callOn(scope) val value = x.value.execute(scope)
if (x.isSplat) { if (x.isSplat) {
when { when {
// IMPORTANT: handle ObjMap BEFORE generic Iterable to ensure map splats // IMPORTANT: handle ObjMap BEFORE generic Iterable to ensure map splats

View File

@ -28,7 +28,39 @@ class BlockStatement(
override val pos: Pos = startPos override val pos: Pos = startPos
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return bytecodeOnly(scope, "block statement") val target = if (scope.skipScopeCreation) scope else scope.createChildScope(startPos)
if (slotPlan.isNotEmpty()) target.applySlotPlan(slotPlan)
if (captureSlots.isNotEmpty()) {
val captureRecords = scope.captureRecords
if (captureRecords != null) {
for (i in captureSlots.indices) {
val capture = captureSlots[i]
val rec = captureRecords.getOrNull(i)
?: scope.raiseSymbolNotFound("capture ${capture.name} not found")
target.updateSlotFor(capture.name, rec)
}
} else {
val applyScope = scope as? ApplyScope
for (capture in captureSlots) {
// Interpreter-only capture resolution; bytecode paths must use captureRecords instead.
val rec = if (applyScope != null) {
applyScope.resolveCaptureRecord(capture.name)
?: applyScope.callScope.resolveCaptureRecord(capture.name)
} else {
scope.resolveCaptureRecord(capture.name)
}
if (rec == null) {
if (scope.getSlotIndexOf(capture.name) == null && scope.getLocalRecordDirect(capture.name) == null) {
continue
}
(applyScope?.callScope ?: scope)
.raiseSymbolNotFound("symbol ${capture.name} not found")
}
target.updateSlotFor(capture.name, rec)
}
}
}
return block.execute(target)
} }
fun statements(): List<Statement> = block.debugStatements() fun statements(): List<Statement> = block.debugStatements()

View File

@ -1,34 +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.bytecode.BytecodeStatement
import net.sergeych.lyng.bytecode.CmdVm
import net.sergeych.lyng.bytecode.seedFrameLocalsFromScope
import net.sergeych.lyng.obj.Obj
internal suspend fun executeBytecodeWithSeed(scope: Scope, stmt: Statement, label: String): Obj {
val bytecode = when (stmt) {
is BytecodeStatement -> stmt
is BytecodeBodyProvider -> stmt.bytecodeBody()
else -> null
} ?: scope.raiseIllegalState("$label requires bytecode statement")
scope.pos = bytecode.pos
return CmdVm().execute(bytecode.bytecodeFunction(), scope, scope.args) { frame, _ ->
seedFrameLocalsFromScope(frame, scope)
}
}

View File

@ -19,6 +19,4 @@ package net.sergeych.lyng
data class CaptureSlot( data class CaptureSlot(
val name: String, val name: String,
val ownerScopeId: Int? = null,
val ownerSlot: Int? = null,
) )

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com * Copyright 2026 Sergey S. Chernov
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -12,12 +12,17 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
*/ */
package net.sergeych.lyng package net.sergeych.lyng
import net.sergeych.lyng.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( data class ClassDeclBaseSpec(
val name: String, val name: String,
@ -40,22 +45,12 @@ data class ClassDeclSpec(
val initScope: List<Statement>, val initScope: List<Statement>,
) )
internal suspend fun executeClassDecl( internal suspend fun executeClassDecl(scope: Scope, spec: ClassDeclSpec): Obj {
scope: Scope,
spec: ClassDeclSpec,
bodyCaptureRecords: List<ObjRecord>? = null,
bodyCaptureNames: List<String>? = null
): Obj {
fun checkClosedParents(parents: List<ObjClass>, pos: Pos) {
val closedParent = parents.firstOrNull { it.isClosed } ?: return
throw ScriptError(pos, "can't inherit from closed class ${closedParent.className}")
}
if (spec.isObject) { if (spec.isObject) {
val parentClasses = spec.baseSpecs.map { baseSpec -> val parentClasses = spec.baseSpecs.map { baseSpec ->
val rec = scope[baseSpec.name] ?: throw ScriptError(spec.startPos, "unknown base class: ${baseSpec.name}") val rec = scope[baseSpec.name] ?: throw ScriptError(spec.startPos, "unknown base class: ${baseSpec.name}")
(rec.value as? ObjClass) ?: throw ScriptError(spec.startPos, "${baseSpec.name} is not a class") (rec.value as? ObjClass) ?: throw ScriptError(spec.startPos, "${baseSpec.name} is not a class")
} }
checkClosedParents(parentClasses, spec.startPos)
val newClass = ObjInstanceClass(spec.className, *parentClasses.toTypedArray()) val newClass = ObjInstanceClass(spec.className, *parentClasses.toTypedArray())
newClass.isAnonymous = spec.isAnonymous newClass.isAnonymous = spec.isAnonymous
@ -66,15 +61,11 @@ internal suspend fun executeClassDecl(
} }
val classScope = scope.createChildScope(newThisObj = newClass) val classScope = scope.createChildScope(newThisObj = newClass)
if (!bodyCaptureRecords.isNullOrEmpty() && !bodyCaptureNames.isNullOrEmpty()) {
classScope.captureRecords = bodyCaptureRecords
classScope.captureNames = bodyCaptureNames
}
classScope.currentClassCtx = newClass classScope.currentClassCtx = newClass
newClass.classScope = classScope newClass.classScope = classScope
classScope.addConst("object", newClass) classScope.addConst("object", newClass)
spec.bodyInit?.let { executeBytecodeWithSeed(classScope, it, "object body init") } spec.bodyInit?.execute(classScope)
val instance = newClass.callOn(scope.createChildScope(Arguments.EMPTY)) val instance = newClass.callOn(scope.createChildScope(Arguments.EMPTY))
if (spec.declaredName != null) { if (spec.declaredName != null) {
@ -106,7 +97,6 @@ internal suspend fun executeClassDecl(
if (rec == null) throw ScriptError(spec.startPos, "unknown base class: ${baseSpec.name}") if (rec == null) throw ScriptError(spec.startPos, "unknown base class: ${baseSpec.name}")
throw ScriptError(spec.startPos, "${baseSpec.name} is not a class") throw ScriptError(spec.startPos, "${baseSpec.name} is not a class")
} }
checkClosedParents(parentClasses, spec.startPos)
val constructorCode = object : Statement() { val constructorCode = object : Statement() {
override val pos: Pos = spec.startPos override val pos: Pos = spec.startPos
@ -141,47 +131,20 @@ internal suspend fun executeClassDecl(
} }
} }
spec.declaredName?.let { name -> spec.declaredName?.let { scope.addItem(it, false, newClass) }
scope.addItem(name, false, newClass)
val module = scope as? ModuleScope
val frame = module?.moduleFrame
if (module != null && frame != null) {
val idx = module.moduleFrameLocalSlotNames.indexOf(name)
if (idx >= 0) {
frame.setObj(idx, newClass)
}
}
}
val classScope = scope.createChildScope(newThisObj = newClass) val classScope = scope.createChildScope(newThisObj = newClass)
if (!bodyCaptureRecords.isNullOrEmpty() && !bodyCaptureNames.isNullOrEmpty()) {
classScope.captureRecords = bodyCaptureRecords
classScope.captureNames = bodyCaptureNames
}
classScope.currentClassCtx = newClass classScope.currentClassCtx = newClass
newClass.classScope = classScope newClass.classScope = classScope
spec.bodyInit?.let { executeBytecodeWithSeed(classScope, it, "class body init") } spec.bodyInit?.execute(classScope)
if (spec.initScope.isNotEmpty()) { if (spec.initScope.isNotEmpty()) {
for (s in spec.initScope) { for (s in spec.initScope) {
executeBytecodeWithSeed(classScope, s, "class init") s.execute(classScope)
} }
} }
newClass.checkAbstractSatisfaction(spec.startPos) newClass.checkAbstractSatisfaction(spec.startPos)
return newClass return newClass
} }
private suspend fun requireBytecodeBody(
scope: Scope,
stmt: Statement,
label: String
): net.sergeych.lyng.bytecode.BytecodeStatement {
val bytecode = when (stmt) {
is net.sergeych.lyng.bytecode.BytecodeStatement -> stmt
is BytecodeBodyProvider -> stmt.bytecodeBody()
else -> null
}
return bytecode ?: scope.raiseIllegalState("$label requires bytecode statement")
}
class ClassDeclStatement( class ClassDeclStatement(
val spec: ClassDeclSpec, val spec: ClassDeclSpec,
) : Statement() { ) : Statement() {

View File

@ -1,84 +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
import net.sergeych.lyng.obj.ObjProperty
class ClassInstanceInitDeclStatement(
val initStatement: Statement,
override val pos: Pos,
) : Statement() {
override suspend fun execute(scope: Scope): Obj {
return bytecodeOnly(scope, "class instance init declaration")
}
}
class ClassInstanceFieldDeclStatement(
val name: String,
val isMutable: Boolean,
val visibility: Visibility,
val writeVisibility: Visibility?,
val isAbstract: Boolean,
val isClosed: Boolean,
val isOverride: Boolean,
val isTransient: Boolean,
val fieldId: Int?,
val initStatement: Statement?,
override val pos: Pos,
) : Statement() {
override suspend fun execute(scope: Scope): Obj {
return bytecodeOnly(scope, "class instance field declaration")
}
}
class ClassInstancePropertyDeclStatement(
val name: String,
val isMutable: Boolean,
val visibility: Visibility,
val writeVisibility: Visibility?,
val isAbstract: Boolean,
val isClosed: Boolean,
val isOverride: Boolean,
val isTransient: Boolean,
val prop: ObjProperty,
val methodId: Int?,
val initStatement: Statement?,
override val pos: Pos,
) : Statement() {
override suspend fun execute(scope: Scope): Obj {
return bytecodeOnly(scope, "class instance property declaration")
}
}
class ClassInstanceDelegatedDeclStatement(
val name: String,
val isMutable: Boolean,
val visibility: Visibility,
val writeVisibility: Visibility?,
val isAbstract: Boolean,
val isClosed: Boolean,
val isOverride: Boolean,
val isTransient: Boolean,
val methodId: Int?,
val initStatement: Statement?,
override val pos: Pos,
) : Statement() {
override suspend fun execute(scope: Scope): Obj {
return bytecodeOnly(scope, "class instance delegated declaration")
}
}

View File

@ -1,111 +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
import net.sergeych.lyng.obj.ObjClass
import net.sergeych.lyng.obj.ObjNull
import net.sergeych.lyng.obj.ObjRecord
import net.sergeych.lyng.obj.ObjString
import net.sergeych.lyng.obj.ObjUnset
import net.sergeych.lyng.obj.ObjVoid
class ClassStaticFieldInitStatement(
val name: String,
val isMutable: Boolean,
val visibility: Visibility,
val writeVisibility: Visibility?,
val initializer: Statement?,
val isDelegated: Boolean,
val isTransient: Boolean,
private val startPos: Pos,
) : Statement() {
override val pos: Pos = startPos
override suspend fun execute(scope: Scope): Obj {
val initValue = initializer?.let { execBytecodeOnly(scope, it, "class static field init") }?.byValueCopy()
?: ObjNull
val cls = scope.thisObj as? ObjClass
?: scope.raiseIllegalState("static field init requires class scope")
return if (isDelegated) {
val accessTypeStr = if (isMutable) "Var" else "Val"
val accessType = ObjString(accessTypeStr)
val finalDelegate = try {
initValue.invokeInstanceMethod(
scope,
"bind",
Arguments(ObjString(name), accessType, scope.thisObj)
)
} catch (_: Exception) {
initValue
}
cls.createClassField(
name,
ObjUnset,
isMutable,
visibility,
writeVisibility,
startPos,
isTransient = isTransient,
type = ObjRecord.Type.Delegated
).apply {
delegate = finalDelegate
}
scope.addItem(
name,
isMutable,
ObjUnset,
visibility,
writeVisibility,
recordType = ObjRecord.Type.Delegated,
isTransient = isTransient
).apply {
delegate = finalDelegate
}
finalDelegate
} else {
cls.createClassField(
name,
initValue,
isMutable,
visibility,
writeVisibility,
startPos,
isTransient = isTransient
)
scope.addItem(
name,
isMutable,
initValue,
visibility,
writeVisibility,
recordType = ObjRecord.Type.Field,
isTransient = isTransient
)
initValue
}
}
private suspend fun execBytecodeOnly(scope: Scope, stmt: Statement, label: String): Obj {
val bytecode = when (stmt) {
is net.sergeych.lyng.bytecode.BytecodeStatement -> stmt
is BytecodeBodyProvider -> stmt.bytecodeBody()
else -> null
} ?: scope.raiseIllegalState("$label requires bytecode statement")
return bytecode.execute(scope)
}
}

View File

@ -18,12 +18,76 @@
package net.sergeych.lyng package net.sergeych.lyng
import net.sergeych.lyng.obj.Obj import net.sergeych.lyng.obj.Obj
import net.sergeych.lyng.obj.ObjClass
import net.sergeych.lyng.obj.ObjRecord import net.sergeych.lyng.obj.ObjRecord
/**
* Scope that adds a "closure" to caller; most often it is used to apply class instance to caller scope.
* Inherits [Scope.args] and [Scope.thisObj] from [callScope] and adds lookup for symbols
* from [closureScope] with proper precedence
*/
class ClosureScope(
val callScope: Scope,
val closureScope: Scope,
private val preferredThisType: String? = null
) :
// Important: use closureScope.thisObj so unqualified members (e.g., fields) resolve to the instance
// we captured, not to the caller's `this` (e.g., FlowBuilder).
Scope(callScope, callScope.args, thisObj = closureScope.thisObj) {
init {
val desired = preferredThisType?.let { typeName ->
callScope.thisVariants.firstOrNull { it.objClass.className == typeName }
}
val primaryThis = closureScope.thisObj
val merged = ArrayList<Obj>(callScope.thisVariants.size + closureScope.thisVariants.size + 1)
desired?.let { merged.add(it) }
merged.addAll(callScope.thisVariants)
merged.addAll(closureScope.thisVariants)
setThisVariants(primaryThis, merged)
// Preserve the lexical class context of the closure by default. This ensures that lambdas
// created inside a class method keep access to that class's private/protected members even
// when executed from within another object's method (e.g., Mutex.withLock), which may set
// its own currentClassCtx temporarily. If the closure has no class context, inherit caller's.
this.currentClassCtx = closureScope.currentClassCtx ?: callScope.currentClassCtx
}
override fun get(name: String): ObjRecord? {
if (name == "this") return thisObj.asReadonly
// 1. Current frame locals (parameters, local variables)
tryGetLocalRecord(this, name, currentClassCtx)?.let { return it }
// 2. Lexical environment (captured locals from entire ancestry)
closureScope.chainLookupIgnoreClosure(name, followClosure = true, caller = currentClassCtx)?.let { return it }
// 3. Lexical this members (captured receiver)
val receiver = thisObj
val effectiveClass = receiver as? ObjClass ?: receiver.objClass
for (cls in effectiveClass.mro) {
val rec = cls.members[name] ?: cls.classScope?.objects?.get(name)
if (rec != null && !rec.isAbstract) {
if (canAccessMember(rec.visibility, rec.declaringClass ?: cls, currentClassCtx, name)) {
return rec.copy(receiver = receiver)
}
}
}
// Finally, root object fallback
Obj.rootObjectType.members[name]?.let { rec ->
if (canAccessMember(rec.visibility, rec.declaringClass, currentClassCtx, name)) {
return rec.copy(receiver = receiver)
}
}
// 4. Call environment (caller locals, caller this, and global fallback)
return callScope.get(name)
}
}
/** /**
* Bytecode-oriented closure scope that keeps the call scope parent chain for stack traces * Bytecode-oriented closure scope that keeps the call scope parent chain for stack traces
* while carrying the lexical closure for `this` variants and module resolution. * while carrying the lexical closure for `this` variants and module resolution.
* Unlike legacy closure scopes, it does not override name lookup. * Unlike [ClosureScope], it does not override name lookup.
*/ */
class BytecodeClosureScope( class BytecodeClosureScope(
val callScope: Scope, val callScope: Scope,
@ -54,7 +118,7 @@ class ApplyScope(val callScope: Scope, val applied: Scope) :
} }
override fun applyClosure(closure: Scope, preferredThisType: String?): Scope { override fun applyClosure(closure: Scope, preferredThisType: String?): Scope {
return BytecodeClosureScope(this, closure, preferredThisType) return ClosureScope(this, closure, preferredThisType)
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -14,10 +14,14 @@
* limitations under the License. * limitations under the License.
*/ */
package net.sergeych.lyng.bytecode package net.sergeych.lyng
data class ForcedLocalSlotInfo( import net.sergeych.lyng.obj.Obj
val index: Int,
val isMutable: Boolean, interface DeclExecutable {
val isDelegated: Boolean, suspend fun execute(scope: Scope): Obj
) }
class StatementDeclExecutable(private val delegate: Statement) : DeclExecutable {
override suspend fun execute(scope: Scope): Obj = delegate.execute(scope)
}

View File

@ -18,6 +18,9 @@
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.ObjNull
import net.sergeych.lyng.obj.ObjRecord
import net.sergeych.lyng.obj.ObjString
class DelegatedVarDeclStatement( class DelegatedVarDeclStatement(
val name: String, val name: String,
@ -32,6 +35,23 @@ class DelegatedVarDeclStatement(
override val pos: Pos = startPos override val pos: Pos = startPos
override suspend fun execute(context: Scope): Obj { override suspend fun execute(context: Scope): Obj {
return bytecodeOnly(context, "delegated var declaration") val initValue = initializer.execute(context)
val accessTypeStr = if (isMutable) "Var" else "Val"
val accessType = ObjString(accessTypeStr)
val finalDelegate = try {
initValue.invokeInstanceMethod(context, "bind", Arguments(ObjString(name), accessType, ObjNull))
} catch (e: Exception) {
initValue
}
val rec = context.addItem(
name,
isMutable,
ObjNull,
visibility,
recordType = ObjRecord.Type.Delegated,
isTransient = isTransient
)
rec.delegate = finalDelegate
return finalDelegate
} }
} }

View File

@ -18,6 +18,7 @@ package net.sergeych.lyng
import net.sergeych.lyng.obj.ListLiteralRef import net.sergeych.lyng.obj.ListLiteralRef
import net.sergeych.lyng.obj.Obj import net.sergeych.lyng.obj.Obj
import net.sergeych.lyng.obj.ObjVoid
class DestructuringVarDeclStatement( class DestructuringVarDeclStatement(
val pattern: ListLiteralRef, val pattern: ListLiteralRef,
@ -29,6 +30,20 @@ class DestructuringVarDeclStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(context: Scope): Obj { override suspend fun execute(context: Scope): Obj {
return bytecodeOnly(context, "destructuring declaration") val value = initializer.execute(context)
for (name in names) {
context.addItem(name, true, ObjVoid, visibility, isTransient = isTransient)
}
pattern.setAt(pos, context, value)
if (!isMutable) {
for (name in names) {
val rec = context.objects[name]!!
val immutableRec = rec.copy(isMutable = false)
context.objects[name] = immutableRec
context.localBindings[name] = immutableRec
context.updateSlotFor(name, immutableRec)
}
}
return ObjVoid
} }
} }

View File

@ -17,6 +17,8 @@
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, val declaredName: String,
@ -28,7 +30,17 @@ class EnumDeclStatement(
override val pos: Pos = startPos override val pos: Pos = startPos
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return bytecodeOnly(scope, "enum declaration") val enumClass = ObjEnumClass.createSimpleEnum(qualifiedName, entries)
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 {

View File

@ -17,7 +17,11 @@
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.ObjExtensionPropertyGetterCallable
import net.sergeych.lyng.obj.ObjExtensionPropertySetterCallable
import net.sergeych.lyng.obj.ObjProperty import net.sergeych.lyng.obj.ObjProperty
import net.sergeych.lyng.obj.ObjRecord
class ExtensionPropertyDeclStatement( class ExtensionPropertyDeclStatement(
val extTypeName: String, val extTypeName: String,
@ -29,6 +33,28 @@ class ExtensionPropertyDeclStatement(
override val pos: Pos = startPos override val pos: Pos = startPos
override suspend fun execute(context: Scope): Obj { override suspend fun execute(context: Scope): Obj {
return bytecodeOnly(context, "extension property declaration") val type = context[extTypeName]?.value ?: context.raiseSymbolNotFound("class $extTypeName not found")
if (type !is ObjClass) context.raiseClassCastError("$extTypeName is not the class instance")
context.addExtension(
type,
property.name,
ObjRecord(
property,
isMutable = false,
visibility = visibility,
writeVisibility = setterVisibility,
declaringClass = null,
type = ObjRecord.Type.Property
)
)
val getterName = extensionPropertyGetterName(extTypeName, property.name)
val getterWrapper = ObjExtensionPropertyGetterCallable(property.name, property)
context.addItem(getterName, false, getterWrapper, visibility, recordType = ObjRecord.Type.Fun)
if (property.setter != null) {
val setterName = extensionPropertySetterName(extTypeName, property.name)
val setterWrapper = ObjExtensionPropertySetterCallable(property.name, property)
context.addItem(setterName, false, setterWrapper, visibility, recordType = ObjRecord.Type.Fun)
}
return property
} }
} }

View File

@ -36,30 +36,16 @@ class FrameSlotRef(
private val frame: FrameAccess, private val frame: FrameAccess,
private val slot: Int, private val slot: Int,
) : net.sergeych.lyng.obj.Obj() { ) : net.sergeych.lyng.obj.Obj() {
override suspend fun compareTo(scope: Scope, other: Obj): Int {
val resolvedOther = when (other) {
is FrameSlotRef -> other.read()
is RecordSlotRef -> other.read()
else -> other
}
return read().compareTo(scope, resolvedOther)
}
fun read(): Obj { fun read(): Obj {
val typeCode = frame.getSlotTypeCode(slot) return when (frame.getSlotTypeCode(slot)) {
return when (typeCode) {
SlotType.INT.code -> ObjInt.of(frame.getInt(slot)) SlotType.INT.code -> ObjInt.of(frame.getInt(slot))
SlotType.REAL.code -> ObjReal.of(frame.getReal(slot)) SlotType.REAL.code -> ObjReal.of(frame.getReal(slot))
SlotType.BOOL.code -> if (frame.getBool(slot)) ObjTrue else ObjFalse SlotType.BOOL.code -> if (frame.getBool(slot)) ObjTrue else ObjFalse
SlotType.OBJ.code -> frame.getObj(slot) SlotType.OBJ.code -> frame.getObj(slot)
else -> frame.getObj(slot) else -> ObjNull
} }
} }
internal fun refersTo(frame: FrameAccess, slot: Int): Boolean {
return this.frame === frame && this.slot == slot
}
fun write(value: Obj) { fun write(value: Obj) {
when (value) { when (value) {
is ObjInt -> frame.setInt(slot, value.value) is ObjInt -> frame.setInt(slot, value.value)
@ -73,15 +59,6 @@ class FrameSlotRef(
class RecordSlotRef( class RecordSlotRef(
private val record: ObjRecord, private val record: ObjRecord,
) : net.sergeych.lyng.obj.Obj() { ) : net.sergeych.lyng.obj.Obj() {
override suspend fun compareTo(scope: Scope, other: Obj): Int {
val resolvedOther = when (other) {
is FrameSlotRef -> other.read()
is RecordSlotRef -> other.read()
else -> other
}
return read().compareTo(scope, resolvedOther)
}
fun read(): Obj { fun read(): Obj {
val direct = record.value val direct = record.value
return if (direct is FrameSlotRef) direct.read() else direct return if (direct is FrameSlotRef) direct.read() else direct

View File

@ -18,7 +18,6 @@ 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.ObjClass
import net.sergeych.lyng.obj.ObjExternCallable
import net.sergeych.lyng.obj.ObjExtensionMethodCallable import net.sergeych.lyng.obj.ObjExtensionMethodCallable
import net.sergeych.lyng.obj.ObjInstance import net.sergeych.lyng.obj.ObjInstance
import net.sergeych.lyng.obj.ObjRecord import net.sergeych.lyng.obj.ObjRecord
@ -29,7 +28,6 @@ import net.sergeych.lyng.obj.ObjVoid
class FunctionClosureBox( class FunctionClosureBox(
var closure: Scope? = null, var closure: Scope? = null,
var captureContext: Scope? = null, var captureContext: Scope? = null,
var captureRecords: List<ObjRecord>? = null,
) )
data class FunctionDeclSpec( data class FunctionDeclSpec(
@ -42,7 +40,6 @@ data class FunctionDeclSpec(
val isTransient: Boolean, val isTransient: Boolean,
val isDelegated: Boolean, val isDelegated: Boolean,
val delegateExpression: Statement?, val delegateExpression: Statement?,
val delegateInitStatement: Statement?,
val extTypeName: String?, val extTypeName: String?,
val extensionWrapperName: String?, val extensionWrapperName: String?,
val memberMethodId: Int?, val memberMethodId: Int?,
@ -53,36 +50,28 @@ data class FunctionDeclSpec(
val fnBody: Statement, val fnBody: Statement,
val closureBox: FunctionClosureBox, val closureBox: FunctionClosureBox,
val captureSlots: List<CaptureSlot>, val captureSlots: List<CaptureSlot>,
val slotIndex: Int?,
val scopeId: Int?,
val startPos: Pos, val startPos: Pos,
) )
internal suspend fun executeFunctionDecl( internal suspend fun executeFunctionDecl(scope: Scope, spec: FunctionDeclSpec): Obj {
scope: Scope,
spec: FunctionDeclSpec,
captureRecords: List<ObjRecord>? = null
): Obj {
spec.closureBox.captureRecords = captureRecords
if (spec.actualExtern && spec.extTypeName == null && !spec.parentIsClassBody) { if (spec.actualExtern && spec.extTypeName == null && !spec.parentIsClassBody) {
val existing = scope.get(spec.name) val existing = scope.get(spec.name)
if (existing != null) { if (existing != null) {
val value = (existing.value as? ObjExternCallable) ?: ObjExternCallable.wrap(existing.value)
scope.addItem( scope.addItem(
spec.name, spec.name,
false, false,
value, existing.value,
spec.visibility, spec.visibility,
callSignature = existing.callSignature callSignature = existing.callSignature
) )
return value return existing.value
} }
} }
if (spec.isDelegated) { if (spec.isDelegated) {
val delegateExpr = spec.delegateExpression ?: scope.raiseError("delegated function missing delegate") val delegateExpr = spec.delegateExpression ?: scope.raiseError("delegated function missing delegate")
val accessType = ObjString("Callable") val accessType = ObjString("Callable")
val initValue = executeBytecodeWithSeed(scope, delegateExpr, "delegated function") val initValue = delegateExpr.execute(scope)
val finalDelegate = try { val finalDelegate = try {
initValue.invokeInstanceMethod(scope, "bind", Arguments(ObjString(spec.name), accessType, scope.thisObj)) initValue.invokeInstanceMethod(scope, "bind", Arguments(ObjString(spec.name), accessType, scope.thisObj))
} catch (e: Exception) { } catch (e: Exception) {
@ -99,7 +88,7 @@ internal suspend fun executeFunctionDecl(
delegate = finalDelegate delegate = finalDelegate
} }
) )
return finalDelegate return ObjVoid
} }
val th = scope.thisObj val th = scope.thisObj
@ -128,6 +117,7 @@ internal suspend fun executeFunctionDecl(
} }
} else if (th is ObjClass) { } else if (th is ObjClass) {
val cls: ObjClass = th val cls: ObjClass = th
val storageName = "${cls.className}::${spec.name}"
cls.createField( cls.createField(
spec.name, spec.name,
ObjUnset, ObjUnset,
@ -143,9 +133,33 @@ internal suspend fun executeFunctionDecl(
type = ObjRecord.Type.Delegated, type = ObjRecord.Type.Delegated,
methodId = spec.memberMethodId methodId = spec.memberMethodId
) )
val initStmt = spec.delegateInitStatement cls.instanceInitializers += object : Statement() {
?: scope.raiseIllegalState("missing delegated init statement for ${spec.name}") override val pos: Pos = spec.startPos
cls.instanceInitializers += requireBytecodeBody(scope, initStmt, "delegated function init") 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 { } else {
scope.addItem( scope.addItem(
spec.name, spec.name,
@ -158,13 +172,13 @@ internal suspend fun executeFunctionDecl(
delegate = finalDelegate delegate = finalDelegate
} }
} }
return finalDelegate return ObjVoid
} }
if (spec.isStatic || !spec.parentIsClassBody) { if (spec.isStatic || !spec.parentIsClassBody) {
spec.closureBox.closure = scope spec.closureBox.closure = scope
} }
if (spec.parentIsClassBody) { if (spec.parentIsClassBody && spec.captureSlots.isNotEmpty()) {
spec.closureBox.captureContext = scope spec.closureBox.captureContext = scope
} }
@ -174,67 +188,65 @@ internal suspend fun executeFunctionDecl(
spec.extTypeName?.let { typeName -> spec.extTypeName?.let { typeName ->
val type = scope[typeName]?.value ?: scope.raiseSymbolNotFound("class $typeName not found") val type = scope[typeName]?.value ?: scope.raiseSymbolNotFound("class $typeName not found")
if (type !is ObjClass) scope.raiseClassCastError("$typeName is not the class instance") if (type !is ObjClass) scope.raiseClassCastError("$typeName is not the class instance")
scope.addExtension( val stmt = object : Statement() {
type, override val pos: Pos = spec.startPos
spec.name, override suspend fun execute(scope: Scope): Obj {
ObjRecord( val result = (scope.thisObj as? ObjInstance)?.let { i ->
compiledFnBody, val execScope = if (compiledFnBody is net.sergeych.lyng.bytecode.BytecodeStatement) {
isMutable = false, scope.applyClosureForBytecode(i.instanceScope).also {
visibility = spec.visibility, it.args = scope.args
declaringClass = null, }
type = ObjRecord.Type.Fun } 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 wrapperName = spec.extensionWrapperName ?: extensionCallableName(typeName, spec.name)
val wrapper = ObjExtensionMethodCallable(spec.name, compiledFnBody) val wrapper = ObjExtensionMethodCallable(spec.name, stmt)
scope.addItem(wrapperName, false, wrapper, spec.visibility, recordType = ObjRecord.Type.Fun) scope.addItem(wrapperName, false, wrapper, spec.visibility, recordType = ObjRecord.Type.Fun)
} ?: run { } ?: run {
val th = scope.thisObj val th = scope.thisObj
if (!spec.isStatic && th is ObjClass) { if (!spec.isStatic && th is ObjClass) {
val cls: ObjClass = th val cls: ObjClass = th
cls.createField( cls.addFn(
spec.name, spec.name,
compiledFnBody,
isMutable = true, isMutable = true,
visibility = spec.visibility, visibility = spec.visibility,
pos = spec.startPos,
declaringClass = cls,
isAbstract = spec.isAbstract, isAbstract = spec.isAbstract,
isClosed = spec.isClosed, isClosed = spec.isClosed,
isOverride = spec.isOverride, isOverride = spec.isOverride,
type = ObjRecord.Type.Fun, pos = spec.startPos,
methodId = spec.memberMethodId methodId = spec.memberMethodId
) ) {
val memberValue = cls.members[spec.name]?.value ?: compiledFnBody val savedCtx = this.currentClassCtx
scope.addItem(spec.name, false, memberValue, spec.visibility, callSignature = spec.externCallSignature) 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 compiledFnBody
} else { } else {
scope.addItem( scope.addItem(spec.name, false, compiledFnBody, spec.visibility, callSignature = spec.externCallSignature)
spec.name,
false,
compiledFnBody,
spec.visibility,
recordType = ObjRecord.Type.Fun,
callSignature = spec.externCallSignature
)
} }
} }
return annotatedFnBody return annotatedFnBody
} }
private suspend fun requireBytecodeBody(
scope: Scope,
stmt: Statement,
label: String
): net.sergeych.lyng.bytecode.BytecodeStatement {
val bytecode = when (stmt) {
is net.sergeych.lyng.bytecode.BytecodeStatement -> stmt
is BytecodeBodyProvider -> stmt.bytecodeBody()
else -> null
}
return bytecode ?: scope.raiseIllegalState("$label requires bytecode statement")
}
class FunctionDeclStatement( class FunctionDeclStatement(
val spec: FunctionDeclSpec, val spec: FunctionDeclSpec,
) : Statement() { ) : Statement() {

View File

@ -28,19 +28,10 @@ class InlineBlockStatement(
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
var last: Obj = ObjVoid var last: Obj = ObjVoid
for (stmt in statements) { for (stmt in statements) {
last = requireBytecodeBody(scope, stmt, "inline block").execute(scope) last = stmt.execute(scope)
} }
return last return last
} }
fun statements(): List<Statement> = statements fun statements(): List<Statement> = statements
private suspend fun requireBytecodeBody(scope: Scope, stmt: Statement, label: String): net.sergeych.lyng.bytecode.BytecodeStatement {
val bytecode = when (stmt) {
is net.sergeych.lyng.bytecode.BytecodeStatement -> stmt
is BytecodeBodyProvider -> stmt.bytecodeBody()
else -> null
}
return bytecode ?: scope.raiseIllegalState("$label requires bytecode statement")
}
} }

View File

@ -1,139 +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
import net.sergeych.lyng.obj.ObjNull
import net.sergeych.lyng.obj.ObjProperty
import net.sergeych.lyng.obj.ObjRecord
import net.sergeych.lyng.obj.ObjString
import net.sergeych.lyng.obj.ObjUnset
import net.sergeych.lyng.obj.ObjVoid
class InstanceFieldInitStatement(
val storageName: String,
val isMutable: Boolean,
val visibility: Visibility,
val writeVisibility: Visibility?,
val isAbstract: Boolean,
val isClosed: Boolean,
val isOverride: Boolean,
val isTransient: Boolean,
val isLateInitVal: Boolean,
val initializer: Statement?,
override val pos: Pos,
) : Statement() {
override suspend fun execute(scope: Scope): Obj {
val initValue = initializer?.let { execBytecodeOnly(scope, it, "instance field init") }?.byValueCopy()
?: if (isLateInitVal) ObjUnset else ObjNull
scope.addItem(
storageName,
isMutable,
initValue,
visibility,
writeVisibility,
recordType = ObjRecord.Type.Field,
isAbstract = isAbstract,
isClosed = isClosed,
isOverride = isOverride,
isTransient = isTransient
)
return ObjVoid
}
private suspend fun execBytecodeOnly(scope: Scope, stmt: Statement, label: String): Obj {
val bytecode = when (stmt) {
is net.sergeych.lyng.bytecode.BytecodeStatement -> stmt
is BytecodeBodyProvider -> stmt.bytecodeBody()
else -> null
} ?: scope.raiseIllegalState("$label requires bytecode statement")
return bytecode.execute(scope)
}
}
class InstancePropertyInitStatement(
val storageName: String,
val isMutable: Boolean,
val visibility: Visibility,
val writeVisibility: Visibility?,
val isAbstract: Boolean,
val isClosed: Boolean,
val isOverride: Boolean,
val isTransient: Boolean,
val prop: ObjProperty,
override val pos: Pos,
) : Statement() {
override suspend fun execute(scope: Scope): Obj {
scope.addItem(
storageName,
isMutable,
prop,
visibility,
writeVisibility,
recordType = ObjRecord.Type.Property,
isAbstract = isAbstract,
isClosed = isClosed,
isOverride = isOverride,
isTransient = isTransient
)
return ObjVoid
}
}
class InstanceDelegatedInitStatement(
val storageName: String,
val memberName: String,
val isMutable: Boolean,
val visibility: Visibility,
val writeVisibility: Visibility?,
val isAbstract: Boolean,
val isClosed: Boolean,
val isOverride: Boolean,
val isTransient: Boolean,
val accessTypeLabel: String,
val initializer: Statement,
override val pos: Pos,
) : Statement() {
override suspend fun execute(scope: Scope): Obj {
val initValue = executeBytecodeWithSeed(scope, initializer, "instance delegated init")
val accessType = ObjString(accessTypeLabel)
val finalDelegate = try {
initValue.invokeInstanceMethod(
scope,
"bind",
Arguments(ObjString(memberName), accessType, scope.thisObj)
)
} catch (_: Exception) {
initValue
}
scope.addItem(
storageName,
isMutable,
ObjUnset,
visibility,
writeVisibility,
recordType = ObjRecord.Type.Delegated,
isAbstract = isAbstract,
isClosed = isClosed,
isOverride = isOverride,
isTransient = isTransient
).apply {
delegate = finalDelegate
}
return ObjVoid
}
}

View File

@ -17,8 +17,6 @@
package net.sergeych.lyng package net.sergeych.lyng
import net.sergeych.lyng.bytecode.BytecodeFrame
import net.sergeych.lyng.bytecode.CmdFunction
import net.sergeych.lyng.obj.ObjRecord import net.sergeych.lyng.obj.ObjRecord
import net.sergeych.lyng.obj.ObjString import net.sergeych.lyng.obj.ObjString
import net.sergeych.lyng.pacman.ImportProvider import net.sergeych.lyng.pacman.ImportProvider
@ -36,27 +34,6 @@ class ModuleScope(
constructor(importProvider: ImportProvider, source: Source) : this(importProvider, source.startPos, source.fileName) constructor(importProvider: ImportProvider, source: Source) : this(importProvider, source.startPos, source.fileName)
internal var importedModules: List<ModuleScope> = emptyList() internal var importedModules: List<ModuleScope> = emptyList()
internal var moduleFrame: BytecodeFrame? = null
internal var moduleFrameLocalCount: Int = -1
internal var moduleFrameLocalSlotNames: Array<String?> = emptyArray()
internal var moduleFrameLocalSlotMutables: BooleanArray = BooleanArray(0)
internal var moduleFrameLocalSlotDelegated: BooleanArray = BooleanArray(0)
internal fun ensureModuleFrame(fn: CmdFunction): BytecodeFrame {
val current = moduleFrame
val frame = if (current == null || moduleFrameLocalCount != fn.localCount) {
BytecodeFrame(fn.localCount, 0).also {
moduleFrame = it
moduleFrameLocalCount = fn.localCount
}
} else {
current
}
moduleFrameLocalSlotNames = fn.localSlotNames
moduleFrameLocalSlotMutables = fn.localSlotMutables
moduleFrameLocalSlotDelegated = fn.localSlotDelegated
return frame
}
/** /**
* Import symbols into the scope. It _is called_ after the module is imported by [ImportProvider.prepareImport] * Import symbols into the scope. It _is called_ after the module is imported by [ImportProvider.prepareImport]

View File

@ -1,60 +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.bytecode.CmdFrame
import net.sergeych.lyng.bytecode.CmdVm
import net.sergeych.lyng.obj.Obj
import net.sergeych.lyng.obj.ObjNull
class PropertyAccessorStatement(
val body: Statement,
val argName: String?,
override val pos: Pos,
) : Statement() {
override suspend fun execute(scope: Scope): Obj {
if (argName != null) {
val prev = scope.skipScopeCreation
scope.skipScopeCreation = true
return try {
val bytecodeStmt = requireBytecodeBody(scope, body, "property accessor")
val fn = bytecodeStmt.bytecodeFunction()
val binder: suspend (CmdFrame, Arguments) -> Unit = { frame, arguments ->
val slotPlan = fn.localSlotPlanByName()
val slotIndex = slotPlan[argName]
?: scope.raiseIllegalState("property accessor argument $argName missing from slot plan")
val argValue = arguments.list.firstOrNull() ?: ObjNull
frame.frame.setObj(slotIndex, argValue)
}
scope.pos = pos
CmdVm().execute(fn, scope, scope.args, binder)
} finally {
scope.skipScopeCreation = prev
}
}
return requireBytecodeBody(scope, body, "property accessor").execute(scope)
}
private suspend fun requireBytecodeBody(scope: Scope, stmt: Statement, label: String): net.sergeych.lyng.bytecode.BytecodeStatement {
val bytecode = when (stmt) {
is net.sergeych.lyng.bytecode.BytecodeStatement -> stmt
is BytecodeBodyProvider -> stmt.bytecodeBody()
else -> null
}
return bytecode ?: scope.raiseIllegalState("$label requires bytecode statement")
}
}

View File

@ -20,7 +20,7 @@ package net.sergeych.lyng
/** /**
* Tiny, size-bounded cache for compiled Regex patterns. Activated only when [PerfFlags.REGEX_CACHE] is true. * Tiny, size-bounded cache for compiled Regex patterns. Activated only when [PerfFlags.REGEX_CACHE] is true.
* This is a very simple FIFO-ish cache sufficient for micro-benchmarks and common repeated patterns. * This is a very simple FIFO-ish cache sufficient for micro-benchmarks and common repeated patterns.
* Not thread-safe by design; the runtime typically runs scripts on confined executors. * Not thread-safe by design; the interpreter typically runs scripts on confined executors.
*/ */
object RegexCache { object RegexCache {
private const val MAX = 64 private const val MAX = 64

View File

@ -39,7 +39,7 @@ fun nextFrameId(): Long = FrameIdGen.nextId()
* *
* There are special types of scopes: * There are special types of scopes:
* *
* - [BytecodeClosureScope] - scope used to apply a closure to some thisObj scope * - [ClosureScope] - scope used to apply a closure to some thisObj scope
*/ */
open class Scope( open class Scope(
var parent: Scope?, var parent: Scope?,
@ -76,19 +76,9 @@ open class Scope(
internal fun setThisVariants(primary: Obj, extras: List<Obj>) { internal fun setThisVariants(primary: Obj, extras: List<Obj>) {
thisObj = primary thisObj = primary
val extrasSnapshot = when {
extras.isEmpty() -> emptyList()
else -> {
try {
extras.toList()
} catch (_: Exception) {
emptyList()
}
}
}
thisVariants.clear() thisVariants.clear()
thisVariants.add(primary) thisVariants.add(primary)
for (obj in extrasSnapshot) { for (obj in extras) {
if (obj !== primary && !thisVariants.contains(obj)) { if (obj !== primary && !thisVariants.contains(obj)) {
thisVariants.add(obj) thisVariants.add(obj)
} }
@ -108,7 +98,7 @@ open class Scope(
for (cls in receiverClass.mro) { for (cls in receiverClass.mro) {
s.extensions[cls]?.get(name)?.let { return it } s.extensions[cls]?.get(name)?.let { return it }
} }
if (s is BytecodeClosureScope) { if (s is ClosureScope) {
s.closureScope.findExtension(receiverClass, name)?.let { return it } s.closureScope.findExtension(receiverClass, name)?.let { return it }
} }
s = s.parent s = s.parent
@ -132,7 +122,7 @@ open class Scope(
/** /**
* Internal lookup helpers that deliberately avoid invoking overridden `get` implementations * Internal lookup helpers that deliberately avoid invoking overridden `get` implementations
* (notably in BytecodeClosureScope) to prevent accidental ping-pong and infinite recursion across * (notably in ClosureScope) to prevent accidental ping-pong and infinite recursion across
* intertwined closure frames. They traverse the plain parent chain and consult only locals * intertwined closure frames. They traverse the plain parent chain and consult only locals
* and bindings of each frame. Instance/class member fallback must be decided by the caller. * and bindings of each frame. Instance/class member fallback must be decided by the caller.
*/ */
@ -175,16 +165,23 @@ open class Scope(
val effectiveCaller = caller ?: currentClassCtx val effectiveCaller = caller ?: currentClassCtx
while (s != null && hops++ < 1024) { while (s != null && hops++ < 1024) {
tryGetLocalRecord(s, name, effectiveCaller)?.let { return it } tryGetLocalRecord(s, name, effectiveCaller)?.let { return it }
s = if (followClosure && s is BytecodeClosureScope) s.closureScope else s.parent s = if (followClosure && s is ClosureScope) s.closureScope else s.parent
} }
return null return null
} }
internal fun resolveCaptureRecord(name: String): ObjRecord? {
if (captureRecords != null) {
raiseIllegalState("resolveCaptureRecord is interpreter-only; bytecode captures use captureRecords")
}
return chainLookupIgnoreClosure(name, followClosure = true, caller = currentClassCtx)
}
/** /**
* Perform base Scope.get semantics for this frame without delegating into parent.get * Perform base Scope.get semantics for this frame without delegating into parent.get
* virtual dispatch. This checks: * virtual dispatch. This checks:
* - locals/bindings in this frame * - locals/bindings in this frame
* - walks raw parent chain for locals/bindings (ignoring BytecodeClosureScope-specific overrides) * - walks raw parent chain for locals/bindings (ignoring ClosureScope-specific overrides)
* - finally falls back to this frame's `thisObj` instance/class members * - finally falls back to this frame's `thisObj` instance/class members
*/ */
internal fun baseGetIgnoreClosure(name: String): ObjRecord? { internal fun baseGetIgnoreClosure(name: String): ObjRecord? {
@ -214,7 +211,7 @@ open class Scope(
* - locals/bindings of each frame * - locals/bindings of each frame
* - then instance/class members of each frame's `thisObj`. * - then instance/class members of each frame's `thisObj`.
* This completely avoids invoking overridden `get` implementations, preventing * This completely avoids invoking overridden `get` implementations, preventing
* ping-pong recursion between `BytecodeClosureScope` frames. * ping-pong recursion between `ClosureScope` frames.
*/ */
internal fun chainLookupWithMembers(name: String, caller: net.sergeych.lyng.obj.ObjClass? = currentClassCtx, followClosure: Boolean = false): ObjRecord? { internal fun chainLookupWithMembers(name: String, caller: net.sergeych.lyng.obj.ObjClass? = currentClassCtx, followClosure: Boolean = false): ObjRecord? {
var s: Scope? = this var s: Scope? = this
@ -231,7 +228,7 @@ open class Scope(
} else return rec } else return rec
} }
} }
s = if (followClosure && s is BytecodeClosureScope) s.closureScope else s.parent s = if (followClosure && s is ClosureScope) s.closureScope else s.parent
} }
return null return null
} }
@ -638,6 +635,11 @@ open class Scope(
it.value = value it.value = value
// keep local binding index consistent within the frame // keep local binding index consistent within the frame
localBindings[name] = it localBindings[name] = it
// If we are a ClosureScope, mirror binding into the caller frame to keep it discoverable
// across suspension when resumed on the call frame
if (this is ClosureScope) {
callScope.localBindings[name] = it
}
bumpClassLayoutIfNeeded(name, value, recordType) bumpClassLayoutIfNeeded(name, value, recordType)
it it
} ?: addItem(name, true, value, visibility, writeVisibility, recordType, isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride) } ?: addItem(name, true, value, visibility, writeVisibility, recordType, isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride)
@ -692,6 +694,19 @@ open class Scope(
} }
// Index this binding within the current frame to help resolve locals across suspension // Index this binding within the current frame to help resolve locals across suspension
localBindings[name] = rec localBindings[name] = rec
// If we are a ClosureScope, mirror binding into the caller frame to keep it discoverable
// across suspension when resumed on the call frame
if (this is ClosureScope) {
callScope.localBindings[name] = rec
// Additionally, expose the binding in caller's objects and slot map so identifier
// resolution after suspension can still find it even if the active scope is a child
// of the callScope (e.g., due to internal withChildFrame usage).
// This keeps visibility within the method body but prevents leaking outside the caller frame.
callScope.objects[name] = rec
if (callScope.getSlotIndexOf(name) == null) {
callScope.allocateSlotFor(name, rec)
}
}
// Map to a slot for fast local access (ensure consistency) // Map to a slot for fast local access (ensure consistency)
if (nameToSlot.isEmpty()) { if (nameToSlot.isEmpty()) {
allocateSlotFor(name, rec) allocateSlotFor(name, rec)
@ -719,7 +734,7 @@ open class Scope(
return ns.objClass return ns.objClass
} }
inline fun addVoidFn(vararg names: String, crossinline fn: suspend ScopeFacade.() -> Unit) { inline fun addVoidFn(vararg names: String, crossinline fn: suspend Scope.() -> Unit) {
addFn(*names) { addFn(*names) {
fn(this) fn(this)
ObjVoid ObjVoid
@ -735,8 +750,13 @@ open class Scope(
return CmdDisassembler.disassemble(bytecode) return CmdDisassembler.disassemble(bytecode)
} }
fun addFn(vararg names: String, callSignature: CallSignature? = null, fn: suspend ScopeFacade.() -> Obj) { fun addFn(vararg names: String, callSignature: CallSignature? = null, fn: suspend Scope.() -> Obj) {
val newFn = net.sergeych.lyng.obj.ObjExternCallable.fromBridge { fn() } val newFn = object : Statement() {
override val pos: Pos = Pos.builtIn
override suspend fun execute(scope: Scope): Obj = scope.fn()
}
for (name in names) { for (name in names) {
addItem( addItem(
name, name,
@ -814,34 +834,41 @@ open class Scope(
} }
open fun applyClosure(closure: Scope, preferredThisType: String? = null): Scope = open fun applyClosure(closure: Scope, preferredThisType: String? = null): Scope =
BytecodeClosureScope(this, closure, preferredThisType) ClosureScope(this, closure, preferredThisType)
internal fun applyClosureForBytecode(closure: Scope, preferredThisType: String? = null): Scope { internal fun applyClosureForBytecode(closure: Scope, preferredThisType: String? = null): Scope {
return BytecodeClosureScope(this, closure, preferredThisType) return BytecodeClosureScope(this, closure, preferredThisType)
} }
/** /**
* Resolve and evaluate a qualified identifier (e.g. `A.B.C`) using the same name/field * Resolve and evaluate a qualified identifier exactly as compiled code would.
* resolution rules as compiled code, without invoking the compiler or ObjRef evaluation. * For input like `A.B.C`, it builds the same ObjRef chain the compiler emits:
* `LocalVarRef("A", Pos.builtIn)` followed by `FieldRef` for each segment, then evaluates it.
* This mirrors `eval("A.B.C")` resolution semantics without invoking the compiler.
*/ */
suspend fun resolveQualifiedIdentifier(qualifiedName: String): Obj { suspend fun resolveQualifiedIdentifier(qualifiedName: String): Obj {
val trimmed = qualifiedName.trim() val trimmed = qualifiedName.trim()
if (trimmed.isEmpty()) raiseSymbolNotFound("empty identifier") if (trimmed.isEmpty()) raiseSymbolNotFound("empty identifier")
val parts = trimmed.split('.') val parts = trimmed.split('.')
val first = parts[0] val first = parts[0]
val base: Obj = if (first == "this") { val ref: ObjRef = if (first == "this") {
thisObj ConstRef(thisObj.asReadonly)
} else { } else {
val rec = get(first) ?: raiseSymbolNotFound(first) var s: Scope? = this
resolve(rec, first) var slot: Int? = null
var guard = 0
while (s != null && guard++ < 1024 && slot == null) {
slot = s.getSlotIndexOf(first)
s = s.parent
}
if (slot == null) raiseSymbolNotFound(first)
LocalSlotRef(first, slot, 0, isMutable = false, isDelegated = false, Pos.builtIn, strict = true)
} }
var current = base var ref0: ObjRef = ref
for (i in 1 until parts.size) { for (i in 1 until parts.size) {
val name = parts[i] ref0 = FieldRef(ref0, parts[i], false)
val rec = current.readField(this, name)
current = resolve(rec, name)
} }
return current return ref0.evalValue(this)
} }
suspend fun resolve(rec: ObjRecord, name: String): Obj { suspend fun resolve(rec: ObjRecord, name: String): Obj {
@ -892,10 +919,4 @@ open class Scope(
fun new(): Scope = fun new(): Scope =
Script.defaultImportManager.copy().newModuleAt(Pos.builtIn) Script.defaultImportManager.copy().newModuleAt(Pos.builtIn)
} }
fun requireClass(name: String): net.sergeych.lyng.obj.ObjClass {
val rec = get(name) ?: raiseSymbolNotFound(name)
return rec.value as? net.sergeych.lyng.obj.ObjClass
?: raiseClassCastError("Expected class $name, got ${rec.value.objClass.className}")
}
} }

View File

@ -1,110 +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
import net.sergeych.lyng.obj.ObjRecord
import net.sergeych.lyng.obj.ObjString
/**
* Limited facade for Kotlin bridge callables.
* Exposes only the minimal API needed to read/write vars and invoke methods.
*/
interface ScopeFacade {
val args: Arguments
var pos: Pos
var thisObj: Obj
operator fun get(name: String): ObjRecord?
suspend fun resolve(rec: ObjRecord, name: String): Obj
suspend fun assign(rec: ObjRecord, name: String, newValue: Obj)
fun raiseError(message: String): Nothing
fun raiseError(obj: net.sergeych.lyng.obj.ObjException): Nothing
fun raiseClassCastError(message: String): Nothing
fun raiseIllegalArgument(message: String): Nothing
fun raiseNoSuchElement(message: String = "No such element"): Nothing
fun raiseSymbolNotFound(name: String): Nothing
fun raiseIllegalState(message: String = "Illegal argument error"): Nothing
fun raiseNotImplemented(what: String = "operation"): Nothing
suspend fun call(callee: Obj, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Obj
suspend fun toStringOf(obj: Obj, forInspect: Boolean = false): ObjString
suspend fun inspect(obj: Obj): String
fun trace(text: String = "")
}
internal class ScopeBridge(internal val scope: Scope) : ScopeFacade {
override val args: Arguments
get() = scope.args
override var pos: Pos
get() = scope.pos
set(value) { scope.pos = value }
override var thisObj: Obj
get() = scope.thisObj
set(value) { scope.thisObj = value }
override fun get(name: String): ObjRecord? = scope[name]
override suspend fun resolve(rec: ObjRecord, name: String): Obj = scope.resolve(rec, name)
override suspend fun assign(rec: ObjRecord, name: String, newValue: Obj) = scope.assign(rec, name, newValue)
override fun raiseError(message: String): Nothing = scope.raiseError(message)
override fun raiseError(obj: net.sergeych.lyng.obj.ObjException): Nothing = scope.raiseError(obj)
override fun raiseClassCastError(message: String): Nothing = scope.raiseClassCastError(message)
override fun raiseIllegalArgument(message: String): Nothing = scope.raiseIllegalArgument(message)
override fun raiseNoSuchElement(message: String): Nothing = scope.raiseNoSuchElement(message)
override fun raiseSymbolNotFound(name: String): Nothing = scope.raiseSymbolNotFound(name)
override fun raiseIllegalState(message: String): Nothing = scope.raiseIllegalState(message)
override fun raiseNotImplemented(what: String): Nothing = scope.raiseNotImplemented(what)
override suspend fun call(callee: Obj, args: Arguments, newThisObj: Obj?): Obj {
return callee.callOn(scope.createChildScope(scope.pos, args = args, newThisObj = newThisObj))
}
override suspend fun toStringOf(obj: Obj, forInspect: Boolean): ObjString = obj.toString(scope, forInspect)
override suspend fun inspect(obj: Obj): String = obj.inspect(scope)
override fun trace(text: String) = scope.trace(text)
}
/** Public factory for bridge facades. */
fun Scope.asFacade(): ScopeFacade = ScopeBridge(this)
inline fun <reified T : Obj> ScopeFacade.requiredArg(index: Int): T {
if (args.list.size <= index) raiseError("Expected at least ${index + 1} argument, got ${args.list.size}")
return (args.list[index].byValueCopy() as? T)
?: raiseClassCastError("Expected type ${T::class.simpleName}, got ${args.list[index]::class.simpleName}")
}
inline fun <reified T : Obj> ScopeFacade.requireOnlyArg(): T {
if (args.list.size != 1) raiseError("Expected exactly 1 argument, got ${args.list.size}")
return requiredArg(0)
}
fun ScopeFacade.requireExactCount(count: Int) {
if (args.list.size != count) {
raiseError("Expected exactly $count arguments, got ${args.list.size}")
}
}
fun ScopeFacade.requireNoArgs() {
if (args.list.isNotEmpty()) {
raiseError("This function does not accept any arguments")
}
}
inline fun <reified T : Obj> ScopeFacade.thisAs(): T {
val obj = thisObj
return (obj as? T) ?: raiseClassCastError(
"Cannot cast ${obj.objClass.className} to ${T::class.simpleName}"
)
}
fun ScopeFacade.requireScope(): Scope =
(this as? ScopeBridge)?.scope ?: raiseIllegalState("ScopeFacade requires ScopeBridge")

View File

@ -26,7 +26,6 @@ import net.sergeych.lyng.miniast.*
import net.sergeych.lyng.obj.* import net.sergeych.lyng.obj.*
import net.sergeych.lyng.pacman.ImportManager import net.sergeych.lyng.pacman.ImportManager
import net.sergeych.lyng.stdlib_included.rootLyng import net.sergeych.lyng.stdlib_included.rootLyng
import net.sergeych.lyng.bridge.LyngClassBridge
import net.sergeych.lynon.ObjLynonClass import net.sergeych.lynon.ObjLynonClass
import net.sergeych.mp_tools.globalDefer import net.sergeych.mp_tools.globalDefer
import kotlin.math.* import kotlin.math.*
@ -36,65 +35,70 @@ class Script(
override val pos: Pos, override val pos: Pos,
private val statements: List<Statement> = emptyList(), private val statements: List<Statement> = emptyList(),
private val moduleSlotPlan: Map<String, Int> = emptyMap(), private val moduleSlotPlan: Map<String, Int> = emptyMap(),
private val moduleDeclaredNames: Set<String> = emptySet(),
private val importBindings: Map<String, ImportBinding> = emptyMap(), private val importBindings: Map<String, ImportBinding> = emptyMap(),
private val importedModules: List<ImportBindingSource.Module> = emptyList(), private val importedModules: List<ImportBindingSource.Module> = emptyList(),
private val moduleBytecode: CmdFunction? = null, private val moduleBytecode: CmdFunction? = null,
// private val catchReturn: Boolean = false, // private val catchReturn: Boolean = false,
) : Statement() { ) : Statement() {
fun statements(): List<Statement> = statements
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
scope.pos = pos scope.pos = pos
val execScope = resolveModuleScope(scope) ?: scope val isModuleScope = scope is ModuleScope
val isModuleScope = execScope is ModuleScope val shouldSeedModule = isModuleScope || scope.thisObj === ObjVoid
val shouldSeedModule = isModuleScope || execScope.thisObj === ObjVoid val moduleTarget = scope
val moduleTarget = execScope if (moduleSlotPlan.isNotEmpty() && shouldSeedModule) {
val hasPlanMapping = moduleSlotPlan.keys.any { moduleTarget.getSlotIndexOf(it) != null }
val needsReset = moduleTarget is ModuleScope ||
moduleTarget.slotCount() == 0 ||
moduleTarget.hasSlotPlanConflict(moduleSlotPlan) ||
(!hasPlanMapping && moduleTarget.slotCount() > 0)
if (needsReset) {
val preserved = LinkedHashMap<String, ObjRecord>()
for (name in moduleSlotPlan.keys) {
moduleTarget.getLocalRecordDirect(name)?.let { preserved[name] = it }
}
moduleTarget.applySlotPlanReset(moduleSlotPlan, preserved)
for (name in moduleSlotPlan.keys) {
if (preserved.containsKey(name)) continue
val inherited = findSeedRecord(moduleTarget.parent, name)
if (inherited != null && inherited.value !== ObjUnset) {
moduleTarget.updateSlotFor(name, inherited)
}
}
} else {
moduleTarget.applySlotPlan(moduleSlotPlan)
for (name in moduleSlotPlan.keys) {
val local = moduleTarget.getLocalRecordDirect(name)
if (local != null && local.value !== ObjUnset) {
moduleTarget.updateSlotFor(name, local)
continue
}
val inherited = findSeedRecord(moduleTarget.parent, name)
if (inherited != null && inherited.value !== ObjUnset) {
moduleTarget.updateSlotFor(name, inherited)
}
}
}
}
if (shouldSeedModule) { if (shouldSeedModule) {
seedModuleSlots(moduleTarget, scope) seedModuleSlots(moduleTarget)
} }
moduleBytecode?.let { fn -> moduleBytecode?.let { fn ->
if (execScope is ModuleScope) { return CmdVm().execute(fn, scope, scope.args)
execScope.ensureModuleFrame(fn)
}
return CmdVm().execute(fn, execScope, scope.args) { frame, _ ->
seedModuleLocals(frame, moduleTarget, scope)
}
} }
if (statements.isNotEmpty()) { var lastResult: Obj = ObjVoid
scope.raiseIllegalState("bytecode-only execution is required; missing module bytecode") for (s in statements) {
lastResult = s.execute(scope)
} }
return ObjVoid return lastResult
} }
private suspend fun seedModuleSlots(scope: Scope, seedScope: Scope) { private suspend fun seedModuleSlots(scope: Scope) {
if (importBindings.isEmpty() && importedModules.isEmpty()) return if (importBindings.isEmpty() && importedModules.isEmpty()) return
seedImportBindings(scope, seedScope) seedImportBindings(scope)
} }
private suspend fun seedModuleLocals( private suspend fun seedImportBindings(scope: Scope) {
frame: net.sergeych.lyng.bytecode.CmdFrame,
scope: Scope,
seedScope: Scope
) {
val localNames = frame.fn.localSlotNames
if (localNames.isEmpty()) return
val values = HashMap<String, Obj>()
val base = frame.fn.scopeSlotCount
for (i in localNames.indices) {
val name = localNames[i] ?: continue
if (moduleDeclaredNames.contains(name)) continue
val record = seedScope.getLocalRecordDirect(name) ?: findSeedRecord(seedScope, name) ?: continue
val value = if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property) {
scope.resolve(record, name)
} else {
record.value
}
frame.setObjUnchecked(base + i, value)
}
}
private suspend fun seedImportBindings(scope: Scope, seedScope: Scope) {
val provider = scope.currentImportProvider val provider = scope.currentImportProvider
val importedModules = LinkedHashSet<ModuleScope>() val importedModules = LinkedHashSet<ModuleScope>()
for (moduleRef in this.importedModules) { for (moduleRef in this.importedModules) {
@ -116,7 +120,7 @@ class Script(
?: scope.raiseSymbolNotFound("symbol ${binding.symbol} not found") ?: scope.raiseSymbolNotFound("symbol ${binding.symbol} not found")
} }
ImportBindingSource.Seed -> { ImportBindingSource.Seed -> {
findSeedRecord(seedScope, binding.symbol) findSeedRecord(scope, binding.symbol)
?: scope.raiseSymbolNotFound("symbol ${binding.symbol} not found") ?: scope.raiseSymbolNotFound("symbol ${binding.symbol} not found")
} }
} }
@ -153,7 +157,12 @@ class Script(
} }
private fun resolveModuleScope(scope: Scope): ModuleScope? { private fun resolveModuleScope(scope: Scope): ModuleScope? {
return scope as? ModuleScope var current: Scope? = scope
while (current != null) {
if (current is ModuleScope) return current
current = current.parent
}
return null
} }
internal fun debugStatements(): List<Statement> = statements internal fun debugStatements(): List<Statement> = statements
@ -176,15 +185,15 @@ class Script(
addConst("Unset", ObjUnset) addConst("Unset", ObjUnset)
addFn("print") { addFn("print") {
for ((i, a) in args.withIndex()) { for ((i, a) in args.withIndex()) {
if (i > 0) print(' ' + toStringOf(a).value) if (i > 0) print(' ' + a.toString(this).value)
else print(toStringOf(a).value) else print(a.toString(this).value)
} }
ObjVoid ObjVoid
} }
addFn("println") { addFn("println") {
for ((i, a) in args.withIndex()) { for ((i, a) in args.withIndex()) {
if (i > 0) print(' ' + toStringOf(a).value) if (i > 0) print(' ' + a.toString(this).value)
else print(toStringOf(a).value) else print(a.toString(this).value)
} }
println() println()
ObjVoid ObjVoid
@ -197,7 +206,7 @@ class Script(
} else { } else {
Arguments.EMPTY Arguments.EMPTY
} }
call(callee, rest) callee.callOn(createChildScope(pos, args = rest))
} }
addFn("floor") { addFn("floor") {
val x = args.firstAndOnly() val x = args.firstAndOnly()
@ -295,12 +304,12 @@ class Script(
var result = value var result = value
if (range.start != null && !range.start.isNull) { if (range.start != null && !range.start.isNull) {
if (result.compareTo(requireScope(), range.start) < 0) { if (result.compareTo(this, range.start) < 0) {
result = range.start result = range.start
} }
} }
if (range.end != null && !range.end.isNull) { if (range.end != null && !range.end.isNull) {
val cmp = range.end.compareTo(requireScope(), result) val cmp = range.end.compareTo(this, result)
if (range.isEndInclusive) { if (range.isEndInclusive) {
if (cmp < 0) result = range.end if (cmp < 0) result = range.end
} else { } else {
@ -323,53 +332,43 @@ class Script(
addVoidFn("assert") { addVoidFn("assert") {
val cond = requiredArg<ObjBool>(0) val cond = requiredArg<ObjBool>(0)
val message = if (args.size > 1) val message = if (args.size > 1)
": " + toStringOf(call(args[1] as Obj)).value ": " + (args[1] as Statement).execute(this).toString(this).value
else "" else ""
if (!cond.value == true) if (!cond.value == true)
raiseError(ObjAssertionFailedException(requireScope(), "Assertion failed$message")) raiseError(ObjAssertionFailedException(this, "Assertion failed$message"))
}
fun unwrapCompareArg(value: Obj): Obj {
val resolved = when (value) {
is FrameSlotRef -> value.read()
is RecordSlotRef -> value.read()
else -> value
}
return if (resolved === ObjUnset.objClass) ObjUnset else resolved
} }
addVoidFn("assertEquals") { addVoidFn("assertEquals") {
val a = unwrapCompareArg(requiredArg(0)) val a = requiredArg<Obj>(0)
val b = unwrapCompareArg(requiredArg(1)) val b = requiredArg<Obj>(1)
if (a.compareTo(requireScope(), b) != 0) { if (a.compareTo(this, b) != 0)
raiseError( raiseError(
ObjAssertionFailedException( ObjAssertionFailedException(
requireScope(), this,
"Assertion failed: ${inspect(a)} == ${inspect(b)}" "Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"
) )
) )
}
} }
// alias used in tests // alias used in tests
addVoidFn("assertEqual") { addVoidFn("assertEqual") {
val a = unwrapCompareArg(requiredArg(0)) val a = requiredArg<Obj>(0)
val b = unwrapCompareArg(requiredArg(1)) val b = requiredArg<Obj>(1)
if (a.compareTo(requireScope(), b) != 0) if (a.compareTo(this, b) != 0)
raiseError( raiseError(
ObjAssertionFailedException( ObjAssertionFailedException(
requireScope(), this,
"Assertion failed: ${inspect(a)} == ${inspect(b)}" "Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"
) )
) )
} }
addVoidFn("assertNotEquals") { addVoidFn("assertNotEquals") {
val a = unwrapCompareArg(requiredArg(0)) val a = requiredArg<Obj>(0)
val b = unwrapCompareArg(requiredArg(1)) val b = requiredArg<Obj>(1)
if (a.compareTo(requireScope(), b) == 0) if (a.compareTo(this, b) == 0)
raiseError( raiseError(
ObjAssertionFailedException( ObjAssertionFailedException(
requireScope(), this,
"Assertion failed: ${inspect(a)} != ${inspect(b)}" "Assertion failed: ${a.inspect(this)} != ${b.inspect(this)}"
) )
) )
} }
@ -386,23 +385,23 @@ class Script(
will be accepted. will be accepted.
""".trimIndent() """.trimIndent()
) { ) {
val code: Obj val code: Statement
val expectedClass: ObjClass? val expectedClass: ObjClass?
when (args.size) { when (args.size) {
1 -> { 1 -> {
code = requiredArg<Obj>(0) code = requiredArg<Statement>(0)
expectedClass = null expectedClass = null
} }
2 -> { 2 -> {
code = requiredArg<Obj>(1) code = requiredArg<Statement>(1)
expectedClass = requiredArg<ObjClass>(0) expectedClass = requiredArg<ObjClass>(0)
} }
else -> raiseIllegalArgument("Expected 1 or 2 arguments, got ${args.size}") else -> raiseIllegalArgument("Expected 1 or 2 arguments, got ${args.size}")
} }
val result = try { val result = try {
call(code) code.execute(this)
null null
} catch (e: ExecutionError) { } catch (e: ExecutionError) {
e.errorObject e.errorObject
@ -411,7 +410,7 @@ class Script(
} }
if (result == null) raiseError( if (result == null) raiseError(
ObjAssertionFailedException( ObjAssertionFailedException(
requireScope(), this,
"Expected exception but nothing was thrown" "Expected exception but nothing was thrown"
) )
) )
@ -425,7 +424,7 @@ class Script(
} }
addFn("dynamic", callSignature = CallSignature(tailBlockReceiverType = "DelegateContext")) { addFn("dynamic", callSignature = CallSignature(tailBlockReceiverType = "DelegateContext")) {
ObjDynamic.create(requireScope(), requireOnlyArg()) ObjDynamic.create(this, requireOnlyArg())
} }
val root = this val root = this
@ -442,7 +441,7 @@ class Script(
val condition = requiredArg<ObjBool>(0) val condition = requiredArg<ObjBool>(0)
if (!condition.value) { if (!condition.value) {
var message = args.list.getOrNull(1) var message = args.list.getOrNull(1)
if (message is Obj && message.objClass == Statement.type) message = call(message) if (message is Statement) message = message.execute(this)
raiseIllegalArgument(message?.toString() ?: "requirement not met") raiseIllegalArgument(message?.toString() ?: "requirement not met")
} }
ObjVoid ObjVoid
@ -451,34 +450,38 @@ class Script(
val condition = requiredArg<ObjBool>(0) val condition = requiredArg<ObjBool>(0)
if (!condition.value) { if (!condition.value) {
var message = args.list.getOrNull(1) var message = args.list.getOrNull(1)
if (message is Obj && message.objClass == Statement.type) message = call(message) if (message is Statement) message = message.execute(this)
raiseIllegalState(message?.toString() ?: "check failed") raiseIllegalState(message?.toString() ?: "check failed")
} }
ObjVoid ObjVoid
} }
addFn("traceScope") { addFn("traceScope") {
trace(args.getOrNull(0)?.toString() ?: "") this.trace(args.getOrNull(0)?.toString() ?: "")
ObjVoid ObjVoid
} }
addFn("run") { addFn("run") {
call(requireOnlyArg()) requireOnlyArg<Statement>().execute(this)
} }
addFn("cached") { addFn("cached") {
val builder = requireOnlyArg<Obj>() val builder = requireOnlyArg<Statement>()
val capturedScope = this val capturedScope = this
var calculated = false var calculated = false
var cachedValue: Obj = ObjVoid var cachedValue: Obj = ObjVoid
net.sergeych.lyng.obj.ObjExternCallable.fromBridge { val thunk = object : Statement() {
if (!calculated) { override val pos: Pos = Pos.builtIn
cachedValue = capturedScope.call(builder) override suspend fun execute(scope: Scope): Obj {
calculated = true if (!calculated) {
cachedValue = builder.execute(capturedScope)
calculated = true
}
return cachedValue
} }
cachedValue
} }
thunk
} }
addFn("lazy") { addFn("lazy") {
val builder = requireOnlyArg<Obj>() val builder = requireOnlyArg<Statement>()
ObjLazyDelegate(builder, requireScope()) ObjLazyDelegate(builder, this)
} }
addVoidFn("delay") { addVoidFn("delay") {
val a = args.firstAndOnly() val a = args.firstAndOnly()
@ -486,7 +489,7 @@ class Script(
is ObjInt -> delay(a.value) is ObjInt -> delay(a.value)
is ObjReal -> delay((a.value * 1000).roundToLong()) is ObjReal -> delay((a.value * 1000).roundToLong())
is ObjDuration -> delay(a.duration) is ObjDuration -> delay(a.duration)
else -> raiseIllegalArgument("Expected Int, Real or Duration, got ${inspect(a)}") else -> raiseIllegalArgument("Expected Int, Real or Duration, got ${a.inspect(this)}")
} }
} }
@ -524,10 +527,9 @@ class Script(
addConst("MapEntry", ObjMapEntry.type) addConst("MapEntry", ObjMapEntry.type)
addFn("launch") { addFn("launch") {
val callable = requireOnlyArg<Obj>() val callable = requireOnlyArg<Statement>()
val captured = this
ObjDeferred(globalDefer { ObjDeferred(globalDefer {
captured.call(callable) callable.execute(this@addFn)
}) })
} }
@ -539,7 +541,7 @@ class Script(
addFn("flow", callSignature = CallSignature(tailBlockReceiverType = "FlowBuilder")) { addFn("flow", callSignature = CallSignature(tailBlockReceiverType = "FlowBuilder")) {
// important is: current context contains closure often used in call; // important is: current context contains closure often used in call;
// we'll need it for the producer // we'll need it for the producer
ObjFlow(requireOnlyArg<Obj>(), requireScope()) ObjFlow(requireOnlyArg<Statement>(), this)
} }
val pi = ObjReal(PI) val pi = ObjReal(PI)
@ -561,10 +563,9 @@ class Script(
val defaultImportManager: ImportManager by lazy { val defaultImportManager: ImportManager by lazy {
ImportManager(rootScope, SecurityManager.allowAll).apply { ImportManager(rootScope, SecurityManager.allowAll).apply {
addPackage("lyng.stdlib") { module -> addTextPackages(
module.eval(Source("lyng.stdlib", rootLyng)) rootLyng
ObjKotlinIterator.bindTo(module.requireClass("KotlinIterator")) )
}
addPackage("lyng.buffer") { addPackage("lyng.buffer") {
it.addConstDoc( it.addConstDoc(
name = "Buffer", name = "Buffer",
@ -615,7 +616,7 @@ class Script(
is ObjInt -> delay(a.value * 1000) is ObjInt -> delay(a.value * 1000)
is ObjReal -> delay((a.value * 1000).roundToLong()) is ObjReal -> delay((a.value * 1000).roundToLong())
is ObjDuration -> delay(a.duration) is ObjDuration -> delay(a.duration)
else -> raiseIllegalArgument("Expected Duration, Int or Real, got ${inspect(a)}") else -> raiseIllegalArgument("Expected Duration, Int or Real, got ${a.inspect(this)}")
} }
} }
} }

View File

@ -19,6 +19,8 @@ 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.ObjClass
import net.sergeych.lyng.obj.ObjException import net.sergeych.lyng.obj.ObjException
import net.sergeych.lyng.obj.ObjUnknownException
import net.sergeych.lyng.obj.ObjVoid
class TryStatement( class TryStatement(
val body: Statement, val body: Statement,
@ -36,7 +38,43 @@ class TryStatement(
) )
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return bytecodeOnly(scope, "try statement") var result: Obj = ObjVoid
try {
result = body.execute(scope)
} catch (e: ReturnException) {
throw e
} catch (e: LoopBreakContinueException) {
throw e
} catch (e: Exception) {
val caughtObj = when (e) {
is ExecutionError -> e.errorObject
else -> ObjUnknownException(scope, e.message ?: e.toString())
}
var isCaught = false
for (cdata in catches) {
var match: Obj? = null
for (exceptionClassName in cdata.classNames) {
val exObj = resolveExceptionClass(scope, exceptionClassName)
if (caughtObj.isInstanceOf(exObj)) {
match = caughtObj
break
}
}
if (match != null) {
val catchContext = scope.createChildScope(pos = cdata.catchVarPos).apply {
skipScopeCreation = true
}
catchContext.addItem(cdata.catchVarName, false, caughtObj)
result = cdata.block.execute(catchContext)
isCaught = true
break
}
}
if (!isCaught) throw e
} finally {
finallyClause?.execute(scope)
}
return result
} }
private fun resolveExceptionClass(scope: Scope, name: String): ObjClass { private fun resolveExceptionClass(scope: Scope, name: String): ObjClass {

View File

@ -18,6 +18,9 @@ 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.ObjClass
import net.sergeych.lyng.obj.ObjNull
import net.sergeych.lyng.obj.ObjRecord
import net.sergeych.lyng.obj.ObjUnset
class VarDeclStatement( class VarDeclStatement(
val name: String, val name: String,
@ -33,6 +36,15 @@ class VarDeclStatement(
override val pos: Pos = startPos override val pos: Pos = startPos
override suspend fun execute(context: Scope): Obj { override suspend fun execute(context: Scope): Obj {
return bytecodeOnly(context, "var declaration") val initValue = initializer?.execute(context)?.byValueCopy() ?: ObjUnset
context.addItem(
name,
isMutable,
initValue,
visibility,
recordType = ObjRecord.Type.Other,
isTransient = isTransient
)
return initValue
} }
} }

View File

@ -17,12 +17,9 @@
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.ObjVoid
sealed class WhenCondition(open val expr: Statement, open val pos: Pos) { sealed class WhenCondition(open val expr: Statement, open val pos: Pos) {
protected fun bytecodeOnly(scope: Scope): Nothing {
return scope.raiseIllegalState("bytecode-only execution is required; when condition needs compiled bytecode")
}
abstract suspend fun matches(scope: Scope, value: Obj): Boolean abstract suspend fun matches(scope: Scope, value: Obj): Boolean
} }
@ -31,7 +28,7 @@ class WhenEqualsCondition(
override val pos: Pos, override val pos: Pos,
) : WhenCondition(expr, pos) { ) : WhenCondition(expr, pos) {
override suspend fun matches(scope: Scope, value: Obj): Boolean { override suspend fun matches(scope: Scope, value: Obj): Boolean {
return bytecodeOnly(scope) return expr.execute(scope).compareTo(scope, value) == 0
} }
} }
@ -41,7 +38,8 @@ class WhenInCondition(
override val pos: Pos, override val pos: Pos,
) : WhenCondition(expr, pos) { ) : WhenCondition(expr, pos) {
override suspend fun matches(scope: Scope, value: Obj): Boolean { override suspend fun matches(scope: Scope, value: Obj): Boolean {
return bytecodeOnly(scope) val result = expr.execute(scope).contains(scope, value)
return if (negated) !result else result
} }
} }
@ -51,7 +49,12 @@ class WhenIsCondition(
override val pos: Pos, override val pos: Pos,
) : WhenCondition(expr, pos) { ) : WhenCondition(expr, pos) {
override suspend fun matches(scope: Scope, value: Obj): Boolean { override suspend fun matches(scope: Scope, value: Obj): Boolean {
return bytecodeOnly(scope) val typeExpr = expr.execute(scope)
val result = when (typeExpr) {
is net.sergeych.lyng.obj.ObjTypeExpr -> net.sergeych.lyng.obj.matchesTypeDecl(scope, value, typeExpr.typeDecl)
else -> value.isInstanceOf(typeExpr)
}
return if (negated) !result else result
} }
} }
@ -64,6 +67,14 @@ class WhenStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return bytecodeOnly(scope, "when statement") val whenValue = value.execute(scope)
for (case in cases) {
for (condition in case.conditions) {
if (condition.matches(scope, whenValue)) {
return case.block.execute(scope)
}
}
}
return elseCase?.execute(scope) ?: ObjVoid
} }
} }

View File

@ -1,533 +0,0 @@
/*
* Kotlin bridge reflection facade: handle-based access for fast get/set/call.
*/
package net.sergeych.lyng.bridge
import net.sergeych.lyng.Arguments
import net.sergeych.lyng.Pos
import net.sergeych.lyng.Scope
import net.sergeych.lyng.ScopeFacade
import net.sergeych.lyng.canAccessMember
import net.sergeych.lyng.extensionCallableName
import net.sergeych.lyng.obj.Obj
import net.sergeych.lyng.obj.ObjClass
import net.sergeych.lyng.obj.ObjIllegalAccessException
import net.sergeych.lyng.obj.ObjInstance
import net.sergeych.lyng.obj.ObjProperty
import net.sergeych.lyng.obj.ObjRecord
import net.sergeych.lyng.obj.ObjString
import net.sergeych.lyng.obj.ObjUnset
import net.sergeych.lyng.obj.ObjVoid
import net.sergeych.lyng.requireScope
import net.sergeych.lyng.ModuleScope
/** Where to resolve names from. */
enum class LookupTarget {
CurrentFrame,
ParentChain,
ModuleFrame
}
/** Explicit receiver view (like this@Base). */
data class ReceiverView(
val type: ObjClass? = null,
val typeName: String? = null
)
/** Lookup rules for bridge resolution. */
data class LookupSpec(
val targets: Set<LookupTarget> = setOf(LookupTarget.CurrentFrame, LookupTarget.ModuleFrame),
val receiverView: ReceiverView? = null
)
/** Base handle type. */
sealed interface BridgeHandle {
val name: String
}
/** Read-only value handle. */
interface ValHandle : BridgeHandle {
suspend fun get(scope: ScopeFacade): Obj
}
/** Read/write value handle. */
interface VarHandle : ValHandle {
suspend fun set(scope: ScopeFacade, value: Obj)
}
/** Callable handle (function/closure/method). */
interface CallableHandle : BridgeHandle {
suspend fun call(scope: ScopeFacade, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Obj
}
/** Member handle resolved against an instance or receiver view. */
interface MemberHandle : BridgeHandle {
val declaringClass: ObjClass?
val receiverView: ReceiverView?
}
/** Member field/property. */
interface MemberValHandle : MemberHandle, ValHandle
/** Member var/property with write access. */
interface MemberVarHandle : MemberHandle, VarHandle
/** Member callable (method or extension). */
interface MemberCallableHandle : MemberHandle, CallableHandle
/** Direct record handle (debug/inspection). */
interface RecordHandle : BridgeHandle {
fun record(): ObjRecord
}
/** Bridge resolver API (entry point for Kotlin bindings). */
interface BridgeResolver {
val pos: Pos
fun selfAs(type: ObjClass): BridgeResolver
fun selfAs(typeName: String): BridgeResolver
fun resolveVal(name: String, lookup: LookupSpec = LookupSpec()): ValHandle
fun resolveVar(name: String, lookup: LookupSpec = LookupSpec()): VarHandle
fun resolveCallable(name: String, lookup: LookupSpec = LookupSpec()): CallableHandle
fun resolveMemberVal(
receiver: Obj,
name: String,
lookup: LookupSpec = LookupSpec()
): MemberValHandle
fun resolveMemberVar(
receiver: Obj,
name: String,
lookup: LookupSpec = LookupSpec()
): MemberVarHandle
fun resolveMemberCallable(
receiver: Obj,
name: String,
lookup: LookupSpec = LookupSpec()
): MemberCallableHandle
/** Extension function treated as a member for reflection. */
fun resolveExtensionCallable(
receiverClass: ObjClass,
name: String,
lookup: LookupSpec = LookupSpec()
): MemberCallableHandle
/** Debug: resolve locals by name (optional, for tooling). */
fun resolveLocalVal(name: String): ValHandle
fun resolveLocalVar(name: String): VarHandle
/** Debug: access raw record handles if needed. */
fun resolveRecord(name: String, lookup: LookupSpec = LookupSpec()): RecordHandle
}
/** Convenience: call by name with implicit caching in resolver implementation. */
interface BridgeCallByName {
suspend fun callByName(
scope: ScopeFacade,
name: String,
args: Arguments = Arguments.EMPTY,
lookup: LookupSpec = LookupSpec()
): Obj
}
/** Optional typed wrappers (sugar). */
interface TypedHandle<T : Obj> : ValHandle {
suspend fun getTyped(scope: ScopeFacade): T
}
/** Factory for bridge resolver. */
fun ScopeFacade.resolver(): BridgeResolver = BridgeResolverImpl(this)
private class BridgeResolverImpl(
private val facade: ScopeFacade,
private val receiverView: ReceiverView? = null
) : BridgeResolver, BridgeCallByName {
private val cachedCallables: MutableMap<String, CallableHandle> = LinkedHashMap()
override val pos: Pos
get() = facade.pos
override fun selfAs(type: ObjClass): BridgeResolver = BridgeResolverImpl(facade, ReceiverView(type = type))
override fun selfAs(typeName: String): BridgeResolver = BridgeResolverImpl(facade, ReceiverView(typeName = typeName))
override fun resolveVal(name: String, lookup: LookupSpec): ValHandle =
LocalValHandle(this, name, lookup)
override fun resolveVar(name: String, lookup: LookupSpec): VarHandle =
LocalVarHandle(this, name, lookup)
override fun resolveCallable(name: String, lookup: LookupSpec): CallableHandle =
LocalCallableHandle(this, name, lookup)
override fun resolveMemberVal(receiver: Obj, name: String, lookup: LookupSpec): MemberValHandle =
MemberValHandleImpl(this, receiver, name, lookup.receiverView ?: receiverView)
override fun resolveMemberVar(receiver: Obj, name: String, lookup: LookupSpec): MemberVarHandle =
MemberVarHandleImpl(this, receiver, name, lookup.receiverView ?: receiverView)
override fun resolveMemberCallable(receiver: Obj, name: String, lookup: LookupSpec): MemberCallableHandle =
MemberCallableHandleImpl(this, receiver, name, lookup.receiverView ?: receiverView)
override fun resolveExtensionCallable(receiverClass: ObjClass, name: String, lookup: LookupSpec): MemberCallableHandle =
ExtensionCallableHandleImpl(this, receiverClass, name, lookup)
override fun resolveLocalVal(name: String): ValHandle =
LocalValHandle(this, name, LookupSpec(targets = setOf(LookupTarget.CurrentFrame)))
override fun resolveLocalVar(name: String): VarHandle =
LocalVarHandle(this, name, LookupSpec(targets = setOf(LookupTarget.CurrentFrame)))
override fun resolveRecord(name: String, lookup: LookupSpec): RecordHandle =
RecordHandleImpl(this, name, lookup)
override suspend fun callByName(scope: ScopeFacade, name: String, args: Arguments, lookup: LookupSpec): Obj {
val handle = cachedCallables.getOrPut(name) { resolveCallable(name, lookup) }
return handle.call(scope, args)
}
fun facade(): ScopeFacade = facade
fun resolveLocalRecord(scope: Scope, name: String, lookup: LookupSpec): ObjRecord {
val caller = scope.currentClassCtx
if (LookupTarget.CurrentFrame in lookup.targets) {
scope.tryGetLocalRecord(scope, name, caller)?.let { return it }
}
if (LookupTarget.ParentChain in lookup.targets) {
scope.chainLookupIgnoreClosure(name, followClosure = false, caller = caller)?.let { return it }
}
if (LookupTarget.ModuleFrame in lookup.targets) {
findModuleScope(scope)?.let { module ->
module.tryGetLocalRecord(module, name, caller)?.let { return it }
}
}
facade.raiseSymbolNotFound(name)
}
fun resolveReceiver(scope: Scope, receiver: Obj, view: ReceiverView?): Obj {
if (view == null) return receiver
if (receiver !== scope.thisObj) return receiver
val target = when {
view.type != null -> scope.thisVariants.firstOrNull { it.isInstanceOf(view.type) }
view.typeName != null -> scope.thisVariants.firstOrNull { it.isInstanceOf(view.typeName) }
else -> null
}
return target ?: facade.raiseSymbolNotFound(view.typeName ?: view.type?.className ?: "<receiver>")
}
fun resolveMemberRecord(scope: Scope, receiver: Obj, name: String): MemberResolution {
if (receiver is ObjClass) {
val rec = receiver.classScope?.objects?.get(name) ?: receiver.members[name]
?: facade.raiseSymbolNotFound("member $name not found on ${receiver.className}")
val decl = rec.declaringClass ?: receiver
if (!canAccessMember(rec.visibility, decl, scope.currentClassCtx, name)) {
facade.raiseError(
ObjIllegalAccessException(
scope,
"can't access ${name}: not visible (declared in ${decl.className}, caller ${scope.currentClassCtx?.className ?: "?"})"
)
)
}
return MemberResolution(rec, decl, receiver, rec.fieldId, rec.methodId)
}
val cls = receiver.objClass
val resolved = cls.resolveInstanceMember(name)
?: facade.raiseSymbolNotFound("member $name not found on ${cls.className}")
val decl = resolved.declaringClass
if (!canAccessMember(resolved.record.visibility, decl, scope.currentClassCtx, name)) {
facade.raiseError(
ObjIllegalAccessException(
scope,
"can't access ${name}: not visible (declared in ${decl.className}, caller ${scope.currentClassCtx?.className ?: "?"})"
)
)
}
val fieldId = if (resolved.record.type == ObjRecord.Type.Field ||
resolved.record.type == ObjRecord.Type.ConstructorField
) {
resolved.record.fieldId ?: cls.instanceFieldIdMap()[name]
} else null
val methodId = if (resolved.record.type == ObjRecord.Type.Fun ||
resolved.record.type == ObjRecord.Type.Property ||
resolved.record.type == ObjRecord.Type.Delegated
) {
resolved.record.methodId ?: cls.instanceMethodIdMap(includeAbstract = true)[name]
} else null
return MemberResolution(resolved.record, decl, receiver.objClass, fieldId, methodId)
}
private fun findModuleScope(scope: Scope): ModuleScope? {
var s: Scope? = scope
var hops = 0
while (s != null && hops++ < 1024) {
if (s is ModuleScope) return s
s = s.parent
}
return null
}
}
private data class LocalResolution(
val record: ObjRecord,
val frameId: Long
)
private data class MemberResolution(
val record: ObjRecord,
val declaringClass: ObjClass,
val receiverClass: ObjClass,
val fieldId: Int?,
val methodId: Int?
)
private abstract class LocalHandleBase(
protected val resolver: BridgeResolverImpl,
override val name: String,
private val lookup: LookupSpec
) : BridgeHandle {
private var cached: LocalResolution? = null
protected fun resolve(scope: Scope): ObjRecord {
val cachedLocal = cached
if (cachedLocal != null && cachedLocal.frameId == scope.frameId) {
return cachedLocal.record
}
val rec = resolver.resolveLocalRecord(scope, name, lookup)
cached = LocalResolution(rec, scope.frameId)
return rec
}
}
private class LocalValHandle(
resolver: BridgeResolverImpl,
name: String,
lookup: LookupSpec
) : LocalHandleBase(resolver, name, lookup), ValHandle {
override suspend fun get(scope: ScopeFacade): Obj {
val real = scope.requireScope()
val rec = resolve(real)
return real.resolve(rec, name)
}
}
private class LocalVarHandle(
resolver: BridgeResolverImpl,
name: String,
lookup: LookupSpec
) : LocalHandleBase(resolver, name, lookup), VarHandle {
override suspend fun get(scope: ScopeFacade): Obj {
val real = scope.requireScope()
val rec = resolve(real)
return real.resolve(rec, name)
}
override suspend fun set(scope: ScopeFacade, value: Obj) {
val real = scope.requireScope()
val rec = resolve(real)
real.assign(rec, name, value)
}
}
private class LocalCallableHandle(
resolver: BridgeResolverImpl,
name: String,
lookup: LookupSpec
) : LocalHandleBase(resolver, name, lookup), CallableHandle {
override suspend fun call(scope: ScopeFacade, args: Arguments, newThisObj: Obj?): Obj {
val real = scope.requireScope()
val rec = resolve(real)
val callee = rec.value
return scope.call(callee, args, newThisObj ?: rec.receiver)
}
}
private abstract class MemberHandleBase(
protected val resolver: BridgeResolverImpl,
receiver: Obj,
override val name: String,
override val receiverView: ReceiverView?
) : MemberHandle {
private val baseReceiver: Obj = receiver
private var cachedResolution: MemberResolution? = null
private var cachedDeclaringClass: ObjClass? = null
protected fun resolve(scope: Scope): Pair<Obj, MemberResolution> {
val resolvedReceiver = resolver.resolveReceiver(scope, baseReceiver, receiverView)
val cached = cachedResolution
if (cached != null && resolvedReceiver.objClass === cached.receiverClass) {
cachedDeclaringClass = cached.declaringClass
return Pair(resolvedReceiver, cached)
}
val res = resolver.resolveMemberRecord(scope, resolvedReceiver, name)
cachedResolution = res
cachedDeclaringClass = res.declaringClass
return Pair(resolvedReceiver, res)
}
protected fun declaringClass(): ObjClass? = cachedDeclaringClass
}
private class MemberValHandleImpl(
resolver: BridgeResolverImpl,
receiver: Obj,
name: String,
receiverView: ReceiverView?
) : MemberHandleBase(resolver, receiver, name, receiverView), MemberValHandle {
override val declaringClass: ObjClass?
get() = declaringClass()
override suspend fun get(scope: ScopeFacade): Obj {
val real = scope.requireScope()
val (receiver, res) = resolve(real)
val rec = resolveMemberRecordFast(receiver, res)
return receiver.resolveRecord(real, rec, name, res.declaringClass).value
}
}
private class MemberVarHandleImpl(
resolver: BridgeResolverImpl,
receiver: Obj,
name: String,
receiverView: ReceiverView?
) : MemberHandleBase(resolver, receiver, name, receiverView), MemberVarHandle {
override val declaringClass: ObjClass?
get() = declaringClass()
override suspend fun get(scope: ScopeFacade): Obj {
val real = scope.requireScope()
val (receiver, res) = resolve(real)
val rec = resolveMemberRecordFast(receiver, res)
return receiver.resolveRecord(real, rec, name, res.declaringClass).value
}
override suspend fun set(scope: ScopeFacade, value: Obj) {
val real = scope.requireScope()
val (receiver, res) = resolve(real)
val rec = resolveMemberRecordFast(receiver, res)
assignMemberRecord(real, receiver, res.declaringClass, rec, name, value)
}
}
private class MemberCallableHandleImpl(
resolver: BridgeResolverImpl,
receiver: Obj,
name: String,
receiverView: ReceiverView?
) : MemberHandleBase(resolver, receiver, name, receiverView), MemberCallableHandle {
override val declaringClass: ObjClass?
get() = declaringClass()
override suspend fun call(scope: ScopeFacade, args: Arguments, newThisObj: Obj?): Obj {
val real = scope.requireScope()
val (receiver, res) = resolve(real)
val rec = resolveMemberRecordFast(receiver, res)
if (rec.type != ObjRecord.Type.Fun) {
scope.raiseError("member $name is not callable")
}
return rec.value.invoke(real, receiver, args, res.declaringClass)
}
}
private class ExtensionCallableHandleImpl(
private val resolver: BridgeResolverImpl,
private val receiverClass: ObjClass,
override val name: String,
private val lookup: LookupSpec
) : MemberCallableHandle {
override val receiverView: ReceiverView?
get() = null
override val declaringClass: ObjClass?
get() = receiverClass
override suspend fun call(scope: ScopeFacade, args: Arguments, newThisObj: Obj?): Obj {
val real = scope.requireScope()
val wrapperName = extensionCallableName(receiverClass.className, name)
val rec = resolver.resolveLocalRecord(real, wrapperName, lookup)
val receiver = newThisObj ?: real.thisObj
val callArgs = Arguments(listOf(receiver) + args.list)
return scope.call(rec.value, callArgs)
}
}
private class RecordHandleImpl(
private val resolver: BridgeResolverImpl,
override val name: String,
private val lookup: LookupSpec
) : RecordHandle {
override fun record(): ObjRecord {
val scope = resolver.facade().requireScope()
return resolver.resolveLocalRecord(scope, name, lookup)
}
}
private class TypedHandleImpl<T : Obj>(
private val inner: ValHandle,
private val clazzName: String
) : TypedHandle<T> {
override val name: String
get() = inner.name
override suspend fun get(scope: ScopeFacade): Obj = inner.get(scope)
@Suppress("UNCHECKED_CAST")
override suspend fun getTyped(scope: ScopeFacade): T {
val value = inner.get(scope)
return (value as? T)
?: scope.raiseClassCastError("Expected $clazzName, got ${value.objClass.className}")
}
}
private fun resolveMemberRecordFast(receiver: Obj, res: MemberResolution): ObjRecord {
val inst = receiver as? ObjInstance
if (inst != null) {
res.fieldId?.let { inst.fieldRecordForId(it)?.let { return it } }
res.methodId?.let { inst.methodRecordForId(it)?.let { return it } }
}
return res.record
}
private suspend fun assignMemberRecord(
scope: Scope,
receiver: Obj,
declaringClass: ObjClass,
rec: ObjRecord,
name: String,
value: Obj
) {
val caller = scope.currentClassCtx
if (!canAccessMember(rec.effectiveWriteVisibility, declaringClass, caller, name)) {
scope.raiseError(
ObjIllegalAccessException(
scope,
"can't assign ${name}: not visible (declared in ${declaringClass.className}, caller ${caller?.className ?: "?"})"
)
)
}
when {
rec.type == ObjRecord.Type.Delegated -> {
val del = rec.delegate ?: scope.raiseError("Internal error: delegated property $name has no delegate")
val th = if (receiver === ObjVoid) net.sergeych.lyng.obj.ObjNull else receiver
del.invokeInstanceMethod(scope, "setValue", Arguments(th, ObjString(name), value))
}
rec.value is ObjProperty || rec.type == ObjRecord.Type.Property -> {
val prop = rec.value as? ObjProperty
?: scope.raiseError("Expected ObjProperty for property member $name")
prop.callSetter(scope, receiver, value, declaringClass)
}
rec.isMutable -> {
val slotRef = rec.value
if (slotRef is net.sergeych.lyng.FrameSlotRef) {
if (!rec.isMutable && slotRef.read() !== ObjUnset) scope.raiseError("can't reassign val $name")
slotRef.write(value)
} else {
rec.value = value
}
}
else -> scope.raiseError("can't assign to read-only field: $name")
}
}

View File

@ -1,286 +0,0 @@
/*
* Kotlin bridge bindings for Lyng classes (Lyng-first workflow).
*/
package net.sergeych.lyng.bridge
import net.sergeych.lyng.Arguments
import net.sergeych.lyng.Pos
import net.sergeych.lyng.ScopeFacade
import net.sergeych.lyng.Script
import net.sergeych.lyng.obj.Obj
import net.sergeych.lyng.obj.ObjClass
import net.sergeych.lyng.obj.ObjExternCallable
import net.sergeych.lyng.obj.ObjInstance
import net.sergeych.lyng.obj.ObjProperty
import net.sergeych.lyng.obj.ObjRecord
import net.sergeych.lyng.obj.ObjVoid
import net.sergeych.lyng.pacman.ImportManager
import net.sergeych.lyng.ModuleScope
import net.sergeych.lyng.ScriptError
import net.sergeych.lyng.requiredArg
import net.sergeych.lyng.InstanceFieldInitStatement
import net.sergeych.lyng.Statement
import net.sergeych.lyng.bytecode.BytecodeStatement
interface BridgeInstanceContext {
val instance: Obj
var data: Any?
}
interface ClassBridgeBinder {
var classData: Any?
fun init(block: suspend BridgeInstanceContext.(ScopeFacade) -> Unit)
fun initWithInstance(block: suspend (ScopeFacade, Obj) -> Unit)
fun addFun(name: String, impl: suspend (ScopeFacade, Obj, Arguments) -> Obj)
fun addVal(name: String, impl: suspend (ScopeFacade, Obj) -> Obj)
fun addVar(
name: String,
get: suspend (ScopeFacade, Obj) -> Obj,
set: suspend (ScopeFacade, Obj, Obj) -> Unit
)
}
object LyngClassBridge {
suspend fun bind(
className: String,
module: String? = null,
importManager: ImportManager = Script.defaultImportManager,
block: ClassBridgeBinder.() -> Unit
): ObjClass {
val cls = resolveClass(className, module, null, importManager)
return bind(cls, block)
}
suspend fun bind(
moduleScope: ModuleScope,
className: String,
block: ClassBridgeBinder.() -> Unit
): ObjClass {
val cls = resolveClass(className, null, moduleScope, Script.defaultImportManager)
return bind(cls, block)
}
fun bind(clazz: ObjClass, block: ClassBridgeBinder.() -> Unit): ObjClass {
val binder = ClassBridgeBinderImpl(clazz)
binder.block()
binder.commit()
return clazz
}
}
var ObjInstance.data: Any?
get() = kotlinInstanceData
set(value) { kotlinInstanceData = value }
var ObjClass.classData: Any?
get() = kotlinClassData
set(value) { kotlinClassData = value }
private enum class MemberKind { Instance, Static }
private data class MemberTarget(
val name: String,
val record: ObjRecord,
val kind: MemberKind,
val mirrorClassScope: Boolean = false
)
private class BridgeInstanceContextImpl(
override val instance: Obj
) : BridgeInstanceContext {
private fun instanceObj(): ObjInstance =
instance as? ObjInstance ?: error("Bridge instance is not an ObjInstance")
override var data: Any?
get() = instanceObj().kotlinInstanceData
set(value) { instanceObj().kotlinInstanceData = value }
}
private class ClassBridgeBinderImpl(
private val cls: ObjClass
) : ClassBridgeBinder {
private val initHooks = mutableListOf<suspend (ScopeFacade, ObjInstance) -> Unit>()
private var checkedTemplate = false
override var classData: Any?
get() = cls.kotlinClassData
set(value) { cls.kotlinClassData = value }
override fun init(block: suspend BridgeInstanceContext.(ScopeFacade) -> Unit) {
initHooks.add { scope, inst ->
val ctx = BridgeInstanceContextImpl(inst)
ctx.block(scope)
}
}
override fun initWithInstance(block: suspend (ScopeFacade, Obj) -> Unit) {
initHooks.add { scope, inst ->
block(scope, inst)
}
}
override fun addFun(name: String, impl: suspend (ScopeFacade, Obj, Arguments) -> Obj) {
ensureTemplateNotBuilt()
val target = findMember(name)
val callable = ObjExternCallable.fromBridge {
impl(this, thisObj, args)
}
val methodId = cls.ensureMethodIdForBridge(name, target.record)
val newRecord = target.record.copy(
value = callable,
type = ObjRecord.Type.Fun,
methodId = methodId
)
replaceMember(target, newRecord)
}
override fun addVal(name: String, impl: suspend (ScopeFacade, Obj) -> Obj) {
ensureTemplateNotBuilt()
val target = findMember(name)
if (target.record.isMutable) {
throw ScriptError(Pos.builtIn, "extern val $name is mutable in class ${cls.className}")
}
val getter = ObjExternCallable.fromBridge {
impl(this, thisObj)
}
val prop = ObjProperty(name, getter, null)
val isFieldLike = target.record.type == ObjRecord.Type.Field ||
target.record.type == ObjRecord.Type.ConstructorField
val newRecord = if (isFieldLike) {
removeFieldInitializersFor(name)
target.record.copy(
value = prop,
type = target.record.type,
fieldId = target.record.fieldId,
methodId = target.record.methodId
)
} else {
val methodId = cls.ensureMethodIdForBridge(name, target.record)
target.record.copy(
value = prop,
type = ObjRecord.Type.Property,
methodId = methodId,
fieldId = null
)
}
replaceMember(target, newRecord)
}
override fun addVar(
name: String,
get: suspend (ScopeFacade, Obj) -> Obj,
set: suspend (ScopeFacade, Obj, Obj) -> Unit
) {
ensureTemplateNotBuilt()
val target = findMember(name)
if (!target.record.isMutable) {
throw ScriptError(Pos.builtIn, "extern var $name is readonly in class ${cls.className}")
}
val getter = ObjExternCallable.fromBridge {
get(this, thisObj)
}
val setter = ObjExternCallable.fromBridge {
val value = requiredArg<Obj>(0)
set(this, thisObj, value)
ObjVoid
}
val prop = ObjProperty(name, getter, setter)
val isFieldLike = target.record.type == ObjRecord.Type.Field ||
target.record.type == ObjRecord.Type.ConstructorField
val newRecord = if (isFieldLike) {
removeFieldInitializersFor(name)
target.record.copy(
value = prop,
type = target.record.type,
fieldId = target.record.fieldId,
methodId = target.record.methodId
)
} else {
val methodId = cls.ensureMethodIdForBridge(name, target.record)
target.record.copy(
value = prop,
type = ObjRecord.Type.Property,
methodId = methodId,
fieldId = null
)
}
replaceMember(target, newRecord)
}
fun commit() {
if (initHooks.isNotEmpty()) {
val target = cls.bridgeInitHooks ?: mutableListOf<suspend (ScopeFacade, ObjInstance) -> Unit>().also {
cls.bridgeInitHooks = it
}
target.addAll(initHooks)
}
}
private fun ensureTemplateNotBuilt() {
if (!checkedTemplate) {
if (cls.instanceTemplateBuilt) {
throw ScriptError(
Pos.builtIn,
"bridge binding for ${cls.className} must happen before first instance is created"
)
}
checkedTemplate = true
}
}
private fun replaceMember(target: MemberTarget, newRecord: ObjRecord) {
when (target.kind) {
MemberKind.Instance -> {
cls.replaceMemberForBridge(target.name, newRecord)
if (target.mirrorClassScope && cls.classScope?.objects?.containsKey(target.name) == true) {
cls.replaceClassScopeMemberForBridge(target.name, newRecord)
}
}
MemberKind.Static -> cls.replaceClassScopeMemberForBridge(target.name, newRecord)
}
}
private fun findMember(name: String): MemberTarget {
val inst = cls.members[name]
val stat = cls.classScope?.objects?.get(name)
if (inst != null) {
return MemberTarget(name, inst, MemberKind.Instance, mirrorClassScope = stat != null)
}
if (stat != null) return MemberTarget(name, stat, MemberKind.Static)
throw ScriptError(Pos.builtIn, "extern member $name not found in class ${cls.className}")
}
private fun removeFieldInitializersFor(name: String) {
if (cls.instanceInitializers.isEmpty()) return
val storageName = cls.mangledName(name)
cls.instanceInitializers.removeAll { init ->
val stmt = init as? Statement ?: return@removeAll false
val original = (stmt as? BytecodeStatement)?.original ?: stmt
original is InstanceFieldInitStatement && original.storageName == storageName
}
}
}
private suspend fun resolveClass(
className: String,
module: String?,
moduleScope: ModuleScope?,
importManager: ImportManager
): ObjClass {
val scope = moduleScope ?: run {
if (module == null) {
throw ScriptError(Pos.builtIn, "module is required to resolve $className")
}
importManager.createModuleScope(Pos.builtIn, module)
}
val rec = scope.get(className)
val direct = rec?.value as? ObjClass
if (direct != null) return direct
if (className.contains('.')) {
val resolved = scope.resolveQualifiedIdentifier(className)
val cls = resolved as? ObjClass
if (cls != null) return cls
}
throw ScriptError(Pos.builtIn, "class $className not found in module ${scope.packageName}")
}

View File

@ -75,95 +75,6 @@ sealed class BytecodeConst {
val visibility: Visibility, val visibility: Visibility,
val isTransient: Boolean, val isTransient: Boolean,
) : BytecodeConst() ) : BytecodeConst()
data class ClassFieldDecl(
val name: String,
val isMutable: Boolean,
val visibility: Visibility,
val writeVisibility: Visibility?,
val isTransient: Boolean,
) : BytecodeConst()
data class ClassDelegatedDecl(
val name: String,
val isMutable: Boolean,
val visibility: Visibility,
val writeVisibility: Visibility?,
val isTransient: Boolean,
) : BytecodeConst()
data class ClassInstanceInitDecl(
val initStatement: Obj,
) : BytecodeConst()
data class ClassInstanceFieldDecl(
val name: String,
val isMutable: Boolean,
val visibility: Visibility,
val writeVisibility: Visibility?,
val isTransient: Boolean,
val isAbstract: Boolean,
val isClosed: Boolean,
val isOverride: Boolean,
val fieldId: Int?,
val initStatement: Obj?,
val pos: Pos,
) : BytecodeConst()
data class ClassInstancePropertyDecl(
val name: String,
val isMutable: Boolean,
val visibility: Visibility,
val writeVisibility: Visibility?,
val isTransient: Boolean,
val isAbstract: Boolean,
val isClosed: Boolean,
val isOverride: Boolean,
val prop: ObjProperty,
val methodId: Int?,
val initStatement: Obj?,
val pos: Pos,
) : BytecodeConst()
data class ClassInstanceDelegatedDecl(
val name: String,
val isMutable: Boolean,
val visibility: Visibility,
val writeVisibility: Visibility?,
val isTransient: Boolean,
val isAbstract: Boolean,
val isClosed: Boolean,
val isOverride: Boolean,
val methodId: Int?,
val initStatement: Obj?,
val pos: Pos,
) : BytecodeConst()
data class InstanceFieldDecl(
val name: String,
val isMutable: Boolean,
val visibility: Visibility,
val writeVisibility: Visibility?,
val isTransient: Boolean,
val isAbstract: Boolean,
val isClosed: Boolean,
val isOverride: Boolean,
) : BytecodeConst()
data class InstancePropertyDecl(
val name: String,
val isMutable: Boolean,
val visibility: Visibility,
val writeVisibility: Visibility?,
val isTransient: Boolean,
val isAbstract: Boolean,
val isClosed: Boolean,
val isOverride: Boolean,
) : BytecodeConst()
data class InstanceDelegatedDecl(
val storageName: String,
val memberName: String,
val isMutable: Boolean,
val visibility: Visibility,
val writeVisibility: Visibility?,
val isTransient: Boolean,
val isAbstract: Boolean,
val isClosed: Boolean,
val isOverride: Boolean,
val accessTypeLabel: String,
) : BytecodeConst()
data class DestructureDecl( data class DestructureDecl(
val pattern: ListLiteralRef, val pattern: ListLiteralRef,
val names: List<String>, val names: List<String>,

View File

@ -53,13 +53,7 @@ class BytecodeFrame(
override fun setObj(slot: Int, value: Obj) { override fun setObj(slot: Int, value: Obj) {
when (val current = objSlots[slot]) { when (val current = objSlots[slot]) {
is net.sergeych.lyng.FrameSlotRef -> { is net.sergeych.lyng.FrameSlotRef -> current.write(value)
if (current.refersTo(this, slot)) {
objSlots[slot] = value
} else {
current.write(value)
}
}
is net.sergeych.lyng.RecordSlotRef -> current.write(value) is net.sergeych.lyng.RecordSlotRef -> current.write(value)
else -> objSlots[slot] = value else -> objSlots[slot] = value
} }

View File

@ -43,13 +43,9 @@ class BytecodeStatement private constructor(
returnLabels: Set<String> = emptySet(), returnLabels: Set<String> = emptySet(),
rangeLocalNames: Set<String> = emptySet(), rangeLocalNames: Set<String> = emptySet(),
allowedScopeNames: Set<String>? = null, allowedScopeNames: Set<String>? = null,
scopeSlotNameSet: Set<String>? = null,
moduleScopeId: Int? = null, moduleScopeId: Int? = null,
forcedLocalSlots: Map<String, Int> = emptyMap(), forcedLocalSlots: Map<String, Int> = emptyMap(),
forcedLocalScopeId: Int? = null, forcedLocalScopeId: Int? = null,
forcedLocalSlotInfo: Map<String, ForcedLocalSlotInfo> = emptyMap(),
globalSlotInfo: Map<String, ForcedLocalSlotInfo> = emptyMap(),
globalSlotScopeId: 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(),
@ -57,9 +53,7 @@ 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(),
externCallableNames: Set<String> = emptySet(),
lambdaCaptureEntriesByRef: Map<ValueFnRef, List<LambdaCaptureEntry>> = emptyMap(), lambdaCaptureEntriesByRef: Map<ValueFnRef, List<LambdaCaptureEntry>> = emptyMap(),
slotTypeDeclByScopeId: Map<Int, Map<Int, TypeDecl>> = emptyMap(),
): Statement { ): Statement {
if (statement is BytecodeStatement) return statement if (statement is BytecodeStatement) return statement
val hasUnsupported = containsUnsupportedStatement(statement) val hasUnsupported = containsUnsupportedStatement(statement)
@ -76,22 +70,16 @@ class BytecodeStatement private constructor(
returnLabels = returnLabels, returnLabels = returnLabels,
rangeLocalNames = rangeLocalNames, rangeLocalNames = rangeLocalNames,
allowedScopeNames = allowedScopeNames, allowedScopeNames = allowedScopeNames,
scopeSlotNameSet = scopeSlotNameSet,
moduleScopeId = moduleScopeId, moduleScopeId = moduleScopeId,
forcedLocalSlots = forcedLocalSlots, forcedLocalSlots = forcedLocalSlots,
forcedLocalScopeId = forcedLocalScopeId, forcedLocalScopeId = forcedLocalScopeId,
forcedLocalSlotInfo = forcedLocalSlotInfo,
globalSlotInfo = globalSlotInfo,
globalSlotScopeId = globalSlotScopeId,
slotTypeByScopeId = slotTypeByScopeId, slotTypeByScopeId = slotTypeByScopeId,
slotTypeDeclByScopeId = slotTypeDeclByScopeId,
knownNameObjClass = knownNameObjClass, knownNameObjClass = knownNameObjClass,
knownObjectNames = knownObjectNames, knownObjectNames = knownObjectNames,
classFieldTypesByName = classFieldTypesByName, classFieldTypesByName = classFieldTypesByName,
enumEntriesByName = enumEntriesByName, enumEntriesByName = enumEntriesByName,
callableReturnTypeByScopeId = callableReturnTypeByScopeId, callableReturnTypeByScopeId = callableReturnTypeByScopeId,
callableReturnTypeByName = callableReturnTypeByName, callableReturnTypeByName = callableReturnTypeByName,
externCallableNames = externCallableNames,
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
) )
val compiled = compiler.compileStatement(nameHint, statement) val compiled = compiler.compileStatement(nameHint, statement)
@ -156,21 +144,6 @@ class BytecodeStatement private constructor(
is net.sergeych.lyng.ClassDeclStatement -> false is net.sergeych.lyng.ClassDeclStatement -> false
is net.sergeych.lyng.FunctionDeclStatement -> false is net.sergeych.lyng.FunctionDeclStatement -> false
is net.sergeych.lyng.EnumDeclStatement -> false is net.sergeych.lyng.EnumDeclStatement -> false
is net.sergeych.lyng.ClassStaticFieldInitStatement ->
target.initializer?.let { containsUnsupportedStatement(it) } ?: false
is net.sergeych.lyng.ClassInstanceInitDeclStatement ->
containsUnsupportedStatement(target.initStatement)
is net.sergeych.lyng.ClassInstanceFieldDeclStatement ->
target.initStatement?.let { containsUnsupportedStatement(it) } ?: false
is net.sergeych.lyng.ClassInstancePropertyDeclStatement ->
target.initStatement?.let { containsUnsupportedStatement(it) } ?: false
is net.sergeych.lyng.ClassInstanceDelegatedDeclStatement ->
target.initStatement?.let { containsUnsupportedStatement(it) } ?: false
is net.sergeych.lyng.InstanceFieldInitStatement ->
target.initializer?.let { containsUnsupportedStatement(it) } ?: false
is net.sergeych.lyng.InstancePropertyInitStatement -> false
is net.sergeych.lyng.InstanceDelegatedInitStatement ->
containsUnsupportedStatement(target.initializer)
is net.sergeych.lyng.TryStatement -> { is net.sergeych.lyng.TryStatement -> {
containsUnsupportedStatement(target.body) || containsUnsupportedStatement(target.body) ||
target.catches.any { containsUnsupportedStatement(it.block) } || target.catches.any { containsUnsupportedStatement(it.block) } ||
@ -293,115 +266,6 @@ class BytecodeStatement private constructor(
stmt.pos stmt.pos
) )
} }
is net.sergeych.lyng.ClassStaticFieldInitStatement -> {
net.sergeych.lyng.ClassStaticFieldInitStatement(
stmt.name,
stmt.isMutable,
stmt.visibility,
stmt.writeVisibility,
stmt.initializer?.let { unwrapDeep(it) },
stmt.isDelegated,
stmt.isTransient,
stmt.pos
)
}
is net.sergeych.lyng.ClassInstanceInitDeclStatement -> {
net.sergeych.lyng.ClassInstanceInitDeclStatement(
unwrapDeep(stmt.initStatement),
stmt.pos
)
}
is net.sergeych.lyng.ClassInstanceFieldDeclStatement -> {
net.sergeych.lyng.ClassInstanceFieldDeclStatement(
stmt.name,
stmt.isMutable,
stmt.visibility,
stmt.writeVisibility,
stmt.isAbstract,
stmt.isClosed,
stmt.isOverride,
stmt.isTransient,
stmt.fieldId,
stmt.initStatement?.let { unwrapDeep(it) },
stmt.pos
)
}
is net.sergeych.lyng.ClassInstancePropertyDeclStatement -> {
net.sergeych.lyng.ClassInstancePropertyDeclStatement(
stmt.name,
stmt.isMutable,
stmt.visibility,
stmt.writeVisibility,
stmt.isAbstract,
stmt.isClosed,
stmt.isOverride,
stmt.isTransient,
stmt.prop,
stmt.methodId,
stmt.initStatement?.let { unwrapDeep(it) },
stmt.pos
)
}
is net.sergeych.lyng.ClassInstanceDelegatedDeclStatement -> {
net.sergeych.lyng.ClassInstanceDelegatedDeclStatement(
stmt.name,
stmt.isMutable,
stmt.visibility,
stmt.writeVisibility,
stmt.isAbstract,
stmt.isClosed,
stmt.isOverride,
stmt.isTransient,
stmt.methodId,
stmt.initStatement?.let { unwrapDeep(it) },
stmt.pos
)
}
is net.sergeych.lyng.InstanceFieldInitStatement -> {
net.sergeych.lyng.InstanceFieldInitStatement(
stmt.storageName,
stmt.isMutable,
stmt.visibility,
stmt.writeVisibility,
stmt.isAbstract,
stmt.isClosed,
stmt.isOverride,
stmt.isTransient,
stmt.isLateInitVal,
stmt.initializer?.let { unwrapDeep(it) },
stmt.pos
)
}
is net.sergeych.lyng.InstancePropertyInitStatement -> {
net.sergeych.lyng.InstancePropertyInitStatement(
stmt.storageName,
stmt.isMutable,
stmt.visibility,
stmt.writeVisibility,
stmt.isAbstract,
stmt.isClosed,
stmt.isOverride,
stmt.isTransient,
stmt.prop,
stmt.pos
)
}
is net.sergeych.lyng.InstanceDelegatedInitStatement -> {
net.sergeych.lyng.InstanceDelegatedInitStatement(
stmt.storageName,
stmt.memberName,
stmt.isMutable,
stmt.visibility,
stmt.writeVisibility,
stmt.isAbstract,
stmt.isClosed,
stmt.isOverride,
stmt.isTransient,
stmt.accessTypeLabel,
unwrapDeep(stmt.initializer),
stmt.pos
)
}
else -> stmt else -> stmt
} }
} }

View File

@ -102,7 +102,7 @@ class CmdBuilder {
} }
operands[i] = v operands[i] = v
} }
cmds.add(createCmd(ins.op, operands, scopeSlotCount, localSlotCaptures)) cmds.add(createCmd(ins.op, operands, scopeSlotCount))
} }
return CmdFunction( return CmdFunction(
name = name, name = name,
@ -128,7 +128,6 @@ class CmdBuilder {
Opcode.NOP, Opcode.RET_VOID, Opcode.POP_SCOPE, Opcode.POP_SLOT_PLAN, Opcode.POP_TRY, Opcode.NOP, Opcode.RET_VOID, Opcode.POP_SCOPE, Opcode.POP_SLOT_PLAN, Opcode.POP_TRY,
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.UNBOX_INT_OBJ, Opcode.UNBOX_REAL_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.GET_OBJ_CLASS,
Opcode.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT, Opcode.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT,
@ -162,10 +161,7 @@ class CmdBuilder {
Opcode.PUSH_TRY -> Opcode.PUSH_TRY ->
listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP) listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP)
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE, Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE,
Opcode.DECL_ENUM, Opcode.DECL_FUNCTION, Opcode.DECL_CLASS, Opcode.DECL_CLASS_FIELD, Opcode.DECL_ENUM, Opcode.DECL_FUNCTION, Opcode.DECL_CLASS,
Opcode.DECL_CLASS_DELEGATED, Opcode.DECL_CLASS_INSTANCE_INIT, Opcode.DECL_CLASS_INSTANCE_FIELD,
Opcode.DECL_CLASS_INSTANCE_PROPERTY, Opcode.DECL_CLASS_INSTANCE_DELEGATED, Opcode.DECL_INSTANCE_FIELD,
Opcode.DECL_INSTANCE_PROPERTY, Opcode.DECL_INSTANCE_DELEGATED,
Opcode.ASSIGN_DESTRUCTURE -> Opcode.ASSIGN_DESTRUCTURE ->
listOf(OperandKind.CONST, OperandKind.SLOT) listOf(OperandKind.CONST, OperandKind.SLOT)
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT, Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
@ -180,14 +176,7 @@ class CmdBuilder {
Opcode.CMP_LTE_INT_REAL, Opcode.CMP_LTE_REAL_INT, Opcode.CMP_GT_INT_REAL, Opcode.CMP_GT_REAL_INT, Opcode.CMP_LTE_INT_REAL, Opcode.CMP_LTE_REAL_INT, Opcode.CMP_GT_INT_REAL, Opcode.CMP_GT_REAL_INT,
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT, Opcode.CMP_NEQ_INT_REAL, Opcode.CMP_NEQ_REAL_INT, Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT, Opcode.CMP_NEQ_INT_REAL, Opcode.CMP_NEQ_REAL_INT,
Opcode.CMP_EQ_OBJ, Opcode.CMP_NEQ_OBJ, Opcode.CMP_REF_EQ_OBJ, Opcode.CMP_REF_NEQ_OBJ, Opcode.CMP_EQ_OBJ, Opcode.CMP_NEQ_OBJ, Opcode.CMP_REF_EQ_OBJ, Opcode.CMP_REF_NEQ_OBJ,
Opcode.CMP_EQ_STR, Opcode.CMP_NEQ_STR, Opcode.CMP_LT_STR, Opcode.CMP_LTE_STR,
Opcode.CMP_GT_STR, Opcode.CMP_GTE_STR,
Opcode.CMP_EQ_INT_OBJ, Opcode.CMP_NEQ_INT_OBJ, Opcode.CMP_LT_INT_OBJ, Opcode.CMP_LTE_INT_OBJ,
Opcode.CMP_GT_INT_OBJ, Opcode.CMP_GTE_INT_OBJ, Opcode.CMP_EQ_REAL_OBJ, Opcode.CMP_NEQ_REAL_OBJ,
Opcode.CMP_LT_REAL_OBJ, Opcode.CMP_LTE_REAL_OBJ, Opcode.CMP_GT_REAL_OBJ, Opcode.CMP_GTE_REAL_OBJ,
Opcode.CMP_LT_OBJ, Opcode.CMP_LTE_OBJ, Opcode.CMP_GT_OBJ, Opcode.CMP_GTE_OBJ, Opcode.CMP_LT_OBJ, Opcode.CMP_LTE_OBJ, Opcode.CMP_GT_OBJ, Opcode.CMP_GTE_OBJ,
Opcode.ADD_INT_OBJ, Opcode.SUB_INT_OBJ, Opcode.MUL_INT_OBJ, Opcode.DIV_INT_OBJ, Opcode.MOD_INT_OBJ,
Opcode.ADD_REAL_OBJ, Opcode.SUB_REAL_OBJ, Opcode.MUL_REAL_OBJ, Opcode.DIV_REAL_OBJ, Opcode.MOD_REAL_OBJ,
Opcode.ADD_OBJ, Opcode.SUB_OBJ, Opcode.MUL_OBJ, Opcode.DIV_OBJ, Opcode.MOD_OBJ, Opcode.CONTAINS_OBJ, Opcode.ADD_OBJ, Opcode.SUB_OBJ, Opcode.MUL_OBJ, Opcode.DIV_OBJ, Opcode.MOD_OBJ, Opcode.CONTAINS_OBJ,
Opcode.AND_BOOL, Opcode.OR_BOOL -> Opcode.AND_BOOL, Opcode.OR_BOOL ->
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT) listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
@ -201,15 +190,11 @@ class CmdBuilder {
listOf(OperandKind.IP) listOf(OperandKind.IP)
Opcode.JMP_IF_TRUE, Opcode.JMP_IF_FALSE -> Opcode.JMP_IF_TRUE, Opcode.JMP_IF_FALSE ->
listOf(OperandKind.SLOT, OperandKind.IP) listOf(OperandKind.SLOT, OperandKind.IP)
Opcode.JMP_IF_EQ_INT, Opcode.JMP_IF_NEQ_INT,
Opcode.JMP_IF_LT_INT, Opcode.JMP_IF_LTE_INT,
Opcode.JMP_IF_GT_INT, Opcode.JMP_IF_GTE_INT ->
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.IP)
Opcode.CALL_DIRECT -> Opcode.CALL_DIRECT ->
listOf(OperandKind.ID, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT) listOf(OperandKind.ID, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT)
Opcode.CALL_MEMBER_SLOT -> Opcode.CALL_MEMBER_SLOT ->
listOf(OperandKind.SLOT, OperandKind.ID, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT) listOf(OperandKind.SLOT, OperandKind.ID, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT)
Opcode.CALL_SLOT, Opcode.CALL_BRIDGE_SLOT -> Opcode.CALL_SLOT ->
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT) listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT)
Opcode.CALL_DYNAMIC_MEMBER -> Opcode.CALL_DYNAMIC_MEMBER ->
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT) listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT)
@ -249,37 +234,19 @@ class CmdBuilder {
ID, ID,
} }
private fun createCmd( private fun createCmd(op: Opcode, operands: IntArray, scopeSlotCount: Int): Cmd {
op: Opcode,
operands: IntArray,
scopeSlotCount: Int,
localSlotCaptures: BooleanArray
): Cmd {
fun isFastLocal(slot: Int): Boolean {
if (slot < scopeSlotCount) return false
val localIndex = slot - scopeSlotCount
return localSlotCaptures.getOrNull(localIndex) != true
}
return when (op) { return when (op) {
Opcode.NOP -> CmdNop() Opcode.NOP -> CmdNop()
Opcode.MOVE_OBJ -> CmdMoveObj(operands[0], operands[1]) Opcode.MOVE_OBJ -> CmdMoveObj(operands[0], operands[1])
Opcode.MOVE_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1])) { Opcode.MOVE_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount) {
CmdMoveIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount) CmdMoveIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount)
} else { } else {
CmdMoveInt(operands[0], operands[1]) CmdMoveInt(operands[0], operands[1])
} }
Opcode.MOVE_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1])) { Opcode.MOVE_REAL -> CmdMoveReal(operands[0], operands[1])
CmdMoveRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount) Opcode.MOVE_BOOL -> CmdMoveBool(operands[0], operands[1])
} else {
CmdMoveReal(operands[0], operands[1])
}
Opcode.MOVE_BOOL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1])) {
CmdMoveBoolLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount)
} else {
CmdMoveBool(operands[0], operands[1])
}
Opcode.CONST_OBJ -> CmdConstObj(operands[0], operands[1]) Opcode.CONST_OBJ -> CmdConstObj(operands[0], operands[1])
Opcode.CONST_INT -> if (isFastLocal(operands[1])) { Opcode.CONST_INT -> if (operands[1] >= scopeSlotCount) {
CmdConstIntLocal(operands[0], operands[1] - scopeSlotCount) CmdConstIntLocal(operands[0], operands[1] - scopeSlotCount)
} else { } else {
CmdConstInt(operands[0], operands[1]) CmdConstInt(operands[0], operands[1])
@ -289,16 +256,6 @@ class CmdBuilder {
Opcode.CONST_NULL -> CmdConstNull(operands[0]) Opcode.CONST_NULL -> CmdConstNull(operands[0])
Opcode.MAKE_LAMBDA_FN -> CmdMakeLambda(operands[0], operands[1]) Opcode.MAKE_LAMBDA_FN -> CmdMakeLambda(operands[0], operands[1])
Opcode.BOX_OBJ -> CmdBoxObj(operands[0], operands[1]) Opcode.BOX_OBJ -> CmdBoxObj(operands[0], operands[1])
Opcode.UNBOX_INT_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1])) {
CmdUnboxIntObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount)
} else {
CmdUnboxIntObj(operands[0], operands[1])
}
Opcode.UNBOX_REAL_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1])) {
CmdUnboxRealObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount)
} else {
CmdUnboxRealObj(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.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])
@ -323,419 +280,119 @@ class CmdBuilder {
Opcode.STORE_REAL_ADDR -> CmdStoreRealAddr(operands[0], operands[1]) Opcode.STORE_REAL_ADDR -> CmdStoreRealAddr(operands[0], operands[1])
Opcode.LOAD_BOOL_ADDR -> CmdLoadBoolAddr(operands[0], operands[1]) Opcode.LOAD_BOOL_ADDR -> CmdLoadBoolAddr(operands[0], operands[1])
Opcode.STORE_BOOL_ADDR -> CmdStoreBoolAddr(operands[0], operands[1]) Opcode.STORE_BOOL_ADDR -> CmdStoreBoolAddr(operands[0], operands[1])
Opcode.INT_TO_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1])) { Opcode.INT_TO_REAL -> CmdIntToReal(operands[0], operands[1])
CmdIntToRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount) Opcode.REAL_TO_INT -> CmdRealToInt(operands[0], operands[1])
} else { Opcode.BOOL_TO_INT -> CmdBoolToInt(operands[0], operands[1])
CmdIntToReal(operands[0], operands[1]) Opcode.INT_TO_BOOL -> CmdIntToBool(operands[0], operands[1])
} Opcode.ADD_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount && operands[2] >= scopeSlotCount) {
Opcode.REAL_TO_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1])) {
CmdRealToIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount)
} else {
CmdRealToInt(operands[0], operands[1])
}
Opcode.BOOL_TO_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1])) {
CmdBoolToIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount)
} else {
CmdBoolToInt(operands[0], operands[1])
}
Opcode.INT_TO_BOOL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1])) {
CmdIntToBoolLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount)
} else {
CmdIntToBool(operands[0], operands[1])
}
Opcode.ADD_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdAddIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) CmdAddIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else { } else {
CmdAddInt(operands[0], operands[1], operands[2]) CmdAddInt(operands[0], operands[1], operands[2])
} }
Opcode.SUB_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.SUB_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount && operands[2] >= scopeSlotCount) {
CmdSubIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) CmdSubIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else { } else {
CmdSubInt(operands[0], operands[1], operands[2]) CmdSubInt(operands[0], operands[1], operands[2])
} }
Opcode.MUL_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.MUL_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount && operands[2] >= scopeSlotCount) {
CmdMulIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) CmdMulIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else { } else {
CmdMulInt(operands[0], operands[1], operands[2]) CmdMulInt(operands[0], operands[1], operands[2])
} }
Opcode.DIV_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.DIV_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount && operands[2] >= scopeSlotCount) {
CmdDivIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) CmdDivIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else { } else {
CmdDivInt(operands[0], operands[1], operands[2]) CmdDivInt(operands[0], operands[1], operands[2])
} }
Opcode.MOD_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.MOD_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount && operands[2] >= scopeSlotCount) {
CmdModIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) CmdModIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else { } else {
CmdModInt(operands[0], operands[1], operands[2]) CmdModInt(operands[0], operands[1], operands[2])
} }
Opcode.NEG_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1])) { Opcode.NEG_INT -> CmdNegInt(operands[0], operands[1])
CmdNegIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount) Opcode.INC_INT -> if (operands[0] >= scopeSlotCount) {
} else {
CmdNegInt(operands[0], operands[1])
}
Opcode.INC_INT -> if (isFastLocal(operands[0])) {
CmdIncIntLocal(operands[0] - scopeSlotCount) CmdIncIntLocal(operands[0] - scopeSlotCount)
} else { } else {
CmdIncInt(operands[0]) CmdIncInt(operands[0])
} }
Opcode.DEC_INT -> if (isFastLocal(operands[0])) { Opcode.DEC_INT -> if (operands[0] >= scopeSlotCount) {
CmdDecIntLocal(operands[0] - scopeSlotCount) CmdDecIntLocal(operands[0] - scopeSlotCount)
} else { } else {
CmdDecInt(operands[0]) CmdDecInt(operands[0])
} }
Opcode.ADD_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.ADD_REAL -> CmdAddReal(operands[0], operands[1], operands[2])
CmdAddRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) Opcode.SUB_REAL -> CmdSubReal(operands[0], operands[1], operands[2])
} else { Opcode.MUL_REAL -> CmdMulReal(operands[0], operands[1], operands[2])
CmdAddReal(operands[0], operands[1], operands[2]) Opcode.DIV_REAL -> CmdDivReal(operands[0], operands[1], operands[2])
} Opcode.NEG_REAL -> CmdNegReal(operands[0], operands[1])
Opcode.SUB_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.AND_INT -> CmdAndInt(operands[0], operands[1], operands[2])
CmdSubRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) Opcode.OR_INT -> CmdOrInt(operands[0], operands[1], operands[2])
} else { Opcode.XOR_INT -> CmdXorInt(operands[0], operands[1], operands[2])
CmdSubReal(operands[0], operands[1], operands[2]) Opcode.SHL_INT -> CmdShlInt(operands[0], operands[1], operands[2])
} Opcode.SHR_INT -> CmdShrInt(operands[0], operands[1], operands[2])
Opcode.MUL_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.USHR_INT -> CmdUshrInt(operands[0], operands[1], operands[2])
CmdMulRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) Opcode.INV_INT -> CmdInvInt(operands[0], operands[1])
} else { Opcode.CMP_EQ_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount && operands[2] >= scopeSlotCount) {
CmdMulReal(operands[0], operands[1], operands[2])
}
Opcode.DIV_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdDivRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdDivReal(operands[0], operands[1], operands[2])
}
Opcode.NEG_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1])) {
CmdNegRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount)
} else {
CmdNegReal(operands[0], operands[1])
}
Opcode.AND_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdAndIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdAndInt(operands[0], operands[1], operands[2])
}
Opcode.OR_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdOrIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdOrInt(operands[0], operands[1], operands[2])
}
Opcode.XOR_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdXorIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdXorInt(operands[0], operands[1], operands[2])
}
Opcode.SHL_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdShlIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdShlInt(operands[0], operands[1], operands[2])
}
Opcode.SHR_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdShrIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdShrInt(operands[0], operands[1], operands[2])
}
Opcode.USHR_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdUshrIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdUshrInt(operands[0], operands[1], operands[2])
}
Opcode.INV_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1])) {
CmdInvIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount)
} else {
CmdInvInt(operands[0], operands[1])
}
Opcode.CMP_EQ_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpEqIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) CmdCmpEqIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else { } else {
CmdCmpEqInt(operands[0], operands[1], operands[2]) CmdCmpEqInt(operands[0], operands[1], operands[2])
} }
Opcode.CMP_NEQ_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.CMP_NEQ_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount && operands[2] >= scopeSlotCount) {
CmdCmpNeqIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) CmdCmpNeqIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else { } else {
CmdCmpNeqInt(operands[0], operands[1], operands[2]) CmdCmpNeqInt(operands[0], operands[1], operands[2])
} }
Opcode.CMP_LT_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.CMP_LT_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount && operands[2] >= scopeSlotCount) {
CmdCmpLtIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) CmdCmpLtIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else { } else {
CmdCmpLtInt(operands[0], operands[1], operands[2]) CmdCmpLtInt(operands[0], operands[1], operands[2])
} }
Opcode.CMP_LTE_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.CMP_LTE_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount && operands[2] >= scopeSlotCount) {
CmdCmpLteIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) CmdCmpLteIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else { } else {
CmdCmpLteInt(operands[0], operands[1], operands[2]) CmdCmpLteInt(operands[0], operands[1], operands[2])
} }
Opcode.CMP_GT_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.CMP_GT_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount && operands[2] >= scopeSlotCount) {
CmdCmpGtIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) CmdCmpGtIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else { } else {
CmdCmpGtInt(operands[0], operands[1], operands[2]) CmdCmpGtInt(operands[0], operands[1], operands[2])
} }
Opcode.CMP_GTE_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.CMP_GTE_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount && operands[2] >= scopeSlotCount) {
CmdCmpGteIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) CmdCmpGteIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else { } else {
CmdCmpGteInt(operands[0], operands[1], operands[2]) CmdCmpGteInt(operands[0], operands[1], operands[2])
} }
Opcode.CMP_EQ_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.CMP_EQ_REAL -> CmdCmpEqReal(operands[0], operands[1], operands[2])
CmdCmpEqRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) Opcode.CMP_NEQ_REAL -> CmdCmpNeqReal(operands[0], operands[1], operands[2])
} else { Opcode.CMP_LT_REAL -> CmdCmpLtReal(operands[0], operands[1], operands[2])
CmdCmpEqReal(operands[0], operands[1], operands[2]) Opcode.CMP_LTE_REAL -> CmdCmpLteReal(operands[0], operands[1], operands[2])
} Opcode.CMP_GT_REAL -> CmdCmpGtReal(operands[0], operands[1], operands[2])
Opcode.CMP_NEQ_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.CMP_GTE_REAL -> CmdCmpGteReal(operands[0], operands[1], operands[2])
CmdCmpNeqRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) Opcode.CMP_EQ_BOOL -> CmdCmpEqBool(operands[0], operands[1], operands[2])
} else { Opcode.CMP_NEQ_BOOL -> CmdCmpNeqBool(operands[0], operands[1], operands[2])
CmdCmpNeqReal(operands[0], operands[1], operands[2]) Opcode.CMP_EQ_INT_REAL -> CmdCmpEqIntReal(operands[0], operands[1], operands[2])
} Opcode.CMP_EQ_REAL_INT -> CmdCmpEqRealInt(operands[0], operands[1], operands[2])
Opcode.CMP_LT_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.CMP_LT_INT_REAL -> CmdCmpLtIntReal(operands[0], operands[1], operands[2])
CmdCmpLtRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) Opcode.CMP_LT_REAL_INT -> CmdCmpLtRealInt(operands[0], operands[1], operands[2])
} else { Opcode.CMP_LTE_INT_REAL -> CmdCmpLteIntReal(operands[0], operands[1], operands[2])
CmdCmpLtReal(operands[0], operands[1], operands[2]) Opcode.CMP_LTE_REAL_INT -> CmdCmpLteRealInt(operands[0], operands[1], operands[2])
} Opcode.CMP_GT_INT_REAL -> CmdCmpGtIntReal(operands[0], operands[1], operands[2])
Opcode.CMP_LTE_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.CMP_GT_REAL_INT -> CmdCmpGtRealInt(operands[0], operands[1], operands[2])
CmdCmpLteRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) Opcode.CMP_GTE_INT_REAL -> CmdCmpGteIntReal(operands[0], operands[1], operands[2])
} else { Opcode.CMP_GTE_REAL_INT -> CmdCmpGteRealInt(operands[0], operands[1], operands[2])
CmdCmpLteReal(operands[0], operands[1], operands[2]) Opcode.CMP_NEQ_INT_REAL -> CmdCmpNeqIntReal(operands[0], operands[1], operands[2])
} Opcode.CMP_NEQ_REAL_INT -> CmdCmpNeqRealInt(operands[0], operands[1], operands[2])
Opcode.CMP_GT_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpGtRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpGtReal(operands[0], operands[1], operands[2])
}
Opcode.CMP_GTE_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpGteRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpGteReal(operands[0], operands[1], operands[2])
}
Opcode.CMP_EQ_BOOL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpEqBoolLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpEqBool(operands[0], operands[1], operands[2])
}
Opcode.CMP_NEQ_BOOL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpNeqBoolLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpNeqBool(operands[0], operands[1], operands[2])
}
Opcode.CMP_EQ_INT_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpEqIntRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpEqIntReal(operands[0], operands[1], operands[2])
}
Opcode.CMP_EQ_REAL_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpEqRealIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpEqRealInt(operands[0], operands[1], operands[2])
}
Opcode.CMP_LT_INT_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpLtIntRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpLtIntReal(operands[0], operands[1], operands[2])
}
Opcode.CMP_LT_REAL_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpLtRealIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpLtRealInt(operands[0], operands[1], operands[2])
}
Opcode.CMP_LTE_INT_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpLteIntRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpLteIntReal(operands[0], operands[1], operands[2])
}
Opcode.CMP_LTE_REAL_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpLteRealIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpLteRealInt(operands[0], operands[1], operands[2])
}
Opcode.CMP_GT_INT_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpGtIntRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpGtIntReal(operands[0], operands[1], operands[2])
}
Opcode.CMP_GT_REAL_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpGtRealIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpGtRealInt(operands[0], operands[1], operands[2])
}
Opcode.CMP_GTE_INT_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpGteIntRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpGteIntReal(operands[0], operands[1], operands[2])
}
Opcode.CMP_GTE_REAL_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpGteRealIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpGteRealInt(operands[0], operands[1], operands[2])
}
Opcode.CMP_NEQ_INT_REAL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpNeqIntRealLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpNeqIntReal(operands[0], operands[1], operands[2])
}
Opcode.CMP_NEQ_REAL_INT -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpNeqRealIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpNeqRealInt(operands[0], operands[1], operands[2])
}
Opcode.CMP_EQ_OBJ -> CmdCmpEqObj(operands[0], operands[1], operands[2]) Opcode.CMP_EQ_OBJ -> CmdCmpEqObj(operands[0], operands[1], operands[2])
Opcode.CMP_NEQ_OBJ -> CmdCmpNeqObj(operands[0], operands[1], operands[2]) Opcode.CMP_NEQ_OBJ -> CmdCmpNeqObj(operands[0], operands[1], operands[2])
Opcode.CMP_REF_EQ_OBJ -> CmdCmpRefEqObj(operands[0], operands[1], operands[2]) Opcode.CMP_REF_EQ_OBJ -> CmdCmpRefEqObj(operands[0], operands[1], operands[2])
Opcode.CMP_REF_NEQ_OBJ -> CmdCmpRefNeqObj(operands[0], operands[1], operands[2]) Opcode.CMP_REF_NEQ_OBJ -> CmdCmpRefNeqObj(operands[0], operands[1], operands[2])
Opcode.CMP_EQ_STR -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) { Opcode.NOT_BOOL -> CmdNotBool(operands[0], operands[1])
CmdCmpEqStrLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount) Opcode.AND_BOOL -> CmdAndBool(operands[0], operands[1], operands[2])
} else { Opcode.OR_BOOL -> CmdOrBool(operands[0], operands[1], operands[2])
CmdCmpEqStr(operands[0], operands[1], operands[2])
}
Opcode.CMP_NEQ_STR -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpNeqStrLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpNeqStr(operands[0], operands[1], operands[2])
}
Opcode.CMP_LT_STR -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpLtStrLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpLtStr(operands[0], operands[1], operands[2])
}
Opcode.CMP_LTE_STR -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpLteStrLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpLteStr(operands[0], operands[1], operands[2])
}
Opcode.CMP_GT_STR -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpGtStrLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpGtStr(operands[0], operands[1], operands[2])
}
Opcode.CMP_GTE_STR -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpGteStrLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpGteStr(operands[0], operands[1], operands[2])
}
Opcode.CMP_EQ_INT_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpEqIntObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpEqIntObj(operands[0], operands[1], operands[2])
}
Opcode.CMP_NEQ_INT_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpNeqIntObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpNeqIntObj(operands[0], operands[1], operands[2])
}
Opcode.CMP_LT_INT_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpLtIntObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpLtIntObj(operands[0], operands[1], operands[2])
}
Opcode.CMP_LTE_INT_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpLteIntObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpLteIntObj(operands[0], operands[1], operands[2])
}
Opcode.CMP_GT_INT_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpGtIntObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpGtIntObj(operands[0], operands[1], operands[2])
}
Opcode.CMP_GTE_INT_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpGteIntObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpGteIntObj(operands[0], operands[1], operands[2])
}
Opcode.CMP_EQ_REAL_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpEqRealObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpEqRealObj(operands[0], operands[1], operands[2])
}
Opcode.CMP_NEQ_REAL_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpNeqRealObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpNeqRealObj(operands[0], operands[1], operands[2])
}
Opcode.CMP_LT_REAL_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpLtRealObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpLtRealObj(operands[0], operands[1], operands[2])
}
Opcode.CMP_LTE_REAL_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpLteRealObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpLteRealObj(operands[0], operands[1], operands[2])
}
Opcode.CMP_GT_REAL_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpGtRealObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpGtRealObj(operands[0], operands[1], operands[2])
}
Opcode.CMP_GTE_REAL_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdCmpGteRealObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdCmpGteRealObj(operands[0], operands[1], operands[2])
}
Opcode.NOT_BOOL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1])) {
CmdNotBoolLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount)
} else {
CmdNotBool(operands[0], operands[1])
}
Opcode.AND_BOOL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdAndBoolLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdAndBool(operands[0], operands[1], operands[2])
}
Opcode.OR_BOOL -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdOrBoolLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdOrBool(operands[0], operands[1], operands[2])
}
Opcode.CMP_LT_OBJ -> CmdCmpLtObj(operands[0], operands[1], operands[2]) Opcode.CMP_LT_OBJ -> CmdCmpLtObj(operands[0], operands[1], operands[2])
Opcode.CMP_LTE_OBJ -> CmdCmpLteObj(operands[0], operands[1], operands[2]) Opcode.CMP_LTE_OBJ -> CmdCmpLteObj(operands[0], operands[1], operands[2])
Opcode.CMP_GT_OBJ -> CmdCmpGtObj(operands[0], operands[1], operands[2]) Opcode.CMP_GT_OBJ -> CmdCmpGtObj(operands[0], operands[1], operands[2])
Opcode.CMP_GTE_OBJ -> CmdCmpGteObj(operands[0], operands[1], operands[2]) Opcode.CMP_GTE_OBJ -> CmdCmpGteObj(operands[0], operands[1], operands[2])
Opcode.ADD_INT_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdAddIntObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdAddIntObj(operands[0], operands[1], operands[2])
}
Opcode.SUB_INT_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdSubIntObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdSubIntObj(operands[0], operands[1], operands[2])
}
Opcode.MUL_INT_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdMulIntObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdMulIntObj(operands[0], operands[1], operands[2])
}
Opcode.DIV_INT_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdDivIntObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdDivIntObj(operands[0], operands[1], operands[2])
}
Opcode.MOD_INT_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdModIntObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdModIntObj(operands[0], operands[1], operands[2])
}
Opcode.ADD_REAL_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdAddRealObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdAddRealObj(operands[0], operands[1], operands[2])
}
Opcode.SUB_REAL_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdSubRealObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdSubRealObj(operands[0], operands[1], operands[2])
}
Opcode.MUL_REAL_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdMulRealObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdMulRealObj(operands[0], operands[1], operands[2])
}
Opcode.DIV_REAL_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdDivRealObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdDivRealObj(operands[0], operands[1], operands[2])
}
Opcode.MOD_REAL_OBJ -> if (isFastLocal(operands[0]) && isFastLocal(operands[1]) && isFastLocal(operands[2])) {
CmdModRealObjLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2] - scopeSlotCount)
} else {
CmdModRealObj(operands[0], operands[1], operands[2])
}
Opcode.ADD_OBJ -> CmdAddObj(operands[0], operands[1], operands[2]) Opcode.ADD_OBJ -> CmdAddObj(operands[0], operands[1], operands[2])
Opcode.SUB_OBJ -> CmdSubObj(operands[0], operands[1], operands[2]) Opcode.SUB_OBJ -> CmdSubObj(operands[0], operands[1], operands[2])
Opcode.MUL_OBJ -> CmdMulObj(operands[0], operands[1], operands[2]) Opcode.MUL_OBJ -> CmdMulObj(operands[0], operands[1], operands[2])
@ -744,46 +401,8 @@ class CmdBuilder {
Opcode.CONTAINS_OBJ -> CmdContainsObj(operands[0], operands[1], operands[2]) Opcode.CONTAINS_OBJ -> CmdContainsObj(operands[0], operands[1], operands[2])
Opcode.ASSIGN_OP_OBJ -> CmdAssignOpObj(operands[0], operands[1], operands[2], operands[3], operands[4]) Opcode.ASSIGN_OP_OBJ -> CmdAssignOpObj(operands[0], operands[1], operands[2], operands[3], operands[4])
Opcode.JMP -> CmdJmp(operands[0]) Opcode.JMP -> CmdJmp(operands[0])
Opcode.JMP_IF_TRUE -> if (operands[0] >= scopeSlotCount) { Opcode.JMP_IF_TRUE -> CmdJmpIfTrue(operands[0], operands[1])
CmdJmpIfTrueLocal(operands[0] - scopeSlotCount, operands[1]) Opcode.JMP_IF_FALSE -> CmdJmpIfFalse(operands[0], operands[1])
} else {
CmdJmpIfTrue(operands[0], operands[1])
}
Opcode.JMP_IF_FALSE -> if (operands[0] >= scopeSlotCount) {
CmdJmpIfFalseLocal(operands[0] - scopeSlotCount, operands[1])
} else {
CmdJmpIfFalse(operands[0], operands[1])
}
Opcode.JMP_IF_EQ_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount) {
CmdJmpIfEqIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2])
} else {
CmdJmpIfEqInt(operands[0], operands[1], operands[2])
}
Opcode.JMP_IF_NEQ_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount) {
CmdJmpIfNeqIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2])
} else {
CmdJmpIfNeqInt(operands[0], operands[1], operands[2])
}
Opcode.JMP_IF_LT_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount) {
CmdJmpIfLtIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2])
} else {
CmdJmpIfLtInt(operands[0], operands[1], operands[2])
}
Opcode.JMP_IF_LTE_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount) {
CmdJmpIfLteIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2])
} else {
CmdJmpIfLteInt(operands[0], operands[1], operands[2])
}
Opcode.JMP_IF_GT_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount) {
CmdJmpIfGtIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2])
} else {
CmdJmpIfGtInt(operands[0], operands[1], operands[2])
}
Opcode.JMP_IF_GTE_INT -> if (operands[0] >= scopeSlotCount && operands[1] >= scopeSlotCount) {
CmdJmpIfGteIntLocal(operands[0] - scopeSlotCount, operands[1] - scopeSlotCount, operands[2])
} else {
CmdJmpIfGteInt(operands[0], operands[1], operands[2])
}
Opcode.RET -> CmdRet(operands[0]) Opcode.RET -> CmdRet(operands[0])
Opcode.RET_VOID -> CmdRetVoid() Opcode.RET_VOID -> CmdRetVoid()
Opcode.PUSH_SCOPE -> CmdPushScope(operands[0]) Opcode.PUSH_SCOPE -> CmdPushScope(operands[0])
@ -799,21 +418,11 @@ class CmdBuilder {
Opcode.DECL_ENUM -> CmdDeclEnum(operands[0], operands[1]) Opcode.DECL_ENUM -> CmdDeclEnum(operands[0], operands[1])
Opcode.DECL_FUNCTION -> CmdDeclFunction(operands[0], operands[1]) Opcode.DECL_FUNCTION -> CmdDeclFunction(operands[0], operands[1])
Opcode.DECL_CLASS -> CmdDeclClass(operands[0], operands[1]) Opcode.DECL_CLASS -> CmdDeclClass(operands[0], operands[1])
Opcode.DECL_CLASS_FIELD -> CmdDeclClassField(operands[0], operands[1])
Opcode.DECL_CLASS_DELEGATED -> CmdDeclClassDelegated(operands[0], operands[1])
Opcode.DECL_CLASS_INSTANCE_INIT -> CmdDeclClassInstanceInit(operands[0], operands[1])
Opcode.DECL_CLASS_INSTANCE_FIELD -> CmdDeclClassInstanceField(operands[0], operands[1])
Opcode.DECL_CLASS_INSTANCE_PROPERTY -> CmdDeclClassInstanceProperty(operands[0], operands[1])
Opcode.DECL_CLASS_INSTANCE_DELEGATED -> CmdDeclClassInstanceDelegated(operands[0], operands[1])
Opcode.DECL_INSTANCE_FIELD -> CmdDeclInstanceField(operands[0], operands[1])
Opcode.DECL_INSTANCE_PROPERTY -> CmdDeclInstanceProperty(operands[0], operands[1])
Opcode.DECL_INSTANCE_DELEGATED -> CmdDeclInstanceDelegated(operands[0], operands[1])
Opcode.DECL_EXT_PROPERTY -> CmdDeclExtProperty(operands[0], operands[1]) Opcode.DECL_EXT_PROPERTY -> CmdDeclExtProperty(operands[0], operands[1])
Opcode.CALL_DIRECT -> CmdCallDirect(operands[0], operands[1], operands[2], operands[3]) Opcode.CALL_DIRECT -> CmdCallDirect(operands[0], operands[1], operands[2], operands[3])
Opcode.ASSIGN_DESTRUCTURE -> CmdAssignDestructure(operands[0], operands[1]) Opcode.ASSIGN_DESTRUCTURE -> CmdAssignDestructure(operands[0], operands[1])
Opcode.CALL_MEMBER_SLOT -> CmdCallMemberSlot(operands[0], operands[1], operands[2], operands[3], operands[4]) Opcode.CALL_MEMBER_SLOT -> CmdCallMemberSlot(operands[0], operands[1], operands[2], operands[3], operands[4])
Opcode.CALL_SLOT -> CmdCallSlot(operands[0], operands[1], operands[2], operands[3]) Opcode.CALL_SLOT -> CmdCallSlot(operands[0], operands[1], operands[2], operands[3])
Opcode.CALL_BRIDGE_SLOT -> CmdCallBridgeSlot(operands[0], operands[1], operands[2], operands[3])
Opcode.CALL_DYNAMIC_MEMBER -> CmdCallDynamicMember(operands[0], operands[1], operands[2], operands[3], operands[4]) Opcode.CALL_DYNAMIC_MEMBER -> CmdCallDynamicMember(operands[0], operands[1], operands[2], operands[3], operands[4])
Opcode.GET_INDEX -> CmdGetIndex(operands[0], operands[1], operands[2]) Opcode.GET_INDEX -> CmdGetIndex(operands[0], operands[1], operands[2])
Opcode.SET_INDEX -> CmdSetIndex(operands[0], operands[1], operands[2]) Opcode.SET_INDEX -> CmdSetIndex(operands[0], operands[1], operands[2])

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com * Copyright 2026 Sergey S. Chernov
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
*/ */
package net.sergeych.lyng.bytecode package net.sergeych.lyng.bytecode
@ -77,8 +76,6 @@ object CmdDisassembler {
is CmdMoveObj -> Opcode.MOVE_OBJ to intArrayOf(cmd.src, cmd.dst) is CmdMoveObj -> Opcode.MOVE_OBJ to intArrayOf(cmd.src, cmd.dst)
is CmdMoveInt -> Opcode.MOVE_INT to intArrayOf(cmd.src, cmd.dst) is CmdMoveInt -> Opcode.MOVE_INT to intArrayOf(cmd.src, cmd.dst)
is CmdMoveIntLocal -> Opcode.MOVE_INT to intArrayOf(cmd.src + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount) is CmdMoveIntLocal -> Opcode.MOVE_INT to intArrayOf(cmd.src + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdMoveRealLocal -> Opcode.MOVE_REAL to intArrayOf(cmd.src + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdMoveBoolLocal -> Opcode.MOVE_BOOL to intArrayOf(cmd.src + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdMoveReal -> Opcode.MOVE_REAL to intArrayOf(cmd.src, cmd.dst) is CmdMoveReal -> Opcode.MOVE_REAL to intArrayOf(cmd.src, cmd.dst)
is CmdMoveBool -> Opcode.MOVE_BOOL to intArrayOf(cmd.src, cmd.dst) is CmdMoveBool -> Opcode.MOVE_BOOL to intArrayOf(cmd.src, cmd.dst)
is CmdConstObj -> Opcode.CONST_OBJ to intArrayOf(cmd.constId, cmd.dst) is CmdConstObj -> Opcode.CONST_OBJ to intArrayOf(cmd.constId, cmd.dst)
@ -117,13 +114,9 @@ object CmdDisassembler {
is CmdLoadBoolAddr -> Opcode.LOAD_BOOL_ADDR to intArrayOf(cmd.addrSlot, cmd.dst) is CmdLoadBoolAddr -> Opcode.LOAD_BOOL_ADDR to intArrayOf(cmd.addrSlot, cmd.dst)
is CmdStoreBoolAddr -> Opcode.STORE_BOOL_ADDR to intArrayOf(cmd.src, cmd.addrSlot) is CmdStoreBoolAddr -> Opcode.STORE_BOOL_ADDR to intArrayOf(cmd.src, cmd.addrSlot)
is CmdIntToReal -> Opcode.INT_TO_REAL to intArrayOf(cmd.src, cmd.dst) is CmdIntToReal -> Opcode.INT_TO_REAL to intArrayOf(cmd.src, cmd.dst)
is CmdIntToRealLocal -> Opcode.INT_TO_REAL to intArrayOf(cmd.src + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdRealToInt -> Opcode.REAL_TO_INT to intArrayOf(cmd.src, cmd.dst) is CmdRealToInt -> Opcode.REAL_TO_INT to intArrayOf(cmd.src, cmd.dst)
is CmdRealToIntLocal -> Opcode.REAL_TO_INT to intArrayOf(cmd.src + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdBoolToInt -> Opcode.BOOL_TO_INT to intArrayOf(cmd.src, cmd.dst) is CmdBoolToInt -> Opcode.BOOL_TO_INT to intArrayOf(cmd.src, cmd.dst)
is CmdBoolToIntLocal -> Opcode.BOOL_TO_INT to intArrayOf(cmd.src + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdIntToBool -> Opcode.INT_TO_BOOL to intArrayOf(cmd.src, cmd.dst) is CmdIntToBool -> Opcode.INT_TO_BOOL to intArrayOf(cmd.src, cmd.dst)
is CmdIntToBoolLocal -> Opcode.INT_TO_BOOL to intArrayOf(cmd.src + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdAddInt -> Opcode.ADD_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdAddInt -> Opcode.ADD_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdAddIntLocal -> Opcode.ADD_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount) is CmdAddIntLocal -> Opcode.ADD_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdSubInt -> Opcode.SUB_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdSubInt -> Opcode.SUB_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
@ -135,35 +128,22 @@ object CmdDisassembler {
is CmdModInt -> Opcode.MOD_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdModInt -> Opcode.MOD_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdModIntLocal -> Opcode.MOD_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount) is CmdModIntLocal -> Opcode.MOD_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdNegInt -> Opcode.NEG_INT to intArrayOf(cmd.src, cmd.dst) is CmdNegInt -> Opcode.NEG_INT to intArrayOf(cmd.src, cmd.dst)
is CmdNegIntLocal -> Opcode.NEG_INT to intArrayOf(cmd.src + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdIncInt -> Opcode.INC_INT to intArrayOf(cmd.slot) is CmdIncInt -> Opcode.INC_INT to intArrayOf(cmd.slot)
is CmdIncIntLocal -> Opcode.INC_INT to intArrayOf(cmd.slot + fn.scopeSlotCount) is CmdIncIntLocal -> Opcode.INC_INT to intArrayOf(cmd.slot + fn.scopeSlotCount)
is CmdDecInt -> Opcode.DEC_INT to intArrayOf(cmd.slot) is CmdDecInt -> Opcode.DEC_INT to intArrayOf(cmd.slot)
is CmdDecIntLocal -> Opcode.DEC_INT to intArrayOf(cmd.slot + fn.scopeSlotCount) is CmdDecIntLocal -> Opcode.DEC_INT to intArrayOf(cmd.slot + fn.scopeSlotCount)
is CmdAddReal -> Opcode.ADD_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdAddReal -> Opcode.ADD_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdAddRealLocal -> Opcode.ADD_REAL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdSubReal -> Opcode.SUB_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdSubReal -> Opcode.SUB_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdSubRealLocal -> Opcode.SUB_REAL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdMulReal -> Opcode.MUL_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdMulReal -> Opcode.MUL_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdMulRealLocal -> Opcode.MUL_REAL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdDivReal -> Opcode.DIV_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdDivReal -> Opcode.DIV_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdDivRealLocal -> Opcode.DIV_REAL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdNegReal -> Opcode.NEG_REAL to intArrayOf(cmd.src, cmd.dst) is CmdNegReal -> Opcode.NEG_REAL to intArrayOf(cmd.src, cmd.dst)
is CmdNegRealLocal -> Opcode.NEG_REAL to intArrayOf(cmd.src + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdAndInt -> Opcode.AND_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdAndInt -> Opcode.AND_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdAndIntLocal -> Opcode.AND_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdOrInt -> Opcode.OR_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdOrInt -> Opcode.OR_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdOrIntLocal -> Opcode.OR_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdXorInt -> Opcode.XOR_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdXorInt -> Opcode.XOR_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdXorIntLocal -> Opcode.XOR_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdShlInt -> Opcode.SHL_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdShlInt -> Opcode.SHL_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdShlIntLocal -> Opcode.SHL_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdShrInt -> Opcode.SHR_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdShrInt -> Opcode.SHR_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdShrIntLocal -> Opcode.SHR_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdUshrInt -> Opcode.USHR_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdUshrInt -> Opcode.USHR_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdUshrIntLocal -> Opcode.USHR_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdInvInt -> Opcode.INV_INT to intArrayOf(cmd.src, cmd.dst) is CmdInvInt -> Opcode.INV_INT to intArrayOf(cmd.src, cmd.dst)
is CmdInvIntLocal -> Opcode.INV_INT to intArrayOf(cmd.src + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpEqInt -> Opcode.CMP_EQ_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpEqInt -> Opcode.CMP_EQ_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpEqIntLocal -> Opcode.CMP_EQ_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount) is CmdCmpEqIntLocal -> Opcode.CMP_EQ_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpNeqInt -> Opcode.CMP_NEQ_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpNeqInt -> Opcode.CMP_NEQ_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
@ -177,273 +157,36 @@ object CmdDisassembler {
is CmdCmpGteInt -> Opcode.CMP_GTE_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpGteInt -> Opcode.CMP_GTE_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpGteIntLocal -> Opcode.CMP_GTE_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount) is CmdCmpGteIntLocal -> Opcode.CMP_GTE_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpEqReal -> Opcode.CMP_EQ_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpEqReal -> Opcode.CMP_EQ_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpEqRealLocal -> Opcode.CMP_EQ_REAL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpNeqReal -> Opcode.CMP_NEQ_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpNeqReal -> Opcode.CMP_NEQ_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpNeqRealLocal -> Opcode.CMP_NEQ_REAL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpLtReal -> Opcode.CMP_LT_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpLtReal -> Opcode.CMP_LT_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpLtRealLocal -> Opcode.CMP_LT_REAL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpLteReal -> Opcode.CMP_LTE_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpLteReal -> Opcode.CMP_LTE_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpLteRealLocal -> Opcode.CMP_LTE_REAL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpGtReal -> Opcode.CMP_GT_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpGtReal -> Opcode.CMP_GT_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpGtRealLocal -> Opcode.CMP_GT_REAL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpGteReal -> Opcode.CMP_GTE_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpGteReal -> Opcode.CMP_GTE_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpGteRealLocal -> Opcode.CMP_GTE_REAL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpEqBool -> Opcode.CMP_EQ_BOOL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpEqBool -> Opcode.CMP_EQ_BOOL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpEqBoolLocal -> Opcode.CMP_EQ_BOOL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpNeqBool -> Opcode.CMP_NEQ_BOOL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpNeqBool -> Opcode.CMP_NEQ_BOOL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpNeqBoolLocal -> Opcode.CMP_NEQ_BOOL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpEqIntReal -> Opcode.CMP_EQ_INT_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpEqIntReal -> Opcode.CMP_EQ_INT_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpEqIntRealLocal -> Opcode.CMP_EQ_INT_REAL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpEqRealInt -> Opcode.CMP_EQ_REAL_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpEqRealInt -> Opcode.CMP_EQ_REAL_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpEqRealIntLocal -> Opcode.CMP_EQ_REAL_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpLtIntReal -> Opcode.CMP_LT_INT_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpLtIntReal -> Opcode.CMP_LT_INT_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpLtIntRealLocal -> Opcode.CMP_LT_INT_REAL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpLtRealInt -> Opcode.CMP_LT_REAL_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpLtRealInt -> Opcode.CMP_LT_REAL_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpLtRealIntLocal -> Opcode.CMP_LT_REAL_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpLteIntReal -> Opcode.CMP_LTE_INT_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpLteIntReal -> Opcode.CMP_LTE_INT_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpLteIntRealLocal -> Opcode.CMP_LTE_INT_REAL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpLteRealInt -> Opcode.CMP_LTE_REAL_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpLteRealInt -> Opcode.CMP_LTE_REAL_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpLteRealIntLocal -> Opcode.CMP_LTE_REAL_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpGtIntReal -> Opcode.CMP_GT_INT_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpGtIntReal -> Opcode.CMP_GT_INT_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpGtIntRealLocal -> Opcode.CMP_GT_INT_REAL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpGtRealInt -> Opcode.CMP_GT_REAL_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpGtRealInt -> Opcode.CMP_GT_REAL_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpGtRealIntLocal -> Opcode.CMP_GT_REAL_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpGteIntReal -> Opcode.CMP_GTE_INT_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpGteIntReal -> Opcode.CMP_GTE_INT_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpGteIntRealLocal -> Opcode.CMP_GTE_INT_REAL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpGteRealInt -> Opcode.CMP_GTE_REAL_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpGteRealInt -> Opcode.CMP_GTE_REAL_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpGteRealIntLocal -> Opcode.CMP_GTE_REAL_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpNeqIntReal -> Opcode.CMP_NEQ_INT_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpNeqIntReal -> Opcode.CMP_NEQ_INT_REAL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpNeqIntRealLocal -> Opcode.CMP_NEQ_INT_REAL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdCmpNeqRealInt -> Opcode.CMP_NEQ_REAL_INT to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpNeqRealInt -> Opcode.CMP_NEQ_REAL_INT to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpNeqRealIntLocal -> Opcode.CMP_NEQ_REAL_INT to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdJmpIfEqInt -> Opcode.JMP_IF_EQ_INT to intArrayOf(cmd.a, cmd.b, cmd.target)
is CmdJmpIfEqIntLocal -> Opcode.JMP_IF_EQ_INT to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.target
)
is CmdJmpIfNeqInt -> Opcode.JMP_IF_NEQ_INT to intArrayOf(cmd.a, cmd.b, cmd.target)
is CmdJmpIfNeqIntLocal -> Opcode.JMP_IF_NEQ_INT to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.target
)
is CmdJmpIfLtInt -> Opcode.JMP_IF_LT_INT to intArrayOf(cmd.a, cmd.b, cmd.target)
is CmdJmpIfLtIntLocal -> Opcode.JMP_IF_LT_INT to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.target
)
is CmdJmpIfLteInt -> Opcode.JMP_IF_LTE_INT to intArrayOf(cmd.a, cmd.b, cmd.target)
is CmdJmpIfLteIntLocal -> Opcode.JMP_IF_LTE_INT to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.target
)
is CmdJmpIfGtInt -> Opcode.JMP_IF_GT_INT to intArrayOf(cmd.a, cmd.b, cmd.target)
is CmdJmpIfGtIntLocal -> Opcode.JMP_IF_GT_INT to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.target
)
is CmdJmpIfGteInt -> Opcode.JMP_IF_GTE_INT to intArrayOf(cmd.a, cmd.b, cmd.target)
is CmdJmpIfGteIntLocal -> Opcode.JMP_IF_GTE_INT to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.target
)
is CmdCmpEqObj -> Opcode.CMP_EQ_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpEqObj -> Opcode.CMP_EQ_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpNeqObj -> Opcode.CMP_NEQ_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpNeqObj -> Opcode.CMP_NEQ_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpRefEqObj -> Opcode.CMP_REF_EQ_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpRefEqObj -> Opcode.CMP_REF_EQ_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpRefNeqObj -> Opcode.CMP_REF_NEQ_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpRefNeqObj -> Opcode.CMP_REF_NEQ_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpEqStr -> Opcode.CMP_EQ_STR to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpEqStrLocal -> Opcode.CMP_EQ_STR to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpNeqStr -> Opcode.CMP_NEQ_STR to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpNeqStrLocal -> Opcode.CMP_NEQ_STR to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpLtStr -> Opcode.CMP_LT_STR to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpLtStrLocal -> Opcode.CMP_LT_STR to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpLteStr -> Opcode.CMP_LTE_STR to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpLteStrLocal -> Opcode.CMP_LTE_STR to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpGtStr -> Opcode.CMP_GT_STR to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpGtStrLocal -> Opcode.CMP_GT_STR to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpGteStr -> Opcode.CMP_GTE_STR to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpGteStrLocal -> Opcode.CMP_GTE_STR to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpEqIntObj -> Opcode.CMP_EQ_INT_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpEqIntObjLocal -> Opcode.CMP_EQ_INT_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpNeqIntObj -> Opcode.CMP_NEQ_INT_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpNeqIntObjLocal -> Opcode.CMP_NEQ_INT_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpLtIntObj -> Opcode.CMP_LT_INT_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpLtIntObjLocal -> Opcode.CMP_LT_INT_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpLteIntObj -> Opcode.CMP_LTE_INT_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpLteIntObjLocal -> Opcode.CMP_LTE_INT_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpGtIntObj -> Opcode.CMP_GT_INT_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpGtIntObjLocal -> Opcode.CMP_GT_INT_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpGteIntObj -> Opcode.CMP_GTE_INT_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpGteIntObjLocal -> Opcode.CMP_GTE_INT_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpEqRealObj -> Opcode.CMP_EQ_REAL_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpEqRealObjLocal -> Opcode.CMP_EQ_REAL_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpNeqRealObj -> Opcode.CMP_NEQ_REAL_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpNeqRealObjLocal -> Opcode.CMP_NEQ_REAL_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpLtRealObj -> Opcode.CMP_LT_REAL_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpLtRealObjLocal -> Opcode.CMP_LT_REAL_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpLteRealObj -> Opcode.CMP_LTE_REAL_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpLteRealObjLocal -> Opcode.CMP_LTE_REAL_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpGtRealObj -> Opcode.CMP_GT_REAL_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpGtRealObjLocal -> Opcode.CMP_GT_REAL_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpGteRealObj -> Opcode.CMP_GTE_REAL_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpGteRealObjLocal -> Opcode.CMP_GTE_REAL_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdNotBool -> Opcode.NOT_BOOL to intArrayOf(cmd.src, cmd.dst) is CmdNotBool -> Opcode.NOT_BOOL to intArrayOf(cmd.src, cmd.dst)
is CmdNotBoolLocal -> Opcode.NOT_BOOL to intArrayOf(cmd.src + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdAndBool -> Opcode.AND_BOOL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdAndBool -> Opcode.AND_BOOL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdAndBoolLocal -> Opcode.AND_BOOL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdOrBool -> Opcode.OR_BOOL to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdOrBool -> Opcode.OR_BOOL to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdOrBoolLocal -> Opcode.OR_BOOL to intArrayOf(cmd.a + fn.scopeSlotCount, cmd.b + fn.scopeSlotCount, cmd.dst + fn.scopeSlotCount)
is CmdUnboxIntObj -> Opcode.UNBOX_INT_OBJ to intArrayOf(cmd.src, cmd.dst)
is CmdUnboxIntObjLocal -> Opcode.UNBOX_INT_OBJ to intArrayOf(
cmd.src + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdUnboxRealObj -> Opcode.UNBOX_REAL_OBJ to intArrayOf(cmd.src, cmd.dst)
is CmdUnboxRealObjLocal -> Opcode.UNBOX_REAL_OBJ to intArrayOf(
cmd.src + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdCmpLtObj -> Opcode.CMP_LT_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpLtObj -> Opcode.CMP_LT_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpLteObj -> Opcode.CMP_LTE_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpLteObj -> Opcode.CMP_LTE_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpGtObj -> Opcode.CMP_GT_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpGtObj -> Opcode.CMP_GT_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdCmpGteObj -> Opcode.CMP_GTE_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdCmpGteObj -> Opcode.CMP_GTE_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdAddIntObj -> Opcode.ADD_INT_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdAddIntObjLocal -> Opcode.ADD_INT_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdSubIntObj -> Opcode.SUB_INT_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdSubIntObjLocal -> Opcode.SUB_INT_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdMulIntObj -> Opcode.MUL_INT_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdMulIntObjLocal -> Opcode.MUL_INT_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdDivIntObj -> Opcode.DIV_INT_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdDivIntObjLocal -> Opcode.DIV_INT_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdModIntObj -> Opcode.MOD_INT_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdModIntObjLocal -> Opcode.MOD_INT_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdAddRealObj -> Opcode.ADD_REAL_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdAddRealObjLocal -> Opcode.ADD_REAL_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdSubRealObj -> Opcode.SUB_REAL_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdSubRealObjLocal -> Opcode.SUB_REAL_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdMulRealObj -> Opcode.MUL_REAL_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdMulRealObjLocal -> Opcode.MUL_REAL_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdDivRealObj -> Opcode.DIV_REAL_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdDivRealObjLocal -> Opcode.DIV_REAL_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdModRealObj -> Opcode.MOD_REAL_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdModRealObjLocal -> Opcode.MOD_REAL_OBJ to intArrayOf(
cmd.a + fn.scopeSlotCount,
cmd.b + fn.scopeSlotCount,
cmd.dst + fn.scopeSlotCount
)
is CmdAddObj -> Opcode.ADD_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdAddObj -> Opcode.ADD_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdSubObj -> Opcode.SUB_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdSubObj -> Opcode.SUB_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdMulObj -> Opcode.MUL_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdMulObj -> Opcode.MUL_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
@ -453,9 +196,7 @@ object CmdDisassembler {
is CmdAssignOpObj -> Opcode.ASSIGN_OP_OBJ to intArrayOf(cmd.opId, cmd.targetSlot, cmd.valueSlot, cmd.dst, cmd.nameId) is CmdAssignOpObj -> Opcode.ASSIGN_OP_OBJ to intArrayOf(cmd.opId, cmd.targetSlot, cmd.valueSlot, cmd.dst, cmd.nameId)
is CmdJmp -> Opcode.JMP to intArrayOf(cmd.target) is CmdJmp -> Opcode.JMP to intArrayOf(cmd.target)
is CmdJmpIfTrue -> Opcode.JMP_IF_TRUE to intArrayOf(cmd.cond, cmd.target) is CmdJmpIfTrue -> Opcode.JMP_IF_TRUE to intArrayOf(cmd.cond, cmd.target)
is CmdJmpIfTrueLocal -> Opcode.JMP_IF_TRUE to intArrayOf(cmd.cond + fn.scopeSlotCount, cmd.target)
is CmdJmpIfFalse -> Opcode.JMP_IF_FALSE to intArrayOf(cmd.cond, cmd.target) is CmdJmpIfFalse -> Opcode.JMP_IF_FALSE to intArrayOf(cmd.cond, cmd.target)
is CmdJmpIfFalseLocal -> Opcode.JMP_IF_FALSE to intArrayOf(cmd.cond + fn.scopeSlotCount, cmd.target)
is CmdRet -> Opcode.RET to intArrayOf(cmd.slot) is CmdRet -> Opcode.RET to intArrayOf(cmd.slot)
is CmdRetLabel -> Opcode.RET_LABEL to intArrayOf(cmd.labelId, cmd.slot) is CmdRetLabel -> Opcode.RET_LABEL to intArrayOf(cmd.labelId, cmd.slot)
is CmdRetVoid -> Opcode.RET_VOID to intArrayOf() is CmdRetVoid -> Opcode.RET_VOID to intArrayOf()
@ -474,21 +215,11 @@ object CmdDisassembler {
is CmdDeclEnum -> Opcode.DECL_ENUM to intArrayOf(cmd.constId, cmd.slot) is CmdDeclEnum -> Opcode.DECL_ENUM to intArrayOf(cmd.constId, cmd.slot)
is CmdDeclFunction -> Opcode.DECL_FUNCTION to intArrayOf(cmd.constId, cmd.slot) is CmdDeclFunction -> Opcode.DECL_FUNCTION to intArrayOf(cmd.constId, cmd.slot)
is CmdDeclClass -> Opcode.DECL_CLASS to intArrayOf(cmd.constId, cmd.slot) is CmdDeclClass -> Opcode.DECL_CLASS to intArrayOf(cmd.constId, cmd.slot)
is CmdDeclClassField -> Opcode.DECL_CLASS_FIELD to intArrayOf(cmd.constId, cmd.slot)
is CmdDeclClassDelegated -> Opcode.DECL_CLASS_DELEGATED to intArrayOf(cmd.constId, cmd.slot)
is CmdDeclClassInstanceInit -> Opcode.DECL_CLASS_INSTANCE_INIT to intArrayOf(cmd.constId, cmd.slot)
is CmdDeclClassInstanceField -> Opcode.DECL_CLASS_INSTANCE_FIELD to intArrayOf(cmd.constId, cmd.slot)
is CmdDeclClassInstanceProperty -> Opcode.DECL_CLASS_INSTANCE_PROPERTY to intArrayOf(cmd.constId, cmd.slot)
is CmdDeclClassInstanceDelegated -> Opcode.DECL_CLASS_INSTANCE_DELEGATED to intArrayOf(cmd.constId, cmd.slot)
is CmdDeclInstanceField -> Opcode.DECL_INSTANCE_FIELD to intArrayOf(cmd.constId, cmd.slot)
is CmdDeclInstanceProperty -> Opcode.DECL_INSTANCE_PROPERTY to intArrayOf(cmd.constId, cmd.slot)
is CmdDeclInstanceDelegated -> Opcode.DECL_INSTANCE_DELEGATED to intArrayOf(cmd.constId, cmd.slot)
is CmdDeclExtProperty -> Opcode.DECL_EXT_PROPERTY to intArrayOf(cmd.constId, cmd.slot) is CmdDeclExtProperty -> Opcode.DECL_EXT_PROPERTY to intArrayOf(cmd.constId, cmd.slot)
is CmdCallDirect -> Opcode.CALL_DIRECT to intArrayOf(cmd.id, cmd.argBase, cmd.argCount, cmd.dst) is CmdCallDirect -> Opcode.CALL_DIRECT to intArrayOf(cmd.id, cmd.argBase, cmd.argCount, cmd.dst)
is CmdAssignDestructure -> Opcode.ASSIGN_DESTRUCTURE to intArrayOf(cmd.constId, cmd.slot) is CmdAssignDestructure -> Opcode.ASSIGN_DESTRUCTURE to intArrayOf(cmd.constId, cmd.slot)
is CmdCallMemberSlot -> Opcode.CALL_MEMBER_SLOT to intArrayOf(cmd.recvSlot, cmd.methodId, cmd.argBase, cmd.argCount, cmd.dst) is CmdCallMemberSlot -> Opcode.CALL_MEMBER_SLOT to intArrayOf(cmd.recvSlot, cmd.methodId, cmd.argBase, cmd.argCount, cmd.dst)
is CmdCallSlot -> Opcode.CALL_SLOT to intArrayOf(cmd.calleeSlot, cmd.argBase, cmd.argCount, cmd.dst) is CmdCallSlot -> Opcode.CALL_SLOT to intArrayOf(cmd.calleeSlot, cmd.argBase, cmd.argCount, cmd.dst)
is CmdCallBridgeSlot -> Opcode.CALL_BRIDGE_SLOT to intArrayOf(cmd.calleeSlot, cmd.argBase, cmd.argCount, cmd.dst)
is CmdCallDynamicMember -> Opcode.CALL_DYNAMIC_MEMBER to intArrayOf(cmd.recvSlot, cmd.nameId, cmd.argBase, cmd.argCount, cmd.dst) is CmdCallDynamicMember -> Opcode.CALL_DYNAMIC_MEMBER to intArrayOf(cmd.recvSlot, cmd.nameId, cmd.argBase, cmd.argCount, cmd.dst)
is CmdGetIndex -> Opcode.GET_INDEX to intArrayOf(cmd.targetSlot, cmd.indexSlot, cmd.dst) is CmdGetIndex -> Opcode.GET_INDEX to intArrayOf(cmd.targetSlot, cmd.indexSlot, cmd.dst)
is CmdSetIndex -> Opcode.SET_INDEX to intArrayOf(cmd.targetSlot, cmd.indexSlot, cmd.valueSlot) is CmdSetIndex -> Opcode.SET_INDEX to intArrayOf(cmd.targetSlot, cmd.indexSlot, cmd.valueSlot)
@ -521,7 +252,6 @@ object CmdDisassembler {
Opcode.CLEAR_PENDING_THROWABLE, Opcode.RETHROW_PENDING, Opcode.CLEAR_PENDING_THROWABLE, Opcode.RETHROW_PENDING,
Opcode.ITER_POP, Opcode.ITER_CANCEL -> emptyList() Opcode.ITER_POP, Opcode.ITER_CANCEL -> 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.UNBOX_INT_OBJ, Opcode.UNBOX_REAL_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.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)
@ -557,10 +287,7 @@ object CmdDisassembler {
Opcode.PUSH_TRY -> Opcode.PUSH_TRY ->
listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP) listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP)
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE, Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE,
Opcode.DECL_ENUM, Opcode.DECL_FUNCTION, Opcode.DECL_CLASS, Opcode.DECL_CLASS_FIELD, Opcode.DECL_ENUM, Opcode.DECL_FUNCTION, Opcode.DECL_CLASS,
Opcode.DECL_CLASS_DELEGATED, Opcode.DECL_CLASS_INSTANCE_INIT, Opcode.DECL_CLASS_INSTANCE_FIELD,
Opcode.DECL_CLASS_INSTANCE_PROPERTY, Opcode.DECL_CLASS_INSTANCE_DELEGATED, Opcode.DECL_INSTANCE_FIELD,
Opcode.DECL_INSTANCE_PROPERTY, Opcode.DECL_INSTANCE_DELEGATED,
Opcode.ASSIGN_DESTRUCTURE -> Opcode.ASSIGN_DESTRUCTURE ->
listOf(OperandKind.CONST, OperandKind.SLOT) listOf(OperandKind.CONST, OperandKind.SLOT)
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT, Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
@ -575,14 +302,7 @@ object CmdDisassembler {
Opcode.CMP_LTE_INT_REAL, Opcode.CMP_LTE_REAL_INT, Opcode.CMP_GT_INT_REAL, Opcode.CMP_GT_REAL_INT, Opcode.CMP_LTE_INT_REAL, Opcode.CMP_LTE_REAL_INT, Opcode.CMP_GT_INT_REAL, Opcode.CMP_GT_REAL_INT,
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT, Opcode.CMP_NEQ_INT_REAL, Opcode.CMP_NEQ_REAL_INT, Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT, Opcode.CMP_NEQ_INT_REAL, Opcode.CMP_NEQ_REAL_INT,
Opcode.CMP_EQ_OBJ, Opcode.CMP_NEQ_OBJ, Opcode.CMP_REF_EQ_OBJ, Opcode.CMP_REF_NEQ_OBJ, Opcode.CMP_EQ_OBJ, Opcode.CMP_NEQ_OBJ, Opcode.CMP_REF_EQ_OBJ, Opcode.CMP_REF_NEQ_OBJ,
Opcode.CMP_EQ_STR, Opcode.CMP_NEQ_STR, Opcode.CMP_LT_STR, Opcode.CMP_LTE_STR,
Opcode.CMP_GT_STR, Opcode.CMP_GTE_STR,
Opcode.CMP_EQ_INT_OBJ, Opcode.CMP_NEQ_INT_OBJ, Opcode.CMP_LT_INT_OBJ, Opcode.CMP_LTE_INT_OBJ,
Opcode.CMP_GT_INT_OBJ, Opcode.CMP_GTE_INT_OBJ, Opcode.CMP_EQ_REAL_OBJ, Opcode.CMP_NEQ_REAL_OBJ,
Opcode.CMP_LT_REAL_OBJ, Opcode.CMP_LTE_REAL_OBJ, Opcode.CMP_GT_REAL_OBJ, Opcode.CMP_GTE_REAL_OBJ,
Opcode.CMP_LT_OBJ, Opcode.CMP_LTE_OBJ, Opcode.CMP_GT_OBJ, Opcode.CMP_GTE_OBJ, Opcode.CMP_LT_OBJ, Opcode.CMP_LTE_OBJ, Opcode.CMP_GT_OBJ, Opcode.CMP_GTE_OBJ,
Opcode.ADD_INT_OBJ, Opcode.SUB_INT_OBJ, Opcode.MUL_INT_OBJ, Opcode.DIV_INT_OBJ, Opcode.MOD_INT_OBJ,
Opcode.ADD_REAL_OBJ, Opcode.SUB_REAL_OBJ, Opcode.MUL_REAL_OBJ, Opcode.DIV_REAL_OBJ, Opcode.MOD_REAL_OBJ,
Opcode.ADD_OBJ, Opcode.SUB_OBJ, Opcode.MUL_OBJ, Opcode.DIV_OBJ, Opcode.MOD_OBJ, Opcode.CONTAINS_OBJ, Opcode.ADD_OBJ, Opcode.SUB_OBJ, Opcode.MUL_OBJ, Opcode.DIV_OBJ, Opcode.MOD_OBJ, Opcode.CONTAINS_OBJ,
Opcode.AND_BOOL, Opcode.OR_BOOL -> Opcode.AND_BOOL, Opcode.OR_BOOL ->
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT) listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
@ -596,13 +316,9 @@ object CmdDisassembler {
listOf(OperandKind.IP) listOf(OperandKind.IP)
Opcode.JMP_IF_TRUE, Opcode.JMP_IF_FALSE -> Opcode.JMP_IF_TRUE, Opcode.JMP_IF_FALSE ->
listOf(OperandKind.SLOT, OperandKind.IP) listOf(OperandKind.SLOT, OperandKind.IP)
Opcode.JMP_IF_EQ_INT, Opcode.JMP_IF_NEQ_INT,
Opcode.JMP_IF_LT_INT, Opcode.JMP_IF_LTE_INT,
Opcode.JMP_IF_GT_INT, Opcode.JMP_IF_GTE_INT ->
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.IP)
Opcode.CALL_DIRECT -> Opcode.CALL_DIRECT ->
listOf(OperandKind.ID, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT) listOf(OperandKind.ID, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT)
Opcode.CALL_SLOT, Opcode.CALL_BRIDGE_SLOT -> Opcode.CALL_SLOT ->
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT) listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT)
Opcode.CALL_MEMBER_SLOT -> Opcode.CALL_MEMBER_SLOT ->
listOf(OperandKind.SLOT, OperandKind.ID, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT) listOf(OperandKind.SLOT, OperandKind.ID, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com * Copyright 2026 Sergey S. Chernov
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
*/ */
package net.sergeych.lyng.bytecode package net.sergeych.lyng.bytecode
@ -99,36 +98,6 @@ enum class Opcode(val code: Int) {
CMP_NEQ_OBJ(0x6D), CMP_NEQ_OBJ(0x6D),
CMP_REF_EQ_OBJ(0x6E), CMP_REF_EQ_OBJ(0x6E),
CMP_REF_NEQ_OBJ(0x6F), CMP_REF_NEQ_OBJ(0x6F),
CMP_EQ_STR(0xD6),
CMP_NEQ_STR(0xD7),
CMP_LT_STR(0xD8),
CMP_LTE_STR(0xD9),
CMP_GT_STR(0xDA),
CMP_GTE_STR(0xDB),
CMP_EQ_INT_OBJ(0xDC),
CMP_NEQ_INT_OBJ(0xDD),
CMP_LT_INT_OBJ(0xDE),
CMP_LTE_INT_OBJ(0xDF),
CMP_GT_INT_OBJ(0xE0),
CMP_GTE_INT_OBJ(0xE1),
CMP_EQ_REAL_OBJ(0xE2),
CMP_NEQ_REAL_OBJ(0xE3),
CMP_LT_REAL_OBJ(0xE4),
CMP_LTE_REAL_OBJ(0xE5),
CMP_GT_REAL_OBJ(0xE6),
CMP_GTE_REAL_OBJ(0xE7),
UNBOX_INT_OBJ(0xF2),
UNBOX_REAL_OBJ(0xF3),
ADD_INT_OBJ(0xE8),
SUB_INT_OBJ(0xE9),
MUL_INT_OBJ(0xEA),
DIV_INT_OBJ(0xEB),
MOD_INT_OBJ(0xEC),
ADD_REAL_OBJ(0xED),
SUB_REAL_OBJ(0xEE),
MUL_REAL_OBJ(0xEF),
DIV_REAL_OBJ(0xF0),
MOD_REAL_OBJ(0xF1),
NOT_BOOL(0x70), NOT_BOOL(0x70),
AND_BOOL(0x71), AND_BOOL(0x71),
@ -148,12 +117,6 @@ enum class Opcode(val code: Int) {
JMP(0x80), JMP(0x80),
JMP_IF_TRUE(0x81), JMP_IF_TRUE(0x81),
JMP_IF_FALSE(0x82), JMP_IF_FALSE(0x82),
JMP_IF_EQ_INT(0xD0),
JMP_IF_NEQ_INT(0xD1),
JMP_IF_LT_INT(0xD2),
JMP_IF_LTE_INT(0xD3),
JMP_IF_GT_INT(0xD4),
JMP_IF_GTE_INT(0xD5),
RET(0x83), RET(0x83),
RET_VOID(0x84), RET_VOID(0x84),
RET_LABEL(0xBA), RET_LABEL(0xBA),
@ -173,7 +136,6 @@ enum class Opcode(val code: Int) {
ASSIGN_DESTRUCTURE(0x91), ASSIGN_DESTRUCTURE(0x91),
CALL_MEMBER_SLOT(0x92), CALL_MEMBER_SLOT(0x92),
CALL_SLOT(0x93), CALL_SLOT(0x93),
CALL_BRIDGE_SLOT(0x94),
GET_INDEX(0xA2), GET_INDEX(0xA2),
SET_INDEX(0xA3), SET_INDEX(0xA3),
@ -206,15 +168,6 @@ enum class Opcode(val code: Int) {
BIND_DELEGATE_LOCAL(0xC4), BIND_DELEGATE_LOCAL(0xC4),
DECL_FUNCTION(0xC5), DECL_FUNCTION(0xC5),
DECL_CLASS(0xC6), DECL_CLASS(0xC6),
DECL_CLASS_FIELD(0xC7),
DECL_CLASS_DELEGATED(0xC8),
DECL_CLASS_INSTANCE_INIT(0xC9),
DECL_CLASS_INSTANCE_FIELD(0xCA),
DECL_CLASS_INSTANCE_PROPERTY(0xCB),
DECL_CLASS_INSTANCE_DELEGATED(0xCC),
DECL_INSTANCE_FIELD(0xCD),
DECL_INSTANCE_PROPERTY(0xCE),
DECL_INSTANCE_DELEGATED(0xCF),
; ;
companion object { companion object {

View File

@ -1,44 +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
import net.sergeych.lyng.Scope
import net.sergeych.lyng.obj.ObjRecord
internal suspend fun seedFrameLocalsFromScope(frame: CmdFrame, scope: Scope) {
val localNames = frame.fn.localSlotNames
if (localNames.isEmpty()) return
val base = frame.fn.scopeSlotCount
for (i in localNames.indices) {
val name = localNames[i] ?: continue
val slotType = frame.getLocalSlotTypeCode(i)
if (slotType != SlotType.UNKNOWN.code && slotType != SlotType.OBJ.code) continue
if (slotType == SlotType.OBJ.code && frame.frame.getRawObj(i) != null) continue
val record = scope.getLocalRecordDirect(name)
?: scope.chainLookupIgnoreClosure(name, followClosure = true)
?: continue
val value = if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property) {
scope.resolve(record, name)
} else {
record.value
}
if (value is net.sergeych.lyng.FrameSlotRef && value.refersTo(frame.frame, base + i)) {
continue
}
frame.setObjUnchecked(base + i, value)
}
}

View File

@ -74,30 +74,29 @@ object CompletionEngineLight {
val word = DocLookupUtils.wordRangeAt(text, caret) val word = DocLookupUtils.wordRangeAt(text, caret)
val memberDot = DocLookupUtils.findDotLeft(text, word?.first ?: caret) val memberDot = DocLookupUtils.findDotLeft(text, word?.first ?: caret)
if (memberDot != null) { if (memberDot != null) {
val staticOnly = DocLookupUtils.isStaticReceiver(mini, text, memberDot, imported, binding)
val inferredCls = (DocLookupUtils.guessReturnClassFromMemberCallBeforeMini(mini, text, memberDot, imported, binding) ?: DocLookupUtils.guessReceiverClass(text, memberDot, imported, mini)) val inferredCls = (DocLookupUtils.guessReturnClassFromMemberCallBeforeMini(mini, text, memberDot, imported, binding) ?: DocLookupUtils.guessReceiverClass(text, memberDot, imported, mini))
// 0) Try chained member call return type inference // 0) Try chained member call return type inference
DocLookupUtils.guessReturnClassFromMemberCallBeforeMini(mini, text, memberDot, imported, binding)?.let { cls -> DocLookupUtils.guessReturnClassFromMemberCallBeforeMini(mini, text, memberDot, imported, binding)?.let { cls ->
offerMembersAdd(out, prefix, imported, cls, mini, staticOnly) offerMembersAdd(out, prefix, imported, cls, mini)
return out return out
} }
DocLookupUtils.guessReturnClassFromMemberCallBefore(text, memberDot, imported, mini)?.let { cls -> DocLookupUtils.guessReturnClassFromMemberCallBefore(text, memberDot, imported, mini)?.let { cls ->
offerMembersAdd(out, prefix, imported, cls, mini, staticOnly) offerMembersAdd(out, prefix, imported, cls, mini)
return out return out
} }
// 0a) Top-level call before dot // 0a) Top-level call before dot
DocLookupUtils.guessReturnClassFromTopLevelCallBefore(text, memberDot, imported, mini)?.let { cls -> DocLookupUtils.guessReturnClassFromTopLevelCallBefore(text, memberDot, imported, mini)?.let { cls ->
offerMembersAdd(out, prefix, imported, cls, mini, staticOnly) offerMembersAdd(out, prefix, imported, cls, mini)
return out return out
} }
// 0b) Across-known-callees (Iterable/Iterator/List preference) // 0b) Across-known-callees (Iterable/Iterator/List preference)
DocLookupUtils.guessReturnClassAcrossKnownCallees(text, memberDot, imported, mini)?.let { cls -> DocLookupUtils.guessReturnClassAcrossKnownCallees(text, memberDot, imported, mini)?.let { cls ->
offerMembersAdd(out, prefix, imported, cls, mini, staticOnly) offerMembersAdd(out, prefix, imported, cls, mini)
return out return out
} }
// 1) Receiver inference fallback // 1) Receiver inference fallback
(DocLookupUtils.guessReceiverClassViaMini(mini, text, memberDot, imported, binding) ?: DocLookupUtils.guessReceiverClass(text, memberDot, imported, mini))?.let { cls -> (DocLookupUtils.guessReceiverClassViaMini(mini, text, memberDot, imported, binding) ?: DocLookupUtils.guessReceiverClass(text, memberDot, imported, mini))?.let { cls ->
offerMembersAdd(out, prefix, imported, cls, mini, staticOnly) offerMembersAdd(out, prefix, imported, cls, mini)
return out return out
} }
// In member context and unknown receiver/return type: show nothing (no globals after dot) // In member context and unknown receiver/return type: show nothing (no globals after dot)
@ -107,20 +106,10 @@ object CompletionEngineLight {
// Global identifiers: params > local decls > imported > stdlib; Functions > Classes > Values; alphabetical // Global identifiers: params > local decls > imported > stdlib; Functions > Classes > Values; alphabetical
offerParamsInScope(out, prefix, mini, text, caret) offerParamsInScope(out, prefix, mini, text, caret)
val localsFromBinding = DocLookupUtils.collectLocalsFromBinding(mini, binding, caret) val locals = DocLookupUtils.extractLocalsAt(text, caret)
if (localsFromBinding.isNotEmpty()) { for (name in locals) {
for (sym in localsFromBinding) { if (name.startsWith(prefix, true)) {
if (sym.name.startsWith(prefix, true)) { out.add(CompletionItem(name, Kind.Value, priority = 150.0))
val t = sym.type?.let { ": $it" }
out.add(CompletionItem(sym.name, Kind.Value, typeText = t, priority = 150.0))
}
}
} else {
val locals = DocLookupUtils.extractLocalsAt(text, caret)
for (name in locals) {
if (name.startsWith(prefix, true)) {
out.add(CompletionItem(name, Kind.Value, priority = 150.0))
}
} }
} }
@ -249,7 +238,7 @@ object CompletionEngineLight {
} }
} }
private fun offerMembersAdd(out: MutableList<CompletionItem>, prefix: String, imported: List<String>, className: String, mini: MiniScript? = null, staticOnly: Boolean = false) { private fun offerMembersAdd(out: MutableList<CompletionItem>, prefix: String, imported: List<String>, className: String, mini: MiniScript? = null) {
val classes = DocLookupUtils.aggregateClasses(imported, mini) val classes = DocLookupUtils.aggregateClasses(imported, mini)
val visited = mutableSetOf<String>() val visited = mutableSetOf<String>()
val directMap = LinkedHashMap<String, MutableList<MiniMemberDecl>>() val directMap = LinkedHashMap<String, MutableList<MiniMemberDecl>>()
@ -258,15 +247,10 @@ object CompletionEngineLight {
fun addMembersOf(name: String, direct: Boolean) { fun addMembersOf(name: String, direct: Boolean) {
val cls = classes[name] ?: return val cls = classes[name] ?: return
val target = if (direct) directMap else inheritedMap val target = if (direct) directMap else inheritedMap
if (!staticOnly) { for (cf in cls.ctorFields + cls.classFields) {
for (cf in cls.ctorFields + cls.classFields) { target.getOrPut(cf.name) { mutableListOf() }.add(DocLookupUtils.toMemberVal(cf))
target.getOrPut(cf.name) { mutableListOf() }.add(DocLookupUtils.toMemberVal(cf))
}
}
for (m in cls.members) {
if (staticOnly && !m.isStatic) continue
target.getOrPut(m.name) { mutableListOf() }.add(m)
} }
for (m in cls.members) target.getOrPut(m.name) { mutableListOf() }.add(m)
for (b in cls.bases) if (visited.add(b)) addMembersOf(b, false) for (b in cls.bases) if (visited.add(b)) addMembersOf(b, false)
} }
@ -326,7 +310,7 @@ object CompletionEngineLight {
emitGroup(inheritedMap, 0.0) emitGroup(inheritedMap, 0.0)
// Supplement with extension members (both stdlib and local) // Supplement with extension members (both stdlib and local)
if (!staticOnly) run { run {
val already = (directMap.keys + inheritedMap.keys).toMutableSet() val already = (directMap.keys + inheritedMap.keys).toMutableSet()
val extensions = DocLookupUtils.collectExtensionMemberNames(imported, className, mini) val extensions = DocLookupUtils.collectExtensionMemberNames(imported, className, mini)
for (name in extensions) { for (name in extensions) {

View File

@ -20,7 +20,6 @@
*/ */
package net.sergeych.lyng.miniast package net.sergeych.lyng.miniast
import net.sergeych.lyng.Pos
import net.sergeych.lyng.binding.BindingSnapshot import net.sergeych.lyng.binding.BindingSnapshot
import net.sergeych.lyng.highlight.offsetOf import net.sergeych.lyng.highlight.offsetOf
@ -302,42 +301,32 @@ object DocLookupUtils {
isExtern = false isExtern = false
) )
fun resolveMemberWithInheritance( fun resolveMemberWithInheritance(importedModules: List<String>, className: String, member: String, localMini: MiniScript? = null): Pair<String, MiniNamedDecl>? {
importedModules: List<String>,
className: String,
member: String,
localMini: MiniScript? = null,
staticOnly: Boolean = false
): Pair<String, MiniNamedDecl>? {
val classes = aggregateClasses(importedModules, localMini) val classes = aggregateClasses(importedModules, localMini)
fun dfs(name: String, visited: MutableSet<String>): Pair<String, MiniNamedDecl>? { fun dfs(name: String, visited: MutableSet<String>): Pair<String, MiniNamedDecl>? {
if (!visited.add(name)) return null if (!visited.add(name)) return null
val cls = classes[name] val cls = classes[name]
if (cls != null) { if (cls != null) {
cls.members.firstOrNull { it.name == member && (!staticOnly || it.isStatic) }?.let { return name to it } cls.members.firstOrNull { it.name == member }?.let { return name to it }
if (!staticOnly) { cls.ctorFields.firstOrNull { it.name == member }?.let { return name to toMemberVal(it) }
cls.ctorFields.firstOrNull { it.name == member }?.let { return name to toMemberVal(it) } cls.classFields.firstOrNull { it.name == member }?.let { return name to toMemberVal(it) }
cls.classFields.firstOrNull { it.name == member }?.let { return name to toMemberVal(it) }
}
for (baseName in cls.bases) { for (baseName in cls.bases) {
dfs(baseName, visited)?.let { return it } dfs(baseName, visited)?.let { return it }
} }
} }
if (!staticOnly) { // 1) local extensions in this class or bases
// 1) local extensions in this class or bases localMini?.declarations?.firstOrNull { d ->
localMini?.declarations?.firstOrNull { d -> (d is MiniFunDecl && d.receiver != null && simpleClassNameOf(d.receiver) == name && d.name == member) ||
(d is MiniValDecl && d.receiver != null && simpleClassNameOf(d.receiver) == name && d.name == member)
}?.let { return name to it as MiniNamedDecl }
// 2) built-in extensions from BuiltinDocRegistry
for (mod in importedModules) {
val decls = BuiltinDocRegistry.docsForModule(mod)
decls.firstOrNull { d ->
(d is MiniFunDecl && d.receiver != null && simpleClassNameOf(d.receiver) == name && d.name == member) || (d is MiniFunDecl && d.receiver != null && simpleClassNameOf(d.receiver) == name && d.name == member) ||
(d is MiniValDecl && d.receiver != null && simpleClassNameOf(d.receiver) == name && d.name == member) (d is MiniValDecl && d.receiver != null && simpleClassNameOf(d.receiver) == name && d.name == member)
}?.let { return name to it as MiniNamedDecl } }?.let { return name to it as MiniNamedDecl }
// 2) built-in extensions from BuiltinDocRegistry
for (mod in importedModules) {
val decls = BuiltinDocRegistry.docsForModule(mod)
decls.firstOrNull { d ->
(d is MiniFunDecl && d.receiver != null && simpleClassNameOf(d.receiver) == name && d.name == member) ||
(d is MiniValDecl && d.receiver != null && simpleClassNameOf(d.receiver) == name && d.name == member)
}?.let { return name to it as MiniNamedDecl }
}
} }
return null return null
@ -441,7 +430,6 @@ object DocLookupUtils {
if (ref != null) { if (ref != null) {
val sym = binding.symbols.firstOrNull { it.id == ref.symbolId } val sym = binding.symbols.firstOrNull { it.id == ref.symbolId }
if (sym != null) { if (sym != null) {
simpleClassNameOfType(sym.type)?.let { return it }
val type = findTypeByRange(mini, sym.name, sym.declStart, text, imported) val type = findTypeByRange(mini, sym.name, sym.declStart, text, imported)
simpleClassNameOf(type)?.let { return it } simpleClassNameOf(type)?.let { return it }
} }
@ -449,7 +437,6 @@ object DocLookupUtils {
// Check if it's a declaration (e.g. static access to a class) // Check if it's a declaration (e.g. static access to a class)
val sym = binding.symbols.firstOrNull { it.declStart == wordRange.first && it.name == ident } val sym = binding.symbols.firstOrNull { it.declStart == wordRange.first && it.name == ident }
if (sym != null) { if (sym != null) {
simpleClassNameOfType(sym.type)?.let { return it }
val type = findTypeByRange(mini, sym.name, sym.declStart, text, imported) val type = findTypeByRange(mini, sym.name, sym.declStart, text, imported)
simpleClassNameOf(type)?.let { return it } simpleClassNameOf(type)?.let { return it }
// if it's a class/enum, return its name directly // if it's a class/enum, return its name directly
@ -1055,16 +1042,6 @@ object DocLookupUtils {
is MiniTypeIntersection -> null is MiniTypeIntersection -> null
} }
fun simpleClassNameOfType(type: String?): String? {
if (type.isNullOrBlank()) return null
var t = type.trim()
if (t.endsWith("?")) t = t.dropLast(1)
val first = t.split('|', '&').firstOrNull()?.trim() ?: return null
val base = first.substringBefore('<').trim()
val short = base.substringAfterLast('.').trim()
return short.ifBlank { null }
}
fun typeOf(t: MiniTypeRef?): String = when (t) { fun typeOf(t: MiniTypeRef?): String = when (t) {
is MiniTypeName -> t.segments.joinToString(".") { it.name } + (if (t.nullable) "?" else "") is MiniTypeName -> t.segments.joinToString(".") { it.name } + (if (t.nullable) "?" else "")
is MiniGenericType -> typeOf(t.base) + "<" + t.args.joinToString(", ") { typeOf(it) } + ">" + (if (t.nullable) "?" else "") is MiniGenericType -> typeOf(t.base) + "<" + t.args.joinToString(", ") { typeOf(it) } + ">" + (if (t.nullable) "?" else "")
@ -1078,90 +1055,6 @@ object DocLookupUtils {
null -> "" null -> ""
} }
fun collectLocalsFromBinding(mini: MiniScript?, binding: BindingSnapshot?, offset: Int): List<net.sergeych.lyng.binding.Symbol> {
if (mini == null || binding == null) return emptyList()
val src = mini.range.start.source
data class FnCtx(val nameStart: Pos, val body: MiniRange)
fun consider(nameStart: Pos, body: MiniRange?, best: FnCtx?): FnCtx? {
if (body == null) return best
val start = src.offsetOf(body.start)
val end = src.offsetOf(body.end)
if (offset < start || offset > end) return best
val len = end - start
val bestLen = best?.let { src.offsetOf(it.body.end) - src.offsetOf(it.body.start) } ?: Int.MAX_VALUE
return if (len < bestLen) FnCtx(nameStart, body) else best
}
var best: FnCtx? = null
for (d in mini.declarations) {
when (d) {
is MiniFunDecl -> best = consider(d.nameStart, d.body?.range, best)
is MiniClassDecl -> {
for (m in d.members) {
if (m is MiniMemberFunDecl) {
best = consider(m.nameStart, m.body?.range, best)
}
}
}
else -> {}
}
}
val fn = best ?: return emptyList()
val fnDeclStart = src.offsetOf(fn.nameStart)
val fnSym = binding.symbols.firstOrNull {
it.kind == net.sergeych.lyng.binding.SymbolKind.Function && it.declStart == fnDeclStart
} ?: return emptyList()
return binding.symbols.filter {
it.containerId == fnSym.id &&
(it.kind == net.sergeych.lyng.binding.SymbolKind.Parameter ||
it.kind == net.sergeych.lyng.binding.SymbolKind.Value ||
it.kind == net.sergeych.lyng.binding.SymbolKind.Variable) &&
it.declStart < offset
}
}
fun isStaticReceiver(
mini: MiniScript?,
text: String,
dotPos: Int,
importedModules: List<String>,
binding: BindingSnapshot? = null
): Boolean {
val i = prevNonWs(text, dotPos - 1)
if (i < 0 || !isIdentChar(text[i])) return false
val wordRange = wordRangeAt(text, i + 1) ?: return false
val ident = text.substring(wordRange.first, wordRange.second)
if (ident == "this") return false
if (binding != null) {
val ref = binding.references.firstOrNull { wordRange.first >= it.start && wordRange.first < it.end }
val sym = ref?.let { r -> binding.symbols.firstOrNull { it.id == r.symbolId } }
?: binding.symbols.firstOrNull { it.declStart == wordRange.first && it.name == ident }
if (sym != null) {
return sym.kind == net.sergeych.lyng.binding.SymbolKind.Class ||
sym.kind == net.sergeych.lyng.binding.SymbolKind.Enum ||
sym.kind == net.sergeych.lyng.binding.SymbolKind.TypeAlias
}
}
if (mini != null) {
val src = mini.range.start.source
val decl = mini.declarations
.filter { it.name == ident && src.offsetOf(it.nameStart) < dotPos }
.maxByOrNull { src.offsetOf(it.nameStart) }
if (decl is MiniClassDecl || decl is MiniEnumDecl || decl is MiniTypeAliasDecl) return true
if (decl is MiniFunDecl || decl is MiniValDecl) return false
}
val classes = aggregateClasses(importedModules, mini)
if (classes.containsKey(ident)) return true
for (mod in importedModules) {
val aliases = BuiltinDocRegistry.docsForModule(mod).filterIsInstance<MiniTypeAliasDecl>()
if (aliases.any { it.name == ident }) return true
}
return false
}
fun findDotLeft(text: String, offset: Int): Int? { fun findDotLeft(text: String, offset: Int): Int? {
var i = (offset - 1).coerceAtLeast(0) var i = (offset - 1).coerceAtLeast(0)
while (i >= 0 && text[i].isWhitespace()) i-- while (i >= 0 && text[i].isWhitespace()) i--

View File

@ -19,7 +19,6 @@ package net.sergeych.lyng.miniast
import net.sergeych.lyng.ModuleScope import net.sergeych.lyng.ModuleScope
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.ScopeFacade
import net.sergeych.lyng.Visibility import net.sergeych.lyng.Visibility
import net.sergeych.lyng.obj.Obj import net.sergeych.lyng.obj.Obj
import net.sergeych.lyng.obj.ObjClass import net.sergeych.lyng.obj.ObjClass
@ -41,7 +40,7 @@ inline fun <reified T : Obj> Scope.addFnDoc(
tags: Map<String, List<String>> = emptyMap(), tags: Map<String, List<String>> = emptyMap(),
moduleName: String? = null, moduleName: String? = null,
callSignature: net.sergeych.lyng.CallSignature? = null, callSignature: net.sergeych.lyng.CallSignature? = null,
crossinline fn: suspend ScopeFacade.() -> T crossinline fn: suspend Scope.() -> T
) { ) {
// Register runtime function(s) // Register runtime function(s)
addFn(*names, callSignature = callSignature) { fn() } addFn(*names, callSignature = callSignature) { fn() }
@ -58,7 +57,7 @@ inline fun Scope.addVoidFnDoc(
doc: String, doc: String,
tags: Map<String, List<String>> = emptyMap(), tags: Map<String, List<String>> = emptyMap(),
moduleName: String? = null, moduleName: String? = null,
crossinline fn: suspend ScopeFacade.() -> Unit crossinline fn: suspend Scope.() -> Unit
) { ) {
addFnDoc<ObjVoid>( addFnDoc<ObjVoid>(
*names, *names,
@ -99,7 +98,7 @@ fun ObjClass.addFnDoc(
visibility: Visibility = Visibility.Public, visibility: Visibility = Visibility.Public,
tags: Map<String, List<String>> = emptyMap(), tags: Map<String, List<String>> = emptyMap(),
moduleName: String? = null, moduleName: String? = null,
code: suspend ScopeFacade.() -> Obj code: suspend Scope.() -> Obj
) { ) {
// Register runtime method // Register runtime method
addFn(name, isOpen, visibility, code = code) addFn(name, isOpen, visibility, code = code)
@ -137,7 +136,7 @@ fun ObjClass.addClassFnDoc(
isOpen: Boolean = false, isOpen: Boolean = false,
tags: Map<String, List<String>> = emptyMap(), tags: Map<String, List<String>> = emptyMap(),
moduleName: String? = null, moduleName: String? = null,
code: suspend ScopeFacade.() -> Obj code: suspend Scope.() -> Obj
) { ) {
addClassFn(name, isOpen, code) addClassFn(name, isOpen, code)
BuiltinDocRegistry.module(moduleName ?: ownerModuleNameFromClassOrUnknown()) { BuiltinDocRegistry.module(moduleName ?: ownerModuleNameFromClassOrUnknown()) {
@ -153,8 +152,8 @@ fun ObjClass.addPropertyDoc(
type: TypeDoc? = null, type: TypeDoc? = null,
visibility: Visibility = Visibility.Public, visibility: Visibility = Visibility.Public,
moduleName: String? = null, moduleName: String? = null,
getter: (suspend ScopeFacade.() -> Obj)? = null, getter: (suspend Scope.() -> Obj)? = null,
setter: (suspend ScopeFacade.(Obj) -> Unit)? = null setter: (suspend Scope.(Obj) -> Unit)? = null
) { ) {
addProperty(name, getter, setter, visibility) addProperty(name, getter, setter, visibility)
BuiltinDocRegistry.module(moduleName ?: ownerModuleNameFromClassOrUnknown()) { BuiltinDocRegistry.module(moduleName ?: ownerModuleNameFromClassOrUnknown()) {

View File

@ -20,6 +20,7 @@ package net.sergeych.lyng.obj
import net.sergeych.lyng.Compiler import net.sergeych.lyng.Compiler
import net.sergeych.lyng.Pos import net.sergeych.lyng.Pos
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.ScriptError
// avoid KDOC bug: keep it // avoid KDOC bug: keep it
@Suppress("unused") @Suppress("unused")
@ -36,11 +37,10 @@ private class LambdaRef(
private val getterFn: suspend (Scope) -> ObjRecord, private val getterFn: suspend (Scope) -> ObjRecord,
private val setterFn: (suspend (Pos, Scope, Obj) -> Unit)? = null private val setterFn: (suspend (Pos, Scope, Obj) -> Unit)? = null
) : ObjRef { ) : ObjRef {
override suspend fun get(scope: Scope): ObjRecord { override suspend fun get(scope: Scope): ObjRecord = getterFn(scope)
return scope.raiseIllegalState("bytecode-only execution is required; Accessor evaluation is disabled")
}
override suspend fun setAt(pos: Pos, scope: Scope, newValue: Obj) { override suspend fun setAt(pos: Pos, scope: Scope, newValue: Obj) {
scope.raiseIllegalState("bytecode-only execution is required; Accessor assignment is disabled") val s = setterFn ?: throw ScriptError(pos, "can't assign value")
s(pos, scope, newValue)
} }
} }

View File

@ -20,14 +20,12 @@ import net.sergeych.lyng.ArgsDeclaration
import net.sergeych.lyng.Pos import net.sergeych.lyng.Pos
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.bytecode.CmdFunction import net.sergeych.lyng.bytecode.CmdFunction
import net.sergeych.lyng.bytecode.LambdaCaptureEntry
class LambdaFnRef( class LambdaFnRef(
valueFn: suspend (Scope) -> ObjRecord, valueFn: suspend (Scope) -> ObjRecord,
val bytecodeFn: CmdFunction?, val bytecodeFn: CmdFunction?,
val paramSlotPlan: Map<String, Int>, val paramSlotPlan: Map<String, Int>,
val argsDeclaration: ArgsDeclaration?, val argsDeclaration: ArgsDeclaration?,
val captureEntries: List<LambdaCaptureEntry>,
val preferredThisType: String?, val preferredThisType: String?,
val wrapAsExtensionCallable: Boolean, val wrapAsExtensionCallable: Boolean,
val returnLabels: Set<String>, val returnLabels: Set<String>,

View File

@ -265,35 +265,13 @@ open class Obj {
open val objClass: ObjClass get() = rootObjectType open val objClass: ObjClass get() = rootObjectType
open suspend fun plus(scope: Scope, other: Obj): Obj { open suspend fun plus(scope: Scope, other: Obj): Obj {
val otherValue = when (other) { return invokeInstanceMethod(scope, "plus", Arguments(other)) {
is FrameSlotRef -> other.read()
is RecordSlotRef -> other.read()
else -> other
}
val self = when (this) {
is FrameSlotRef -> this.read()
is RecordSlotRef -> this.read()
else -> this
}
if (self !== this) return self.plus(scope, otherValue)
return invokeInstanceMethod(scope, "plus", Arguments(otherValue)) {
scope.raiseNotImplemented("plus for ${objClass.className}") scope.raiseNotImplemented("plus for ${objClass.className}")
} }
} }
open suspend fun minus(scope: Scope, other: Obj): Obj { open suspend fun minus(scope: Scope, other: Obj): Obj {
val otherValue = when (other) { return invokeInstanceMethod(scope, "minus", Arguments(other)) {
is FrameSlotRef -> other.read()
is RecordSlotRef -> other.read()
else -> other
}
val self = when (this) {
is FrameSlotRef -> this.read()
is RecordSlotRef -> this.read()
else -> this
}
if (self !== this) return self.minus(scope, otherValue)
return invokeInstanceMethod(scope, "minus", Arguments(otherValue)) {
scope.raiseNotImplemented("minus for ${objClass.className}") scope.raiseNotImplemented("minus for ${objClass.className}")
} }
} }
@ -305,52 +283,19 @@ open class Obj {
} }
open suspend fun mul(scope: Scope, other: Obj): Obj { open suspend fun mul(scope: Scope, other: Obj): Obj {
val otherValue = when (other) { return invokeInstanceMethod(scope, "mul", Arguments(other)) {
is FrameSlotRef -> other.read()
is RecordSlotRef -> other.read()
else -> other
}
val self = when (this) {
is FrameSlotRef -> this.read()
is RecordSlotRef -> this.read()
else -> this
}
if (self !== this) return self.mul(scope, otherValue)
return invokeInstanceMethod(scope, "mul", Arguments(otherValue)) {
scope.raiseNotImplemented("mul for ${objClass.className}") scope.raiseNotImplemented("mul for ${objClass.className}")
} }
} }
open suspend fun div(scope: Scope, other: Obj): Obj { open suspend fun div(scope: Scope, other: Obj): Obj {
val otherValue = when (other) { return invokeInstanceMethod(scope, "div", Arguments(other)) {
is FrameSlotRef -> other.read()
is RecordSlotRef -> other.read()
else -> other
}
val self = when (this) {
is FrameSlotRef -> this.read()
is RecordSlotRef -> this.read()
else -> this
}
if (self !== this) return self.div(scope, otherValue)
return invokeInstanceMethod(scope, "div", Arguments(otherValue)) {
scope.raiseNotImplemented("div for ${objClass.className}") scope.raiseNotImplemented("div for ${objClass.className}")
} }
} }
open suspend fun mod(scope: Scope, other: Obj): Obj { open suspend fun mod(scope: Scope, other: Obj): Obj {
val otherValue = when (other) { return invokeInstanceMethod(scope, "mod", Arguments(other)) {
is FrameSlotRef -> other.read()
is RecordSlotRef -> other.read()
else -> other
}
val self = when (this) {
is FrameSlotRef -> this.read()
is RecordSlotRef -> this.read()
else -> this
}
if (self !== this) return self.mod(scope, otherValue)
return invokeInstanceMethod(scope, "mod", Arguments(otherValue)) {
scope.raiseNotImplemented("mod for ${objClass.className}") scope.raiseNotImplemented("mod for ${objClass.className}")
} }
} }
@ -502,7 +447,7 @@ open class Obj {
caller.members[name]?.let { rec -> caller.members[name]?.let { rec ->
if (rec.visibility == Visibility.Private && !rec.isAbstract) { if (rec.visibility == Visibility.Private && !rec.isAbstract) {
val resolved = resolveRecord(scope, rec, name, caller) val resolved = resolveRecord(scope, rec, name, caller)
if (resolved.type == ObjRecord.Type.Fun) if (resolved.type == ObjRecord.Type.Fun && resolved.value is Statement)
return resolved.copy(value = resolved.value.invoke(scope, this, Arguments.EMPTY, caller)) return resolved.copy(value = resolved.value.invoke(scope, this, Arguments.EMPTY, caller))
return resolved return resolved
} }
@ -516,7 +461,7 @@ open class Obj {
if (rec != null && !rec.isAbstract) { if (rec != null && !rec.isAbstract) {
val decl = rec.declaringClass ?: cls val decl = rec.declaringClass ?: cls
val resolved = resolveRecord(scope, rec, name, decl) val resolved = resolveRecord(scope, rec, name, decl)
if (resolved.type == ObjRecord.Type.Fun) if (resolved.type == ObjRecord.Type.Fun && resolved.value is Statement)
return resolved.copy(value = resolved.value.invoke(scope, this, Arguments.EMPTY, decl)) return resolved.copy(value = resolved.value.invoke(scope, this, Arguments.EMPTY, decl))
return resolved return resolved
} }
@ -531,7 +476,7 @@ open class Obj {
if (!canAccessMember(rec.visibility, decl, caller, name)) if (!canAccessMember(rec.visibility, decl, caller, name))
scope.raiseError(ObjIllegalAccessException(scope, "can't access field ${name}: not visible (declared in ${decl.className}, caller ${caller?.className ?: "?"})")) scope.raiseError(ObjIllegalAccessException(scope, "can't access field ${name}: not visible (declared in ${decl.className}, caller ${caller?.className ?: "?"})"))
val resolved = resolveRecord(scope, rec, name, decl) val resolved = resolveRecord(scope, rec, name, decl)
if (resolved.type == ObjRecord.Type.Fun) if (resolved.type == ObjRecord.Type.Fun && resolved.value is Statement)
return resolved.copy(value = resolved.value.invoke(scope, this, Arguments.EMPTY, decl)) return resolved.copy(value = resolved.value.invoke(scope, this, Arguments.EMPTY, decl))
return resolved return resolved
} }
@ -555,17 +500,16 @@ open class Obj {
if (obj.type == ObjRecord.Type.Delegated) { if (obj.type == ObjRecord.Type.Delegated) {
val del = obj.delegate ?: scope.raiseError("Internal error: delegated property $name has no delegate") val del = obj.delegate ?: scope.raiseError("Internal error: delegated property $name has no delegate")
val th = if (this === ObjVoid) ObjNull else this val th = if (this === ObjVoid) ObjNull else this
val getValueRec = when (del) { val getValueRec = del.objClass.getInstanceMemberOrNull("getValue")
is ObjInstance -> del.methodRecordForKey("getValue")
?: del.instanceScope.objects["getValue"]
?: del.objClass.getInstanceMemberOrNull("getValue")
else -> del.objClass.getInstanceMemberOrNull("getValue")
}
if (getValueRec == null || getValueRec.declaringClass?.className == "Delegate") { if (getValueRec == null || getValueRec.declaringClass?.className == "Delegate") {
val wrapper = ObjExternCallable.fromBridge { val wrapper = object : Statement() {
val th2 = if (thisObj === ObjVoid) ObjNull else thisObj override val pos: Pos = Pos.builtIn
val allArgs = (listOf(th2, ObjString(name)) + args.list).toTypedArray()
del.invokeInstanceMethod(requireScope(), "invoke", Arguments(*allArgs)) override suspend fun execute(s: Scope): Obj {
val th2 = if (s.thisObj === ObjVoid) ObjNull else s.thisObj
val allArgs = (listOf(th2, ObjString(name)) + s.args.list).toTypedArray()
return del.invokeInstanceMethod(s, "invoke", Arguments(*allArgs))
}
} }
return obj.copy( return obj.copy(
value = wrapper, value = wrapper,
@ -573,11 +517,14 @@ open class Obj {
) )
} }
val res = del.invokeInstanceMethod(scope, "getValue", Arguments(th, ObjString(name))) val res = del.invokeInstanceMethod(scope, "getValue", Arguments(th, ObjString(name)))
return obj.copy(value = res, type = ObjRecord.Type.Other) return obj.copy(
value = res,
type = ObjRecord.Type.Other
)
} }
val value = obj.value val value = obj.value
if (value is ObjProperty || obj.type == ObjRecord.Type.Property) { if (value is ObjProperty || obj.type == ObjRecord.Type.Property) {
val prop = (value as? ObjProperty) val prop = if (value is ObjProperty) value else (value as? Statement)?.execute(scope.createChildScope(scope.pos, newThisObj = this)) as? ObjProperty
?: scope.raiseError("Expected ObjProperty for property member $name, got ${value::class}") ?: scope.raiseError("Expected ObjProperty for property member $name, got ${value::class}")
val res = prop.callGetter(scope, this, decl) val res = prop.callGetter(scope, this, decl)
return ObjRecord(res, obj.isMutable) return ObjRecord(res, obj.isMutable)
@ -740,7 +687,7 @@ open class Obj {
returns = type("lyng.String"), returns = type("lyng.String"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
toStringOf(thisObj, true) thisObj.toString(this, true)
} }
addFnDoc( addFnDoc(
name = "inspect", name = "inspect",
@ -748,7 +695,7 @@ open class Obj {
returns = type("lyng.String"), returns = type("lyng.String"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
inspect(thisObj).toObj() thisObj.inspect(this).toObj()
} }
addFnDoc( addFnDoc(
name = "contains", name = "contains",
@ -757,7 +704,7 @@ open class Obj {
returns = type("lyng.Bool"), returns = type("lyng.Bool"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
ObjBool(thisObj.contains(requireScope(), args.firstAndOnly())) ObjBool(thisObj.contains(this, args.firstAndOnly()))
} }
// utilities // utilities
addFnDoc( addFnDoc(
@ -766,7 +713,7 @@ open class Obj {
params = listOf(ParamDoc("block")), params = listOf(ParamDoc("block")),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
call(args.firstAndOnly(), Arguments(thisObj)) args.firstAndOnly().callOn(createChildScope(Arguments(thisObj)))
} }
addFnDoc( addFnDoc(
name = "apply", name = "apply",
@ -775,12 +722,11 @@ open class Obj {
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val body = args.firstAndOnly() val body = args.firstAndOnly()
val scope = requireScope()
(thisObj as? ObjInstance)?.let { (thisObj as? ObjInstance)?.let {
body.callOn(ApplyScope(scope, it.instanceScope)) body.callOn(ApplyScope(this, it.instanceScope))
} ?: run { } ?: run {
val appliedScope = scope.createChildScope(newThisObj = thisObj) val appliedScope = createChildScope(newThisObj = thisObj)
body.callOn(ApplyScope(scope, appliedScope)) body.callOn(ApplyScope(this, appliedScope))
} }
thisObj thisObj
} }
@ -790,7 +736,7 @@ open class Obj {
params = listOf(ParamDoc("block")), params = listOf(ParamDoc("block")),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
call(args.firstAndOnly(), Arguments(thisObj)) args.firstAndOnly().callOn(createChildScope(Arguments(thisObj)))
thisObj thisObj
} }
addFnDoc( addFnDoc(
@ -799,33 +745,25 @@ open class Obj {
params = listOf(ParamDoc("block")), params = listOf(ParamDoc("block")),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
call(args.firstAndOnly()) args.firstAndOnly().callOn(this)
} }
addFn("getAt") { addFn("getAt") {
requireExactCount(1) requireExactCount(1)
thisObj.getAt(requireScope(), requiredArg<Obj>(0)) thisObj.getAt(this, requiredArg<Obj>(0))
} }
addFn("putAt") { addFn("putAt") {
requireExactCount(2) requireExactCount(2)
val newValue = args[1] val newValue = args[1]
thisObj.putAt(requireScope(), requiredArg<Obj>(0), newValue) thisObj.putAt(this, requiredArg<Obj>(0), newValue)
newValue newValue
} }
addFnDoc(
name = "toJson",
doc = "Encodes this object to JSON.",
returns = type("lyng.String"),
moduleName = "lyng.stdlib"
) {
thisObj.toJson(requireScope()).toString().toObj()
}
addFnDoc( addFnDoc(
name = "toJsonString", name = "toJsonString",
doc = "Encodes this object to a JSON string.", doc = "Encodes this object to a JSON string.",
returns = type("lyng.String"), returns = type("lyng.String"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
thisObj.toJson(requireScope()).toString().toObj() thisObj.toJson(this).toString().toObj()
} }
addFnDoc( addFnDoc(
name = "clamp", name = "clamp",
@ -837,12 +775,12 @@ open class Obj {
var result = thisObj var result = thisObj
if (range.start != null && !range.start.isNull) { if (range.start != null && !range.start.isNull) {
if (result.compareTo(requireScope(), range.start) < 0) { if (result.compareTo(this, range.start) < 0) {
result = range.start result = range.start
} }
} }
if (range.end != null && !range.end.isNull) { if (range.end != null && !range.end.isNull) {
val cmp = range.end.compareTo(requireScope(), result) val cmp = range.end.compareTo(this, result)
if (range.isEndInclusive) { if (range.isEndInclusive) {
if (cmp < 0) result = range.end if (cmp < 0) result = range.end
} else { } else {
@ -977,14 +915,7 @@ object ObjNull : Obj() {
@Serializable @Serializable
@SerialName("unset") @SerialName("unset")
object ObjUnset : Obj() { object ObjUnset : Obj() {
override suspend fun compareTo(scope: Scope, other: Obj): Int { override suspend fun compareTo(scope: Scope, other: Obj): Int = if (other === this) 0 else -1
val resolved = when (other) {
is FrameSlotRef -> other.read()
is RecordSlotRef -> other.read()
else -> other
}
return if (resolved === this) 0 else -1
}
override fun equals(other: Any?): Boolean = other === this override fun equals(other: Any?): Boolean = other === this
override fun toString(): String = "Unset" override fun toString(): String = "Unset"

View File

@ -32,7 +32,7 @@ val ObjArray by lazy {
doc = "Iterator over elements of this array using its indexer.", doc = "Iterator over elements of this array using its indexer.",
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Any"))), returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Any"))),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ObjArrayIterator(thisObj).also { it.init(requireScope()) } } ) { ObjArrayIterator(thisObj).also { it.init(this) } }
addFnDoc( addFnDoc(
name = "contains", name = "contains",
@ -42,10 +42,9 @@ val ObjArray by lazy {
isOpen = true, isOpen = true,
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val scope = requireScope()
val obj = args.firstAndOnly() val obj = args.firstAndOnly()
for (i in 0..<thisObj.invokeInstanceMethod(scope, "size").toInt()) { for (i in 0..<thisObj.invokeInstanceMethod(this, "size").toInt()) {
if (thisObj.getAt(scope, ObjInt(i.toLong())).compareTo(scope, obj) == 0) return@addFnDoc ObjTrue if (thisObj.getAt(this, ObjInt(i.toLong())).compareTo(this, obj) == 0) return@addFnDoc ObjTrue
} }
ObjFalse ObjFalse
} }
@ -56,11 +55,10 @@ val ObjArray by lazy {
type = type("lyng.Any"), type = type("lyng.Any"),
moduleName = "lyng.stdlib", moduleName = "lyng.stdlib",
getter = { getter = {
val scope = requireScope()
this.thisObj.invokeInstanceMethod( this.thisObj.invokeInstanceMethod(
scope, this,
"getAt", "getAt",
(this.thisObj.invokeInstanceMethod(scope, "size").toInt() - 1).toObj() (this.thisObj.invokeInstanceMethod(this, "size").toInt() - 1).toObj()
) )
} }
) )
@ -70,10 +68,7 @@ val ObjArray by lazy {
doc = "Index of the last element (size - 1).", doc = "Index of the last element (size - 1).",
type = type("lyng.Int"), type = type("lyng.Int"),
moduleName = "lyng.stdlib", moduleName = "lyng.stdlib",
getter = { getter = { (this.thisObj.invokeInstanceMethod(this, "size").toInt() - 1).toObj() }
val scope = requireScope()
(this.thisObj.invokeInstanceMethod(scope, "size").toInt() - 1).toObj()
}
) )
addPropertyDoc( addPropertyDoc(
@ -81,10 +76,7 @@ val ObjArray by lazy {
doc = "Range of valid indices for this array.", doc = "Range of valid indices for this array.",
type = type("lyng.Range"), type = type("lyng.Range"),
moduleName = "lyng.stdlib", moduleName = "lyng.stdlib",
getter = { getter = { ObjRange(0.toObj(), this.thisObj.invokeInstanceMethod(this, "size"), false) }
val scope = requireScope()
ObjRange(0.toObj(), this.thisObj.invokeInstanceMethod(scope, "size"), false)
}
) )
addFnDoc( addFnDoc(
@ -94,16 +86,15 @@ val ObjArray by lazy {
returns = type("lyng.Int"), returns = type("lyng.Int"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val scope = requireScope()
val target = args.firstAndOnly() val target = args.firstAndOnly()
var low = 0 var low = 0
var high = thisObj.invokeInstanceMethod(scope, "size").toInt() - 1 var high = thisObj.invokeInstanceMethod(this, "size").toInt() - 1
while (low <= high) { while (low <= high) {
val mid = (low + high) / 2 val mid = (low + high) / 2
val midVal = thisObj.getAt(scope, ObjInt(mid.toLong())) val midVal = thisObj.getAt(this, ObjInt(mid.toLong()))
val cmp = midVal.compareTo(scope, target) val cmp = midVal.compareTo(this, target)
when { when {
cmp == 0 -> return@addFnDoc (mid).toObj() cmp == 0 -> return@addFnDoc (mid).toObj()
cmp > 0 -> high = mid - 1 cmp > 0 -> high = mid - 1

View File

@ -38,8 +38,8 @@ class ObjArrayIterator(val array: Obj) : Obj() {
addFn("next") { addFn("next") {
val self = thisAs<ObjArrayIterator>() val self = thisAs<ObjArrayIterator>()
if (self.nextIndex < self.lastIndex) { if (self.nextIndex < self.lastIndex) {
self.array.invokeInstanceMethod(requireScope(), "getAt", (self.nextIndex++).toObj()) self.array.invokeInstanceMethod(this, "getAt", (self.nextIndex++).toObj())
} else raiseError(ObjIterationFinishedException(requireScope())) } else raiseError(ObjIterationFinishedException(this))
} }
addFn("hasNext") { addFn("hasNext") {
val self = thisAs<ObjArrayIterator>() val self = thisAs<ObjArrayIterator>()

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com * Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -73,8 +73,6 @@ data class ObjBool(val value: Boolean) : Obj() {
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder,lynonType: LynonType?): Obj { override suspend fun deserialize(scope: Scope, decoder: LynonDecoder,lynonType: LynonType?): Obj {
return ObjBool(decoder.unpackBoolean()) return ObjBool(decoder.unpackBoolean())
} }
}.apply {
isClosed = true
} }
} }
} }

View File

@ -61,7 +61,7 @@ val ObjClassType by lazy {
val names = mutableListOf<Obj>() val names = mutableListOf<Obj>()
for (c in cls.mro) { for (c in cls.mro) {
for ((n, rec) in c.members) { for ((n, rec) in c.members) {
if (rec.type != ObjRecord.Type.Fun && seen.add(n)) names += ObjString(n) if (rec.value !is Statement && seen.add(n)) names += ObjString(n)
} }
} }
ObjList(names.toMutableList()) ObjList(names.toMutableList())
@ -79,7 +79,7 @@ val ObjClassType by lazy {
val names = mutableListOf<Obj>() val names = mutableListOf<Obj>()
for (c in cls.mro) { for (c in cls.mro) {
for ((n, rec) in c.members) { for ((n, rec) in c.members) {
if (rec.type == ObjRecord.Type.Fun && seen.add(n)) names += ObjString(n) if (rec.value is Statement && seen.add(n)) names += ObjString(n)
} }
} }
ObjList(names.toMutableList()) ObjList(names.toMutableList())
@ -109,7 +109,6 @@ open class ObjClass(
var isAnonymous: Boolean = false var isAnonymous: Boolean = false
var isAbstract: Boolean = false var isAbstract: Boolean = false
var isClosed: Boolean = false
// Stable identity and simple structural version for PICs // Stable identity and simple structural version for PICs
val classId: Long = ClassIdGen.nextId() val classId: Long = ClassIdGen.nextId()
@ -137,7 +136,7 @@ open class ObjClass(
} }
} }
cls.classScope?.objects?.forEach { (name, rec) -> cls.classScope?.objects?.forEach { (name, rec) ->
if (rec.visibility == Visibility.Public && (rec.type == ObjRecord.Type.Fun || rec.type == ObjRecord.Type.Delegated)) { if (rec.visibility == Visibility.Public && (rec.value is Statement || rec.type == ObjRecord.Type.Delegated)) {
val key = if (rec.type == ObjRecord.Type.Delegated) cls.mangledName(name) else name val key = if (rec.type == ObjRecord.Type.Delegated) cls.mangledName(name) else name
res[name] = key res[name] = key
} }
@ -151,13 +150,13 @@ open class ObjClass(
val classNameObj by lazy { ObjString(className) } val classNameObj by lazy { ObjString(className) }
var constructorMeta: ArgsDeclaration? = null var constructorMeta: ArgsDeclaration? = null
var instanceConstructor: Obj? = null var instanceConstructor: Statement? = null
/** /**
* Per-instance initializers collected from class body (for instance fields). These are executed * Per-instance initializers collected from class body (for instance fields). These are executed
* during construction in the instance scope of the object, once per class in the hierarchy. * during construction in the instance scope of the object, once per class in the hierarchy.
*/ */
val instanceInitializers: MutableList<Obj> = mutableListOf() val instanceInitializers: MutableList<Statement> = mutableListOf()
/** /**
* the scope for class methods, initialize class vars, etc. * the scope for class methods, initialize class vars, etc.
@ -290,14 +289,6 @@ open class ObjClass(
private var methodSlotMap: Map<String, MethodSlot> = emptyMap() private var methodSlotMap: Map<String, MethodSlot> = emptyMap()
private var methodSlotCount: Int = 0 private var methodSlotCount: Int = 0
/** Kotlin bridge class-level storage (no name lookup). */
internal var kotlinClassData: Any? = null
/** Kotlin bridge instance init hooks. */
internal var bridgeInitHooks: MutableList<suspend (net.sergeych.lyng.ScopeFacade, ObjInstance) -> Unit>? = null
internal var instanceTemplateBuilt: Boolean = false
private fun ensureFieldSlots(): Map<String, FieldSlot> { private fun ensureFieldSlots(): Map<String, FieldSlot> {
if (fieldSlotLayoutVersion == layoutVersion) return fieldSlotMap if (fieldSlotLayoutVersion == layoutVersion) return fieldSlotMap
val res = mutableMapOf<String, FieldSlot>() val res = mutableMapOf<String, FieldSlot>()
@ -358,7 +349,8 @@ open class ObjClass(
if (cls.className == "Obj") break if (cls.className == "Obj") break
for ((name, rec) in cls.members) { for ((name, rec) in cls.members) {
if (rec.isAbstract) continue if (rec.isAbstract) continue
if (rec.type != ObjRecord.Type.Delegated && if (rec.value !is Statement &&
rec.type != ObjRecord.Type.Delegated &&
rec.type != ObjRecord.Type.Fun && rec.type != ObjRecord.Type.Fun &&
rec.type != ObjRecord.Type.Property) { rec.type != ObjRecord.Type.Property) {
continue continue
@ -371,9 +363,9 @@ open class ObjClass(
} }
cls.classScope?.objects?.forEach { (name, rec) -> cls.classScope?.objects?.forEach { (name, rec) ->
if (rec.isAbstract) return@forEach if (rec.isAbstract) return@forEach
if (rec.type != ObjRecord.Type.Delegated && if (rec.value !is Statement &&
rec.type != ObjRecord.Type.Property && rec.type != ObjRecord.Type.Delegated &&
rec.type != ObjRecord.Type.Fun) return@forEach rec.type != ObjRecord.Type.Property) return@forEach
val key = if (rec.visibility == Visibility.Private || rec.type == ObjRecord.Type.Delegated) cls.mangledName(name) else name val key = if (rec.visibility == Visibility.Private || rec.type == ObjRecord.Type.Delegated) cls.mangledName(name) else name
if (res.containsKey(key)) return@forEach if (res.containsKey(key)) return@forEach
val methodId = rec.methodId ?: cls.assignMethodId(name, rec) val methodId = rec.methodId ?: cls.assignMethodId(name, rec)
@ -400,16 +392,7 @@ open class ObjClass(
internal fun fieldSlotMap(): Map<String, FieldSlot> = ensureFieldSlots() internal fun fieldSlotMap(): Map<String, FieldSlot> = ensureFieldSlots()
internal fun fieldRecordForId(fieldId: Int): ObjRecord? { internal fun fieldRecordForId(fieldId: Int): ObjRecord? {
ensureFieldSlots() ensureFieldSlots()
fieldSlotMap.values.firstOrNull { it.slot == fieldId }?.record?.let { return it } return fieldSlotMap.values.firstOrNull { it.slot == fieldId }?.record
// Fallback: resolve by id through name mapping if slot map is stale.
val name = fieldIdMap.entries.firstOrNull { it.value == fieldId }?.key
if (name != null) {
for (cls in mro) {
cls.members[name]?.let { return it }
cls.classScope?.objects?.get(name)?.let { return it }
}
}
return null
} }
internal fun resolveInstanceMember(name: String): ResolvedMember? = ensureInstanceMemberCache()[name] internal fun resolveInstanceMember(name: String): ResolvedMember? = ensureInstanceMemberCache()[name]
internal fun methodSlotCount(): Int { internal fun methodSlotCount(): Int {
@ -433,18 +416,9 @@ open class ObjClass(
if (rec.methodId == methodId) return rec if (rec.methodId == methodId) return rec
} }
} }
// Final fallback: resolve by id through name mapping if slot map is stale.
val name = methodIdMap.entries.firstOrNull { it.value == methodId }?.key
if (name != null) {
for (cls in mro) {
cls.members[name]?.let { return it }
cls.classScope?.objects?.get(name)?.let { return it }
}
}
return null return null
} }
internal fun instanceFieldIdMap(): Map<String, Int> { internal fun instanceFieldIdMap(): Map<String, Int> {
val result = mutableMapOf<String, Int>() val result = mutableMapOf<String, Int>()
for (cls in mro) { for (cls in mro) {
@ -498,13 +472,7 @@ open class ObjClass(
fieldIdMap[name] = existingId fieldIdMap[name] = existingId
return existingId return existingId
} }
val id = fieldIdMap[name] ?: run { val id = fieldIdMap.getOrPut(name) { nextFieldId++ }
val next = nextFieldId++
fieldIdMap[name] = next
// Field id map affects slot layout; invalidate caches when a new id is assigned.
layoutVersion += 1
next
}
return id return id
} }
@ -513,26 +481,12 @@ open class ObjClass(
val existingId = rec.methodId val existingId = rec.methodId
if (existingId != null) { if (existingId != null) {
methodIdMap[name] = existingId methodIdMap[name] = existingId
if (existingId >= nextMethodId) {
nextMethodId = existingId + 1
}
return existingId return existingId
} }
val id = methodIdMap[name] ?: run { val id = methodIdMap.getOrPut(name) { nextMethodId++ }
val next = nextMethodId++
methodIdMap[name] = next
// Method id map affects slot layout; invalidate caches when a new id is assigned.
layoutVersion += 1
next
}
if (id >= nextMethodId) {
nextMethodId = id + 1
}
return id return id
} }
internal fun ensureMethodIdForBridge(name: String, rec: ObjRecord): Int = assignMethodId(name, rec)
private fun ensureMethodIdSeeded() { private fun ensureMethodIdSeeded() {
if (methodIdSeeded) return if (methodIdSeeded) return
var maxId = -1 var maxId = -1
@ -562,17 +516,6 @@ open class ObjClass(
methodIdSeeded = true methodIdSeeded = true
} }
internal fun replaceMemberForBridge(name: String, newRecord: ObjRecord) {
members[name] = newRecord
layoutVersion += 1
}
internal fun replaceClassScopeMemberForBridge(name: String, newRecord: ObjRecord) {
initClassScope()
classScope!!.objects[name] = newRecord
layoutVersion += 1
}
override fun toString(): String = className override fun toString(): String = className
override suspend fun compareTo(scope: Scope, other: Obj): Int = if (other === this) 0 else -1 override suspend fun compareTo(scope: Scope, other: Obj): Int = if (other === this) 0 else -1
@ -590,7 +533,7 @@ open class ObjClass(
for (cls in mro) { for (cls in mro) {
// 1) members-defined methods and fields // 1) members-defined methods and fields
for ((k, v) in cls.members) { for ((k, v) in cls.members) {
if (!v.isAbstract && (v.type == ObjRecord.Type.Fun || v.type == ObjRecord.Type.Property || v.type == ObjRecord.Type.Delegated || v.type == ObjRecord.Type.Field || v.type == ObjRecord.Type.ConstructorField)) { if (!v.isAbstract && (v.value is Statement || v.type == ObjRecord.Type.Delegated || v.type == ObjRecord.Type.Field || v.type == ObjRecord.Type.ConstructorField)) {
val key = if (v.visibility == Visibility.Private || v.type == ObjRecord.Type.Field || v.type == ObjRecord.Type.ConstructorField || v.type == ObjRecord.Type.Delegated) cls.mangledName(k) else k val key = if (v.visibility == Visibility.Private || v.type == ObjRecord.Type.Field || v.type == ObjRecord.Type.ConstructorField || v.type == ObjRecord.Type.Delegated) cls.mangledName(k) else k
if (!res.containsKey(key)) { if (!res.containsKey(key)) {
res[key] = v res[key] = v
@ -601,7 +544,7 @@ open class ObjClass(
cls.classScope?.objects?.forEach { (k, rec) -> cls.classScope?.objects?.forEach { (k, rec) ->
// ONLY copy methods and delegated members from class scope to instance scope. // ONLY copy methods and delegated members from class scope to instance scope.
// Fields in class scope are static fields and must NOT be per-instance. // Fields in class scope are static fields and must NOT be per-instance.
if (!rec.isAbstract && (rec.type == ObjRecord.Type.Fun || rec.type == ObjRecord.Type.Property || rec.type == ObjRecord.Type.Delegated)) { if (!rec.isAbstract && (rec.value is Statement || rec.type == ObjRecord.Type.Delegated)) {
val key = if (rec.visibility == Visibility.Private || rec.type == ObjRecord.Type.Delegated) cls.mangledName(k) else k val key = if (rec.visibility == Visibility.Private || rec.type == ObjRecord.Type.Delegated) cls.mangledName(k) else k
// if not already present, copy reference for dispatch // if not already present, copy reference for dispatch
if (!res.containsKey(key)) { if (!res.containsKey(key)) {
@ -610,7 +553,6 @@ open class ObjClass(
} }
} }
} }
instanceTemplateBuilt = true
res res
} }
@ -634,11 +576,6 @@ open class ObjClass(
val stableParent = classScope ?: scope.parent val stableParent = classScope ?: scope.parent
instance.instanceScope = Scope(stableParent, scope.args, scope.pos, instance) instance.instanceScope = Scope(stableParent, scope.args, scope.pos, instance)
instance.instanceScope.currentClassCtx = null instance.instanceScope.currentClassCtx = null
val classCaptureRecords = classScope?.captureRecords
if (classCaptureRecords != null) {
instance.instanceScope.captureRecords = classCaptureRecords
instance.instanceScope.captureNames = classScope?.captureNames
}
val fieldSlots = fieldSlotMap() val fieldSlots = fieldSlotMap()
if (fieldSlots.isNotEmpty()) { if (fieldSlots.isNotEmpty()) {
instance.initFieldSlots(fieldSlotCount()) instance.initFieldSlots(fieldSlotCount())
@ -693,14 +630,6 @@ open class ObjClass(
args: Arguments?, args: Arguments?,
runConstructors: Boolean runConstructors: Boolean
) { ) {
bridgeInitHooks?.let { hooks ->
if (hooks.isNotEmpty()) {
val facade = instance.instanceScope.asFacade()
for (hook in hooks) {
hook(facade, instance)
}
}
}
val visited = hashSetOf<ObjClass>() val visited = hashSetOf<ObjClass>()
initClassInternal(instance, visited, this, args, isRoot = true, runConstructors = runConstructors) initClassInternal(instance, visited, this, args, isRoot = true, runConstructors = runConstructors)
} }
@ -786,11 +715,7 @@ open class ObjClass(
instance.instanceScope.currentClassCtx = c instance.instanceScope.currentClassCtx = c
try { try {
for (initStmt in c.instanceInitializers) { for (initStmt in c.instanceInitializers) {
if (initStmt is net.sergeych.lyng.Statement) { initStmt.execute(instance.instanceScope)
executeBytecodeWithSeed(instance.instanceScope, initStmt, "instance init")
} else {
initStmt.callOn(instance.instanceScope)
}
} }
} finally { } finally {
instance.instanceScope.currentClassCtx = savedCtx instance.instanceScope.currentClassCtx = savedCtx
@ -801,7 +726,7 @@ open class ObjClass(
c.instanceConstructor?.let { ctor -> c.instanceConstructor?.let { ctor ->
val execScope = val execScope =
instance.instanceScope.createChildScope(args = argsForThis ?: Arguments.EMPTY, newThisObj = instance) instance.instanceScope.createChildScope(args = argsForThis ?: Arguments.EMPTY, newThisObj = instance)
ctor.callOn(execScope) ctor.execute(execScope)
} }
} }
} }
@ -1006,9 +931,9 @@ open class ObjClass(
isOverride: Boolean = false, isOverride: Boolean = false,
pos: Pos = Pos.builtIn, pos: Pos = Pos.builtIn,
methodId: Int? = null, methodId: Int? = null,
code: (suspend net.sergeych.lyng.ScopeFacade.() -> Obj)? = null code: (suspend Scope.() -> Obj)? = null
) { ) {
val stmt = code?.let { ObjExternCallable.fromBridge { it() } } ?: ObjNull val stmt = code?.let { statement { it() } } ?: ObjNull
createField( createField(
name, stmt, isMutable, visibility, writeVisibility, pos, declaringClass, name, stmt, isMutable, visibility, writeVisibility, pos, declaringClass,
isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride, isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride,
@ -1021,8 +946,8 @@ open class ObjClass(
fun addProperty( fun addProperty(
name: String, name: String,
getter: (suspend net.sergeych.lyng.ScopeFacade.() -> Obj)? = null, getter: (suspend Scope.() -> Obj)? = null,
setter: (suspend net.sergeych.lyng.ScopeFacade.(Obj) -> Unit)? = null, setter: (suspend Scope.(Obj) -> Unit)? = null,
visibility: Visibility = Visibility.Public, visibility: Visibility = Visibility.Public,
writeVisibility: Visibility? = null, writeVisibility: Visibility? = null,
declaringClass: ObjClass? = this, declaringClass: ObjClass? = this,
@ -1033,8 +958,8 @@ open class ObjClass(
prop: ObjProperty? = null, prop: ObjProperty? = null,
methodId: Int? = null methodId: Int? = null
) { ) {
val g = getter?.let { ObjExternCallable.fromBridge { it() } } val g = getter?.let { statement { it() } }
val s = setter?.let { ObjExternCallable.fromBridge { it(requiredArg(0)); ObjVoid } } val s = setter?.let { statement { it(requiredArg(0)); ObjVoid } }
val finalProp = prop ?: if (isAbstract) ObjNull else ObjProperty(name, g, s) val finalProp = prop ?: if (isAbstract) ObjNull else ObjProperty(name, g, s)
createField( createField(
name, finalProp, false, visibility, writeVisibility, pos, declaringClass, name, finalProp, false, visibility, writeVisibility, pos, declaringClass,
@ -1045,8 +970,8 @@ open class ObjClass(
} }
fun addClassConst(name: String, value: Obj) = createClassField(name, value) fun addClassConst(name: String, value: Obj) = createClassField(name, value)
fun addClassFn(name: String, isOpen: Boolean = false, code: suspend net.sergeych.lyng.ScopeFacade.() -> Obj) { fun addClassFn(name: String, isOpen: Boolean = false, code: suspend Scope.() -> Obj) {
createClassField(name, ObjExternCallable.fromBridge { code() }, isOpen, type = ObjRecord.Type.Fun) createClassField(name, statement { code() }, isOpen, type = ObjRecord.Type.Fun)
} }

View File

@ -21,6 +21,7 @@ import kotlinx.datetime.*
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.JsonPrimitive
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement
import net.sergeych.lyng.miniast.addClassFnDoc import net.sergeych.lyng.miniast.addClassFnDoc
import net.sergeych.lyng.miniast.addFnDoc import net.sergeych.lyng.miniast.addFnDoc
import net.sergeych.lyng.miniast.addPropertyDoc import net.sergeych.lyng.miniast.addPropertyDoc
@ -46,18 +47,14 @@ class ObjDateTime(val instant: Instant, val timeZone: TimeZone) : Obj() {
if (rec != null) { if (rec != null) {
if (rec.type == ObjRecord.Type.Property) { if (rec.type == ObjRecord.Type.Property) {
val prop = rec.value as? ObjProperty val prop = rec.value as? ObjProperty
?: (rec.value as? Statement)?.execute(scope) as? ObjProperty
if (prop != null) { if (prop != null) {
return ObjRecord(prop.callGetter(scope, this, rec.declaringClass ?: cls), rec.isMutable) return ObjRecord(prop.callGetter(scope, this, rec.declaringClass ?: cls), rec.isMutable)
} }
} }
if (rec.type == ObjRecord.Type.Fun) { if (rec.type == ObjRecord.Type.Fun || rec.value is Statement) {
val target = rec.value val s = rec.value as Statement
return ObjRecord( return ObjRecord(net.sergeych.lyng.statement { s.execute(this.createChildScope(newThisObj = this@ObjDateTime)) }, rec.isMutable)
ObjExternCallable.fromBridge {
call(target, args, newThisObj = this@ObjDateTime)
},
rec.isMutable
)
} }
return resolveRecord(scope, rec, name, rec.declaringClass ?: cls) return resolveRecord(scope, rec, name, rec.declaringClass ?: cls)
} }

View File

@ -19,6 +19,7 @@ package net.sergeych.lyng.obj
import net.sergeych.lyng.Arguments import net.sergeych.lyng.Arguments
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement
class ObjDynamicContext(val delegate: ObjDynamic) : Obj() { class ObjDynamicContext(val delegate: ObjDynamic) : Obj() {
override val objClass: ObjClass get() = type override val objClass: ObjClass get() = type
@ -50,7 +51,7 @@ class ObjDynamicContext(val delegate: ObjDynamic) : Obj() {
* Object that delegates all its field access/invocation operations to a callback. It is used to implement dynamic * Object that delegates all its field access/invocation operations to a callback. It is used to implement dynamic
* objects using "dynamic" keyword. * objects using "dynamic" keyword.
*/ */
open class ObjDynamic(var readCallback: Obj? = null, var writeCallback: Obj? = null) : Obj() { open class ObjDynamic(var readCallback: Statement? = null, var writeCallback: Statement? = null) : Obj() {
override val objClass: ObjClass get() = type override val objClass: ObjClass get() = type
// Capture the lexical scope used to build this dynamic so callbacks can see outer locals // Capture the lexical scope used to build this dynamic so callbacks can see outer locals
@ -62,7 +63,7 @@ open class ObjDynamic(var readCallback: Obj? = null, var writeCallback: Obj? = n
*/ */
override suspend fun readField(scope: Scope, name: String): ObjRecord { override suspend fun readField(scope: Scope, name: String): ObjRecord {
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
return readCallback?.callOn(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
else else
@ -82,32 +83,32 @@ open class ObjDynamic(var readCallback: Obj? = null, var writeCallback: Obj? = n
onNotFoundResult: (suspend () -> Obj?)? onNotFoundResult: (suspend () -> Obj?)?
): Obj { ): Obj {
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
val over = readCallback?.callOn(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 { scope.applyClosure(it) } ?: scope
writeCallback?.callOn(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 { scope.applyClosure(it) } ?: scope
return readCallback?.callOn(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 { scope.applyClosure(it) } ?: scope
writeCallback?.callOn(execBase.createChildScope(Arguments(index, newValue))) writeCallback?.execute(execBase.createChildScope(Arguments(index, newValue)))
?: super.putAt(scope, index, newValue) ?: super.putAt(scope, index, newValue)
} }
companion object { companion object {
suspend fun create(scope: Scope, builder: Obj): ObjDynamic { suspend fun create(scope: Scope, builder: Statement): ObjDynamic {
val delegate = ObjDynamic() val delegate = ObjDynamic()
val context = ObjDynamicContext(delegate) val context = ObjDynamicContext(delegate)
// Capture the function's lexical scope (scope) so callbacks can see outer locals like parameters. // Capture the function's lexical scope (scope) so callbacks can see outer locals like parameters.
@ -115,7 +116,7 @@ open class ObjDynamic(var readCallback: Obj? = null, var writeCallback: Obj? = n
val buildScope = scope.createChildScope(newThisObj = context) val buildScope = scope.createChildScope(newThisObj = context)
// Snapshot the caller scope to capture locals/args even if the runtime pools/reuses frames // Snapshot the caller scope to capture locals/args even if the runtime pools/reuses frames
delegate.builderScope = scope.snapshotForClosure() delegate.builderScope = scope.snapshotForClosure()
builder.callOn(buildScope) builder.execute(buildScope)
return delegate return delegate
} }

View File

@ -139,7 +139,7 @@ open class ObjException(
class ExceptionClass(val name: String, vararg parents: ObjClass) : ObjClass(name, *parents) { class ExceptionClass(val name: String, vararg parents: ObjClass) : ObjClass(name, *parents) {
init { init {
constructorMeta = ArgsDeclaration( constructorMeta = ArgsDeclaration(
listOf(ArgsDeclaration.Item("message", defaultValue = ObjExternCallable.fromBridge { ObjString(name) })), listOf(ArgsDeclaration.Item("message", defaultValue = statement { ObjString(name) })),
Token.Type.RPAREN Token.Type.RPAREN
) )
} }
@ -177,17 +177,17 @@ open class ObjException(
} }
val Root = ExceptionClass("Exception").apply { val Root = ExceptionClass("Exception").apply {
instanceInitializers.add(ObjExternCallable.fromBridge { instanceInitializers.add(statement {
if (thisObj is ObjInstance) { if (thisObj is ObjInstance) {
val msg = get("message")?.value ?: ObjString("Exception") val msg = get("message")?.value ?: ObjString("Exception")
(thisObj as ObjInstance).instanceScope.addItem("Exception::message", false, msg) (thisObj as ObjInstance).instanceScope.addItem("Exception::message", false, msg)
val stack = captureStackTrace(requireScope()) val stack = captureStackTrace(this)
(thisObj as ObjInstance).instanceScope.addItem("Exception::stackTrace", false, stack) (thisObj as ObjInstance).instanceScope.addItem("Exception::stackTrace", false, stack)
} }
ObjVoid ObjVoid
}) })
instanceConstructor = ObjExternCallable.fromBridge { ObjVoid } instanceConstructor = statement { ObjVoid }
addPropertyDoc( addPropertyDoc(
name = "message", name = "message",
doc = "Human‑readable error message.", doc = "Human‑readable error message.",
@ -244,7 +244,7 @@ open class ObjException(
is ObjInstance -> t.instanceScope.get("Exception::stackTrace")?.value as? ObjList ?: ObjList() is ObjInstance -> t.instanceScope.get("Exception::stackTrace")?.value as? ObjList ?: ObjList()
else -> ObjList() else -> ObjList()
} }
val at = stack.list.firstOrNull()?.let { toStringOf(it) } ?: ObjString("(unknown)") val at = stack.list.firstOrNull()?.toString(this) ?: ObjString("(unknown)")
ObjString("${thisObj.objClass.className}: $msg at $at") ObjString("${thisObj.objClass.className}: $msg at $at")
} }
} }

View File

@ -1,47 +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.obj
import net.sergeych.lyng.Scope
import net.sergeych.lyng.ScopeBridge
import net.sergeych.lyng.ScopeFacade
import net.sergeych.lyng.Statement
class ObjExternCallable private constructor(
private val target: Obj?,
private val fn: (suspend ScopeFacade.() -> Obj)?
) : Obj() {
override val objClass: ObjClass
get() = Statement.type
override suspend fun callOn(scope: Scope): Obj {
val facade = ScopeBridge(scope)
return when {
fn != null -> facade.fn()
target != null -> target.callOn(scope)
else -> ObjVoid
}
}
override fun toString(): String = "ExternCallable@${hashCode()}"
companion object {
fun wrap(target: Obj): ObjExternCallable = ObjExternCallable(target, null)
fun fromBridge(fn: suspend ScopeFacade.() -> Obj): ObjExternCallable = ObjExternCallable(null, fn)
}
}

View File

@ -71,13 +71,13 @@ class ObjFlowBuilder(val output: SendChannel<Obj>) : Obj() {
} }
} }
private fun createLyngFlowInput(scope: Scope, producer: Obj): ReceiveChannel<Obj> { private fun createLyngFlowInput(scope: Scope, producer: Statement): ReceiveChannel<Obj> {
val channel = Channel<Obj>(Channel.RENDEZVOUS) val channel = Channel<Obj>(Channel.RENDEZVOUS)
val builder = ObjFlowBuilder(channel) val builder = ObjFlowBuilder(channel)
val builderScope = scope.createChildScope(newThisObj = builder) val builderScope = scope.createChildScope(newThisObj = builder)
globalLaunch { globalLaunch {
try { try {
producer.callOn(builderScope) producer.execute(builderScope)
} catch (x: ScriptFlowIsNoMoreCollected) { } catch (x: ScriptFlowIsNoMoreCollected) {
// premature flow closing, OK // premature flow closing, OK
} catch (x: Exception) { } catch (x: Exception) {
@ -89,7 +89,7 @@ private fun createLyngFlowInput(scope: Scope, producer: Obj): ReceiveChannel<Obj
return channel return channel
} }
class ObjFlow(val producer: Obj, val scope: Scope) : Obj() { class ObjFlow(val producer: Statement, val scope: Scope) : Obj() {
override val objClass get() = type override val objClass get() = type
@ -106,8 +106,8 @@ class ObjFlow(val producer: Obj, val scope: Scope) : Obj() {
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val objFlow = thisAs<ObjFlow>() val objFlow = thisAs<ObjFlow>()
ObjFlowIterator(ObjExternCallable.fromBridge { ObjFlowIterator(statement {
call(objFlow.producer) objFlow.producer.execute(this)
}) })
} }
} }
@ -115,7 +115,7 @@ class ObjFlow(val producer: Obj, val scope: Scope) : Obj() {
} }
class ObjFlowIterator(val producer: Obj) : Obj() { class ObjFlowIterator(val producer: Statement) : Obj() {
override val objClass: ObjClass get() = type override val objClass: ObjClass get() = type
@ -164,7 +164,7 @@ class ObjFlowIterator(val producer: Obj) : Obj() {
doc = "Whether another element is available from the flow.", doc = "Whether another element is available from the flow.",
returns = type("lyng.Bool"), returns = type("lyng.Bool"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { thisAs<ObjFlowIterator>().hasNext(requireScope()).toObj() } ) { thisAs<ObjFlowIterator>().hasNext(this).toObj() }
addFnDoc( addFnDoc(
name = "next", name = "next",
doc = "Receive the next element from the flow or throw if completed.", doc = "Receive the next element from the flow or throw if completed.",
@ -172,7 +172,7 @@ class ObjFlowIterator(val producer: Obj) : Obj() {
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val x = thisAs<ObjFlowIterator>() val x = thisAs<ObjFlowIterator>()
x.next(requireScope()) x.next(this)
} }
addFnDoc( addFnDoc(
name = "cancelIteration", name = "cancelIteration",

View File

@ -29,7 +29,6 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
internal lateinit var instanceScope: Scope internal lateinit var instanceScope: Scope
internal var fieldSlots: Array<ObjRecord?> = emptyArray() internal var fieldSlots: Array<ObjRecord?> = emptyArray()
internal var methodSlots: Array<ObjRecord?> = emptyArray() internal var methodSlots: Array<ObjRecord?> = emptyArray()
internal var kotlinInstanceData: Any? = null
internal fun initFieldSlots(size: Int) { internal fun initFieldSlots(size: Int) {
fieldSlots = arrayOfNulls(size) fieldSlots = arrayOfNulls(size)
@ -171,17 +170,16 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
} }
} }
del = del ?: scope.raiseError("Internal error: delegated property $name has no delegate") del = del ?: scope.raiseError("Internal error: delegated property $name has no delegate")
val getValueRec = when (del) { val getValueRec = del.objClass.getInstanceMemberOrNull("getValue")
is ObjInstance -> del.methodRecordForKey("getValue")
?: del.instanceScope.objects["getValue"]
?: del.objClass.getInstanceMemberOrNull("getValue")
else -> del.objClass.getInstanceMemberOrNull("getValue")
}
if (getValueRec == null || getValueRec.declaringClass?.className == "Delegate") { if (getValueRec == null || getValueRec.declaringClass?.className == "Delegate") {
val wrapper = ObjExternCallable.fromBridge { val wrapper = object : Statement() {
val th2 = if (thisObj === ObjVoid) ObjNull else thisObj override val pos: Pos = Pos.builtIn
val allArgs = (listOf(th2, ObjString(name)) + args.list).toTypedArray()
del.invokeInstanceMethod(requireScope(), "invoke", Arguments(*allArgs)) override suspend fun execute(s: Scope): Obj {
val th2 = if (s.thisObj === ObjVoid) ObjNull else s.thisObj
val allArgs = (listOf(th2, ObjString(name)) + s.args.list).toTypedArray()
return del.invokeInstanceMethod(s, "invoke", Arguments(*allArgs))
}
} }
return obj.copy(value = wrapper, type = ObjRecord.Type.Other) return obj.copy(value = wrapper, type = ObjRecord.Type.Other)
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com * Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -189,7 +189,6 @@ class ObjInt(val value: Long, override val isConst: Boolean = false) : Obj(), Nu
else -> scope.raiseIllegalState("illegal type code for Int: $lynonType") else -> scope.raiseIllegalState("illegal type code for Int: $lynonType")
} }
}.apply { }.apply {
isClosed = true
addFnDoc( addFnDoc(
name = "toInt", name = "toInt",
doc = "Returns this integer (identity operation).", doc = "Returns this integer (identity operation).",

View File

@ -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.Statement
import net.sergeych.lyng.miniast.ParamDoc import net.sergeych.lyng.miniast.ParamDoc
import net.sergeych.lyng.miniast.addFnDoc import net.sergeych.lyng.miniast.addFnDoc
import net.sergeych.lyng.miniast.addPropertyDoc import net.sergeych.lyng.miniast.addPropertyDoc
@ -41,11 +42,10 @@ val ObjIterable by lazy {
returns = type("lyng.List"), returns = type("lyng.List"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val scope = requireScope()
val result = mutableListOf<Obj>() val result = mutableListOf<Obj>()
val it = thisObj.invokeInstanceMethod(scope, "iterator") val it = thisObj.invokeInstanceMethod(this, "iterator")
while (it.invokeInstanceMethod(scope, "hasNext").toBool()) { while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
result.add(it.invokeInstanceMethod(scope, "next")) result.add(it.invokeInstanceMethod(this, "next"))
} }
ObjList(result) ObjList(result)
} }
@ -59,11 +59,10 @@ val ObjIterable by lazy {
isOpen = true, isOpen = true,
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val scope = requireScope()
val obj = args.firstAndOnly() val obj = args.firstAndOnly()
val it = thisObj.invokeInstanceMethod(scope, "iterator") val it = thisObj.invokeInstanceMethod(this, "iterator")
while (it.invokeInstanceMethod(scope, "hasNext").toBool()) { while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
if (obj.equals(scope, it.invokeInstanceMethod(scope, "next"))) if (obj.equals(this, it.invokeInstanceMethod(this, "next")))
return@addFnDoc ObjTrue return@addFnDoc ObjTrue
} }
ObjFalse ObjFalse
@ -77,12 +76,11 @@ val ObjIterable by lazy {
isOpen = true, isOpen = true,
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val scope = requireScope()
val obj = args.firstAndOnly() val obj = args.firstAndOnly()
var index = 0 var index = 0
val it = thisObj.invokeInstanceMethod(scope, "iterator") val it = thisObj.invokeInstanceMethod(this, "iterator")
while (it.invokeInstanceMethod(scope, "hasNext").toBool()) { while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
if (obj.equals(scope, it.invokeInstanceMethod(scope, "next"))) if (obj.equals(this, it.invokeInstanceMethod(this, "next")))
return@addFnDoc ObjInt(index.toLong()) return@addFnDoc ObjInt(index.toLong())
index++ index++
} }
@ -99,10 +97,9 @@ val ObjIterable by lazy {
this.thisObj this.thisObj
else { else {
val result = mutableSetOf<Obj>() val result = mutableSetOf<Obj>()
val scope = requireScope() val it = this.thisObj.invokeInstanceMethod(this, "iterator")
val it = this.thisObj.invokeInstanceMethod(scope, "iterator") while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
while (it.invokeInstanceMethod(scope, "hasNext").toBool()) { result.add(it.invokeInstanceMethod(this, "next"))
result.add(it.invokeInstanceMethod(scope, "next"))
} }
ObjSet(result) ObjSet(result)
} }
@ -116,11 +113,10 @@ val ObjIterable by lazy {
moduleName = "lyng.stdlib", moduleName = "lyng.stdlib",
getter = { getter = {
val result = mutableMapOf<Obj, Obj>() val result = mutableMapOf<Obj, Obj>()
val scope = requireScope() this.thisObj.enumerate(this) { pair ->
this.thisObj.enumerate(scope) { pair ->
when (pair) { when (pair) {
is ObjMapEntry -> result[pair.key] = pair.value is ObjMapEntry -> result[pair.key] = pair.value
else -> result[pair.getAt(scope, 0)] = pair.getAt(scope, 1) else -> result[pair.getAt(this, 0)] = pair.getAt(this, 1)
} }
true true
} }
@ -135,10 +131,10 @@ val ObjIterable by lazy {
returns = type("lyng.Map"), returns = type("lyng.Map"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val association = requireOnlyArg<Obj>() val association = requireOnlyArg<Statement>()
val result = ObjMap() val result = ObjMap()
thisObj.toFlow(requireScope()).collect { thisObj.toFlow(this).collect {
result.map[call(association, Arguments(it))] = it result.map[association.call(this, it)] = it
} }
result result
} }
@ -150,12 +146,11 @@ val ObjIterable by lazy {
isOpen = true, isOpen = true,
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val scope = requireScope() val it = thisObj.invokeInstanceMethod(this, "iterator")
val it = thisObj.invokeInstanceMethod(scope, "iterator") val fn = requiredArg<Statement>(0)
val fn = requiredArg<Obj>(0) while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
while (it.invokeInstanceMethod(scope, "hasNext").toBool()) { val x = it.invokeInstanceMethod(this, "next")
val x = it.invokeInstanceMethod(scope, "next") fn.execute(this.createChildScope(Arguments(listOf(x))))
call(fn, Arguments(listOf(x)))
} }
ObjVoid ObjVoid
} }
@ -168,10 +163,10 @@ val ObjIterable by lazy {
isOpen = true, isOpen = true,
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val fn = requiredArg<Obj>(0) val fn = requiredArg<Statement>(0)
val result = mutableListOf<Obj>() val result = mutableListOf<Obj>()
thisObj.toFlow(requireScope()).collect { thisObj.toFlow(this).collect {
result.add(call(fn, Arguments(it))) result.add(fn.call(this, it))
} }
ObjList(result) ObjList(result)
} }
@ -184,10 +179,10 @@ val ObjIterable by lazy {
isOpen = true, isOpen = true,
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val fn = requiredArg<Obj>(0) val fn = requiredArg<Statement>(0)
val result = mutableListOf<Obj>() val result = mutableListOf<Obj>()
thisObj.toFlow(requireScope()).collect { thisObj.toFlow(this).collect {
val transformed = call(fn, Arguments(it)) val transformed = fn.call(this, it)
if( transformed != ObjNull) result.add(transformed) if( transformed != ObjNull) result.add(transformed)
} }
ObjList(result) ObjList(result)
@ -203,7 +198,7 @@ val ObjIterable by lazy {
var n = requireOnlyArg<ObjInt>().value.toInt() var n = requireOnlyArg<ObjInt>().value.toInt()
val result = mutableListOf<Obj>() val result = mutableListOf<Obj>()
if (n > 0) { if (n > 0) {
thisObj.enumerate(requireScope()) { thisObj.enumerate(this) {
result.add(it) result.add(it)
--n > 0 --n > 0
} }
@ -218,8 +213,8 @@ val ObjIterable by lazy {
moduleName = "lyng.stdlib", moduleName = "lyng.stdlib",
getter = { getter = {
ObjBool( ObjBool(
this.thisObj.invokeInstanceMethod(requireScope(), "iterator") this.thisObj.invokeInstanceMethod(this, "iterator")
.invokeInstanceMethod(requireScope(), "hasNext").toBool() .invokeInstanceMethod(this, "hasNext").toBool()
.not() .not()
) )
} }
@ -232,10 +227,10 @@ val ObjIterable by lazy {
returns = type("lyng.List"), returns = type("lyng.List"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val list = thisObj.callMethod<ObjList>(requireScope(), "toList") val list = thisObj.callMethod<ObjList>(this, "toList")
val comparator = requireOnlyArg<Obj>() val comparator = requireOnlyArg<Statement>()
list.quicksort { a, b -> list.quicksort { a, b ->
call(comparator, Arguments(a, b)).toInt() comparator.call(this, a, b).toInt()
} }
list list
} }
@ -246,7 +241,7 @@ val ObjIterable by lazy {
returns = type("lyng.List"), returns = type("lyng.List"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val list = thisObj.callMethod<ObjList>(requireScope(), "toList") val list = thisObj.callMethod<ObjList>(this, "toList")
list.list.reverse() list.list.reverse()
list list
} }

View File

@ -69,12 +69,13 @@ val ObjIterator by lazy {
) { ) {
val out = mutableListOf<Obj>() val out = mutableListOf<Obj>()
while (true) { while (true) {
val has = thisObj.invokeInstanceMethod(requireScope(), "hasNext").toBool() val has = thisObj.invokeInstanceMethod(this, "hasNext").toBool()
if (!has) break if (!has) break
val v = thisObj.invokeInstanceMethod(requireScope(), "next") val v = thisObj.invokeInstanceMethod(this, "next")
out += v out += v
} }
ObjList(out.toMutableList()) ObjList(out.toMutableList())
} }
} }
} }

View File

@ -22,7 +22,6 @@ package net.sergeych.lyng.obj
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.bridge.LyngClassBridge
/** /**
* Iterator wrapper to allow Kotlin collections to be returned from Lyng objects; * Iterator wrapper to allow Kotlin collections to be returned from Lyng objects;
@ -33,33 +32,9 @@ class ObjKotlinIterator(val iterator: Iterator<Any?>) : Obj() {
override val objClass get() = type override val objClass get() = type
companion object { companion object {
private var boundType: ObjClass? = null val type = ObjClass("KotlinIterator", ObjIterator).apply {
addFn("next") { thisAs<ObjKotlinIterator>().iterator.next().toObj() }
val type: ObjClass addFn("hasNext") { thisAs<ObjKotlinIterator>().iterator.hasNext().toObj() }
get() = boundType ?: error("KotlinIterator class is not bound; import lyng.stdlib first")
internal fun bindTo(cls: ObjClass) {
if (boundType != null) {
if (boundType === cls) return
return
}
boundType = cls
LyngClassBridge.bind(cls) {
addFun("next") { scope, self, _ ->
when (self) {
is ObjKotlinIterator -> self.iterator.next().toObj()
is ObjKotlinObjIterator -> self.iterator.next()
else -> scope.raiseClassCastError("Expected KotlinIterator, got ${self.objClass.className}")
}
}
addFun("hasNext") { scope, self, _ ->
when (self) {
is ObjKotlinIterator -> self.iterator.hasNext().toObj()
is ObjKotlinObjIterator -> self.iterator.hasNext().toObj()
else -> scope.raiseClassCastError("Expected KotlinIterator, got ${self.objClass.className}")
}
}
}
} }
} }
@ -74,8 +49,14 @@ class ObjKotlinObjIterator(val iterator: Iterator<Obj>) : Obj() {
override val objClass get() = type override val objClass get() = type
companion object { companion object {
val type: ObjClass val type = ObjClass("KotlinIterator", ObjIterator).apply {
get() = ObjKotlinIterator.type addFn("next") {
thisAs<ObjKotlinObjIterator>().iterator.next()
}
addFn("hasNext") {
thisAs<ObjKotlinObjIterator>().iterator.hasNext().toObj()
}
}
} }
} }
@ -94,3 +75,4 @@ fun Obj.toFlow(scope: Scope): Flow<Obj> = flow {
emit(next.invoke(scope, iterator)) emit(next.invoke(scope, iterator))
} }
} }

View File

@ -17,18 +17,13 @@
package net.sergeych.lyng.obj package net.sergeych.lyng.obj
import net.sergeych.lyng.Arguments import net.sergeych.lyng.*
import net.sergeych.lyng.Pos
import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement
import net.sergeych.lyng.Visibility
import net.sergeych.lyng.executeBytecodeWithSeed
/** /**
* Lazy delegate used by `val x by lazy { ... }`. * Lazy delegate used by `val x by lazy { ... }`.
*/ */
class ObjLazyDelegate( class ObjLazyDelegate(
private val builder: Obj, private val builder: Statement,
private val capturedScope: Scope, private val capturedScope: Scope,
) : Obj() { ) : Obj() {
override val objClass: ObjClass = type override val objClass: ObjClass = type
@ -46,11 +41,7 @@ class ObjLazyDelegate(
"getValue" -> { "getValue" -> {
if (!calculated) { if (!calculated) {
val callScope = capturedScope.createChildScope(capturedScope.pos, args = Arguments.EMPTY) val callScope = capturedScope.createChildScope(capturedScope.pos, args = Arguments.EMPTY)
cachedValue = if (builder is Statement) { cachedValue = builder.callOn(callScope)
executeBytecodeWithSeed(callScope, builder, "lazy delegate")
} else {
builder.callOn(callScope)
}
calculated = true calculated = true
} }
cachedValue cachedValue

View File

@ -20,7 +20,7 @@ package net.sergeych.lyng.obj
import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.Arguments import net.sergeych.lyng.Statement
import net.sergeych.lyng.miniast.ParamDoc import net.sergeych.lyng.miniast.ParamDoc
import net.sergeych.lyng.miniast.addFnDoc import net.sergeych.lyng.miniast.addFnDoc
import net.sergeych.lyng.miniast.addPropertyDoc import net.sergeych.lyng.miniast.addPropertyDoc
@ -371,10 +371,8 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
params = listOf(ParamDoc("comparator")), params = listOf(ParamDoc("comparator")),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val comparator = requireOnlyArg<Obj>() val comparator = requireOnlyArg<Statement>()
thisAs<ObjList>().quicksort { a, b -> thisAs<ObjList>().quicksort { a, b -> comparator.call(this, a, b).toInt() }
call(comparator, Arguments(a, b)).toInt()
}
ObjVoid ObjVoid
} }
addFnDoc( addFnDoc(
@ -393,7 +391,6 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
val self = thisAs<ObjList>() val self = thisAs<ObjList>()
val l = self.list val l = self.list
if (l.isEmpty()) return@addFnDoc ObjNull if (l.isEmpty()) return@addFnDoc ObjNull
val scope = requireScope()
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) { if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
// Fast path: all ints → accumulate as long // Fast path: all ints → accumulate as long
var i = 0 var i = 0
@ -407,7 +404,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
// Fallback to generic dynamic '+' accumulation starting from current acc // Fallback to generic dynamic '+' accumulation starting from current acc
var res: Obj = ObjInt(acc) var res: Obj = ObjInt(acc)
while (i < l.size) { while (i < l.size) {
res = res.plus(scope, l[i]) res = res.plus(this, l[i])
i++ i++
} }
return@addFnDoc res return@addFnDoc res
@ -419,7 +416,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
var res: Obj = l[0] var res: Obj = l[0]
var k = 1 var k = 1
while (k < l.size) { while (k < l.size) {
res = res.plus(scope, l[k]) res = res.plus(this, l[k])
k++ k++
} }
res res
@ -431,7 +428,6 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
) { ) {
val l = thisAs<ObjList>().list val l = thisAs<ObjList>().list
if (l.isEmpty()) return@addFnDoc ObjNull if (l.isEmpty()) return@addFnDoc ObjNull
val scope = requireScope()
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) { if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
var i = 0 var i = 0
var hasOnlyInts = true var hasOnlyInts = true
@ -452,7 +448,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
var i = 1 var i = 1
while (i < l.size) { while (i < l.size) {
val v = l[i] val v = l[i]
if (v.compareTo(scope, res) < 0) res = v if (v.compareTo(this, res) < 0) res = v
i++ i++
} }
res res
@ -464,7 +460,6 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
) { ) {
val l = thisAs<ObjList>().list val l = thisAs<ObjList>().list
if (l.isEmpty()) return@addFnDoc ObjNull if (l.isEmpty()) return@addFnDoc ObjNull
val scope = requireScope()
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) { if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
var i = 0 var i = 0
var hasOnlyInts = true var hasOnlyInts = true
@ -485,7 +480,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
var i = 1 var i = 1
while (i < l.size) { while (i < l.size) {
val v = l[i] val v = l[i]
if (v.compareTo(scope, res) > 0) res = v if (v.compareTo(this, res) > 0) res = v
i++ i++
} }
res res
@ -499,7 +494,6 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
) { ) {
val l = thisAs<ObjList>().list val l = thisAs<ObjList>().list
val needle = args.firstAndOnly() val needle = args.firstAndOnly()
val scope = requireScope()
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS && needle is ObjInt) { if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS && needle is ObjInt) {
var i = 0 var i = 0
while (i < l.size) { while (i < l.size) {
@ -511,7 +505,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
} }
var i = 0 var i = 0
while (i < l.size) { while (i < l.size) {
if (l[i].compareTo(scope, needle) == 0) return@addFnDoc ObjInt(i.toLong()) if (l[i].compareTo(this, needle) == 0) return@addFnDoc ObjInt(i.toLong())
i++ i++
} }
ObjInt((-1).toLong()) ObjInt((-1).toLong())
@ -528,3 +522,4 @@ fun <T>MutableList<T>.swap(i: Int, j: Int) {
this[j] = temp this[j] = temp
} }
} }

View File

@ -20,6 +20,7 @@ package net.sergeych.lyng.obj
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement
import net.sergeych.lyng.miniast.* import net.sergeych.lyng.miniast.*
import net.sergeych.lynon.LynonDecoder import net.sergeych.lynon.LynonDecoder
import net.sergeych.lynon.LynonEncoder import net.sergeych.lynon.LynonEncoder
@ -260,8 +261,8 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
) { ) {
val key = requiredArg<Obj>(0) val key = requiredArg<Obj>(0)
thisAs<ObjMap>().map.getOrPut(key) { thisAs<ObjMap>().map.getOrPut(key) {
val lambda = requiredArg<Obj>(1) val lambda = requiredArg<Statement>(1)
call(lambda) lambda.execute(this)
} }
} }
addPropertyDoc( addPropertyDoc(

View File

@ -20,6 +20,7 @@ package net.sergeych.lyng.obj
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement
import net.sergeych.lyng.miniast.ParamDoc import net.sergeych.lyng.miniast.ParamDoc
import net.sergeych.lyng.miniast.addFnDoc import net.sergeych.lyng.miniast.addFnDoc
import net.sergeych.lyng.miniast.type import net.sergeych.lyng.miniast.type
@ -40,11 +41,11 @@ class ObjMutex(val mutex: Mutex): Obj() {
returns = type("lyng.Any"), returns = type("lyng.Any"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val f = requiredArg<Obj>(0) val f = requiredArg<Statement>(0)
// Execute user lambda directly in the current scope to preserve the active scope // Execute user lambda directly in the current scope to preserve the active scope
// ancestry across suspension points. The lambda still constructs a closure scope // ancestry across suspension points. The lambda still constructs a ClosureScope
// on top of this frame, and parseLambdaExpression sets skipScopeCreation for its body. // on top of this frame, and parseLambdaExpression sets skipScopeCreation for its body.
thisAs<ObjMutex>().mutex.withLock { call(f) } thisAs<ObjMutex>().mutex.withLock { f.execute(this) }
} }
} }
} }

View File

@ -18,11 +18,8 @@
package net.sergeych.lyng.obj package net.sergeych.lyng.obj
import net.sergeych.lyng.Arguments import net.sergeych.lyng.Arguments
import net.sergeych.lyng.BytecodeBodyProvider
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement import net.sergeych.lyng.Statement
import net.sergeych.lyng.bytecode.BytecodeStatement
import net.sergeych.lyng.executeBytecodeWithSeed
/** /**
* Property accessor storage. Per instructions, properties do NOT have * Property accessor storage. Per instructions, properties do NOT have
@ -30,8 +27,8 @@ import net.sergeych.lyng.executeBytecodeWithSeed
*/ */
class ObjProperty( class ObjProperty(
val name: String, val name: String,
val getter: Obj?, val getter: Statement?,
val setter: Obj? val setter: Statement?
) : Obj() { ) : Obj() {
suspend fun callGetter(scope: Scope, instance: Obj, declaringClass: ObjClass? = null): Obj { suspend fun callGetter(scope: Scope, instance: Obj, declaringClass: ObjClass? = null): Obj {
@ -41,15 +38,7 @@ class ObjProperty(
val instanceScope = (instance as? ObjInstance)?.instanceScope ?: instance.autoInstanceScope(scope) val instanceScope = (instance as? ObjInstance)?.instanceScope ?: instance.autoInstanceScope(scope)
val execScope = scope.applyClosure(instanceScope).createChildScope(newThisObj = instance) val execScope = scope.applyClosure(instanceScope).createChildScope(newThisObj = instance)
execScope.currentClassCtx = declaringClass execScope.currentClassCtx = declaringClass
return when (g) { return g.execute(execScope)
is BytecodeStatement -> executeBytecodeWithSeed(execScope, g, "property getter")
is BytecodeBodyProvider -> {
val body = g.bytecodeBody()
if (body != null) executeBytecodeWithSeed(execScope, body, "property getter") else g.callOn(execScope)
}
is Statement -> g.callOn(execScope)
else -> g.callOn(execScope)
}
} }
suspend fun callSetter(scope: Scope, instance: Obj, value: Obj, declaringClass: ObjClass? = null) { suspend fun callSetter(scope: Scope, instance: Obj, value: Obj, declaringClass: ObjClass? = null) {
@ -59,15 +48,7 @@ class ObjProperty(
val instanceScope = (instance as? ObjInstance)?.instanceScope ?: instance.autoInstanceScope(scope) val instanceScope = (instance as? ObjInstance)?.instanceScope ?: instance.autoInstanceScope(scope)
val execScope = scope.applyClosure(instanceScope).createChildScope(args = Arguments(value), newThisObj = instance) val execScope = scope.applyClosure(instanceScope).createChildScope(args = Arguments(value), newThisObj = instance)
execScope.currentClassCtx = declaringClass execScope.currentClassCtx = declaringClass
when (s) { s.execute(execScope)
is BytecodeStatement -> executeBytecodeWithSeed(execScope, s, "property setter")
is BytecodeBodyProvider -> {
val body = s.bytecodeBody()
if (body != null) executeBytecodeWithSeed(execScope, body, "property setter") else s.callOn(execScope)
}
is Statement -> s.callOn(execScope)
else -> s.callOn(execScope)
}
} }
override fun toString(): String = "Property($name)" override fun toString(): String = "Property($name)"

View File

@ -270,7 +270,7 @@ class ObjRange(
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val self = thisAs<ObjRange>() val self = thisAs<ObjRange>()
self.buildIterator(requireScope()) self.buildIterator(this)
} }
} }
} }

View File

@ -65,10 +65,10 @@ class ObjRangeIterator(
companion object { companion object {
val type = ObjClass("RangeIterator", ObjIterator).apply { val type = ObjClass("RangeIterator", ObjIterator).apply {
addFn("hasNext") { addFn("hasNext") {
thisAs<ObjRangeIterator>().hasNext(requireScope()).toObj() thisAs<ObjRangeIterator>().hasNext(this).toObj()
} }
addFn("next") { addFn("next") {
thisAs<ObjRangeIterator>().next(requireScope()) thisAs<ObjRangeIterator>().next(this)
} }
} }
} }
@ -98,7 +98,7 @@ class ObjFastIntRangeIterator(private val start: Int, private val endExclusive:
companion object { companion object {
val type = ObjClass("FastIntRangeIterator", ObjIterator).apply { val type = ObjClass("FastIntRangeIterator", ObjIterator).apply {
addFn("hasNext") { thisAs<ObjFastIntRangeIterator>().hasNext().toObj() } addFn("hasNext") { thisAs<ObjFastIntRangeIterator>().hasNext().toObj() }
addFn("next") { thisAs<ObjFastIntRangeIterator>().next(requireScope()) } addFn("next") { thisAs<ObjFastIntRangeIterator>().next(this) }
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com * Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,10 +19,12 @@ package net.sergeych.lyng.obj
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.JsonPrimitive
import net.sergeych.lyng.Pos
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.miniast.addConstDoc import net.sergeych.lyng.miniast.addConstDoc
import net.sergeych.lyng.miniast.addFnDoc import net.sergeych.lyng.miniast.addFnDoc
import net.sergeych.lyng.miniast.type import net.sergeych.lyng.miniast.type
import net.sergeych.lyng.statement
import net.sergeych.lynon.LynonDecoder import net.sergeych.lynon.LynonDecoder
import net.sergeych.lynon.LynonEncoder import net.sergeych.lynon.LynonEncoder
import net.sergeych.lynon.LynonType import net.sergeych.lynon.LynonType
@ -121,12 +123,11 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj = override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj =
of(decoder.unpackDouble()) of(decoder.unpackDouble())
}.apply { }.apply {
isClosed = true
// roundToInt: number rounded to the nearest integer // roundToInt: number rounded to the nearest integer
addConstDoc( addConstDoc(
name = "roundToInt", name = "roundToInt",
value = ObjExternCallable.fromBridge { value = statement(Pos.builtIn) {
(thisObj as ObjReal).value.roundToLong().toObj() (it.thisObj as ObjReal).value.roundToLong().toObj()
}, },
doc = "This real number rounded to the nearest integer.", doc = "This real number rounded to the nearest integer.",
type = type("lyng.Int"), type = type("lyng.Int"),

File diff suppressed because it is too large Load Diff

View File

@ -17,13 +17,11 @@
package net.sergeych.lyng.obj package net.sergeych.lyng.obj
import net.sergeych.lyng.FrameSlotRef
import net.sergeych.lyng.PerfFlags import net.sergeych.lyng.PerfFlags
import net.sergeych.lyng.Pos import net.sergeych.lyng.Pos
import net.sergeych.lyng.RecordSlotRef
import net.sergeych.lyng.RegexCache import net.sergeych.lyng.RegexCache
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.requireScope import net.sergeych.lyng.Statement
import net.sergeych.lyng.miniast.* import net.sergeych.lyng.miniast.*
class ObjRegex(val regex: Regex) : Obj() { class ObjRegex(val regex: Regex) : Obj() {
@ -31,20 +29,7 @@ class ObjRegex(val regex: Regex) : Obj() {
override suspend fun operatorMatch(scope: Scope, other: Obj): Obj { override suspend fun operatorMatch(scope: Scope, other: Obj): Obj {
return regex.find(other.cast<ObjString>(scope).value)?.let { return regex.find(other.cast<ObjString>(scope).value)?.let {
val match = ObjRegexMatch(it) scope.addOrUpdateItem("$~", ObjRegexMatch(it))
val record = scope.chainLookupIgnoreClosure("$~", followClosure = true)
if (record != null) {
if (!record.isMutable) {
scope.raiseIllegalAssignment("symbol is readonly: $~")
}
when (val value = record.value) {
is FrameSlotRef -> value.write(match)
is RecordSlotRef -> value.write(match)
else -> record.value = match
}
} else {
scope.addOrUpdateItem("$~", match)
}
ObjTrue ObjTrue
} ?: ObjFalse } ?: ObjFalse
} }
@ -91,11 +76,14 @@ class ObjRegex(val regex: Regex) : Obj() {
} }
createField( createField(
name = "operatorMatch", name = "operatorMatch",
initialValue = ObjExternCallable.fromBridge { initialValue = object : Statement() {
val other = args.firstAndOnly(Pos.builtIn) override val pos: Pos = Pos.builtIn
val scope = requireScope()
val targetScope = scope.parent ?: scope override suspend fun execute(scope: Scope): Obj {
(thisObj as ObjRegex).operatorMatch(targetScope, other) val other = scope.args.firstAndOnly(pos)
val targetScope = scope.parent ?: scope
return (scope.thisObj as ObjRegex).operatorMatch(targetScope, other)
}
}, },
type = ObjRecord.Type.Fun type = ObjRecord.Type.Fun
) )

View File

@ -175,7 +175,7 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
returns = type("lyng.Set"), returns = type("lyng.Set"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
thisAs<ObjSet>().mul(requireScope(), args.firstAndOnly()) thisAs<ObjSet>().mul(this, args.firstAndOnly())
} }
addFnDoc( addFnDoc(
name = "iterator", name = "iterator",
@ -192,7 +192,7 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
returns = type("lyng.Set"), returns = type("lyng.Set"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
thisAs<ObjSet>().plus(requireScope(), args.firstAndOnly()) thisAs<ObjSet>().plus(this, args.firstAndOnly())
} }
addFnDoc( addFnDoc(
name = "subtract", name = "subtract",
@ -201,7 +201,7 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
returns = type("lyng.Set"), returns = type("lyng.Set"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
thisAs<ObjSet>().minus(requireScope(), args.firstAndOnly()) thisAs<ObjSet>().minus(this, args.firstAndOnly())
} }
addFnDoc( addFnDoc(
name = "remove", name = "remove",

View File

@ -25,8 +25,8 @@ import net.sergeych.lyng.PerfFlags
import net.sergeych.lyng.Pos import net.sergeych.lyng.Pos
import net.sergeych.lyng.RegexCache import net.sergeych.lyng.RegexCache
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement
import net.sergeych.lyng.miniast.* import net.sergeych.lyng.miniast.*
import net.sergeych.lyng.requireScope
import net.sergeych.lynon.LynonDecoder import net.sergeych.lynon.LynonDecoder
import net.sergeych.lynon.LynonEncoder import net.sergeych.lynon.LynonEncoder
import net.sergeych.lynon.LynonType import net.sergeych.lynon.LynonType
@ -137,7 +137,6 @@ data class ObjString(val value: String) : Obj() {
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj = override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj =
ObjString(decoder.unpackBinaryData().decodeToString()) ObjString(decoder.unpackBinaryData().decodeToString())
}.apply { }.apply {
isClosed = true
addFnDoc( addFnDoc(
name = "iterator", name = "iterator",
doc = "Iterator over characters of this string.", doc = "Iterator over characters of this string.",
@ -345,10 +344,14 @@ data class ObjString(val value: String) : Obj() {
name = "re", name = "re",
initialValue = ObjProperty( initialValue = ObjProperty(
name = "re", name = "re",
getter = ObjExternCallable.fromBridge { getter = object : Statement() {
val pattern = (thisObj as ObjString).value override val pos: Pos = Pos.builtIn
val re = if (PerfFlags.REGEX_CACHE) RegexCache.get(pattern) else pattern.toRegex()
ObjRegex(re) override suspend fun execute(scope: Scope): Obj {
val pattern = (scope.thisObj as ObjString).value
val re = if (PerfFlags.REGEX_CACHE) RegexCache.get(pattern) else pattern.toRegex()
return ObjRegex(re)
}
}, },
setter = null setter = null
), ),
@ -356,11 +359,14 @@ data class ObjString(val value: String) : Obj() {
) )
createField( createField(
name = "operatorMatch", name = "operatorMatch",
initialValue = ObjExternCallable.fromBridge { initialValue = object : Statement() {
val other = args.firstAndOnly(Pos.builtIn) override val pos: Pos = Pos.builtIn
val scope = requireScope()
val targetScope = scope.parent ?: scope override suspend fun execute(scope: Scope): Obj {
(thisObj as ObjString).operatorMatch(targetScope, other) val other = scope.args.firstAndOnly(pos)
val targetScope = scope.parent ?: scope
return (scope.thisObj as ObjString).operatorMatch(targetScope, other)
}
}, },
type = ObjRecord.Type.Fun type = ObjRecord.Type.Fun
) )

View File

@ -1,38 +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.obj
import net.sergeych.lyng.Scope
import net.sergeych.lyng.ScopeFacade
import net.sergeych.lyng.requireExactCount as coreRequireExactCount
import net.sergeych.lyng.requireNoArgs as coreRequireNoArgs
import net.sergeych.lyng.requireOnlyArg as coreRequireOnlyArg
import net.sergeych.lyng.requiredArg as coreRequiredArg
import net.sergeych.lyng.requireScope as coreRequireScope
import net.sergeych.lyng.thisAs as coreThisAs
inline fun <reified T : Obj> ScopeFacade.requiredArg(index: Int): T = coreRequiredArg(index)
inline fun <reified T : Obj> ScopeFacade.requireOnlyArg(): T = coreRequireOnlyArg()
fun ScopeFacade.requireExactCount(count: Int) = coreRequireExactCount(count)
fun ScopeFacade.requireNoArgs() = coreRequireNoArgs()
inline fun <reified T : Obj> ScopeFacade.thisAs(): T = coreThisAs()
internal fun ScopeFacade.requireScope(): Scope = coreRequireScope()

View File

@ -64,27 +64,21 @@ abstract class ImportProvider(
fun newModuleAt(pos: Pos): ModuleScope = fun newModuleAt(pos: Pos): ModuleScope =
ModuleScope(this, pos, "unknown") ModuleScope(this, pos, "unknown")
private data class StdScopeSeed( private var cachedStdScope = CachedExpression<Scope>()
val stdlib: ModuleScope,
val plan: Map<String, Int>
)
private var cachedStdScope = CachedExpression<StdScopeSeed>() suspend fun newStdScope(pos: Pos = Pos.builtIn): Scope =
cachedStdScope.get {
suspend fun newStdScope(pos: Pos = Pos.builtIn): Scope { val module = newModuleAt(pos)
val seed = cachedStdScope.get {
val stdlib = prepareImport(pos, "lyng.stdlib", null) val stdlib = prepareImport(pos, "lyng.stdlib", null)
val plan = LinkedHashMap<String, Int>() val plan = LinkedHashMap<String, Int>()
for ((name, record) in stdlib.objects) { for ((name, record) in stdlib.objects) {
if (!record.visibility.isPublic) continue if (!record.visibility.isPublic) continue
plan[name] = plan.size plan[name] = plan.size
} }
StdScopeSeed(stdlib, plan) if (plan.isNotEmpty()) module.applySlotPlan(plan)
} stdlib.importInto(module, null)
val module = newModuleAt(pos) module
if (seed.plan.isNotEmpty()) module.applySlotPlan(seed.plan) }.createChildScope()
seed.stdlib.importInto(module, null)
return module
}
} }

View File

@ -70,7 +70,7 @@ object CompileTimeResolver {
source, source,
importProvider, importProvider,
resolutionSink = collector, resolutionSink = collector,
compileBytecode = false, useBytecodeStatements = false,
allowUnresolvedRefs = true allowUnresolvedRefs = true
) )
return collector.buildReport() return collector.buildReport()

View File

@ -19,6 +19,17 @@ 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.ObjClass
import net.sergeych.lyng.obj.ObjInt
import net.sergeych.lyng.obj.ObjIterable
import net.sergeych.lyng.obj.ObjNull
import net.sergeych.lyng.obj.ObjException
import net.sergeych.lyng.obj.ObjRange
import net.sergeych.lyng.obj.ObjRecord
import net.sergeych.lyng.obj.ObjString
import net.sergeych.lyng.obj.ObjVoid
import net.sergeych.lyng.obj.toBool
import net.sergeych.lyng.obj.toInt
import net.sergeych.lyng.obj.toLong
fun String.toSource(name: String = "eval"): Source = Source(name, this) fun String.toSource(name: String = "eval"): Source = Source(name, this)
@ -60,10 +71,6 @@ abstract class Statement(
suspend fun call(scope: Scope, vararg args: Obj) = execute(scope.createChildScope(args = Arguments(*args))) suspend fun call(scope: Scope, vararg args: Obj) = execute(scope.createChildScope(args = Arguments(*args)))
protected fun bytecodeOnly(scope: Scope, label: String): Nothing {
return scope.raiseIllegalState("bytecode-only execution is required; $label needs compiled bytecode")
}
} }
class IfStatement( class IfStatement(
@ -73,7 +80,11 @@ class IfStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return bytecodeOnly(scope, "if statement") return if (condition.execute(scope).toBool()) {
ifBody.execute(scope)
} else {
elseBody?.execute(scope) ?: ObjVoid
}
} }
} }
@ -92,7 +103,200 @@ class ForInStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return bytecodeOnly(scope, "for-in statement") val forContext = scope.createChildScope(pos)
if (loopSlotPlan.isNotEmpty()) {
forContext.applySlotPlan(loopSlotPlan)
}
val loopSO = forContext.addItem(loopVarName, true, ObjNull)
val loopSlotIndex = forContext.getSlotIndexOf(loopVarName) ?: -1
if (constRange != null && PerfFlags.PRIMITIVE_FASTOPS) {
return loopIntRange(
forContext,
constRange.start,
constRange.endExclusive,
loopSO,
loopSlotIndex,
body,
elseStatement,
label,
canBreak
)
}
val sourceObj = source.execute(forContext)
return if (sourceObj is ObjRange && sourceObj.isIntRange && !sourceObj.hasExplicitStep && PerfFlags.PRIMITIVE_FASTOPS) {
loopIntRange(
forContext,
sourceObj.start!!.toLong(),
if (sourceObj.isEndInclusive) sourceObj.end!!.toLong() + 1 else sourceObj.end!!.toLong(),
loopSO,
loopSlotIndex,
body,
elseStatement,
label,
canBreak
)
} else if (sourceObj.isInstanceOf(ObjIterable)) {
loopIterable(forContext, sourceObj, loopSO, body, elseStatement, label, canBreak)
} else {
val size = runCatching { sourceObj.readField(forContext, "size").value.toInt() }
.getOrElse {
throw ScriptError(
pos,
"object is not enumerable: no size in $sourceObj",
it
)
}
var result: Obj = ObjVoid
var breakCaught = false
if (size > 0) {
var current = runCatching { sourceObj.getAt(forContext, ObjInt.of(0)) }
.getOrElse {
throw ScriptError(
pos,
"object is not enumerable: no index access for ${sourceObj.inspect(scope)}",
it
)
}
var index = 0
while (true) {
loopSO.value = current
try {
result = body.execute(forContext)
} catch (lbe: LoopBreakContinueException) {
if (lbe.label == label || lbe.label == null) {
breakCaught = true
if (lbe.doContinue) continue
result = lbe.result
break
} else {
throw lbe
}
}
if (++index >= size) break
current = sourceObj.getAt(forContext, ObjInt.of(index.toLong()))
}
}
if (!breakCaught && elseStatement != null) {
result = elseStatement.execute(scope)
}
result
}
}
private suspend fun loopIntRange(
forScope: Scope,
start: Long,
end: Long,
loopVar: ObjRecord,
loopSlotIndex: Int,
body: Statement,
elseStatement: Statement?,
label: String?,
catchBreak: Boolean,
): Obj {
var result: Obj = ObjVoid
val cacheLow = ObjInt.CACHE_LOW
val cacheHigh = ObjInt.CACHE_HIGH
val useCache = start >= cacheLow && end <= cacheHigh + 1
val cache = if (useCache) ObjInt.cacheArray() else null
val useSlot = loopSlotIndex >= 0
if (catchBreak) {
if (useCache && cache != null) {
var i = start
while (i < end) {
val v = cache[(i - cacheLow).toInt()]
if (useSlot) forScope.setSlotValue(loopSlotIndex, v) else loopVar.value = v
try {
result = body.execute(forScope)
} catch (lbe: LoopBreakContinueException) {
if (lbe.label == label || lbe.label == null) {
if (lbe.doContinue) {
i++
continue
}
return lbe.result
}
throw lbe
}
i++
}
} else {
for (i in start..<end) {
val v = ObjInt.of(i)
if (useSlot) forScope.setSlotValue(loopSlotIndex, v) else loopVar.value = v
try {
result = body.execute(forScope)
} catch (lbe: LoopBreakContinueException) {
if (lbe.label == label || lbe.label == null) {
if (lbe.doContinue) continue
return lbe.result
}
throw lbe
}
}
}
} else {
if (useCache && cache != null) {
var i = start
while (i < end) {
val v = cache[(i - cacheLow).toInt()]
if (useSlot) forScope.setSlotValue(loopSlotIndex, v) else loopVar.value = v
result = body.execute(forScope)
i++
}
} else {
for (i in start..<end) {
val v = ObjInt.of(i)
if (useSlot) forScope.setSlotValue(loopSlotIndex, v) else loopVar.value = v
result = body.execute(forScope)
}
}
}
return elseStatement?.execute(forScope) ?: result
}
private suspend fun loopIterable(
forScope: Scope,
sourceObj: Obj,
loopVar: ObjRecord,
body: Statement,
elseStatement: Statement?,
label: String?,
catchBreak: Boolean,
): Obj {
var result: Obj = ObjVoid
var breakCaught = false
sourceObj.enumerate(forScope) { item ->
loopVar.value = item
if (catchBreak) {
try {
result = body.execute(forScope)
true
} catch (lbe: LoopBreakContinueException) {
if (lbe.label == label || lbe.label == null) {
breakCaught = true
if (lbe.doContinue) true else {
result = lbe.result
false
}
} else {
throw lbe
}
}
} else {
result = body.execute(forScope)
true
}
}
if (!breakCaught && elseStatement != null) {
result = elseStatement.execute(forScope)
}
return result
} }
} }
@ -106,7 +310,29 @@ class WhileStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return bytecodeOnly(scope, "while statement") var result: Obj = ObjVoid
var wasBroken = false
while (condition.execute(scope).toBool()) {
val loopScope = scope.createChildScope().apply { skipScopeCreation = true }
if (canBreak) {
try {
result = body.execute(loopScope)
} catch (lbe: LoopBreakContinueException) {
if (lbe.label == label || lbe.label == null) {
if (lbe.doContinue) continue
result = lbe.result
wasBroken = true
break
} else {
throw lbe
}
}
} else {
result = body.execute(loopScope)
}
}
if (!wasBroken) elseStatement?.let { s -> result = s.execute(scope) }
return result
} }
} }
@ -119,7 +345,30 @@ class DoWhileStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return bytecodeOnly(scope, "do-while statement") var wasBroken = false
var result: Obj = ObjVoid
while (true) {
val doScope = scope.createChildScope().apply { skipScopeCreation = true }
try {
result = body.execute(doScope)
} catch (e: LoopBreakContinueException) {
if (e.label == label || e.label == null) {
if (!e.doContinue) {
result = e.result
wasBroken = true
break
}
// continue: fall through to condition check
} else {
throw e
}
}
if (!condition.execute(doScope).toBool()) {
break
}
}
if (!wasBroken) elseStatement?.let { s -> result = s.execute(scope) }
return result
} }
} }
@ -129,7 +378,12 @@ class BreakStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return bytecodeOnly(scope, "break statement") val returnValue = resultExpr?.execute(scope)
throw LoopBreakContinueException(
doContinue = false,
label = label,
result = returnValue ?: ObjVoid
)
} }
} }
@ -138,7 +392,10 @@ class ContinueStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return bytecodeOnly(scope, "continue statement") throw LoopBreakContinueException(
doContinue = true,
label = label,
)
} }
} }
@ -148,7 +405,8 @@ class ReturnStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return bytecodeOnly(scope, "return statement") val returnValue = resultExpr?.execute(scope) ?: ObjVoid
throw ReturnException(returnValue, label)
} }
} }
@ -157,7 +415,28 @@ class ThrowStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return bytecodeOnly(scope, "throw statement") var errorObject = throwExpr.execute(scope)
val throwScope = scope.createChildScope(pos = pos)
if (errorObject is ObjString) {
errorObject = ObjException(throwScope, errorObject.value).apply { getStackTrace() }
}
if (!errorObject.isInstanceOf(ObjException.Root)) {
throwScope.raiseError("this is not an exception object: $errorObject")
}
if (errorObject is ObjException) {
errorObject = ObjException(
errorObject.exceptionClass,
throwScope,
errorObject.message,
errorObject.extraData,
errorObject.useStackTrace
).apply { getStackTrace() }
throwScope.raiseError(errorObject)
} else {
val msg = errorObject.invokeInstanceMethod(scope, "message").toString(scope).value
throwScope.raiseError(errorObject, pos, msg)
}
return ObjVoid
} }
} }
@ -165,7 +444,7 @@ class ExpressionStatement(
val ref: net.sergeych.lyng.obj.ObjRef, val ref: net.sergeych.lyng.obj.ObjRef,
override val pos: Pos override val pos: Pos
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj = bytecodeOnly(scope, "expression statement") override suspend fun execute(scope: Scope): Obj = ref.evalValue(scope)
} }
fun Statement.raise(text: String): Nothing { fun Statement.raise(text: String): Nothing {
@ -177,8 +456,20 @@ fun Statement.require(cond: Boolean, message: () -> String) {
if (!cond) raise(message()) if (!cond) raise(message())
} }
fun statement(pos: Pos, isStaticConst: Boolean = false, isConst: Boolean = false, f: suspend (Scope) -> Obj): Statement =
object : Statement(isStaticConst, isConst) {
override val pos: Pos = pos
override suspend fun execute(scope: Scope): Obj = f(scope)
}
fun statement(isStaticConst: Boolean = false, isConst: Boolean = false, f: suspend Scope.() -> Obj): Statement =
object : Statement(isStaticConst, isConst) {
override val pos: Pos = Pos.builtIn
override suspend fun execute(scope: Scope): Obj = f(scope)
}
object NopStatement: Statement(true, true, ObjType.Void) { object NopStatement: Statement(true, true, ObjType.Void) {
override val pos: Pos = Pos.builtIn override val pos: Pos = Pos.builtIn
override suspend fun execute(scope: Scope): Obj = bytecodeOnly(scope, "nop statement") override suspend fun execute(scope: Scope): Obj = ObjVoid
} }

View File

@ -106,7 +106,7 @@ object LyngLanguageTools {
request.importProvider, request.importProvider,
miniSink = miniSink, miniSink = miniSink,
resolutionSink = resolutionCollector, resolutionSink = resolutionCollector,
compileBytecode = false, useBytecodeStatements = false,
allowUnresolvedRefs = true, allowUnresolvedRefs = true,
seedScope = request.seedScope seedScope = request.seedScope
) )

View File

@ -18,8 +18,6 @@
package net.sergeych.lynon package net.sergeych.lynon
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.requireOnlyArg
import net.sergeych.lyng.requireScope
import net.sergeych.lyng.obj.* import net.sergeych.lyng.obj.*
// Most often used types: // Most often used types:
@ -44,10 +42,10 @@ object ObjLynonClass : ObjClass("Lynon") {
init { init {
addClassConst("test", ObjString("test_const")) addClassConst("test", ObjString("test_const"))
addClassFn("encode") { addClassFn("encode") {
encodeAny(requireScope(), requireOnlyArg<Obj>()) encodeAny(this, requireOnlyArg<Obj>())
} }
addClassFn("decode") { addClassFn("decode") {
decodeAny(requireScope(), requireOnlyArg<Obj>()) decodeAny(this, requireOnlyArg<Obj>())
} }
} }
} }

View File

@ -1,148 +0,0 @@
package net.sergeych.lyng
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.bridge.LyngClassBridge
import net.sergeych.lyng.bridge.data
import net.sergeych.lyng.obj.ObjInt
import net.sergeych.lyng.obj.ObjString
class BridgeBindingTest {
private data class CounterState(var count: Long)
@Test
fun testExternClassBinding() = runTest {
val im = Script.defaultImportManager.copy()
im.addPackage("bridge.mod") { scope ->
scope.eval(
"""
class Foo {
extern fun add(a: Int, b: Int): Int
extern val status: String
extern var count: Int
private extern fun secret(): Int
static extern fun ping(): Int
fun callAdd() = add(2, 3)
fun callSecret() = secret()
fun bump() { count = count + 1 }
}
class Bar {
extern var count: Int
fun inc() { count = count + 1 }
}
""".trimIndent()
)
}
LyngClassBridge.bind(className = "Foo", module = "bridge.mod", importManager = im) {
classData = "OK"
init { _ ->
data = CounterState(0)
}
addFun("add") { _, _, args ->
val a = (args.list[0] as ObjInt).value
val b = (args.list[1] as ObjInt).value
ObjInt.of(a + b)
}
addVal("status") { _, _ -> ObjString(classData as String) }
addVar(
"count",
get = { _, instance ->
val st = (instance as net.sergeych.lyng.obj.ObjInstance).data as CounterState
ObjInt.of(st.count)
},
set = { _, instance, value ->
val st = (instance as net.sergeych.lyng.obj.ObjInstance).data as CounterState
st.count = (value as ObjInt).value
}
)
addFun("secret") { _, _, _ -> ObjInt.of(42) }
addFun("ping") { _, _, _ -> ObjInt.of(7) }
}
LyngClassBridge.bind(className = "Bar", module = "bridge.mod", importManager = im) {
initWithInstance { _, instance ->
(instance as net.sergeych.lyng.obj.ObjInstance).data = CounterState(10)
}
addVar(
"count",
get = { _, instance ->
val st = (instance as net.sergeych.lyng.obj.ObjInstance).data as CounterState
ObjInt.of(st.count)
},
set = { _, instance, value ->
val st = (instance as net.sergeych.lyng.obj.ObjInstance).data as CounterState
st.count = (value as ObjInt).value
}
)
}
val scope = im.newStdScope()
scope.eval(
"""
import bridge.mod
val f = Foo()
assertEquals(5, f.callAdd())
assertEquals("OK", f.status)
assertEquals(0, f.count)
f.bump()
assertEquals(1, f.count)
assertEquals(42, f.callSecret())
assertEquals(7, Foo.ping())
val b = Bar()
assertEquals(10, b.count)
b.inc()
assertEquals(11, b.count)
""".trimIndent()
)
val privateCallFails = try {
scope.eval(
"""
import bridge.mod
Foo().secret()
""".trimIndent()
)
false
} catch (_: ScriptError) {
true
}
assertTrue(privateCallFails)
}
@Test
fun testBindAfterInstanceFails() = runTest {
val im = Script.defaultImportManager.copy()
im.addPackage("bridge.late") { scope ->
scope.eval(
"""
class Late {
extern val status: String
}
""".trimIndent()
)
}
val scope = im.newStdScope()
scope.eval(
"""
import bridge.late
val l = Late()
""".trimIndent()
)
val bindFailed = try {
LyngClassBridge.bind(className = "Late", module = "bridge.late", importManager = im) {
addVal("status") { _, _ -> ObjString("late") }
}
false
} catch (_: ScriptError) {
true
}
assertTrue(bindFailed)
}
}

View File

@ -1,108 +0,0 @@
package net.sergeych.lyng
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.bridge.BridgeCallByName
import net.sergeych.lyng.bridge.LookupSpec
import net.sergeych.lyng.bridge.LookupTarget
import net.sergeych.lyng.bridge.resolver
import net.sergeych.lyng.obj.ObjInstance
import net.sergeych.lyng.obj.ObjInt
class BridgeResolverTest {
@Test
fun testLocalAndMemberHandles() = runTest {
val im = Script.defaultImportManager.copy()
val scope = im.newStdScope()
scope.eval(
"""
var x = 1
fun add(a: Int, b: Int) = a + b
class Foo {
var count: Int = 0
fun bump() { count = count + 1 }
}
val f = Foo()
""".trimIndent()
)
val facade = scope.asFacade()
val resolver = facade.resolver()
val xHandle = resolver.resolveVar("x")
assertEquals(1, (xHandle.get(facade) as ObjInt).value)
xHandle.set(facade, ObjInt.of(5))
assertEquals(5, (xHandle.get(facade) as ObjInt).value)
val addHandle = resolver.resolveCallable("add")
val addResult = addHandle.call(facade, Arguments(ObjInt.of(2), ObjInt.of(3)))
assertEquals(5, (addResult as ObjInt).value)
val fRec = scope["f"] ?: error("f not found")
val fObj = scope.resolve(fRec, "f") as ObjInstance
val countHandle = resolver.resolveMemberVar(fObj, "count")
assertEquals(0, (countHandle.get(facade) as ObjInt).value)
val bumpHandle = resolver.resolveMemberCallable(fObj, "bump")
bumpHandle.call(facade)
assertEquals(1, (countHandle.get(facade) as ObjInt).value)
val callByName = resolver as BridgeCallByName
val addByName = callByName.callByName(facade, "add", Arguments(ObjInt.of(4), ObjInt.of(6)))
assertEquals(10, (addByName as ObjInt).value)
}
@Test
fun testExtensionCallableHandle() = runTest {
val im = Script.defaultImportManager.copy()
val scope = im.newStdScope()
scope.eval(
"""
class Foo {
var count: Int = 1
}
fun Foo.bumped() = count + 2
val f = Foo()
""".trimIndent()
)
val facade = scope.asFacade()
val resolver = facade.resolver()
val fooRec = scope["Foo"] ?: error("Foo not found")
val fooClass = scope.resolve(fooRec, "Foo") as net.sergeych.lyng.obj.ObjClass
val fRec = scope["f"] ?: error("f not found")
val fObj = scope.resolve(fRec, "f") as ObjInstance
val extHandle = resolver.resolveExtensionCallable(fooClass, "bumped")
val result = extHandle.call(facade, newThisObj = fObj) as ObjInt
assertEquals(3, result.value)
}
@Test
fun testModuleFrameLookup() = runTest {
val im = Script.defaultImportManager.copy()
val module = im.newModuleAt(Pos.builtIn)
module.eval(
"""
var x = 7
""".trimIndent()
)
val child = module.createChildScope()
child.eval(
"""
var x = 1
""".trimIndent()
)
val facade = child.asFacade()
val resolver = facade.resolver()
val handle = resolver.resolveVal("x", LookupSpec(targets = setOf(LookupTarget.ModuleFrame)))
val result = handle.get(facade) as ObjInt
assertEquals(7, result.value)
}
}

View File

@ -279,6 +279,26 @@ class BytecodeRecentOpsTest {
assertTrue(disasm.contains("DECL_DELEGATED"), 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 @Test
fun unionMemberDispatchSubtype() = runTest { fun unionMemberDispatchSubtype() = runTest {
eval( eval(
@ -372,10 +392,10 @@ class BytecodeRecentOpsTest {
val script = Compiler.compileWithResolution( val script = Compiler.compileWithResolution(
Source("<fast-local>", code), Source("<fast-local>", code),
Script.defaultImportManager, Script.defaultImportManager,
useBytecodeStatements = true,
useFastLocalRefs = true useFastLocalRefs = true
) )
val result = script.execute(Script.defaultImportManager.newStdScope()) val result = script.execute(Script.defaultImportManager.newStdScope())
assertEquals(2, result.toInt()) assertEquals(2, result.toInt())
} }
} }

View File

@ -38,6 +38,7 @@ class CompileTimeResolutionRuntimeTest {
val script = Compiler.compileWithResolution( val script = Compiler.compileWithResolution(
Source("<strict-slot>", code), Source("<strict-slot>", code),
Script.defaultImportManager, Script.defaultImportManager,
useBytecodeStatements = false,
strictSlotRefs = true strictSlotRefs = true
) )
val result = script.execute(Script.defaultImportManager.newStdScope()) val result = script.execute(Script.defaultImportManager.newStdScope())
@ -60,6 +61,7 @@ class CompileTimeResolutionRuntimeTest {
val script = Compiler.compileWithResolution( val script = Compiler.compileWithResolution(
Source("<shadow-slot>", code), Source("<shadow-slot>", code),
Script.defaultImportManager, Script.defaultImportManager,
useBytecodeStatements = true,
strictSlotRefs = true strictSlotRefs = true
) )
val result = script.execute(Script.defaultImportManager.newStdScope()) val result = script.execute(Script.defaultImportManager.newStdScope())
@ -83,6 +85,7 @@ class CompileTimeResolutionRuntimeTest {
val script = Compiler.compileWithResolution( val script = Compiler.compileWithResolution(
Source("<shadow-block>", code), Source("<shadow-block>", code),
Script.defaultImportManager, Script.defaultImportManager,
useBytecodeStatements = true,
strictSlotRefs = true strictSlotRefs = true
) )
val result = script.execute(Script.defaultImportManager.newStdScope()) val result = script.execute(Script.defaultImportManager.newStdScope())

View File

@ -225,21 +225,6 @@ class MiniAstTest {
assertTrue(names.contains("V2"), "Should contain V2") assertTrue(names.contains("V2"), "Should contain V2")
} }
@Test
fun complete_static_members_only() = runTest {
val code = """
class C {
static fun s() {}
fun i() {}
}
C.<caret>
"""
val items = CompletionEngineLight.completeAtMarkerSuspend(code)
val names = items.map { it.name }.toSet()
assertTrue(names.contains("s"), "Should contain static member")
assertTrue(!names.contains("i"), "Should not contain instance member")
}
@Test @Test
fun miniAst_captures_extern_docs() = runTest { fun miniAst_captures_extern_docs() = runTest {
val code = """ val code = """

View File

@ -17,13 +17,11 @@
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.Benchmarks import net.sergeych.lyng.Benchmarks
import net.sergeych.lyng.BytecodeBodyProvider
import net.sergeych.lyng.Compiler import net.sergeych.lyng.Compiler
import net.sergeych.lyng.ForInStatement import net.sergeych.lyng.ForInStatement
import net.sergeych.lyng.Script import net.sergeych.lyng.Script
import net.sergeych.lyng.Statement import net.sergeych.lyng.Statement
import net.sergeych.lyng.bytecode.CmdDisassembler import net.sergeych.lyng.bytecode.CmdDisassembler
import net.sergeych.lyng.bytecode.CmdFunction
import net.sergeych.lyng.bytecode.BytecodeStatement import net.sergeych.lyng.bytecode.BytecodeStatement
import net.sergeych.lyng.obj.ObjInt import net.sergeych.lyng.obj.ObjInt
import kotlin.time.TimeSource import kotlin.time.TimeSource
@ -59,7 +57,6 @@ class NestedRangeBenchmarkTest {
scope.eval(script) scope.eval(script)
val fnDisasm = scope.disassembleSymbol("naiveCountHappyNumbers") val fnDisasm = scope.disassembleSymbol("naiveCountHappyNumbers")
println("[DEBUG_LOG] [BENCH] nested-happy function naiveCountHappyNumbers cmd:\n$fnDisasm") println("[DEBUG_LOG] [BENCH] nested-happy function naiveCountHappyNumbers cmd:\n$fnDisasm")
dumpFunctionSlots(scope, "naiveCountHappyNumbers")
runMode(scope) runMode(scope)
} }
@ -99,30 +96,4 @@ class NestedRangeBenchmarkTest {
} }
} }
private fun dumpFunctionSlots(scope: net.sergeych.lyng.Scope, name: String) {
val record = scope[name]?.value as? Statement ?: return
val fn = bytecodeFromStatement(record) ?: return
val scopeNames = fn.scopeSlotNames.mapIndexedNotNull { idx, slotName ->
slotName?.let { "$it@${fn.scopeSlotIndices[idx]}" }
}
val localNames = fn.localSlotNames.mapIndexedNotNull { idx, slotName ->
slotName?.let { "$it@$idx" }
}
val captures = fn.localSlotNames.mapIndexedNotNull { idx, slotName ->
if (slotName != null && fn.localSlotCaptures.getOrNull(idx) == true) "$slotName@$idx" else null
}
println(
"[DEBUG_LOG] [BENCH] nested-happy function $name slots: " +
"scopeCount=${fn.scopeSlotCount} " +
"scope=[${scopeNames.joinToString(", ")}] " +
"locals=[${localNames.joinToString(", ")}] " +
"captures=[${captures.joinToString(", ")}]"
)
}
private fun bytecodeFromStatement(stmt: Statement): CmdFunction? {
return (stmt as? BytecodeStatement)?.bytecodeFunction()
?: (stmt as? BytecodeBodyProvider)?.bytecodeBody()?.bytecodeFunction()
}
} }

View File

@ -1,79 +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.
*
*/
import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.Benchmarks
import net.sergeych.lyng.Script
import net.sergeych.lyng.obj.ObjInt
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.time.TimeSource
class RepresentativeBenchmarkTest {
@Test
fun benchmarkMixedOps() = runTest {
if (!Benchmarks.enabled) return@runTest
val iterations = 40000
val script = """
fun mixedBench(n) {
var acc = 0
var len = 0
val list = [1,2,3,4,5,6,7,8]
val map = {"a":1, "b":2, "c":3}
fun bump(x) { x + 1 }
var i = 0
while(i < n) {
acc += i
acc += bump(i)
if( i % 2 == 0 ) acc += 3 else acc -= 1
acc += list[i % 8]
acc += map["a"]
if( i % 7 == 0 ) len += 1
if( i % 11 == 0 ) len += 1
i++
}
acc + len
}
""".trimIndent()
val scope = Script.newScope()
scope.eval(script)
val expected = expectedValue(iterations)
val start = TimeSource.Monotonic.markNow()
val result = scope.eval("mixedBench($iterations)") as ObjInt
val elapsedMs = start.elapsedNow().inWholeMilliseconds
println("[DEBUG_LOG] [BENCH] mixed-ops elapsed=${elapsedMs} ms")
assertEquals(expected, result.value)
}
private fun expectedValue(iterations: Int): Long {
val list = intArrayOf(1, 2, 3, 4, 5, 6, 7, 8)
var acc = 0L
var len = 0L
for (i in 0 until iterations) {
acc += i
acc += i + 1L
if (i % 2 == 0) acc += 3 else acc -= 1
acc += list[i % list.size]
acc += 1
if (i % 7 == 0) len += 1
if (i % 11 == 0) len += 1
}
return acc + len
}
}

View File

@ -1,163 +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.
*
*/
import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.Benchmarks
import net.sergeych.lyng.BytecodeBodyProvider
import net.sergeych.lyng.Script
import net.sergeych.lyng.Statement
import net.sergeych.lyng.bytecode.*
import net.sergeych.lyng.obj.ObjInt
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.time.TimeSource
class RepresentativeObjectBenchmarkTest {
@Test
fun benchmarkObjectOps() = runTest {
if (!Benchmarks.enabled) return@runTest
val iterations = 20000
val script = """
fun objectBench(n) {
var acc = 0
var s = ""
val list = ["a","b","c","d","e","f","g","h","i","j"]
val rlist = [1.0,2.0,3.0,4.0,5.0]
val map = {"a":1, "b":2, "c":3, "d":4}
var i = 0
while(i < n) {
val k = list[i % 10]
val v = map[k] ?: 0
val j = i % 10
val r = i * 0.5
val rr = rlist[j % 5]
val oi: Object = i
val oj: Object = j
val or: Object = r
val or2: Object = r + 1.0
s = s + k
if( k in list ) acc += 1
if( k == "a" ) acc += v else acc -= 1
if( k != "z" ) acc += 1
if( k < "m" ) acc += 1 else acc -= 1
if( k >= "d" ) acc += 1 else acc -= 1
if( j < 7 ) acc += 1 else acc -= 1
if( j >= 3 ) acc += 1 else acc -= 1
if( r <= 2.5 ) acc += 1 else acc -= 1
if( r > 3.5 ) acc += 1 else acc -= 1
if( rr < 3.5 ) acc += 1 else acc -= 1
if( rr >= 2.5 ) acc += 1 else acc -= 1
if( oi == oj ) acc += 1 else acc -= 1
if( oi < oj ) acc += 1 else acc -= 1
if( or > or2 ) acc += 1 else acc -= 1
if( or <= or2 ) acc += 1 else acc -= 1
if( i % 4 == 0 ) acc += list.size
i++
}
acc + s.size
}
""".trimIndent()
val scope = Script.newScope()
scope.eval(script)
val expected = expectedValue(iterations)
dumpFunctionBytecode(scope, "objectBench")
dumpFastCompareStats(scope, "objectBench")
val start = TimeSource.Monotonic.markNow()
val result = scope.eval("objectBench($iterations)") as ObjInt
val elapsedMs = start.elapsedNow().inWholeMilliseconds
println("[DEBUG_LOG] [BENCH] object-ops elapsed=${elapsedMs} ms")
assertEquals(expected, result.value)
}
private fun expectedValue(iterations: Int): Long {
val list = arrayOf("a","b","c","d","e","f","g","h","i","j")
val rlist = doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0)
val map = mapOf("a" to 1, "b" to 2, "c" to 3, "d" to 4)
var acc = 0L
var s = ""
for (i in 0 until iterations) {
val k = list[i % list.size]
val v = map[k] ?: 0
val j = i % 10
val r = i * 0.5
val rr = rlist[j % 5]
val oi = i
val oj = j
val or = r
val or2 = r + 1.0
s += k
acc += 1
if (k == "a") acc += v else acc -= 1
if (k != "z") acc += 1
if (k < "m") acc += 1 else acc -= 1
if (k >= "d") acc += 1 else acc -= 1
if (j < 7) acc += 1 else acc -= 1
if (j >= 3) acc += 1 else acc -= 1
if (r <= 2.5) acc += 1 else acc -= 1
if (r > 3.5) acc += 1 else acc -= 1
if (rr < 3.5) acc += 1 else acc -= 1
if (rr >= 2.5) acc += 1 else acc -= 1
if (oi == oj) acc += 1 else acc -= 1
if (oi < oj) acc += 1 else acc -= 1
if (or > or2) acc += 1 else acc -= 1
if (or <= or2) acc += 1 else acc -= 1
if (i % 4 == 0) acc += list.size
}
return acc + s.length
}
private fun dumpFunctionBytecode(scope: net.sergeych.lyng.Scope, name: String) {
val disasm = scope.disassembleSymbol(name)
println("[DEBUG_LOG] [BENCH] object-ops cmd:\n$disasm")
}
private fun dumpFastCompareStats(scope: net.sergeych.lyng.Scope, name: String) {
val fn = resolveBytecodeFunction(scope, name) ?: return
var strLocal = 0
var intObjLocal = 0
var realObjLocal = 0
for (cmd in fn.cmds) {
when (cmd) {
is CmdCmpEqStrLocal,
is CmdCmpNeqStrLocal,
is CmdCmpLtStrLocal,
is CmdCmpGteStrLocal -> strLocal += 1
is CmdCmpEqIntObjLocal,
is CmdCmpNeqIntObjLocal,
is CmdCmpLtIntObjLocal,
is CmdCmpGteIntObjLocal -> intObjLocal += 1
is CmdCmpEqRealObjLocal,
is CmdCmpNeqRealObjLocal,
is CmdCmpLtRealObjLocal,
is CmdCmpGteRealObjLocal -> realObjLocal += 1
else -> {}
}
}
println(
"[DEBUG_LOG] [BENCH] object-ops fast-compare local cmds: str=$strLocal intObj=$intObjLocal realObj=$realObjLocal"
)
}
private fun resolveBytecodeFunction(scope: net.sergeych.lyng.Scope, name: String): CmdFunction? {
val record = scope.get(name) ?: return null
val stmt = record.value as? Statement ?: return null
return (stmt as? BytecodeStatement)?.bytecodeFunction()
?: (stmt as? BytecodeBodyProvider)?.bytecodeBody()?.bytecodeFunction()
}
}

View File

@ -30,7 +30,6 @@ import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.encodeToJsonElement import kotlinx.serialization.json.encodeToJsonElement
import net.sergeych.lyng.* import net.sergeych.lyng.*
import net.sergeych.lyng.obj.* import net.sergeych.lyng.obj.*
import net.sergeych.lyng.thisAs
import net.sergeych.lyng.pacman.InlineSourcesImportProvider import net.sergeych.lyng.pacman.InlineSourcesImportProvider
import net.sergeych.mp_tools.globalDefer import net.sergeych.mp_tools.globalDefer
import net.sergeych.tools.bm import net.sergeych.tools.bm
@ -657,58 +656,56 @@ class ScriptTest {
@Test @Test
fun whileTest() = runTest { fun whileTest() = runTest {
withTimeout(2.seconds) { assertEquals(
assertEquals( 5.0,
5.0, eval(
eval( """
""" var acc = 0
var acc = 0 while( acc < 5 ) acc = acc + 0.5
while( acc < 5 ) acc = acc + 0.5 acc
"""
).toDouble()
)
assertEquals(
5.0,
eval(
"""
var acc = 0
// return from while
while( acc < 5 ) {
acc = acc + 0.5
acc acc
""" }
).toDouble() """
) ).toDouble()
assertEquals( )
5.0, assertEquals(
eval( 3.0,
""" eval(
var acc = 0 """
// return from while var acc = 0
while( acc < 5 ) { while( acc < 5 ) {
acc = acc + 0.5 acc = acc + 0.5
acc if( acc >= 3 ) break
} }
"""
).toDouble()
)
assertEquals(
3.0,
eval(
"""
var acc = 0
while( acc < 5 ) {
acc = acc + 0.5
if( acc >= 3 ) break
}
acc acc
""" """
).toDouble() ).toDouble()
) )
assertEquals( assertEquals(
17.0, 17.0,
eval( eval(
""" """
var acc = 0 var acc = 0
while( acc < 5 ) { while( acc < 5 ) {
acc = acc + 0.5 acc = acc + 0.5
if( acc >= 3 ) break 17 if( acc >= 3 ) break 17
} }
""" """
).toDouble() ).toDouble()
) )
}
} }
@Test @Test
@ -737,7 +734,7 @@ class ScriptTest {
listOf( listOf(
ArgsDeclaration.Item("a"), ArgsDeclaration.Item("a"),
ArgsDeclaration.Item("b"), ArgsDeclaration.Item("b"),
ArgsDeclaration.Item("c", defaultValue = ObjExternCallable.fromBridge { ObjInt(100) }), ArgsDeclaration.Item("c", defaultValue = statement { ObjInt(100) }),
), ttEnd ), ttEnd
) )
pa.assignToContext(c) pa.assignToContext(c)
@ -1937,6 +1934,7 @@ class ScriptTest {
println("limit reached after "+n+" rounds") println("limit reached after "+n+" rounds")
break sum break sum
} }
n++
} }
else { else {
println("limit not reached") println("limit not reached")
@ -4024,37 +4022,6 @@ class ScriptTest {
) )
} }
@Test
fun moduleFramePersistsAcrossEval() = runTest {
val scope = ModuleScope(Script.defaultImportManager, Pos.builtIn, "test.mod")
scope.eval(
"""
var x = 1
x
""".trimIndent()
)
assertEquals(1, scope.eval("x").toInt())
scope.eval("x = x + 1")
assertEquals(2, scope.eval("x").toInt())
}
@Test
fun moduleExtensionWrapperUsesFrameSlots() = runTest {
val scope = ModuleScope(Script.defaultImportManager, Pos.builtIn, "test.mod.ext")
scope.eval(
"""
fun String.foo() { this + "_m" }
""".trimIndent()
)
assertEquals("a_m", scope.eval("""__ext__String__foo("a")""").toString())
scope.eval(
"""
fun String.foo() { this + "_n" }
""".trimIndent()
)
assertEquals("a_n", scope.eval("""__ext__String__foo("a")""").toString())
}
@Test @Test
fun testThrowReportsSource() = runTest { fun testThrowReportsSource() = runTest {
try { try {

View File

@ -129,6 +129,7 @@ class TypesTest {
println("limit reached after "+n+" rounds") println("limit reached after "+n+" rounds")
break sum break sum
} }
n++
} }
else { else {
println("limit not reached") println("limit not reached")
@ -187,24 +188,12 @@ class TypesTest {
val base = [1, 2] val base = [1, 2]
acceptInts([...base, 3]) acceptInts([...base, 3])
""".trimIndent()) """.trimIndent())
eval("""
fun acceptReals<T: Real>(xs: List<T>) { }
acceptReals([1.0, 2.0, 3.0])
val base = [1.0, 2.0]
acceptReals([...base, 3.0])
""".trimIndent())
assertFailsWith<net.sergeych.lyng.ScriptError> { assertFailsWith<net.sergeych.lyng.ScriptError> {
eval(""" eval("""
fun acceptInts<T: Int>(xs: List<T>) { } fun acceptInts<T: Int>(xs: List<T>) { }
acceptInts([1, "a"]) acceptInts([1, "a"])
""".trimIndent()) """.trimIndent())
} }
assertFailsWith<net.sergeych.lyng.ScriptError> {
eval("""
fun acceptReals<T: Real>(xs: List<T>) { }
acceptReals([1.0, "a"])
""".trimIndent())
}
} }
@Test @Test

View File

@ -45,7 +45,8 @@ class ParamTypeInferenceTest {
Compiler.compileWithResolution( Compiler.compileWithResolution(
Source("<eval>", code.trimIndent()), Source("<eval>", code.trimIndent()),
Script.defaultImportManager, Script.defaultImportManager,
miniSink = sink miniSink = sink,
useBytecodeStatements = false
) )
val mini = sink.build()!! val mini = sink.build()!!
val binding = Binder.bind(code, mini) val binding = Binder.bind(code, mini)

Some files were not shown because too many files have changed in this diff Show More