Compare commits
No commits in common. "fd2da1efd3405856a4d241ce8a35139df844a5f2" and "db4f7d0973584958450143e38dd112fe26460380" have entirely different histories.
fd2da1efd3
...
db4f7d0973
@ -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.
|
||||||
|
|||||||
@ -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 )
|
||||||
|
|||||||
@ -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",
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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,
|
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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() {
|
||||||
|
|||||||
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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
@ -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)
|
||||||
|
}
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 savedCtx = this.currentClassCtx
|
||||||
|
this.currentClassCtx = cls
|
||||||
|
try {
|
||||||
|
(thisObj as? ObjInstance)?.let { i ->
|
||||||
|
val execScope = i.instanceScope.createChildScope(
|
||||||
|
pos = this.pos,
|
||||||
|
args = this.args,
|
||||||
|
newThisObj = i
|
||||||
)
|
)
|
||||||
val memberValue = cls.members[spec.name]?.value ?: compiledFnBody
|
execScope.currentClassCtx = cls
|
||||||
scope.addItem(spec.name, false, memberValue, spec.visibility, callSignature = spec.externCallSignature)
|
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() {
|
||||||
|
|||||||
@ -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")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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]
|
||||||
|
|||||||
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
var current = base
|
if (slot == null) raiseSymbolNotFound(first)
|
||||||
|
LocalSlotRef(first, slot, 0, isMutable = false, isDelegated = false, Pos.builtIn, strict = true)
|
||||||
|
}
|
||||||
|
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}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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")
|
|
||||||
@ -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, _ ->
|
var lastResult: Obj = ObjVoid
|
||||||
seedModuleLocals(frame, moduleTarget, scope)
|
for (s in statements) {
|
||||||
|
lastResult = s.execute(scope)
|
||||||
}
|
}
|
||||||
}
|
return lastResult
|
||||||
if (statements.isNotEmpty()) {
|
|
||||||
scope.raiseIllegalState("bytecode-only execution is required; missing module bytecode")
|
|
||||||
}
|
|
||||||
return ObjVoid
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
||||||
|
override val pos: Pos = Pos.builtIn
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
if (!calculated) {
|
if (!calculated) {
|
||||||
cachedValue = capturedScope.call(builder)
|
cachedValue = builder.execute(capturedScope)
|
||||||
calculated = true
|
calculated = true
|
||||||
}
|
}
|
||||||
cachedValue
|
return 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)}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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}")
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -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>,
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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])
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -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 {
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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,22 +106,12 @@ 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)
|
|
||||||
if (localsFromBinding.isNotEmpty()) {
|
|
||||||
for (sym in localsFromBinding) {
|
|
||||||
if (sym.name.startsWith(prefix, true)) {
|
|
||||||
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)
|
val locals = DocLookupUtils.extractLocalsAt(text, caret)
|
||||||
for (name in locals) {
|
for (name in locals) {
|
||||||
if (name.startsWith(prefix, true)) {
|
if (name.startsWith(prefix, true)) {
|
||||||
out.add(CompletionItem(name, Kind.Value, priority = 150.0))
|
out.add(CompletionItem(name, Kind.Value, priority = 150.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val decls = mini.declarations
|
val decls = mini.declarations
|
||||||
val funs = decls.filterIsInstance<MiniFunDecl>().sortedBy { it.name.lowercase() }
|
val funs = decls.filterIsInstance<MiniFunDecl>().sortedBy { it.name.lowercase() }
|
||||||
@ -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) target.getOrPut(m.name) { mutableListOf() }.add(m)
|
||||||
for (m in cls.members) {
|
|
||||||
if (staticOnly && !m.isStatic) continue
|
|
||||||
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) {
|
||||||
|
|||||||
@ -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,28 +301,19 @@ 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 MiniFunDecl && d.receiver != null && simpleClassNameOf(d.receiver) == name && d.name == member) ||
|
||||||
@ -338,7 +328,6 @@ object DocLookupUtils {
|
|||||||
(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 }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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--
|
||||||
|
|||||||
@ -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()) {
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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>,
|
||||||
|
|||||||
@ -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"
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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>()
|
||||||
|
|||||||
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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",
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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).",
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)"
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
@ -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()
|
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val other = scope.args.firstAndOnly(pos)
|
||||||
val targetScope = scope.parent ?: scope
|
val targetScope = scope.parent ?: scope
|
||||||
(thisObj as ObjRegex).operatorMatch(targetScope, other)
|
return (scope.thisObj as ObjRegex).operatorMatch(targetScope, other)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
type = ObjRecord.Type.Fun
|
type = ObjRecord.Type.Fun
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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",
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
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()
|
val re = if (PerfFlags.REGEX_CACHE) RegexCache.get(pattern) else pattern.toRegex()
|
||||||
ObjRegex(re)
|
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()
|
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val other = scope.args.firstAndOnly(pos)
|
||||||
val targetScope = scope.parent ?: scope
|
val targetScope = scope.parent ?: scope
|
||||||
(thisObj as ObjString).operatorMatch(targetScope, other)
|
return (scope.thisObj as ObjString).operatorMatch(targetScope, other)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
type = ObjRecord.Type.Fun
|
type = ObjRecord.Type.Fun
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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()
|
|
||||||
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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())
|
||||||
|
|||||||
@ -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 = """
|
||||||
|
|||||||
@ -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()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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,7 +656,6 @@ class ScriptTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun whileTest() = runTest {
|
fun whileTest() = runTest {
|
||||||
withTimeout(2.seconds) {
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
5.0,
|
5.0,
|
||||||
eval(
|
eval(
|
||||||
@ -709,7 +707,6 @@ class ScriptTest {
|
|||||||
).toDouble()
|
).toDouble()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testAssignArgumentsNoEllipsis() = runTest {
|
fun testAssignArgumentsNoEllipsis() = runTest {
|
||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
Loading…
x
Reference in New Issue
Block a user