Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ad2e5ff06 | ||
|
|
c105c159e8 |
@ -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"
|
||||
|
||||
4
.github/workflows/linux.yml
vendored
4
.github/workflows/linux.yml
vendored
@ -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
|
||||
|
||||
4
.github/workflows/macos.yml
vendored
4
.github/workflows/macos.yml
vendored
@ -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
|
||||
|
||||
6
.github/workflows/windows.yml
vendored
6
.github/workflows/windows.yml
vendored
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
17
Cargo.toml
17
Cargo.toml
@ -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"]
|
||||
|
||||
|
||||
@ -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"]
|
||||
```
|
||||
|
||||
11
README.md
11
README.md
@ -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
|
||||
|
||||
|
||||
34
RELEASES.md
34
RELEASES.md
@ -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
26
cli/package-lock.json
generated
@ -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": {
|
||||
|
||||
@ -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": {
|
||||
|
||||
@ -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 || []);
|
||||
|
||||
@ -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 }
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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)?;
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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)?;
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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());
|
||||
}
|
||||
@ -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::*;
|
||||
|
||||
@ -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")]
|
||||
|
||||
@ -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
|
||||
|
||||
484
crates/neon-runtime/src/napi/serde/de.rs
Normal file
484
crates/neon-runtime/src/napi/serde/de.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
149
crates/neon-runtime/src/napi/serde/error.rs
Normal file
149
crates/neon-runtime/src/napi/serde/error.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
282
crates/neon-runtime/src/napi/serde/js.rs
Normal file
282
crates/neon-runtime/src/napi/serde/js.rs
Normal 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(())
|
||||
}
|
||||
35
crates/neon-runtime/src/napi/serde/mod.rs
Normal file
35
crates/neon-runtime/src/napi/serde/mod.rs
Normal 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))
|
||||
}
|
||||
432
crates/neon-runtime/src/napi/serde/se.rs
Normal file
432
crates/neon-runtime/src/napi/serde/se.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
);
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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={}",
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"neon": "0.8",
|
||||
"neon": "0.7",
|
||||
"napi": "6",
|
||||
"cargo-cp-artifact": "0.1"
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
BIN
doc/types.jpg
BIN
doc/types.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 29 KiB |
BIN
doc/types.pptx
BIN
doc/types.pptx
Binary file not shown.
@ -1,4 +1,4 @@
|
||||
use crate::borrow::LoanError;
|
||||
use borrow::LoanError;
|
||||
use std;
|
||||
use std::collections::HashSet;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)]
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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>;
|
||||
|
||||
|
||||
@ -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
|
||||
///
|
||||
|
||||
125
src/event/mod.rs
125
src/event/mod.rs
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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> {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
135
src/lib.rs
135
src/lib.rs
@ -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]
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
};
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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>);
|
||||
|
||||
@ -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>,
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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>,
|
||||
|
||||
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)]
|
||||
|
||||
@ -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> {}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)]
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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")]
|
||||
|
||||
44
test/cli/package-lock.json
generated
44
test/cli/package-lock.json
generated
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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": {
|
||||
|
||||
18
test/electron/package-lock.json
generated
18
test/electron/package-lock.json
generated
@ -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": {
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"]
|
||||
|
||||
4863
test/napi/fixtures/pokedex.json
Normal file
4863
test/napi/fixtures/pokedex.json
Normal file
File diff suppressed because it is too large
Load Diff
21
test/napi/lib/serde.js
Normal file
21
test/napi/lib/serde.js
Normal 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);
|
||||
});
|
||||
});
|
||||
@ -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
100
test/napi/src/js/serde.rs
Normal 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))
|
||||
}
|
||||
@ -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())
|
||||
}
|
||||
|
||||
@ -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(())
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user