Class: Dommy::Js::Wasmtime::VM
- Inherits:
-
Object
- Object
- Dommy::Js::Wasmtime::VM
- Defined in:
- lib/dommy/js/wasmtime/vm.rb
Overview
A wasmtime-rb host for an mruby-wasm-js build (e.g. the Lilac runtime). It implements the two import modules the wasm needs:
- `js` the 25-function handle-table interop ABI,
routed to a pluggable JS engine (Engines::Quickjs
by default) whose values implement the bridge
ABI (__js_get__/__js_set__/__js_call__/__js_new__)
- `wasi_snapshot_preview1` wasmtime's bundled WASI preview1, with fd_write
shadowed to capture mruby stdout/stderr
Extracted from lilac’s reference host (test/ruby_spec/mruby_wasm.rb).
Instance Attribute Summary collapse
-
#engine ⇒ Object
readonly
Returns the value of attribute engine.
-
#wasm_path ⇒ Object
readonly
Returns the value of attribute wasm_path.
Instance Method Summary collapse
-
#document ⇒ Object
The Dommy document/window the engine renders into — the same DOM the wasm runtime mutates.
-
#drain_async! ⇒ Object
Drive the engine’s event loop to quiescence.
-
#eval(source) ⇒ Object
Evaluate mruby source.
-
#eval!(source) ⇒ Object
Like #eval but raises RubyError on a non-zero exit code.
-
#initialize(wasm:, engine: nil) ⇒ VM
constructor
A new instance of VM.
-
#invoke_callback(callback_id, args) ⇒ Object
Fire an mruby block registered via ‘JS.callback`.
-
#load_bytecode(bytes) ⇒ Object
Load pre-compiled mrbc bytecode.
- #stderr ⇒ Object
-
#stdout ⇒ Object
Captured stdout/stderr since the last read (clears the buffer).
- #window ⇒ Object
Constructor Details
#initialize(wasm:, engine: nil) ⇒ VM
Returns a new instance of VM.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/dommy/js/wasmtime/vm.rb', line 31 def initialize(wasm:, engine: nil) @wasm_path = wasm.to_s @engine = engine || default_engine @stdout_buf = String.new(encoding: Encoding::BINARY) @stderr_buf = String.new(encoding: Encoding::BINARY) wire_console! # Handle table. id 0 = undefined/null sentinel; id 1 = the JS global # (engine.global). User values (primitives, JsRefs, Ruby Hash/Array) # live at ids >= 100. @handles = { 0 => nil, 1 => @engine.global } @next_handle = 100 # A JS-side exception captured during js_call (a host callback can't # raise out of a wasmtime host function — it would unwind the wasm # runtime), handed back to mruby via the js_take_error import so it # surfaces as JS::Error instead of crashing the host. @pending_error = 0 boot! end |
Instance Attribute Details
#engine ⇒ Object (readonly)
Returns the value of attribute engine.
25 26 27 |
# File 'lib/dommy/js/wasmtime/vm.rb', line 25 def engine @engine end |
#wasm_path ⇒ Object (readonly)
Returns the value of attribute wasm_path.
25 26 27 |
# File 'lib/dommy/js/wasmtime/vm.rb', line 25 def wasm_path @wasm_path end |
Instance Method Details
#document ⇒ Object
The Dommy document/window the engine renders into — the same DOM the wasm runtime mutates. Host-side tests drive and inspect it via Dommy’s Ruby API.
53 |
# File 'lib/dommy/js/wasmtime/vm.rb', line 53 def document = @engine.document |
#drain_async! ⇒ Object
Drive the engine’s event loop to quiescence.
89 |
# File 'lib/dommy/js/wasmtime/vm.rb', line 89 def drain_async! = @engine.run_until_idle |
#eval(source) ⇒ Object
Evaluate mruby source. Returns mruby’s exit code (0 on success, 1 on error, 2 if the compiler is absent). After the eval, drives the event loop so fibers suspended on ‘.await` settle (the Ruby-host equivalent of the browser/Node event loop unwinding the stack).
60 61 62 63 64 65 66 67 |
# File 'lib/dommy/js/wasmtime/vm.rb', line 60 def eval(source) handle = store_handle(source.b) rc = @js_eval_handle.call(handle, 0, 0) drain_async! rc ensure @handles.delete(handle) if handle end |
#eval!(source) ⇒ Object
Like #eval but raises RubyError on a non-zero exit code.
70 71 72 73 74 75 |
# File 'lib/dommy/js/wasmtime/vm.rb', line 70 def eval!(source) rc = eval(source) raise RubyError, "mruby eval failed (rc=#{rc})#{stderr_tail}" unless rc.zero? rc end |
#invoke_callback(callback_id, args) ⇒ Object
Fire an mruby block registered via ‘JS.callback`. `args` are Ruby values (already unwrapped by the engine); they cross as a single handle to the args array.
94 95 96 97 98 99 100 101 |
# File 'lib/dommy/js/wasmtime/vm.rb', line 94 def invoke_callback(callback_id, args) args_handle = store_handle(args) result_handle = @js_invoke_proc.call(callback_id, args_handle) @handles[result_handle] ensure @handles.delete(args_handle) if args_handle @handles.delete(result_handle) if result_handle && result_handle >= 100 end |
#load_bytecode(bytes) ⇒ Object
Load pre-compiled mrbc bytecode. Bytes flow in via the handle table as an array-like (js_load_irep_handle reads length + indexed bytes).
79 80 81 82 83 84 85 86 |
# File 'lib/dommy/js/wasmtime/vm.rb', line 79 def load_bytecode(bytes) handle = store_handle(bytes.bytes) rc = @js_load_irep_handle.call(handle) drain_async! rc ensure @handles.delete(handle) if handle end |
#stderr ⇒ Object
110 111 112 113 114 |
# File 'lib/dommy/js/wasmtime/vm.rb', line 110 def stderr out = @stderr_buf @stderr_buf = String.new(encoding: Encoding::BINARY) out end |
#stdout ⇒ Object
Captured stdout/stderr since the last read (clears the buffer).
104 105 106 107 108 |
# File 'lib/dommy/js/wasmtime/vm.rb', line 104 def stdout out = @stdout_buf @stdout_buf = String.new(encoding: Encoding::BINARY) out end |
#window ⇒ Object
54 |
# File 'lib/dommy/js/wasmtime/vm.rb', line 54 def window = @engine.window |