Compare commits
2 Commits
3d8bfe8863
...
24937c7cf5
| Author | SHA1 | Date | |
|---|---|---|---|
| 24937c7cf5 | |||
| ecc4d8b9ed |
@ -72,6 +72,14 @@ For module-level APIs, the default workflow is:
|
||||
1. declare globals in Lyng using `extern fun` / `extern val` / `extern var`;
|
||||
2. bind Kotlin implementation via `ModuleScope.globalBinder()`.
|
||||
|
||||
This is also the recommended way to expose a Kotlin-backed value that should behave like a true
|
||||
Lyng global variable/property. If you need `x` to read/write through Kotlin on every access, use
|
||||
`extern var` / `extern val` plus `bindGlobalVar(...)`.
|
||||
|
||||
Do not use `addConst(...)` for this case: `addConst(...)` installs a value, not a Kotlin-backed
|
||||
property accessor. It is appropriate for fixed values and objects, but not for a global that should
|
||||
delegate reads/writes back into Kotlin state.
|
||||
|
||||
```kotlin
|
||||
import net.sergeych.lyng.bridge.*
|
||||
import net.sergeych.lyng.obj.ObjInt
|
||||
@ -117,6 +125,12 @@ assertEquals("changed", globalProp)
|
||||
assertEquals("1.0.0", globalVersion)
|
||||
```
|
||||
|
||||
Minimal rule of thumb:
|
||||
|
||||
- use `bindGlobalFun(...)` for global functions
|
||||
- use `bindGlobalVar(...)` for Kotlin-backed global variables/properties
|
||||
- use `addConst(...)` only for fixed values/objects that do not need getter/setter behavior
|
||||
|
||||
For custom argument handling and full runtime access:
|
||||
|
||||
```kotlin
|
||||
@ -462,14 +476,21 @@ im.addPackage("my.tools") { module: ModuleScope ->
|
||||
module.eval(
|
||||
"""
|
||||
extern val version: String
|
||||
extern var status: String
|
||||
extern fun triple(x: Int): Int
|
||||
""".trimIndent()
|
||||
)
|
||||
val binder = module.globalBinder()
|
||||
var status = "ready"
|
||||
binder.bindGlobalVar(
|
||||
name = "version",
|
||||
get = { "1.0" }
|
||||
)
|
||||
binder.bindGlobalVar(
|
||||
name = "status",
|
||||
get = { status },
|
||||
set = { status = it }
|
||||
)
|
||||
binder.bindGlobalFun1<Int>("triple") { x ->
|
||||
ObjInt.of((x * 3).toLong())
|
||||
}
|
||||
@ -479,6 +500,7 @@ im.addPackage("my.tools") { module: ModuleScope ->
|
||||
scope.eval("""
|
||||
import my.tools.*
|
||||
val v = triple(14)
|
||||
status = "busy"
|
||||
""")
|
||||
val v = scope.eval("v").toKotlin(scope) // -> 42
|
||||
```
|
||||
|
||||
@ -46,6 +46,28 @@ class Script(
|
||||
// private val catchReturn: Boolean = false,
|
||||
) : Statement() {
|
||||
fun statements(): List<Statement> = statements
|
||||
|
||||
/**
|
||||
* Explicitly apply this script's import/module bindings to [scope] without executing the script.
|
||||
* This is intended for embedding scenarios where the host owns scope lifecycle and wants
|
||||
* script-specific imports to be a separate, opt-in preparation step.
|
||||
*/
|
||||
suspend fun importInto(scope: Scope, seedScope: Scope = scope): Scope {
|
||||
prepareScriptScope(scope, seedScope)
|
||||
return scope
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fresh raw module scope and apply this script's import/module bindings to it.
|
||||
* If [seedScope] is provided, its import provider is reused and seed-bound imports resolve from it.
|
||||
*/
|
||||
suspend fun instantiateModule(seedScope: Scope? = null, pos: Pos = this.pos): ModuleScope {
|
||||
val provider = seedScope?.currentImportProvider ?: defaultImportManager
|
||||
val module = provider.newModuleAt(pos)
|
||||
prepareScriptScope(module, seedScope ?: module)
|
||||
return module
|
||||
}
|
||||
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
scope.pos = pos
|
||||
val execScope = resolveModuleScope(scope) ?: scope
|
||||
@ -75,6 +97,19 @@ class Script(
|
||||
return ObjVoid
|
||||
}
|
||||
|
||||
private suspend fun prepareScriptScope(scope: Scope, seedScope: Scope) {
|
||||
if (importBindings.isNotEmpty() || importedModules.isNotEmpty()) {
|
||||
seedImportBindings(scope, seedScope)
|
||||
}
|
||||
if (moduleSlotPlan.isNotEmpty()) {
|
||||
scope.applySlotPlan(moduleSlotPlan)
|
||||
for (name in moduleSlotPlan.keys) {
|
||||
val record = scope.objects[name] ?: scope.localBindings[name] ?: continue
|
||||
scope.updateSlotFor(name, record)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun seedModuleSlots(scope: Scope, seedScope: Scope) {
|
||||
if (importBindings.isEmpty() && importedModules.isEmpty()) return
|
||||
seedImportBindings(scope, seedScope)
|
||||
|
||||
@ -36,7 +36,7 @@ internal suspend fun seedFrameLocalsFromScope(frame: CmdFrame, scope: Scope) {
|
||||
} else {
|
||||
record.value
|
||||
}
|
||||
if (value is net.sergeych.lyng.FrameSlotRef && value.refersTo(frame.frame, base + i)) {
|
||||
if (value is net.sergeych.lyng.FrameSlotRef && value.refersTo(frame.frame, i)) {
|
||||
continue
|
||||
}
|
||||
frame.setObjUnchecked(base + i, value)
|
||||
|
||||
87
lynglib/src/commonTest/kotlin/ScriptImportPreparationTest.kt
Normal file
87
lynglib/src/commonTest/kotlin/ScriptImportPreparationTest.kt
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import net.sergeych.lyng.Compiler
|
||||
import net.sergeych.lyng.Script
|
||||
import net.sergeych.lyng.Source
|
||||
import net.sergeych.lyng.obj.toInt
|
||||
import net.sergeych.lyng.pacman.ImportManager
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class ScriptImportPreparationTest {
|
||||
|
||||
@Test
|
||||
fun scriptImportIntoExplicitlyPreparesExistingScope() = runTest {
|
||||
val manager = ImportManager()
|
||||
manager.addTextPackages(
|
||||
"""
|
||||
package foo
|
||||
|
||||
val answer = 42
|
||||
""".trimIndent()
|
||||
)
|
||||
val script = Compiler.compile(
|
||||
Source(
|
||||
"<prepare-scope>",
|
||||
"""
|
||||
import foo
|
||||
answer
|
||||
""".trimIndent()
|
||||
),
|
||||
manager
|
||||
)
|
||||
val scope = manager.newModule()
|
||||
|
||||
assertNull(scope["answer"])
|
||||
|
||||
script.importInto(scope)
|
||||
|
||||
val record = assertNotNull(scope["answer"])
|
||||
assertEquals(42, scope.resolve(record, "answer").toInt())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scriptInstantiateModuleUsesSeedScopeImportProvider() = runTest {
|
||||
val manager = ImportManager()
|
||||
manager.addTextPackages(
|
||||
"""
|
||||
package foo
|
||||
|
||||
val answer = 42
|
||||
""".trimIndent()
|
||||
)
|
||||
val script = Compiler.compile(
|
||||
Source(
|
||||
"<instantiate-module>",
|
||||
"""
|
||||
import foo
|
||||
answer
|
||||
""".trimIndent()
|
||||
),
|
||||
manager
|
||||
)
|
||||
val seedScope = manager.newModule()
|
||||
|
||||
val module = script.instantiateModule(seedScope)
|
||||
|
||||
val record = assertNotNull(module["answer"])
|
||||
assertEquals(42, module.resolve(record, "answer").toInt())
|
||||
}
|
||||
}
|
||||
60
lynglib/src/commonTest/kotlin/SeedLocalsRegressionTest.kt
Normal file
60
lynglib/src/commonTest/kotlin/SeedLocalsRegressionTest.kt
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2026 Sergey S. Chernov
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import net.sergeych.lyng.FrameSlotRef
|
||||
import net.sergeych.lyng.Pos
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.bytecode.BytecodeConst
|
||||
import net.sergeych.lyng.bytecode.CmdFrame
|
||||
import net.sergeych.lyng.bytecode.CmdFunction
|
||||
import net.sergeych.lyng.bytecode.CmdVm
|
||||
import net.sergeych.lyng.bytecode.seedFrameLocalsFromScope
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class SeedLocalsRegressionTest {
|
||||
|
||||
@Test
|
||||
fun seedFrameLocalsSkipsSelfReferentialFrameSlotRef() = runTest {
|
||||
val fn = CmdFunction(
|
||||
name = "seed-self-ref",
|
||||
localCount = 1,
|
||||
addrCount = 0,
|
||||
returnLabels = emptySet(),
|
||||
scopeSlotCount = 1,
|
||||
scopeSlotIndices = intArrayOf(0),
|
||||
scopeSlotNames = arrayOf(null),
|
||||
scopeSlotIsModule = booleanArrayOf(false),
|
||||
localSlotNames = arrayOf("x"),
|
||||
localSlotMutables = booleanArrayOf(true),
|
||||
localSlotDelegated = booleanArrayOf(false),
|
||||
localSlotCaptures = booleanArrayOf(false),
|
||||
constants = listOf(BytecodeConst.IntVal(0)),
|
||||
cmds = emptyArray(),
|
||||
posByIp = emptyArray()
|
||||
)
|
||||
val scope = Scope(null, pos = Pos.builtIn)
|
||||
val record = scope.addConst("x", net.sergeych.lyng.obj.ObjInt.of(1))
|
||||
val frame = CmdFrame(CmdVm(), fn, scope, emptyList())
|
||||
record.value = FrameSlotRef(frame.frame, 0)
|
||||
scope.updateSlotFor("x", record)
|
||||
|
||||
seedFrameLocalsFromScope(frame, scope)
|
||||
|
||||
assertNull(frame.frame.getRawObj(0))
|
||||
}
|
||||
}
|
||||
66
notes/lynglib_compiler_vm_review_2026-03-26.md
Normal file
66
notes/lynglib_compiler_vm_review_2026-03-26.md
Normal file
@ -0,0 +1,66 @@
|
||||
# :lynglib Compiler and VM Review (2026-03-26)
|
||||
|
||||
## Scope
|
||||
- Reviewed `:lynglib` compiler and bytecode VM paths, focusing on compile/execution correctness.
|
||||
- Read core files around `Compiler`, `Script`, `BytecodeCompiler`, `CmdRuntime`, `Scope`, `ModuleScope`, and capture/slot plumbing.
|
||||
- Ran `./gradlew :lynglib:jvmTest` on 2026-03-26: PASS.
|
||||
|
||||
## Findings
|
||||
|
||||
### 1. High: local seeding uses the wrong slot index when checking for self-referential `FrameSlotRef`
|
||||
- Status: fixed in worktree on 2026-03-26; covered by `SeedLocalsRegressionTest`.
|
||||
- File: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/SeedLocals.kt:39`
|
||||
- The self-reference guard compares `FrameSlotRef` against `base + i`, but `FrameSlotRef` stores the underlying `BytecodeFrame` local slot index, not the VM absolute slot id.
|
||||
- The same slot is then written using `setObjUnchecked(base + i, value)` at `SeedLocals.kt:42`, which means a self-reference is not filtered out before being reinserted.
|
||||
- Impact: this can seed a local with a reference to itself, creating recursive `FrameSlotRef` chains. Reads then recurse through `BytecodeFrame.getObj()` / `FrameSlotRef.read()` instead of resolving to a value, which is a realistic path to stack overflow or non-terminating execution in bytecode-only helper execution (`executeBytecodeWithSeed`).
|
||||
- Why this looks real: the equivalent check in `CmdFrame.applyCaptureRecords()` uses the local index directly (`CmdRuntime.kt:3867`), and `Script.seedModuleLocals()` also compares with the local index, not `scopeSlotCount + i` (`Script.kt:109`).
|
||||
- Suggested fix: compare with `i`, not `base + i`, and add a regression test around `executeBytecodeWithSeed` seeding a scope that already contains a `FrameSlotRef` to the same local.
|
||||
|
||||
### 2. Design note: script-specific import/module preparation should stay explicit, not hidden in `execute(scope)`
|
||||
- Status: resolved by API addition, without changing `Script.execute(scope)` semantics.
|
||||
- File: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt:51-57`
|
||||
- `shouldSeedModule` is only true when `execScope` is a `ModuleScope` or `thisObj === ObjVoid`.
|
||||
- If a compiled script is executed against a non-module scope with a real receiver object, `seedModuleSlots()` is skipped entirely, so script imports and explicit module bindings are never installed into `moduleTarget`.
|
||||
- Original concern: top-level bytecode that expects imported names or module globals can see different behavior depending on whether the host prepared the target scope explicitly.
|
||||
- Resolution taken: do **not** change `Script.execute(scope)`, because in this codebase it is expected to run on exactly the provided scope and many embedding flows already rely on explicit scope setup via `Script.newScope()`, `Scope.eval(...)`, `addFn(...)`, `addConst(...)`, and direct import-manager configuration.
|
||||
- New explicit APIs added instead:
|
||||
- `Script.importInto(scope, seedScope = scope)`
|
||||
- `Script.instantiateModule(seedScope = null, pos = script.pos)`
|
||||
- This keeps existing execution behavior stable while giving hosts an opt-in way to apply a script's import/module bindings when they actually want that preparation step.
|
||||
- Coverage added: `ScriptImportPreparationTest`.
|
||||
|
||||
### 3. Medium: missing module captures are silently converted into fresh `Unset` slots
|
||||
- File: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt:3950-3970`
|
||||
- In `CmdFrame.buildCaptureRecords()`, the module-capture path first tries several lookups. If the requested `slotId` is missing but the capture has a `name`, it calls `target.applySlotPlan(mapOf(name to slotId))` and immediately returns `target.getSlotRecord(slotId)`.
|
||||
- That record is a newly created placeholder from `Scope.applySlotPlan()` (`Scope.kt:460-471`) and defaults to `ObjUnset`.
|
||||
- Impact: a compiler/runtime disagreement in capture resolution is masked as a normal capture of `Unset`, so the failure moves far away from closure creation and becomes data corruption or an unrelated later exception. This will be difficult to debug when it happens.
|
||||
- Suggested fix: if the named module capture cannot be resolved to an existing record, fail immediately with a Lyng error instead of manufacturing a placeholder slot. Add a regression test around missing imported/module captures.
|
||||
|
||||
### 4. Medium: subject-less `when { ... }` still crashes through a raw Kotlin `TODO`
|
||||
- File: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt:6447-6449`
|
||||
- The unsupported branch uses `TODO("when without object is not yet implemented")`.
|
||||
- Current docs explicitly say subject-less `when` is not implemented, so the language limitation itself is documented. The problem is the failure mode: the compiler throws a raw Kotlin `NotImplementedError` instead of a normal `ScriptError` or a feature diagnostic.
|
||||
- Impact: IDE/embedding callers get an implementation exception rather than a source-positioned language error, which is especially bad across non-JVM targets and for editor tooling.
|
||||
- Suggested fix: replace the `TODO(...)` with a `ScriptError` at the current source position, or gate it earlier in parsing with a normal diagnostic.
|
||||
|
||||
## Risks Worth Checking Next
|
||||
|
||||
### 5. Medium risk: module frame growth only retargets records stored in `objects`/`localBindings`
|
||||
- File: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/ModuleScope.kt:39-67`
|
||||
- When `ensureModuleFrame()` replaces the old `BytecodeFrame` with a larger one, it retargets `FrameSlotRef`s only in `objects` and `localBindings`.
|
||||
- Existing closures/capture records that already hold `FrameSlotRef(oldFrame, slot)` are not retargeted here.
|
||||
- Impact: if a module scope survives across recompilation/re-execution with a larger local frame, previously exported closures can keep reading stale values from the old frame instance.
|
||||
- Confidence is lower because this depends on module reuse across differing compiled shapes, but the reference-retargeting logic is clearly incomplete for that scenario.
|
||||
- Suggested check: add a regression covering module re-execution with increased local count and an exported closure captured before the resize.
|
||||
|
||||
## Test Status
|
||||
- `./gradlew :lynglib:jvmTest` passed during this review.
|
||||
- `./gradlew :lynglib:jvmTest --tests ScriptImportPreparationTest --tests SeedLocalsRegressionTest` passed after the fixes/API additions.
|
||||
- Finding 1 is covered directly; finding 2 is covered by explicit preparation API tests.
|
||||
|
||||
## Suggested Fix Order
|
||||
1. Fix finding 1 first: it is a concrete slot-index bug with likely recursive failure modes. Done.
|
||||
2. Keep `Script.execute(scope)` semantics stable and use explicit preparation APIs where script-owned import/module setup is needed. Done.
|
||||
3. Tighten finding 3 next: fail fast on capture mismatches.
|
||||
4. Replace the raw `TODO` in finding 4 so unsupported syntax produces normal diagnostics.
|
||||
5. Decide whether finding 5 matters for current module-reload workflows; add a regression before changing behavior.
|
||||
46
proposals/wasm_vs_lyng_memory.md
Normal file
46
proposals/wasm_vs_lyng_memory.md
Normal file
@ -0,0 +1,46 @@
|
||||
WASM использует стековую модель памяти. Lyng стеки не использует. В чем разница?
|
||||
|
||||
Стек это очень древняя структура, была придумана в 1955 (магазинная память Бауэра и Самельсона), и приняла современную форму в 1960, благодаря Барбаре Лисокв, вполседствии лауреата премии Тьюринга, которого, в свою очередь, за выдающийся вклад в развитие IT кастрировали, формально, за гомосексуализм, но в британии, где на трех джентльменов приходилось тогда, и приходится и теперь, четыре гомосексуала, все понимают, что это был просто предлог.
|
||||
|
||||
Стек был прекрасен в 60е годы, когда IBM планировала продавать по компьютеру в год в лучшем случае, а за идеи о микропроцессорах или ядрах можно было устроиться только пациентом психушки. Стек это заранее жестко выделенная область памяти, которая обычно вообще не используется, и по ней в одну сторону растет используемая область, которая хранит локальные переменные, адреса возврата и иногда состояние процессора.
|
||||
|
||||
Она крайне плохо подходит для многозадачности. Представь себе что у тебя есть wasm процесс, со своим стеком, и ему надо переключиться, остановив текущий поток исполнения (например, он ждет ответа от сети). Тогда тебе придется создать _новый стек_ — пока еще пустой, переключиться на него и исполнять там другую задачу. Сколько потоков - столько и стеков. И все они по большей части не используются — там просто запас памяти "на вырост", если исполнение потребует. А оценить заранее адекватно почти невозможно, дают с запасом.
|
||||
|
||||
Далее, васм использует фиксированную память выделенную на процесс. Ее тоже приходится брать "с запасом", так как если по ходу не хватит, процесс вылетит. И она тоже в изрядной части пустая. Ну и назасладочку, васм был задуман однозадачным, и все попытки туда хотя бы треды засунуть, получаются очень кривыми.
|
||||
|
||||
Линг использует фреймы и сопрограммы. Линг вообще треды не использует. Основная команда исполнения программы на линге - сопрограмма (coroutine), ей не нужен стейк, она им не пользуется. Вместо этого каждый вызов в линге, грубо говоря, это вызов сопрограммы. Для него формируется фрейм (не на стеке, а в динамической памяти!), которого гарантировано достаточно для исполнения собственно кода. Он заменяет собой "стековый фрейм", но его размер известен заранее, и его можно распределять и освобождать проще, как обычную динамическую память.
|
||||
|
||||
Далее, линг использует ту же динамическую память, что и его родительская платформа. Если он работает на Java, то использует память со сборкой мусора JVM, на Javascript - память машины JS, в нативных машинах - специальнй менеджер памяти со сборкой мусора, от Kotlin Native (довольно хороший и очень быстро развивающийся). В результате, запустить одновременно десять, или сто тысяч программ на лигне, которые будут исполняться конкурентно, вполне реально. Просто попытка запустить 10к васм-машин на v8 или другом движке скорее всего убьет систему, да и 10 тысяч тредов редко какой сервер приложению даст. Огромный перерасход ресурсов и тормоза.
|
||||
|
||||
Далее. Васм СТЕКОВАЯ машина со стековыми командами, он без стека вообще не может. Посмотрим как он считает примитивное выражение:
|
||||
|
||||
```wasm
|
||||
(module
|
||||
;; Экспортируем функцию, чтобы её можно было вызвать из JS
|
||||
(func (export "calc") (param $a i32) (param $b i32) (param $c i32) (param $d i32) (result i32)
|
||||
;; --- вычисление (a + b) / c * d ---
|
||||
local.get $a ;; помещаем a на стек
|
||||
local.get $b ;; помещаем b на стек
|
||||
i32.add ;; складываем: стек содержит (a+b)
|
||||
|
||||
local.get $c ;; помещаем c
|
||||
i32.div_s ;; знаковое деление: стек содержит ((a+b) / c)
|
||||
|
||||
local.get $d ;; помещаем d
|
||||
i32.mul ;; умножение: стек содержит (((a+b)/c) * d)
|
||||
|
||||
;; результат остаётся на стеке — это возвращаемое значение функции
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
На ВМ Линга нет стека, он трехадресный универсальный ассемблер, если можно так сказать:
|
||||
|
||||
```
|
||||
add_int s1, s2 -> s5 // a + b -> s5
|
||||
div_int s5, s3 -> s5 // s5 -> s5 / c
|
||||
mul_int s5, s4 -> s5 // s5 -> s5 * d
|
||||
return s5
|
||||
```
|
||||
|
||||
Этот код крайне эффективно реализуется на любом реальном процессоре, и хорошо оптимизируется. Исполняется он без всякого стека.
|
||||
21
proposals/why_not_wasm.md
Normal file
21
proposals/why_not_wasm.md
Normal file
@ -0,0 +1,21 @@
|
||||
Почему не WASM?
|
||||
|
||||
- wasm очень громоздкий и неудобный для разработки, он проектировался очень давно и с другими целями, так что сейчас его безуспешно пытаются "перезаточить" в современные области, но он поддается с трудом. Компилировать в wasm была бы задача по сложности превосходящая весь проект, а результат был бы уныл: мы и так имеем все достоинсва wasm, полезные для Lyng, но не имеем многих его недостатков.
|
||||
|
||||
Виртуалка lyng легкая, и доступна на всех платформах уже сейчас, а вот прикрутить скажем wasm машину к котлину на сервере, чтобы она работала как сопрограммы первого класса вместе с котлинскими, задача сейчас неразрешимая.
|
||||
|
||||
То есть, преимущества wasm что он доступен на многих платформах, для нас неважно - мы и так на тех же платформах имеем полную поддержку.
|
||||
|
||||
- Lyng VM изначально заточена на исполнение именно сопрограмм со сборкой мусора. Эти два ключевых механизма линга практически отсутствуют в wasm, их мучительно прикручивают как расширения, и они довольно слабо поддерживаются.
|
||||
|
||||
- LyngVM заточен на исполнение программ с гибридной ООП-ФП моделью, она хорошо поддерживает множественное наследование, делегацию, работу со сложными списками аргументов (ФП фичи, подстановки, деструктурирование), на васме это очень непросто написать, будет медленно и громоздко.
|
||||
|
||||
- одна из целей Lyng была получить быстрое безопасное и очень мощное скриптовое решение для вклучения в проекты на Java/Kotlin multiplatform. На wasm это в принципе невозможно - его включение это кошмар, он громоздкий. К тому же требует компиляции, а Lyng работает с JIT (исполняет прямо исходник, компилируя его на лету, как JS), и может использоваться как скрипт (wasm не может)
|
||||
|
||||
- типы данных которые мы используем в Lyng в WASM отсутстсвтуют, их бы пришлось добавлять
|
||||
|
||||
В результате если бы мы компилировали Lyng->WASM мы бы получили огромный и медленный код. Для которого потребовалась бы огромная wasm VM. Практического смысла в этом нет.
|
||||
|
||||
Если же ты рассматриваешь идею в единой платформе использовать разные языки, а wasm как промежуточный язых совместимости, то это собственно противоречит идее использовать общую кодовую базу и интерфейсы. Разные языки имеют несовпадающие, несовместимые интерфейсы вызовов, так что приходится писать руками биндинги, которые не добавляют скорости и добавляют ошибки, к тому же, разные языки предоставляют часто вообще несовместимые модели программирования (например прототипы в JS, множественное наследование С++ или Линга и кошмар на улице вязов от руста, в владением ссылками).
|
||||
|
||||
Платформ на васме полно, и там и ловить нечего, и неинтересно. Я же предлагаю вылезти в другой класс. Навеяно реальным опоытом переносов контрактов из старого золота в мольтпей, которое получается на удивление хорошо
|
||||
Loading…
x
Reference in New Issue
Block a user