From d5f085f91501594ef41b075af1fbf3bf9eb973ae Mon Sep 17 00:00:00 2001 From: David Herman Date: Fri, 25 Feb 2022 10:45:02 -0800 Subject: [PATCH 1/7] Add migration guide for 0.10. --- MIGRATION_GUIDE_0.10.md | 246 ++++++++++++++++++ .../MIGRATION_GUIDE_NAPI.md | 0 2 files changed, 246 insertions(+) create mode 100644 MIGRATION_GUIDE_0.10.md rename MIGRATION_GUIDE.md => doc/MIGRATION_GUIDE_NAPI.md (100%) diff --git a/MIGRATION_GUIDE_0.10.md b/MIGRATION_GUIDE_0.10.md new file mode 100644 index 000000000..7a53dce72 --- /dev/null +++ b/MIGRATION_GUIDE_0.10.md @@ -0,0 +1,246 @@ +# Neon 0.10 Migration Guide + +With the API improvements of Neon 0.10, a few backwards-incompatible changes have been introduced. This migration guide provides all the information you should need to make the (small!) code changes required for upgrading to Neon 0.10. + +We have made an effort to minimize these changes, and believe they should typically lead to simpler, clearer code. **If you run into any trouble migrating your code to 0.10, please file an issue or reach out for help on the [community Slack](https://rust-bindings.slack.com/)!** + +# Major changes + +## Object property access is generic + +To improve the ergonomics of the common case of downcasting the result of property access to a specific type, the signature of `Object::get()` has been changed to have a generic return type. This means that code that follows `Object::get()` with `.downcast_or_throw()` or `.downcast::().or_throw()` no longer needs to do so. + +**Before:** + +```rust +obj.get(&mut cx, "name")?.downcast::(&mut cx).or_throw(&mut cx)? +``` + +**After (option 1):** + +```rust +obj.get::(&mut cx, "name")? +``` + +**After (option 2):** + +```rust +let v: Handle = obj.get(&mut cx, "name")? +``` + +## Layered APIs for function calls + +The API for calling (or constructing, i.e. the Neon equivalent of the JavaScript new operator) a JS function has been split into two layered alternatives. The existing `.call()` and `.construct()` functions are now a lower-level primitive, which no longer offers automatic downcasting of arguments or result. But Neon 0.10 now offers a more convenient API for calling functions with an options object and method chaining, with the introduction of the `.call_with()` and `.construct_with()` methods. + +### Example: Calling a function + +**Before:** + +```rust +let s: Handle = ...; +let n: Handle = ...; +let args: Vec> = vec![s.upcast(), n.upcast()]; +let this = cx.global(); +f.call(&mut cx, this, args) +``` + +**After (low-level API):** + +```rust +let s: Handle = ...; +let n: Handle = ...; +let this = cx.global(); +f.call(&mut cx, this, [s.upcast(), n.upcast()]) +``` + +**After (high-level API):** + +```rust +let s: Handle = ...; +let n: Handle = ...; +f.call_with(&cx) + .args((s, n)) + .apply(&mut cx) +``` + +### Example: Constructing with a function + +**Before:** + +```rust +let s: Handle = ...; +let n: Handle = ...; +let args: Vec> = vec![s.upcast(), n.upcast()]; +f.construct(&mut cx, args) +``` + +**After (low-level API):** + +```rust +let s: Handle = ...; +let n: Handle = ...; +f.construct(&mut cx, [s.upcast(), n.upcast()]) +``` + +**After (high-level API):** + +```rust +let s: Handle = ...; +let n: Handle = ...; +f.construct_with(&cx) + .args((s, n)) + .apply(&mut cx) +``` + +## Idiomatic typed arrays + +Neon 0.10 replaces the previous typed array API with a more idiomatic API. JavaScript typed arrays (e.g. [`Uint32Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array)) can be represented with corresponding Rust types (e.g. [`JsTypedArray`](https://docs.rs/neon/0.10.0-alpha.3/neon/types/struct.JsTypedArray.html)), allowing easy access to their internals as Rust slices. + +### Example: Reading a buffer + +**Before:** + +```rust +let b: Handle = ...; +{ + let guard = cx.lock(); + let data = b.borrow(&guard); + let slice = data.as_slice::(); + ... +} +``` + +**After:** + +```rust +let b: Handle> = ...; +let slice = b.as_slice(&cx); +``` + +### Example: Reading and writing buffers + +**Before:** + +```rust +let b1: Handle = ...; +let b2: Handle = ...; +{ + let guard = cx.lock(); + let data1 = b1.borrow(&guard); + let data2 = b2.borrow(&guard); + let slice1 = data1.as_slice::(); + let slice2 = data2.as_slice::(); + ... +} +``` + +**After:** + +```rust +let src_buf: Handle> = ...; +let dst_buf: Handle> = ...; +{ + let lock = cx.lock(); + let src = src_buf.as_slice(&lock).unwrap(); + let dst = dst_buf.as_mut_slice(&lock).unwrap(); + ... +} +``` + +### Example: Casting buffer types + +Previous versions of Neon came with a special datatype for casting the data of an ArrayBuffer, but this had incomplete handling of unaligned data and is deprecated in Neon 0.10. Crates like [bytemuck](https://crates.io/crates/bytemuck) can be used for casting buffer slices. + +**Before:** + +```rust +let b: Handle = ...; +{ + let guard = cx.lock(); + let data = b.borrow(&guard); + let u8_slice = data.as_slice::(); + let f32_slice = data.as_slice::(); + ... +} +``` + +**After:** + +```rust +use bytemuck::cast_slice; + +let b: Handle = ...; +let u8_slice = b.as_slice(&cx); +let f32_slice = cast_slice(u8_slice); +``` + +# Minor changes + +## Uncaught errors in tasks + +Starting with 0.10, uncaught errors (whether Rust panics or JavaScript exceptions) in a task are now captured and reported to Node as an [`uncaughtException` event](https://nodejs.org/api/process.html#event-uncaughtexception). Previously, an uncaught JavaScript exception would be ignored. To handle uncaught exceptions, either wrap the body of a task with [`try_catch`](https://docs.rs/neon/0.10.0-alpha.3/neon/context/trait.Context.html#method.try_catch) or, alternatively, capture all uncaught exceptions in Node with [`process.setUncaughtExceptionCaptureCallback()`](https://nodejs.org/api/process.html#processsetuncaughtexceptioncapturecallbackfn). + +**Before:** + +```rust +cx.task(|| "hello".to_string()) + .and_then(|mut cx, _| { + cx.throw_error("ignore me") + }) +``` + +**After:** + +```rust +cx.task(|| "hello".to_string()) + .and_then(|mut cx, _| { + let _ = cx.try_catch(() { + cx.throw_error("ignore me") + }); + Ok(()) + }) +``` + +## `Channel::send` returns `JoinHandle` + +The [`Channel::send()`](https://docs.rs/neon/0.10.0-alpha.3/neon/event/struct.Channel.html#method.send) method now returns a [`JoinHandle`](https://docs.rs/neon/0.10.0-alpha.3/neon/event/struct.JoinHandle.html) type instead of `()`, allowing code to optionally and conveniently block on the result with [`JoinHandle::join()`](https://docs.rs/neon/0.10.0-alpha.3/neon/event/struct.JoinHandle.html#method.join). Non-blocking code should usually work with little to no change; sometimes a semicolon may be needed to explicitly ignore the `JoinHandle`. + +**Before:** + +```rust +fn helper<'a, C: Context<'a>>(cx: &mut C, ch: Channel) { + ch.send(...) +} +``` + +**After:** + +```rust +fn helper<'a, C: Context<'a>>(cx: &mut C, ch: Channel) { + ch.send(...); +} +``` + +## Value types aren't cloneable + +Previous versions of Neon had a safety bug in allowing types that implement the [`Value`](https://docs.rs/neon/0.10.0-alpha.3/neon/types/trait.Value.html) trait to be cloned. This has been fixed in Neon 0.10. It should be rare for code to ever need to clone a value. In most cases where this may be occurring, a [`Handle`](https://docs.rs/neon/0.10.0-alpha.3/neon/handle/struct.Handle.html) or reference (`&`) should work. For longer-lived use cases such as storing a value in a Rust static variable, use a [`Root`](https://docs.rs/neon/0.10.0-alpha.3/neon/handle/struct.Root.html). + +**Before:** + +```rust +fn helper<'a, C: Context<'a>>(cx: &mut C, s: JsString) -> ... { + ... +} +``` + +**After:** + +```rust +fn helper<'a, C: Context<'a>>(cx: &mut C, s: Handle) -> ... { + ... +} +``` + +## Context methods now all take `&mut self` + +Context methods such as [`execute_scoped`](https://docs.rs/neon/0.10.0-alpha.3/neon/context/trait.Context.html#method.execute_scoped), [`compute_scoped`](https://docs.rs/neon/0.10.0-alpha.3/neon/context/trait.Context.html#method.compute_scoped), and [`lock`](https://docs.rs/neon/0.10.0-alpha.3/neon/context/trait.Context.html#method.lock) all take `&mut self` instead of the previous `&self`. This was necessary for safety and is more consistent with other `Context` methods. In normal usage, this should not require code changes. diff --git a/MIGRATION_GUIDE.md b/doc/MIGRATION_GUIDE_NAPI.md similarity index 100% rename from MIGRATION_GUIDE.md rename to doc/MIGRATION_GUIDE_NAPI.md From efde9a7cc6ce9929ba62d499d874d4ff767df0fc Mon Sep 17 00:00:00 2001 From: David Herman Date: Fri, 25 Feb 2022 10:55:40 -0800 Subject: [PATCH 2/7] Update README to point to new migration guide. --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d7c9e938b..158e4f5b0 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,11 @@ _**Note:** This will create a new project with the `napi-backend` and some docum See our [Neon fundamentals docs](https://neon-bindings.com/docs/intro) and our [API docs](https://docs.rs/neon/latest/neon). -## Node-API (formerly N-API) Migration Guide +## Neon 0.10 Migration Guide -We've ported Neon to a new backend based on [Node-API (formerly N-API)](https://nodejs.org/api/n-api.html), which will -be the basis for Neon 1.0. +The latest version of Neon, 0.10, includes a few small breaking changes in order to clean up and improve the usability of our APIs. -**Read the new [migration guide](https://github.com/neon-bindings/neon/blob/main/MIGRATION_GUIDE.md)** to learn how to -port your Neon projects to N-API! +**Read the new [migration guide](https://github.com/neon-bindings/neon/blob/main/MIGRATION_GUIDE_0.10.md)** to learn how to port your Neon projects to 0.10! ## Platform Support From c4ae1b5d83929ad835e2b3766f1dc56070ce82d5 Mon Sep 17 00:00:00 2001 From: Dave Herman Date: Thu, 3 Mar 2022 15:07:33 -0800 Subject: [PATCH 3/7] Backticks for `new` Co-authored-by: K.J. Valencik --- MIGRATION_GUIDE_0.10.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATION_GUIDE_0.10.md b/MIGRATION_GUIDE_0.10.md index 7a53dce72..f998d0431 100644 --- a/MIGRATION_GUIDE_0.10.md +++ b/MIGRATION_GUIDE_0.10.md @@ -30,7 +30,7 @@ let v: Handle = obj.get(&mut cx, "name")? ## Layered APIs for function calls -The API for calling (or constructing, i.e. the Neon equivalent of the JavaScript new operator) a JS function has been split into two layered alternatives. The existing `.call()` and `.construct()` functions are now a lower-level primitive, which no longer offers automatic downcasting of arguments or result. But Neon 0.10 now offers a more convenient API for calling functions with an options object and method chaining, with the introduction of the `.call_with()` and `.construct_with()` methods. +The API for calling (or constructing, i.e. the Neon equivalent of the JavaScript `new` operator) a JS function has been split into two layered alternatives. The existing `.call()` and `.construct()` functions are now a lower-level primitive, which no longer offers automatic downcasting of arguments or result. But Neon 0.10 now offers a more convenient API for calling functions with an options object and method chaining, with the introduction of the `.call_with()` and `.construct_with()` methods. ### Example: Calling a function From 47c80568eda02ce2946aa35df571a14218054ea3 Mon Sep 17 00:00:00 2001 From: David Herman Date: Thu, 3 Mar 2022 15:12:27 -0800 Subject: [PATCH 4/7] Compute `call_with().args(...)` arguments inline, to demonstrate more of the win of the high-level API. --- MIGRATION_GUIDE_0.10.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/MIGRATION_GUIDE_0.10.md b/MIGRATION_GUIDE_0.10.md index f998d0431..8fb236235 100644 --- a/MIGRATION_GUIDE_0.10.md +++ b/MIGRATION_GUIDE_0.10.md @@ -47,8 +47,8 @@ f.call(&mut cx, this, args) **After (low-level API):** ```rust -let s: Handle = ...; -let n: Handle = ...; +let s: Handle = cx.string("hello"); +let n: Handle = cx.number(42); let this = cx.global(); f.call(&mut cx, this, [s.upcast(), n.upcast()]) ``` @@ -56,10 +56,8 @@ f.call(&mut cx, this, [s.upcast(), n.upcast()]) **After (high-level API):** ```rust -let s: Handle = ...; -let n: Handle = ...; f.call_with(&cx) - .args((s, n)) + .args((cx.string("hello"), cx.number(42))) .apply(&mut cx) ``` From 1f30b91163e58c8451403cdaea1c3313e3cb74dc Mon Sep 17 00:00:00 2001 From: Dave Herman Date: Thu, 3 Mar 2022 15:13:34 -0800 Subject: [PATCH 5/7] Typo: `h32` -> `u32` Co-authored-by: K.J. Valencik --- MIGRATION_GUIDE_0.10.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATION_GUIDE_0.10.md b/MIGRATION_GUIDE_0.10.md index 8fb236235..dbfd09f8b 100644 --- a/MIGRATION_GUIDE_0.10.md +++ b/MIGRATION_GUIDE_0.10.md @@ -136,7 +136,7 @@ let b2: Handle = ...; ```rust let src_buf: Handle> = ...; -let dst_buf: Handle> = ...; +let dst_buf: Handle> = ...; { let lock = cx.lock(); let src = src_buf.as_slice(&lock).unwrap(); From 75e45589fe9289f9fe4e299eddcd91de193e4637 Mon Sep 17 00:00:00 2001 From: Dave Herman Date: Thu, 3 Mar 2022 15:14:05 -0800 Subject: [PATCH 6/7] Add missing type annotation Co-authored-by: K.J. Valencik --- MIGRATION_GUIDE_0.10.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATION_GUIDE_0.10.md b/MIGRATION_GUIDE_0.10.md index dbfd09f8b..b32acc87c 100644 --- a/MIGRATION_GUIDE_0.10.md +++ b/MIGRATION_GUIDE_0.10.md @@ -169,7 +169,7 @@ use bytemuck::cast_slice; let b: Handle = ...; let u8_slice = b.as_slice(&cx); -let f32_slice = cast_slice(u8_slice); +let f32_slice: &[f32] = cast_slice(u8_slice); ``` # Minor changes From c5d6ed8536f0be6ea6a72a749bc9e72071f8298c Mon Sep 17 00:00:00 2001 From: Dave Herman Date: Thu, 3 Mar 2022 15:15:06 -0800 Subject: [PATCH 7/7] Uncaught errors are now reported as promise rejections See PR #854 Co-authored-by: K.J. Valencik --- MIGRATION_GUIDE_0.10.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATION_GUIDE_0.10.md b/MIGRATION_GUIDE_0.10.md index b32acc87c..e5b3aa947 100644 --- a/MIGRATION_GUIDE_0.10.md +++ b/MIGRATION_GUIDE_0.10.md @@ -176,7 +176,7 @@ let f32_slice: &[f32] = cast_slice(u8_slice); ## Uncaught errors in tasks -Starting with 0.10, uncaught errors (whether Rust panics or JavaScript exceptions) in a task are now captured and reported to Node as an [`uncaughtException` event](https://nodejs.org/api/process.html#event-uncaughtexception). Previously, an uncaught JavaScript exception would be ignored. To handle uncaught exceptions, either wrap the body of a task with [`try_catch`](https://docs.rs/neon/0.10.0-alpha.3/neon/context/trait.Context.html#method.try_catch) or, alternatively, capture all uncaught exceptions in Node with [`process.setUncaughtExceptionCaptureCallback()`](https://nodejs.org/api/process.html#processsetuncaughtexceptioncapturecallbackfn). +Starting with 0.10, uncaught errors (whether Rust panics or JavaScript exceptions) in a task are now captured and reported to Node as an [`unhandledRejection ` event](https://nodejs.org/api/process.html#event-unhandledrejection). Previously, an uncaught JavaScript exception would be ignored. To handle uncaught exceptions, either wrap the body of a task with [`try_catch`](https://docs.rs/neon/0.10.0-alpha.3/neon/context/trait.Context.html#method.try_catch) or, alternatively, capture all uncaught rejections in Node with `process.on("unhandledRejection, (err) => {})`. **Before:**