Class: Dommy::Js::Quickjs::WasmBridge

Inherits:
Object
  • Object
show all
Defined in:
lib/dommy/js/quickjs/wasm_bridge.rb

Overview

Handle-oriented JS access for a wasm guest (e.g. mruby-in-wasm under wasmtime-rb). Distinct from the Proxy-based HostBridge: instead of exposing Ruby DOM objects to JS as proxies, this lets a guest treat any JS value as an opaque integer ref it can get/set/call/new on — the shape the guest’s ‘js_*` bridge imports need.

The JS half lives in host_runtime.js (the ‘wasm*` functions on `__rbHost`); this is the thin Ruby facade over them. Every non-primitive JS value crosses as a `JSValue` (an opaque ref into the VM); primitives cross as plain Ruby values. Callbacks the guest registers become JS functions (also refs) that route back through `__rbWasmInvoke`.

Defined Under Namespace

Classes: JSValue

Instance Method Summary collapse

Constructor Details

#initialize(backend) ⇒ WasmBridge

Returns a new instance of WasmBridge.



26
27
28
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 26

def initialize(backend)
  @backend = backend
end

Instance Method Details

#apply(fn, this_arg, args) ⇒ Object

Apply a function ref directly (optionally with an explicit ‘this`).



64
65
66
67
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 64

def apply(fn, this_arg, args)
  this_ref = this_arg.nil? ? nil : ref_of(this_arg)
  unpack(@backend.call_js("__rbHost.wasmApply", ref_of(fn), this_ref, args.map { |a| pack(a) }))
end

#call(recv, method, args) ⇒ Object



59
60
61
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 59

def call(recv, method, args)
  unpack(@backend.call_js("__rbHost.wasmCall", ref_of(recv), method.to_s, args.map { |a| pack(a) }))
end

#construct(ctor, args) ⇒ Object



69
70
71
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 69

def construct(ctor, args)
  unpack(@backend.call_js("__rbHost.wasmNew", ref_of(ctor), args.map { |a| pack(a) }))
end

#eval_js(src) ⇒ Object

Evaluate real JS source in global scope; returns the (packed) result.



46
47
48
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 46

def eval_js(src)
  unpack(@backend.call_js("__rbHost.wasmEval", src.to_s))
end

#get(recv, prop) ⇒ Object



50
51
52
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 50

def get(recv, prop)
  unpack(@backend.call_js("__rbHost.wasmGet", ref_of(recv), prop.to_s))
end

#global_refObject

A ref to the VM’s globalThis — the guest’s ‘js_global`.



41
42
43
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 41

def global_ref
  unpack(@backend.call_js("__rbHost.wasmGlobalRef"))
end

#instance_of?(value, ctor) ⇒ Boolean

Returns:

  • (Boolean)


91
92
93
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 91

def instance_of?(value, ctor)
  @backend.call_js("__rbHost.wasmInstanceof", ref_of(value), ref_of(ctor))
end

#js_null?(value) ⇒ Boolean

Returns:

  • (Boolean)


85
86
87
88
89
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 85

def js_null?(value)
  return value.nil? unless value.is_a?(JSValue)

  @backend.call_js("__rbHost.wasmIsNull", value.ref)
end

#make_callback(invoke_id) ⇒ Object

Make a JS function (returned as a ref) that calls back into the guest with the given invoke-id when invoked.



97
98
99
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 97

def make_callback(invoke_id)
  unpack(@backend.call_js("__rbHost.wasmMakeCallback", invoke_id.to_i))
end

#on_invoke(&block) ⇒ Object

Install the dispatcher JS callbacks route back through. The block receives (invoke_id, packed_args) and must return a packed result (the same tagged shape #pack produces). Called once by the embedder.



33
34
35
36
37
38
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 33

def on_invoke(&block)
  @backend.define_host_function("__rbWasmInvoke") do |invoke_id, args|
    block.call(invoke_id.to_i, args)
  end
  self
end

#pack(value) ⇒ Object

Ruby value -> wasm-tagged JS value. Public so the embedder’s #on_invoke dispatcher can pack the values it hands back into JS.



108
109
110
111
112
113
114
115
116
117
118
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 108

def pack(value)
  case value
  when JSValue then {"__rb_js_ref" => value.ref}
  when nil, true, false, Integer, Float, String then value
  when Symbol then value.to_s
  when Array then value.map { |e| pack(e) }
  when Hash then value.each_with_object({}) { |(k, v), h| h[k.to_s] = pack(v) }
  else
    raise ArgumentError, "cannot pack #{value.class} for the wasm JS bridge"
  end
end

#release(value) ⇒ Object



101
102
103
104
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 101

def release(value)
  @backend.call_js("__rbHost.wasmReleaseRef", value.ref) if value.is_a?(JSValue)
  nil
end

#set(recv, prop, value) ⇒ Object



54
55
56
57
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 54

def set(recv, prop, value)
  @backend.call_js("__rbHost.wasmSet", ref_of(recv), prop.to_s, pack(value))
  nil
end

#strict_equal(a, b) ⇒ Object



81
82
83
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 81

def strict_equal(a, b)
  @backend.call_js("__rbHost.wasmStrictEqual", ref_of(a), ref_of(b))
end

#to_string(value) ⇒ Object



77
78
79
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 77

def to_string(value)
  @backend.call_js("__rbHost.wasmToString", ref_of(value))
end

#typeof(value) ⇒ Object



73
74
75
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 73

def typeof(value)
  @backend.call_js("__rbHost.wasmTypeof", ref_of(value))
end

#unpack(value) ⇒ Object

wasm-tagged JS value -> Ruby value (JSValue for refs). Public for the same reason as #pack.



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/dommy/js/quickjs/wasm_bridge.rb', line 122

def unpack(value)
  case value
  when Hash
    if value.key?("__rb_js_ref")
      JSValue.new(value["__rb_js_ref"])
    elsif value.key?("__rb_undefined")
      nil
    elsif value.key?("__rb_bytes")
      value["__rb_bytes"]
    elsif value.key?("__rb_arraybuffer")
      value["__rb_arraybuffer"]
    else
      value.each_with_object({}) { |(k, v), h| h[k] = unpack(v) }
    end
  when Array then value.map { |e| unpack(e) }
  else value
  end
end