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 Handle
s, 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
- Ecma International. Execution contexts, ECMAScript Language Specification.
- Madhavan Nagarajan. What is the Execution Context and Stack in JavaScript?
- 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.