Skip to content

Neon 0.2 Migration Guide

Dave Herman edited this page Jul 25, 2018 · 3 revisions

Neon 0.2 makes some backwards-incompatible changes, in order to get us closer to an API that we feel comfortable stabilizing on for 1.0. We don’t intend to make many such incompatible changes, but there will probably be a few more before we reach 1.0. In the meantime, this migration guide should provide all the information you need to upgrade a project from the Neon 0.1.x series to Neon 0.2.

Major changes

Module reorganization

Following the module reorganization RFC, Neon’s APIs are organized differently from the 0.1.x series. Fortunately, for the vast majority of cases, you should now be able to radically simplify all of your Neon imports down to a single line:

use neon::prelude::*;

If you’d like more detailed information, check out the latest API documentation.

Execution contexts

The single biggest change in 0.2, both conceptually and to the API syntax, is the replacement of the 0.1.x scopes abstraction with the concept of execution contexts. The major differences are:

Neon functions take a FunctionContext

Where your Neon functions used to look like:

fn my_neon_function(call: Call) -> JsResult<JsString> {
    let scope = call.scope;
    let x = call.arguments.require(0)?.check::<JsNumber>()?;
    ... some_neon_api(scope, ...) ...
}

They now look like:

fn my_neon_function(cx: FunctionContext) -> JsResult<JsString> {
    let x = cx.argument::<JsNumber>(0)?;
    ... some_neon_api(&mut cx, ...) ...
}

Notice that instead of passing a scope to various Neon APIs, you now pass a mutable reference to the context. Also notice that argument extraction has gotten a little more expressive, which helps eliminate some common annoying boilerplate.

Locking the JS engine now uses cx.lock() and Borrow

In Neon 0.1.x, you use the Lock trait and its grab() method to get access to the internals of a JavaScript object. In Neon 0.2, you lock the JavaScript engine by calling the execution context’s cx.lock() method, and you use the Borrow (or BorrowMut) trait to get access to the internals of an object. The Borrow::borrow() and BorrowMut::borrow_mut() methods require a Lock object, which is returned from cx.lock().

As a shortcut, if you only need to borrow a single object’s internals at a time (which is very common), you can use the context’s cx.borrow() or cx.borrow_mut() methods.

For more details, check out the execution context API docs.

Create values with convenience methods of execution contexts

Allocating a JavaScript value is a little more convenient now with new convenience methods of execution contexts. For example, instead of:

JsString::new(&mut cx, "hello world")

You can call a method of the cx itself:

cx.string("hello world")

You can find all the convenience methods in the execution context API docs.

Perform type casts with x.downcast::<T>().or_throw(&mut cx)

Casting a JavaScript value to another type used to be performed with the handle.check::<T>() method, but for safety, causing a failed cast to throw a JavaScript exception has to take a reference to an execution context. The new syntax is:

x.downcast::<T>().or_throw(&mut cx)

See the handle API docs for more details.

Minor changes

Binary data

Previously, the JsBuffer and JsArrayBuffer provide access to their internals as a CMutSlice. In Neon 0.2, borrowing a binary buffer provides a reference to a BinaryData object, from which you can safely get various typed views:

    let mut b = cx.argument::<JsArrayBuffer>(0)?;
    
    let (a, b, c) = cx.borrow_mut(&mut b, |data| {
        let slice = data.as_mut_slice::<f64>();
        slice[0] = 17.3;
        (slice[1], slice[2], slice[3])
    });

See the JsArrayBuffer API docs for more details.

Error types

Creating errors is more convenient now. Instead of:

JsError::new(scope, Kind::TypeError, "undefined is not a function")

Each different error type has a construction method in JsError:

JsError::type_error(&mut cx, "undefined is not a function")

Alternatively, you can use the shorthand method of cx:

cx.type_error("undefined is not a function")

As an additional convenience, cx has a shorthand method for the common case of immediately throwing a new error object:

cx.throw_type_error("undefined is not a function")

See the JsError API docs and the execution context API docs for more details.

String creation

String creation (JsString::new() and cx.string()) no longer returns an Option by default. In the rare case that a string is too long, this will panic (which results in throwing an exception back into Node). Otherwise this method never fails.

If you are working with strings coming from an untrusted source (such as from the filesystem, the network, or user input) and you want to defensively check for string overflow in your Rust code without triggering a JavaScript exception, use the new JsString::try_new() method.

PropertyKey

The Key trait has been renamed to PropertyKey, and its methods have been renamed. You should almost never need to import this trait directly, so this is unlikely to affect any code in practice. The benefit of this change is to improve the quality of Rust type errors when users forget to import the Object trait.

Deprecations

neon::js::JsInteger

This exposed a low-level implementation detail of V8 and isn’t future-proof for other JS engines like Chakra. For most use cases, you can simply replace JsInteger with JsNumber.

neon::js::Variant

This was an experimental type to allow the use of Rust’s pattern matching syntax for doing runtime type testing of JavaScript values. But it had some significant design flaws and didn’t turn out to be a successful experiment.

If you want to do a sequence of type tests, you can use Rust’s if let form to achieve something similar. For example:

if let Ok(s) = x.downcast::<JsString>() {
    ...
} else if let Ok(a) = x.downcast::<JsArray>() {
    ...
} // etc...

neon::js::ToJsString

This trait was confusing, poorly named, and not serving much purpose. Generally if you have an API that takes a ToJsString, you should just change it to take a JsString (or a generic V: Value, which you could convert to a JsString).

ReferenceError and SyntaxError

These types are not supported by N-API, so we are removing them until and unless they become a standard part of the Node platform.

NEON_NODE_ABI environment variable

This environment variable was no longer actually doing anything, so it shouldn’t affect anyone.

Command-line changes

neon build --debug

By default, neon build will build a debug profile instead of release. This makes it match the Cargo defaults. You can perform a release build with neon build -r or neon build --release.

If you have any build/CI scripts, make sure they use the appropriate flags.

neon +nightly build

In Neon 0.1.x, you could select the Rust toolchain with the -r or --rust flag. In Neon 0.2, toolchain selection matches the Cargo syntax: you can optionally insert a first argument of the form +<toolchain> where <toolchain> is the name of the Rust toolchain to use.