Compare commits

..

75 Commits

Author SHA1 Message Date
K.J. Valencik
f3a96aaa7d
Merge pull request #676 from neon-bindings/dependabot/npm_and_yarn/test/electron/electron-11.1.0
Some checks failed
Lints / lint (14.x, nightly) (push) Has been cancelled
Test on Linux / build (12.x, beta) (push) Has been cancelled
Test on Linux / build (12.x, nightly) (push) Has been cancelled
Test on Linux / build (12.x, stable) (push) Has been cancelled
Test on Linux / build (14.x, beta) (push) Has been cancelled
Test on Linux / build (14.x, nightly) (push) Has been cancelled
Test on Linux / build (14.x, stable) (push) Has been cancelled
Test on Linux / build (16.x, beta) (push) Has been cancelled
Test on Linux / build (16.x, nightly) (push) Has been cancelled
Test on Linux / build (16.x, stable) (push) Has been cancelled
Test on MacOS / build (12.x, beta) (push) Has been cancelled
Test on MacOS / build (12.x, nightly) (push) Has been cancelled
Test on MacOS / build (12.x, stable) (push) Has been cancelled
Test on MacOS / build (14.x, beta) (push) Has been cancelled
Test on MacOS / build (14.x, nightly) (push) Has been cancelled
Test on MacOS / build (14.x, stable) (push) Has been cancelled
Test on MacOS / build (16.x, beta) (push) Has been cancelled
Test on MacOS / build (16.x, nightly) (push) Has been cancelled
Test on MacOS / build (16.x, stable) (push) Has been cancelled
Test on Windows / build (12.x, beta) (push) Has been cancelled
Test on Windows / build (12.x, nightly) (push) Has been cancelled
Test on Windows / build (12.x, stable) (push) Has been cancelled
Test on Windows / build (14.x, beta) (push) Has been cancelled
Test on Windows / build (14.x, nightly) (push) Has been cancelled
Test on Windows / build (14.x, stable) (push) Has been cancelled
Test on Windows / build (16.x, beta) (push) Has been cancelled
Test on Windows / build (16.x, nightly) (push) Has been cancelled
Test on Windows / build (16.x, stable) (push) Has been cancelled
build(deps-dev): bump electron from 11.0.4 to 11.1.0 in /test/electron
2021-05-18 11:09:28 -04:00
K.J. Valencik
e0c25bfd75
Merge pull request #726 from neon-bindings/dependabot/npm_and_yarn/test/cli/lodash-4.17.21
Bump lodash from 4.17.19 to 4.17.21 in /test/cli
2021-05-18 11:09:16 -04:00
K.J. Valencik
c81d98f195
Merge pull request #731 from neon-bindings/dependabot/npm_and_yarn/test/electron/lodash-4.17.21
Bump lodash from 4.17.20 to 4.17.21 in /test/electron
2021-05-18 11:08:59 -04:00
K.J. Valencik
d401a32b78
Merge pull request #730 from neon-bindings/dependabot/npm_and_yarn/test/electron/ua-parser-js-0.7.28
Bump ua-parser-js from 0.7.22 to 0.7.28 in /test/electron
2021-05-18 11:08:56 -04:00
K.J. Valencik
d69768f567
Merge pull request #725 from neon-bindings/dependabot/npm_and_yarn/cli/lodash-4.17.21
Bump lodash from 4.17.19 to 4.17.21 in /cli
2021-05-18 11:08:49 -04:00
K.J. Valencik
2f93620146
Merge pull request #724 from neon-bindings/dependabot/npm_and_yarn/test/cli/handlebars-4.7.7
Bump handlebars from 4.5.3 to 4.7.7 in /test/cli
2021-05-18 11:08:44 -04:00
K.J. Valencik
8814886b41
Merge pull request #723 from neon-bindings/dependabot/npm_and_yarn/cli/handlebars-4.7.7
Bump handlebars from 4.7.6 to 4.7.7 in /cli
2021-05-18 11:08:37 -04:00
K.J. Valencik
e2cc8cc81d
Merge pull request #737 from neon-bindings/kv/v0.8.2
v0.8.2
2021-05-18 10:59:15 -04:00
K.J. Valencik
5c308aec22
v0.8.2 2021-05-18 10:48:44 -04:00
K.J. Valencik
d9eac77765
Update migration guide to include removing build.rs 2021-05-18 10:33:04 -04:00
K.J. Valencik
73bde85776
Merge pull request #736 from neon-bindings/types-apidocs
Top-level API docs for `neon::types`
2021-05-18 10:30:59 -04:00
David Herman
8758b7f416 API docs for the neon::types module top level. 2021-05-17 18:47:46 -07:00
Dave Herman
73390414ee
Merge pull request #735 from neon-bindings/types-diagram
Types diagram for docs
2021-05-17 07:47:51 -07:00
David Herman
fc5a214389 Types diagram, for use in the docs 2021-05-16 14:49:41 -07:00
Dave Herman
c5c277e98b
Merge pull request #733 from neon-bindings/event-api-docs
Top level API docs for `neon::event`
2021-05-15 12:37:43 -07:00
K.J. Valencik
264a6a4ce0 chore(cli): Suggest using create-neon instead
Resolves https://github.com/neon-bindings/neon/issues/696
2021-05-15 09:54:39 -07:00
David Herman
0df7033978 Use error message from PSD error on failure. 2021-05-14 17:39:35 -07:00
David Herman
3f6804c79f cargo fmt 2021-05-14 17:38:20 -07:00
David Herman
1e8d6261a4 Upgrade to Rust 2018 Edition 2021-05-14 11:25:31 -07:00
David Herman
56834b5afc Add initial section title, and some rewording of opening sentence 2021-05-14 02:11:32 -07:00
David Herman
b64f9203ab Delete trailing spaces 2021-05-14 02:06:21 -07:00
David Herman
5b20370bee Top level API docs for neon::event 2021-05-13 20:41:29 -07:00
dependabot[bot]
acd5ba6e2a
Bump lodash from 4.17.20 to 4.17.21 in /test/electron
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-10 14:21:09 +00:00
dependabot[bot]
d473ead705
Bump ua-parser-js from 0.7.22 to 0.7.28 in /test/electron
Bumps [ua-parser-js](https://github.com/faisalman/ua-parser-js) from 0.7.22 to 0.7.28.
- [Release notes](https://github.com/faisalman/ua-parser-js/releases)
- [Commits](https://github.com/faisalman/ua-parser-js/compare/0.7.22...0.7.28)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-08 04:39:35 +00:00
dependabot[bot]
01fbf284a4
Bump lodash from 4.17.19 to 4.17.21 in /test/cli
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.19 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.19...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-06 18:46:00 +00:00
dependabot[bot]
ec3e057ec6
Bump lodash from 4.17.19 to 4.17.21 in /cli
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.19 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.19...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-06 18:45:47 +00:00
dependabot[bot]
5fe99a8941
Bump handlebars from 4.5.3 to 4.7.7 in /test/cli
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.5.3 to 4.7.7.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.5.3...v4.7.7)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-06 16:43:52 +00:00
dependabot[bot]
0ab3b6ee2e
Bump handlebars from 4.7.6 to 4.7.7 in /cli
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.7.6 to 4.7.7.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.7.6...v4.7.7)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-06 16:43:40 +00:00
K.J. Valencik
2f2bfca92a
Merge pull request #722 from neon-bindings/kv/suggest-napi
chore(cli): Suggest using `create-neon` instead
2021-05-05 12:04:42 -04:00
K.J. Valencik
378dcc8e8a
chore(cli): Suggest using create-neon instead
Resolves https://github.com/neon-bindings/neon/issues/696
2021-05-05 10:42:53 -04:00
K.J. Valencik
165dffdf4a
Merge pull request #721 from neon-bindings/nom-init-snippet
docs nit: fix CLI example in main `neon` module
2021-05-05 10:24:56 -04:00
Dave Herman
77f2ac06de
Fix CLI example in top level API docs
`neon init neon` -> `npm init neon` in main lib API docs
2021-05-01 09:31:46 -07:00
K.J. Valencik
14bc6072ec
Merge pull request #720 from neon-bindings/kv/v0.8.1
v0.8.1
2021-04-30 13:22:19 -04:00
K.J. Valencik
a20636e548
v0.8.1 2021-04-30 13:05:02 -04:00
K.J. Valencik
0e259239d3
Merge pull request #719 from neon-bindings/kv/doc-tests
chore(neon): Run all of the doc tests and run tests in debug mode
2021-04-30 12:56:58 -04:00
K.J. Valencik
fbd09f0bff
chore(neon): Drop support for Node 10 which is end of life 2021-04-30 12:43:07 -04:00
K.J. Valencik
d7ac291d97
chore(neon): Run all of the doc tests and run tests in debug mode 2021-04-28 15:41:08 -04:00
K.J. Valencik
1e9bedb2d7
Merge pull request #713 from neon-bindings/result-apidocs
Top-level module API docs for `neon::result`
2021-04-28 15:03:51 -04:00
David Herman
859d531fe8 Remove unnecessary ignores. 2021-04-28 09:12:08 -07:00
David Herman
671add6f37 Fix broken code in doc examples. 2021-04-28 09:08:36 -07:00
David Herman
0ab1a9d52f Better links to context docs from result docs. 2021-04-27 17:02:53 -07:00
David Herman
d3a296928f Much more complete documentation on contexts. 2021-04-27 16:53:55 -07:00
K.J. Valencik
ad209e30da
Merge pull request #717 from antonok-edm/readme-node-16-support
Update README to mention Node 16 support
2021-04-27 14:21:57 -04:00
Anton Lazarev
f4cd384ed5
update README to mention Node 16 support 2021-04-27 11:12:24 -07:00
K.J. Valencik
be85d188fc
Merge pull request #715 from ogoffart/fix_build_node_16
Fix build with node 16
2021-04-27 10:46:54 -04:00
Olivier Goffart
830836167e Github worflow: replace Node 15 by node 16
Update the Github CI workflows to reflect the current supported Node
versions by removing Node 15 and adding Node 16
2021-04-27 15:39:47 +02:00
Olivier Goffart
50cb290278 Fix build with Node 16
The overload of node::AtExit that takes only two arguments was removed in
dfc288e7fd (diff-4acdf5a5c11188763423b746a9a43917c90546f3b48b9aa6f6eeb60e157ae2a7)
2021-04-27 14:45:57 +02:00
Olivier Goffart
3189ee00c6 Show the compilation error in the console and abort the build
... in case of failure

instead of having an error later when the .so cannot be found.
2021-04-27 14:45:37 +02:00
David Herman
e302ab9e31 Mention idiomatic use of question mark operator. 2021-04-07 10:52:45 -07:00
David Herman
17a0ea921c Top-level module API docs for neon::result. 2021-04-07 10:46:05 -07:00
Dave Herman
b8c8c2a1ae
Merge pull request #707 from neon-bindings/dependabot/npm_and_yarn/cli/y18n-4.0.1
Bump y18n from 4.0.0 to 4.0.1 in /cli
2021-04-07 09:12:58 -07:00
Dave Herman
3a16627093
Merge pull request #710 from neon-bindings/handle-apidocs
Top-level API docs for `neon::handle`
2021-04-07 09:06:46 -07:00
David Herman
5078b1714f Semantic links to stdlib. 2021-04-07 09:05:10 -07:00
Dave Herman
dd3ada4944
Merge pull request #711 from neon-bindings/amilajack-patch-1
update NAPI impl status in readme
2021-04-07 08:35:03 -07:00
Amila Welihinda
c2667bde11 update n-api status in readme 2021-04-06 18:31:13 -07:00
David Herman
54c35c52b5 Align wording for all module top-level entries. 2021-04-06 17:57:53 -07:00
David Herman
01fe92123c Update Deref wording to be closer to the JsBox wording. 2021-04-06 17:17:04 -07:00
David Herman
ed3bb5392e Add section on the implementation of Deref. 2021-04-06 15:44:30 -07:00
David Herman
687fd06042 Top-level API docs for neon::handle. 2021-04-06 13:34:26 -07:00
Dave Herman
534c49575a
Merge pull request #708 from neon-bindings/context-api-docs
Module API docs: neon::context
2021-04-05 11:15:27 -07:00
David Herman
50827bee3d Add examples. 2021-04-05 10:51:12 -07:00
David Herman
0e270d1c7c Top level module API docs for the neon::context module. 2021-04-02 09:22:17 -07:00
dependabot[bot]
b7c26755c1
Bump y18n from 4.0.0 to 4.0.1 in /cli
Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-01 06:09:09 +00:00
Dave Herman
03c7e9e425
Merge pull request #705 from neon-bindings/api-docs
API docs: Neon root, module summaries, neon::borrow
2021-03-29 16:01:38 -07:00
David Herman
e860829dac Address feedback from @kjvalencik's review 2021-03-26 22:37:23 -07:00
Dave Herman
f4ec6eaa29
Apply suggestions from code review
Co-authored-by: K.J. Valencik <kjvalencik@gmail.com>
2021-03-26 19:16:02 -07:00
David Herman
96ff123914 Ignore code blocks in tests. 2021-03-26 18:52:01 -07:00
David Herman
3792dfd5c6 API docs for:
- main Neon root doc
- all module summaries
- `neon::borrow` module
2021-03-23 20:39:16 -07:00
K.J. Valencik
d5bfee3dac
Merge pull request #704 from neon-bindings/kv/v0.8.0
v0.8.0
2021-03-23 12:54:10 -04:00
K.J. Valencik
e6e92cf431
v0.8.0 2021-03-22 10:07:52 -04:00
K.J. Valencik
bf32f66e95
Merge pull request #700 from neon-bindings/kv/drop-queue
Global drop queue for Root
2021-03-22 09:22:15 -04:00
K.J. Valencik
4c224560b6
review(neon): Address review feedback for the global drop queue 2021-03-19 13:47:53 -04:00
K.J. Valencik
194945ebc6
Lint fix 2021-03-16 15:54:35 -04:00
K.J. Valencik
3ab690e8f8
feat(neon): Add a drop queue for safely dropping Root without leaking on N-API 6+ 2021-03-16 15:54:32 -04:00
dependabot[bot]
1a4cecf9b0
build(deps-dev): bump electron from 11.0.4 to 11.1.0 in /test/electron
Bumps [electron](https://github.com/electron/electron) from 11.0.4 to 11.1.0.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/master/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v11.0.4...v11.1.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-24 20:14:16 +00:00
74 changed files with 1097 additions and 6771 deletions

View File

@ -2,4 +2,5 @@
# 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,serde -- -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"

View File

@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
node-version: [10.x, 12.x, 14.x, 15.x]
node-version: [12.x, 14.x, 16.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 test --release -- --nocapture
run: xvfb-run --auto-servernum cargo neon-test -- --nocapture
- name: run CLI test
working-directory: ./create-neon
run: npm test

View File

@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
node-version: [10.x, 12.x, 14.x, 15.x]
node-version: [12.x, 14.x, 16.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 test --release
run: cargo neon-test
- name: run CLI test
working-directory: ./create-neon
run: npm test

View File

@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
node-version: [10.x, 12.x, 14.x, 15.x]
node-version: [12.x, 14.x, 16.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 == '15.x' }}
if: ${{ matrix.node-version == '16.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 test --release
run: cargo neon-test
env:
LIBCLANG_PATH: ${{ runner.temp }}/llvm/bin
- name: run CLI test

View File

@ -17,6 +17,7 @@ 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)
@ -28,6 +29,7 @@ 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)
@ -38,6 +40,7 @@ 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)
@ -53,6 +56,7 @@ Neon owes its existence to the contributions of these fine people.
* [Roberto Vidal](https://github.com/jrvidal)
* [Georg Vienna](https://github.com/geovie)
* [Daijiro Wachi](https://github.com/watilde)
* [Chi Wang](https://github.com/patr0nus)
* [wangcong](https://github.com/king6cong)
* [Amila Welihinda](https://github.com/amilajack)
* [Felix Yan](https://github.com/felixonmars)

View File

@ -1,30 +1,32 @@
[package]
name = "neon"
version = "0.7.1"
version = "0.8.2"
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"]
exclude = ["neon.jpg", "doc/**/*"]
build = "build.rs"
edition = "2018"
[build-dependencies]
neon-build = { version = "=0.7.1", path = "crates/neon-build" }
neon-build = { version = "=0.8.2", 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.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 }
neon-runtime = { version = "=0.8.2", path = "crates/neon-runtime" }
neon-macros = { version = "=0.8.2", path = "crates/neon-macros", optional = true }
[features]
default = ["legacy-runtime"]
@ -73,9 +75,6 @@ event-queue-api = []
# Feature flag to include procedural macros
proc-macros = ["neon-macros"]
# Feature flag to enable serde on Neon types
serde = ["serde_crate", "neon-runtime/serde"]
[package.metadata.docs.rs]
features = ["docs-only", "event-handler-api", "proc-macros", "try-catch-api"]

View File

@ -33,14 +33,15 @@ The N-API backend of Neon requires a minimum Node version of 10.0.
To enable the N-API backend, you need to:
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).
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).
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.7.1"
version = "0.8.2"
default-features = false
features = ["napi-4"]
```
@ -192,7 +193,7 @@ The supported mechanism for concurrency is the Event Queue API (`neon::event::Ev
```toml
[dependencies.neon]
version = "0.7.1"
version = "0.8.1"
default-features = false
features = ["napi-4", "event-queue-api"]
```

View File

@ -13,19 +13,20 @@ 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 install -g neon-cli
$ neon new my-project
$ npm init neon 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 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.
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.
**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!
@ -39,11 +40,11 @@ We are [hard at work](https://github.com/neon-bindings/neon/issues/444) porting
### Node.js
| Node 10 | Node 12 | Node 14 |
| Node 12 | Node 14 | Node 16 |
| ------- | ------- | ------- |
| ✓ | ✓ | ✓ |
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.
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.
### Rust

View File

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

26
cli/package-lock.json generated
View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
[package]
name = "neon-build"
version = "0.7.1"
version = "0.8.2"
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.7.1", path = "../neon-sys", optional = true }
neon-sys = { version = "=0.8.2", path = "../neon-sys", optional = true }

View File

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

View File

@ -14,14 +14,15 @@ use legacy as macros;
// Implementations are in the backend dependent module
#[proc_macro_attribute]
/// 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
/// 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
/// be called each time the module is initialized in a context.
///
/// ```ignore
/// # use neon::prelude::*;
/// #[neon::main]
/// fn my_module(mut cx: ModuleContext) -> NeonResult<()> {
/// fn main(mut cx: ModuleContext) -> NeonResult<()> {
/// let version = cx.string("1.0.0");
///
/// cx.export_value("version", version)?;

View File

@ -1,6 +1,6 @@
[package]
name = "neon-runtime"
version = "0.7.1"
version = "0.8.2"
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,10 +9,8 @@ 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.7.1", path = "../neon-sys", optional = true }
serde_crate = { package = "serde", version = "1", optional = true }
neon-sys = { version = "=0.8.2", path = "../neon-sys", optional = true }
smallvec = "1.4.2"
[dev-dependencies]
@ -27,7 +25,6 @@ napi-4 = ["napi-3"]
napi-5 = ["napi-4"]
napi-6 = ["napi-5"]
napi-experimental = ["napi-6"]
serde = ["serde_crate", "conv"]
docs-only = ["neon-sys/docs-only"]
[package.metadata.docs.rs]

View File

@ -200,17 +200,6 @@ 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::*;
@ -268,6 +257,7 @@ mod napi5 {
#[cfg(feature = "napi-6")]
mod napi6 {
use super::super::types::*;
use std::os::raw::c_void;
generate!(
extern "C" {
@ -279,13 +269,20 @@ 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")]
@ -317,9 +314,6 @@ pub(crate) unsafe fn load(env: Env) -> Result<(), libloading::Error> {
napi1::load(&host, version, 1)?;
#[cfg(feature = "serde")]
napi1_serde::load(&host, version, 1)?;
#[cfg(feature = "napi-4")]
napi4::load(&host, version, 4)?;

View File

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

View File

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

View File

@ -8,6 +8,8 @@ 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;
@ -19,8 +21,5 @@ pub mod tag;
#[cfg(feature = "napi-4")]
pub mod tsfn;
#[cfg(feature = "serde")]
pub mod serde;
mod bindings;
pub use bindings::*;

View File

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

View File

@ -3,23 +3,17 @@ 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) {
assert_eq!(
napi::get_undefined(env, out as *mut Local),
napi::Status::Ok,
);
napi::get_undefined(env, out as *mut Local);
}
/// Mutates the `out` argument provided to refer to the global `null` object.
pub unsafe fn null(out: &mut Local, env: Env) {
assert_eq!(napi::get_null(env, out as *mut Local), napi::Status::Ok,);
napi::get_null(env, out as *mut Local);
}
/// 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) {
assert_eq!(
napi::get_boolean(env, b, out as *mut Local),
napi::Status::Ok,
);
napi::get_boolean(env, b, out as *mut Local);
}
/// Get the boolean value out of a `Local` object. If the `Local` object does not contain a
@ -36,10 +30,7 @@ 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) {
assert_eq!(
napi::create_double(env, v, out as *mut Local),
napi::Status::Ok,
);
napi::create_double(env, v, out as *mut Local);
}
/// Gets the underlying value of an `Local` object containing a JavaScript number. Panics if

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

BIN
doc/types.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
doc/types.pptx Normal file

Binary file not shown.

View File

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

View File

@ -1,4 +1,35 @@
//! Types and traits for obtaining temporary access to the internals of JavaScript values.
//! 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
pub(crate) mod internal;
@ -7,7 +38,7 @@ use std::ops::{Deref, DerefMut, Drop};
use std::os::raw::c_void;
use self::internal::Pointer;
use context::Lock;
use crate::context::Lock;
/// A trait for JS values whose internal contents can be borrowed immutably by Rust while the JS engine is locked.
pub trait Borrow: Sized {

View File

@ -1,14 +1,15 @@
use super::ModuleContext;
use handle::Handle;
use crate::handle::Handle;
#[cfg(feature = "legacy-runtime")]
use crate::object::class::ClassMap;
use crate::result::NeonResult;
use crate::types::{JsObject, JsValue};
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;
@ -16,7 +17,6 @@ use std::mem::MaybeUninit;
use std::os::raw::c_void;
#[cfg(feature = "legacy-runtime")]
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
use types::{JsObject, JsValue};
#[cfg(feature = "legacy-runtime")]
#[repr(C)]

View File

@ -1,24 +1,175 @@
//! 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};
//! 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
pub(crate) mod internal;
use borrow::internal::Ledger;
use borrow::{Borrow, BorrowMut, Ref, RefMut};
use context::internal::Env;
use crate::borrow::internal::Ledger;
use crate::borrow::{Borrow, BorrowMut, Ref, RefMut};
use crate::context::internal::Env;
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
use event::EventQueue;
use handle::{Handle, Managed};
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 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;
@ -27,16 +178,6 @@ 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};
@ -117,16 +258,18 @@ impl CallbackInfo<'_> {
}
}
/// Indicates whether a function call was called with JavaScript's `[[Call]]` or `[[Construct]]` semantics.
/// Indicates whether a function was called with `new`.
#[derive(Clone, Copy, Debug)]
pub enum CallKind {
Construct,
Call,
}
/// 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.
/// A temporary lock of an execution context.
///
/// 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.
/// 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.
pub struct Lock<'a> {
pub(crate) ledger: RefCell<Ledger>,
pub(crate) env: Env,
@ -143,7 +286,9 @@ impl<'a> Lock<'a> {
}
}
/// 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.
/// 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.
///
/// 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> {
@ -409,30 +554,9 @@ 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()) }
}
}
/// A view of the JS engine in the context of top-level initialization of a Neon module.
/// An execution context of module initialization.
pub struct ModuleContext<'a> {
scope: Scope<'a, raw::HandleScope>,
exports: Handle<'a, JsObject>,
@ -500,7 +624,7 @@ impl<'a> ContextInternal<'a> for ModuleContext<'a> {
impl<'a> Context<'a> for ModuleContext<'a> {}
/// A view of the JS engine in the context of a scoped computation started by `Context::execute_scoped()`.
/// An execution context of a scope created by [`Context::execute_scoped()`](Context::execute_scoped).
pub struct ExecuteContext<'a> {
scope: Scope<'a, raw::HandleScope>,
}
@ -522,7 +646,7 @@ impl<'a> ContextInternal<'a> for ExecuteContext<'a> {
impl<'a> Context<'a> for ExecuteContext<'a> {}
/// A view of the JS engine in the context of a scoped computation started by `Context::compute_scoped()`.
/// An execution context of a scope created by [`Context::compute_scoped()`](Context::compute_scoped).
pub struct ComputeContext<'a, 'outer> {
scope: Scope<'a, raw::EscapableHandleScope>,
phantom_inner: PhantomData<&'a ()>,
@ -552,7 +676,7 @@ impl<'a, 'b> ContextInternal<'a> for ComputeContext<'a, 'b> {
impl<'a, 'b> Context<'a> for ComputeContext<'a, 'b> {}
/// A view of the JS engine in the context of a function call.
/// An execution context of a function call.
///
/// The type parameter `T` is the type of the `this`-binding.
pub struct CallContext<'a, T: This> {
@ -566,7 +690,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 via the JavaScript `[[Call]]` or `[[Construct]]` semantics.
/// Indicates whether the function was called with `new`.
pub fn kind(&self) -> CallKind {
#[cfg(feature = "legacy-runtime")]
let kind = self.info.kind();
@ -653,13 +777,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` with `this`-type `JsObject`.
/// A shorthand for a [`CallContext`](CallContext) with `this`-type [`JsObject`](crate::types::JsObject).
pub type FunctionContext<'a> = CallContext<'a, JsObject>;
/// An alias for `CallContext`, useful for indicating that the function is a method of a class.
/// An alias for [`CallContext`](CallContext), useful for indicating that the function is a method of a class.
pub type MethodContext<'a, T> = CallContext<'a, T>;
/// A view of the JS engine in the context of a task completion callback.
/// An execution context of a task completion callback.
pub struct TaskContext<'a> {
/// We use an "inherited HandleScope" here because the C++ `neon::Task::complete`
/// method sets up and tears down a `HandleScope` for us.

View File

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

View File

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

View File

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

View File

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

View File

@ -1,33 +1,87 @@
//! Safe _handles_ to managed JavaScript memory.
//! 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))
//! }
//! # }
//! ```
pub(crate) mod internal;
#[cfg(feature = "napi-1")]
mod root;
pub(crate) mod root;
#[cfg(feature = "napi-1")]
pub use self::root::Root;
use self::internal::SuperType;
use context::internal::Env;
use context::Context;
use crate::context::internal::Env;
use crate::context::Context;
use crate::result::{JsResult, JsResultExt};
use crate::types::Value;
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 that is managed by the JS garbage collector and can only be accessed via handles.
/// The trait of data owned by the JavaScript engine and that 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 safely rooted _handle_ to a JS value in memory that is managed by the garbage collector.
/// A handle to a JavaScript value that is owned by the JavaScript engine.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct Handle<'a, T: Managed + 'a> {
@ -84,7 +138,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()`.
/// The result of a call to [`Handle::downcast()`](Handle::downcast).
pub type DowncastResult<'a, F, T> = Result<Handle<'a, T>, DowncastError<F, T>>;
impl<'a, F: Value, T: Value> JsResultExt<'a, T> for DowncastResult<'a, F, T> {

View File

@ -1,25 +1,40 @@
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 context::Context;
use handle::Handle;
use object::Object;
use types::boxed::Finalize;
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;
#[repr(transparent)]
#[derive(Clone)]
struct NapiRef(*mut c_void);
pub(crate) struct NapiRef(*mut c_void);
/// `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)]
// # 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.
pub struct Root<T> {
internal: NapiRef,
#[cfg(feature = "napi-6")]
drop_queue: Arc<ThreadsafeFunction<NapiRef>>,
_phantom: PhantomData<T>,
}
@ -40,15 +55,19 @@ 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 _must_ ensure `Root::into_inner` or `Root::drop` is called
/// The caller _should_ 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, it will *panic*.
/// 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
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,
}
}
@ -75,6 +94,8 @@ impl<T: Object> Root<T> {
Self {
internal: self.internal.clone(),
#[cfg(feature = "napi-6")]
drop_queue: Arc::clone(&self.drop_queue),
_phantom: PhantomData,
}
}
@ -124,6 +145,7 @@ 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.
@ -140,4 +162,9 @@ impl<T> Drop for Root<T> {
);
}
}
#[cfg(feature = "napi-6")]
fn drop(&mut self) {
let _ = self.drop_queue.call(self.internal.clone(), None);
}
}

View File

@ -1,18 +1,83 @@
//! 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;
//! 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
pub mod borrow;
pub mod context;
@ -25,6 +90,7 @@ 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")]
@ -37,27 +103,14 @@ 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")))]
/// 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(())
/// });
/// ```
#[doc(hidden)]
#[macro_export]
macro_rules! register_module {
($module:pat, $init:block) => {
@ -98,13 +151,19 @@ macro_rules! register_module {
///
/// Example:
///
/// ```rust,ignore
/// ```
/// # #[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()) }
/// 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 {
@ -295,7 +354,6 @@ macro_rules! impl_managed {
/// Example:
///
/// ```rust
/// # #[macro_use] extern crate neon;
/// # use neon::prelude::*;
/// # fn main() {}
/// pub struct Greeter {
@ -383,7 +441,7 @@ macro_rules! neon_stringify {
#[cfg(test)]
mod tests {
extern crate rustversion;
use lazy_static::lazy_static;
use semver::Version;
use std::path::{Path, PathBuf};
use std::process::Command;
@ -450,10 +508,7 @@ mod tests {
log("static_test");
run(
"cargo test --release",
&project_root().join("test").join("static"),
);
run("cargo test", &project_root().join("test").join("static"));
}
// Only run the static tests in Beta. This will catch changes to error reporting
@ -507,7 +562,7 @@ mod tests {
log("dynamic_cargo_test");
let test_dynamic_cargo = project_root().join("test").join("dynamic").join("native");
run("cargo test --release", &test_dynamic_cargo);
run("cargo test", &test_dynamic_cargo);
}
#[test]

76
src/lifecycle.rs Normal file
View File

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

View File

@ -1,10 +1,10 @@
//! Internals needed by macros. These have to be exported for the macros to work
pub use context::internal::{initialize_module, Env};
pub use crate::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 object::class::internal::{
pub use crate::object::class::internal::{
AllocateCallback, ConstructCallback, ConstructorCallCallback, MethodCallback,
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,10 @@
use context::Context;
use handle::{Handle, Managed};
use result::JsResult;
use types::{build, JsString, JsValue};
//! 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};
#[cfg(feature = "napi-1")]
pub fn eval<'a, 'b, C: Context<'a>>(
cx: &mut C,
script: Handle<'b, JsString>,

View File

@ -1,19 +1,51 @@
//! Types and traits for working with JavaScript exceptions.
//! 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
#[cfg(all(feature = "napi-1", feature = "serde"))]
pub use neon_runtime::serde::Error as SerdeError;
use context::Context;
use handle::Handle;
use crate::context::Context;
use crate::handle::Handle;
use crate::types::Value;
use std::fmt::{Display, Formatter, Result as FmtResult};
use types::Value;
/// An error sentinel type used by `NeonResult` (and `JsResult`) to indicate that the JavaScript engine
/// has entered into a throwing state.
/// A [unit type][unit] indicating that the JavaScript thread is throwing an exception.
///
/// `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.
/// `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
#[derive(Debug)]
pub struct Throw;
@ -23,36 +55,14 @@ impl Display for Throw {
}
}
/// The result of a computation that might send the JS engine into a throwing state.
/// The result type for throwing APIs.
pub type NeonResult<T> = Result<T, Throw>;
/// The result of a computation that produces a JavaScript value and might send the JS engine into a throwing state.
/// Shorthand for a [`NeonResult`](NeonResult) that produces JavaScript values.
pub type JsResult<'b, T> = NeonResult<Handle<'b, T>>;
/// An extension trait for `Result` values that can be converted into `JsResult` values by throwing a JavaScript
/// exception in the error case.
/// Extension trait for converting Rust [`Result`](std::result::Result) values
/// into [`JsResult`](JsResult) values by throwing JavaScript exceptions.
pub trait JsResultExt<'a, V: Value> {
fn or_throw<'b, C: Context<'b>>(self, cx: &mut C) -> JsResult<'a, V>;
}
/// An extension trait for `Result` values that can be converted into `NeonResult` values by throwing a JavaScript
/// exception in the error case.
pub trait ResultExt<T> {
fn or_throw<'a, C: Context<'a>>(self, cx: &mut C) -> NeonResult<T>;
}
#[cfg(all(feature = "napi-1", feature = "serde"))]
impl<T> ResultExt<T> for Result<T, SerdeError> {
fn or_throw<'a, C: Context<'a>>(self, cx: &mut C) -> NeonResult<T> {
let err = match self {
Ok(v) => return Ok(v),
Err(err) => err,
};
if err.is_exception_pending() {
Err(Throw)
} else {
cx.throw_error(err.to_string())
}
}
}

View File

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

View File

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

View File

@ -7,6 +7,7 @@ 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;
@ -159,6 +160,8 @@ impl<T: Send + 'static> Clone for JsBox<T> {
}
}
impl<T: Send + 'static> Object for JsBox<T> {}
impl<T: Send + 'static> Copy for JsBox<T> {}
impl<T: Send + 'static> Value for JsBox<T> {}

View File

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

View File

@ -5,12 +5,12 @@ use std::panic::{catch_unwind, UnwindSafe};
use neon_runtime;
use neon_runtime::raw;
use 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};
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};
/// A JS `Error` object.
#[repr(C)]

View File

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

View File

@ -1,4 +1,76 @@
//! 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")]
@ -12,20 +84,20 @@ pub(crate) mod utf8;
use self::internal::{FunctionCallback, ValueInternal};
use self::utf8::Utf8;
use context::internal::Env;
use context::{Context, FunctionContext};
use handle::internal::SuperType;
use handle::{Handle, Managed};
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 neon_runtime;
use neon_runtime::raw;
use object::{Object, This};
use result::{JsResult, JsResultExt, NeonResult, Throw};
use smallvec::SmallVec;
use std::fmt;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::os::raw::c_void;
use types::internal::Callback;
pub use self::binary::{BinaryData, BinaryViewType, JsArrayBuffer, JsBuffer};
#[cfg(feature = "napi-1")]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,12 +9,8 @@ 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", "serde"]
features = ["default-panic-hook", "napi-6", "try-catch-api", "event-queue-api"]

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -64,4 +64,11 @@ 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);
});
});

View File

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

View File

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

View File

@ -9,7 +9,6 @@ mod js {
pub mod functions;
pub mod numbers;
pub mod objects;
pub mod serde;
pub mod strings;
pub mod threads;
pub mod types;
@ -23,7 +22,6 @@ use js::errors::*;
use js::functions::*;
use js::numbers::*;
use js::objects::*;
use js::serde::*;
use js::strings::*;
use js::threads::*;
use js::types::*;
@ -258,9 +256,7 @@ 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("parse_pokedex", parse_pokedex)?;
cx.export_function("stringify_pokedex", stringify_pokedex)?;
cx.export_function("drop_global_queue", drop_global_queue)?;
Ok(())
}