Compare commits

..

2 Commits

Author SHA1 Message Date
K.J. Valencik
4ad2e5ff06
feat(neon): Initial serde implementation 2021-03-18 13:01:38 -04:00
K.J. Valencik
c105c159e8
fix(neon-runtime): Add #[must_use] to napi::Status and fix places it was being ignored 2021-03-18 13:01:34 -04:00
74 changed files with 6771 additions and 1097 deletions

View File

@ -2,5 +2,4 @@
# Neon defines mutually exclusive feature flags which prevents using `cargo clippy --all-features`
# The following aliases simplify linting the entire workspace
clippy-legacy = "clippy --all-targets --no-default-features -p neon -p neon-runtime -p neon-build -p neon-macros -p tests -p static_tests --features event-handler-api,proc-macros,try-catch-api,legacy-runtime -- -A clippy::missing_safety_doc"
clippy-napi = "clippy --all-targets --no-default-features -p neon -p neon-runtime -p neon-build -p neon-macros -p electron-tests -p napi-tests --features proc-macros,try-catch-api,napi-experimental -- -A clippy::missing_safety_doc"
neon-test = "test --no-default-features --features napi-experimental"
clippy-napi = "clippy --all-targets --no-default-features -p neon -p neon-runtime -p neon-build -p neon-macros -p electron-tests -p napi-tests --features proc-macros,try-catch-api,napi-experimental,serde -- -A clippy::missing_safety_doc"

View File

@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
node-version: [12.x, 14.x, 16.x]
node-version: [10.x, 12.x, 14.x, 15.x]
rust-toolchain: [stable, beta, nightly]
steps:
@ -32,7 +32,7 @@ jobs:
- name: install build-essential
run: sudo apt-get install -y build-essential
- name: run cargo test
run: xvfb-run --auto-servernum cargo neon-test -- --nocapture
run: xvfb-run --auto-servernum cargo test --release -- --nocapture
- name: run CLI test
working-directory: ./create-neon
run: npm test

View File

@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
node-version: [12.x, 14.x, 16.x]
node-version: [10.x, 12.x, 14.x, 15.x]
rust-toolchain: [stable, beta, nightly]
steps:
@ -34,7 +34,7 @@ jobs:
# # https://github.com/nodejs/node-gyp/issues/1933#issuecomment-586915535
# run: npm install -g node-gyp@latest
- name: run cargo test
run: cargo neon-test
run: cargo test --release
- name: run CLI test
working-directory: ./create-neon
run: npm test

View File

@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
node-version: [12.x, 14.x, 16.x]
node-version: [10.x, 12.x, 14.x, 15.x]
rust-toolchain: [stable, beta, nightly]
steps:
@ -31,7 +31,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- name: Use npm v6
if: ${{ matrix.node-version == '16.x' }}
if: ${{ matrix.node-version == '15.x' }}
run: npm install -g npm@6
- name: Install libclang
uses: KyleMayes/install-llvm-action@01144dc97b1e2693196c3056414a44f15180648b
@ -43,7 +43,7 @@ jobs:
# run: npm install -g node-gyp@latest
- run: npm config set msvs_version 2019
- name: run cargo test
run: cargo neon-test
run: cargo test --release
env:
LIBCLANG_PATH: ${{ runner.temp }}/llvm/bin
- name: run CLI test

View File

@ -17,7 +17,6 @@ Neon owes its existence to the contributions of these fine people.
* [Nathaniel Daniel](https://github.com/adumbidiot)
* [Joey Ezechiëls](https://github.com/jjpe)
* [Cory Forsyth](https://github.com/bantic)
* [Olivier Goffart](https://github.com/ogoffart)
* [Dave Herman](https://github.com/dherman)
* [Himself65](https://github.com/Himself65)
* [Maciej Hirsz](https://github.com/maciejhirsz)
@ -29,7 +28,6 @@ Neon owes its existence to the contributions of these fine people.
* [Aleksey Kladov](https://github.com/matklad)
* [Adam Kloboucnik](https://github.com/akloboucnik)
* [Renée Kooi](https://github.com/goto-bus-stop)
* [Anton Lazarev](https://github.com/antonok-edm)
* [Simon Liang](https://github.com/lhr0909)
* [Terence Lee](https://github.com/hone)
* [Milan Loveless](https://github.com/MilanLoveless)
@ -40,7 +38,6 @@ Neon owes its existence to the contributions of these fine people.
* [Robbie Pitts](https://github.com/robbiepitts)
* [Thiago Pontes](https://github.com/thiagopnts)
* [Sean Prashad](https://github.com/SeanPrashad)
* [Jordan Rose](https://github.com/jrose-signal)
* [Antonio Scandurra](https://github.com/as-cii)
* [Edward Shaw](https://github.com/EdShaw)
* [Nathan Sobo](https://github.com/nathansobo)
@ -56,7 +53,6 @@ Neon owes its existence to the contributions of these fine people.
* [Roberto Vidal](https://github.com/jrvidal)
* [Georg Vienna](https://github.com/geovie)
* [Daijiro Wachi](https://github.com/watilde)
* [Chi Wang](https://github.com/patr0nus)
* [wangcong](https://github.com/king6cong)
* [Amila Welihinda](https://github.com/amilajack)
* [Felix Yan](https://github.com/felixonmars)

View File

@ -1,32 +1,30 @@
[package]
name = "neon"
version = "0.8.2"
version = "0.7.1"
authors = ["Dave Herman <david.herman@gmail.com>"]
description = "A safe abstraction layer for Node.js."
readme = "README.md"
homepage = "https://www.neon-bindings.com"
repository = "https://github.com/neon-bindings/neon"
license = "MIT/Apache-2.0"
exclude = ["neon.jpg", "doc/**/*"]
exclude = ["neon.jpg"]
build = "build.rs"
edition = "2018"
[build-dependencies]
neon-build = { version = "=0.8.2", path = "crates/neon-build" }
neon-build = { version = "=0.7.1", path = "crates/neon-build" }
[dev-dependencies]
lazy_static = "1.4.0"
rustversion = "0.1.4"
semver = "0.9"
psd = "0.1.9" # used for a doc example
failure = "0.1.5" # used for a doc example
[dependencies]
cslice = "0.2"
semver = "0.9.0"
smallvec = "1.4.2"
neon-runtime = { version = "=0.8.2", path = "crates/neon-runtime" }
neon-macros = { version = "=0.8.2", path = "crates/neon-macros", optional = true }
neon-runtime = { version = "=0.7.1", path = "crates/neon-runtime" }
neon-macros = { version = "=0.7.1", path = "crates/neon-macros", optional = true }
serde_crate = { package = "serde", version = "1", optional = true }
[features]
default = ["legacy-runtime"]
@ -75,6 +73,9 @@ event-queue-api = []
# Feature flag to include procedural macros
proc-macros = ["neon-macros"]
# Feature flag to enable serde on Neon types
serde = ["serde_crate", "neon-runtime/serde"]
[package.metadata.docs.rs]
features = ["docs-only", "event-handler-api", "proc-macros", "try-catch-api"]

View File

@ -33,15 +33,14 @@ The N-API backend of Neon requires a minimum Node version of 10.0.
To enable the N-API backend, you need to:
1. Remove `build.rs` from the project directory and `build = "build.rs"` from the `Cargo.toml`. The N-API backend does not require a Cargo build script.
2. Disable the default features (for now, the default features select the legacy backend) by setting `default-features = false`; and
3. Enable the appropriate feature flag in your `Cargo.toml` to select the N-API version you need support for (each N-API version N uses the feature flag `"napi-N"`, for example `"napi-4"` for N-API version 4).
1. Disable the default features (for now, the default features select the legacy backend) by setting `default-features = false`; and
2. Enable the appropriate feature flag in your `Cargo.toml` to select the N-API version you need support for (each N-API version N uses the feature flag `"napi-N"`, for example `"napi-4"` for N-API version 4).
As a rule, you should choose the **oldest version of N-API that has the APIs you need.** (We will be adding N-API version requirements to the Neon API docs to make this clearer in the future.) You can consult the [official N-API feature matrix](https://nodejs.org/api/n-api.html#n_api_node_api_version_matrix) to see which N-API versions come with various versions of Node.
```toml
[dependencies.neon]
version = "0.8.2"
version = "0.7.1"
default-features = false
features = ["napi-4"]
```
@ -193,7 +192,7 @@ The supported mechanism for concurrency is the Event Queue API (`neon::event::Ev
```toml
[dependencies.neon]
version = "0.8.1"
version = "0.7.1"
default-features = false
features = ["napi-4", "event-queue-api"]
```

View File

@ -13,20 +13,19 @@ Rust bindings for writing safe and fast native Node.js modules.
Once you have the [platform dependencies](https://neon-bindings.com/docs/getting-started#install-node-build-tools/) installed, getting started is as simple as:
```
$ npm init neon my-project
$ npm install -g neon-cli
$ neon new my-project
```
Then see the [Hello World guide](https://neon-bindings.com/docs/hello-world/) for writing your first Hello World in Neon!
_**Note:** This will create a new project with the `napi-backend` and some documentation may not be up to date._
# Docs
See our [Neon fundamentals docs](https://neon-bindings.com/docs/intro) and our [API docs](https://docs.rs/neon/latest/neon).
# N-API Migration Guide
We've ported Neon to a new backend based on [N-API](https://nodejs.org/api/n-api.html), which will be the basis for Neon 1.0.
We are [hard at work](https://github.com/neon-bindings/neon/issues/444) porting Neon to a new backend based on [N-API](https://nodejs.org/api/n-api.html), which will be the basis for Neon 1.0.
**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!
@ -40,11 +39,11 @@ We've ported Neon to a new backend based on [N-API](https://nodejs.org/api/n-api
### Node.js
| Node 12 | Node 14 | Node 16 |
| Node 10 | Node 12 | Node 14 |
| ------- | ------- | ------- |
| ✓ | ✓ | ✓ |
Support for [LTS versions of Node](https://github.com/nodejs/LTS#release-schedule) and current are expected. If you're using a different version of Node and believe it should be supported, let us know.
Support for [LTS versions of Node](https://github.com/nodejs/LTS#lts-schedule) and current are expected. If you're using a different version of Node and believe it should be supported, let us know.
### Rust

View File

@ -1,37 +1,3 @@
# Version 0.8.2
* More docs improvements
* Added a deprecation warning to `neon new` (https://github.com/neon-bindings/neon/pull/722)
# Version 0.8.1
* Fix `legacy-backend` for Node 16 (https://github.com/neon-bindings/neon/pull/715)
* Various docs improvements
# Version 0.8.0
## Fixes
* `as_slice` and `as_mut_slice` properly handle a `null` pointer from an empty buffer (https://github.com/neon-bindings/neon/pull/681)
* Global drop queue added to avoid panics on N-API 6+ when dropping a `Root` (https://github.com/neon-bindings/neon/pull/700)
## Features
* Added `neon::reflect::eval` (https://github.com/neon-bindings/neon/pull/692)
* Added `create-neon` for creating an N-API project (https://github.com/neon-bindings/neon/pull/690)
* Added details to the `README.md` generated by `create-neon` (https://github.com/neon-bindings/neon/pull/697)
## Improvements
* Switched N-API tests to `cargo-cp-artifact` (https://github.com/neon-bindings/neon/pull/687)
* Added `impl<T: Finalize> Finalize for Option<T>` (https://github.com/neon-bindings/neon/pull/680)
* Added a N-API migration guide (https://github.com/neon-bindings/neon/pull/685)
## Housekeeping
* Lint fixes (https://github.com/neon-bindings/neon/pull/609)
* Lint CI enforcement and `cargo fmt` (https://github.com/neon-bindings/neon/pull/698)
# Version 0.7.1
### Features

26
cli/package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "neon-cli",
"version": "0.8.2",
"version": "0.7.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -616,9 +616,9 @@
"dev": true
},
"handlebars": {
"version": "4.7.7",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
"integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
"version": "4.7.6",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
"integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
"requires": {
"minimist": "^1.2.5",
"neo-async": "^2.6.0",
@ -849,9 +849,9 @@
}
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
},
"lodash.camelcase": {
"version": "4.3.0",
@ -1365,9 +1365,9 @@
"integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw=="
},
"uglify-js": {
"version": "3.13.5",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.5.tgz",
"integrity": "sha512-xtB8yEqIkn7zmOyS2zUNBsYCBRhDkvlNxMMY2smuJ/qA8NCHeQvKCF3i9Z4k8FJH4+PJvZRtMrPynfZ75+CSZw==",
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.0.tgz",
"integrity": "sha512-Esj5HG5WAyrLIdYU74Z3JdG2PxdIusvj6IWHMtlyESxc7kcDz7zYlYjpnSokn1UbpV0d/QX9fan7gkCNd/9BQA==",
"optional": true
},
"validate-npm-package-license": {
@ -1552,9 +1552,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"y18n": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
"integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
},
"yargs": {

View File

@ -1,6 +1,6 @@
{
"name": "neon-cli",
"version": "0.8.2",
"version": "0.7.1",
"description": "Build and load native Rust/Neon modules.",
"author": "Dave Herman <david.herman@gmail.com>",
"repository": {

View File

@ -99,11 +99,6 @@ interface Answers {
}
export default async function wizard(pwd: string, name: string, neon: string | null, features: string | null, noDefaultFeatures: boolean) {
let warning = "WARN: `neon new` is deprecated. To create a new project use `npm init neon my-project`.";
let banner = "".padStart(warning.length + 4, "#");
console.warn(`${banner}\n# ${warning} #\n${banner}\n`);
let its = validateName(name);
if (!its.validForNewPackages) {
let errors = (its.errors || []).concat(its.warnings || []);

View File

@ -1,6 +1,6 @@
[package]
name = "neon-build"
version = "0.8.2"
version = "0.7.1"
authors = ["Dave Herman <david.herman@gmail.com>"]
description = "Build logic required for Neon projects."
repository = "https://github.com/neon-bindings/neon"
@ -9,4 +9,4 @@ edition = "2018"
build = "build.rs"
[dependencies]
neon-sys = { version = "=0.8.2", path = "../neon-sys", optional = true }
neon-sys = { version = "=0.7.1", path = "../neon-sys", optional = true }

View File

@ -1,6 +1,6 @@
[package]
name = "neon-macros"
version = "0.8.2"
version = "0.7.1"
authors = ["Dave Herman <david.herman@gmail.com>"]
description = "Procedural macros supporting Neon"
repository = "https://github.com/neon-bindings/neon"

View File

@ -14,15 +14,14 @@ use legacy as macros;
// Implementations are in the backend dependent module
#[proc_macro_attribute]
/// Marks a function as the main entry point for initialization in
/// a Neon module.
///
/// This attribute should only be used _once_ in a module and will
/// Marks a method as the main entrypoint for initialization in a Neon
/// module. This attribute should only be used _once_ in a module and will
/// be called each time the module is initialized in a context.
///
/// ```ignore
/// # use neon::prelude::*;
/// #[neon::main]
/// fn main(mut cx: ModuleContext) -> NeonResult<()> {
/// fn my_module(mut cx: ModuleContext) -> NeonResult<()> {
/// let version = cx.string("1.0.0");
///
/// cx.export_value("version", version)?;

View File

@ -1,6 +1,6 @@
[package]
name = "neon-runtime"
version = "0.8.2"
version = "0.7.1"
authors = ["Dave Herman <david.herman@gmail.com>"]
description = "Bindings to the Node.js native addon API, used by the Neon implementation."
repository = "https://github.com/neon-bindings/neon"
@ -9,8 +9,10 @@ edition = "2018"
[dependencies]
cfg-if = "1.0.0"
conv = { version = "0.3", optional = true }
libloading = { version = "0.6.5", optional = true }
neon-sys = { version = "=0.8.2", path = "../neon-sys", optional = true }
neon-sys = { version = "=0.7.1", path = "../neon-sys", optional = true }
serde_crate = { package = "serde", version = "1", optional = true }
smallvec = "1.4.2"
[dev-dependencies]
@ -25,6 +27,7 @@ napi-4 = ["napi-3"]
napi-5 = ["napi-4"]
napi-6 = ["napi-5"]
napi-experimental = ["napi-6"]
serde = ["serde_crate", "conv"]
docs-only = ["neon-sys/docs-only"]
[package.metadata.docs.rs]

View File

@ -200,6 +200,17 @@ mod napi1 {
);
}
#[cfg(feature = "serde")]
mod napi1_serde {
use super::super::types::*;
generate!(
extern "C" {
fn get_property_names(env: Env, object: Value, result: *mut Value) -> Status;
}
);
}
#[cfg(feature = "napi-4")]
mod napi4 {
use super::super::types::*;
@ -257,7 +268,6 @@ mod napi5 {
#[cfg(feature = "napi-6")]
mod napi6 {
use super::super::types::*;
use std::os::raw::c_void;
generate!(
extern "C" {
@ -269,20 +279,13 @@ mod napi6 {
key_conversion: KeyConversion,
result: *mut Value,
) -> Status;
fn set_instance_data(
env: Env,
data: *mut c_void,
finalize_cb: Finalize,
finalize_hint: *mut c_void,
) -> Status;
fn get_instance_data(env: Env, data: *mut *mut c_void) -> Status;
}
);
}
pub(crate) use napi1::*;
#[cfg(feature = "serde")]
pub(crate) use napi1_serde::*;
#[cfg(feature = "napi-4")]
pub(crate) use napi4::*;
#[cfg(feature = "napi-5")]
@ -314,6 +317,9 @@ pub(crate) unsafe fn load(env: Env) -> Result<(), libloading::Error> {
napi1::load(&host, version, 1)?;
#[cfg(feature = "serde")]
napi1_serde::load(&host, version, 1)?;
#[cfg(feature = "napi-4")]
napi4::load(&host, version, 4)?;

View File

@ -69,6 +69,7 @@ pub type ThreadsafeFunctionCallJs = Option<
#[allow(dead_code)]
#[repr(u32)]
#[must_use]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Status {
Ok = 0,

View File

@ -1,49 +0,0 @@
//! # Environment life cycle APIs
//!
//! These APIs map to the life cycle of a specific "Agent" or self-contained
//! environment. If a Neon module is loaded multiple times (Web Workers, worker
//! threads), these API will be handle data associated with a specific instance.
//!
//! See the [N-API Lifecycle][npai-docs] documentation for more details.
//!
//! [napi-docs]: https://nodejs.org/api/n-api.html#n_api_environment_life_cycle_apis
use std::mem::MaybeUninit;
use std::os::raw::c_void;
use std::ptr;
use crate::napi::bindings as napi;
use crate::raw::Env;
/// # Safety
/// `env` must point to a valid `napi_env` for this thread
pub unsafe fn set_instance_data<T: Send + 'static>(env: Env, data: T) -> *mut T {
let data = Box::into_raw(Box::new(data));
assert_eq!(
napi::set_instance_data(env, data.cast(), Some(drop_box::<T>), ptr::null_mut(),),
napi::Status::Ok,
);
data
}
/// # Safety
/// * `T` must be the same type used in `set_instance_data`
/// * Caller must ensure reference does not outlive `Env`
/// * Return value may be `null`
/// * `env` must point to a valid `napi_env` for this thread
pub unsafe fn get_instance_data<T: Send + 'static>(env: Env) -> *mut T {
let mut data = MaybeUninit::uninit();
assert_eq!(
napi::get_instance_data(env, data.as_mut_ptr(),),
napi::Status::Ok,
);
data.assume_init().cast()
}
unsafe extern "C" fn drop_box<T>(_env: Env, data: *mut c_void, _hint: *mut c_void) {
Box::<T>::from_raw(data.cast());
}

View File

@ -8,8 +8,6 @@ pub mod date;
pub mod error;
pub mod external;
pub mod fun;
#[cfg(feature = "napi-6")]
pub mod lifecycle;
pub mod mem;
pub mod object;
pub mod primitive;
@ -21,5 +19,8 @@ pub mod tag;
#[cfg(feature = "napi-4")]
pub mod tsfn;
#[cfg(feature = "serde")]
pub mod serde;
mod bindings;
pub use bindings::*;

View File

@ -5,7 +5,7 @@ use crate::raw::{Env, Local};
/// Mutates the `out` argument to refer to a `napi_value` containing a newly created JavaScript Object.
pub unsafe fn new(out: &mut Local, env: Env) {
napi::create_object(env, out as *mut _);
assert_eq!(napi::create_object(env, out as *mut _), napi::Status::Ok,);
}
#[cfg(feature = "napi-6")]

View File

@ -3,17 +3,23 @@ use crate::raw::{Env, Local};
/// Mutates the `out` argument provided to refer to the global `undefined` object.
pub unsafe fn undefined(out: &mut Local, env: Env) {
napi::get_undefined(env, out as *mut Local);
assert_eq!(
napi::get_undefined(env, out as *mut Local),
napi::Status::Ok,
);
}
/// Mutates the `out` argument provided to refer to the global `null` object.
pub unsafe fn null(out: &mut Local, env: Env) {
napi::get_null(env, out as *mut Local);
assert_eq!(napi::get_null(env, out as *mut Local), napi::Status::Ok,);
}
/// Mutates the `out` argument provided to refer to one of the global `true` or `false` objects.
pub unsafe fn boolean(out: &mut Local, env: Env, b: bool) {
napi::get_boolean(env, b, out as *mut Local);
assert_eq!(
napi::get_boolean(env, b, out as *mut Local),
napi::Status::Ok,
);
}
/// Get the boolean value out of a `Local` object. If the `Local` object does not contain a
@ -30,7 +36,10 @@ pub unsafe fn boolean_value(env: Env, p: Local) -> bool {
/// Mutates the `out` argument provided to refer to a newly created `Local` containing a
/// JavaScript number.
pub unsafe fn number(out: &mut Local, env: Env, v: f64) {
napi::create_double(env, v, out as *mut Local);
assert_eq!(
napi::create_double(env, v, out as *mut Local),
napi::Status::Ok,
);
}
/// Gets the underlying value of an `Local` object containing a JavaScript number. Panics if

View File

@ -0,0 +1,484 @@
//! Implements a serde deserializer for JavaScript values
//!
//! # Safety
//!
//! All JavaScript types are neither `Send` or `Sync`. Threads should be used.
use conv::{ApproxFrom, DefaultApprox};
use serde_crate::de::{
self, DeserializeSeed, EnumAccess, IntoDeserializer, MapAccess, SeqAccess, VariantAccess,
Visitor,
};
use super::*;
use crate::napi;
/// High level deserializer for all JavaScript values
pub(super) struct Deserializer {
env: napi::Env,
value: napi::Value,
}
impl Deserializer {
pub(super) fn new(env: napi::Env, value: napi::Value) -> Self {
Deserializer { env, value }
}
}
/// Specialized deserializer for `Array`
struct ArrayAccessor {
env: napi::Env,
array: napi::Value,
len: u32,
index: u32,
}
impl ArrayAccessor {
fn new(env: napi::Env, array: napi::Value) -> Result<Self, Error> {
Ok(Self {
env,
array,
len: get_array_len(env, array)?,
index: 0,
})
}
fn next(&mut self) -> Result<Option<napi::Value>, Error> {
if self.index >= self.len {
return Ok(None);
}
let element = get_array_element(self.env, self.array, self.index)?;
self.index += 1;
Ok(Some(element))
}
}
/// Specialized deserializer for generic `Object`
/// Only enumerable keys are read
struct ObjectAccessor {
env: napi::Env,
object: napi::Value,
keys: ArrayAccessor,
// Cache the most recent key for reading the next value
next: Option<napi::Value>,
}
impl ObjectAccessor {
fn new(env: napi::Env, object: napi::Value) -> Result<Self, Error> {
let keys = get_property_names(env, object)?;
let keys = ArrayAccessor::new(env, keys)?;
Ok(Self {
env,
object,
keys,
next: None,
})
}
}
impl de::Deserializer<'static> for Deserializer {
type Error = Error;
// JavaScript is a self describing format, allowing us to provide a deserialization
// implementation without prior knowledge of the schema. This is useful for types
// like `serde_json::Value`.
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
match typeof_value(self.env, self.value)? {
napi::ValueType::Undefined | napi::ValueType::Null => self.deserialize_unit(visitor),
napi::ValueType::Boolean => self.deserialize_bool(visitor),
napi::ValueType::Number => self.deserialize_f64(visitor),
napi::ValueType::String => self.deserialize_string(visitor),
napi::ValueType::Object => self.deserialize_map(visitor),
typ => Err(Error::unsupported_type(typ)),
}
}
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
visitor.visit_bool(get_value_bool(self.env, self.value)?)
}
// JavaScript only provides an `f64` number type. All integer types require
// a lossy approximation. Out of range values will error.
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <i8 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_i8(n)
}
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <i16 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_i16(n)
}
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <i32 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_i32(n)
}
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <i64 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_i64(n)
}
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <u8 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_u8(n)
}
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <u16 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_u16(n)
}
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <u32 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_u32(n)
}
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <u64 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_u64(n)
}
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <f32 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_f32(n)
}
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
visitor.visit_f64(n)
}
// `char` are serialized as a single character `string`
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
self.deserialize_string(visitor)
}
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
self.deserialize_string(visitor)
}
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
visitor.visit_string(get_value_string(self.env, self.value)?)
}
// This could be optimized to borrow the bytes from the JavaScript value
// However, since JavaScript values are neither `Send` or `Sync` it is not
// generally useful
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
self.deserialize_byte_buf(visitor)
}
// Bytes are serialized as the idiomatic `ArrayBuffer` JavaScript type
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
visitor.visit_byte_buf(get_value_arraybuffer(self.env, self.value)?)
}
// `None` are serialized as `null`, but when deserializing `undefined` is
// also accepted.
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
match typeof_value(self.env, self.value)? {
napi::ValueType::Null | napi::ValueType::Undefined => visitor.visit_none(),
_ => visitor.visit_some(self),
}
}
// JavaScript does not have a concept of unit; `null` or `undefined` is accepted
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
match typeof_value(self.env, self.value)? {
napi::ValueType::Null | napi::ValueType::Undefined => visitor.visit_unit(),
_ => Err(Error::expected_null()),
}
}
fn deserialize_unit_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
self.deserialize_unit(visitor)
}
fn deserialize_newtype_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
visitor.visit_newtype_struct(self)
}
// `Array` is used since it is the only sequence type in JavaScript
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
visitor.visit_seq(ArrayAccessor::new(self.env, self.value)?)
}
// `Array` are used to serialize tuples; this is a common pattern, especially in TypeScript
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
self.deserialize_seq(visitor)
}
fn deserialize_tuple_struct<V>(
self,
_name: &'static str,
_len: usize,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
self.deserialize_seq(visitor)
}
// Generic `Object` are used to serialize map
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
visitor.visit_map(ObjectAccessor::new(self.env, self.value)?)
}
fn deserialize_struct<V>(
self,
_name: &'static str,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
self.deserialize_map(visitor)
}
fn deserialize_enum<V>(
self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
// No-value enums are serialized as `string`
if typeof_value(self.env, self.value)? == napi::ValueType::String {
let s = get_value_string(self.env, self.value)?;
visitor.visit_enum(s.into_deserializer())
} else {
visitor.visit_enum(self)
}
}
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
self.deserialize_string(visitor)
}
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
visitor.visit_unit()
}
}
impl SeqAccess<'static> for ArrayAccessor {
type Error = Error;
// This will have unpredictable results if the `Array` has a getter that mutates
// the object. It should be _safe_ and return an `Error`, but hopefully users
// don't do this.
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: DeserializeSeed<'static>,
{
self.next()?
.map(|v| seed.deserialize(Deserializer::new(self.env, v)))
.transpose()
}
// We can efficiently provide a size hint since `Array` have known length
fn size_hint(&self) -> Option<usize> {
Some((self.len - self.index) as usize)
}
}
impl MapAccess<'static> for ObjectAccessor {
type Error = Error;
// This will have unpredictable results if the `Object` has a getter that mutates
// the object. It should be _safe_ and return an `Error`, but hopefully users
// don't do this on serializable types.
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
where
K: DeserializeSeed<'static>,
{
// Store the next `key` for deserializing the value in `next_value_seed`
self.next = self.keys.next()?;
self.next
.map(|v| seed.deserialize(Deserializer::new(self.env, v)))
.transpose()
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
where
V: DeserializeSeed<'static>,
{
// `Error::missing_key` should only happen in a buggy serde implementation
let key = self.next.ok_or_else(Error::missing_key)?;
let value = get_property(self.env, self.object, key)?;
seed.deserialize(Deserializer::new(self.env, value))
}
// We can efficiently provide a size hint since we fetch all keys ahead of time
fn size_hint(&self) -> Option<usize> {
self.keys.size_hint()
}
}
impl EnumAccess<'static> for Deserializer {
type Error = Error;
type Variant = Self;
// Enums are serialized as `{ [type]: value }`
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
where
V: DeserializeSeed<'static>,
{
let keys = get_property_names(self.env, self.value)?;
let key = get_array_element(self.env, keys, 0)?;
let value = get_property(self.env, self.value, key)?;
let deserializer = Deserializer::new(self.env, value);
let key = seed.deserialize(self)?;
Ok((key, deserializer))
}
}
// Externally tagged enum can be treated equivalent to the enclosed type
impl VariantAccess<'static> for Deserializer {
type Error = Error;
fn unit_variant(self) -> Result<(), Self::Error> {
Err(Error::expected_string())
}
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
where
T: DeserializeSeed<'static>,
{
seed.deserialize(self)
}
fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
de::Deserializer::deserialize_seq(self, visitor)
}
fn struct_variant<V>(
self,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
de::Deserializer::deserialize_map(self, visitor)
}
}

View File

@ -0,0 +1,149 @@
//! Error type and conversions for serde transcoding
use std::error;
use std::fmt;
use conv::{FloatError, PosOverflow, RangeError};
use serde_crate::{de, ser};
use crate::napi;
#[derive(Clone, Debug, PartialEq)]
/// This type represents all possible errors that can occur when serializing or
/// deserializing JavaScript types.
pub struct Error {
kind: ErrorKind,
}
impl error::Error for Error {}
impl Error {
fn new(kind: ErrorKind) -> Self {
Self { kind }
}
/// Indicates if the error was due to an exception in the JavaScript VM
/// If an exception is pending, all other JavaScript operations will fail
/// until it is cleared.
pub fn is_exception_pending(&self) -> bool {
self.kind == ErrorKind::Napi(napi::Status::PendingException)
}
pub(super) fn expected_null() -> Self {
ErrorKind::ExpectedNull.into()
}
pub(super) fn expected_string() -> Self {
ErrorKind::ExpectedString.into()
}
pub(super) fn missing_key() -> Self {
ErrorKind::MissingKey.into()
}
pub(super) fn unsupported_type(typ: napi::ValueType) -> Self {
ErrorKind::UnsupportedType(typ).into()
}
}
#[derive(Clone, Debug, PartialEq)]
pub(super) enum ErrorKind {
// Serde
Custom(String),
// Serde reads and writes key/value pairs as distinct steps requiring
// Neon to cache the intermediate key. This error is unexpected and should
// never occur outside of a buggy serde implementation.
MissingKey,
// Number conversions
FloatError(FloatError<f64>),
I64Error(RangeError<i64>),
U64Error(PosOverflow<u64>),
UsizeError(PosOverflow<usize>),
// deserialize_any
ExpectedNull,
ExpectedString,
UnsupportedType(napi::ValueType),
// N-API
Napi(napi::Status),
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
Error::new(kind)
}
}
impl From<FloatError<f64>> for Error {
fn from(other: FloatError<f64>) -> Self {
ErrorKind::FloatError(other).into()
}
}
impl From<RangeError<f64>> for Error {
fn from(other: RangeError<f64>) -> Self {
let err = match other {
RangeError::PosOverflow(v) => FloatError::PosOverflow(v),
RangeError::NegOverflow(v) => FloatError::NegOverflow(v),
};
err.into()
}
}
impl From<RangeError<i64>> for Error {
fn from(other: RangeError<i64>) -> Self {
ErrorKind::I64Error(other).into()
}
}
impl From<PosOverflow<u64>> for Error {
fn from(other: PosOverflow<u64>) -> Self {
ErrorKind::U64Error(other).into()
}
}
impl From<PosOverflow<usize>> for Error {
fn from(other: PosOverflow<usize>) -> Self {
ErrorKind::UsizeError(other).into()
}
}
impl From<napi::Status> for Error {
fn from(other: napi::Status) -> Self {
ErrorKind::Napi(other).into()
}
}
impl de::Error for Error {
fn custom<T: fmt::Display>(err: T) -> Self {
Error {
kind: ErrorKind::Custom(err.to_string()),
}
}
}
impl ser::Error for Error {
fn custom<T: fmt::Display>(err: T) -> Self {
de::Error::custom(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.kind {
ErrorKind::Custom(err) => f.write_str(err),
ErrorKind::MissingKey => f.write_str("MissingKey"),
ErrorKind::FloatError(err) => fmt::Display::fmt(err, f),
ErrorKind::I64Error(err) => fmt::Display::fmt(err, f),
ErrorKind::U64Error(err) => fmt::Display::fmt(err, f),
ErrorKind::UsizeError(err) => fmt::Display::fmt(err, f),
ErrorKind::ExpectedNull => f.write_str("ExpectedNull"),
ErrorKind::ExpectedString => f.write_str("ExpectedString"),
ErrorKind::UnsupportedType(typ) => write!(f, "UnsupportedType({:?})", typ),
ErrorKind::Napi(err) => write!(f, "Napi({:?})", err),
}
}
}

View File

@ -0,0 +1,282 @@
//! N-API wrappers used by serde transcoding
//!
//! In many cases, these functions provide similar functionality to functions
//! available elsewhere in `neon-runtime`. However, keeping serde fully self
//! contained has a few benefits:
//!
//! * Wrappers can be written, altered, combined and otherwise optimized for
//! providing the most efficient possible serde implementation
//! * All wrappers can be written idiomatically instead of matching the legacy
//! behavior of `neon-sys`
//! * The serde implementation remains self contained for potential extraction
//! into a separate crate
//!
//! # Safety
//!
//! _Do not export anything from this file outside of the serde module._
//!
//! Nearly all functions in this crate are `unsafe` and should be treated that
//! way despite being marked safe. However, since the serde implementation is
//! unsafe in it's entirety, these wrappers are marked safe to make the
//! implementation easier to read and review.
use std::mem::MaybeUninit;
use std::ptr;
use std::slice;
use crate::napi;
trait Verify {
fn verify(self) -> Result<(), napi::Status>;
}
impl Verify for napi::Status {
fn verify(self) -> Result<(), napi::Status> {
if self == napi::Status::Ok {
Ok(())
} else {
Err(self)
}
}
}
pub(super) fn get_value_bool(env: napi::Env, value: napi::Value) -> Result<bool, napi::Status> {
let mut out = false;
unsafe {
napi::get_value_bool(env, value, &mut out as *mut bool).verify()?;
};
Ok(out)
}
pub(super) fn get_value_double(env: napi::Env, value: napi::Value) -> Result<f64, napi::Status> {
let mut out = 0f64;
unsafe {
napi::get_value_double(env, value, &mut out as *mut f64).verify()?;
};
Ok(out)
}
pub(super) fn get_string_len(env: napi::Env, value: napi::Value) -> Result<usize, napi::Status> {
let mut out = 0usize;
unsafe {
napi::get_value_string_utf8(env, value, ptr::null_mut(), 0, &mut out as *mut usize)
.verify()?;
}
Ok(out)
}
pub(super) fn get_value_string(env: napi::Env, value: napi::Value) -> Result<String, napi::Status> {
let mut out = 0usize;
let string_len = get_string_len(env, value)?;
let buf_len = string_len + 1;
let mut buf = Vec::<u8>::with_capacity(buf_len);
unsafe {
napi::get_value_string_utf8(
env,
value,
buf.as_mut_ptr().cast(),
buf_len,
&mut out as *mut usize,
)
.verify()?;
debug_assert_eq!(out, string_len);
buf.set_len(string_len);
Ok(String::from_utf8_unchecked(buf))
}
}
pub(super) fn get_value_arraybuffer(
env: napi::Env,
value: napi::Value,
) -> Result<Vec<u8>, napi::Status> {
let mut len = 0usize;
let mut out = MaybeUninit::uninit();
unsafe {
napi::get_arraybuffer_info(env, value, out.as_mut_ptr(), &mut len as *mut usize)
.verify()?;
};
let buf = unsafe { slice::from_raw_parts(out.assume_init().cast(), len) };
Ok(buf.to_vec())
}
pub(super) fn get_array_len(env: napi::Env, value: napi::Value) -> Result<u32, napi::Status> {
let mut len = 0u32;
unsafe {
napi::get_array_length(env, value, &mut len as *mut u32).verify()?;
};
Ok(len)
}
pub(super) fn get_array_element(
env: napi::Env,
arr: napi::Value,
i: u32,
) -> Result<napi::Value, napi::Status> {
let mut out = MaybeUninit::uninit();
unsafe {
napi::get_element(env, arr, i, out.as_mut_ptr()).verify()?;
Ok(out.assume_init())
}
}
pub(super) fn typeof_value(
env: napi::Env,
value: napi::Value,
) -> Result<napi::ValueType, napi::Status> {
let mut out = MaybeUninit::uninit();
unsafe {
napi::typeof_value(env, value, out.as_mut_ptr()).verify()?;
Ok(out.assume_init())
}
}
pub(super) fn get_property_names(
env: napi::Env,
value: napi::Value,
) -> Result<napi::Value, napi::Status> {
let mut out = MaybeUninit::uninit();
unsafe {
napi::get_property_names(env, value, out.as_mut_ptr()).verify()?;
Ok(out.assume_init())
}
}
pub(super) fn get_property(
env: napi::Env,
object: napi::Value,
key: napi::Value,
) -> Result<napi::Value, napi::Status> {
let mut out = MaybeUninit::uninit();
unsafe {
napi::get_property(env, object, key, out.as_mut_ptr()).verify()?;
Ok(out.assume_init())
}
}
pub(super) fn get_null(env: napi::Env) -> Result<napi::Value, napi::Status> {
let mut value = MaybeUninit::uninit();
unsafe {
napi::get_null(env, value.as_mut_ptr()).verify()?;
Ok(value.assume_init())
}
}
pub(super) fn create_double(
env: napi::Env,
v: impl Into<f64>,
) -> Result<napi::Value, napi::Status> {
let mut value = MaybeUninit::uninit();
let v = v.into();
unsafe {
napi::create_double(env, v, value.as_mut_ptr()).verify()?;
Ok(value.assume_init())
}
}
pub(super) fn create_bool(env: napi::Env, v: bool) -> Result<napi::Value, napi::Status> {
let mut value = MaybeUninit::uninit();
unsafe {
napi::get_boolean(env, v, value.as_mut_ptr()).verify()?;
Ok(value.assume_init())
}
}
pub(super) fn create_string(
env: napi::Env,
v: impl AsRef<str>,
) -> Result<napi::Value, napi::Status> {
let mut value = MaybeUninit::uninit();
let v = v.as_ref();
unsafe {
napi::create_string_utf8(env, v.as_ptr().cast(), v.len(), value.as_mut_ptr()).verify()?;
Ok(value.assume_init())
}
}
pub(super) fn create_object(env: napi::Env) -> Result<napi::Value, napi::Status> {
let mut value = MaybeUninit::uninit();
unsafe {
napi::create_object(env, value.as_mut_ptr()).verify()?;
Ok(value.assume_init())
}
}
pub(super) fn create_array_with_length(
env: napi::Env,
len: usize,
) -> Result<napi::Value, napi::Status> {
let mut value = MaybeUninit::uninit();
unsafe {
napi::create_array_with_length(env, len, value.as_mut_ptr()).verify()?;
Ok(value.assume_init())
}
}
pub(super) fn create_arraybuffer(env: napi::Env, v: &[u8]) -> Result<napi::Value, napi::Status> {
let mut value = MaybeUninit::uninit();
let mut data = MaybeUninit::uninit();
unsafe {
napi::create_arraybuffer(env, v.len(), data.as_mut_ptr(), value.as_mut_ptr()).verify()?;
};
let data = unsafe {
let data = data.assume_init().cast();
std::slice::from_raw_parts_mut(data, v.len())
};
data.copy_from_slice(v);
Ok(unsafe { value.assume_init() })
}
pub(super) fn object_set(
env: napi::Env,
o: napi::Value,
k: napi::Value,
v: napi::Value,
) -> Result<(), napi::Status> {
unsafe {
napi::set_property(env, o, k, v).verify()?;
};
Ok(())
}
pub(super) fn array_set(
env: napi::Env,
arr: napi::Value,
k: u32,
v: napi::Value,
) -> Result<(), napi::Status> {
unsafe {
napi::set_element(env, arr, k, v).verify()?;
}
Ok(())
}

View File

@ -0,0 +1,35 @@
//! Serde implementation for converting between Rust and JavaScript data types
mod de;
mod error;
mod js;
mod se;
use serde_crate::{Deserialize, Serialize};
use crate::napi;
pub use self::error::Error;
use self::js::*;
/// Attempts to read a JavaScript value into a Rust data type using the serde::Deserialize implementation
/// # Safety
/// * `env` must point to the JavaScript runtime executing on the current thread
/// * `value` must be a valid JavaScript object associated with the same runtime as `env`
pub unsafe fn from_value<T: ?Sized>(env: napi::Env, value: napi::Value) -> Result<T, Error>
where
T: Deserialize<'static>,
{
T::deserialize(de::Deserializer::new(env, value))
}
/// Attempts to write Rust data into a JavaScript value using the serde::Serialize implementation
/// # Safety
/// * The returned `napi::Value` must not outlive the `env` parameter
/// * `env` must point to the JavaScript runtime executing on the current thread
pub unsafe fn to_value<T: ?Sized>(env: napi::Env, value: &T) -> Result<napi::Value, Error>
where
T: Serialize,
{
value.serialize(se::Serializer::new(env))
}

View File

@ -0,0 +1,432 @@
//! Implements a serde serializer for JavaScript values
//!
//! # Safety
//!
//! All JavaScript types are neither `Send` or `Sync`. Threads should be used.
use conv::ValueFrom;
use serde_crate::{ser, Serialize};
use super::*;
use crate::napi;
#[derive(Clone, Copy)]
#[repr(transparent)]
/// High level deserializer for all JavaScript values
pub(super) struct Serializer {
env: napi::Env,
}
impl Serializer {
pub(super) fn new(env: napi::Env) -> Self {
Self { env }
}
}
// Specialized serializer for writing to an `Array`
pub(super) struct ArraySerializer {
serializer: Serializer,
value: napi::Value,
offset: usize,
}
impl ArraySerializer {
fn new(serializer: Serializer, value: napi::Value) -> Self {
Self {
serializer,
value,
offset: 0,
}
}
}
// `Array` serializer for externally tagged enum `{ [key]: value }`
pub(super) struct WrappedArraySerializer {
serializer: ArraySerializer,
value: napi::Value,
}
impl WrappedArraySerializer {
fn new(serializer: ArraySerializer, value: napi::Value) -> Self {
Self { serializer, value }
}
}
// Specialized serializer for writing to a generic `Object`
pub(super) struct ObjectSerializer {
serializer: Serializer,
value: napi::Value,
key: Option<napi::Value>,
}
impl ObjectSerializer {
fn new(serializer: Serializer, value: napi::Value) -> Self {
Self {
serializer,
value,
key: None,
}
}
}
// `Object` serializer for externally tagged enum `{ [key]: value }`
pub(super) struct WrappedObjectSerializer {
serializer: ObjectSerializer,
value: napi::Value,
}
impl WrappedObjectSerializer {
fn new(serializer: ObjectSerializer, value: napi::Value) -> Self {
Self { serializer, value }
}
}
impl ser::Serializer for Serializer {
type Ok = napi::Value;
type Error = Error;
// Limited JavaScript types require sequences and tuples to both use `Array`
type SerializeSeq = ArraySerializer;
type SerializeTuple = ArraySerializer;
type SerializeTupleStruct = ArraySerializer;
type SerializeTupleVariant = WrappedArraySerializer;
type SerializeMap = ObjectSerializer;
type SerializeStruct = ObjectSerializer;
type SerializeStructVariant = WrappedObjectSerializer;
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
Ok(create_bool(self.env, v)?)
}
// All numeric types are serialized into `f64`
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
Ok(create_double(self.env, v)?)
}
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
Ok(create_double(self.env, v)?)
}
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
Ok(create_double(self.env, v)?)
}
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
let v = f64::value_from(v)?;
Ok(create_double(self.env, v)?)
}
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
Ok(create_double(self.env, v)?)
}
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
Ok(create_double(self.env, v)?)
}
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
Ok(create_double(self.env, v)?)
}
fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
let v = f64::value_from(v)?;
Ok(create_double(self.env, v)?)
}
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
Ok(create_double(self.env, v)?)
}
fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
Ok(create_double(self.env, v)?)
}
// `char` are serialized as single character string
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
Ok(create_string(self.env, v.to_string())?)
}
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
Ok(create_string(self.env, v)?)
}
// Bytes are serialized as `ArrayBuffer`
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
Ok(create_arraybuffer(self.env, v)?)
}
// `None` is serialized as a `null`
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
self.serialize_unit()
}
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
// JavaScript does not have a unit type; `null` is used instead
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
Ok(get_null(self.env)?)
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
self.serialize_unit()
}
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
self.serialize_str(variant)
}
fn serialize_newtype_struct<T>(
self,
_name: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
fn serialize_newtype_variant<T>(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
let o = create_object(self.env)?;
let k = create_string(self.env, variant)?;
let v = value.serialize(self)?;
object_set(self.env, o, k, v)?;
Ok(o)
}
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
let len = len.unwrap_or_default();
let value = create_array_with_length(self.env, len)?;
Ok(ArraySerializer::new(self, value))
}
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
self.serialize_seq(Some(len))
}
fn serialize_tuple_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
self.serialize_seq(Some(len))
}
// Externally tagged enum; `{ [key]: value }`
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
let env = self.env;
let wrapper = create_object(env)?;
let arr = create_array_with_length(env, len)?;
let k = create_string(env, variant)?;
let serializer = ArraySerializer::new(self, arr);
object_set(env, wrapper, k, arr)?;
Ok(WrappedArraySerializer::new(serializer, wrapper))
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
let value = create_object(self.env)?;
Ok(ObjectSerializer::new(self, value))
}
fn serialize_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
self.serialize_map(Some(len))
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
let env = self.env;
let wrapper = create_object(env)?;
let value = create_object(env)?;
let k = create_string(env, variant)?;
let serializer = ObjectSerializer::new(self, value);
object_set(env, wrapper, k, value)?;
Ok(WrappedObjectSerializer::new(serializer, wrapper))
}
}
impl<'a> ser::SerializeSeq for ArraySerializer {
type Ok = napi::Value;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
let value = value.serialize(self.serializer)?;
let k = u32::value_from(self.offset)?;
array_set(self.serializer.env, self.value, k, value)?;
self.offset += 1;
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(self.value)
}
}
impl<'a> ser::SerializeTuple for ArraySerializer {
type Ok = napi::Value;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
ser::SerializeSeq::serialize_element(self, value)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
ser::SerializeSeq::end(self)
}
}
impl<'a> ser::SerializeTupleStruct for ArraySerializer {
type Ok = napi::Value;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
ser::SerializeSeq::serialize_element(self, value)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
ser::SerializeSeq::end(self)
}
}
impl<'a> ser::SerializeTupleVariant for WrappedArraySerializer {
type Ok = napi::Value;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
ser::SerializeSeq::serialize_element(&mut self.serializer, value)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(self.value)
}
}
impl ser::SerializeMap for ObjectSerializer {
type Ok = napi::Value;
type Error = Error;
fn serialize_key<T>(&mut self, key: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
self.key = Some(key.serialize(self.serializer)?);
Ok(())
}
fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
let k = self.key.ok_or_else(Error::missing_key)?;
let v = value.serialize(self.serializer)?;
object_set(self.serializer.env, self.value, k, v)?;
Ok(())
}
fn serialize_entry<K, V>(&mut self, key: &K, value: &V) -> Result<(), Self::Error>
where
K: ?Sized + Serialize,
V: ?Sized + Serialize,
{
let k = key.serialize(self.serializer)?;
let v = value.serialize(self.serializer)?;
object_set(self.serializer.env, self.value, k, v)?;
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(self.value)
}
}
impl ser::SerializeStruct for ObjectSerializer {
type Ok = napi::Value;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
ser::SerializeMap::serialize_entry(self, key, value)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(self.value)
}
}
impl ser::SerializeStructVariant for WrappedObjectSerializer {
type Ok = napi::Value;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
ser::SerializeMap::serialize_entry(&mut self.serializer, key, value)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(self.value)
}
}

View File

@ -168,7 +168,8 @@ impl<T: Send + 'static> ThreadsafeFunction<T> {
impl<T> Drop for ThreadsafeFunction<T> {
fn drop(&mut self) {
unsafe {
napi::release_threadsafe_function(
// Status is ignored because cannot take any action if there was an error
let _ = napi::release_threadsafe_function(
self.tsfn.0,
napi::ThreadsafeFunctionReleaseMode::Release,
);

View File

@ -1,6 +1,6 @@
[package]
name = "neon-sys"
version = "0.8.2"
version = "0.7.1"
authors = ["David Herman <david.herman@gmail.com>"]
description = "Exposes the low-level V8/NAN C/C++ APIs. Will be superseded by N-API."
edition = "2018"

View File

@ -205,14 +205,6 @@ mod build {
.output()
.expect("Failed to run \"node-gyp build\" for neon-sys!");
if !build_output.status.success() {
panic!(
"Failed to run \"node-gyp build\" for neon-sys!\n Out: {}\n Err: {}",
String::from_utf8_lossy(&build_output.stdout),
String::from_utf8_lossy(&build_output.stderr)
);
}
let node_gyp_build_output = String::from_utf8_lossy(&build_output.stderr);
println!(
"cargo:node_arch={}",

View File

@ -332,7 +332,7 @@ extern "C" void Neon_Class_SetClassMap(v8::Isolate *isolate, void *map, Neon_Dro
neon::ClassMapHolder *holder = new neon::ClassMapHolder(map, drop_map);
isolate->SetData(NEON_ISOLATE_SLOT, holder);
// ISSUE(#77): When workers land in node, this will need to be generalized to a per-worker version.
node::AtExit(node::GetCurrentEnvironment(isolate->GetCurrentContext()), cleanup_class_map, holder);
node::AtExit(cleanup_class_map, holder);
}
extern "C" void *Neon_Class_GetCallKernel(void *wrapper) {

View File

@ -1,5 +1,5 @@
{
"neon": "0.8",
"neon": "0.7",
"napi": "6",
"cargo-cp-artifact": "0.1"
}

View File

@ -1,6 +1,6 @@
{
"name": "create-neon",
"version": "0.1.3",
"version": "0.1.2",
"description": "Create Neon projects with no build configuration.",
"author": "Dave Herman <david.herman@gmail.com>",
"license": "MIT",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

View File

@ -1,4 +1,4 @@
use crate::borrow::LoanError;
use borrow::LoanError;
use std;
use std::collections::HashSet;
use std::os::raw::c_void;

View File

@ -1,35 +1,4 @@
//! Provides temporary access to JavaScript typed arrays.
//!
//! ## Typed Arrays
//!
//! JavaScript's [typed arrays][typed-arrays] are objects that allow reading and writing
//! raw binary data in memory.
//!
//! Typed arrays are managed with the [`ArrayBuffer`][ArrayBuffer] type, which controls
//! the storage of the underlying data buffer, and several typed views for managing access
//! to the buffer. Neon provides access to the `ArrayBuffer` class with the
//! [`JsArrayBuffer`](crate::types::JsArrayBuffer) type.
//!
//! Node also provides a [`Buffer`][Buffer] type, which is built on top of `ArrayBuffer`
//! and provides additional functionality. Neon provides access to the `Buffer` class
//! with the [`JsBuffer`](crate::types::JsBuffer) type.
//!
//! Many of Node's I/O APIs work with these types, and they can also be used for
//! compact in-memory data structures, which can be shared efficiently between
//! JavaScript and Rust without copying.
//!
//! ## Borrowing
//!
//! Neon makes it possible to [borrow][borrow] temporary access to the internal memory
//! of a typed array by pausing execution of JavaScript with a
//! [`Lock`](crate::context::Lock) and returning a reference to a
//! [`BinaryData`](crate::types::BinaryData) struct. The [`Borrow`](Borrow) and
//! [`BorrowMut`](BorrowMut) traits provide the methods for borrowing this typed array data.
//!
//! [typed-arrays]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays
//! [borrow]: https://doc.rust-lang.org/beta/rust-by-example/scope/borrow.html
//! [ArrayBuffer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
//! [Buffer]: https://nodejs.org/api/buffer.html
//! Types and traits for obtaining temporary access to the internals of JavaScript values.
pub(crate) mod internal;
@ -38,7 +7,7 @@ use std::ops::{Deref, DerefMut, Drop};
use std::os::raw::c_void;
use self::internal::Pointer;
use crate::context::Lock;
use context::Lock;
/// A trait for JS values whose internal contents can be borrowed immutably by Rust while the JS engine is locked.
pub trait Borrow: Sized {

View File

@ -1,15 +1,14 @@
use super::ModuleContext;
use crate::handle::Handle;
#[cfg(feature = "legacy-runtime")]
use crate::object::class::ClassMap;
use crate::result::NeonResult;
use crate::types::{JsObject, JsValue};
use handle::Handle;
use neon_runtime;
use neon_runtime::raw;
use neon_runtime::scope::Root;
#[cfg(feature = "legacy-runtime")]
use neon_runtime::try_catch::TryCatchControl;
#[cfg(feature = "legacy-runtime")]
use object::class::ClassMap;
use result::NeonResult;
#[cfg(feature = "legacy-runtime")]
use std::any::Any;
use std::cell::{Cell, RefCell};
use std::mem::MaybeUninit;
@ -17,6 +16,7 @@ use std::mem::MaybeUninit;
use std::os::raw::c_void;
#[cfg(feature = "legacy-runtime")]
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
use types::{JsObject, JsValue};
#[cfg(feature = "legacy-runtime")]
#[repr(C)]

View File

@ -1,175 +1,24 @@
//! 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`](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 [`CallContext`](CallContext), which is passed
//! to all Neon functions as their initial execution context (or [`FunctionContext`](FunctionContext),
//! a convenient shorthand for `CallContext<JsObject>`):
//!
//! ```
//! # use neon::prelude::*;
//! fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
//! Ok(cx.string("hello Neon"))
//! }
//! ```
//!
//! Another important context type is [`ModuleContext`](ModuleContext), which is provided
//! to a Neon module's [`main`](crate::main) function to enable sharing Neon functions back
//! with JavaScript:
//!
//! ```
//! # #[cfg(feature = "neon-macros")] {
//! # use neon::prelude::*;
//! # fn hello(_: FunctionContext) -> JsResult<JsValue> { todo!() }
//! #[neon::main]
//! fn main(mut cx: ModuleContext) -> NeonResult<()> {
//! cx.export_function("hello", hello)?;
//! Ok(())
//! }
//! # }
//! ```
//!
//! ## Memory Management
//!
//! Because contexts represent the engine at a point in time, they are associated with a
//! [_lifetime_][lifetime], which limits how long Rust code is allowed to access them. This
//! is also used to determine the lifetime of [`Handle`](crate::handle::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`](crate::types::JsNumber):
//!
//! ```
//! # #[cfg(feature = "napi-1")] {
//! # use neon::prelude::*;
//! 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`](Context::execute_scoped) and [`compute_scoped`](Context::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][iterator] from Rust,
//! a Neon function has to work with several temporary handles on each pass through
//! the loop:
//!
//! ```
//! # #[cfg(feature = "napi-1")] {
//! # use neon::prelude::*;
//! # fn iterate(mut cx: FunctionContext) -> JsResult<JsUndefined> {
//! let iterator = cx.argument::<JsObject>(0)?; // iterator object
//! let next = iterator.get(&mut cx, "next")? // iterator's `next` method
//! .downcast_or_throw::<JsFunction, _>(&mut cx)?;
//! let mut numbers = vec![]; // results vector
//! let mut done = false; // loop controller
//!
//! while !done {
//! done = cx.execute_scoped(|mut cx| { // temporary scope
//! let args: Vec<Handle<JsValue>> = vec![];
//! let obj = next.call(&mut cx, iterator, args)? // temporary object
//! .downcast_or_throw::<JsObject, _>(&mut cx)?;
//! let number = obj.get(&mut cx, "value")? // temporary number
//! .downcast_or_throw::<JsNumber, _>(&mut cx)?
//! .value(&mut cx);
//! numbers.push(number);
//! Ok(obj.get(&mut cx, "done")? // temporary boolean
//! .downcast_or_throw::<JsBoolean, _>(&mut cx)?
//! .value(&mut cx))
//! })?;
//! }
//! # Ok(cx.undefined())
//! # }
//! # }
//! ```
//!
//! 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`](std::result::Result::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 (`?`)][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`](Context::try_catch) method, which catches any thrown
//! exception and restores the context to non-throwing state.
//!
//! ## See also
//!
//! 1. Ecma International. [Execution contexts](https://tc39.es/ecma262/#sec-execution-contexts), _ECMAScript Language Specification_.
//! 2. Madhavan Nagarajan. [What is the Execution Context and Stack in JavaScript?](https://medium.com/@itIsMadhavan/what-is-the-execution-context-stack-in-javascript-e169812e851a)
//! 3. Rupesh Mishra. [Execution context, Scope chain and JavaScript internals](https://medium.com/@happymishra66/execution-context-in-javascript-319dd72e8e2c).
//!
//! [lifetime]: https://doc.rust-lang.org/book/ch10-00-generics.html
//! [iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
//! [question-mark]: https://doc.rust-lang.org/edition-guide/rust-2018/error-handling-and-panics/the-question-mark-operator-for-easier-error-handling.html
//! Node _execution contexts_, which manage access to the JavaScript engine at various points in the Node.js runtime lifecycle.
#[cfg(all(feature = "napi-1", feature = "serde"))]
use serde_crate::{Deserialize, Serialize};
pub(crate) mod internal;
use crate::borrow::internal::Ledger;
use crate::borrow::{Borrow, BorrowMut, Ref, RefMut};
use crate::context::internal::Env;
use borrow::internal::Ledger;
use borrow::{Borrow, BorrowMut, Ref, RefMut};
use context::internal::Env;
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
use crate::event::EventQueue;
use crate::handle::{Handle, Managed};
#[cfg(feature = "legacy-runtime")]
use crate::object::class::Class;
use crate::object::{Object, This};
use crate::result::{JsResult, NeonResult, Throw};
use crate::types::binary::{JsArrayBuffer, JsBuffer};
#[cfg(feature = "napi-1")]
use crate::types::boxed::{Finalize, JsBox};
#[cfg(feature = "napi-5")]
use crate::types::date::{DateError, JsDate};
use crate::types::error::JsError;
use crate::types::{
JsArray, JsBoolean, JsFunction, JsNull, JsNumber, JsObject, JsString, JsUndefined, JsValue,
StringResult, Value,
};
use event::EventQueue;
use handle::{Handle, Managed};
use neon_runtime;
use neon_runtime::raw;
#[cfg(feature = "legacy-runtime")]
use object::class::Class;
use object::{Object, This};
#[cfg(all(feature = "napi-1", feature = "serde"))]
use result::SerdeError;
use result::{JsResult, NeonResult, Throw};
#[cfg(feature = "napi-1")]
use smallvec::SmallVec;
use std;
@ -178,6 +27,16 @@ use std::convert::Into;
use std::marker::PhantomData;
use std::os::raw::c_void;
use std::panic::UnwindSafe;
use types::binary::{JsArrayBuffer, JsBuffer};
#[cfg(feature = "napi-1")]
use types::boxed::{Finalize, JsBox};
#[cfg(feature = "napi-5")]
use types::date::{DateError, JsDate};
use types::error::JsError;
use types::{
JsArray, JsBoolean, JsFunction, JsNull, JsNumber, JsObject, JsString, JsUndefined, JsValue,
StringResult, Value,
};
use self::internal::{ContextInternal, Scope, ScopeMetadata};
@ -258,18 +117,16 @@ impl CallbackInfo<'_> {
}
}
/// Indicates whether a function was called with `new`.
/// Indicates whether a function call was called with JavaScript's `[[Call]]` or `[[Construct]]` semantics.
#[derive(Clone, Copy, Debug)]
pub enum CallKind {
Construct,
Call,
}
/// A temporary lock of an execution context.
/// An RAII implementation of a "scoped lock" of the JS engine. When this structure is dropped (falls out of scope), the engine will be unlocked.
///
/// While a lock is alive, no JavaScript code can be executed in the execution context.
///
/// Objects that support the `Borrow` and `BorrowMut` traits can be inspected while the context is locked by passing a reference to a `Lock` to their methods.
/// Types of JS values that support the `Borrow` and `BorrowMut` traits can be inspected while the engine is locked by passing a reference to a `Lock` to their methods.
pub struct Lock<'a> {
pub(crate) ledger: RefCell<Ledger>,
pub(crate) env: Env,
@ -286,9 +143,7 @@ impl<'a> Lock<'a> {
}
}
/// An _execution context_, which represents the current state of a thread of execution in the JavaScript engine.
///
/// All interaction with the JavaScript engine in Neon code is mediated through instances of this trait.
/// An _execution context_, which provides context-sensitive access to the JavaScript engine. Most operations that interact with the engine require passing a reference to a context.
///
/// A context has a lifetime `'a`, which ensures the safety of handles managed by the JS garbage collector. All handles created during the lifetime of a context are kept alive for that duration and cannot outlive the context.
pub trait Context<'a>: ContextInternal<'a> {
@ -554,9 +409,30 @@ pub trait Context<'a>: ContextInternal<'a> {
fn queue(&mut self) -> EventQueue {
EventQueue::new(self)
}
#[cfg(all(feature = "napi-1", feature = "serde"))]
#[allow(clippy::wrong_self_convention)]
/// Attempt to write a struct into a JavaScript value using the serde::ser::Serialize implementation
fn to_js_value<T: ?Sized>(&mut self, v: &T) -> Result<Handle<'a, JsValue>, SerdeError>
where
T: Serialize,
{
let result = unsafe { neon_runtime::serde::to_value(self.env().to_raw(), v) };
result.map(JsValue::new_internal)
}
#[cfg(all(feature = "napi-1", feature = "serde"))]
/// Attempt to read a struct into a JavaScript value using the serde::de::Deserialize implementation
fn from_js_value<T: ?Sized, U: Value>(&mut self, v: Handle<U>) -> Result<T, SerdeError>
where
T: Deserialize<'static>,
{
unsafe { neon_runtime::serde::from_value(self.env().to_raw(), v.to_raw()) }
}
}
/// An execution context of module initialization.
/// A view of the JS engine in the context of top-level initialization of a Neon module.
pub struct ModuleContext<'a> {
scope: Scope<'a, raw::HandleScope>,
exports: Handle<'a, JsObject>,
@ -624,7 +500,7 @@ impl<'a> ContextInternal<'a> for ModuleContext<'a> {
impl<'a> Context<'a> for ModuleContext<'a> {}
/// An execution context of a scope created by [`Context::execute_scoped()`](Context::execute_scoped).
/// A view of the JS engine in the context of a scoped computation started by `Context::execute_scoped()`.
pub struct ExecuteContext<'a> {
scope: Scope<'a, raw::HandleScope>,
}
@ -646,7 +522,7 @@ impl<'a> ContextInternal<'a> for ExecuteContext<'a> {
impl<'a> Context<'a> for ExecuteContext<'a> {}
/// An execution context of a scope created by [`Context::compute_scoped()`](Context::compute_scoped).
/// A view of the JS engine in the context of a scoped computation started by `Context::compute_scoped()`.
pub struct ComputeContext<'a, 'outer> {
scope: Scope<'a, raw::EscapableHandleScope>,
phantom_inner: PhantomData<&'a ()>,
@ -676,7 +552,7 @@ impl<'a, 'b> ContextInternal<'a> for ComputeContext<'a, 'b> {
impl<'a, 'b> Context<'a> for ComputeContext<'a, 'b> {}
/// An execution context of a function call.
/// A view of the JS engine in the context of a function call.
///
/// The type parameter `T` is the type of the `this`-binding.
pub struct CallContext<'a, T: This> {
@ -690,7 +566,7 @@ pub struct CallContext<'a, T: This> {
impl<'a, T: This> UnwindSafe for CallContext<'a, T> {}
impl<'a, T: This> CallContext<'a, T> {
/// Indicates whether the function was called with `new`.
/// Indicates whether the function was called via the JavaScript `[[Call]]` or `[[Construct]]` semantics.
pub fn kind(&self) -> CallKind {
#[cfg(feature = "legacy-runtime")]
let kind = self.info.kind();
@ -777,13 +653,13 @@ impl<'a, T: This> ContextInternal<'a> for CallContext<'a, T> {
impl<'a, T: This> Context<'a> for CallContext<'a, T> {}
/// A shorthand for a [`CallContext`](CallContext) with `this`-type [`JsObject`](crate::types::JsObject).
/// A shorthand for a `CallContext` with `this`-type `JsObject`.
pub type FunctionContext<'a> = CallContext<'a, JsObject>;
/// An alias for [`CallContext`](CallContext), useful for indicating that the function is a method of a class.
/// An alias for `CallContext`, useful for indicating that the function is a method of a class.
pub type MethodContext<'a, T> = CallContext<'a, T>;
/// An execution context of a task completion callback.
/// A view of the JS engine in the context of a task completion callback.
pub struct TaskContext<'a> {
/// We use an "inherited HandleScope" here because the C++ `neon::Task::complete`
/// method sets up and tears down a `HandleScope` for us.

View File

@ -2,13 +2,13 @@
use std::os::raw::c_void;
use crate::context::internal::ContextInternal;
use crate::context::Context;
use crate::handle::{Handle, Managed};
use crate::types::*;
use context::internal::ContextInternal;
use context::Context;
use handle::{Handle, Managed};
use neon_runtime;
use neon_runtime::raw;
use std::sync::Arc;
use types::*;
type EventContext<'a> = crate::context::TaskContext<'a>;

View File

@ -1,12 +1,12 @@
use neon_runtime::raw::Env;
use neon_runtime::tsfn::ThreadsafeFunction;
use crate::context::{Context, TaskContext};
use crate::result::NeonResult;
use context::{Context, TaskContext};
use result::NeonResult;
type Callback = Box<dyn FnOnce(Env) + Send + 'static>;
/// Queue for scheduling Rust closures to execute on the JavaScript main thread.
/// Queue for scheduling Rust closures to execute on tge JavaScript main thread
///
/// # Example
///

View File

@ -1,128 +1,3 @@
//! Exposes the JavaScript event loop for scheduling asynchronous events.
//!
//! ## The Event Loop
//!
//! The [_event loop_][event-loop] is how Node.js provides JavaScript programs
//! access to concurrent events such as completion of [file][fs] or
//! [network][net] operations, notification of scheduled [timers][timer], or
//! receiving of messages from other [processes][process].
//!
//! When an asynchronous operation is started from JavaScript, it registers
//! a JavaScript callback function to wait for the operation to complete. When
//! the operation completes, the callback and the result data are added to an
//! internal _event queue_ in the Node.js runtime so that the event can be
//! processed in order.
//!
//! The event loop processes completed events one at a time in the JavaScript
//! execution thread by calling the registered callback function with its result
//! value as an argument.
//!
//! ## Creating Custom Events
//!
//! This module allows Neon programs to create new types of concurrent events
//! in Rust and expose them to JavaScript as asynchronous functions.
//!
//! A common use for custom events is to run expensive or long-lived
//! computations in a background thread without blocking the JavaScript
//! thread. For example, using the [`psd` crate][psd-crate], a Neon program could
//! asynchronously parse (potentially large) [PSD files][psd-file] in a
//! background thread:
//!
//! ```
//! # #[cfg(feature = "napi-1")] {
//! # use neon::prelude::*;
//! #
//! # fn parse(filename: String, callback: Root<JsFunction>, queue: EventQueue) { }
//! #
//! fn parse_async(mut cx: FunctionContext) -> JsResult<JsUndefined> {
//! // The types `String`, `Root<JsFunction>`, and `EventQueue` can all be
//! // sent across threads.
//! let filename = cx.argument::<JsString>(0)?.value(&mut cx);
//! let callback = cx.argument::<JsFunction>(1)?.root(&mut cx);
//! let queue = cx.queue();
//!
//! // Spawn a thread to complete the execution. This will _not_ block the
//! // JavaScript event loop.
//! std::thread::spawn(move || {
//! // Do the heavy lifting inside the background thread.
//! parse(filename, callback, queue);
//! });
//!
//! Ok(cx.undefined())
//! }
//! # }
//! ```
//!
//! (Note that this usage of [`spawn`](std::thread::spawn) makes use of Rust's
//! [`move`][move] syntax to transfer ownership of data to the background
//! thread.)
//!
//! Upon completion of its task, the background thread can use the JavaScript
//! callback and the event queue to notify the main thread of the result:
//!
//! ```
//! # #[cfg(feature = "napi-1")] {
//! # use neon::prelude::*;
//! # use psd::Psd;
//! # use failure::Error;
//! #
//! fn psd_from_filename(filename: String) -> Result<Psd, Error> {
//! Psd::from_bytes(&std::fs::read(&filename)?)
//! }
//!
//! fn parse(filename: String, callback: Root<JsFunction>, queue: EventQueue) {
//! let result = psd_from_filename(filename);
//!
//! // Send a closure as a task to be executed by the JavaScript event
//! // queue. This _will_ block the event queue while executing.
//! queue.send(move |mut cx| {
//! let callback = callback.into_inner(&mut cx);
//! let this = cx.undefined();
//! let null = cx.null();
//! let args = match result {
//! Ok(psd) => {
//! // Extract data from the parsed file.
//! let width = cx.number(psd.width());
//! let height = cx.number(psd.height());
//!
//! // Save the data in a result object.
//! let obj = cx.empty_object();
//! obj.set(&mut cx, "width", width)?;
//! obj.set(&mut cx, "height", height)?;
//! vec![
//! cx.null().upcast::<JsValue>(),
//! obj.upcast(),
//! ]
//! }
//! Err(err) => {
//! let err = cx.string(err.to_string());
//! vec![
//! err.upcast::<JsValue>(),
//! ]
//! }
//! };
//!
//! callback.call(&mut cx, this, args)?;
//!
//! Ok(())
//! });
//! }
//! # }
//! ```
//!
//! ## See also
//!
//! 1. Panu Pitkamaki. [Event loop from 10,000ft][event-loop].
//!
//! [event-loop]: https://bytearcher.com/articles/event-loop-10-000ft/
//! [fs]: https://nodejs.org/dist/latest/docs/api/fs.html
//! [net]: https://nodejs.org/dist/latest/docs/api/net.html
//! [process]: https://nodejs.org/dist/latest/docs/api/process.html
//! [timer]: https://nodejs.org/dist/latest/docs/api/timers.html
//! [move]: https://doc.rust-lang.org/std/keyword.move.html
//! [psd-crate]: https://crates.io/crates/psd
//! [psd-file]: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
mod event_queue;

View File

@ -1,5 +1,5 @@
use crate::types::Value;
use types::Value;
pub trait SuperType<T: Value> {
fn upcast_internal(v: T) -> Self;
fn upcast_internal(T) -> Self;
}

View File

@ -1,87 +1,33 @@
//! References to garbage-collected JavaScript values.
//!
//! A _handle_ is a safe reference to a JavaScript value that is owned and managed
//! by the JavaScript engine's memory management system (the garbage collector).
//!
//! Neon APIs that accept and return JavaScript values never use raw pointer types
//! ([`*T`](pointer)) or reference types ([`&T`](reference)). Instead they use the
//! special Neon type [`Handle`](Handle), which encapsulates a JavaScript
//! [`Value`](crate::types::Value) and ensures that Rust only maintains access to
//! the value while it is guaranteed to be valid.
//!
//! ## Working with Handles
//!
//! The `Handle<T>` type automatically dereferences to `T` (via the standard
//! [`Deref`](std::ops::Deref) trait), so you can call `T`'s methods on a value of
//! type `Handle<T>`. For example, we can call
//! [`JsNumber::value()`](crate::types::JsNumber::value) on a `Handle<JsNumber>`:
//!
//! ```
//! # #[cfg(feature = "napi-1")] {
//! # use neon::prelude::*;
//! # fn run(mut cx: FunctionContext) -> JsResult<JsUndefined> {
//! let n: Handle<JsNumber> = cx.argument(0)?;
//! let v = n.value(&mut cx); // JsNumber::value()
//! # Ok(cx.undefined())
//! # }
//! # }
//! ```
//!
//! ## Example
//!
//! This Neon function takes an object as its argument, extracts two properties,
//! `width` and `height`, and multiplies them together as numbers. Each JavaScript
//! value in the calculation is stored locally in a `Handle`.
//!
//! ```
//! # #[cfg(feature = "napi-1")] {
//! # use neon::prelude::*;
//! fn area(mut cx: FunctionContext) -> JsResult<JsNumber> {
//! let rect: Handle<JsObject> = cx.argument(0)?;
//!
//! let width: Handle<JsNumber> = rect
//! .get(&mut cx, "width")?
//! .downcast_or_throw(&mut cx)?;
//! let w: f64 = width.value(&mut cx);
//!
//! let height: Handle<JsNumber> = rect
//! .get(&mut cx, "height")?
//! .downcast_or_throw(&mut cx)?;
//! let h: f64 = height.value(&mut cx);
//!
//! Ok(cx.number(w * h))
//! }
//! # }
//! ```
//! Safe _handles_ to managed JavaScript memory.
pub(crate) mod internal;
#[cfg(feature = "napi-1")]
pub(crate) mod root;
mod root;
#[cfg(feature = "napi-1")]
pub use self::root::Root;
use self::internal::SuperType;
use crate::context::internal::Env;
use crate::context::Context;
use crate::result::{JsResult, JsResultExt};
use crate::types::Value;
use context::internal::Env;
use context::Context;
use neon_runtime;
use neon_runtime::raw;
use result::{JsResult, JsResultExt};
use std::error::Error;
use std::fmt::{self, Debug, Display};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use types::Value;
/// The trait of data owned by the JavaScript engine and that can only be accessed via handles.
/// The trait of data that is managed by the JS garbage collector and can only be accessed via handles.
pub trait Managed: Copy {
fn to_raw(self) -> raw::Local;
fn from_raw(env: Env, h: raw::Local) -> Self;
}
/// A handle to a JavaScript value that is owned by the JavaScript engine.
/// A safely rooted _handle_ to a JS value in memory that is managed by the garbage collector.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct Handle<'a, T: Managed + 'a> {
@ -138,7 +84,7 @@ impl<F: Value, T: Value> Display for DowncastError<F, T> {
impl<F: Value, T: Value> Error for DowncastError<F, T> {}
/// The result of a call to [`Handle::downcast()`](Handle::downcast).
/// The result of a call to `Handle::downcast()`.
pub type DowncastResult<'a, F, T> = Result<Handle<'a, T>, DowncastError<F, T>>;
impl<'a, F: Value, T: Value> JsResultExt<'a, T> for DowncastResult<'a, F, T> {

View File

@ -1,40 +1,25 @@
use std::ffi::c_void;
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::sync::Arc;
use neon_runtime::reference;
#[cfg(feature = "napi-6")]
use neon_runtime::tsfn::ThreadsafeFunction;
use crate::context::Context;
use crate::handle::Handle;
#[cfg(feature = "napi-6")]
use crate::lifecycle::InstanceData;
use crate::object::Object;
use crate::types::boxed::Finalize;
use context::Context;
use handle::Handle;
use object::Object;
use types::boxed::Finalize;
#[repr(transparent)]
#[derive(Clone)]
pub(crate) struct NapiRef(*mut c_void);
struct NapiRef(*mut c_void);
// # Safety
// `NapiRef` are reference counted types that allow references to JavaScript objects
// to outlive a `Context` (`napi_env`). Since access is serialized by obtaining a
// `Context`, they are both `Send` and `Sync`.
// https://nodejs.org/api/n-api.html#n_api_references_to_objects_with_a_lifespan_longer_than_that_of_the_native_method
unsafe impl Send for NapiRef {}
unsafe impl Sync for NapiRef {}
/// A thread-safe handle that holds a reference to a JavaScript object and
/// prevents it from being garbage collected.
///
/// A `Root<T>` may be sent across threads, but the referenced object may
/// only be accessed on the JavaScript thread that created it.
/// `Root<T>` holds a reference to a `JavaScript` object and prevents it from
/// being garbage collected. `Root<T>` may be sent across threads, but the
/// referenced objected may only be accessed on the JavaScript thread that
/// created it.
#[repr(transparent)]
pub struct Root<T> {
internal: NapiRef,
#[cfg(feature = "napi-6")]
drop_queue: Arc<ThreadsafeFunction<NapiRef>>,
_phantom: PhantomData<T>,
}
@ -55,19 +40,15 @@ impl<T: Object> Root<T> {
/// garbage collected until the `Root` is dropped. A `Root<T>` may only
/// be dropped on the JavaScript thread that created it.
///
/// The caller _should_ ensure `Root::into_inner` or `Root::drop` is called
/// The caller _must_ ensure `Root::into_inner` or `Root::drop` is called
/// to properly dispose of the `Root<T>`. If the value is dropped without
/// calling one of these methods:
/// * N-API < 6, Neon will `panic` to notify of the leak
/// * N-API >= 6, Neon will drop from a global queue at a runtime cost
/// calling one of these methods, it will *panic*.
pub fn new<'a, C: Context<'a>>(cx: &mut C, value: &T) -> Self {
let env = cx.env().to_raw();
let internal = unsafe { reference::new(env, value.to_raw()) };
Self {
internal: NapiRef(internal as *mut _),
#[cfg(feature = "napi-6")]
drop_queue: InstanceData::drop_queue(cx),
_phantom: PhantomData,
}
}
@ -94,8 +75,6 @@ impl<T: Object> Root<T> {
Self {
internal: self.internal.clone(),
#[cfg(feature = "napi-6")]
drop_queue: Arc::clone(&self.drop_queue),
_phantom: PhantomData,
}
}
@ -145,7 +124,6 @@ impl<T: Object> Finalize for Root<T> {
}
impl<T> Drop for Root<T> {
#[cfg(not(feature = "napi-6"))]
fn drop(&mut self) {
// Destructors are called during stack unwinding, prevent a double
// panic and instead prefer to leak.
@ -162,9 +140,4 @@ impl<T> Drop for Root<T> {
);
}
}
#[cfg(feature = "napi-6")]
fn drop(&mut self) {
let _ = self.drop_queue.call(self.internal.clone(), None);
}
}

View File

@ -1,83 +1,18 @@
//! The [Neon][neon] crate provides bindings for writing [Node.js addons][addons]
//! (i.e., dynamically-loaded binary modules) with a safe and fast Rust API.
//!
//! ## Getting Started
//!
//! You can conveniently bootstrap a new Neon project with the Neon project
//! generator. You don't need to install anything special on your machine as
//! long as you have a [supported version of Node and Rust][supported] on
//! your system.
//!
//! To start a new project, open a terminal in the directory where you would
//! like to place the project, and run at the command prompt:
//!
//! ```text
//! % npm init neon my-project
//! ... answer the user prompts ...
//! ✨ Created Neon project `my-project`. Happy 🦀 hacking! ✨
//! ```
//!
//! where `my-project` can be any name you like for the project. This will
//! run the Neon project generator, prompting you with a few questions and
//! placing a simple but working Neon project in a subdirectory called
//! `my-project` (or whatever name you chose).
//!
//! You can then install and build the project by changing into the project
//! directory and running the standard Node installation command:
//!
//! ```text
//! % cd my-project
//! % npm install
//! % node
//! > require(".").hello()
//! 'hello node'
//! ```
//!
//! You can look in the project's generated `README.md` for more details on
//! the project structure.
//!
//! ## Example
//!
//! The generated `src/lib.rs` contains a function annotated with the
//! [`#[neon::main]`](main) attribute, marking it as the module's main entry
//! point to be executed when the module is loaded. This function can have
//! any name but is conventionally called `main`:
//!
//! ```no_run
//! # #[cfg(feature = "neon-macros")] {
//! # use neon::prelude::*;
//! #
//! # fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
//! # Ok(cx.string("hello node"))
//! # }
//! #
//! #[neon::main]
//! fn main(mut cx: ModuleContext) -> NeonResult<()> {
//! cx.export_function("hello", hello)?;
//! Ok(())
//! }
//! # }
//! ```
//!
//! The example code generated by `npm init neon` exports a single
//! function via [`ModuleContext::export_function`](context::ModuleContext::export_function).
//! The `hello` function is defined just above `main` in `src/lib.rs`:
//!
//! ```
//! # use neon::prelude::*;
//! #
//! fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
//! Ok(cx.string("hello node"))
//! }
//! ```
//!
//! The `hello` function takes a [`FunctionContext`](context::FunctionContext) and
//! returns a JavaScript string. Because all Neon functions can potentially throw a
//! JavaScript exception, the return type is wrapped in a [`JsResult`](result::JsResult).
//!
//! [neon]: https://www.neon-bindings.com/
//! [addons]: https://nodejs.org/api/addons.html
//! [supported]: https://github.com/neon-bindings/neon#platform-support
//! The [Neon](https://www.neon-bindings.com/) crate provides bindings for writing Node.js plugins with a safe and fast Rust API.
extern crate cslice;
extern crate neon_runtime;
extern crate semver;
#[cfg(all(feature = "napi-1", feature = "serde"))]
extern crate serde_crate;
extern crate smallvec;
#[cfg(feature = "proc-macros")]
extern crate neon_macros;
#[cfg(test)]
#[macro_use]
extern crate lazy_static;
pub mod borrow;
pub mod context;
@ -90,7 +25,6 @@ pub mod handle;
pub mod meta;
pub mod object;
pub mod prelude;
#[cfg(feature = "napi-1")]
pub mod reflect;
pub mod result;
#[cfg(feature = "legacy-runtime")]
@ -103,14 +37,27 @@ pub mod macro_internal;
#[cfg(feature = "proc-macros")]
pub use neon_macros::*;
#[cfg(feature = "napi-6")]
mod lifecycle;
#[cfg(all(feature = "legacy-runtime", feature = "napi-1"))]
compile_error!("Cannot enable both `legacy-runtime` and `napi-*` features.\n\nTo use `napi-*`, disable `legacy-runtime` by setting `default-features` to `false` in Cargo.toml\nor with cargo's --no-default-features flag.");
#[cfg(all(feature = "napi-1", not(feature = "legacy-runtime")))]
#[doc(hidden)]
/// Register the current crate as a Node module, providing startup
/// logic for initializing the module object at runtime.
///
/// The first argument is a pattern bound to a `neon::context::ModuleContext`. This
/// is usually bound to a mutable variable `mut cx`, which can then be used to
/// pass to Neon APIs that require mutable access to an execution context.
///
/// Example:
///
/// ```rust,ignore
/// register_module!(mut cx, {
/// cx.export_function("foo", foo)?;
/// cx.export_function("bar", bar)?;
/// cx.export_function("baz", baz)?;
/// Ok(())
/// });
/// ```
#[macro_export]
macro_rules! register_module {
($module:pat, $init:block) => {
@ -151,19 +98,13 @@ macro_rules! register_module {
///
/// Example:
///
/// ```
/// # #[cfg(feature = "n-api")] {
/// # use neon::prelude::*;
/// # fn foo(mut cx: FunctionContext) -> JsResult<JsUndefined> { Ok(cx.undefined()) }
/// # fn bar(mut cx: FunctionContext) -> JsResult<JsUndefined> { Ok(cx.undefined()) }
/// # fn baz(mut cx: FunctionContext) -> JsResult<JsUndefined> { Ok(cx.undefined()) }
/// ```rust,ignore
/// register_module!(mut cx, {
/// cx.export_function("foo", foo)?;
/// cx.export_function("bar", bar)?;
/// cx.export_function("baz", baz)?;
/// Ok(())
/// });
/// # }
/// ```
#[macro_export]
macro_rules! register_module {
@ -354,6 +295,7 @@ macro_rules! impl_managed {
/// Example:
///
/// ```rust
/// # #[macro_use] extern crate neon;
/// # use neon::prelude::*;
/// # fn main() {}
/// pub struct Greeter {
@ -441,7 +383,7 @@ macro_rules! neon_stringify {
#[cfg(test)]
mod tests {
use lazy_static::lazy_static;
extern crate rustversion;
use semver::Version;
use std::path::{Path, PathBuf};
use std::process::Command;
@ -508,7 +450,10 @@ mod tests {
log("static_test");
run("cargo test", &project_root().join("test").join("static"));
run(
"cargo test --release",
&project_root().join("test").join("static"),
);
}
// Only run the static tests in Beta. This will catch changes to error reporting
@ -562,7 +507,7 @@ mod tests {
log("dynamic_cargo_test");
let test_dynamic_cargo = project_root().join("test").join("dynamic").join("native");
run("cargo test", &test_dynamic_cargo);
run("cargo test --release", &test_dynamic_cargo);
}
#[test]

View File

@ -1,76 +0,0 @@
//! # Environment life cycle APIs
//!
//! These APIs map to the life cycle of a specific "Agent" or self-contained
//! environment. If a Neon module is loaded multiple times (Web Workers, worker
//! threads), these API will be handle data associated with a specific instance.
//!
//! See the [N-API Lifecycle][npai-docs] documentation for more details.
//!
//! [napi-docs]: https://nodejs.org/api/n-api.html#n_api_environment_life_cycle_apis
use std::mem;
use std::sync::Arc;
use neon_runtime::raw::Env;
use neon_runtime::reference;
use neon_runtime::tsfn::ThreadsafeFunction;
use crate::context::Context;
use crate::handle::root::NapiRef;
/// `InstanceData` holds Neon data associated with a particular instance of a
/// native module. If a module is loaded multiple times (e.g., worker threads), this
/// data will be unique per instance.
pub(crate) struct InstanceData {
/// Used to free `Root` in the same JavaScript environment that created it
///
/// _Design Note_: An `Arc` ensures the `ThreadsafeFunction` outlives the unloading
/// of a module. Since it is unlikely that modules will be re-loaded frequently, this
/// could be replaced with a leaked `&'static ThreadsafeFunction<NapiRef>`. However,
/// given the cost of FFI, this optimization is omitted until the cost of an
/// `Arc` is demonstrated as significant.
drop_queue: Arc<ThreadsafeFunction<NapiRef>>,
}
fn drop_napi_ref(env: Option<Env>, data: NapiRef) {
if let Some(env) = env {
unsafe {
reference::unreference(env, mem::transmute(data));
}
}
}
impl InstanceData {
/// Return the data associated with this module instance, lazily initializing if
/// necessary.
///
/// # Safety
/// No additional locking (e.g., `Mutex`) is necessary because holding a
/// `Context` reference ensures serialized access.
pub(crate) fn get<'a, C: Context<'a>>(cx: &mut C) -> &'a mut InstanceData {
let env = cx.env().to_raw();
let data =
unsafe { neon_runtime::lifecycle::get_instance_data::<InstanceData>(env).as_mut() };
if let Some(data) = data {
return data;
}
let drop_queue = unsafe {
let mut queue = ThreadsafeFunction::new(env, drop_napi_ref);
queue.unref(env);
queue
};
let data = InstanceData {
drop_queue: Arc::new(drop_queue),
};
unsafe { &mut *neon_runtime::lifecycle::set_instance_data(env, data) }
}
/// Helper to return a reference to the `drop_queue` field of `InstanceData`
pub(crate) fn drop_queue<'a, C: Context<'a>>(cx: &mut C) -> Arc<ThreadsafeFunction<NapiRef>> {
Arc::clone(&InstanceData::get(cx).drop_queue)
}
}

View File

@ -1,10 +1,10 @@
//! Internals needed by macros. These have to be exported for the macros to work
pub use crate::context::internal::{initialize_module, Env};
pub use context::internal::{initialize_module, Env};
/// but are subject to change and should never be explicitly used.
#[cfg(feature = "legacy-runtime")]
// Used by the class macro.
pub use crate::object::class::internal::{
pub use object::class::internal::{
AllocateCallback, ConstructCallback, ConstructorCallCallback, MethodCallback,
};

View File

@ -1,4 +1,4 @@
//! Metadata about the Neon version and build.
//! Utilities exposing metadata about the Neon version and build.
use semver::Version;
@ -11,7 +11,7 @@ pub const MAJOR: &str = env!("CARGO_PKG_VERSION_MAJOR");
/// The Neon minor version.
pub const MINOR: &str = env!("CARGO_PKG_VERSION_MINOR");
/// The Neon patch version.
/// The neon patch version.
pub const PATCH: &str = env!("CARGO_PKG_VERSION_PATCH");
/// Produces a `semver::Version` data structure representing the Neon version.

View File

@ -1,15 +1,15 @@
use super::{Callback, Class, ClassInternal};
use crate::context::internal::{ContextInternal, Env};
use crate::context::{CallContext, CallbackInfo, Context};
use crate::handle::{Handle, Managed};
use crate::result::{JsResult, NeonResult, Throw};
use crate::types::error::convert_panics;
use crate::types::{build, JsFunction, JsObject, JsUndefined, JsValue};
use context::internal::{ContextInternal, Env};
use context::{CallContext, CallbackInfo, Context};
use handle::{Handle, Managed};
use neon_runtime;
use neon_runtime::raw;
use result::{JsResult, NeonResult, Throw};
use std::mem;
use std::os::raw::c_void;
use std::ptr::null_mut;
use types::error::convert_panics;
use types::{build, JsFunction, JsObject, JsUndefined, JsValue};
#[repr(C)]
pub struct MethodCallback<T: Class>(pub fn(CallContext<T>) -> JsResult<JsValue>);

View File

@ -5,21 +5,21 @@ pub(crate) mod internal;
use self::internal::{
AllocateCallback, ClassMetadata, ConstructCallback, ConstructorCallCallback, MethodCallback,
};
use crate::borrow::{Borrow, BorrowMut, LoanError, Ref, RefMut};
use crate::context::internal::Env;
use crate::context::{Context, Lock};
use crate::handle::{Handle, Managed};
use crate::object::{Object, This};
use crate::result::{JsResult, NeonResult, Throw};
use crate::types::internal::{Callback, ValueInternal};
use crate::types::{build, JsFunction, JsValue, Value};
use borrow::{Borrow, BorrowMut, LoanError, Ref, RefMut};
use context::internal::Env;
use context::{Context, Lock};
use handle::{Handle, Managed};
use neon_runtime;
use neon_runtime::raw;
use object::{Object, This};
use result::{JsResult, NeonResult, Throw};
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::mem;
use std::os::raw::c_void;
use std::slice;
use types::internal::{Callback, ValueInternal};
use types::{build, JsFunction, JsValue, Value};
pub(crate) struct ClassMap {
map: HashMap<TypeId, ClassMetadata>,

View File

@ -9,12 +9,12 @@ pub use self::traits::*;
#[cfg(feature = "legacy-runtime")]
mod traits {
use crate::context::Context;
use crate::handle::{Handle, Managed};
use crate::result::{JsResult, NeonResult, Throw};
use crate::types::utf8::Utf8;
use crate::types::{build, JsArray, JsValue, Value};
use context::Context;
use handle::{Handle, Managed};
use neon_runtime::raw;
use result::{JsResult, NeonResult, Throw};
use types::utf8::Utf8;
use types::{build, JsArray, JsValue, Value};
/// A property key in a JavaScript object.
pub trait PropertyKey {
@ -95,18 +95,18 @@ mod traits {
#[cfg(feature = "napi-1")]
mod traits {
use crate::context::internal::Env;
use crate::context::Context;
use crate::handle::{Handle, Managed, Root};
use crate::result::{NeonResult, Throw};
use crate::types::utf8::Utf8;
use crate::types::{build, JsValue, Value};
use context::internal::Env;
use context::Context;
use handle::{Handle, Managed, Root};
use neon_runtime::raw;
use result::{NeonResult, Throw};
use types::utf8::Utf8;
use types::{build, JsValue, Value};
#[cfg(feature = "napi-6")]
use crate::result::JsResult;
use result::JsResult;
#[cfg(feature = "napi-6")]
use crate::types::JsArray;
use types::JsArray;
/// A property key in a JavaScript object.
pub trait PropertyKey {

View File

@ -1,30 +1,30 @@
//! Convenience module for the most common Neon imports.
//! A convenience module that re-exports the most commonly-used Neon APIs.
pub use crate::borrow::{Borrow, BorrowMut};
pub use crate::context::{
CallContext, CallKind, ComputeContext, Context, ExecuteContext, FunctionContext, MethodContext,
ModuleContext, TaskContext,
};
#[cfg(feature = "legacy-runtime")]
pub use crate::declare_types;
#[cfg(all(not(feature = "napi-1"), feature = "event-handler-api"))]
pub use crate::event::EventHandler;
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
pub use crate::event::{EventQueue, EventQueueError};
pub use crate::handle::Handle;
#[cfg(feature = "legacy-runtime")]
pub use crate::object::Class;
pub use crate::object::Object;
pub use crate::register_module;
pub use crate::result::{JsResult, JsResultExt, NeonResult};
#[cfg(feature = "legacy-runtime")]
pub use crate::task::Task;
pub use crate::types::{
BinaryData, JsArray, JsArrayBuffer, JsBoolean, JsBuffer, JsError, JsFunction, JsNull, JsNumber,
JsObject, JsString, JsUndefined, JsValue, Value,
};
#[cfg(feature = "napi-1")]
pub use crate::{
handle::Root,
types::boxed::{Finalize, JsBox},
};
pub use borrow::{Borrow, BorrowMut};
pub use context::{
CallContext, CallKind, ComputeContext, Context, ExecuteContext, FunctionContext, MethodContext,
ModuleContext, TaskContext,
};
#[cfg(all(not(feature = "napi-1"), feature = "event-handler-api"))]
pub use event::EventHandler;
pub use handle::Handle;
#[cfg(feature = "legacy-runtime")]
pub use object::Class;
pub use object::Object;
pub use result::{JsResult, JsResultExt, NeonResult, ResultExt};
#[cfg(feature = "legacy-runtime")]
pub use task::Task;
pub use types::{
BinaryData, JsArray, JsArrayBuffer, JsBoolean, JsBuffer, JsError, JsFunction, JsNull, JsNumber,
JsObject, JsString, JsUndefined, JsValue, Value,
};

View File

@ -1,10 +1,9 @@
//! Exposes JavaScript's reflection API to Rust.
use crate::context::Context;
use crate::handle::{Handle, Managed};
use crate::result::JsResult;
use crate::types::{build, JsString, JsValue};
use context::Context;
use handle::{Handle, Managed};
use result::JsResult;
use types::{build, JsString, JsValue};
#[cfg(feature = "napi-1")]
pub fn eval<'a, 'b, C: Context<'a>>(
cx: &mut C,
script: Handle<'b, JsString>,

View File

@ -1,51 +1,19 @@
//! Represents JavaScript exceptions as a Rust [`Result`](std::result) type.
//!
//! Most interactions with the JavaScript engine can throw a JavaScript exception. Neon APIs
//! that can throw an exception are called _throwing APIs_ and return the type
//! [`NeonResult`](NeonResult) (or its shorthand [`JsResult`](JsResult)).
//!
//! When a throwing API triggers a JavaScript exception, it returns an [Err](std::result::Result::Err)
//! result. This indicates that the thread associated with the [`Context`](crate::context::Context)
//! is now throwing, and allows Rust code to perform any cleanup. See the
//! [`neon::context`](crate::context) module documentation for more about
//! [contexts and exceptions](crate::context#throwing-exceptions).
//!
//! Typically, Neon code can manage JavaScript exceptions correctly and conveniently by
//! using Rust's [question mark (`?`)][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.
//!
//! ## Example
//!
//! Neon functions typically use [`JsResult`](JsResult) for their return type. This
//! example defines a function that extracts a property called `"message"` from an object,
//! throwing an exception if the argument is not of the right type or extracting the property
//! fails:
//!
//! ```
//! # use neon::prelude::*;
//! fn get_message(mut cx: FunctionContext) -> JsResult<JsValue> {
//! let obj: Handle<JsObject> = cx.argument(0)?;
//! let prop: Handle<JsValue> = obj.get(&mut cx, "message")?;
//! Ok(prop)
//! }
//! ```
//!
//! [question-mark]: https://doc.rust-lang.org/edition-guide/rust-2018/error-handling-and-panics/the-question-mark-operator-for-easier-error-handling.html
//! Types and traits for working with JavaScript exceptions.
use crate::context::Context;
use crate::handle::Handle;
use crate::types::Value;
#[cfg(all(feature = "napi-1", feature = "serde"))]
pub use neon_runtime::serde::Error as SerdeError;
use context::Context;
use handle::Handle;
use std::fmt::{Display, Formatter, Result as FmtResult};
use types::Value;
/// A [unit type][unit] indicating that the JavaScript thread is throwing an exception.
/// An error sentinel type used by `NeonResult` (and `JsResult`) to indicate that the JavaScript engine
/// has entered into a throwing state.
///
/// `Throw` deliberately does not implement [`std::error::Error`](std::error::Error). It's
/// not recommended to chain JavaScript exceptions with other kinds of Rust errors,
/// since throwing means that the JavaScript thread is unavailable until the exception
/// is handled.
///
/// [unit]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html#unit-like-structs-without-any-fields
/// `Throw` deliberately does not implement `std::error::Error`, because it's generally not a good idea
/// to chain JavaScript exceptions with other kinds of Rust errors, since entering into the throwing
/// state means that the JavaScript engine is unavailable until the exception is handled.
#[derive(Debug)]
pub struct Throw;
@ -55,14 +23,36 @@ impl Display for Throw {
}
}
/// The result type for throwing APIs.
/// The result of a computation that might send the JS engine into a throwing state.
pub type NeonResult<T> = Result<T, Throw>;
/// Shorthand for a [`NeonResult`](NeonResult) that produces JavaScript values.
/// The result of a computation that produces a JavaScript value and might send the JS engine into a throwing state.
pub type JsResult<'b, T> = NeonResult<Handle<'b, T>>;
/// Extension trait for converting Rust [`Result`](std::result::Result) values
/// into [`JsResult`](JsResult) values by throwing JavaScript exceptions.
/// An extension trait for `Result` values that can be converted into `JsResult` values by throwing a JavaScript
/// exception in the error case.
pub trait JsResultExt<'a, V: Value> {
fn or_throw<'b, C: Context<'b>>(self, cx: &mut C) -> JsResult<'a, V>;
}
/// An extension trait for `Result` values that can be converted into `NeonResult` values by throwing a JavaScript
/// exception in the error case.
pub trait ResultExt<T> {
fn or_throw<'a, C: Context<'a>>(self, cx: &mut C) -> NeonResult<T>;
}
#[cfg(all(feature = "napi-1", feature = "serde"))]
impl<T> ResultExt<T> for Result<T, SerdeError> {
fn or_throw<'a, C: Context<'a>>(self, cx: &mut C) -> NeonResult<T> {
let err = match self {
Ok(v) => return Ok(v),
Err(err) => err,
};
if err.is_exception_pending() {
Err(Throw)
} else {
cx.throw_error(err.to_string())
}
}
}

View File

@ -3,12 +3,12 @@
use std::marker::{Send, Sized};
use std::os::raw::c_void;
use crate::context::TaskContext;
use crate::handle::{Handle, Managed};
use crate::result::JsResult;
use crate::types::{JsFunction, Value};
use context::TaskContext;
use handle::{Handle, Managed};
use neon_runtime;
use neon_runtime::raw;
use result::JsResult;
use types::{JsFunction, Value};
/// A Rust task that can be executed in the background on the Node thread pool.
pub trait Task: Send + Sized + 'static {

View File

@ -1,21 +1,21 @@
//! Types and traits representing binary JavaScript data.
use crate::borrow::internal::Pointer;
use crate::borrow::{Borrow, BorrowMut, LoanError, Ref, RefMut};
use crate::context::internal::Env;
use crate::context::{Context, Lock};
use borrow::internal::Pointer;
use borrow::{Borrow, BorrowMut, LoanError, Ref, RefMut};
use context::internal::Env;
use context::{Context, Lock};
#[cfg(feature = "napi-1")]
use crate::handle::Handle;
use crate::handle::Managed;
use crate::result::JsResult;
use crate::types::internal::ValueInternal;
use crate::types::{build, Object, Value};
use handle::Handle;
use handle::Managed;
use neon_runtime;
use neon_runtime::raw;
use result::JsResult;
use std::marker::PhantomData;
use std::mem::{self, MaybeUninit};
use std::os::raw::c_void;
use std::slice;
use types::internal::ValueInternal;
use types::{build, Object, Value};
/// The Node [`Buffer`](https://nodejs.org/api/buffer.html) type.
#[repr(C)]

View File

@ -7,7 +7,6 @@ use neon_runtime::raw;
use crate::context::internal::Env;
use crate::context::{Context, FinalizeContext};
use crate::handle::{Handle, Managed};
use crate::object::Object;
use crate::types::internal::ValueInternal;
use crate::types::Value;
@ -160,8 +159,6 @@ impl<T: Send + 'static> Clone for JsBox<T> {
}
}
impl<T: Send + 'static> Object for JsBox<T> {}
impl<T: Send + 'static> Copy for JsBox<T> {}
impl<T: Send + 'static> Value for JsBox<T> {}

View File

@ -1,11 +1,11 @@
use super::{Value, ValueInternal};
use crate::context::internal::Env;
use crate::context::Context;
use crate::handle::{Handle, Managed};
use crate::object::Object;
use crate::result::{JsResult, JsResultExt};
use context::internal::Env;
use context::Context;
use handle::{Handle, Managed};
use neon_runtime;
use neon_runtime::raw;
use object::Object;
use result::{JsResult, JsResultExt};
use std::error::Error;
use std::fmt;
use std::fmt::Debug;

View File

@ -5,12 +5,12 @@ use std::panic::{catch_unwind, UnwindSafe};
use neon_runtime;
use neon_runtime::raw;
use crate::context::internal::Env;
use crate::context::Context;
use crate::result::{NeonResult, Throw};
use crate::types::internal::ValueInternal;
use crate::types::utf8::Utf8;
use crate::types::{build, Handle, Managed, Object, Value};
use context::internal::Env;
use context::Context;
use result::{NeonResult, Throw};
use types::internal::ValueInternal;
use types::utf8::Utf8;
use types::{build, Handle, Managed, Object, Value};
/// A JS `Error` object.
#[repr(C)]

View File

@ -1,14 +1,14 @@
use super::Value;
use crate::context::internal::Env;
use crate::context::{CallbackInfo, FunctionContext};
use crate::result::JsResult;
use crate::types::error::convert_panics;
use crate::types::{Handle, JsObject, Managed};
use context::internal::Env;
use context::{CallbackInfo, FunctionContext};
use neon_runtime;
use neon_runtime::call::CCallback;
use neon_runtime::raw;
use result::JsResult;
use std::mem;
use std::os::raw::c_void;
use types::error::convert_panics;
use types::{Handle, JsObject, Managed};
pub trait ValueInternal: Managed + 'static {
fn name() -> String;

View File

@ -1,76 +1,4 @@
//! Representations of JavaScript's core builtin types.
//!
//! ## Modeling JavaScript Types
//!
//! All JavaScript values in Neon implement the abstract [`Value`] trait, which
//! is the most generic way to work with JavaScript values. Neon provides a
//! number of types that implement this trait, each representing a particular
//! type of JavaScript value.
//!
//! By convention, JavaScript types in Neon have the prefix `Js` in their name,
//! such as [`JsNumber`](crate::types::JsNumber) (for the JavaScript `number`
//! type) or [`JsFunction`](crate::types::JsFunction) (for the JavaScript
//! `function` type).
//!
//! ### Handles and Casts
//!
//! Access to JavaScript values in Neon works through [handles](crate::handle),
//! which ensure the safe interoperation between Rust and the JavaScript garbage
//! collector. This means, for example, a Rust variable that stores a JavaScript string
//! will have the type `Handle<JsString>` rather than [`JsString`](crate::types::JsString).
//!
//! Neon types model the JavaScript type hierarchy through the use of *casts*.
//! The [`Handle::upcast()`](crate::handle::Handle::upcast) method safely converts
//! a handle to a JavaScript value of one type into a handle to a value of its
//! supertype. For example, it's safe to treat a [`JsArray`](crate::types::JsArray)
//! as a [`JsObject`](crate::types::JsObject), so you can do an "upcast" and it will
//! never fail:
//!
//! ```
//! # use neon::prelude::*;
//! fn as_object(array: Handle<JsArray>) -> Handle<JsObject> {
//! let object: Handle<JsObject> = array.upcast();
//! object
//! }
//! ```
//!
//! Unlike upcasts, the [`Handle::downcast()`](crate::handle::Handle::downcast) method
//! requires a runtime check to test a value's type at runtime, so it can fail with
//! a [`DowncastError`](crate::handle::DowncastError):
//!
//! ```
//! # use neon::prelude::*;
//! fn as_array<'a>(
//! cx: &mut impl Context<'a>,
//! object: Handle<'a, JsObject>
//! ) -> JsResult<'a, JsArray> {
//! object.downcast(cx).or_throw(cx)
//! }
//! ```
//!
//! ### The JavaScript Type Hierarchy
//!
//! ![The Neon type hierarchy, described in detail below.][types]
//!
//! The JavaScript type hierarchy includes:
//!
//! - [`JsValue`](JsValue): This is the top of the type hierarchy, and can refer to
//! any JavaScript value. (For TypeScript programmers, this can be thought of as
//! similar to TypeScript's [`unknown`][unknown] type.)
//! - [`JsObject`](JsObject): This is the top of the object type hierarchy. Object
//! types all implement the [`Object`](crate::object::Object) trait, which allows
//! getting and setting properties.
//! - **Standard object types:** [`JsFunction`](JsFunction), [`JsArray`](JsArray),
//! [`JsDate`](JsDate), and [`JsError`](JsError).
//! - **Typed arrays:** [`JsBuffer`](JsBuffer) and [`JsArrayBuffer`](JsArrayBuffer).
//! - **Custom types:** [`JsBox`](JsBox), a special Neon type that allows the creation
//! of custom objects that own Rust data structures.
//! - **Primitive types:** These are the built-in JavaScript datatypes that are not
//! object types: [`JsNumber`](JsNumber), [`JsBoolean`](JsBoolean),
//! [`JsString`](JsString), [`JsNull`](JsNull), and [`JsUndefined`](JsUndefined).
//!
//! [types]: https://raw.githubusercontent.com/neon-bindings/neon/main/doc/types.jpg
//! [unknown]: https://mariusschulz.com/blog/the-unknown-type-in-typescript#the-unknown-type
pub(crate) mod binary;
#[cfg(feature = "napi-1")]
@ -84,20 +12,20 @@ pub(crate) mod utf8;
use self::internal::{FunctionCallback, ValueInternal};
use self::utf8::Utf8;
use crate::context::internal::Env;
use crate::context::{Context, FunctionContext};
use crate::handle::internal::SuperType;
use crate::handle::{Handle, Managed};
use crate::object::{Object, This};
use crate::result::{JsResult, JsResultExt, NeonResult, Throw};
use crate::types::internal::Callback;
use context::internal::Env;
use context::{Context, FunctionContext};
use handle::internal::SuperType;
use handle::{Handle, Managed};
use neon_runtime;
use neon_runtime::raw;
use object::{Object, This};
use result::{JsResult, JsResultExt, NeonResult, Throw};
use smallvec::SmallVec;
use std::fmt;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::os::raw::c_void;
use types::internal::Callback;
pub use self::binary::{BinaryData, BinaryViewType, JsArrayBuffer, JsBuffer};
#[cfg(feature = "napi-1")]

View File

@ -687,15 +687,14 @@
"dev": true
},
"handlebars": {
"version": "4.7.7",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
"integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
"version": "4.5.3",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz",
"integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==",
"requires": {
"minimist": "^1.2.5",
"neo-async": "^2.6.0",
"optimist": "^0.6.1",
"source-map": "^0.6.1",
"uglify-js": "^3.1.4",
"wordwrap": "^1.0.0"
"uglify-js": "^3.1.4"
}
},
"has-flag": {
@ -838,9 +837,9 @@
}
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
},
"lodash.padend": {
"version": "4.6.1",
@ -927,9 +926,9 @@
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
},
"mkdirp": {
"version": "0.5.1",
@ -1072,6 +1071,15 @@
"mimic-fn": "^1.0.0"
}
},
"optimist": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
"integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
"requires": {
"minimist": "~0.0.1",
"wordwrap": "~0.0.2"
}
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
@ -1489,9 +1497,9 @@
"integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0="
},
"uglify-js": {
"version": "3.13.5",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.5.tgz",
"integrity": "sha512-xtB8yEqIkn7zmOyS2zUNBsYCBRhDkvlNxMMY2smuJ/qA8NCHeQvKCF3i9Z4k8FJH4+PJvZRtMrPynfZ75+CSZw==",
"version": "3.10.3",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.3.tgz",
"integrity": "sha512-Lh00i69Uf6G74mvYpHCI9KVVXLcHW/xu79YTvH7Mkc9zyKUeSPz0owW0dguj0Scavns3ZOh3wY63J0Zb97Za2g==",
"optional": true
},
"v8flags": {
@ -1545,9 +1553,9 @@
}
},
"wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
"integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
},
"wordwrapjs": {
"version": "3.0.0",

View File

@ -14,7 +14,7 @@
"command-line-commands": "^2.0.0",
"command-line-usage": "^4.0.0",
"git-config": "0.0.7",
"handlebars": "^4.7.7",
"handlebars": "^4.5.3",
"inquirer": "^3.0.6",
"mkdirp": "^0.5.1",
"quickly-copy-file": "^1.0.0",

View File

@ -6,7 +6,7 @@
"author": "The Neon Community",
"license": "MIT",
"scripts": {
"install": "node ../../cli/bin/cli.js build",
"install": "node ../../cli/bin/cli.js build --release",
"test": "mocha --timeout 5000 --recursive lib"
},
"devDependencies": {

View File

@ -810,9 +810,9 @@
"dev": true
},
"electron": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/electron/-/electron-11.1.0.tgz",
"integrity": "sha512-RFAhR/852VMaRd9NSe7jprwSoG9dLc6u1GwnqRWg+/3cy/8Zrwt1Betw1lXiZH7hGuB9K2cqju83Xv5Pq5ZSGA==",
"version": "11.0.4",
"resolved": "https://registry.npmjs.org/electron/-/electron-11.0.4.tgz",
"integrity": "sha512-ipfQ28Km52iuDSe9VK0G5uuyxi8qy8szg+01kQTRXZFCLlpgsgU+vQxWkld2tkhXWdu+H3dmgYHvWtoijOvhjw==",
"dev": true,
"requires": {
"@electron/get": "^1.0.1",
@ -1342,9 +1342,9 @@
}
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true,
"optional": true
},
@ -2085,9 +2085,9 @@
"dev": true
},
"ua-parser-js": {
"version": "0.7.28",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz",
"integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==",
"version": "0.7.22",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.22.tgz",
"integrity": "sha512-YUxzMjJ5T71w6a8WWVcMGM6YWOTX27rCoIQgLXiWaxqXSx9D7DNjiGWn1aJIRSQ5qr0xuhra77bSIh6voR/46Q==",
"dev": true
},
"unbzip2-stream": {

View File

@ -13,7 +13,7 @@
"repository": "https://github.com/electron/electron-quick-start",
"devDependencies": {
"cargo-cp-artifact": "^0.1.0 ",
"electron": "^11.1.0",
"electron": "^11.0.3",
"spectron": "^13.0.0"
}
}

View File

@ -9,8 +9,12 @@ edition = "2018"
[lib]
crate-type = ["cdylib"]
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
[dependencies.neon]
version = "*"
path = "../.."
default-features = false
features = ["default-panic-hook", "napi-6", "try-catch-api", "event-queue-api"]
features = ["default-panic-hook", "napi-6", "try-catch-api", "event-queue-api", "serde"]

File diff suppressed because it is too large Load Diff

21
test/napi/lib/serde.js Normal file
View File

@ -0,0 +1,21 @@
"use strict";
const assert = require('assert');
const addon = require('..');
const pokedex = require('../fixtures/pokedex.json');
describe('serde', () => {
it('should be able to parse a pokedex from a string', () => {
const s = JSON.stringify(pokedex);
const result = addon.parse_pokedex(s);
assert.deepStrictEqual(result, pokedex);
});
it('should be able to stringify a pokedex', () => {
const result = addon.stringify_pokedex(pokedex);
assert.deepStrictEqual(JSON.parse(result), pokedex);
});
});

View File

@ -64,11 +64,4 @@ const assert = require('chai').assert;
// If the EventQueue is not unreferenced, the test runner will not cleanly exit
addon.leak_event_queue();
});
it('should drop leaked Root from the global queue', function (cb) {
addon.drop_global_queue(cb);
// Asynchronously GC to give the task queue a chance to execute
setTimeout(() => global.gc(), 10);
});
});

100
test/napi/src/js/serde.rs Normal file
View File

@ -0,0 +1,100 @@
// Pokedex example from https://app.quicktype.io/
use neon::prelude::*;
use serde::{Deserialize, Serialize};
/// A collection of pokémon
#[derive(Serialize, Deserialize)]
pub struct PokedexSchema {
/// All pokémon contained in the pokédex
pokemon: Vec<Pokemon>,
}
/// A 'pocket monster.' One must catch them all.
#[derive(Serialize, Deserialize)]
pub struct Pokemon {
avg_spawns: f64,
/// The flavor of candy preferred by this pokémon
candy: String,
candy_count: Option<i64>,
egg: Egg,
height: String,
/// A unique identifier for this pokémon.
/// Higher ids generally imply rarer and more evolved pokémon.
id: i64,
/// Photographic evidence of this pokémon's existence
img: String,
multipliers: Option<Vec<f64>>,
name: String,
next_evolution: Option<Vec<Evolution>>,
num: String,
prev_evolution: Option<Vec<Evolution>>,
spawn_chance: f64,
spawn_time: String,
#[serde(rename = "type")]
pokemon_type: Vec<Type>,
/// Types of pokémon that cause extra damage to this pokémon
weaknesses: Vec<Type>,
weight: String,
}
/// A description of an evolutionary stage of a pokémon
#[derive(Serialize, Deserialize)]
pub struct Evolution {
/// The name of the Pokémon to or from which the containing Pokémon evolves
name: String,
/// The number of the pokémon to or from which the containing pokémon evolves
num: String,
}
#[derive(Serialize, Deserialize)]
pub enum Egg {
#[serde(rename = "Not in Eggs")]
NotInEggs,
#[serde(rename = "Omanyte Candy")]
OmanyteCandy,
#[serde(rename = "10 km")]
The10Km,
#[serde(rename = "2 km")]
The2Km,
#[serde(rename = "5 km")]
The5Km,
}
#[derive(Serialize, Deserialize)]
pub enum Type {
Bug,
Dark,
Dragon,
Electric,
Fairy,
Fighting,
Fire,
Flying,
Ghost,
Grass,
Ground,
Ice,
Normal,
Poison,
Psychic,
Rock,
Steel,
Water,
}
pub fn parse_pokedex(mut cx: FunctionContext) -> JsResult<JsValue> {
let pokedex = cx.argument::<JsString>(0)?.value(&mut cx);
let pokedex: PokedexSchema =
serde_json::from_str(&pokedex).or_else(|err| cx.throw_error(err.to_string()))?;
cx.to_js_value(&pokedex).or_throw(&mut cx)
}
pub fn stringify_pokedex(mut cx: FunctionContext) -> JsResult<JsString> {
let pokedex = cx.argument::<JsObject>(0)?;
let pokedex: PokedexSchema = cx.from_js_value(pokedex).or_throw(&mut cx)?;
let s = serde_json::to_string(&pokedex).or_else(|err| cx.throw_error(err.to_string()))?;
Ok(cx.string(s))
}

View File

@ -144,44 +144,3 @@ pub fn leak_event_queue(mut cx: FunctionContext) -> JsResult<JsUndefined> {
Ok(cx.undefined())
}
pub fn drop_global_queue(mut cx: FunctionContext) -> JsResult<JsUndefined> {
struct Wrapper {
callback: Option<Root<JsFunction>>,
queue: EventQueue,
}
impl Finalize for Wrapper {}
// To verify that the type is dropped on the global drop queue, the callback
// is called from the `Drop` impl on `Wrapper`
impl Drop for Wrapper {
fn drop(&mut self) {
if let Some(callback) = self.callback.take() {
self.queue.send(|mut cx| {
let callback = callback.into_inner(&mut cx);
let this = cx.undefined();
let args = vec![cx.undefined()];
callback.call(&mut cx, this, args)?;
Ok(())
});
}
}
}
let callback = cx.argument::<JsFunction>(0)?.root(&mut cx);
let queue = cx.queue();
let wrapper = cx.boxed(Wrapper {
callback: Some(callback),
queue,
});
// Put the `Wrapper` instance in a `Root` and drop it
// Without the global drop queue, this will panic
let _ = wrapper.root(&mut cx);
Ok(cx.undefined())
}

View File

@ -9,6 +9,7 @@ mod js {
pub mod functions;
pub mod numbers;
pub mod objects;
pub mod serde;
pub mod strings;
pub mod threads;
pub mod types;
@ -22,6 +23,7 @@ use js::errors::*;
use js::functions::*;
use js::numbers::*;
use js::objects::*;
use js::serde::*;
use js::strings::*;
use js::threads::*;
use js::types::*;
@ -256,7 +258,9 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("greeter_new", greeter_new)?;
cx.export_function("greeter_greet", greeter_greet)?;
cx.export_function("leak_event_queue", leak_event_queue)?;
cx.export_function("drop_global_queue", drop_global_queue)?;
cx.export_function("parse_pokedex", parse_pokedex)?;
cx.export_function("stringify_pokedex", stringify_pokedex)?;
Ok(())
}