neon

Module context

Source
Expand description

Provides runtime access to the JavaScript engine.

An execution context represents the current state of a thread of execution in the JavaScript engine. Internally, it tracks things like the set of pending function calls, whether the engine is currently throwing an exception or not, and whether the engine is in the process of shutting down. The context uses this internal state to manage what operations are safely available and when.

The Context trait provides an abstract interface to the JavaScript execution context. All interaction with the JavaScript engine in Neon code is mediated through instances of this trait.

One particularly useful context type is FunctionContext, which is passed to all Neon functions as their initial execution context.

fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
    Ok(cx.string("hello Neon"))
}

Another important context type is ModuleContext, which is provided to a Neon module’s main function to enable sharing Neon functions back with JavaScript:

#[neon::main]
fn lib(mut cx: ModuleContext) -> NeonResult<()> {
    cx.export_function("hello", hello)?;
    Ok(())
}

§Writing Generic Helpers

Depending on the entrypoint, a user may have a FunctionContext, ModuleContext, or generic Cx. While it is possible to write a helper that is generic over the Context trait, it is often simpler to accept a Cx argument. Due to deref coercion, other contexts may be passed into a function that accepts a reference to Cx.

fn log(cx: &mut Cx, msg: &str) -> NeonResult<()> {
    cx.global::<JsObject>("console")?
        .method(cx, "log")?
        .arg(msg)?
        .exec()?;
    Ok(())
}

fn print(mut cx: FunctionContext) -> JsResult<JsUndefined> {
    let msg = cx.argument::<JsString>(0)?.value(&mut cx);
    log(&mut cx, &msg)?;
    Ok(cx.undefined())
}

§Memory Management

Because contexts represent the engine at a point in time, they are associated with a lifetime, which limits how long Rust code is allowed to access them. This is also used to determine the lifetime of Handles, which provide safe references to JavaScript memory managed by the engine’s garbage collector.

For example, we can write a simple string scanner that counts whitespace in a JavaScript string and returns a JsNumber:

fn count_whitespace(mut cx: FunctionContext) -> JsResult<JsNumber> {
    let s: Handle<JsString> = cx.argument(0)?;
    let contents = s.value(&mut cx);
    let count = contents
        .chars()                       // iterate over the characters
        .filter(|c| c.is_whitespace()) // select the whitespace chars
        .count();                      // count the resulting chars
    Ok(cx.number(count as f64))
}

In this example, s is assigned a handle to a string, which ensures that the string is kept alive (i.e., prevented from having its storage reclaimed by the JavaScript engine’s garbage collector) for the duration of the count_whitespace function. This is how Neon takes advantage of Rust’s type system to allow your Rust code to safely interact with JavaScript values.

§Temporary Scopes

Sometimes it can be useful to limit the scope of a handle’s lifetime, to allow the engine to reclaim memory sooner. This can be important when, for example, an expensive inner loop generates temporary JavaScript values that are only needed inside the loop. In these cases, the execute_scoped and compute_scoped methods allow you to create temporary contexts in order to allocate temporary handles.

For example, to extract the elements of a JavaScript iterator from Rust, a Neon function has to work with several temporary handles on each pass through the loop:

    let iterator = cx.argument::<JsObject>(0)?;         // iterator object
    let next: Handle<JsFunction> =                      // iterator's `next` method
        iterator.prop(&mut cx, "next").get()?;
    let mut numbers = vec![];                           // results vector
    let mut done = false;                               // loop controller

    while !done {
        done = cx.execute_scoped(|mut cx| {                   // temporary scope
            let obj: Handle<JsObject> = next                  // temporary object
                .bind(&mut cx)
                .this(iterator)?
                .call()?;
            numbers.push(obj.prop(&mut cx, "value").get()?);  // temporary number
            obj.prop(&mut cx, "done").get()                   // temporary boolean
        })?;
    }

The temporary scope ensures that the temporary values are only kept alive during a single pass through the loop, since the temporary context is discarded (and all of its handles released) on the inside of the loop.

§Throwing Exceptions

When a Neon API causes a JavaScript exception to be thrown, it returns an Err result, indicating that the thread associated with the context is now throwing. This allows Rust code to perform any cleanup before returning, but with an important restriction:

While a JavaScript thread is throwing, its context cannot be used.

Unless otherwise documented, any Neon API that uses a context (as self or as a parameter) immediately panics if called while the context’s thread is throwing.

Typically, Neon code can manage JavaScript exceptions correctly and conveniently by using Rust’s question mark (?) operator. This ensures that Rust code “short-circuits” when an exception is thrown and returns back to JavaScript without calling any throwing APIs.

Alternatively, to invoke a Neon API and catch any JavaScript exceptions, use the Context::try_catch method, which catches any thrown exception and restores the context to non-throwing state.

§See also

  1. Ecma International. Execution contexts, ECMAScript Language Specification.
  2. Madhavan Nagarajan. What is the Execution Context and Stack in JavaScript?
  3. Rupesh Mishra. Execution context, Scope chain and JavaScript internals.

Structs§

  • Context representing access to the JavaScript runtime
  • An execution context of a function call.
  • A temporary lock of an execution context.
  • An execution context of module initialization.

Enums§

  • Indicates whether a function was called with new.

Traits§

  • An execution context, which represents the current state of a thread of execution in the JavaScript engine.