js_engine

    Pure MoonBit cross-target embedded JavaScript engine

    javascript
    interpreter
    embedded
    scripting
    Download zip
    Author
    Version
    0.9.0
    License
    Apache-2.0
    Last updated
    4 hours ago
    Downloads
    3K

    #js_engine

    A pure MoonBit, cross-target embedded JavaScript engine. The stateful engine and CLI use the verified bytecode candidate by default and fall back to the tree-walking executor for unsupported source. It runs on MoonBit's native, JavaScript, Wasm, and Wasm-GC targets.

    • Conformance on test262: each file is run in strict and non-strict modes and reported per mode. Do not sum the modes. Generate current numbers from CI artifacts with make test262-report; see docs/TEST262.md.
    • Cross-target embedding: the same stateful Engine API is tested on native, JavaScript, Wasm, and Wasm-GC.
    • Benchmark dashboard: https://dowdiness.github.io/js_engine/benchmarks/
    • Interactive JavaScript Playground.

    #Quick Start

    #CLI

    moon run cmd/main -- -e 'console.log(1 + 2)' # 3

    moon run cmd/main -- -e ' function fib(n) { if (n <= 1) { return n; } return fib(n - 1) + fib(n - 2); } console.log(fib(10)); ' # 55

    Pass a script filename to run a file, and put script arguments after --:

    moon run cmd/main -- path/to/script.js -- first --second

    The shell provides load(), read() / readFile(), print(), console, arguments, scriptArgs, and monotonic performance.now(). load() evaluates in the current realm and resolves nested relative paths from the loading file. read(path, "binary") returns an ArrayBuffer. File execution installs the argument globals even when no arguments are passed. Eval mode installs them only when arguments follow --.

    More sample programs live in example/.

    #As a Library

    ///|
    test "README stateful rule engine" {
    let engine = @js_engine.Engine()
    let source =
    #|let evaluations = 0;
    #|function allow(request) {
    #| evaluations += 1;
    #| return { allowed: request.role === "admin", evaluations };
    #|}
    engine.eval(source)
    let admin = Json::object({ "role": Json::string("admin") })
    let member_request = Json::object({ "role": Json::string("member") })
    json_inspect(engine.call_json("allow", [admin]), content={
    "allowed": true,
    "evaluations": 1,
    })
    json_inspect(engine.call_json("allow", [member_request]), content={
    "allowed": false,
    "evaluations": 2,
    })
    }

    Engine keeps one global realm alive across calls. Its strict JSON boundary copies plain data directly: it does not consult a mutable global JSON, call getters or toJSON, or execute Proxy traps. Promise results and non-JSON values are rejected. This API is intended for trusted application scripts, not as a security sandbox. See example/rule_engine/ for the runnable example.

    For application-owned Host Capabilities and lifecycle checks, use HostEnvironment to select the immutable capability set, bind concrete services with SessionBindings, and execute through an ExecutionSession. This higher-level path automatically completes the Promise-job checkpoint for each Hosted Turn. Console output, Script Resources, and application-scheduled one-shot timers cross purpose-specific typed boundaries without exposing Runtime Values. See the stable embedding guide.

    The stable embedding guide defines the JSON boundary, lookup rules, queue checkpoints, retained-state behavior, error reuse limits, and four-target contract.

    For one-shot evaluation, the existing facade remains available:

    ///|
    test "README one-shot facade" {
    let (output, _) = @js_engine.run("console.log(1 + 2)")
    json_inspect(output, content=["3"])
    }

    The public entry points are defined in js_engine.mbt and classified in the stable guide:

    • Stable embedding: run; Engine, EngineError, and its explicit-queue methods; plus HostEnvironment, SessionBindings, ExecutionSession, and their typed Host Capability contracts listed in the guide.
    • Staged Stage 4 availability: Engine::eval_bounded, Engine::call_json_bounded, Engine::run_microtask_checkpoint_bounded, Engine::run_timer_checkpoint_bounded, ExecutionPolicy, ExecutionPolicyError, and InterruptionHandle.
    • Compatibility: run_module / run_modules; their export maps expose raw runtime values.
    • Advanced/internal: run_compiled and the module-level event-loop APIs that expose or accept a raw interpreter.

    #Embedding (custom host objects)

    For DOM-style globals and native methods, create a wired interpreter and inject bindings — do not reverse-engineer Interpreter::new / setup_builtins unless you need to replace builtin installation itself:

    let interp = @interpreter.new_interpreter()
    // Build query_selector with realm_state=Some(interp.realm_state) — see guide.
    let document = @runtime.make_host_object(
    name="Document",
    proto=@runtime.get_obj_proto(realm_state=Some(interp.realm_state)),
    methods={ "querySelector": query_selector },
    )
    interp.global.def_builtin("document", document)
    // Then parse, interp.run, interp.run_microtasks(), interp.run_timers().

    Full advanced cookbook (make_*_func + realm_state, errors, host slots, globalThis, custom setup_builtins): docs/advanced-embedding.md.

    #Supported Language

    Core ES5 plus selected ES6+ features: let / const / var, arrow functions, closures, classes, for / while / for-in / for-of, try / catch / finally, template literals, destructuring, spread / rest, ES Modules, Promises + microtasks, setTimeout / setInterval, ES6 Proxy (13 traps) + Reflect API (13 methods), TypedArrays (9 types), ArrayBuffer, DataView, RegExp, JSON, Map / Set / WeakMap / WeakSet, generators, Symbols.

    For the full per-category breakdown, see docs/supported-features.md.

    #Conformance

    Test262 conformance by edition — CI run 30346236658, tip 265bbfd, 2026-07-28. P/E = passed ÷ executed (excludes skipped tests). Refresh: make test262-report ARGS="--format=readme".

    #strict

    EditionDiscoveredSkippedExecutedPassedFailedTimeout/ErrPassed / ExecutedPassed / Discovered
    Pre-ES2015 (baseline)13,281013,27713,057220498.3%98.3%
    ES201510,30016110,13110,03398899.0%97.4%
    ES20161000999901100.0%99.0%
    ES201773634439239200100.0%53.3%
    ES20184,7257273,9983,822176095.6%80.9%
    ES2019128012810622082.8%82.8%
    ES20201,7841,5372472443098.8%13.7%
    ES202146812834032614095.9%69.7%
    ES20225,065345,0312,7652,266055.0%54.6%
    ES2023254332212183098.6%85.8%
    ES20241,07286620610898052.4%10.1%
    ES20251,14877936929673080.2%25.8%
    Annex B3654431926554283.1%72.6%
    Stage 35,5315,5191266050.0%0.1%
    Total44,98610,20134,77031,7373,0331591.3%70.5%

    Fully-skipped buckets (no tests executed) folded into Total: Unmapped (29).

    #non-strict

    EditionDiscoveredSkippedExecutedPassedFailedTimeout/ErrPassed / ExecutedPassed / Discovered
    Pre-ES2015 (baseline)13,917013,90913,544365897.4%97.3%
    ES201510,78816010,62010,498122898.9%97.3%
    ES20161000999901100.0%99.0%
    ES201777534443143100100.0%55.6%
    ES20184,7817354,0463,870176095.7%80.9%
    ES2019127012710522082.7%82.7%
    ES20201,9841,6043803773099.2%19.0%
    ES202144412831630214095.6%68.0%
    ES20225,3612965,0652,7882,277055.0%52.0%
    ES2023277562212183098.6%78.7%
    ES20241,07787020710998052.7%10.1%
    ES20251,18081336729473080.1%24.9%
    Annex B1,156441,110909201281.9%78.6%
    Stage 35,6965,593103109309.7%0.2%
    Total47,69210,67237,00133,5543,4471990.7%70.4%

    Fully-skipped buckets (no tests executed) folded into Total: Unmapped (29).

    #Package Structure

    token/ Token types and source locations errors/ JavaScript error variants and formatting helpers lexer/ Tokenizer ast/ AST node definitions parser/ Recursive descent parser with Pratt precedence static_semantics/ Early-error and declaration-fact analysis compiler/ Bytecode compiler plus legacy closure-conversion experiments interpreter/ Wiring layer for runtime + standard library interpreter/runtime/ Tree-walking evaluator, value model, host state interpreter/stdlib/ JavaScript built-ins cmd/main/ CLI entry point cmd/test262_runner/ Native test262 runner cmd/report_test262/ CI artifact report generator benchmarks/ Benchmark workloads and runner example/rule_engine/ Canonical stateful JSON rule-engine embedding

    #Development

    moon check # Type check moon test # Run unit tests moon fmt # Format code moon info # Update .mbti interface files moon build # Build

    Run the test262 conformance suite with make test262. See docs/TEST262.md for prerequisites, filtering, and options.

    #Documentation

    #License

    Apache-2.0

    ConsoleOutputKind

    Stable category of Realm-independent console output.

    ExecutionPolicy

    Opaque policy and interruption controls for the staged bounded-evaluation facade. Runtime owns their invariants; the root package is the consumer API.

    ExecutionPolicyError

    Opaque policy and interruption controls for the staged bounded-evaluation facade. Runtime owns their invariants; the root package is the consumer API.

    InterruptionHandle

    Opaque policy and interruption controls for the staged bounded-evaluation facade. Runtime owns their invariants; the root package is the consumer API.

    EngineError

    pub(all) suberror EngineError {
    ParseError(String)
    JavaScriptException(String)
    MissingGlobal(String)
    NotCallable(String)
    JsonConversionError(String)
    InternalError(String)
    } derive(
    Debug
    )

    Stable errors raised by the stateful Engine facade.
    impl Show for EngineError

    EngineError::output

    fn EngineError::output(self : EngineError, logger : &Logger) -> Unit

    EngineError::to_string

    fn EngineError::to_string(self : EngineError) -> String

    ExecutionSessionError

    pub(all) suberror ExecutionSessionError {
    ExecutionFailure(EngineDiagnostic)
    HostFailure(capability~ : String, detail~ : String)
    SessionBusy
    SessionClosed
    SessionFaulted
    ScheduledTurnUnavailable
    }

    Errors reported by an Execution Session at its application boundary.

    ExecutionSessionError::output

    fn ExecutionSessionError::output(self : ExecutionSessionError, logger : &Logger) -> Unit

    ExecutionSessionError::to_string

    fn ExecutionSessionError::to_string(self : ExecutionSessionError) -> String

    SessionCreationError

    pub(all) suberror SessionCreationError {
    BindingForAbsentCapability(String)
    MissingBindingForCapability(String)
    } derive(
    Debug
    )

    Invalid combinations of a fixed Host Capability Set and Session Bindings.

    SessionCreationError::output

    fn SessionCreationError::output(self : SessionCreationError, logger : &Logger) -> Unit

    SessionCreationError::to_string

    fn SessionCreationError::to_string(self : SessionCreationError) -> String

    ConsoleOutput

    pub struct ConsoleOutput {
    // private fields
    }

    Realm-independent Console Output delivered to an application-owned Output Sink. Runtime Values never cross this boundary.

    ConsoleOutput::text

    fn ConsoleOutput::text(self : ConsoleOutput) -> String

    Engine

    A persistent JavaScript realm for repeated evaluation and JSON calls.

    Engine::Engine

    fn Engine::Engine(annex_b? : Bool) -> Engine

    Engine::call_json

    fn Engine::call_json(self : Engine, name : String, args : Array[Json]) -> Json raise EngineError

    Engine::call_json_bounded

    fn Engine::call_json_bounded(self : Engine, name : String, args : Array[Json], policy :
    ExecutionPolicy
    ) -> Result[Json, EngineDiagnostic]

    Call a JSON-boundary function under one explicitly supplied, operation-scoped execution policy. The same fresh control carrier spans global lookup, direct JSON conversion, target execution, and direct result conversion. The direct bridge itself does not execute JavaScript.

    Engine::call_json_diagnostic

    fn Engine::call_json_diagnostic(self : Engine, name : String, args : Array[Json]) -> Result[Json, EngineDiagnostic]

    Call a JSON-boundary function while returning operation-aware failure details.

    Engine::eval

    fn Engine::eval(self : Engine, source : String) -> Unit raise EngineError

    Engine::eval_bounded

    fn Engine::eval_bounded(self : Engine, source : String, policy :
    ExecutionPolicy
    , source_id? : String) -> Result[Unit, EngineDiagnostic]

    Evaluate source under one explicitly supplied, operation-scoped policy. Parsing remains outside the control carrier; all existing unbounded Engine entry points retain their current behavior and signatures.

    Engine::eval_diagnostic

    fn Engine::eval_diagnostic(self : Engine, source : String, source_id? : String) -> Result[Unit, EngineDiagnostic]

    Evaluate source while returning operation-aware failure details atomically.

    Engine::has_pending_microtasks

    fn Engine::has_pending_microtasks(self : Engine) -> Bool

    Engine::has_pending_timers

    fn Engine::has_pending_timers(self : Engine) -> Bool

    Engine::inject_json

    fn Engine::inject_json(self : Engine, name : String, value : Json) -> Result[Unit, EngineDiagnostic]

    Copy host-owned JSON into this Engine as an immutable global binding and matching immutable own property of globalThis.

    Engine::run_microtask_checkpoint

    fn Engine::run_microtask_checkpoint(self : Engine) -> Bool raise EngineError

    Engine::run_microtask_checkpoint_bounded

    fn Engine::run_microtask_checkpoint_bounded(self : Engine, policy :
    ExecutionPolicy
    ) -> Result[Bool, EngineDiagnostic]

    Run one microtask checkpoint under a fresh operation-scoped execution policy. Empty-queue detection is outside the execution-step budget.

    Engine::run_microtask_checkpoint_diagnostic

    fn Engine::run_microtask_checkpoint_diagnostic(self : Engine) -> Result[Bool, EngineDiagnostic]

    Run a microtask checkpoint while returning operation-aware failure details.

    Engine::run_timer_checkpoint

    fn Engine::run_timer_checkpoint(self : Engine) -> Unit raise EngineError

    Engine::run_timer_checkpoint_bounded

    fn Engine::run_timer_checkpoint_bounded(self : Engine, policy :
    ExecutionPolicy
    ) -> Result[Unit, EngineDiagnostic]

    Run one timer checkpoint under a fresh operation-scoped execution policy. Queue dispatch, callbacks, and timer-following microtasks share this scope.

    Engine::run_timer_checkpoint_diagnostic

    fn Engine::run_timer_checkpoint_diagnostic(self : Engine) -> Result[Unit, EngineDiagnostic]

    Run a timer checkpoint while returning operation-aware failure details.

    Engine::take_output

    fn Engine::take_output(self : Engine) -> Array[String]

    EngineDiagnostic

    pub struct EngineDiagnostic {
    failure_kind_code_ : String
    message_ : String
    operation_code_ : String
    phase_code_ : String
    source_identity_ : String?
    source_location_ : SourceLocation?
    engine_integrity_ : EngineIntegrity
    retained_effects_ : RetainedEffects
    pending_jobs_ : PendingJobs
    }

    Portable, operation-aware details for a failed stable-facade operation.

    EngineDiagnostic::engine_integrity

    fn EngineDiagnostic::engine_integrity(self : EngineDiagnostic) -> EngineIntegrity

    EngineDiagnostic::failure_kind_code

    fn EngineDiagnostic::failure_kind_code(self : EngineDiagnostic) -> String

    EngineDiagnostic::message

    fn EngineDiagnostic::message(self : EngineDiagnostic) -> String

    EngineDiagnostic::operation_code

    fn EngineDiagnostic::operation_code(self : EngineDiagnostic) -> String

    EngineDiagnostic::pending_jobs

    fn EngineDiagnostic::pending_jobs(self : EngineDiagnostic) -> PendingJobs

    EngineDiagnostic::phase_code

    fn EngineDiagnostic::phase_code(self : EngineDiagnostic) -> String

    EngineDiagnostic::retained_effects

    fn EngineDiagnostic::retained_effects(self : EngineDiagnostic) -> RetainedEffects

    EngineDiagnostic::source_identity

    fn EngineDiagnostic::source_identity(self : EngineDiagnostic) -> String?

    EngineDiagnostic::source_location

    fn EngineDiagnostic::source_location(self : EngineDiagnostic) -> SourceLocation?

    EngineIntegrity

    pub(all) enum EngineIntegrity {
    Reusable
    Discard
    Unknown
    NotApplicable
    }

    Whether a persistent Engine remains supported for later operations.

    ExecutionSession

    pub struct ExecutionSession {
    // private fields
    }

    One mutable JavaScript execution state created from a Host Environment.

    ExecutionSession::call_json

    fn ExecutionSession::call_json(self : ExecutionSession, name : String, arguments : Array[Json]) -> Json raise ExecutionSessionError

    Call a global JavaScript function through the explicit JSON Data Copy boundary.

    ExecutionSession::close

    Permanently make this Execution Session unavailable for new Hosted Turns. Closing a running Session is rejected and does not interrupt execution.

    ExecutionSession::evaluate

    fn ExecutionSession::evaluate(self : ExecutionSession, source : String) -> Unit raise ExecutionSessionError

    Evaluate source in this Execution Session's persistent Realm.

    ExecutionSession::resume_scheduled_turn

    fn ExecutionSession::resume_scheduled_turn(self : ExecutionSession, turn : ScheduledTurn) -> Unit raise ExecutionSessionError

    Resume one application-scheduled callback as a new Hosted Turn.

    HostEnvironment

    pub struct HostEnvironment {
    // private fields
    }

    Reusable application-owned configuration for creating independent Execution Sessions.

    HostEnvironment::HostEnvironment

    fn HostEnvironment::HostEnvironment(annex_b? : Bool, console? : Bool, script_resources? : Bool, timers? : Bool) -> HostEnvironment

    HostEnvironment::create_session

    Create a new Execution Session with an independent Hosted Realm.

    LoadedScript

    pub struct LoadedScript {
    identity : String
    source : String
    }

    Source returned by a shell host after resolving a requested script name. identity is used as the referrer for nested load() calls.

    LoadedScript::LoadedScript

    fn LoadedScript::LoadedScript(identity~ : String, source~ : String) -> LoadedScript

    PendingJobs

    pub(all) enum PendingJobs {
    None
    Present
    Unknown
    }

    Whether either Engine job queue contains pending work at the failure boundary.

    RetainedEffects

    pub(all) enum RetainedEffects {
    None
    MayRemain
    Unknown
    }

    Whether observable work from the failed operation may remain committed.

    ScheduledTurn

    pub struct ScheduledTurn {
    // private fields
    }

    Opaque permission to resume one Session-owned timer callback once.

    ScriptResource

    pub struct ScriptResource {
    // private fields
    }

    JavaScript source resolved by an application under an opaque identity.

    ScriptResource::ScriptResource

    fn ScriptResource::ScriptResource(identity~ : String, source~ : String) -> ScriptResource

    ScriptResource::identity

    fn ScriptResource::identity(self : ScriptResource) -> String

    ScriptResource::source

    fn ScriptResource::source(self : ScriptResource) -> String

    ScriptResourceRequest

    pub struct ScriptResourceRequest {
    // private fields
    }

    Application-defined request for a Script Resource. The request and referrer are opaque names; the engine does not apply path or URL rules.

    ScriptResourceRequest::referrer

    fn ScriptResourceRequest::referrer(self : ScriptResourceRequest) -> String?

    ScriptResourceRequest::request

    fn ScriptResourceRequest::request(self : ScriptResourceRequest) -> String

    SessionBindings

    pub struct SessionBindings {
    // private fields
    }

    Application services bound while creating one Execution Session.

    SessionBindings::SessionBindings

    fn SessionBindings::SessionBindings(console_output_sink? : (ConsoleOutput) -> Unit raise?, script_resource_resolver? : (ScriptResourceRequest) -> ScriptResource? raise?, timer_scheduler? : (TimerSchedule) -> Unit raise?) -> SessionBindings

    Shell

    pub struct Shell {
    // private fields
    }

    A persistent JavaScript shell realm. The host owns filesystem policy and supplies script loading as a capability; the shell owns evaluation order, the load stack, and JavaScript-visible host functions.

    Shell::Shell

    fn Shell::Shell(load_source~ : (String, String?) -> LoadedScript raise, read_bytes? : (String, String?) -> Bytes raise?, now_millis? : () -> Double?, annex_b? : Bool) -> Shell

    Shell::drain_jobs

    fn Shell::drain_jobs(self : Shell) -> Unit raise EngineError

    Shell::eval

    fn Shell::eval(self : Shell, source : String) -> Unit raise EngineError

    Evaluate source in the shell's persistent realm without changing the load referrer. This is the implementation of the command-line -e mode.

    Shell::run_file

    fn Shell::run_file(self : Shell, path : String) -> Unit raise EngineError

    Resolve and execute a script as the shell entry point.

    Shell::run_modules

    fn Shell::run_modules(self : Shell, modules : Array[(String, String)]) -> Unit raise EngineError

    Evaluate a pre-resolved module graph in this Shell's Realm. Host globals, output, arguments, and pending jobs are shared with script evaluation.

    Shell::set_arguments

    fn Shell::set_arguments(self : Shell, script_name : String, args : Array[String]) -> Unit raise EngineError

    Install command-line arguments without parsing an extra JavaScript setup program. arguments follows d8/JSC and excludes the script name; scriptArgs follows QuickJS and includes it as element zero.

    Shell::take_output

    fn Shell::take_output(self : Shell) -> Array[String]

    SourceLocation

    pub struct SourceLocation {
    start_ : SourcePosition
    end_ : SourcePosition?
    }

    A half-open source range. The end position is absent when unavailable.

    SourceLocation::end

    SourceLocation::start

    SourcePosition

    pub struct SourcePosition {
    line_ : Int
    column_ : Int
    offset_ : Int
    }

    A position within a source identified by an Engine diagnostic.

    SourcePosition::column

    fn SourcePosition::column(self : SourcePosition) -> Int

    SourcePosition::line

    fn SourcePosition::line(self : SourcePosition) -> Int

    SourcePosition::offset

    fn SourcePosition::offset(self : SourcePosition) -> Int

    TimerSchedule

    pub struct TimerSchedule {
    // private fields
    }

    A timer request delivered to an application-owned scheduler.

    TimerSchedule::delay_milliseconds

    fn TimerSchedule::delay_milliseconds(self : TimerSchedule) -> Int

    TimerSchedule::turn

    has_pending_microtasks

    fn has_pending_microtasks(interp :
    Interpreter
    ) -> Bool

    Check if there are pending microtasks in the queue

    has_pending_timers

    Check if there are pending timers in the queue

    run

    fn run(source : String, annex_b? : Bool) -> (Array[String], String) raise

    run_compiled

    fn run_compiled(source : String, annex_b? : Bool) -> (Array[String], String) raise

    Run JavaScript source through the opt-in closure-conversion prototype.

    The normal run facade remains the default interpreter path. This entry point parses once, compiles the supported script subset to executable closures, then runs it with the same event-loop drain behavior as run.

    run_diagnostic

    fn run_diagnostic(source : String, source_id? : String, annex_b? : Bool) -> Result[(Array[String], String), EngineDiagnostic]

    Run a one-shot script while returning operation-aware failure details.

    run_microtask_checkpoint

    fn run_microtask_checkpoint(interp :
    Interpreter
    ) -> Bool raise

    Run a single microtask checkpoint Returns true if there are more microtasks to process

    run_module

    fn run_module(source : String, annex_b? : Bool) -> (Array[String], Map[String,
    Value
    ]) raise

    Run a JavaScript module source and return its exports The module is executed in strict mode and its exports are collected

    run_modules

    fn run_modules(modules : Array[(String, String)], annex_b? : Bool) -> (Array[String], Map[String,
    Value
    ]) raise

    Run multiple modules with dependency resolution. The runtime graph runner pre-registers every module specifier before instantiation/evaluation, so callers do not need to order modules by dependency. Returns the exports of the last module.

    run_timer_checkpoint

    fn run_timer_checkpoint(interp :
    Interpreter
    ) -> Unit raise

    Run all pending timers with microtask draining between each

    run_with_event_loop

    Run JavaScript source with event loop support This version allows the host to control microtask and timer execution timing