Documentation
¶
Index ¶
- Constants
- func Init() error
- func JsAgentInterrupt(h uint64, agentId uint64) (uint32, error)
- func JsAgentSpawn(h uint64, glue string, glueLen uint32, src string, srcLen uint32) (uint64, error)
- func JsAgentWake(h uint64) error
- func JsBytesNew(h uint64, data string, dataLen uint32) (uint64, error)
- func JsBytesRead(h uint64, objHandle uint64) (string, error)
- func JsCall(h uint64, fnHandle uint64, thisHandle uint64, args string, argsLen uint32) (string, error)
- func JsCloneFree(cloneHandle uint64) error
- func JsCloneRead(h uint64, cloneHandle uint64) (string, error)
- func JsCloneWrite(h uint64, val string, valLen uint32) (uint64, error)
- func JsClose(h uint64) error
- func JsConstruct(h uint64, fnHandle uint64, args string, argsLen uint32) (string, error)
- func JsDefineConstructor(h uint64, objHandle uint64, name string, nameLen uint32, key string, ...) (string, error)
- func JsDefineFunction(h uint64, objHandle uint64, name string, nameLen uint32, key string, ...) (string, error)
- func JsDetachArrayBuffer(h uint64, objHandle uint64) (string, error)
- func JsEval(h uint64, src string, srcLen uint32) (string, error)
- func JsEvalIn(h uint64, globalHandle uint64, src string, srcLen uint32) (string, error)
- func JsEvalModule(h uint64, specifier string, specifierLen uint32, src string, srcLen uint32) (string, error)
- func JsFreeObject(objHandle uint64) error
- func JsGc(h uint64) error
- func JsGet(h uint64, objHandle uint64, name string, nameLen uint32) (string, error)
- func JsGlobal(h uint64) (uint64, error)
- func JsInterruptAddr(h uint64) (uint32, error)
- func JsInterruptBitsAddr(h uint64) (uint32, error)
- func JsInterruptBitsValue(h uint64) (uint32, error)
- func JsNew(maxHeapBytes uint32, nativeStackQuotaBytes uint32) (uint64, error)
- func JsNewFunction(h uint64, name string, nameLen uint32, key string, keyLen uint32, nargs uint32) (uint64, error)
- func JsNewHtmldda(h uint64) (uint64, error)
- func JsNewPlainObject(h uint64) (uint64, error)
- func JsNewRealm(h uint64) (uint64, error)
- func JsRunJobs(h uint64) (string, error)
- func JsSet(h uint64, objHandle uint64, name string, nameLen uint32, val string, ...) (string, error)
- func JsSourceIsModule(h uint64, src string, srcLen uint32) (string, error)
- func JsTakeUnhandledRejections(h uint64) (string, error)
- type CallbackHandler
- type JS
- func (js *JS) AgentInterrupt(id uint64) (bool, error)
- func (js *JS) AgentSpawn(glue, src string) (uint64, error)
- func (js *JS) AgentWake() error
- func (js *JS) BytesNew(data []byte) (uint64, error)
- func (js *JS) BytesRead(obj uint64) ([]byte, error)
- func (js *JS) Call(fn, this uint64, args string) (string, error)
- func (js *JS) CallContext(ctx context.Context, fn, this uint64, args string) (string, error)
- func (js *JS) CloneFree(clone uint64) error
- func (js *JS) CloneRead(clone uint64) (string, error)
- func (js *JS) CloneWrite(val string) (uint64, error)
- func (js *JS) Close() error
- func (js *JS) Construct(fn uint64, args string) (string, error)
- func (js *JS) DefineConstructor(obj uint64, name, key string, nargs uint32) error
- func (js *JS) DefineFunction(obj uint64, name, key string, nargs uint32) error
- func (js *JS) DetachArrayBuffer(obj uint64) (string, error)
- func (js *JS) Eval(src string) (string, error)
- func (js *JS) EvalContext(ctx context.Context, src string) (string, error)
- func (js *JS) EvalIn(global uint64, src string) (string, error)
- func (js *JS) EvalInContext(ctx context.Context, global uint64, src string) (string, error)
- func (js *JS) EvalModule(specifier, src string) (string, error)
- func (js *JS) EvalModuleContext(ctx context.Context, specifier, src string) (string, error)
- func (js *JS) FreeObject(obj uint64) error
- func (js *JS) Gc() error
- func (js *JS) Get(obj uint64, name string) (string, error)
- func (js *JS) Global() (uint64, error)
- func (js *JS) NewFunction(name, key string, nargs uint32) (uint64, error)
- func (js *JS) NewHTMLDDA() (uint64, error)
- func (js *JS) NewPlainObject() (uint64, error)
- func (js *JS) NewRealm() (uint64, error)
- func (js *JS) RunJobs() (string, error)
- func (js *JS) RunJobsContext(ctx context.Context) (string, error)
- func (js *JS) Set(obj uint64, name, val string) (string, error)
- func (js *JS) SourceIsModule(src string) (bool, error)
- func (js *JS) TakeUnhandledRejections() (string, error)
- func (js *JS) UnlockForHostCallback() (relock func())
- type Module
- type Options
Constants ¶
const ( // AgentReceiveKey: args [id]. The host may block this agent's goroutine // until it has a value; the reply is 'R' + a clone-handle decimal, or // 'E' + message (shutdown / refusal), which unwinds the agent script. // This is the BROADCAST channel (one latched value to every agent). AgentReceiveKey = "\x00agent-receive" // AgentInboxKey: args [id]. Like AgentReceiveKey but the POINT-TO-POINT // channel — blocks until this specific agent's inbox has a message // (Send delivers per-agent, FIFO). Reply is 'R' + a clone-handle decimal // or 'E' + message. This is what a Worker's onmessage is built on. AgentInboxKey = "\x00agent-inbox" // AgentTryInboxKey: args [id]. NON-blocking inbox poll — reply is 'R' + a // clone-handle decimal when a message is waiting, or 'R' with an empty // payload when the inbox is empty. An async Worker event loop polls this // (yielding to the job queue via a timed Atomics.waitAsync between polls), // so an async onmessage's promises still drain. AgentTryInboxKey = "\x00agent-try-inbox" // AgentPostKey: args [id, cloneHandle]. The host owns the handle from // here (JsCloneRead + JsCloneFree on its own thread). Reply 'R'. AgentPostKey = "\x00agent-post" // AgentSleepKey: args [id, ms]. The host blocks this agent's goroutine // for ms milliseconds. Reply 'R'. AgentSleepKey = "\x00agent-sleep" // AgentNowKey: args [id]. Reply 'R' + monotonic milliseconds (decimal). AgentNowKey = "\x00agent-now" // AgentExitKey: args [id]; the agent's thread is ending. Reply ignored. AgentExitKey = "\x00agent-exit" )
Reserved go_host_call keys for the agent-side primitives (__agent__ in a spawned agent's global). They arrive on the AGENT's goroutine — concurrently with main-thread work — and the host owns all communication policy behind them. NUL-prefixed like the module loader's key.
const ModuleLoaderKey = "\x00module-load"
ModuleLoaderKey is the reserved go_host_call key the C++ module-resolve hook uses to fetch a module's source on a registry miss. It is NUL-prefixed so it can never collide with a host function name. An env that recognises it is the ES module loader: args are the JSON array [specifier, referrer] and the reply is 'R' + the raw module source (or 'E' + an error message) — raw, not JSON, because module source is bytes, not a value.
Variables ¶
This section is empty.
Functions ¶
func Init ¶
func Init() error
Init initializes the global module. Must be called before any API use. Safe to call multiple times (uses sync.Once).
func JsAgentInterrupt ¶ added in v0.5.0
Stop one agent, whatever it is doing — the FORCEFUL counterpart to the cooperative shutdown the host composes on top of the channels above.
A cooperative stop (a sentinel the agent acts on between job-queue drains) cannot reach a guest that never drains: `new Worker('while(true){}')` never returns to its job queue, so it never reads the sentinel. This trips the agent's OWN context interrupt — the per-agent equivalent of the host interrupt described below — and its script ends with the same uncatchable exception, so guest JS cannot swallow it. An agent parked in Atomics.wait is woken; an idle agent parked on the event futex is released. After the script unwinds the agent leaves for good rather than resuming its pump.
Asynchronous: it returns once the agent has been SIGNALLED, not once it is gone (the agent may be mid-bytecode on another thread). The exit arrives host-side as the usual "\0agent-exit". Returns 1 when an agent with this id was running and was signalled, 0 when the id is unknown or the agent has already exited — in which case there is nothing left to stop.
func JsAgentSpawn ¶
Spawn a new agent evaluating src on its own thread/context/global. The agent's global sees the standard classes, print/console, and the RAW host channels — no policy:
__agent_call__(op, extra?) one reserved-key round trip ("\0" + op, op
must start with "agent-"); the agent id is
injected as the first argument; the host may
block this goroutine (that is how a receive
waits). Returns tag+payload as a string.
__clone_read__(handle) deserialize a host-owned clone here
__clone_write__(v) clone v here; returns the handle
__agent_leaving__() mark done; the agent exits once idle
`glue` (trusted adapter setup composing its agent API — $262.agent, a Worker scope, ... — from those natives) and `src` (the user source) are evaluated as SEPARATE scripts, glue first: NEVER concatenated, so the user source keeps its own "use strict", line numbers and directive prologue, and can even be a module. After both evaluate, the agent keeps draining its job queue until leaving or runtime close; on exit the skeleton sends "\0agent-exit". Returns an opaque non-zero agent id, or 0 on failure.
func JsAgentWake ¶
Wake every agent pump parked on the event futex, so an agent whose inbox the host just filled (Send) delivers promptly. Safe to call at any time.
func JsBytesNew ¶ added in v0.3.0
Create a fresh Uint8Array of data_len bytes initialized with a copy of `data`, and return it as an object handle (0 on failure). The copy happens inside this call, so no engine data pointer ever crosses the bridge.
func JsBytesRead ¶ added in v0.3.0
Copy the binary contents of obj_handle out of the engine: a Uint8Array or any other ArrayBuffer view (read as its raw bytes, honoring offset/length), an ArrayBuffer, or a SharedArrayBuffer. Returns 'B' + the bytes on success, or 'E' + message when the object is not binary (the one-byte tag disambiguates an empty buffer from an error).
func JsCall ¶
func JsCall(h uint64, fnHandle uint64, thisHandle uint64, args string, argsLen uint32) (string, error)
Call callable fn_handle with this_handle (0 = undefined) and args (a JSON array of value encodings); return the result as a value encoding.
func JsCloneRead ¶
Deserialize a clone handle into the MAIN runtime; returns the value encoding. The handle stays valid — one broadcast clone can be read by many receivers — until js_clone_free.
func JsCloneWrite ¶
Clone the decoded value encoding on the MAIN thread into a clone handle (0 = not clonable). The handle is owned by the caller: hand it to an agent (reply to "\0agent-receive") or free it with js_clone_free.
func JsClose ¶
Destroy the runtime (JS_DestroyContext). JS_ShutDown runs at process teardown, not here, so the handle is fully torn down but the process stays usable.
func JsConstruct ¶ added in v0.3.0
Construct `new fn(...args)` — the [[Construct]] counterpart of js_call (fn_handle must be a constructor: a class or function). args is the same JSON array of value encodings; the return is the new instance's value encoding ({"k":"error",...} when construction threw).
func JsDefineConstructor ¶
func JsDefineConstructor(h uint64, objHandle uint64, name string, nameLen uint32, key string, keyLen uint32, nargs uint32) (string, error)
Define a CONSTRUCTABLE host function on obj_handle — like js_define_function but `new name(...)` is allowed, so a real host class (e.g. `new Worker(...)`) can be defined from the host. The Go function's returned object becomes the instance.
func JsDefineFunction ¶
func JsDefineFunction(h uint64, objHandle uint64, name string, nameLen uint32, key string, keyLen uint32, nargs uint32) (string, error)
Define a host function on obj_handle — the deliberate host-surface opt-in: the sandbox exposes nothing until the embedder defines a function. Calls dispatch to the Go function the embedder registered under `key` (arguments as a JSON array of value encodings; reply 'R' + one encoding or 'E' + message); `name` is the property name on the object.
func JsDetachArrayBuffer ¶
Detach an ArrayBuffer object. Returns {"k":"undefined"} or an error encoding.
func JsEval ¶
Evaluate `src` as a classic script in the runtime's persistent global and return the result as a JSON string:
{"ok":
<bool >,"result": <string >,"stdout": <string >,"stderr": <string >,
"error":
<string >}
"result" holds the script's completion value as a VALUE ENCODING (see the object-handle bindings below): a primitive carries its data and type, an object or function carries a persistent handle, so identity survives the bridge. Valid only when "ok" is true. "stdout"/"stderr" hold anything the script wrote through the `print()` / `console.log()` / `console.error()` functions this bridge installs — SpiderMonkey itself has no I/O, and this bridge deliberately exposes no file, network, or timer builtins. Global state persists across calls on the same handle (REPL-like).
On an uncaught JS exception, "ok" is false and "error" holds the exception's stringification followed by its stack. On a host interrupt (see below), "ok" is false and "error" is "JS execution interrupted".
Promise jobs queued by the script are drained before returning, so a top-level `Promise.resolve().then(...)` runs. The bridge installs no timers, so a job that never settles cannot block: there is nothing to wait on.
A single JSON string return is used because the bridge generator surfaces only one response value to Go; bundling the outputs keeps one round-trip and one atomic result. The Go wrapper unmarshals it.
func JsEvalIn ¶
Evaluate src as a classic script in the realm of global_handle and return the completion value as a value encoding ({"k":"error",...} on throw). The raw synchronous evaluation primitive: it does NOT drain the job queue.
func JsEvalModule ¶
func JsEvalModule(h uint64, specifier string, specifierLen uint32, src string, srcLen uint32) (string, error)
Compile `src` as an ES module registered under `specifier`, load its dependency graph (through the loader), link, evaluate, and drain the job queue. Same JSON shape as js_eval: "ok" true when the module (including top-level await) evaluated to completion; "error" carries compile/link/import/runtime failures.
func JsFreeObject ¶
Release an object handle (delete its persistent root).
func JsGet ¶
Get obj_handle[name] as a value encoding (primitive data, or an object/ function handle) preserving identity — not a stringification.
func JsInterruptAddr ¶
---- Interruption support ------------------------------------------------
Lets a host watchdog goroutine abort a runaway script (e.g. `while(1){}`) WITHOUT executing any wasm/C code on that instance — which would corrupt the shared linear-memory C stack. The host performs plain 32-bit stores into linear memory; the guest notices at the next bytecode loop head.
SpiderMonkey's own mechanism already has exactly this shape. JSContext holds an `interruptBits_` word; `JS_RequestInterruptCallback` does nothing but a relaxed atomic store into it, and the interpreter polls it at every loop head (`JSOp::LoopHead` in PortableBaselineInterpret.cpp, `CHECK_BRANCH()` in Interpreter.cpp). When set, the engine calls the registered interrupt callback; a callback returning false terminates the script with an UNCATCHABLE exception — guest JS cannot swallow it with `try { while(1){} } catch {}`. That is a stronger guarantee than Perl's croak, which a `eval {}` can catch.
We cannot call JS_RequestInterruptCallback from the host: it is guest code, and running it on another goroutine would clobber the instance's C stack pointer. So the host writes the two words directly, IN THIS ORDER:
*(uint32_t *)js_interrupt_addr(h) = 1; // "the host asked" *(uint32_t *)js_interrupt_bits_addr(h) |= js_interrupt_bits_value(h);
Both stores are needed and they mean different things. The second trips SpiderMonkey's poll, so the engine calls our callback at the next loop head. The first tells that callback the interrupt is ours rather than one the engine raised for its own reasons; terminating a script on one of those would be a spurious abort. The callback clears the first flag when it fires.
The order matters because the guest reads the two words with independent, unordered loads on another thread. Storing the flag first means a guest that sees the bit will, at worst, see the flag one loop head later — the callback re-arms and resumes when the flag is not yet visible, so a late flag costs a few bytecodes rather than losing the interrupt. Storing the bit first would make the reverse window lose it outright: the callback would resume, the engine would have cleared the bit, and Eval would run forever while the host believed it had cancelled the script.
js_interrupt_bits_addr returns 0 when the address of `interruptBits_` could not be established (it lives in SpiderMonkey's internal JSContext, which the public headers do not describe; js.cc locates it at startup by probing, see discover_interrupt_bits). The runtime then falls back to keeping the interrupt permanently armed, which costs a trip through handleInterrupt at every loop head but needs only the first store. Callers must therefore treat a 0 from js_interrupt_bits_addr as "skip the second store", not as an error.
Like Perl's opcode loop and CPython's eval-breaker, this only fires at bytecode loop heads: a single long-running operation (a pathological regex, a huge sort) is not preempted until it returns to the interpreter loop.
Addresses are 32-bit linear-memory offsets (wasm32).
func JsInterruptBitsAddr ¶
func JsInterruptBitsValue ¶
func JsNew ¶
Initialize a JS runtime and return an opaque handle (0 on failure). Call once per wasm instance.
Internally: JS_Init (once per process), JS_NewContext, InitSelfHostedCode, a fresh global with the standard classes, and the interrupt plumbing described below.
`max_heap_bytes`, when non-zero, caps the GC heap (JSGC_MAX_BYTES): an allocation past it fails with an out-of-memory error inside the guest rather than growing wasm linear memory. Zero means uncapped on the JS side — the host-side wasm memory cap (Config.MaxMemoryBytes) is then the single effective limit, which is the supported configuration.
`native_stack_quota_bytes`, when non-zero, caps native recursion depth (JS_SetNativeStackQuota) so runaway recursion raises a catchable "too much recursion" error instead of overflowing the wasm C stack, which would trap the whole instance. Keep it comfortably below the linker's -Wl,-z,stack-size.
func JsNewFunction ¶ added in v0.3.0
func JsNewFunction(h uint64, name string, nameLen uint32, key string, keyLen uint32, nargs uint32) (uint64, error)
A fresh host-backed FUNCTION object, attached to nothing: calling it from JS dispatches to the Go function the embedder registered under `key`, exactly like a js_define_function stub, but the function is returned as a handle instead of being defined as a property. This is the Go-side FuncOf: the embedder composes it into any structure (a callback argument, an underlyingSource.pull, an object method) via js_set / js_call / js_construct. Returns 0 on failure.
func JsNewHtmldda ¶
A fresh [[IsHTMLDDA]] object (emulates undefined, yields null when called — document.all semantics; the class flag is engine-level), as a handle.
func JsNewPlainObject ¶
A fresh plain object (JS_NewPlainObject) as a handle.
func JsNewRealm ¶
A fresh SAME-COMPARTMENT realm (objects flow between realms directly) with the standard classes and an EMPTY host surface; returns its global object as a handle. js_define_function / js_set / js_get / js_call enter the target object's realm, so composing the new realm works like composing the main one.
func JsRunJobs ¶
One step of the host event loop: run due host timers, then js::RunJobs — the engine's own drain of the job queue (ECMA-262 Jobs, §9.5: microtasks plus cross-thread Dispatchables another agent queued, e.g. Atomics.waitAsync resolutions). This is deliberately the ONLY pre/post-processing bundled in: the timer store and the pending-work probe live in C++ (the timers are this bridge's own state; the probe needs engine internals), so every conceivable host loop would have to do exactly these steps around js::RunJobs. Loop POLICY — when to stop, how long to wait — stays host-side.
Returns the same {ok, result, error} envelope as js_eval; result is "1" if the step made progress (output was produced or a job ran), "2" if nothing ran but work is still pending (a timer not yet due, or an engine-delayed Atomics.waitAsync timeout) — wait briefly and call again — and "0" if nothing ran and nothing is pending, so the loop can stop. stdout/stderr produced by the drained jobs is captured exactly like js_eval's.
func JsSet ¶
func JsSet(h uint64, objHandle uint64, name string, nameLen uint32, val string, valLen uint32) (string, error)
Set obj_handle[name] to the decoded value encoding `val` (a primitive, an object/function by handle, or {"k":"json","v": <data >} — host composite data that materializes as a fresh guest Array/Object). Returns {"k":"undefined"} on success, or an error encoding.
func JsSourceIsModule ¶ added in v0.5.0
Does `src` need ES-module semantics? Answered by the PARSER rather than by matching import/export against the source text — text matching misses a minified one-line bundle and fires on the word `export` inside a comment or a string literal, and the difference decides whether a file is loaded as a module or as CommonJS.
The rule is Node's: a source is a module when it compiles as one but NOT as CommonJS. Both compiles are needed — nearly every plain script is also a valid module, so it is the CommonJS compile failing that isolates the constructs which can appear nowhere else (a top-level `import`/`export` declaration, `import.meta`, top-level `await`). The CommonJS side is compiled inside the module wrapper function, as CommonJS is really evaluated, so a top-level `return` stays legal there.
Same JSON envelope as js_eval; "result" is "1" for a module and "0" otherwise. A source that compiles as NEITHER reports "0": it is broken, and the caller's real load then surfaces the syntax error against the file's own name and line numbers. Nothing is registered, evaluated or cached — this only parses.
func JsTakeUnhandledRejections ¶ added in v0.5.0
Hand back every promise rejection that is still unhandled, and forget them.
A rejection with no handler is observable ONLY here: the engine reports it to the embedder, and guest JS cannot see it (an async function's promise is created by the engine, so wrapping the Promise constructor host-side misses exactly those). Anything shaped like `unhandledRejection` / `unhandledrejection` is composed host-side from this.
Same JSON envelope as js_eval; "result" holds a JSON array of {"reason": <encoding >,"promise": <encoding >} in rejection order — the pair such an event is defined in terms of. Call it after a job drain (js_eval / js_run_jobs): by then a rejection the guest handled in the same tick has already retracted itself and is not reported.
Draining is DESTRUCTIVE: each rejection is reported exactly once, however often this is called.
Types ¶
type CallbackHandler ¶
CallbackHandler is implemented by Go types that need to be called from C++. The type is always defined (the Module struct references it); the registration/dispatch machinery is only emitted when the wasm imports wasmify.callback_invoke.
type JS ¶
type JS struct {
// contains filtered or unexported fields
}
JS is one interpreter: its own wasm module and one SpiderMonkey runtime.
func New ¶
New brings up an interpreter: its own wasm module (its own linear memory) and one SpiderMonkey runtime, with the interrupt addresses resolved and cached.
func (*JS) AgentInterrupt ¶ added in v0.5.0
AgentInterrupt stops ONE agent whatever it is doing, by tripping that agent's own JSContext interrupt. Its script ends with an uncatchable exception (guest JS cannot swallow it) and the agent leaves instead of resuming its pump.
This is the forceful counterpart to a cooperative stop: a sentinel an agent reads between job-queue drains cannot reach a guest that never drains, which is exactly the runaway `while(true){}` worker a terminate has to be able to stop. It also wakes an agent parked in Atomics.wait or idling on the event futex.
Asynchronous: it returns once the agent is signalled, not once it is gone — the agent may be mid-bytecode on its own thread, and its exit arrives through the usual AgentExitKey. Reports false when no agent with this id is running, in which case there is nothing left to stop.
func (*JS) AgentSpawn ¶
AgentSpawn runs src on a NEW agent — its own thread (a goroutine under wasm2go), its own JSContext and global, sharing nothing with the main runtime but SharedArrayBuffer memory. The agent's __agent__ primitives call back through the reserved Agent*Key host-call keys, so the env owns all communication policy. Returns the agent's opaque non-zero id.
func (*JS) AgentWake ¶
AgentWake wakes every agent pump parked on the event futex, so an agent whose inbox was just filled (Send) delivers promptly.
func (*JS) BytesNew ¶ added in v0.3.0
BytesNew creates a fresh guest Uint8Array holding a copy of data and returns its object handle. The bytes cross the bridge RAW — the protobuf channel is length-delimited and 8-bit clean — with no base64/JSON encoding.
func (*JS) BytesRead ¶ added in v0.3.0
BytesRead copies the binary contents of the object behind the handle (Uint8Array, any other ArrayBuffer view, ArrayBuffer, SharedArrayBuffer) out of the guest, raw. The reply is 'B' + bytes or 'E' + message.
func (*JS) Call ¶
Call invokes the callable fn (an object handle) with `this` (an object handle, 0 = undefined) and args — a JSON array of value encodings. The return is one value encoding (possibly {"k":"error",...} when the call threw).
func (*JS) CallContext ¶
CallContext runs Call under ctx, so a callee that spins is interrupted on cancellation just like a runaway Eval.
func (*JS) CloneRead ¶
CloneRead deserializes a clone handle into the main runtime and returns the value encoding. The handle stays valid until CloneFree.
func (*JS) CloneWrite ¶
CloneWrite structured-clones the value the encoding describes (shared-memory objects allowed: a SAB is shared, everything else copied) into a clone handle the caller owns. Runs on the main runtime.
func (*JS) Close ¶
Close destroys the runtime (JS_DestroyContext) and unmaps the linear memory if it came from a shared copy-on-write image.
func (*JS) Construct ¶ added in v0.3.0
Construct runs `new fn(...args)` — Call's [[Construct]] counterpart. fn must be a constructor (a class or function) handle; args is a JSON array of value encodings. The return is the new instance's value encoding.
func (*JS) DefineConstructor ¶
DefineConstructor defines a CONSTRUCTABLE host-backed function `name` on obj (new name(...) is allowed). Guest constructs dispatch to env under `key`.
func (*JS) DefineFunction ¶
DefineFunction defines a host-backed function `name` on obj. Guest calls dispatch to env under `key`; nargs sets the function's declared arity.
func (*JS) DetachArrayBuffer ¶
DetachArrayBuffer detaches the ArrayBuffer behind obj. The reply is a value encoding: {"k":"undefined"} on success or an error encoding.
func (*JS) Eval ¶
Eval runs src as a classic script and returns the {ok,result,error} JSON envelope (see JsEval).
func (*JS) EvalContext ¶
EvalContext runs src under ctx (see withContext).
func (*JS) EvalIn ¶
EvalIn evaluates src as a classic script in the realm of the given global handle; the reply is the completion value's encoding ({"k":"error",...} on throw). It does NOT drain the job queue.
func (*JS) EvalInContext ¶
EvalInContext runs EvalIn under ctx (see withContext).
func (*JS) EvalModule ¶
EvalModule compiles src as an ES module registered under specifier, loads its dependency graph (each miss asks the env's module loader via ModuleLoaderKey), links, evaluates, and drains the job queue. Same {ok,result,error} envelope as Eval.
func (*JS) EvalModuleContext ¶
EvalModuleContext runs EvalModule under ctx (see withContext).
func (*JS) FreeObject ¶
FreeObject releases an object handle (deletes its persistent root).
func (*JS) Get ¶
Get returns obj[name] as a value ENCODING — the identity-preserving JSON tag the C++ codec emits: {"k":"bool|number|string|null|undefined","v":...} for a primitive, {"k":"object|function","h":<handle>} for an object (the handle is a fresh persistent root the caller must eventually FreeObject), and {"k":"error","v":<message>} when the property access threw.
func (*JS) NewFunction ¶ added in v0.3.0
NewFunction creates a fresh host-backed guest FUNCTION object (attached to nothing) and returns its handle. Guest calls dispatch to env under `key`; name is the function's name property, nargs its declared arity. The Go-side FuncOf primitive.
func (*JS) NewHTMLDDA ¶
NewHTMLDDA returns a fresh [[IsHTMLDDA]] object handle (emulates undefined; yields null when called — document.all semantics).
func (*JS) NewPlainObject ¶
NewPlainObject returns a handle to a fresh plain object (JS_NewPlainObject).
func (*JS) NewRealm ¶
NewRealm creates a fresh same-compartment realm (standard classes, empty host surface) and returns its global object's handle.
func (*JS) RunJobs ¶
RunJobs performs one step of the host event loop: due host timers, then the engine's own job-queue drain (js::RunJobs — microtasks plus cross-thread dispatchables). Returns the {ok,result,error} envelope; result is "1" if the step made progress, "2" if work is still pending (wait and call again), "0" if the queue is idle. The host loops on this to run an event loop.
func (*JS) RunJobsContext ¶
RunJobsContext runs RunJobs under ctx, so a runaway job (a job that spins) is interrupted on cancellation just like a runaway Eval.
func (*JS) Set ¶
Set assigns obj[name] = the value the encoding `val` describes (same encoding language as Get). The reply is {"k":"undefined"} on success or an error encoding.
func (*JS) SourceIsModule ¶ added in v0.5.0
SourceIsModule reports whether src needs ES-module semantics, decided by the engine's own parser rather than by matching import/export against the source text. A source is a module when it compiles as one but NOT as CommonJS — nearly every script is also a valid module, so it is the CommonJS compile failing that isolates the constructs which can appear nowhere else (a top-level import/export declaration, import.meta, top-level await).
A source that compiles as neither is broken and reports false; the real load then surfaces the syntax error against the file's own name and line numbers. Nothing is registered, evaluated or cached — this only parses.
func (*JS) TakeUnhandledRejections ¶ added in v0.5.0
TakeUnhandledRejections hands back every promise rejection still unhandled and forgets them, as a JSON array of {"reason":<encoding>,"promise":<encoding>} in rejection order.
A rejection nobody handled is observable ONLY through the engine: an async function's promise is created by the engine, so wrapping Promise host-side misses exactly the cases that matter. Call this after a job drain (Eval, RunJobs) — by then a rejection the guest handled in the same tick has already retracted itself and is not reported. Draining is destructive: each rejection is reported exactly once however often this is called.
func (*JS) UnlockForHostCallback ¶
func (js *JS) UnlockForHostCallback() (relock func())
UnlockForHostCallback releases this instance's invoke lock for the duration of a guest→host callback, so the callback can re-enter the interpreter (Eval, Get, Set, Call, ...) without self-deadlocking on the non-reentrant invoke mutex; it returns the function that reacquires the lock. Call it ONLY from inside a host import (go_host_call), where the guest is paused waiting for the reply: re-entry then continues from the current wasm stack pointer, exactly like a native function calling back into the engine.
relock := js.UnlockForHostCallback() ret, err := userCallback(args) relock()
type Module ¶
type Module struct {
// contains filtered or unexported fields
}
Module is the bridge handle. It wraps the wasm2go-transpiled module (*base.Module) and serialises every entry on m.mu.
Reentrant callbacks — a host-import callback whose handler needs to make further calls back into the transpiled module — are supported by releasing m.mu around the user handler in handleCallback.
Safety rests on a single structural property: every path that touches transpiled module state goes through m.invoke, and m.invoke holds m.mu for the entire duration of its call. The release window in handleCallback simply lets another m.invoke grab the mutex and run as a fully nested top-level call — same goroutine via the user handler, different goroutine via an unrelated caller, it doesn't matter. Either way the nested call enters, balances its own state changes, and exits before the outer call resumes, so the outer call never observes a mid-flight inner call.
type Options ¶
type Options struct {
// Env receives guest->host calls (the callbacks DefineFunction wires up).
// nil means the guest can register nothing.
Env base.EnvImports
// Environ is the environment the guest sees. It is baked into the shared
// snapshot (the guest's libc caches it during startup), so it keys the
// snapshot cache — instances with different Environ get different snapshots.
Environ []string
// Stdin/Stdout/Stderr back the guest's raw fds 0/1/2. Unset streams are
// sandboxed (empty stdin, discarded stdout/stderr) — never the host's.
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
// MaxMemory is the linear-memory ceiling in bytes; 0 means default.
MaxMemory int
}
Options configures a new interpreter's wasm instance. All fields are the sandbox's business; the public API maps its Config onto them.