Compare commits

...

180 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
K.J. Valencik
b10a779daa
Merge pull request #692 from patr0nus/main
Add binding to napi_run_script
2021-03-15 16:35:17 -04:00
patr0nus
ccbc5916fe cargo fmt 2021-03-15 20:43:13 +08:00
patr0nus
64ee1659c2 Merge remote-tracking branch 'upstream/main' into main
# Conflicts:
#	crates/neon-runtime/src/napi/bindings/functions.rs
#	src/lib.rs
2021-03-14 23:52:32 +08:00
K.J. Valencik
b730df7fa5
Merge pull request #698 from neon-bindings/kv/rustfmt
Lint CI enforcement and cargo fmt
2021-03-14 09:06:31 -04:00
Wang, Chi
bd795eabae
Update mod.rs 2021-03-14 13:14:23 +08:00
K.J. Valencik
111d2ac73c
Simplify clippy lint workflows with the -Zpackage-features RFC
Also adds, `cargo clippy-legacy` and `cargo clippy-napi` aliases
2021-03-13 18:17:23 -05:00
patr0nus
397c734839 Move the run_script binding to neon::reflect::eval 2021-03-13 21:58:26 +08:00
patr0nus
2918268580 Merge remote-tracking branch 'upstream/main' into main
# Conflicts:
#	crates/neon-runtime/src/napi/bindings/functions.rs
2021-03-13 21:42:03 +08:00
David Herman
bd69a89cda Bump create-neon patch version. 2021-03-12 16:48:15 -08:00
Dave Herman
5d1d659f1a
Merge pull request #697 from neon-bindings/create-neon-readme
create-neon README
2021-03-12 15:17:37 -08:00
K.J. Valencik
56b3715d7b
cargo fmt all the things! 2021-03-12 18:05:44 -05:00
K.J. Valencik
fdb2a48a44
Clippy and cargo fmt check in CI 2021-03-12 18:05:26 -05:00
K.J. Valencik
188c8d1852
Lint fixes 2021-03-12 18:05:15 -05:00
K.J. Valencik
c0d72cba94
Merge pull request #609 from neon-bindings/kv/cleanup
Clean-up: Lots of lint fixes
2021-03-12 17:30:22 -05:00
K.J. Valencik
0f7572ab00
Fix clippy lints in tests 2021-03-12 17:10:48 -05:00
David Herman
98a8435062 Explain the build process a bit more. 2021-03-12 13:57:11 -08:00
K.J. Valencik
553650ada6
Fix all clippy lints except for clippy::missing_safety_doc
A couple of lints are ignored because they would be breaking changes to the API
2021-03-12 16:53:36 -05:00
K.J. Valencik
f467acf8ff
fix(neon-build): Fix incorrect panic mesasge 2021-03-12 16:53:05 -05:00
K.J. Valencik
f35724bdfe
Remove unnecessary extern "C" 2021-03-12 16:53:04 -05:00
K.J. Valencik
3486ff34fe
Merge pull request #667 from neon-bindings/kv/external-buffer
feat(neon): Add `Buffer::external` for creating a `Buffer` backed by Rust data
2021-03-12 15:15:12 -05:00
K.J. Valencik
b0ff15ef97
feat(neon): Add Buffer::external and ArrayBuffer::external for creating buffers backed by Rust data 2021-03-11 14:42:11 -05:00
David Herman
446b60cdec Project README and more content for the generated README. 2021-03-10 22:28:24 -08:00
David Herman
3e76b0196d Bump create-neon to 0.1.1 so we can publish. 2021-03-10 09:54:41 -08:00
Dave Herman
691a66f809
Merge pull request #690 from neon-bindings/create-neon
create-neon
2021-03-10 09:18:35 -08:00
David Herman
6165246692 Shell to npm using shell: true instead of tacking on the extension for Windows. 2021-03-10 08:50:19 -08:00
patr0nus
272fe0d209 Test run_string_as_script with invalid syntax 2021-03-10 23:11:03 +08:00
David Herman
e9a82cb9c2 Relax test timeout from 2s to 5s, since GH Actions test runners are fairly slow and there aren't very many tests anyway. 2021-03-09 23:41:55 -08:00
David Herman
af4a264f77 Tweaking diagnostic output... 2021-03-09 23:14:48 -08:00
David Herman
208346f37b Call npm.cmd on Windows 2021-03-09 21:13:06 -08:00
David Herman
993d106e30 More CI debugging: print stderr on failure 2021-03-09 21:03:45 -08:00
David Herman
20727befc1 Debugging CI: print output on failure of child process 2021-03-09 20:35:08 -08:00
David Herman
8400efa8ae Add rimraf shim since { recursive: true } isn't supported in all our supported Node versions. 2021-03-09 13:01:26 -08:00
David Herman
6b21eb10cb Longer retry delay for rmdir? 2021-03-09 10:38:12 -08:00
David Herman
9d975415da Add 3 retries to rmdir after each test. 2021-03-09 10:30:45 -08:00
David Herman
7b136788fc Remove fs/promises since some supported Node versions don't support it. 2021-03-09 10:21:04 -08:00
David Herman
7c3c97b2f7 Ensure we run npm install before running the tests, for CI. 2021-03-09 09:48:36 -08:00
David Herman
ddcbcbc657 Add create-neon tests to CI. 2021-03-09 09:40:15 -08:00
David Herman
e8803c6b36 - Move test helpers into dev/ directory
- Delete test output directory after every test
- Add test output directories to gitignore
- Add npm-debug.log to gitignore template
2021-03-08 21:51:23 -08:00
David Herman
7840161021 Initial test suite. 2021-03-07 23:11:25 -08:00
patr0nus
2e7a34a071 Add binding to napi_run_script 2021-03-07 19:25:30 +08:00
David Herman
d79f9eebb1 Simplify the datatypes for the template expansion. 2021-03-06 10:46:48 -08:00
David Herman
70fe6e4504 Avoids asking the "main" or "test" package.json entries by creating the package.json up front with default values. 2021-03-06 10:27:49 -08:00
David Herman
f7ee6b6d95 Updates the generated package.json with Neon-specific default configuration. 2021-03-05 13:39:33 -08:00
David Herman
73bbe423ff Add shebang 2021-03-05 10:01:21 -08:00
David Herman
d7f1c7f3fd - remove dead import
- eliminate `+` hack
2021-03-05 09:56:34 -08:00
David Herman
01c0526a3b Just call npm directly since npm init create-neon doesn't pass the environment variables through to npx. 2021-03-05 09:52:42 -08:00
David Herman
96f3f8672a Abstract out die helper. 2021-03-04 23:15:20 -08:00
David Herman
fe9e5882ec Address @kjvalencik's review, plus some general cleanup and refactoring:
- Abstracted handlebars template logic into a Template class
- Abstracted the template context type into a Metadata module with some type definitions
- Better error messages
- Infer the N-API version from the current process
- Drop the Neon patch version number from the generated manifest
- Use JSON.stringify for quoting text
2021-03-04 23:07:33 -08:00
David Herman
5cffd02772 First working version, with a simple manual test. Next up we need automated tests. 2021-03-04 13:45:32 -08:00
David Herman
e548f23b74 Add shelling to npm init and reading from package.json. 2021-03-03 11:37:34 -08:00
David Herman
e40c6125b6 WIP 2021-02-28 23:28:38 -08:00
Dave Herman
6e2557c4aa
Merge pull request #685 from neon-bindings/migration-experiment
N-API migration guide
2021-02-26 14:23:14 -08:00
David Herman
1a2c1a59bc - Add link to migration guide to readme
- Uncommit accidentally-committed changes to the code
2021-02-26 13:50:57 -08: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
K.J. Valencik
ff0593b823
Merge pull request #687 from neon-bindings/kv/cargo-cp-artifact
Switch N-API tests to `cargo-cp-artifact` from `neon-build`
2021-02-24 15:12:54 -05:00
K.J. Valencik
19e2c4a452
Switch N-API tests to cargo-cp-artifact from neon-build 2021-02-24 12:01:40 -05:00
David Herman
375dc4a396 Some more explanation around the task API and contention with libuv thread pool. 2021-02-19 11:01:23 -08:00
David Herman
25a4dd3619 Flesh out the JsBox example with Finalize and a pure JS class wrapper (per @kjvalencik suggestions). 2021-02-19 10:55:45 -08:00
David Herman
7cb91d9314 More hate for declare_types! :) 2021-02-19 10:45:58 -08:00
David Herman
af6506ae8e Explain different N-API version feature flags. 2021-02-18 12:04:27 -08:00
David Herman
077f9e5afb Add intro 2021-02-16 18:17:44 -08:00
David Herman
7cae7c774e Add feature flag info for "event-queue-api" flag. 2021-02-15 08:27:16 -08:00
David Herman
4fda49d283 Wrote up a complete migration guide with all of the incompatible changes between the legacy backend and N-API backend. 2021-02-14 23:22:38 -08:00
K.J. Valencik
eabe09026c
Merge pull request #681 from jrose-signal/empty-buffers
Fix as_slice and as_mut_slice for empty Buffers and ArrayBuffers
2021-02-10 15:37:27 -05:00
Jordan Rose
c6768569ee Fix as_slice and as_mut_slice for empty Buffers and ArrayBuffers
These buffers may have NULL as their address, which
slice::from_raw_parts does not allow.
2021-02-10 11:29:03 -08:00
K.J. Valencik
d775895e21
Merge pull request #680 from jrose-signal/Optional-Finalize
Make Option<T: Finalize> implement Finalize
2021-02-09 19:20:10 -05:00
Jordan Rose
10b84c773e Make Option<T: Finalize> implement Finalize 2021-02-09 15:13:21 -08:00
K.J. Valencik
8af9d22508
Merge pull request #679 from neon-bindings/kv/v0.7.1
v0.7.1 RC
2021-02-04 16:46:21 -05:00
K.J. Valencik
22b3196442
Release v0.7.1 2021-02-04 16:18:15 -05:00
K.J. Valencik
0a35c58e96
Merge pull request #677 from neon-bindings/kv/no-panic-shutdown
fix(neon): Do not panic when dropping a Root if the VM has shutdown
2021-02-02 10:45:53 -05:00
K.J. Valencik
ea15e232b9
fix(ci): Fix linux builds to work on Ubuntu 20.04 2021-01-29 15:25:14 -05:00
K.J. Valencik
ace68bdd55
fix(neon): Do not panic when dropping a Root if the VM has shutdown 2021-01-29 14:29:57 -05:00
Amila Welihinda
d566f83de4
Merge pull request #669 from neon-bindings/fix/is-date-napi-5
fix: gate is_date api behind napi-5 feature flag
2021-01-13 17:46:39 -08:00
Amila Welihinda
1e3be80740 fix: gate is_date api behind napi-5 feature flag 2021-01-13 17:42:09 -08:00
K.J. Valencik
6e0b62750a
Merge pull request #668 from ab-cgh/correct-cast-types
Don't assume pointer type in runtime casts
2021-01-13 18:09:03 -05:00
ABGH, C
c59f20b123 Don't assume pointer type in runtime casts 2021-01-13 16:23:41 -06:00
Amila Welihinda
7a76945fea
Merge pull request #639 from neon-bindings/feat/date-api
feat: impl Date API
2021-01-13 10:40:32 -08:00
Dave Herman
5adcdba556
Merge pull request #666 from neon-bindings/same-handle
N-API: deprecate PartialEq and add strict_equals method
2021-01-13 10:07:45 -08:00
David Herman
5c908229ac Make it a compile-time error to use PartialEq / Eq on Handles in the new runtime. 2021-01-13 09:10:56 -08:00
Amila Welihinda
22a7ea90a5 docs: more rust doc additions 2021-01-12 11:08:43 -08:00
Amila Welihinda
6a375cb6af chore: more refactors to reflect comments 2021-01-12 10:14:51 -08:00
Amila Welihinda
1cce66bbd9 feat: add safety doc comments to neon runtime date api 2021-01-11 11:23:00 -08:00
David Herman
cd460af96b Deprecates PartialEq for the N-API backend and adds a strict_equals method for handles. 2021-01-09 17:47:34 -08:00
Amila Welihinda
7a5ae29f75 fix: refator date logic to separate file 2021-01-09 15:49:51 -08:00
Amila Welihinda
0426692743 feat: impl new_lossy 2021-01-09 15:49:49 -08:00
Amila Welihinda
7105a63ad6 test: add additional date api test cases 2021-01-09 15:48:00 -08:00
Amila Welihinda
3799bfc8db test: add additional date api test cases 2021-01-09 15:48:00 -08:00
Amila Welihinda
ea1011f8f3 test: add out of range date test case 2021-01-09 15:48:00 -08:00
Amila Welihinda
d8d4032e6b feat: implement JsDate::try_new API 2021-01-09 15:48:00 -08:00
Amila Welihinda
e46340bd55 fix: address more cr comments 2021-01-09 15:48:00 -08:00
Amila Welihinda
9774b95145 fix: cr comemnt fixes 2021-01-09 15:48:00 -08:00
Amila Welihinda
49fd8b308f feat: impl Date API (closes #70) 2021-01-09 15:48:00 -08:00
Dave Herman
a68d5fc8bb
Merge pull request #664 from neon-bindings/jsbuffer-uninitialized
JsBuffer::uninitialized() for N-API backend
2021-01-07 11:48:35 -08:00
K.J. Valencik
c0937e36c9
Update lock files 2021-01-07 14:34:28 -05:00
K.J. Valencik
aae89f36d0
Merge pull request #661 from Himself65/patch-1
Update documentation URL
2021-01-07 08:10:15 -05:00
Himself65
df9f5cbba9 Update documentation URL
Co-authored-by: K.J. Valencik <kjvalencik@gmail.com>
2021-01-07 14:42:38 +08:00
David Herman
75457c97e3 Implements JsBuffer::uninitialized() for N-API backend. This checks off one of the last remaining tasks for #444. 2021-01-06 16:07:57 -08:00
K.J. Valencik
f64543a2bd
Merge pull request #663 from neon-bindings/kv/remove-deprecated
Remove and feature flag features that are being removed in N-API
2021-01-06 12:48:31 -05:00
K.J. Valencik
0734bf4a07
Remove classes from N-API 2021-01-06 12:29:57 -05:00
K.J. Valencik
6cf59213fe
Remove APIs that will not be implemented in N-API 2021-01-06 12:09:38 -05:00
K.J. Valencik
94bb1cd933
Merge pull request #660 from neon-bindings/kv/0.7.0-rc
v0.7.0 Release Candidate
2021-01-05 11:54:04 -05:00
K.J. Valencik
999a4bb7ea
v0.7.0 2021-01-05 10:07:17 -05:00
K.J. Valencik
4730ab7166
Threadsafe Handles (#622)
feat: Implements Threadsafe Handles as specified by https://github.com/neon-bindings/rfcs/pull/32

Requires the `event-queue-api` feature flag until the RFC is accepted.
2021-01-05 09:56:46 -05:00
K.J. Valencik
f98dec2679
feat(neon-runtime): Add selectable N-API versions (#649)
feat(neon-runtime): Add selectable N-API versions

* Clear error message around minimum N-API version
* Only load the host process as a library once
2021-01-04 09:06:46 -05:00
K.J. Valencik
39a6c4fbdc
Merge pull request #651 from neon-bindings/dependabot/npm_and_yarn/test/electron/ini-1.3.7
build(deps): bump ini from 1.3.5 to 1.3.7 in /test/electron
2020-12-11 16:06:55 -05:00
dependabot[bot]
0013378993
build(deps): bump ini from 1.3.5 to 1.3.7 in /test/electron
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-11 20:41:37 +00:00
136 changed files with 8062 additions and 2389 deletions

6
.cargo/config.toml Normal file
View File

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

39
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: Lints
on:
push:
# Prevent duplicate runs of this workflow on our own internal PRs.
branches:
- main
pull_request:
branches:
- main
jobs:
lint:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x]
rust-toolchain: [nightly]
steps:
- uses: actions/checkout@v2
- name: Use Rust ${{ matrix.rust-toolchain }}
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust-toolchain }}
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: install build-essential
run: sudo apt-get install -y build-essential
- name: Formatting
run: cargo fmt --all -- --check
- name: Clippy (N-API)
run: cargo clippy-napi
- name: Clippy (Legacy)
run: cargo clippy-legacy

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:
@ -29,7 +29,10 @@ jobs:
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: install g++-4.8
run: sudo apt-get install -y g++-4.8
- 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,4 +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,10 +31,10 @@ 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@01144dc
uses: KyleMayes/install-llvm-action@01144dc97b1e2693196c3056414a44f15180648b
with:
version: "10"
directory: ${{ runner.temp }}/llvm
@ -43,6 +43,9 @@ 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
working-directory: ./create-neon
run: npm test

3
.gitignore vendored
View File

@ -7,6 +7,9 @@ Cargo.lock
**/index.node
**/artifacts.json
cli/lib
create-neon/dist
create-neon/create-neon-test-project
create-neon/create-neon-manual-test-project
test/cli/lib
npm-debug.log
rls*.log

View File

@ -17,7 +17,9 @@ 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)
* [Amal Hussein](https://github.com/nomadtechie)
* [Usagi Ito](https://github.com/usagi)
@ -27,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)
@ -37,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)
@ -52,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,29 +1,32 @@
[package]
name = "neon"
version = "0.6.0"
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.6.0", 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.6.0", path = "crates/neon-runtime" }
neon-macros = { version = "=0.6.0", path = "crates/neon-macros", 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"]
@ -42,9 +45,22 @@ default-panic-hook = []
# enabled by default.
legacy-runtime = ["neon-runtime/neon-sys", "neon-build/neon-sys"]
# Feature flag to enable the experimental N-API runtime. For now, this feature
# Default N-API version. Prefer to select a minimum required version.
napi-runtime = ["napi-6"]
# Select the N-API version
# Feature flags to enable the experimental N-API runtime. For now, this feature
# is disabled by default.
napi-runtime = ["proc-macros", "neon-macros/napi", "neon-runtime/napi"]
# The Node N-API documentation specifies N-API and Node version requirements
# https://nodejs.org/api/n-api.html
napi-1 = ["proc-macros", "neon-macros/napi", "neon-runtime/napi"]
napi-2 = ["napi-1", "neon-runtime/napi-2"]
napi-3 = ["napi-2", "neon-runtime/napi-3"]
napi-4 = ["napi-3", "neon-runtime/napi-4"]
napi-5 = ["napi-4", "neon-runtime/napi-5"]
napi-6 = ["napi-5", "neon-runtime/napi-6"]
napi-latest = ["napi-6"]
napi-experimental = ["napi-6", "neon-runtime/napi-experimental"]
# Feature flag to disable external dependencies on docs build
docs-only = ["neon-runtime/docs-only"]
@ -52,6 +68,10 @@ docs-only = ["neon-runtime/docs-only"]
# Feature flag to enable the try_catch API of RFC 29.
try-catch-api = []
# Feature flag to enable the `EventQueue` API of RFC 33.
# https://github.com/neon-bindings/rfcs/pull/32
event-queue-api = []
# Feature flag to include procedural macros
proc-macros = ["neon-macros"]

View File

@ -1,14 +1,320 @@
# [WIP] N-API Migration Guide
# N-API Migration Guide
## Context
## What is this about?
Since v10, Node.js supports an improved API for building native modules, known as [N-API](https://nodejs.org/api/n-api.html). N-API offers a clearer, more complete, and more stable API layer for writing Node.js plugins than previous Node versions did.
The Neon community has been [hard at work](https://github.com/neon-bindings/neon/issues/444) porting the library to a new backend based on N-API.
### Why N-API?
Some key benefits of the new backend include:
- Compiled Neon modules will work in all versions of Node _without needing to be recompiled_, guaranteed!
- You can precompile Neon-based libraries to be completely transparent to downstream consumers.
- The build process is streamlined, making Neon apps more reliable and easier to debug.
- The stability guarantees of N-API allow us to avoid risk of incompatible changes to future releases of Neon.
### What does this mean for me?
Porting Neon to N-API has been mostly transparent, but it has required a few backwards-incompatible changes. This guide provides instructions on how to migrate existing apps to the new N-API backend.
Fortunately, the guaranteed stability of N-API means that once Neon users do this migration, we have increased confidence in the stability of Neon. We expect this to be the **last major breaking change before reaching Neon 1.0.**
If you have any trouble porting, **please reach out to us** with a Neon issue or on the community Slack! We want to help everyone upgrade as smoothly and seamlessly as possible.
## Getting started
### Supported Node versions
The N-API backend of Neon requires a minimum Node version of 10.0.
### Enabling the N-API backend
To enable the N-API backend, you need to:
1. Remove `build.rs` from the project directory and `build = "build.rs"` from the `Cargo.toml`. The N-API backend does not require a Cargo build script.
2. Disable the default features (for now, the default features select the legacy backend) by setting `default-features = false`; and
3. Enable the appropriate feature flag in your `Cargo.toml` to select the N-API version you need support for (each N-API version N uses the feature flag `"napi-N"`, for example `"napi-4"` for N-API version 4).
As a rule, you should choose the **oldest version of N-API that has the APIs you need.** (We will be adding N-API version requirements to the Neon API docs to make this clearer in the future.) You can consult the [official N-API feature matrix](https://nodejs.org/api/n-api.html#n_api_node_api_version_matrix) to see which N-API versions come with various versions of Node.
```toml
[dependencies.neon]
version = "0.8.2"
default-features = false
features = ["napi-4"]
```
## Minor API changes
### Context
Many methods that previously did not require context (e.g., `JsString::size`) now require a context. In many cases, this means adding an additional argument or using a convenience method on the `Context` trait.
### Impacted methods
#### Affected methods
##### Handle
* `Handle`
- `is_a`
- `downcast`
`Handle::downcast` also requires a second type argument for the context type. This can usually be inferred, so you can typically use `_`.
**Before:**
```rust
value.downcast::<JsNumber>()
```
**After:**
```rust
value.downcast::<JsNumber, _>(&mut cx)
```
##### Primitive types
* `JsBoolean`
- `value`
* `JsNull`
- `new`
* `JsString`
- `size`
- `value`
* `JsNumber`
- `value`
* `JsUndefined`
- `new`
##### Object APIs
* `PropertyKey`
- `get_from`
- `set_from`
### Handle equality
Handles no longer implement `Eq` or `PartialEq`, which had underspecified behavior. Use `Value::strict_equals` instead to invoke the behavior of JavaScript's `===` operator.
## Major API changes
The N-API backend introduces two categories of significant change:
1. Embedding Rust data, which is no longer done through the awkward and complex `declare_types!` (i.e. classes) macro, but through a simpler primitive: the `JsBox` API.
2. Concurrency, which is offered through the Event Queue API instead of the Task API or Event Handlers, both of which are deprecated and removed in the N-API backend.
### Embedding Rust data
The `declare_types!` macro is deprecated and replaced by the `JsBox` type.
_Rationale:_ The `declare_types!` macro provides a syntax for defining classes, but requires substantial boilerplate and is unergonomic for simple cases and tends to interact poorly with IDEs. It's also not flexible enough to express the full range of JavaScript classes syntax and semantics. With the `JsBox` type, it's easy to embed Rust data in JavaScript objects, which can then be nested inside of more feature-rich classes defined in pure JavaScript (or TypeScript).
**Before:**
```rust
struct User {
first_name: String,
last_name: String,
}
impl User {
fn full_name(&self) -> String {
format!("{} {}", self.first_name, self.last_name)
}
}
declare_types! {
class JsUser for User {
init(mut cx) {
let first_name = cx.argument::<JsString>(0)?;
let last_name = cx.argument::<JsString>(1)?;
Ok(User { first_name, last_name })
}
method full_name(mut cx) {
let this = cx.this();
let guard = cx.lock();
let user = this.borrow(&guard);
let full_name = user.full_name();
Ok(cx.string(full_name).upcast())
}
}
}
```
**After:**
On the Rust side, the wrapped type must implement the `Finalize` trait, but this comes with a default implementation so it can be implemented with an empty `impl` block:
```rust
struct User {
first_name: String,
last_name: String,
}
impl Finalize for User { }
```
The type can then be exposed to JavaScript with simple functions that wrap `User` in a `JsBox`:
```rust
fn create_user(mut cx: FunctionContext) -> JsResult<JsBox<User>> {
let first_name = cx.argument::<JsString>(0)?.value(&mut cx);
let last_name = cx.argument::<JsString>(1)?.value(&mut cx);
Ok(cx.boxed(User { first_name, last_name }))
}
fn user_full_name(mut cx: FunctionContext) -> JsResult<JsString> {
let user = cx.argument::<JsBox<User>>(0)?;
let full_name = user.full_name();
Ok(cx.string(full_name))
}
```
Finally, you can provide an idiomatic JavaScript interface to the type by wrapping the boxed type in a class:
```js
class User {
constructor(firstName, lastName) {
this.boxed = addon.createUser(firstName, lastName);
}
fullName() {
return addon.userFullName(this.boxed);
}
}
```
### Concurrency
The supported mechanism for concurrency is the Event Queue API (`neon::event::EventQueue`). This feature has not yet stabilized, so to use this API, you'll also need to enable the `"event-queue-api"` feature flag as well:
```toml
[dependencies.neon]
version = "0.8.1"
default-features = false
features = ["napi-4", "event-queue-api"]
```
#### Deprecated: Task API
The Task API (`neon::task`) is deprecated, and should in most cases be translated to using the Event Queue API.
_Rationale:_ The Task API was built on top of the low-level libuv thread pool, which manages the concurrency of the Node.js system internals and should rarely be exposed to user-level programs. For most use cases, Neon users took advantage of this API as the only way to implement background, asynchronous computations. The Event Queue API is a more general-purpose, convenient, and safe way of achieving that purpose.
That said, **if you believe you need access to the libuv thread pool, please [file an issue in the Neon repository](https://github.com/neon-bindings/neon/issues) with a description of your use case to let us know about it.** We don't believe this is commonly needed, but we don't want to leave you stuck!
**Before:**
With the `Task` API it was possible to define background computations off the main JavaScript thread, but these could only be run within the libuv thread pool--which runs all the system logic for the internals of Node.js. This gave Neon programmers a real power but forced them to contend with Node.js system tasks.
```rust
impl Task for MyTask {
type Output = i32;
type Error = String;
type JsEvent = JsNumber;
fn perform(&self) -> Result<Self::Output, Self::Error> {
// compute the result...
}
fn complete(self, mut cx: TaskContext, result: Result<Self::Output, Self::Error>) -> JsResult<Self::JsEvent> {
match result {
Ok(n) => {
Ok(cx.number(n))
}
Err(s) => {
cx.throw_error(s)
}
}
}
}
pub fn start_task(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let callback = cx.argument::<JsFunction>(0)?;
MyTask.schedule(callback);
Ok(cx.undefined())
}
```
**After:**
With the N-API backend, Neon programmers can use their own native threads and avoid competing with the Node.js system internals. This also brings some convenience since it doesn't require defining any custom trait implementations.
```rust
pub fn start_task(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let callback = cx.argument::<JsFunction>(0)?.root(&mut cx);
let queue = cx.queue();
std::thread::spawn(move || {
let result = // compute the result...
queue.send(move |mut cx| {
let callback = callback.into_inner(&mut cx);
let this = cx.undefined();
let args = match result {
Ok(n) => vec![
cx.null().upcast::<JsValue>(),
cx.number(result).upcast()
],
Err(msg) => vec![
cx.error(msg).upcast()
]
};
callback.call(&mut cx, this, args)?;
Ok(())
});
});
Ok(cx.undefined())
}
```
#### Deprecated: Event Handler API
The Event Handler API (`neon::event::EventHandler`) is deprecated and should be replaced by the Event Queue API.
_Rationale_: The Event Handler API had multiple issues with safety, memory leaks, and ergonomics ([1](https://github.com/neon-bindings/neon/issues/551), [2](https://github.com/neon-bindings/rfcs/issues/31)).
**Before:**
```rust
pub fn start_task(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let callback = cx.argument::<JsFunction>(0)?;
let handler = EventHandler::new(callback);
thread::spawn(move || {
let result = // compute the result...
handler.schedule(move |cx| {
vec![cx.number(result).upcast()]
});
});
Ok(cx.undefined())
}
```
**After:**
```rust
pub fn start_task(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let callback = cx.argument::<JsFunction>(0)?.root(&mut cx);
let queue = cx.queue();
std::thread::spawn(move || {
let result = // compute the result...
queue.send(move |mut cx| {
let callback = callback.into_inner(&mut cx);
let this = cx.undefined();
let args = vec![
cx.null().upcast::<JsValue>(),
cx.number(result).upcast()
];
callback.call(&mut cx, this, args)?;
Ok(())
});
});
Ok(cx.undefined())
}
```

View File

@ -13,15 +13,22 @@ 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/primitives) and our [API docs](https://neon-bindings.com/api/neon/).
See our [Neon fundamentals docs](https://neon-bindings.com/docs/intro) and our [API docs](https://docs.rs/neon/latest/neon).
# N-API Migration Guide
We've ported Neon to a new backend based on [N-API](https://nodejs.org/api/n-api.html), which will be the basis for Neon 1.0.
**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!
# Platform Support
@ -33,11 +40,11 @@ See our [Neon fundamentals docs](https://neon-bindings.com/docs/primitives) and
### 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,68 @@
# 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
* Added `JsDate` to N-API backend (https://github.com/neon-bindings/neon/pull/639)
* Implement `JsBuffer::unitialized` for N-API backend (https://github.com/neon-bindings/neon/pull/664)
### Fixes
* Do not panic if a `Root` is leaked after the event loop has stopped (https://github.com/neon-bindings/neon/pull/677)
* Stubs for features that will not be implemented in the N-API backend are removed (https://github.com/neon-bindings/neon/pull/663)
* Fix doc URL link (https://github.com/neon-bindings/neon/pull/663)
# Version 0.7.0
## N-API
### Version Selection
Neon supports a large number of different Node versions which may have different N-API requirements. Neon now supports selecting the minimum required N-API version required by a module. For example, for N-API Version 4:
```toml
neon = { version = "0.7", default-features = false, features = ["napi-4"] }
```
If the Neon module is loaded in an older version of Node that does not support that N-API version, a `panic` message will inform the user.
### Threadsafe Functions
A prerelease version of `EventQueue` for calling into the main JavaScript thread from Rust threads can be enabled with the `event-queue-api` feature flag. The API is considered unstable and may change in the future until the [RFC](https://github.com/neon-bindings/rfcs/pull/32) is merged.
# Version 0.6.0
The `cx.try_catch(..)` API has been updated to return `T: Sized` instead of `T: Value` (https://github.com/neon-bindings/neon/pull/631). This API is strictly more powerful and allows users to return both JavaScript and Rust values from `try_catch` closures.

26
cli/package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "neon-cli",
"version": "0.6.0",
"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.6.0",
"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.6.0"
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.6.0", path = "../neon-sys", optional = true }
neon-sys = { version = "=0.8.2", path = "../neon-sys", optional = true }

View File

@ -94,7 +94,9 @@ impl Setup {
}
fn absolute_output_file(&self) -> PathBuf {
let output_file = self.output_file.clone()
let output_file = self
.output_file
.clone()
.unwrap_or_else(|| PathBuf::from("index.node"));
// Don't prepend `output_dir` if `output_file` is absolute
@ -145,9 +147,7 @@ fn test_absolute_output_file_absolute_file() {
#[test]
fn test_absolute_output_file_absolute_dir() {
let expected = PathBuf::from("/tmp/index.node");
let actual = Setup::options()
.output_dir("/tmp")
.absolute_output_file();
let actual = Setup::options().output_dir("/tmp").absolute_output_file();
assert_eq!(actual, expected);
}
@ -155,9 +155,7 @@ fn test_absolute_output_file_absolute_dir() {
#[test]
fn test_absolute_output_file_relative_dir() {
let expected = manifest_dir().join("lib").join("index.node");
let actual = Setup::options()
.output_dir("lib")
.absolute_output_file();
let actual = Setup::options().output_dir("lib").absolute_output_file();
assert_eq!(actual, expected);
}

View File

@ -1,6 +1,6 @@
[package]
name = "neon-macros"
version = "0.6.0"
version = "0.8.2"
authors = ["Dave Herman <david.herman@gmail.com>"]
description = "Procedural macros supporting Neon"
repository = "https://github.com/neon-bindings/neon"
@ -16,8 +16,3 @@ napi = []
[dependencies]
quote = "1"
syn = { version = "1", features = ["full"] }
[dev-dependencies.neon]
version = "*"
path = "../.."
features = ["proc-macros"]

View File

@ -14,18 +14,19 @@ 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.
///
/// ```no_run
/// # use neon::prelude::*;
/// ```ignore
/// #[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)?;
///
///
/// Ok(())
/// }
/// ```

View File

@ -23,7 +23,7 @@ pub(crate) fn main(
::std::mem::transmute(m),
#name,
);
m
}

View File

@ -1,6 +1,6 @@
[package]
name = "neon-runtime"
version = "0.6.0"
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"
@ -10,7 +10,7 @@ edition = "2018"
[dependencies]
cfg-if = "1.0.0"
libloading = { version = "0.6.5", optional = true }
neon-sys = { version = "=0.6.0", path = "../neon-sys", optional = true }
neon-sys = { version = "=0.8.2", path = "../neon-sys", optional = true }
smallvec = "1.4.2"
[dev-dependencies]
@ -19,6 +19,12 @@ nodejs-sys = "0.7.0" # Not strictly needed; just here for easy manual copying
[features]
default = []
napi = ["libloading"]
napi-2 = ["napi"]
napi-3 = ["napi-2"]
napi-4 = ["napi-3"]
napi-5 = ["napi-4"]
napi-6 = ["napi-5"]
napi-experimental = ["napi-6"]
docs-only = ["neon-sys/docs-only"]
[package.metadata.docs.rs]

View File

@ -1,5 +1,7 @@
#[cfg(all(not(feature = "neon-sys"), not(feature = "napi")))]
compile_error!("The Neon runtime must have at least one of the `neon-sys` or `napi` backends enabled.");
compile_error!(
"The Neon runtime must have at least one of the `neon-sys` or `napi` backends enabled."
);
use cfg_if::cfg_if;

View File

@ -1,19 +1,19 @@
pub mod raw;
pub mod call;
pub mod scope;
pub mod object;
pub mod array;
pub mod string;
pub mod primitive;
pub mod error;
pub mod arraybuffer;
pub mod buffer;
pub mod tag;
pub mod module;
pub mod mem;
pub mod fun;
pub mod convert;
pub mod call;
pub mod class;
pub mod task;
pub mod convert;
pub mod error;
pub mod fun;
pub mod handler;
pub mod mem;
pub mod module;
pub mod object;
pub mod primitive;
pub mod raw;
pub mod scope;
pub mod string;
pub mod tag;
pub mod task;
pub mod try_catch;

View File

@ -1,3 +1,5 @@
//! Fundamental definitions for mapping to the V8 memory space.
pub use neon_sys::{Local, FunctionCallbackInfo, Isolate, HandleScope, EscapableHandleScope, InheritedHandleScope};
pub use neon_sys::{
EscapableHandleScope, FunctionCallbackInfo, HandleScope, InheritedHandleScope, Isolate, Local,
};

View File

@ -1,15 +1,23 @@
//! Facilities for working with `v8::HandleScope`s and `v8::EscapableHandleScope`s.
use crate::raw::{HandleScope, EscapableHandleScope, InheritedHandleScope, Isolate};
use crate::raw::{EscapableHandleScope, HandleScope, InheritedHandleScope, Isolate};
pub trait Root {
/// # Safety
/// Allocates an uninitialized scope. See `enter` and `exit`.
unsafe fn allocate() -> Self;
/// # Safety
/// Must be called exactly once after creating a `Root` and before usage
unsafe fn enter(&mut self, _: Isolate);
/// # Safety
/// Must be called exactly once, if and only if `enter` succeeds
unsafe fn exit(&mut self, _: Isolate);
}
impl Root for HandleScope {
unsafe fn allocate() -> Self { HandleScope::new() }
unsafe fn allocate() -> Self {
HandleScope::new()
}
unsafe fn enter(&mut self, isolate: Isolate) {
enter(self, isolate)
}
@ -19,7 +27,9 @@ impl Root for HandleScope {
}
impl Root for EscapableHandleScope {
unsafe fn allocate() -> Self { EscapableHandleScope::new() }
unsafe fn allocate() -> Self {
EscapableHandleScope::new()
}
unsafe fn enter(&mut self, isolate: Isolate) {
enter_escapable(self, isolate)
}
@ -29,9 +39,11 @@ impl Root for EscapableHandleScope {
}
impl Root for InheritedHandleScope {
unsafe fn allocate() -> Self { InheritedHandleScope }
unsafe fn enter(&mut self, _: Isolate) { }
unsafe fn exit(&mut self, _: Isolate) { }
unsafe fn allocate() -> Self {
InheritedHandleScope
}
unsafe fn enter(&mut self, _: Isolate) {}
unsafe fn exit(&mut self, _: Isolate) {}
}
/// Mutates the `out` argument provided to refer to the newly escaped `v8::Local` value.

View File

@ -4,7 +4,7 @@ use crate::raw::{Env, Local};
use crate::napi::bindings as napi;
pub unsafe extern "C" fn new(out: &mut Local, env: Env, length: u32) {
pub unsafe fn new(out: &mut Local, env: Env, length: u32) {
assert_eq!(
napi::create_array_with_length(env, length as usize, out as *mut _),
napi::Status::Ok,
@ -16,7 +16,7 @@ pub unsafe extern "C" fn new(out: &mut Local, env: Env, length: u32) {
/// # Panics
/// This function panics if `array` is not an Array, or if a previous n-api call caused a pending
/// exception.
pub unsafe extern "C" fn len(env: Env, array: Local) -> u32 {
pub unsafe fn len(env: Env, array: Local) -> u32 {
let mut len = 0;
assert_eq!(
napi::get_array_length(env, array, &mut len as *mut _),

View File

@ -1,16 +1,17 @@
use crate::raw::{Env, Local};
use std::mem::MaybeUninit;
use std::os::raw::c_void;
use std::ptr::null_mut;
use crate::napi::bindings as napi;
pub unsafe extern "C" fn new(out: &mut Local, env: Env, size: u32) -> bool {
pub unsafe fn new(out: &mut Local, env: Env, size: u32) -> bool {
let status = napi::create_arraybuffer(env, size as usize, null_mut(), out as *mut _);
status == napi::Status::Ok
}
pub unsafe extern "C" fn data<'a, 'b>(env: Env, base_out: &'a mut *mut c_void, obj: Local) -> usize {
pub unsafe fn data(env: Env, base_out: &mut *mut c_void, obj: Local) -> usize {
let mut size = 0;
assert_eq!(
napi::get_arraybuffer_info(env, obj, base_out as *mut _, &mut size as *mut _),
@ -18,3 +19,32 @@ pub unsafe extern "C" fn data<'a, 'b>(env: Env, base_out: &'a mut *mut c_void, o
);
size
}
pub unsafe fn new_external<T>(env: Env, data: T) -> Local
where
T: AsMut<[u8]> + Send,
{
// Safety: Boxing could move the data; must box before grabbing a raw pointer
let mut data = Box::new(data);
let buf = data.as_mut().as_mut();
let length = buf.len();
let mut result = MaybeUninit::uninit();
assert_eq!(
napi::create_external_arraybuffer(
env,
buf.as_mut_ptr() as *mut _,
length,
Some(drop_external::<T>),
Box::into_raw(data) as *mut _,
result.as_mut_ptr(),
),
napi::Status::Ok,
);
result.assume_init()
}
unsafe extern "C" fn drop_external<T>(_env: Env, _data: *mut c_void, hint: *mut c_void) {
Box::<T>::from_raw(hint as *mut _);
}

View File

@ -1,204 +1,327 @@
#[cfg(windows)]
use libloading::os::windows::Library;
#[cfg(not(windows))]
use libloading::os::unix::Library;
#![allow(clippy::too_many_arguments)]
use std::os::raw::{c_char, c_void};
use super::types::*;
mod napi1 {
use super::super::types::*;
use std::os::raw::{c_char, c_void};
generate!(extern "C" {
fn get_undefined(env: Env, result: *mut Value) -> Status;
generate!(
extern "C" {
fn get_undefined(env: Env, result: *mut Value) -> Status;
fn get_null(env: Env, result: *mut Value) -> Status;
fn get_null(env: Env, result: *mut Value) -> Status;
fn get_global(env: Env, result: *mut Value) -> Status;
fn get_global(env: Env, result: *mut Value) -> Status;
fn get_boolean(env: Env, value: bool, result: *mut Value) -> Status;
fn get_boolean(env: Env, value: bool, result: *mut Value) -> Status;
fn create_double(env: Env, value: f64, result: *mut Value) -> Status;
fn create_double(env: Env, value: f64, result: *mut Value) -> Status;
fn create_object(env: Env, result: *mut Value) -> Status;
fn create_object(env: Env, result: *mut Value) -> Status;
fn get_value_bool(env: Env, value: Value, result: *mut bool) -> Status;
fn get_value_bool(env: Env, value: Value, result: *mut bool) -> Status;
fn get_value_double(env: Env, value: Value, result: *mut f64) -> Status;
fn get_value_double(env: Env, value: Value, result: *mut f64) -> Status;
fn create_array_with_length(env: Env, length: usize, result: *mut Value) -> Status;
fn create_array_with_length(env: Env, length: usize, result: *mut Value) -> Status;
fn get_array_length(env: Env, value: Value, result: *mut u32)-> Status;
fn get_array_length(env: Env, value: Value, result: *mut u32) -> Status;
fn get_new_target(env: Env, cbinfo: CallbackInfo, result: *mut Value) -> Status;
fn get_new_target(env: Env, cbinfo: CallbackInfo, result: *mut Value) -> Status;
fn coerce_to_object(env: Env, value: Value, result: *mut Value) -> Status;
fn coerce_to_object(env: Env, value: Value, result: *mut Value) -> Status;
fn coerce_to_string(env: Env, value: Value, result: *mut Value) -> Status;
fn coerce_to_string(env: Env, value: Value, result: *mut Value) -> Status;
fn throw(env: Env, error: Value) -> Status;
fn throw(env: Env, error: Value) -> Status;
fn create_error(env: Env, code: Value, msg: Value, result: *mut Value) -> Status;
fn create_error(env: Env, code: Value, msg: Value, result: *mut Value) -> Status;
fn get_and_clear_last_exception(env: Env, result: *mut Value) -> Status;
fn get_and_clear_last_exception(env: Env, result: *mut Value) -> Status;
fn is_exception_pending(env: Env, result: *mut bool) -> Status;
fn is_exception_pending(env: Env, result: *mut bool) -> Status;
fn get_value_external(env: Env, value: Value, result: *mut *mut c_void) -> Status;
fn get_value_external(env: Env, value: Value, result: *mut *mut c_void) -> Status;
fn typeof_value(env: Env, value: Value, result: *mut ValueType) -> Status;
fn typeof_value(env: Env, value: Value, result: *mut ValueType) -> Status;
fn close_escapable_handle_scope(env: Env, scope: EscapableHandleScope) -> Status;
fn close_escapable_handle_scope(env: Env, scope: EscapableHandleScope) -> Status;
fn open_escapable_handle_scope(env: Env, result: *mut EscapableHandleScope) -> Status;
fn open_escapable_handle_scope(env: Env, result: *mut EscapableHandleScope) -> Status;
fn open_handle_scope(env: Env, result: *mut HandleScope) -> Status;
fn open_handle_scope(env: Env, result: *mut HandleScope) -> Status;
fn close_handle_scope(env: Env, scope: HandleScope) -> Status;
fn close_handle_scope(env: Env, scope: HandleScope) -> Status;
fn is_arraybuffer(env: Env, value: Value, result: *mut bool) -> Status;
fn is_buffer(env: Env, value: Value, result: *mut bool) -> Status;
fn is_error(env: Env, value: Value, result: *mut bool) -> Status;
fn is_array(env: Env, value: Value, result: *mut bool) -> Status;
fn is_arraybuffer(env: Env, value: Value, result: *mut bool) -> Status;
fn is_buffer(env: Env, value: Value, result: *mut bool) -> Status;
fn is_error(env: Env, value: Value, result: *mut bool) -> Status;
fn is_array(env: Env, value: Value, result: *mut bool) -> Status;
fn get_value_string_utf8(
env: Env,
value: Value,
buf: *mut c_char,
bufsize: usize,
result: *mut usize,
) -> Status;
fn get_value_string_utf8(
env: Env,
value: Value,
buf: *mut c_char,
bufsize: usize,
result: *mut usize,
) -> Status;
fn create_type_error(
env: Env,
code: Value,
msg: Value,
result: *mut Value,
) -> Status;
fn create_type_error(env: Env, code: Value, msg: Value, result: *mut Value) -> Status;
fn create_range_error(
env: Env,
code: Value,
msg: Value,
result: *mut Value,
) -> Status;
fn create_range_error(env: Env, code: Value, msg: Value, result: *mut Value) -> Status;
fn create_string_utf8(
env: Env,
str: *const c_char,
length: usize,
result: *mut Value,
) -> Status;
fn create_string_utf8(
env: Env,
str: *const c_char,
length: usize,
result: *mut Value,
) -> Status;
fn create_arraybuffer(
env: Env,
byte_length: usize,
data: *mut *mut c_void,
result: *mut Value,
) -> Status;
fn create_arraybuffer(
env: Env,
byte_length: usize,
data: *mut *mut c_void,
result: *mut Value,
) -> Status;
fn get_arraybuffer_info(
env: Env,
arraybuffer: Value,
data: *mut *mut c_void,
byte_length: *mut usize,
) -> Status;
fn get_arraybuffer_info(
env: Env,
arraybuffer: Value,
data: *mut *mut c_void,
byte_length: *mut usize,
) -> Status;
fn create_buffer(
env: Env,
length: usize,
data: *mut *mut c_void,
result: *mut Value,
) -> Status;
fn create_buffer(
env: Env,
length: usize,
data: *mut *mut c_void,
result: *mut Value,
) -> Status;
fn get_buffer_info(
env: Env,
value: Value,
data: *mut *mut c_void,
length: *mut usize,
) -> Status;
fn get_buffer_info(
env: Env,
value: Value,
data: *mut *mut c_void,
length: *mut usize,
) -> Status;
fn get_cb_info(
env: Env,
cbinfo: CallbackInfo,
argc: *mut usize,
argv: *mut Value,
this_arg: *mut Value,
data: *mut *mut c_void,
) -> Status;
fn get_cb_info(
env: Env,
cbinfo: CallbackInfo,
argc: *mut usize,
argv: *mut Value,
this_arg: *mut Value,
data: *mut *mut c_void,
) -> Status;
fn create_external(
env: Env,
data: *mut c_void,
finalize_cb: Finalize,
finalize_hint: *mut c_void,
result: *mut Value,
) -> Status;
fn create_external(
env: Env,
data: *mut c_void,
finalize_cb: Finalize,
finalize_hint: *mut c_void,
result: *mut Value,
) -> Status;
fn new_instance(
env: Env,
constructor: Value,
argc: usize,
argv: *const Value,
result: *mut Value,
) -> Status;
fn new_instance(
env: Env,
constructor: Value,
argc: usize,
argv: *const Value,
result: *mut Value,
) -> Status;
fn call_function(
env: Env,
recv: Value,
func: Value,
argc: usize,
argv: *const Value,
result: *mut Value,
) -> Status;
fn call_function(
env: Env,
recv: Value,
func: Value,
argc: usize,
argv: *const Value,
result: *mut Value,
) -> Status;
fn create_function(
env: Env,
utf8name: *const c_char,
length: usize,
cb: Callback,
data: *mut c_void,
result: *mut Value,
) -> Status;
fn create_function(
env: Env,
utf8name: *const c_char,
length: usize,
cb: Callback,
data: *mut c_void,
result: *mut Value,
) -> Status;
fn set_property(
env: Env,
object: Value,
key: Value,
value: Value,
) -> Status;
fn set_property(env: Env, object: Value, key: Value, value: Value) -> Status;
fn get_property(
env: Env,
object: Value,
key: Value,
result: *mut Value,
) -> Status;
fn get_property(env: Env, object: Value, key: Value, result: *mut Value) -> Status;
fn set_element(
env: Env,
object: Value,
index: u32,
value: Value,
) -> Status;
fn set_element(env: Env, object: Value, index: u32, value: Value) -> Status;
fn get_element(
env: Env,
object: Value,
index: u32,
result: *mut Value,
) -> Status;
fn get_element(env: Env, object: Value, index: u32, result: *mut Value) -> Status;
fn get_all_property_names(
env: Env,
object: Value,
key_mode: KeyCollectionMode,
key_filter: KeyFilter,
key_conversion: KeyConversion,
result: *mut Value,
) -> Status;
fn escape_handle(
env: Env,
scope: EscapableHandleScope,
escapee: Value,
result: *mut Value,
) -> Status;
fn escape_handle(
env: Env,
scope: EscapableHandleScope,
escapee: Value,
result: *mut Value,
) -> Status;
});
fn create_reference(
env: Env,
value: Value,
initial_ref_count: u32,
result: *mut Ref,
) -> Status;
fn reference_ref(env: Env, reference: Ref, result: *mut u32) -> Status;
fn reference_unref(env: Env, reference: Ref, result: *mut u32) -> Status;
fn get_reference_value(env: Env, reference: Ref, result: *mut Value) -> Status;
fn strict_equals(env: Env, lhs: Value, rhs: Value, result: *mut bool) -> Status;
fn create_external_arraybuffer(
env: Env,
data: *mut c_void,
length: usize,
finalize_cb: Finalize,
finalize_hint: *mut c_void,
result: *mut Value,
) -> Status;
fn create_external_buffer(
env: Env,
length: usize,
data: *mut c_void,
finalize_cb: Finalize,
finalize_hint: *mut c_void,
result: *mut Value,
) -> Status;
fn run_script(env: Env, script: Value, result: *mut Value) -> Status;
}
);
}
#[cfg(feature = "napi-4")]
mod napi4 {
use super::super::types::*;
use std::os::raw::c_void;
generate!(
extern "C" {
fn create_threadsafe_function(
env: Env,
func: Value,
async_resource: Value,
async_resource_name: Value,
max_queue_size: usize,
initial_thread_count: usize,
thread_finalize_data: *mut c_void,
thread_finalize_cb: Finalize,
context: *mut c_void,
call_js_cb: ThreadsafeFunctionCallJs,
result: *mut ThreadsafeFunction,
) -> Status;
fn call_threadsafe_function(
func: ThreadsafeFunction,
data: *mut c_void,
is_blocking: ThreadsafeFunctionCallMode,
) -> Status;
fn release_threadsafe_function(
func: ThreadsafeFunction,
mode: ThreadsafeFunctionReleaseMode,
) -> Status;
fn ref_threadsafe_function(env: Env, func: ThreadsafeFunction) -> Status;
fn unref_threadsafe_function(env: Env, func: ThreadsafeFunction) -> Status;
}
);
}
#[cfg(feature = "napi-5")]
mod napi5 {
use super::super::types::*;
generate!(
extern "C" {
fn create_date(env: Env, value: f64, result: *mut Value) -> Status;
fn get_date_value(env: Env, value: Value, result: *mut f64) -> Status;
fn is_date(env: Env, value: Value, result: *mut bool) -> Status;
}
);
}
#[cfg(feature = "napi-6")]
mod napi6 {
use super::super::types::*;
use std::os::raw::c_void;
generate!(
extern "C" {
fn get_all_property_names(
env: Env,
object: Value,
key_mode: KeyCollectionMode,
key_filter: KeyFilter,
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 = "napi-4")]
pub(crate) use napi4::*;
#[cfg(feature = "napi-5")]
pub(crate) use napi5::*;
#[cfg(feature = "napi-6")]
pub(crate) use napi6::*;
use super::{Env, Status};
// This symbol is loaded separately because it is a prerequisite
unsafe fn get_version(host: &libloading::Library, env: Env) -> Result<u32, libloading::Error> {
let get_version = host.get::<fn(Env, *mut u32) -> Status>(b"napi_get_version")?;
let mut version = 0;
assert_eq!(get_version(env, &mut version as *mut _), Status::Ok,);
Ok(version)
}
pub(crate) unsafe fn load(env: Env) -> Result<(), libloading::Error> {
#[cfg(not(windows))]
let host = libloading::os::unix::Library::this().into();
#[cfg(windows)]
let host = libloading::os::windows::Library::this()?.into();
// This never fail since `get_version` is in N-API Version 1 and the module will fail
// with `Error: Module did not self-register` if N-API does not exist.
let version = get_version(&host, env).expect("Failed to find N-API version");
napi1::load(&host, version, 1)?;
#[cfg(feature = "napi-4")]
napi4::load(&host, version, 4)?;
#[cfg(feature = "napi-5")]
napi5::load(&host, version, 5)?;
#[cfg(feature = "napi-6")]
napi6::load(&host, version, 6)?;
Ok(())
}

View File

@ -77,12 +77,17 @@ macro_rules! napi_name {
///
/// // Load N-API symbols from the host process
/// // # Safety: Must only be called once
/// pub(crate) unsafe fn load() -> Result<(), libloading::Error> {
/// // Load the host process as a library
/// let host = Library::this();
/// #[cfg(windows)]
/// // On Windows, the host process might not be a library
/// let host = host?;
/// pub(crate) unsafe fn load(
/// host: &libloading::Library,
/// actual_napi_version: u32,
/// expected_napi_version: u32,
/// ) -> Result<(), libloading::Error> {
/// assert!(
/// actual_napi_version >= expected_napi_version,
/// "Minimum required N-API version {}, found {}.",
/// expected_napi_version,
/// actual_napi_version,
/// );
///
/// NAPI = Napi {
/// // Load each N-API symbol
@ -131,10 +136,17 @@ macro_rules! generate {
}
};
pub(crate) unsafe fn load() -> Result<(), libloading::Error> {
let host = Library::this();
#[cfg(windows)]
let host = host?;
pub(crate) unsafe fn load(
host: &libloading::Library,
actual_napi_version: u32,
expected_napi_version: u32,
) -> Result<(), libloading::Error> {
assert!(
actual_napi_version >= expected_napi_version,
"Minimum required N-API version {}, found {}.",
expected_napi_version,
actual_napi_version,
);
NAPI = Napi {
$(
@ -159,16 +171,15 @@ use std::sync::Once;
pub(crate) use functions::*;
pub(crate) use types::*;
mod types;
mod functions;
mod types;
static SETUP: Once = Once::new();
/// Loads N-API symbols from host process.
/// Must be called at least once before using any functions in `neon-runtime` or
/// they will panic.
pub fn setup() {
SETUP.call_once(|| unsafe {
load().expect("Failed to load N-API symbols");
});
/// Safety: `env` must be a valid `napi_env` for the current thread
pub unsafe fn setup(env: Env) {
SETUP.call_once(|| load(env).expect("Failed to load N-API symbols"));
}

View File

@ -22,6 +22,8 @@ pub struct CallbackInfo__ {
_unused: [u8; 0],
}
pub type CallbackInfo = *mut CallbackInfo__;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct EscapableHandleScope__ {
@ -34,26 +36,41 @@ pub type EscapableHandleScope = *mut EscapableHandleScope__;
pub struct HandleScope__ {
_unused: [u8; 0],
}
pub type HandleScope = *mut HandleScope__;
pub type CallbackInfo = *mut CallbackInfo__;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Ref__ {
_unused: [u8; 0],
}
pub(crate) type Callback = Option<
unsafe extern "C" fn(env: Env, info: CallbackInfo) -> Value,
>;
pub type Ref = *mut Ref__;
pub(crate) type Finalize = Option<
unsafe extern "C" fn(
env: Env,
finalize_data: *mut c_void,
finalize_hint: *mut c_void,
),
#[cfg(feature = "napi-4")]
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ThreadsafeFunction__ {
_unused: [u8; 0],
}
#[cfg(feature = "napi-4")]
pub type ThreadsafeFunction = *mut ThreadsafeFunction__;
pub(crate) type Callback = Option<unsafe extern "C" fn(env: Env, info: CallbackInfo) -> Value>;
pub(crate) type Finalize =
Option<unsafe extern "C" fn(env: Env, finalize_data: *mut c_void, finalize_hint: *mut c_void)>;
#[cfg(feature = "napi-4")]
pub type ThreadsafeFunctionCallJs = Option<
unsafe extern "C" fn(env: Env, js_callback: Value, context: *mut c_void, data: *mut c_void),
>;
#[allow(dead_code)]
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) enum Status {
pub enum Status {
Ok = 0,
InvalidArg = 1,
ObjectExpected = 2,
@ -110,6 +127,23 @@ pub enum KeyConversion {
NumbersToStrings = 1,
}
#[cfg(feature = "napi-4")]
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ThreadsafeFunctionCallMode {
NonBlocking = 0,
Blocking = 1,
}
#[cfg(feature = "napi-4")]
#[allow(dead_code)]
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ThreadsafeFunctionReleaseMode {
Release = 0,
Abort = 1,
}
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) struct KeyFilter(pub ::std::os::raw::c_uint);

View File

@ -1,10 +1,11 @@
use crate::raw::{Env, Local};
use std::mem::MaybeUninit;
use std::os::raw::c_void;
use std::ptr::null_mut;
use crate::napi::bindings as napi;
pub unsafe extern "C" fn new(env: Env, out: &mut Local, size: u32) -> bool {
pub unsafe fn new(env: Env, out: &mut Local, size: u32) -> bool {
let mut bytes = null_mut();
let status = napi::create_buffer(env, size as usize, &mut bytes as *mut _, out as *mut _);
if status == napi::Status::Ok {
@ -17,9 +18,38 @@ pub unsafe extern "C" fn new(env: Env, out: &mut Local, size: u32) -> bool {
}
}
pub unsafe extern "C" fn uninitialized(_out: &mut Local, _size: u32) -> bool { unimplemented!() }
pub unsafe fn uninitialized(env: Env, out: &mut Local, size: u32) -> bool {
let mut bytes = null_mut();
let status = napi::create_buffer(env, size as usize, &mut bytes as *mut _, out as *mut _);
status == napi::Status::Ok
}
pub unsafe extern "C" fn data<'a, 'b>(env: Env, base_out: &'a mut *mut c_void, obj: Local) -> usize {
pub unsafe fn new_external<T>(env: Env, data: T) -> Local
where
T: AsMut<[u8]> + Send,
{
// Safety: Boxing could move the data; must box before grabbing a raw pointer
let mut data = Box::new(data);
let buf = data.as_mut().as_mut();
let length = buf.len();
let mut result = MaybeUninit::uninit();
assert_eq!(
napi::create_external_buffer(
env,
length,
buf.as_mut_ptr() as *mut _,
Some(drop_external::<T>),
Box::into_raw(data) as *mut _,
result.as_mut_ptr(),
),
napi::Status::Ok,
);
result.assume_init()
}
pub unsafe fn data(env: Env, base_out: &mut *mut c_void, obj: Local) -> usize {
let mut size = 0;
assert_eq!(
napi::get_buffer_info(env, obj, base_out as *mut _, &mut size as *mut _),
@ -27,3 +57,7 @@ pub unsafe extern "C" fn data<'a, 'b>(env: Env, base_out: &'a mut *mut c_void, o
);
size
}
unsafe extern "C" fn drop_external<T>(_env: Env, _data: *mut c_void, hint: *mut c_void) {
Box::<T>::from_raw(hint as *mut _);
}

View File

@ -4,41 +4,28 @@ use std::ptr::null_mut;
use smallvec::{smallvec, SmallVec};
use crate::raw::{FunctionCallbackInfo, Env, Local};
use crate::napi::bindings as napi;
use crate::raw::{Env, FunctionCallbackInfo, Local};
#[repr(C)]
pub struct CCallback {
pub static_callback: *mut c_void,
pub dynamic_callback: *mut c_void
pub dynamic_callback: *mut c_void,
}
impl Default for CCallback {
fn default() -> Self {
CCallback {
static_callback: null_mut(),
dynamic_callback: null_mut()
dynamic_callback: null_mut(),
}
}
}
pub unsafe extern "C" fn set_return(_info: FunctionCallbackInfo, _value: Local) {
}
pub unsafe extern "C" fn get_isolate(_info: FunctionCallbackInfo) -> Env { unimplemented!() }
// FIXME: Remove. This will never be implemented
pub unsafe extern "C" fn current_isolate() -> Env { panic!("current_isolate won't be implemented in n-api") }
pub unsafe extern "C" fn is_construct(env: Env, info: FunctionCallbackInfo) -> bool {
pub unsafe fn is_construct(env: Env, info: FunctionCallbackInfo) -> bool {
let mut target: MaybeUninit<Local> = MaybeUninit::zeroed();
let status = napi::get_new_target(
env,
info,
target.as_mut_ptr()
);
let status = napi::get_new_target(env, info, target.as_mut_ptr());
assert_eq!(status, napi::Status::Ok);
@ -51,21 +38,14 @@ pub unsafe extern "C" fn is_construct(env: Env, info: FunctionCallbackInfo) -> b
!target.is_null()
}
pub unsafe extern "C" fn this(env: Env, info: FunctionCallbackInfo, out: &mut Local) {
let status = napi::get_cb_info(
env,
info,
null_mut(),
null_mut(),
out as *mut _,
null_mut(),
);
pub unsafe fn this(env: Env, info: FunctionCallbackInfo, out: &mut Local) {
let status = napi::get_cb_info(env, info, null_mut(), null_mut(), out as *mut _, null_mut());
assert_eq!(status, napi::Status::Ok);
}
/// Mutates the `out` argument provided to refer to the associated data value of the
/// `napi_callback_info`.
pub unsafe extern "C" fn data(env: Env, info: FunctionCallbackInfo, out: &mut *mut c_void) {
pub unsafe fn data(env: Env, info: FunctionCallbackInfo, out: &mut *mut c_void) {
let mut data = null_mut();
let status = napi::get_cb_info(
env,
@ -81,7 +61,7 @@ pub unsafe extern "C" fn data(env: Env, info: FunctionCallbackInfo, out: &mut *m
}
/// Gets the number of arguments passed to the function.
pub unsafe extern "C" fn len(env: Env, info: FunctionCallbackInfo) -> i32 {
pub unsafe fn len(env: Env, info: FunctionCallbackInfo) -> i32 {
let mut argc = 0usize;
let status = napi::get_cb_info(
env,
@ -96,7 +76,7 @@ pub unsafe extern "C" fn len(env: Env, info: FunctionCallbackInfo) -> i32 {
}
/// Returns the function arguments as a `SmallVec<[Local; 8]>`
pub unsafe extern "C" fn argv(env: Env, info: FunctionCallbackInfo) -> SmallVec<[Local; 8]> {
pub unsafe fn argv(env: Env, info: FunctionCallbackInfo) -> SmallVec<[Local; 8]> {
let len = len(env, info);
let mut args = smallvec![null_mut(); len as usize];
let mut num_args = args.len();

View File

@ -1,39 +0,0 @@
use std::os::raw::c_void;
use crate::call::CCallback;
use crate::raw::{Env, Local};
pub unsafe extern "C" fn get_class_map(_isolate: Env) -> *mut c_void { unimplemented!() }
pub unsafe extern "C" fn set_class_map(_isolate: Env, _map: *mut c_void, _free_map: *mut c_void) { unimplemented!() }
pub unsafe extern "C" fn create_base(_isolate: Env,
_allocate: CCallback,
_construct: CCallback,
_call: CCallback,
_drop: extern "C" fn(*mut c_void)) -> *mut c_void { unimplemented!() }
pub unsafe extern "C" fn get_name<'a>(_base_out: &'a mut *mut u8, _isolate: Env, _metadata: *const c_void) -> usize { unimplemented!() }
pub unsafe extern "C" fn set_name(_isolate: Env, _metadata: *mut c_void, _name: *const u8, _byte_length: u32) -> bool { unimplemented!() }
pub unsafe extern "C" fn throw_call_error(_isolate: Env, _metadata: *mut c_void) { unimplemented!() }
pub unsafe extern "C" fn throw_this_error(_isolate: Env, _metadata: *mut c_void) { unimplemented!() }
pub unsafe extern "C" fn add_method(_isolate: Env, _metadata: *mut c_void, _name: *const u8, _byte_length: u32, _method: Local) -> bool { unimplemented!() }
pub unsafe extern "C" fn metadata_to_constructor(_out: &mut Local, _isolate: Env, _metadata: *mut c_void) -> bool { unimplemented!() }
// FIXME: get rid of all the "kernel" nomenclature
pub unsafe extern "C" fn get_allocate_kernel(_data: *mut c_void) -> *mut c_void { unimplemented!() }
pub unsafe extern "C" fn get_construct_kernel(_data: *mut c_void) -> *mut c_void { unimplemented!() }
pub unsafe extern "C" fn get_call_kernel(_data: *mut c_void) -> *mut c_void { unimplemented!() }
pub unsafe extern "C" fn constructor(_out: &mut Local, _ft: Local) -> bool { unimplemented!() }
pub unsafe extern "C" fn has_instance(_metadata: *mut c_void, _v: Local) -> bool { unimplemented!() }
pub unsafe extern "C" fn get_instance_internals(_obj: Local) -> *mut c_void { unimplemented!() }

View File

@ -2,13 +2,13 @@ use crate::napi::bindings as napi;
use crate::raw::{Env, Local};
/// This API is currently unused, see https://github.com/neon-bindings/neon/issues/572
pub unsafe extern "C" fn to_object(out: &mut Local, env: Env, value: Local) -> bool {
pub unsafe fn to_object(out: &mut Local, env: Env, value: Local) -> bool {
let status = napi::coerce_to_object(env, value, out as *mut _);
status == napi::Status::Ok
}
pub unsafe extern "C" fn to_string(out: &mut Local, env: Env, value: Local) -> bool {
pub unsafe fn to_string(out: &mut Local, env: Env, value: Local) -> bool {
let status = napi::coerce_to_string(env, value, out as *mut _);
status == napi::Status::Ok

View File

@ -0,0 +1,28 @@
use crate::napi::bindings as napi;
use crate::raw::{Env, Local};
use std::mem::MaybeUninit;
/// Create a new date object
///
/// # Safety
///
/// `env` is a raw pointer. Please ensure it points to a napi_env that is valid for the current context.
pub unsafe fn new_date(env: Env, value: f64) -> Local {
let mut local = MaybeUninit::zeroed();
let status = napi::create_date(env, value, local.as_mut_ptr());
assert_eq!(status, napi::Status::Ok);
local.assume_init()
}
/// Get the value of a date object
///
/// # Safety
///
/// `env` is a raw pointer. Please ensure it points to a napi_env that is valid for the current context.
/// `Local` must be an NAPI value associated with the given `Env`
pub unsafe fn value(env: Env, p: Local) -> f64 {
let mut value = 0.0;
let status = napi::get_date_value(env, p, &mut value as *mut _);
assert_eq!(status, napi::Status::Ok);
value
}

View File

@ -50,12 +50,7 @@ pub unsafe fn throw(env: Env, val: Local) {
pub unsafe fn new_error(env: Env, out: &mut Local, msg: Local) {
let mut result = MaybeUninit::uninit();
let status = napi::create_error(
env,
ptr::null_mut(),
msg,
result.as_mut_ptr(),
);
let status = napi::create_error(env, ptr::null_mut(), msg, result.as_mut_ptr());
assert_eq!(status, napi::Status::Ok);
@ -64,12 +59,7 @@ pub unsafe fn new_error(env: Env, out: &mut Local, msg: Local) {
pub unsafe fn new_type_error(env: Env, out: &mut Local, msg: Local) {
let mut result = MaybeUninit::uninit();
let status = napi::create_type_error(
env,
ptr::null_mut(),
msg,
result.as_mut_ptr(),
);
let status = napi::create_type_error(env, ptr::null_mut(), msg, result.as_mut_ptr());
assert_eq!(status, napi::Status::Ok);
@ -78,12 +68,7 @@ pub unsafe fn new_type_error(env: Env, out: &mut Local, msg: Local) {
pub unsafe fn new_range_error(env: Env, out: &mut Local, msg: Local) {
let mut result = MaybeUninit::uninit();
let status = napi::create_range_error(
env,
ptr::null_mut(),
msg,
result.as_mut_ptr(),
);
let status = napi::create_range_error(env, ptr::null_mut(), msg, result.as_mut_ptr());
assert_eq!(status, napi::Status::Ok);
@ -92,22 +77,12 @@ pub unsafe fn new_range_error(env: Env, out: &mut Local, msg: Local) {
pub unsafe fn throw_error_from_utf8(env: Env, msg: *const u8, len: i32) {
let mut out = MaybeUninit::uninit();
let status = napi::create_string_utf8(
env,
msg as *const i8,
len as usize,
out.as_mut_ptr(),
);
let status = napi::create_string_utf8(env, msg as *const _, len as usize, out.as_mut_ptr());
assert_eq!(status, napi::Status::Ok);
let mut err = MaybeUninit::uninit();
let status = napi::create_error(
env,
ptr::null_mut(),
out.assume_init(),
err.as_mut_ptr(),
);
let status = napi::create_error(env, ptr::null_mut(), out.assume_init(), err.as_mut_ptr());
assert_eq!(status, napi::Status::Ok);

View File

@ -1,7 +1,7 @@
use std::mem::MaybeUninit;
use crate::raw::{Env, Local};
use crate::napi::bindings as napi;
use crate::raw::{Env, Local};
/// `finalize_external` is invoked immediately before a `napi_external` is garbage collected
extern "C" fn finalize_external<T: Send + 'static>(
@ -25,16 +25,9 @@ extern "C" fn finalize_external<T: Send + 'static>(
/// module. Calling `deref` with an external created by another native module,
/// even another neon module, is undefined behavior.
/// https://github.com/neon-bindings/neon/issues/591
pub unsafe fn deref<T: Send + 'static>(
env: Env,
local: Local,
) -> Option<*const T> {
pub unsafe fn deref<T: Send + 'static>(env: Env, local: Local) -> Option<*const T> {
let mut result = MaybeUninit::uninit();
let status = napi::typeof_value(
env,
local,
result.as_mut_ptr(),
);
let status = napi::typeof_value(env, local, result.as_mut_ptr());
assert_eq!(status, napi::Status::Ok);
@ -49,11 +42,7 @@ pub unsafe fn deref<T: Send + 'static>(
}
let mut result = MaybeUninit::uninit();
let status = napi::get_value_external(
env,
local,
result.as_mut_ptr(),
);
let status = napi::get_value_external(env, local, result.as_mut_ptr());
assert_eq!(status, napi::Status::Ok);
@ -61,11 +50,7 @@ pub unsafe fn deref<T: Send + 'static>(
}
/// Creates a `napi_external` from a Rust type
pub unsafe fn create<T: Send + 'static>(
env: Env,
v: T,
finalizer: fn(Env, T),
) -> Local {
pub unsafe fn create<T: Send + 'static>(env: Env, v: T, finalizer: fn(Env, T)) -> Local {
let v = Box::new(v);
let mut result = MaybeUninit::uninit();

View File

@ -4,12 +4,12 @@ use std::os::raw::c_void;
use std::ptr::null;
use crate::call::CCallback;
use crate::raw::{Env, Local};
use crate::napi::bindings as napi;
use crate::raw::{Env, Local};
/// Mutates the `out` argument provided to refer to a newly created `v8::Function`. Returns
/// `false` if the value couldn't be created.
pub unsafe extern "C" fn new(out: &mut Local, env: Env, callback: CCallback) -> bool {
pub unsafe fn new(out: &mut Local, env: Env, callback: CCallback) -> bool {
let status = napi::create_function(
env,
null(),
@ -22,21 +22,37 @@ pub unsafe extern "C" fn new(out: &mut Local, env: Env, callback: CCallback) ->
status == napi::Status::Ok
}
pub unsafe extern "C" fn new_template(_out: &mut Local, _env: Env, _callback: CCallback) -> bool {
unimplemented!()
}
pub unsafe extern "C" fn get_dynamic_callback(_env: Env, data: *mut c_void) -> *mut c_void {
pub unsafe fn get_dynamic_callback(_env: Env, data: *mut c_void) -> *mut c_void {
data
}
pub unsafe extern "C" fn call(out: &mut Local, env: Env, fun: Local, this: Local, argc: i32, argv: *mut c_void) -> bool {
let status = napi::call_function(env, this, fun, argc as usize, argv as *const _, out as *mut _);
pub unsafe fn call(
out: &mut Local,
env: Env,
fun: Local,
this: Local,
argc: i32,
argv: *mut c_void,
) -> bool {
let status = napi::call_function(
env,
this,
fun,
argc as usize,
argv as *const _,
out as *mut _,
);
status == napi::Status::Ok
}
pub unsafe extern "C" fn construct(out: &mut Local, env: Env, fun: Local, argc: i32, argv: *mut c_void) -> bool {
pub unsafe fn construct(
out: &mut Local,
env: Env,
fun: Local,
argc: i32,
argv: *mut c_void,
) -> bool {
let status = napi::new_instance(env, fun, argc as usize, argv as *const _, out as *mut _);
status == napi::Status::Ok

View File

@ -1,7 +0,0 @@
use crate::raw::Local;
use std::os::raw::c_void;
pub unsafe extern "C" fn new(_isolate: *mut c_void, _this: Local, _callback: Local) -> *mut c_void { unimplemented!() }
pub unsafe extern "C" fn schedule(_thread_safe_cb: *mut c_void, _rust_callback: *mut c_void,
_complete: unsafe extern fn(Local, Local, *mut c_void)) { unimplemented!() }
pub unsafe extern "C" fn delete(_thread_safe_cb: *mut c_void) { unimplemented!() }

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

@ -1,3 +1,11 @@
use crate::raw::Local;
use crate::napi::bindings as napi;
use crate::raw::{Env, Local};
pub unsafe extern "C" fn same_handle(_h1: Local, _h2: Local) -> bool { unimplemented!() }
pub unsafe fn strict_equals(env: Env, lhs: Local, rhs: Local) -> bool {
let mut result = false;
assert_eq!(
napi::strict_equals(env, lhs, rhs, &mut result as *mut _),
napi::Status::Ok
);
result
}

View File

@ -2,20 +2,24 @@ pub mod array;
pub mod arraybuffer;
pub mod buffer;
pub mod call;
pub mod class;
pub mod convert;
#[cfg(feature = "napi-5")]
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;
pub mod raw;
pub mod reference;
pub mod scope;
pub mod string;
pub mod tag;
pub mod task;
pub mod handler;
#[cfg(feature = "napi-4")]
pub mod tsfn;
mod bindings;
pub use bindings::*;

View File

@ -4,13 +4,14 @@ use crate::napi::bindings as napi;
use crate::raw::{Env, Local};
/// Mutates the `out` argument to refer to a `napi_value` containing a newly created JavaScript Object.
pub unsafe extern "C" fn new(out: &mut Local, env: Env) {
pub unsafe fn new(out: &mut Local, env: Env) {
napi::create_object(env, out as *mut _);
}
#[cfg(feature = "napi-6")]
/// Mutates the `out` argument to refer to a `napi_value` containing the own property names of the
/// `object` as a JavaScript Array.
pub unsafe extern "C" fn get_own_property_names(out: &mut Local, env: Env, object: Local) -> bool {
pub unsafe fn get_own_property_names(out: &mut Local, env: Env, object: Local) -> bool {
let mut property_names = MaybeUninit::uninit();
if napi::get_all_property_names(
@ -20,7 +21,8 @@ pub unsafe extern "C" fn get_own_property_names(out: &mut Local, env: Env, objec
napi::KeyFilter::ALL_PROPERTIES | napi::KeyFilter::SKIP_SYMBOLS,
napi::KeyConversion::NumbersToStrings,
property_names.as_mut_ptr(),
) != napi::Status::Ok {
) != napi::Status::Ok
{
return false;
}
@ -29,13 +31,8 @@ pub unsafe extern "C" fn get_own_property_names(out: &mut Local, env: Env, objec
true
}
// Unused.
pub unsafe extern "C" fn get_isolate(_obj: Local) -> Env {
unimplemented!()
}
/// Mutate the `out` argument to refer to the value at `index` in the given `object`. Returns `false` if the value couldn't be retrieved.
pub unsafe extern "C" fn get_index(out: &mut Local, env: Env, object: Local, index: u32) -> bool {
pub unsafe fn get_index(out: &mut Local, env: Env, object: Local, index: u32) -> bool {
let status = napi::get_element(env, object, index, out as *mut _);
status == napi::Status::Ok
@ -48,7 +45,7 @@ pub unsafe extern "C" fn get_index(out: &mut Local, env: Env, object: Local, ind
/// see [discussion].
///
/// [discussion]: https://github.com/neon-bindings/neon/pull/458#discussion_r344827965
pub unsafe extern "C" fn set_index(out: &mut bool, env: Env, object: Local, index: u32, val: Local) -> bool {
pub unsafe fn set_index(out: &mut bool, env: Env, object: Local, index: u32, val: Local) -> bool {
let status = napi::set_element(env, object, index, val);
*out = status == napi::Status::Ok;
@ -56,21 +53,25 @@ pub unsafe extern "C" fn set_index(out: &mut bool, env: Env, object: Local, inde
}
/// Mutate the `out` argument to refer to the value at a named `key` in the given `object`. Returns `false` if the value couldn't be retrieved.
pub unsafe extern "C" fn get_string(env: Env, out: &mut Local, object: Local, key: *const u8, len: i32) -> bool {
pub unsafe fn get_string(
env: Env,
out: &mut Local,
object: Local,
key: *const u8,
len: i32,
) -> bool {
let mut key_val = MaybeUninit::uninit();
// Not using `crate::string::new()` because it requires a _reference_ to a Local,
// while we only have uninitialized memory.
if napi::create_string_utf8(env, key as *const i8, len as usize, key_val.as_mut_ptr())
if napi::create_string_utf8(env, key as *const _, len as usize, key_val.as_mut_ptr())
!= napi::Status::Ok
{
return false;
}
// Not using napi_get_named_property() because the `key` may not be null terminated.
if napi::get_property(env, object, key_val.assume_init(), out as *mut _)
!= napi::Status::Ok
{
if napi::get_property(env, object, key_val.assume_init(), out as *mut _) != napi::Status::Ok {
return false;
}
@ -83,21 +84,26 @@ pub unsafe extern "C" fn get_string(env: Env, out: &mut Local, object: Local, ke
/// see [discussion].
///
/// [discussion]: https://github.com/neon-bindings/neon/pull/458#discussion_r344827965
pub unsafe extern "C" fn set_string(env: Env, out: &mut bool, object: Local, key: *const u8, len: i32, val: Local) -> bool {
pub unsafe fn set_string(
env: Env,
out: &mut bool,
object: Local,
key: *const u8,
len: i32,
val: Local,
) -> bool {
let mut key_val = MaybeUninit::uninit();
*out = true;
if napi::create_string_utf8(env, key as *const i8, len as usize, key_val.as_mut_ptr())
if napi::create_string_utf8(env, key as *const _, len as usize, key_val.as_mut_ptr())
!= napi::Status::Ok
{
*out = false;
return false;
}
if napi::set_property(env, object, key_val.assume_init(), val)
!= napi::Status::Ok
{
if napi::set_property(env, object, key_val.assume_init(), val) != napi::Status::Ok {
*out = false;
return false;
}
@ -107,7 +113,7 @@ pub unsafe extern "C" fn set_string(env: Env, out: &mut bool, object: Local, key
/// Mutates `out` to refer to the value of the property of `object` named by the `key` value.
/// Returns false if the value couldn't be retrieved.
pub unsafe extern "C" fn get(out: &mut Local, env: Env, object: Local, key: Local) -> bool {
pub unsafe fn get(out: &mut Local, env: Env, object: Local, key: Local) -> bool {
let status = napi::get_property(env, object, key, out as *mut _);
status == napi::Status::Ok
@ -119,7 +125,7 @@ pub unsafe extern "C" fn get(out: &mut Local, env: Env, object: Local, key: Loca
/// see [discussion].
///
/// [discussion]: https://github.com/neon-bindings/neon/pull/458#discussion_r344827965
pub unsafe extern "C" fn set(out: &mut bool, env: Env, object: Local, key: Local, val: Local) -> bool {
pub unsafe fn set(out: &mut bool, env: Env, object: Local, key: Local, val: Local) -> bool {
let status = napi::set_property(env, object, key, val);
*out = status == napi::Status::Ok;

View File

@ -1,49 +1,45 @@
use crate::raw::{Local, Env};
use crate::napi::bindings as napi;
use crate::raw::{Env, Local};
/// Mutates the `out` argument provided to refer to the global `undefined` object.
pub unsafe extern "C" fn undefined(out: &mut Local, env: Env) {
pub unsafe fn undefined(out: &mut Local, env: Env) {
napi::get_undefined(env, out as *mut Local);
}
/// Mutates the `out` argument provided to refer to the global `null` object.
pub unsafe extern "C" fn null(out: &mut Local, env: Env) {
pub unsafe fn null(out: &mut Local, env: Env) {
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 extern "C" fn boolean(out: &mut Local, env: Env, b: bool) {
pub unsafe fn boolean(out: &mut Local, env: Env, b: bool) {
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
/// boolean, this function panics.
pub unsafe extern "C" fn boolean_value(env: Env, p: Local) -> bool {
pub unsafe fn boolean_value(env: Env, p: Local) -> bool {
let mut value = false;
assert_eq!(napi::get_value_bool(env, p, &mut value as *mut bool), napi::Status::Ok);
assert_eq!(
napi::get_value_bool(env, p, &mut value as *mut bool),
napi::Status::Ok
);
value
}
// DEPRECATE(0.2)
pub unsafe extern "C" fn integer(_out: &mut Local, _isolate: Env, _x: i32) { unimplemented!() }
pub unsafe extern "C" fn is_u32(_p: Local) -> bool { unimplemented!() }
pub unsafe extern "C" fn is_i32(_p: Local) -> bool { unimplemented!() }
// DEPRECATE(0.2)
pub unsafe extern "C" fn integer_value(_p: Local) -> i64 { unimplemented!() }
/// Mutates the `out` argument provided to refer to a newly created `Local` containing a
/// JavaScript number.
pub unsafe extern "C" fn number(out: &mut Local, env: Env, v: f64) {
pub unsafe fn number(out: &mut Local, env: Env, v: f64) {
napi::create_double(env, v, out as *mut Local);
}
/// Gets the underlying value of an `Local` object containing a JavaScript number. Panics if
/// the given `Local` is not a number.
pub unsafe extern "C" fn number_value(env: Env, p: Local) -> f64 {
pub unsafe fn number_value(env: Env, p: Local) -> f64 {
let mut value = 0.0;
assert_eq!(napi::get_value_double(env, p, &mut value as *mut f64), napi::Status::Ok);
return value;
assert_eq!(
napi::get_value_double(env, p, &mut value as *mut f64),
napi::Status::Ok
);
value
}

View File

@ -11,21 +11,41 @@ pub type Env = napi::Env;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct HandleScope {
pub word: napi::HandleScope
pub word: napi::HandleScope,
}
impl HandleScope {
pub fn new() -> Self { Self { word: ptr::null_mut() } }
pub fn new() -> Self {
Self {
word: ptr::null_mut(),
}
}
}
impl Default for HandleScope {
fn default() -> Self {
Self::new()
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct EscapableHandleScope {
pub word: napi::EscapableHandleScope
pub word: napi::EscapableHandleScope,
}
impl EscapableHandleScope {
pub fn new() -> Self { Self { word: ptr::null_mut() } }
pub fn new() -> Self {
Self {
word: ptr::null_mut(),
}
}
}
impl Default for EscapableHandleScope {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy)]

View File

@ -0,0 +1,49 @@
use std::mem::MaybeUninit;
use crate::napi::bindings as napi;
use crate::raw::{Env, Local};
pub unsafe fn new(env: Env, value: Local) -> napi::Ref {
let mut result = MaybeUninit::uninit();
assert_eq!(
napi::create_reference(env, value, 1, result.as_mut_ptr()),
napi::Status::Ok,
);
result.assume_init()
}
pub unsafe fn reference(env: Env, value: napi::Ref) -> usize {
let mut result = MaybeUninit::uninit();
assert_eq!(
napi::reference_ref(env, value, result.as_mut_ptr()),
napi::Status::Ok,
);
result.assume_init() as usize
}
pub unsafe fn unreference(env: Env, value: napi::Ref) -> usize {
let mut result = MaybeUninit::uninit();
assert_eq!(
napi::reference_unref(env, value, result.as_mut_ptr()),
napi::Status::Ok,
);
result.assume_init() as usize
}
pub unsafe fn get(env: Env, value: napi::Ref) -> Local {
let mut result = MaybeUninit::uninit();
assert_eq!(
napi::get_reference_value(env, value, result.as_mut_ptr()),
napi::Status::Ok,
);
result.assume_init()
}

View File

@ -1,8 +1,7 @@
use std::mem::MaybeUninit;
use crate::raw::{Env, HandleScope, EscapableHandleScope, InheritedHandleScope, Local};
use crate::napi::bindings as napi;
use crate::raw::{Env, EscapableHandleScope, HandleScope, InheritedHandleScope, Local};
// TODO: This leaves a lot of room for UB; we can have a cleaner
// implementation for N-API.
@ -13,7 +12,9 @@ pub trait Root {
}
impl Root for HandleScope {
unsafe fn allocate() -> Self { HandleScope::new() }
unsafe fn allocate() -> Self {
HandleScope::new()
}
unsafe fn enter(&mut self, env: Env) {
let mut scope = MaybeUninit::uninit();
let status = napi::open_handle_scope(env, scope.as_mut_ptr());
@ -30,7 +31,9 @@ impl Root for HandleScope {
}
impl Root for EscapableHandleScope {
unsafe fn allocate() -> Self { EscapableHandleScope::new() }
unsafe fn allocate() -> Self {
EscapableHandleScope::new()
}
unsafe fn enter(&mut self, env: Env) {
let mut scope = MaybeUninit::uninit();
let status = napi::open_escapable_handle_scope(env, scope.as_mut_ptr());
@ -47,17 +50,22 @@ impl Root for EscapableHandleScope {
}
impl Root for InheritedHandleScope {
unsafe fn allocate() -> Self { InheritedHandleScope }
unsafe fn enter(&mut self, _: Env) { }
unsafe fn exit(&mut self, _: Env) { }
unsafe fn allocate() -> Self {
InheritedHandleScope
}
unsafe fn enter(&mut self, _: Env) {}
unsafe fn exit(&mut self, _: Env) {}
}
pub unsafe extern "C" fn escape(env: Env, out: &mut Local, scope: *mut EscapableHandleScope, value: Local) {
pub unsafe fn escape(env: Env, out: &mut Local, scope: *mut EscapableHandleScope, value: Local) {
let status = napi::escape_handle(env, (*scope).word, value, out as *mut _);
assert_eq!(status, napi::Status::Ok);
}
pub unsafe extern "C" fn get_global(env: Env, out: &mut Local) {
assert_eq!(crate::napi::bindings::get_global(env, out as *mut _), napi::Status::Ok);
pub unsafe fn get_global(env: Env, out: &mut Local) {
assert_eq!(
crate::napi::bindings::get_global(env, out as *mut _),
napi::Status::Ok
);
}

View File

@ -1,46 +1,36 @@
use std::mem::MaybeUninit;
use std::ptr;
use crate::raw::{Env, Local};
use crate::napi::bindings as napi;
use crate::raw::{Env, Local};
pub unsafe fn new(out: &mut Local, env: Env, data: *const u8, len: i32) -> bool {
let status = napi::create_string_utf8(
env,
data as *const i8,
len as usize,
out,
);
let status = napi::create_string_utf8(env, data as *const _, len as usize, out);
status == napi::Status::Ok
}
pub unsafe extern "C" fn utf8_len(env: Env, value: Local) -> isize {
pub unsafe fn utf8_len(env: Env, value: Local) -> isize {
let mut len = MaybeUninit::uninit();
let status = napi::get_value_string_utf8(
env,
value,
ptr::null_mut(),
0,
len.as_mut_ptr(),
);
let status = napi::get_value_string_utf8(env, value, ptr::null_mut(), 0, len.as_mut_ptr());
assert_eq!(status, napi::Status::Ok);
len.assume_init() as isize
}
pub unsafe extern "C" fn data(env: Env, out: *mut u8, len: isize, value: Local) -> isize {
pub unsafe fn data(env: Env, out: *mut u8, len: isize, value: Local) -> isize {
let mut read = MaybeUninit::uninit();
let status = napi::get_value_string_utf8(
env,
value,
out as *mut i8,
len as usize,
read.as_mut_ptr(),
);
let status =
napi::get_value_string_utf8(env, value, out as *mut _, len as usize, read.as_mut_ptr());
assert_eq!(status, napi::Status::Ok);
read.assume_init() as isize
}
pub unsafe fn run_script(out: &mut Local, env: Env, value: Local) -> bool {
let status = napi::run_script(env, value, out as *mut _);
status == napi::Status::Ok
}

View File

@ -1,66 +1,91 @@
use crate::raw::{Env, Local};
use crate::napi::bindings as napi;
use crate::raw::{Env, Local};
/// Return true if an `napi_value` `val` has the expected value type.
unsafe fn is_type(env: Env, val: Local, expect: napi::ValueType) -> bool {
let mut actual = napi::ValueType::Undefined;
assert_eq!(napi::typeof_value(env, val, &mut actual as *mut _), napi::Status::Ok);
assert_eq!(
napi::typeof_value(env, val, &mut actual as *mut _),
napi::Status::Ok
);
actual == expect
}
pub unsafe extern "C" fn is_undefined(env: Env, val: Local) -> bool {
pub unsafe fn is_undefined(env: Env, val: Local) -> bool {
is_type(env, val, napi::ValueType::Undefined)
}
pub unsafe extern "C" fn is_null(env: Env, val: Local) -> bool {
pub unsafe fn is_null(env: Env, val: Local) -> bool {
is_type(env, val, napi::ValueType::Null)
}
/// Is `val` a JavaScript number?
pub unsafe extern "C" fn is_number(env: Env, val: Local) -> bool {
pub unsafe fn is_number(env: Env, val: Local) -> bool {
is_type(env, val, napi::ValueType::Number)
}
/// Is `val` a JavaScript boolean?
pub unsafe extern "C" fn is_boolean(env: Env, val: Local) -> bool {
pub unsafe fn is_boolean(env: Env, val: Local) -> bool {
is_type(env, val, napi::ValueType::Boolean)
}
/// Is `val` a JavaScript string?
pub unsafe extern "C" fn is_string(env: Env, val: Local) -> bool {
pub unsafe fn is_string(env: Env, val: Local) -> bool {
is_type(env, val, napi::ValueType::String)
}
pub unsafe extern "C" fn is_object(env: Env, val: Local) -> bool {
pub unsafe fn is_object(env: Env, val: Local) -> bool {
is_type(env, val, napi::ValueType::Object)
}
pub unsafe extern "C" fn is_array(env: Env, val: Local) -> bool {
pub unsafe fn is_array(env: Env, val: Local) -> bool {
let mut result = false;
assert_eq!(napi::is_array(env, val, &mut result as *mut _), napi::Status::Ok);
assert_eq!(
napi::is_array(env, val, &mut result as *mut _),
napi::Status::Ok
);
result
}
pub unsafe extern "C" fn is_function(env: Env, val: Local) -> bool {
pub unsafe fn is_function(env: Env, val: Local) -> bool {
is_type(env, val, napi::ValueType::Function)
}
pub unsafe extern "C" fn is_error(env: Env, val: Local) -> bool {
pub unsafe fn is_error(env: Env, val: Local) -> bool {
let mut result = false;
assert_eq!(napi::is_error(env, val, &mut result as *mut _), napi::Status::Ok);
assert_eq!(
napi::is_error(env, val, &mut result as *mut _),
napi::Status::Ok
);
result
}
/// Is `val` a Node.js Buffer instance?
pub unsafe extern "C" fn is_buffer(env: Env, val: Local) -> bool {
pub unsafe fn is_buffer(env: Env, val: Local) -> bool {
let mut result = false;
assert_eq!(napi::is_buffer(env, val, &mut result as *mut _), napi::Status::Ok);
assert_eq!(
napi::is_buffer(env, val, &mut result as *mut _),
napi::Status::Ok
);
result
}
/// Is `val` an ArrayBuffer instance?
pub unsafe extern "C" fn is_arraybuffer(env: Env, val: Local) -> bool {
pub unsafe fn is_arraybuffer(env: Env, val: Local) -> bool {
let mut result = false;
assert_eq!(napi::is_arraybuffer(env, val, &mut result as *mut _), napi::Status::Ok);
assert_eq!(
napi::is_arraybuffer(env, val, &mut result as *mut _),
napi::Status::Ok
);
result
}
#[cfg(feature = "napi-5")]
pub unsafe fn is_date(env: Env, val: Local) -> bool {
let mut result = false;
assert_eq!(
napi::is_date(env, val, &mut result as *mut _),
napi::Status::Ok
);
result
}

View File

@ -1,7 +0,0 @@
use crate::raw::Local;
use std::os::raw::c_void;
pub unsafe extern "C" fn schedule(_task: *mut c_void,
_perform: unsafe extern fn(*mut c_void) -> *mut c_void,
_complete: unsafe extern fn(*mut c_void, *mut c_void, &mut Local),
_callback: Local) { unimplemented!() }

View File

@ -0,0 +1,177 @@
//! Idiomatic Rust wrappers for N-API threadsafe functions
use std::ffi::c_void;
use std::mem::MaybeUninit;
use crate::napi::bindings as napi;
use crate::raw::{Env, Local};
unsafe fn string(env: Env, s: impl AsRef<str>) -> Local {
let s = s.as_ref();
let mut result = MaybeUninit::uninit();
assert_eq!(
napi::create_string_utf8(
env,
s.as_bytes().as_ptr() as *const _,
s.len(),
result.as_mut_ptr(),
),
napi::Status::Ok,
);
result.assume_init()
}
#[derive(Debug)]
struct Tsfn(napi::ThreadsafeFunction);
unsafe impl Send for Tsfn {}
unsafe impl Sync for Tsfn {}
#[derive(Debug)]
/// Threadsafe Function encapsulate a Rust function pointer and N-API threadsafe
/// function for scheduling tasks to execute on a JavaScript thread.
pub struct ThreadsafeFunction<T> {
tsfn: Tsfn,
callback: fn(Option<Env>, T),
}
#[derive(Debug)]
struct Callback<T> {
callback: fn(Option<Env>, T),
data: T,
}
/// Error returned when scheduling a threadsafe function with some data
pub struct CallError<T> {
kind: napi::Status,
data: T,
}
impl<T> CallError<T> {
/// The specific error that occurred
pub fn kind(&self) -> napi::Status {
self.kind
}
/// Returns the data that was sent when scheduling to allow re-scheduling
pub fn into_inner(self) -> T {
self.data
}
}
impl<T: Send + 'static> ThreadsafeFunction<T> {
/// Creates a new unbounded N-API Threadsafe Function
/// Safety: `Env` must be valid for the current thread
pub unsafe fn new(env: Env, callback: fn(Option<Env>, T)) -> Self {
Self::with_capacity(env, 0, callback)
}
/// Creates a bounded N-API Threadsafe Function
/// Safety: `Env` must be valid for the current thread
pub unsafe fn with_capacity(
env: Env,
max_queue_size: usize,
callback: fn(Option<Env>, T),
) -> Self {
let mut result = MaybeUninit::uninit();
assert_eq!(
napi::create_threadsafe_function(
env,
std::ptr::null_mut(),
std::ptr::null_mut(),
string(env, "neon threadsafe function"),
max_queue_size,
// Always set the reference count to 1. Prefer using
// Rust `Arc` to maintain the struct.
1,
std::ptr::null_mut(),
None,
std::ptr::null_mut(),
Some(Self::callback),
result.as_mut_ptr(),
),
napi::Status::Ok,
);
Self {
tsfn: Tsfn(result.assume_init()),
callback,
}
}
/// Schedule a threadsafe function to be executed with some data
pub fn call(
&self,
data: T,
is_blocking: Option<napi::ThreadsafeFunctionCallMode>,
) -> Result<(), CallError<T>> {
let is_blocking = is_blocking.unwrap_or(napi::ThreadsafeFunctionCallMode::Blocking);
let callback = Box::into_raw(Box::new(Callback {
callback: self.callback,
data,
}));
let status =
unsafe { napi::call_threadsafe_function(self.tsfn.0, callback as *mut _, is_blocking) };
if status == napi::Status::Ok {
Ok(())
} else {
// If the call failed, the callback won't execute
let callback = unsafe { Box::from_raw(callback) };
Err(CallError {
kind: status,
data: callback.data,
})
}
}
/// References a threadsafe function to prevent exiting the event loop until it has been dropped. (Default)
/// Safety: `Env` must be valid for the current thread
pub unsafe fn reference(&mut self, env: Env) {
assert_eq!(
napi::ref_threadsafe_function(env, self.tsfn.0,),
napi::Status::Ok,
);
}
/// Unreferences a threadsafe function to allow exiting the event loop before it has been dropped.
/// Safety: `Env` must be valid for the current thread
pub unsafe fn unref(&mut self, env: Env) {
assert_eq!(
napi::unref_threadsafe_function(env, self.tsfn.0,),
napi::Status::Ok,
);
}
// Provides a C ABI wrapper for invoking the user supplied function pointer
unsafe extern "C" fn callback(
env: Env,
_js_callback: napi::Value,
_context: *mut c_void,
data: *mut c_void,
) {
let Callback { callback, data } = *Box::from_raw(data as *mut Callback<T>);
// Event loop has terminated
let env = if env.is_null() { None } else { Some(env) };
callback(env, data);
}
}
impl<T> Drop for ThreadsafeFunction<T> {
fn drop(&mut self) {
unsafe {
napi::release_threadsafe_function(
self.tsfn.0,
napi::ThreadsafeFunctionReleaseMode::Release,
);
};
}
}

View File

@ -1,6 +1,6 @@
[package]
name = "neon-sys"
version = "0.6.0"
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

@ -12,11 +12,11 @@ mod build {
#[cfg(not(feature = "docs-only"))]
mod build {
use std::process::Command;
use regex::Regex;
use std::env;
use std::fs;
use std::path::Path;
use regex::Regex;
use std::process::Command;
pub fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
@ -94,7 +94,8 @@ mod build {
//
// gyp verb architecture ia32
fn parse_node_arch(node_gyp_output: &str) -> String {
let version_regex = Regex::new(r"gyp verb architecture (?P<arch>ia32|x64|arm|arm64)").unwrap();
let version_regex =
Regex::new(r"gyp verb architecture (?P<arch>ia32|x64|arm|arm64)").unwrap();
let captures = version_regex.captures(&node_gyp_output).unwrap();
String::from(&captures["arch"])
}
@ -109,7 +110,10 @@ mod build {
.find(node_root_dir_flag_pattern)
.map(|i| i + node_root_dir_flag_pattern.len())
.expect("Couldn't find node_root_dir in node-gyp output.");
let node_root_dir_end_index = node_gyp_output[node_root_dir_start_index..].find("'").unwrap() + node_root_dir_start_index;
let node_root_dir_end_index = node_gyp_output[node_root_dir_start_index..]
.find('\'')
.unwrap()
+ node_root_dir_start_index;
&node_gyp_output[node_root_dir_start_index..node_root_dir_end_index]
}
@ -131,7 +135,10 @@ mod build {
.find(node_lib_file_flag_pattern)
.map(|i| i + node_lib_file_flag_pattern.len())
.expect("Couldn't find node_lib_file in node-gyp output.");
let node_lib_file_end_index = node_gyp_output[node_lib_file_start_index..].find("'").unwrap() + node_lib_file_start_index;
let node_lib_file_end_index = node_gyp_output[node_lib_file_start_index..]
.find('\'')
.unwrap()
+ node_lib_file_start_index;
&node_gyp_output[node_lib_file_start_index..node_lib_file_end_index]
}
@ -147,37 +154,70 @@ mod build {
}
// Ensure that all package.json dependencies and dev dependencies are installed.
npm(native_dir).args(&["install", "--silent"]).status().ok().expect("Failed to run \"npm install\" for neon-sys!");
npm(native_dir)
.args(&["install", "--silent"])
.status()
.expect("Failed to run \"npm install\" for neon-sys!");
// Run `node-gyp configure` in verbose mode to read node_root_dir on Windows.
let output = npm(native_dir)
.args(&["run", if debug() { "configure-debug" } else { "configure-release" }])
.args(&[
"run",
if debug() {
"configure-debug"
} else {
"configure-release"
},
])
.output()
.expect("Failed to run \"node-gyp configure\" for neon-sys!");
if !output.status.success() {
panic!(format!(
panic!(
"Failed to run \"node-gyp configure\" for neon-sys!\n Out: {}\n Err: {}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
));
);
}
if cfg!(windows) {
let node_gyp_output = String::from_utf8_lossy(&output.stderr);
println!("cargo:node_root_dir={}", parse_node_root_dir(&node_gyp_output));
println!("cargo:node_lib_file={}", parse_node_lib_file(&node_gyp_output));
println!(
"cargo:node_root_dir={}",
parse_node_root_dir(&node_gyp_output)
);
println!(
"cargo:node_lib_file={}",
parse_node_lib_file(&node_gyp_output)
);
}
// Run `node-gyp build`.
let build_output = npm(native_dir)
.args(&["run", if debug() { "build-debug" } else { "build-release" }])
.args(&[
"run",
if debug() {
"build-debug"
} else {
"build-release"
},
])
.output()
.ok()
.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={}", parse_node_arch(&node_gyp_build_output));
println!(
"cargo:node_arch={}",
parse_node_arch(&node_gyp_build_output)
);
}
// Link the built object file into a static library.
@ -206,7 +246,10 @@ mod build {
}
};
cc::Build::new().cpp(true).object(object_path).compile("libneon.a");
cc::Build::new()
.cpp(true)
.object(object_path)
.compile("libneon.a");
}
#[cfg(unix)]
@ -237,7 +280,7 @@ mod build {
fn debug() -> bool {
match env::var("DEBUG") {
Ok(s) => s == "true",
Err(_) => false
Err(_) => false,
}
}
}

View File

@ -199,7 +199,7 @@ extern "C" bool Neon_Buffer_New(v8::Isolate *isolate, v8::Local<v8::Object> *out
return true;
}
extern "C" bool Neon_Buffer_Uninitialized(v8::Local<v8::Object> *out, uint32_t size) {
extern "C" bool Neon_Buffer_Uninitialized(v8::Isolate *isolate, v8::Local<v8::Object> *out, uint32_t size) {
Nan::MaybeLocal<v8::Object> maybe = Nan::NewBuffer(size);
return maybe.ToLocal(out);
}
@ -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 @@
use std::os::raw::c_void;
use std::mem;
use std::os::raw::c_void;
use std::ptr::null_mut;
/// A V8 `Local` handle.
@ -9,7 +9,7 @@ use std::ptr::null_mut;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Local {
pub handle: *mut c_void
pub handle: *mut c_void,
}
/// Represents the details of how the function was called from JavaScript.
@ -38,11 +38,19 @@ const HANDLE_SCOPE_SIZE: usize = 24;
#[derive(Clone, Copy)]
pub struct HandleScope {
pub align_to_pointer: [*mut c_void; 0],
pub fields: [u8; HANDLE_SCOPE_SIZE]
pub fields: [u8; HANDLE_SCOPE_SIZE],
}
impl HandleScope {
pub fn new() -> HandleScope { unsafe { mem::zeroed() } }
pub fn new() -> HandleScope {
unsafe { mem::zeroed() }
}
}
impl Default for HandleScope {
fn default() -> Self {
Self::new()
}
}
const ESCAPABLE_HANDLE_SCOPE_SIZE: usize = 32;
@ -56,24 +64,32 @@ const ESCAPABLE_HANDLE_SCOPE_SIZE: usize = 32;
#[derive(Clone, Copy)]
pub struct EscapableHandleScope {
pub align_to_pointer: [*mut c_void; 0],
pub fields: [u8; ESCAPABLE_HANDLE_SCOPE_SIZE]
pub fields: [u8; ESCAPABLE_HANDLE_SCOPE_SIZE],
}
impl EscapableHandleScope {
pub fn new() -> EscapableHandleScope { unsafe { mem::zeroed() } }
pub fn new() -> EscapableHandleScope {
unsafe { mem::zeroed() }
}
}
impl Default for EscapableHandleScope {
fn default() -> Self {
Self::new()
}
}
#[repr(C)]
pub struct CCallback {
pub static_callback: *mut c_void,
pub dynamic_callback: *mut c_void
pub dynamic_callback: *mut c_void,
}
impl Default for CCallback {
fn default() -> Self {
CCallback {
static_callback: null_mut(),
dynamic_callback: null_mut()
dynamic_callback: null_mut(),
}
}
}
@ -83,7 +99,7 @@ pub enum TryCatchControl {
Returned = 0,
Threw = 1,
Panicked = 2,
UnexpectedErr = 3
UnexpectedErr = 3,
}
#[derive(Clone, Copy)]
@ -95,10 +111,12 @@ pub struct InheritedHandleScope;
/// function returns `TryCatchControl::Returned`.
/// The `unwind_value` argument can be assumed to be initialized if and only if the
/// glue function returns `TryCatchControl::Panicked`.
pub type TryCatchGlue = extern fn(rust_thunk: *mut c_void,
cx: *mut c_void,
result: *mut c_void,
unwind_value: *mut *mut c_void) -> TryCatchControl;
pub type TryCatchGlue = extern "C" fn(
rust_thunk: *mut c_void,
cx: *mut c_void,
result: *mut c_void,
unwind_value: *mut *mut c_void,
) -> TryCatchControl;
extern "C" {
@ -106,11 +124,19 @@ extern "C" {
pub fn Neon_Array_Length(isolate: Isolate, array: Local) -> u32;
pub fn Neon_ArrayBuffer_New(out: &mut Local, isolate: Isolate, size: u32) -> bool;
pub fn Neon_ArrayBuffer_Data<'a, 'b>(isolate: Isolate, base_out: &'a mut *mut c_void, obj: Local) -> usize;
pub fn Neon_ArrayBuffer_Data<'a, 'b>(
isolate: Isolate,
base_out: &'a mut *mut c_void,
obj: Local,
) -> usize;
pub fn Neon_Buffer_New(isolate: Isolate, out: &mut Local, size: u32) -> bool;
pub fn Neon_Buffer_Uninitialized(out: &mut Local, size: u32) -> bool;
pub fn Neon_Buffer_Data<'a, 'b>(isolate: Isolate, base_out: &'a mut *mut c_void, obj: Local) -> usize;
pub fn Neon_Buffer_Uninitialized(isolate: Isolate, out: &mut Local, size: u32) -> bool;
pub fn Neon_Buffer_Data<'a, 'b>(
isolate: Isolate,
base_out: &'a mut *mut c_void,
obj: Local,
) -> usize;
pub fn Neon_Call_SetReturn(info: FunctionCallbackInfo, value: Local);
pub fn Neon_Call_GetIsolate(info: FunctionCallbackInfo) -> Isolate;
@ -123,17 +149,38 @@ extern "C" {
pub fn Neon_Class_GetClassMap(isolate: Isolate) -> *mut c_void;
pub fn Neon_Class_SetClassMap(isolate: Isolate, map: *mut c_void, free_map: *mut c_void);
pub fn Neon_Class_CreateBase(isolate: Isolate,
allocate: CCallback,
construct: CCallback,
call: CCallback,
drop: extern "C" fn(*mut c_void)) -> *mut c_void;
pub fn Neon_Class_GetName<'a>(base_out: &'a mut *mut u8, isolate: Isolate, metadata: *const c_void) -> usize;
pub fn Neon_Class_SetName(isolate: Isolate, metadata: *mut c_void, name: *const u8, byte_length: u32) -> bool;
pub fn Neon_Class_CreateBase(
isolate: Isolate,
allocate: CCallback,
construct: CCallback,
call: CCallback,
drop: extern "C" fn(*mut c_void),
) -> *mut c_void;
pub fn Neon_Class_GetName<'a>(
base_out: &'a mut *mut u8,
isolate: Isolate,
metadata: *const c_void,
) -> usize;
pub fn Neon_Class_SetName(
isolate: Isolate,
metadata: *mut c_void,
name: *const u8,
byte_length: u32,
) -> bool;
pub fn Neon_Class_ThrowCallError(isolate: Isolate, metadata: *mut c_void);
pub fn Neon_Class_ThrowThisError(isolate: Isolate, metadata: *mut c_void);
pub fn Neon_Class_AddMethod(isolate: Isolate, metadata: *mut c_void, name: *const u8, byte_length: u32, method: Local) -> bool;
pub fn Neon_Class_MetadataToConstructor(out: &mut Local, isolate: Isolate, metadata: *mut c_void) -> bool;
pub fn Neon_Class_AddMethod(
isolate: Isolate,
metadata: *mut c_void,
name: *const u8,
byte_length: u32,
method: Local,
) -> bool;
pub fn Neon_Class_MetadataToConstructor(
out: &mut Local,
isolate: Isolate,
metadata: *mut c_void,
) -> bool;
pub fn Neon_Class_GetAllocateKernel(data: *mut c_void) -> *mut c_void;
pub fn Neon_Class_GetConstructKernel(data: *mut c_void) -> *mut c_void;
pub fn Neon_Class_GetCallKernel(data: *mut c_void) -> *mut c_void;
@ -153,22 +200,52 @@ extern "C" {
pub fn Neon_Fun_New(out: &mut Local, isolate: Isolate, callback: CCallback) -> bool;
pub fn Neon_Fun_Template_New(out: &mut Local, isolate: Isolate, callback: CCallback) -> bool;
pub fn Neon_Fun_GetDynamicCallback(isolate: Isolate, data: *mut c_void) -> *mut c_void;
pub fn Neon_Fun_Call(out: &mut Local, isolate: Isolate, fun: Local, this: Local, argc: i32, argv: *mut c_void) -> bool;
pub fn Neon_Fun_Construct(out: &mut Local, isolate: Isolate, fun: Local, argc: i32, argv: *mut c_void) -> bool;
pub fn Neon_Fun_Call(
out: &mut Local,
isolate: Isolate,
fun: Local,
this: Local,
argc: i32,
argv: *mut c_void,
) -> bool;
pub fn Neon_Fun_Construct(
out: &mut Local,
isolate: Isolate,
fun: Local,
argc: i32,
argv: *mut c_void,
) -> bool;
pub fn Neon_Mem_SameHandle(h1: Local, h2: Local) -> bool;
pub fn Neon_Module_ExecKernel(kernel: *mut c_void, callback: extern fn(*mut c_void, *mut c_void, *mut c_void, *mut c_void), exports: Local, scope: *mut c_void, vm: *mut c_void);
pub fn Neon_Module_ExecKernel(
kernel: *mut c_void,
callback: extern "C" fn(*mut c_void, *mut c_void, *mut c_void, *mut c_void),
exports: Local,
scope: *mut c_void,
vm: *mut c_void,
);
pub fn Neon_Module_ExecCallback(callback: CCallback, exports: Local, vm: *mut c_void);
pub fn Neon_Module_GetVersion() -> i32;
pub fn Neon_Object_New(out: &mut Local, isolate: Isolate);
pub fn Neon_Object_GetOwnPropertyNames(out: &mut Local, isolate: Isolate, object: Local) -> bool;
pub fn Neon_Object_GetOwnPropertyNames(
out: &mut Local,
isolate: Isolate,
object: Local,
) -> bool;
pub fn Neon_Object_GetIsolate(obj: Local) -> Isolate;
pub fn Neon_Object_Get_Index(out: &mut Local, object: Local, index: u32) -> bool;
pub fn Neon_Object_Set_Index(out: &mut bool, object: Local, index: u32, val: Local) -> bool;
pub fn Neon_Object_Get_String(out: &mut Local, object: Local, key: *const u8, len: i32) -> bool;
pub fn Neon_Object_Set_String(out: &mut bool, object: Local, key: *const u8, len: i32, val: Local) -> bool;
pub fn Neon_Object_Get_String(out: &mut Local, object: Local, key: *const u8, len: i32)
-> bool;
pub fn Neon_Object_Set_String(
out: &mut bool,
object: Local,
key: *const u8,
len: i32,
val: Local,
) -> bool;
pub fn Neon_Object_Get(out: &mut Local, object: Local, key: Local) -> bool;
pub fn Neon_Object_Set(out: &mut bool, object: Local, key: Local, val: Local) -> bool;
@ -183,9 +260,24 @@ extern "C" {
pub fn Neon_Primitive_Number(out: &mut Local, isolate: Isolate, v: f64);
pub fn Neon_Primitive_NumberValue(p: Local) -> f64;
pub fn Neon_Scope_Escape(isolate: Isolate, out: &mut Local, scope: *mut EscapableHandleScope, value: Local);
pub fn Neon_Scope_Chained(out: *mut c_void, closure: *mut c_void, callback: extern fn(&mut c_void, *mut c_void, *mut c_void, *mut c_void), parent_scope: *mut c_void);
pub fn Neon_Scope_Nested(out: *mut c_void, closure: *mut c_void, callback: extern fn(&mut c_void, *mut c_void, *mut c_void), realm: *mut c_void);
pub fn Neon_Scope_Escape(
isolate: Isolate,
out: &mut Local,
scope: *mut EscapableHandleScope,
value: Local,
);
pub fn Neon_Scope_Chained(
out: *mut c_void,
closure: *mut c_void,
callback: extern "C" fn(&mut c_void, *mut c_void, *mut c_void, *mut c_void),
parent_scope: *mut c_void,
);
pub fn Neon_Scope_Nested(
out: *mut c_void,
closure: *mut c_void,
callback: extern "C" fn(&mut c_void, *mut c_void, *mut c_void),
realm: *mut c_void,
);
pub fn Neon_Scope_Enter(scope: &mut HandleScope, isolate: Isolate);
pub fn Neon_Scope_Exit(scope: &mut HandleScope);
pub fn Neon_Scope_Enter_Escapable(scope: &mut EscapableHandleScope, isolate: Isolate);
@ -212,14 +304,19 @@ extern "C" {
pub fn Neon_Tag_IsBuffer(isolate: Isolate, obj: Local) -> bool;
pub fn Neon_Tag_IsArrayBuffer(isolate: Isolate, obj: Local) -> bool;
pub fn Neon_Task_Schedule(task: *mut c_void,
perform: unsafe extern fn(*mut c_void) -> *mut c_void,
complete: unsafe extern fn(*mut c_void, *mut c_void, &mut Local),
callback: Local);
pub fn Neon_Task_Schedule(
task: *mut c_void,
perform: unsafe extern "C" fn(*mut c_void) -> *mut c_void,
complete: unsafe extern "C" fn(*mut c_void, *mut c_void, &mut Local),
callback: Local,
);
pub fn Neon_EventHandler_New(isolate: Isolate, this: Local, callback: Local) -> *mut c_void;
pub fn Neon_EventHandler_Schedule(thread_safe_cb: *mut c_void, rust_callback: *mut c_void,
complete: unsafe extern fn(Local, Local, *mut c_void));
pub fn Neon_EventHandler_Schedule(
thread_safe_cb: *mut c_void,
rust_callback: *mut c_void,
complete: unsafe extern "C" fn(Local, Local, *mut c_void),
);
pub fn Neon_EventHandler_Delete(thread_safe_cb: *mut c_void);
/// Invokes a Rust closure with a `TryCatch` live on the stack.
@ -227,10 +324,12 @@ extern "C" {
/// does not return `TryCatchControl::Panicked`.
/// The `unwind_value` value can be assumed to be initialized if and only if this
/// function returns `TryCatchControl::Panicked`.
pub fn Neon_TryCatch_With(glue: TryCatchGlue,
rust_thunk: *mut c_void,
cx: *mut c_void,
ok: *mut c_void,
err: *mut Local,
unwind_value: *mut *mut c_void) -> TryCatchControl;
pub fn Neon_TryCatch_With(
glue: TryCatchGlue,
rust_thunk: *mut c_void,
cx: *mut c_void,
ok: *mut c_void,
err: *mut Local,
unwind_value: *mut *mut c_void,
) -> TryCatchControl;
}

View File

@ -0,0 +1,6 @@
{
"extension": ["ts"],
"spec": "test/**/*.ts",
"require": "ts-node/register",
"timeout": 5000
}

11
create-neon/README.md Normal file
View File

@ -0,0 +1,11 @@
# Create Neon
The `create-neon` tool bootstraps [Neon](https://neon-bindings.com) projects, which allows developers to build binary Node modules written in [Rust](https://www.rust-lang.org).
## Usage
You can conveniently use this tool with the [`npm init`](https://docs.npmjs.com/cli/v7/commands/npm-init) syntax:
```sh
$ npm init neon my-project
```

View File

@ -0,0 +1,5 @@
target
index.node
**/node_modules
**/.DS_Store
npm-debug.log*

View File

@ -0,0 +1,22 @@
[package]
name = "{{package.name}}"
version = "{{package.version}}"
{{#if package.description}}
description = {{package.quotedDescription}}
{{/if}}
{{#if package.author}}
authors = [{{package.quotedAuthor}}]
{{/if}}
{{#if package.license}}
license = "{{package.license}}"
{{/if}}
edition = "2018"
exclude = ["index.node"]
[lib]
crate-type = ["cdylib"]
[dependencies.neon]
version = "{{versions.neon}}"
default-features = false
features = ["napi-{{versions.napi}}"]

View File

@ -0,0 +1,109 @@
# {{package.name}}
{{#if package.description}}
**{{package.name}}:** {{package.description}}
{{/if}}
This project was bootstrapped by [create-neon](https://www.npmjs.com/package/create-neon).
## Installing {{package.name}}
Installing {{package.name}} requires a [supported version of Node and Rust](https://github.com/neon-bindings/neon#platform-support).
You can install the project with npm. In the project directory, run:
```sh
$ npm install
```
This fully installs the project, including installing any dependencies and running the build.
## Building {{package.name}}
If you have already installed the project and only want to run the build, run:
```sh
$ npm run build
```
This command uses the [cargo-cp-artifact](https://github.com/neon-bindings/cargo-cp-artifact) utility to run the Rust build and copy the built library into `./index.node`.
## Exploring {{package.name}}
After building {{package.name}}, you can explore its exports at the Node REPL:
```sh
$ npm install
$ node
> require('.').hello()
"hello node"
```
## Available Scripts
In the project directory, you can run:
### `npm install`
Installs the project, including running `npm run build`.
### `npm build`
Builds the Node addon (`index.node`) from source.
### `npm test`
Runs the unit tests by calling `cargo test`. You can learn more about [adding tests to your Rust code](https://doc.rust-lang.org/book/ch11-01-writing-tests.html) from the [Rust book](https://doc.rust-lang.org/book/).
## Project Layout
The directory structure of this project is:
```
{{package.name}}/
├── Cargo.toml
├── README.md
├── index.node
├── package.json
├── src/
| └── lib.rs
└── target/
```
### Cargo.toml
The Cargo [manifest file](https://doc.rust-lang.org/cargo/reference/manifest.html), which informs the `cargo` command.
### README.md
This file.
### index.node
The Node addon—i.e., a binary Node module—generated by building the project. This is the main module for this package, as dictated by the `"main"` key in `package.json`.
Under the hood, a [Node addon](https://nodejs.org/api/addons.html) is a [dynamically-linked shared object](https://en.wikipedia.org/wiki/Library_(computing)#Shared_libraries). The `"build"` script produces this file by copying it from within the `target/` directory, which is where the Rust build produces the shared object.
### package.json
The npm [manifest file](https://docs.npmjs.com/cli/v7/configuring-npm/package-json), which informs the `npm` command.
### src/
The directory tree containing the Rust source code for the project.
### src/lib.rs
The Rust library's main module.
### target/
Binary artifacts generated by the Rust build.
## Learn More
To learn more about Neon, see the [Neon documentation](https://neon-bindings.com).
To learn more about Rust, see the [Rust documentation](https://www.rust-lang.org).
To learn more about Node, see the [Node documentation](https://nodejs.org).

View File

@ -0,0 +1,11 @@
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(())
}

View File

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

104
create-neon/dev/expect.ts Normal file
View File

@ -0,0 +1,104 @@
import { ChildProcess } from 'child_process';
import { PassThrough, Readable, Writable } from 'stream';
import { StringDecoder } from 'string_decoder';
import readStream from 'stream-to-string';
function readChunks(input: Readable): Readable {
let output = new PassThrough({ objectMode: true });
let decoder = new StringDecoder('utf8');
input.on('data', data => {
output.write(decoder.write(data));
});
input.on('close', () => {
output.write(decoder.end());
output.destroy();
});
return output;
}
function splitLines(s: string): string[] {
return s.split(/([^\n]*\r?\n)/).filter(x => x);
}
function isCompleteLine(s: string): boolean {
return s.endsWith('\n');
}
class LinesBuffer {
// INVARIANT: (this.buffer.length > 0) &&
// !isCompleteLine(this.buffer[this.buffer.length - 1])
// In other words, the last line in the buffer is always incomplete.
private buffer: string[];
constructor() {
this.buffer = [""];
}
add(lines: string[]) {
if (isCompleteLine(lines[lines.length - 1])) {
lines.push("");
}
this.buffer[this.buffer.length - 1] += lines.shift();
this.buffer = this.buffer.concat(lines);
}
find(p: (s: string) => boolean): string[] | null {
let index = this.buffer.findIndex(p);
if (index === -1) {
return null;
}
let extracted = this.buffer.splice(0, index + 1);
if (this.buffer.length === 0) {
this.buffer.push("");
}
return extracted;
}
}
async function* run(script: Record<string, string>, stdin: Writable, stdout: Readable) {
let lines = new LinesBuffer();
let keys = Object.keys(script);
let i = 0;
for await (let chunk of readChunks(stdout)) {
lines.add(splitLines(chunk));
let found = lines.find(line => line.startsWith(keys[i]));
if (found) {
stdin.write(script[keys[i]] + "\n");
yield found;
i++;
if (i >= keys.length) {
break;
}
}
}
}
function exit(child: ChildProcess): Promise<number | null> {
let resolve: (code: number | null) => void;
let result: Promise<number | null> = new Promise(res => { resolve = res; });
child.on('exit', code => {
resolve(code);
});
return result;
}
export default async function expect(child: ChildProcess, script: Record<string, string>): Promise<void> {
let output: string[][] = [];
for await (let lines of run(script, child.stdin!, child.stdout!)) {
output.push(lines);
}
let stderr = await readStream(child.stderr!);
let code = await exit(child);
switch (code) {
case null:
throw new Error("child process interrupted");
case 0:
return;
default:
console.log("stderr: " + stderr.trim());
console.log("stdout: " + JSON.stringify(output));
throw new Error("child process exited with code " + code);
}
}

25
create-neon/dev/rimraf.ts Normal file
View File

@ -0,0 +1,25 @@
import rm from 'rimraf';
/**
* A shim for Node's newer builtin `{ recursive: true }` option. When
* we only support new enough Node versions, we should be able to drop
* this shim and directly use the builtin `rmdir` stdlib function.
*
* See: https://nodejs.org/api/fs.html#fs_fspromises_rmdir_path_options
*/
export default function rimraf(pattern: string, opts: rm.Options): Promise<void> {
let resolve: (result: void) => void;
let reject: (error: Error) => void;
let result: Promise<void> = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
rm(pattern, opts, error => {
if (error) {
reject(error);
} else {
resolve(undefined);
}
});
return result;
}

1103
create-neon/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

49
create-neon/package.json Normal file
View File

@ -0,0 +1,49 @@
{
"name": "create-neon",
"version": "0.1.3",
"description": "Create Neon projects with no build configuration.",
"author": "Dave Herman <david.herman@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/neon-bindings/neon/issues"
},
"homepage": "https://github.com/neon-bindings/neon#readme",
"bin": {
"create-neon": "dist/src/bin/create-neon.js"
},
"files": [
"dist/src/**/*",
"dist/data/**/*"
],
"scripts": {
"build": "tsc && cp -r data/templates dist/data",
"prepublishOnly": "npm run build",
"pretest": "npm install && npm run build",
"test": "mocha",
"manual-test": "npm run build && rm -rf create-neon-manual-test-project && node ./dist/src/bin/create-neon.js create-neon-manual-test-project"
},
"repository": {
"type": "git",
"url": "git+https://github.com/neon-bindings/neon.git"
},
"keywords": [
"neon"
],
"devDependencies": {
"@types/chai": "^4.2.15",
"@types/mocha": "^8.2.1",
"@types/node": "^14.14.31",
"@types/rimraf": "^3.0.0",
"chai": "^4.3.3",
"execa": "^5.0.0",
"mocha": "^8.3.1",
"rimraf": "^3.0.2",
"stream-to-string": "^1.2.0",
"toml": "^3.0.0",
"ts-node": "^9.1.1",
"typescript": "^4.2.2"
},
"dependencies": {
"handlebars": "^4.7.7"
}
}

View File

@ -0,0 +1,70 @@
#!/usr/bin/env node
import { promises as fs } from 'fs';
import * as path from 'path';
import die from '../die';
import Package from '../package';
import expand, { Versions } from '../expand';
import versions from '../../data/versions.json';
const TEMPLATES: Record<string, string> = {
'.gitignore.hbs': '.gitignore',
'Cargo.toml.hbs': 'Cargo.toml',
'README.md.hbs': 'README.md',
'lib.rs.hbs': path.join('src', 'lib.rs')
};
function inferVersions(): Versions {
// Select the N-API version associated with the current
// running Node process.
let inferred = process.versions.napi;
let napi = inferred
? Math.min(Number(versions.napi), Number(inferred))
: Number(versions.napi);
return {
neon: versions.neon,
napi: napi
};
}
async function main(name: string) {
try {
await fs.mkdir(name);
} catch (err) {
die(`Could not create \`${name}\`: ${err.message}`);
}
let pkg: Package;
try {
pkg = await Package.create(name);
} catch (err) {
die("Could not create `package.json`: " + err.message);
}
await fs.mkdir(path.join(name, 'src'));
for (let source of Object.keys(TEMPLATES)) {
let target = path.join(name, TEMPLATES[source]);
await expand(source, target, {
package: pkg,
versions: inferVersions()
});
}
console.log(`✨ Created Neon project \`${name}\`. Happy 🦀 hacking! ✨`);
}
if (process.argv.length < 3) {
console.error("✨ create-neon: Create a new Neon project with zero configuration. ✨");
console.error();
console.error("Usage: npm init neon name");
console.error();
console.error(" name The name of your Neon project, placed in a new directory of the same name.");
console.error();
process.exit(1);
}
main(process.argv[2]);

4
create-neon/src/die.ts Normal file
View File

@ -0,0 +1,4 @@
export default function die(message: string): never {
console.error(`${message}`);
process.exit(1);
}

24
create-neon/src/expand.ts Normal file
View File

@ -0,0 +1,24 @@
import { promises as fs } from 'fs';
import handlebars from 'handlebars';
import * as path from 'path';
import Package from './package';
const TEMPLATES_DIR = path.join(__dirname, '..', 'data', 'templates');
export interface Versions {
neon: string,
napi: number
};
export interface Metadata {
package: Package,
versions: Versions
};
export default async function expand(source: string, target: string, metadata: Metadata) {
let template = await fs.readFile(path.join(TEMPLATES_DIR, source), 'utf8');
let compiled = handlebars.compile(template, { noEscape: true });
let expanded = compiled(metadata);
// The 'wx' flag creates the file but fails if it already exists.
await fs.writeFile(target, expanded, { flag: 'wx' });
}

View File

@ -0,0 +1,76 @@
import { promises as fs } from 'fs';
import * as path from 'path';
import versions from '../data/versions.json';
import shell from './shell';
const KEYS = [
"name",
"version",
"description",
"main",
"scripts",
"author",
"license"
];
function sort(json: any): any {
// First copy the keys in the order specified in KEYS.
let next = KEYS.filter(key => json.hasOwnProperty(key))
.map(key => [key, json[key]])
.reduce((acc, [key, val]) => Object.assign(acc, { [key]: val }), {});
// Then copy any remaining keys in the original order.
return Object.assign(next, json);
}
export default class Package {
name: string;
version: string;
author: string;
quotedAuthor: string;
license: string;
description: string;
quotedDescription: string;
static async create(name: string): Promise<Package> {
let seed = {
name: name,
version: "0.1.0",
main: "index.node",
scripts: {
"build": "cargo-cp-artifact -nc index.node -- cargo build --message-format=json-render-diagnostics",
"install": "npm run build",
"test": "cargo test"
},
devDependencies: {
"cargo-cp-artifact": `^${versions['cargo-cp-artifact']}`
}
};
let filename = path.join(name, 'package.json');
// 1. Write initial values to prevent `npm init` from asking unnecessary questions.
await fs.writeFile(filename, JSON.stringify(seed));
// 2. Call `npm init` to ask the user remaining questions.
await shell('npm', ['init'], name);
// 3. Sort the values in idiomatic `npm init` order.
let sorted = sort(JSON.parse(await fs.readFile(filename, 'utf8')));
// 4. Save the result to package.json.
await fs.writeFile(filename, JSON.stringify(sorted, undefined, 2));
return new Package(sorted);
}
constructor(json: any) {
this.name = json.name;
this.version = json.version;
this.author = json.author;
this.quotedAuthor = JSON.stringify(json.author);
this.license = json.license;
this.description = json.description;
this.quotedDescription = JSON.stringify(json.description);
}
}

33
create-neon/src/shell.ts Normal file
View File

@ -0,0 +1,33 @@
import { spawn } from 'child_process';
/**
* Transparently shell out to an executable with a list of arguments.
* All stdio is inherited directly from the current process.
*/
export default function shell(cmd: string, args: string[], cwd: string): Promise<undefined> {
let child = spawn(cmd, args, { stdio: 'inherit', shell: true, cwd });
let resolve: (result: undefined) => void;
let reject: (error: Error) => void;
let result: Promise<undefined> = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
child.on('exit', (code) => {
if (code == null) {
process.exit(1);
}
if (code !== 0) {
process.exit(code);
}
resolve(undefined);
});
child.on('error', (error) => {
reject(error);
});
return result;
}

View File

@ -0,0 +1,100 @@
import { assert } from 'chai';
import { spawn } from 'child_process';
import execa from 'execa';
import * as path from 'path';
import { promises as fs } from 'fs';
import * as TOML from 'toml';
import expect from '../dev/expect';
import rimraf from '../dev/rimraf';
const NODE: string = process.execPath;
const CREATE_NEON = path.join(__dirname, '..', 'dist', 'src', 'bin', 'create-neon.js');
describe('Command-line argument validation', () => {
it('requires an argument', async () => {
try {
await execa(NODE, [CREATE_NEON]);
assert.fail("should fail when no argument is supplied");
} catch (expected) {
assert.isTrue(true);
}
});
it('fails if the directory already exists', async () => {
try {
await execa(NODE, [CREATE_NEON, 'src']);
assert.fail("should fail when directory exists");
} catch (expected) {
assert.isTrue(true);
}
});
});
const PROJECT = 'create-neon-test-project';
describe('Project creation', () => {
afterEach(async () => {
await rimraf(PROJECT, { maxBusyTries: 3, emfileWait: 100 });
});
it('succeeds with all default answers', async () => {
try {
await expect(spawn(NODE, [CREATE_NEON, PROJECT]), {
'package name:': '',
'version:': '',
'description:': '',
'git repository:': '',
'keywords:': '',
'author:': '',
'license:': '',
'Is this OK?': ''
});
} catch (error) {
assert.fail("create-neon unexpectedly failed: " + error.message);
}
let json = JSON.parse(await fs.readFile(path.join(PROJECT, 'package.json'), { encoding: 'utf8' }));
assert.strictEqual(json.name, PROJECT);
assert.strictEqual(json.main, 'index.node');
assert.strictEqual(json.version, '0.1.0');
assert.strictEqual(json.scripts.test, 'cargo test');
assert.strictEqual(json.license, 'ISC');
assert.strictEqual(json.description, '');
assert.strictEqual(json.author, '');
let toml = TOML.parse(await fs.readFile(path.join(PROJECT, 'Cargo.toml'), { encoding: 'utf8' }));
assert.strictEqual(toml.package.name, PROJECT);
assert.strictEqual(toml.package.version, '0.1.0');
assert.strictEqual(toml.package.license, 'ISC');
assert.deepEqual(toml.lib['crate-type'], ['cdylib']);
});
it('handles quotation marks in author and description', async () => {
try {
await expect(spawn(NODE, [CREATE_NEON, PROJECT]), {
'package name:': '',
'version:': '',
'description:': 'the "hello world" of examples',
'git repository:': '',
'keywords:': '',
'author:': '"Dave Herman" <dherman@example.com>',
'license:': '',
'Is this OK?': ''
});
} catch (error) {
assert.fail("create-neon unexpectedly failed");
}
let json = JSON.parse(await fs.readFile(path.join(PROJECT, 'package.json'), { encoding: 'utf8' }));
assert.strictEqual(json.description, 'the "hello world" of examples');
assert.strictEqual(json.author, '"Dave Herman" <dherman@example.com>');
let toml = TOML.parse(await fs.readFile(path.join(PROJECT, 'Cargo.toml'), { encoding: 'utf8' }));
assert.strictEqual(toml.package.description, 'the "hello world" of examples');
assert.deepEqual(toml.package.authors, ['"Dave Herman" <dherman@example.com>']);
});
});

31
create-neon/tsconfig.json Normal file
View File

@ -0,0 +1,31 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"lib": [
"es6",
"es7"
],
"allowJs": false,
"sourceMap": false,
"outDir": "dist",
"esModuleInterop": true,
"strict": true,
"noImplicitReturns": true,
"allowUnreachableCode": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"resolveJsonModule": true
},
"include": [
"src/**/*",
"dev/**/*",
"test/**/*"
],
"exclude": [
"./node_modules/",
"./dist/"
]
}

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,7 +1,7 @@
use crate::borrow::LoanError;
use std;
use std::os::raw::c_void;
use std::collections::HashSet;
use borrow::LoanError;
use std::os::raw::c_void;
pub unsafe trait Pointer {
unsafe fn as_ptr(&self) -> *const c_void;
@ -31,14 +31,14 @@ unsafe impl<'a, T> Pointer for &'a mut T {
pub struct Ledger {
immutable_loans: HashSet<*const c_void>,
mutable_loans: HashSet<*const c_void>
mutable_loans: HashSet<*const c_void>,
}
impl Ledger {
pub fn new() -> Self {
Ledger {
immutable_loans: HashSet::new(),
mutable_loans: HashSet::new()
mutable_loans: HashSet::new(),
}
}

View File

@ -1,66 +1,91 @@
//! 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;
use std::ops::{Deref, DerefMut, Drop};
use std::fmt;
use std::ops::{Deref, DerefMut, Drop};
use std::os::raw::c_void;
use context::Lock;
use self::internal::Pointer;
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 {
/// The type of the value's internal contents.
type Target: Pointer;
/// Borrow the contents of this value immutably.
///
///
/// If there is already an outstanding mutable loan for this value, this method panics.
fn borrow<'a>(self, lock: &'a Lock<'a>) -> Ref<'a, Self::Target> {
match self.try_borrow(lock) {
Ok(r) => r,
Err(e) => panic!("{}", e)
Err(e) => panic!("{}", e),
}
}
/// Borrow the contents of this value immutably.
///
///
/// If there is already an outstanding mutable loan for this value, this method fails with a `LoanError`.
fn try_borrow<'a>(self, lock: &'a Lock<'a>) -> Result<Ref<'a, Self::Target>, LoanError>;
}
/// A trait for JS values whose internal contents can be borrowed mutably by Rust while the JS engine is locked.
pub trait BorrowMut: Borrow {
/// Borrow the contents of this value mutably.
///
///
/// If there is already an outstanding loan for this value, this method panics.
fn borrow_mut<'a>(self, lock: &'a Lock<'a>) -> RefMut<'a, Self::Target> {
match self.try_borrow_mut(lock) {
Ok(r) => r,
Err(e) => panic!("{}", e)
Err(e) => panic!("{}", e),
}
}
/// Borrow the contents of this value mutably.
///
///
/// If there is already an outstanding loan for this value, this method panics.
fn try_borrow_mut<'a>(self, lock: &'a Lock<'a>) -> Result<RefMut<'a, Self::Target>, LoanError>;
}
/// An error produced by a failed loan in the `Borrow` or `BorrowMut` traits.
pub enum LoanError {
/// Indicates that there is already an outstanding mutable loan for the object at this address.
Mutating(*const c_void),
/// Indicates that there is already an outstanding immutable loan for the object at this address.
Frozen(*const c_void)
Frozen(*const c_void),
}
impl fmt::Display for LoanError {
@ -79,7 +104,7 @@ impl fmt::Display for LoanError {
/// An immutable reference to the contents of a borrowed JS value.
pub struct Ref<'a, T: Pointer> {
pointer: T,
lock: &'a Lock<'a>
lock: &'a Lock<'a>,
}
impl<'a, T: Pointer> Ref<'a, T> {
@ -108,7 +133,7 @@ impl<'a, T: Pointer> Deref for Ref<'a, T> {
/// A mutable reference to the contents of a borrowed JS value.
pub struct RefMut<'a, T: Pointer> {
pointer: T,
lock: &'a Lock<'a>
lock: &'a Lock<'a>,
}
impl<'a, T: Pointer> RefMut<'a, T> {

View File

@ -1,33 +1,39 @@
use std;
use super::ModuleContext;
use crate::handle::Handle;
#[cfg(feature = "legacy-runtime")]
use std::any::Any;
use std::boxed::Box;
use std::cell::Cell;
use std::mem::MaybeUninit;
use std::os::raw::c_void;
#[cfg(feature = "legacy-runtime")]
use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
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;
use types::{JsObject, JsValue};
use handle::Handle;
use object::class::ClassMap;
use result::NeonResult;
use super::ModuleContext;
#[cfg(feature = "legacy-runtime")]
use std::any::Any;
use std::cell::{Cell, RefCell};
use std::mem::MaybeUninit;
#[cfg(feature = "legacy-runtime")]
use std::os::raw::c_void;
#[cfg(feature = "legacy-runtime")]
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
#[cfg(feature = "legacy-runtime")]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Env(raw::Isolate);
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Env(raw::Env);
thread_local! {
#[allow(unused)]
pub(crate) static IS_RUNNING: RefCell<bool> = RefCell::new(false);
}
#[cfg(feature = "legacy-runtime")]
extern "C" fn drop_class_map(map: Box<ClassMap>) {
std::mem::drop(map);
}
@ -39,47 +45,41 @@ impl Env {
ptr
}
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
pub(crate) fn to_raw(self) -> raw::Env {
let Self(ptr) = self;
ptr
}
#[cfg(feature = "legacy-runtime")]
pub(crate) fn class_map(&mut self) -> &mut ClassMap {
let mut ptr: *mut c_void = unsafe { neon_runtime::class::get_class_map(self.to_raw()) };
if ptr.is_null() {
let b: Box<ClassMap> = Box::new(ClassMap::new());
let raw = Box::into_raw(b);
ptr = unsafe { std::mem::transmute(raw) };
ptr = raw.cast();
let free_map: *mut c_void = unsafe { std::mem::transmute(drop_class_map as usize) };
unsafe {
neon_runtime::class::set_class_map(self.to_raw(), ptr, free_map);
}
}
unsafe { std::mem::transmute(ptr) }
}
#[cfg(feature = "napi-runtime")]
pub(crate) fn current() -> Env {
panic!("Context::current() will not implemented with n-api")
unsafe { &mut *ptr.cast() }
}
#[cfg(feature = "legacy-runtime")]
pub(crate) fn current() -> Env {
unsafe {
std::mem::transmute(neon_runtime::call::current_isolate())
}
unsafe { std::mem::transmute(neon_runtime::call::current_isolate()) }
}
}
pub struct ScopeMetadata {
env: Env,
active: Cell<bool>
active: Cell<bool>,
}
pub struct Scope<'a, R: Root + 'static> {
pub metadata: ScopeMetadata,
pub handle_scope: &'a mut R
pub handle_scope: &'a mut R,
}
impl<'a, R: Root + 'static> Scope<'a, R> {
@ -92,9 +92,9 @@ impl<'a, R: Root + 'static> Scope<'a, R> {
let scope = Scope {
metadata: ScopeMetadata {
env,
active: Cell::new(true)
active: Cell::new(true),
},
handle_scope: &mut handle_scope
handle_scope: &mut handle_scope,
};
f(scope)
};
@ -122,12 +122,17 @@ pub trait ContextInternal<'a>: Sized {
}
}
fn activate(&self) { self.scope_metadata().active.set(true); }
fn deactivate(&self) { self.scope_metadata().active.set(false); }
fn activate(&self) {
self.scope_metadata().active.set(true);
}
fn deactivate(&self) {
self.scope_metadata().active.set(false);
}
#[cfg(feature = "legacy-runtime")]
fn try_catch_internal<'b: 'a, T, F>(&mut self, f: F) -> Result<T, Handle<'a, JsValue>>
where F: FnOnce(&mut Self) -> NeonResult<T>,
fn try_catch_internal<T, F>(&mut self, f: F) -> Result<T, Handle<'a, JsValue>>
where
F: FnOnce(&mut Self) -> NeonResult<T>,
{
// A closure does not have a guaranteed layout, so we need to box it in order to pass
// a pointer to it across the boundary into C++.
@ -138,12 +143,14 @@ pub trait ContextInternal<'a>: Sized {
let mut unwind_value: MaybeUninit<*mut c_void> = MaybeUninit::zeroed();
let ctrl = unsafe {
neon_runtime::try_catch::with(try_catch_glue::<Self, T, F>,
rust_thunk as *mut c_void,
(self as *mut Self) as *mut c_void,
ok.as_mut_ptr() as *mut c_void,
err.as_mut_ptr(),
unwind_value.as_mut_ptr())
neon_runtime::try_catch::with(
try_catch_glue::<Self, T, F>,
rust_thunk as *mut c_void,
(self as *mut Self) as *mut c_void,
ok.as_mut_ptr() as *mut c_void,
err.as_mut_ptr(),
unwind_value.as_mut_ptr(),
)
};
match ctrl {
@ -164,9 +171,10 @@ pub trait ContextInternal<'a>: Sized {
}
}
#[cfg(feature = "napi-runtime")]
fn try_catch_internal<'b: 'a, T, F>(&mut self, f: F) -> Result<T, Handle<'a, JsValue>>
where F: FnOnce(&mut Self) -> NeonResult<T>
#[cfg(feature = "napi-1")]
fn try_catch_internal<T, F>(&mut self, f: F) -> Result<T, Handle<'a, JsValue>>
where
F: FnOnce(&mut Self) -> NeonResult<T>,
{
let result = f(self);
let mut local: MaybeUninit<raw::Local> = MaybeUninit::zeroed();
@ -183,15 +191,18 @@ pub trait ContextInternal<'a>: Sized {
}
#[cfg(feature = "legacy-runtime")]
extern "C" fn try_catch_glue<'a, 'b: 'a, C, T, F>(rust_thunk: *mut c_void,
cx: *mut c_void,
returned: *mut c_void,
unwind_value: *mut *mut c_void) -> TryCatchControl
where C: ContextInternal<'a>,
F: FnOnce(&mut C) -> NeonResult<T>,
extern "C" fn try_catch_glue<'a, 'b: 'a, C, T, F>(
rust_thunk: *mut c_void,
cx: *mut c_void,
returned: *mut c_void,
unwind_value: *mut *mut c_void,
) -> TryCatchControl
where
C: ContextInternal<'a>,
F: FnOnce(&mut C) -> NeonResult<T>,
{
let f: F = *unsafe { Box::from_raw(rust_thunk as *mut F) };
let cx: &mut C = unsafe { std::mem::transmute(cx) };
let cx: &mut C = unsafe { &mut *cx.cast() };
// The mutable reference to the context is a fiction of the Neon library,
// since it doesn't actually contain any data in the Rust memory space,
@ -203,18 +214,16 @@ extern "C" fn try_catch_glue<'a, 'b: 'a, C, T, F>(rust_thunk: *mut c_void,
Ok(Ok(result)) => unsafe {
(returned as *mut T).write(result);
TryCatchControl::Returned
}
},
// No Rust panic, caught a JS exception.
Ok(Err(_)) => {
TryCatchControl::Threw
}
Ok(Err(_)) => TryCatchControl::Threw,
// Rust panicked.
Err(err) => unsafe {
// A panic value has an undefined layout, so wrap it in an extra box.
let boxed = Box::new(err);
*unwind_value = Box::into_raw(boxed) as *mut c_void;
TryCatchControl::Panicked
}
},
}
}
@ -227,9 +236,20 @@ pub fn initialize_module(exports: Handle<JsObject>, init: fn(ModuleContext) -> N
});
}
#[cfg(feature = "napi-runtime")]
pub fn initialize_module(env: raw::Env, exports: Handle<JsObject>, init: fn(ModuleContext) -> NeonResult<()>) {
neon_runtime::setup();
#[cfg(feature = "napi-1")]
pub fn initialize_module(
env: raw::Env,
exports: Handle<JsObject>,
init: fn(ModuleContext) -> NeonResult<()>,
) {
unsafe {
neon_runtime::setup(env);
}
IS_RUNNING.with(|v| {
*v.borrow_mut() = true;
});
ModuleContext::with(Env(env), exports, |cx| {
let _ = init(cx);
});

View File

@ -1,29 +1,184 @@
//! Node _execution contexts_, which manage access to the JavaScript engine at various points in the Node.js runtime lifecycle.
//! 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 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 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 = "napi-1")]
use smallvec::SmallVec;
use std;
use std::cell::RefCell;
use std::convert::Into;
use std::marker::PhantomData;
use std::os::raw::c_void;
use std::panic::UnwindSafe;
use neon_runtime;
use neon_runtime::raw;
use borrow::{Ref, RefMut, Borrow, BorrowMut};
use borrow::internal::Ledger;
use context::internal::Env;
use handle::{Managed, Handle};
use types::{JsValue, Value, JsObject, JsArray, JsFunction, JsBoolean, JsNumber, JsString, StringResult, JsNull, JsUndefined};
#[cfg(feature = "napi-runtime")]
use types::boxed::{Finalize, JsBox};
use types::binary::{JsArrayBuffer, JsBuffer};
use types::error::JsError;
use object::{Object, This};
use object::class::Class;
use result::{NeonResult, JsResult, Throw};
#[cfg(feature = "napi-runtime")]
use smallvec::SmallVec;
use self::internal::{ContextInternal, Scope, ScopeMetadata};
#[repr(C)]
@ -41,14 +196,17 @@ impl CallbackInfo<'_> {
}
}
pub unsafe fn with_cx<T: This, U, F: for<'a> FnOnce(CallContext<'a, T>) -> U>(&self, env: Env, f: F) -> U {
pub unsafe fn with_cx<T: This, U, F: for<'a> FnOnce(CallContext<'a, T>) -> U>(
&self,
env: Env,
f: F,
) -> U {
CallContext::<T>::with(env, self, f)
}
pub fn set_return<'a, 'b, T: Value>(&'a self, value: Handle<'b, T>) {
unsafe {
neon_runtime::call::set_return(self.info, value.to_raw())
}
#[cfg(feature = "legacy-runtime")]
pub fn set_return<T: Value>(&self, value: Handle<T>) {
unsafe { neon_runtime::call::set_return(self.info, value.to_raw()) }
}
#[cfg(feature = "legacy-runtime")]
@ -60,7 +218,7 @@ impl CallbackInfo<'_> {
}
}
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
fn kind<'b, C: Context<'b>>(&self, cx: &C) -> CallKind {
if unsafe { neon_runtime::call::is_construct(cx.env().to_raw(), self.info) } {
CallKind::Construct
@ -70,9 +228,7 @@ impl CallbackInfo<'_> {
}
pub fn len<'b, C: Context<'b>>(&self, cx: &C) -> i32 {
unsafe {
neon_runtime::call::len(cx.env().to_raw(), self.info)
}
unsafe { neon_runtime::call::len(cx.env().to_raw(), self.info) }
}
#[cfg(feature = "legacy-runtime")]
@ -87,11 +243,9 @@ impl CallbackInfo<'_> {
}
}
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
pub fn argv<'b, C: Context<'b>>(&self, cx: &mut C) -> SmallVec<[raw::Local; 8]> {
unsafe {
neon_runtime::call::argv(cx.env().to_raw(), self.info)
}
unsafe { neon_runtime::call::argv(cx.env().to_raw(), self.info) }
}
pub fn this<'b, C: Context<'b>>(&self, cx: &mut C) -> raw::Local {
@ -104,20 +258,22 @@ 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
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,
phantom: PhantomData<&'a ()>
phantom: PhantomData<&'a ()>,
}
impl<'a> Lock<'a> {
@ -130,13 +286,14 @@ 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> {
/// Lock the JavaScript engine, returning an RAII guard that keeps the lock active as long as the guard is alive.
///
///
/// If this is not the currently active context (for example, if it was used to spawn a scoped context with `execute_scoped` or `compute_scoped`), this method will panic.
fn lock(&self) -> Lock<'_> {
self.check_active();
@ -144,9 +301,9 @@ pub trait Context<'a>: ContextInternal<'a> {
}
/// Convenience method for locking the JavaScript engine and borrowing a single JS value's internals.
///
///
/// # Example:
///
///
/// ```no_run
/// # use neon::prelude::*;
/// # fn my_neon_function(mut cx: FunctionContext) -> JsResult<JsNumber> {
@ -156,16 +313,17 @@ pub trait Context<'a>: ContextInternal<'a> {
/// # Ok(n)
/// # }
/// ```
///
///
/// Note: the borrowed value is required to be a reference to a handle instead of a handle
/// as a workaround for a [Rust compiler bug](https://github.com/rust-lang/rust/issues/29997).
/// We may be able to generalize this compatibly in the future when the Rust bug is fixed,
/// but while the extra `&` is a small ergonomics regression, this API is still a nice
/// convenience.
fn borrow<'c, V, T, F>(&self, v: &'c Handle<V>, f: F) -> T
where V: Value,
&'c V: Borrow,
F: for<'b> FnOnce(Ref<'b, <&'c V as Borrow>::Target>) -> T
where
V: Value,
&'c V: Borrow,
F: for<'b> FnOnce(Ref<'b, <&'c V as Borrow>::Target>) -> T,
{
let lock = self.lock();
let contents = v.borrow(&lock);
@ -173,9 +331,9 @@ pub trait Context<'a>: ContextInternal<'a> {
}
/// Convenience method for locking the JavaScript engine and mutably borrowing a single JS value's internals.
///
///
/// # Example:
///
///
/// ```no_run
/// # use neon::prelude::*;
/// # fn my_neon_function(mut cx: FunctionContext) -> JsResult<JsUndefined> {
@ -187,16 +345,17 @@ pub trait Context<'a>: ContextInternal<'a> {
/// # Ok(cx.undefined())
/// # }
/// ```
///
///
/// Note: the borrowed value is required to be a reference to a handle instead of a handle
/// as a workaround for a [Rust compiler bug](https://github.com/rust-lang/rust/issues/29997).
/// We may be able to generalize this compatibly in the future when the Rust bug is fixed,
/// but while the extra `&mut` is a small ergonomics regression, this API is still a nice
/// convenience.
fn borrow_mut<'c, V, T, F>(&self, v: &'c mut Handle<V>, f: F) -> T
where V: Value,
&'c mut V: BorrowMut,
F: for<'b> FnOnce(RefMut<'b, <&'c mut V as Borrow>::Target>) -> T
where
V: Value,
&'c mut V: BorrowMut,
F: for<'b> FnOnce(RefMut<'b, <&'c mut V as Borrow>::Target>) -> T,
{
let lock = self.lock();
let contents = v.borrow_mut(&lock);
@ -204,12 +363,13 @@ pub trait Context<'a>: ContextInternal<'a> {
}
/// Executes a computation in a new memory management scope.
///
///
/// Handles created in the new scope are kept alive only for the duration of the computation and cannot escape.
///
///
/// This method can be useful for limiting the life of temporary values created during long-running computations, to prevent leaks.
fn execute_scoped<T, F>(&self, f: F) -> T
where F: for<'b> FnOnce(ExecuteContext<'b>) -> T
where
F: for<'b> FnOnce(ExecuteContext<'b>) -> T,
{
self.check_active();
self.deactivate();
@ -219,32 +379,37 @@ pub trait Context<'a>: ContextInternal<'a> {
}
/// Executes a computation in a new memory management scope and computes a single result value that outlives the computation.
///
///
/// Handles created in the new scope are kept alive only for the duration of the computation and cannot escape, with the exception of the result value, which is rooted in the outer context.
///
///
/// This method can be useful for limiting the life of temporary values created during long-running computations, to prevent leaks.
fn compute_scoped<V, F>(&self, f: F) -> JsResult<'a, V>
where V: Value,
F: for<'b, 'c> FnOnce(ComputeContext<'b, 'c>) -> JsResult<'b, V>
where
V: Value,
F: for<'b, 'c> FnOnce(ComputeContext<'b, 'c>) -> JsResult<'b, V>,
{
self.check_active();
self.deactivate();
let result = ComputeContext::with(self, |cx| {
unsafe {
let escapable_handle_scope = cx.scope.handle_scope as *mut raw::EscapableHandleScope;
let escapee = f(cx)?;
let mut result_local: raw::Local = std::mem::zeroed();
neon_runtime::scope::escape(self.env().to_raw(), &mut result_local, escapable_handle_scope, escapee.to_raw());
Ok(Handle::new_internal(V::from_raw(self.env(), result_local)))
}
let result = ComputeContext::with(self, |cx| unsafe {
let escapable_handle_scope = cx.scope.handle_scope as *mut raw::EscapableHandleScope;
let escapee = f(cx)?;
let mut result_local: raw::Local = std::mem::zeroed();
neon_runtime::scope::escape(
self.env().to_raw(),
&mut result_local,
escapable_handle_scope,
escapee.to_raw(),
);
Ok(Handle::new_internal(V::from_raw(self.env(), result_local)))
});
self.activate();
result
}
#[cfg(feature = "try-catch-api")]
fn try_catch<'b: 'a, T, F>(&mut self, f: F) -> Result<T, Handle<'a, JsValue>>
where F: FnOnce(&mut Self) -> NeonResult<T>
fn try_catch<T, F>(&mut self, f: F) -> Result<T, Handle<'a, JsValue>>
where
F: FnOnce(&mut Self) -> NeonResult<T>,
{
self.try_catch_internal(f)
}
@ -260,14 +425,14 @@ pub trait Context<'a>: ContextInternal<'a> {
}
/// Convenience method for creating a `JsString` value.
///
///
/// If the string exceeds the limits of the JS engine, this method panics.
fn string<S: AsRef<str>>(&mut self, s: S) -> Handle<'a, JsString> {
JsString::new(self, s)
}
/// Convenience method for creating a `JsString` value.
///
///
/// If the string exceeds the limits of the JS engine, this method returns an `Err` value.
fn try_string<S: AsRef<str>>(&mut self, s: S) -> StringResult<'a> {
JsString::try_new(self, s)
@ -277,7 +442,7 @@ pub trait Context<'a>: ContextInternal<'a> {
fn null(&mut self) -> Handle<'a, JsNull> {
#[cfg(feature = "legacy-runtime")]
return JsNull::new();
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
return JsNull::new(self);
}
@ -285,7 +450,7 @@ pub trait Context<'a>: ContextInternal<'a> {
fn undefined(&mut self) -> Handle<'a, JsUndefined> {
#[cfg(feature = "legacy-runtime")]
return JsUndefined::new();
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
return JsUndefined::new(self);
}
@ -309,17 +474,21 @@ pub trait Context<'a>: ContextInternal<'a> {
JsBuffer::new(self, size)
}
/// Convenience method for creating a `JsDate` value.
#[cfg(feature = "napi-5")]
fn date(&mut self, value: impl Into<f64>) -> Result<Handle<'a, JsDate>, DateError> {
JsDate::new(self, value)
}
/// Produces a handle to the JavaScript global object.
fn global(&mut self) -> Handle<'a, JsObject> {
JsObject::build(|out| {
unsafe {
neon_runtime::scope::get_global(self.env().to_raw(), out);
}
JsObject::build(|out| unsafe {
neon_runtime::scope::get_global(self.env().to_raw(), out);
})
}
/// Throws a JS value.
fn throw<'b, T: Value, U>(&mut self, v: Handle<'b, T>) -> NeonResult<U> {
fn throw<T: Value, U>(&mut self, v: Handle<T>) -> NeonResult<U> {
unsafe {
neon_runtime::error::throw(self.env().to_raw(), v.to_raw());
}
@ -359,11 +528,11 @@ pub trait Context<'a>: ContextInternal<'a> {
self.throw(err)
}
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
/// Convenience method for wrapping a value in a `JsBox`.
///
/// # Example:
///
///
/// ```rust
/// # use neon::prelude::*;
/// struct Point(usize, usize);
@ -379,40 +548,55 @@ pub trait Context<'a>: ContextInternal<'a> {
fn boxed<U: Finalize + Send + 'static>(&mut self, v: U) -> Handle<'a, JsBox<U>> {
JsBox::new(self, v)
}
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
/// Creates an unbounded queue of events to be executed on a JavaScript thread
fn queue(&mut self) -> EventQueue {
EventQueue::new(self)
}
}
/// 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>
exports: Handle<'a, JsObject>,
}
impl<'a> UnwindSafe for ModuleContext<'a> { }
impl<'a> UnwindSafe for ModuleContext<'a> {}
impl<'a> ModuleContext<'a> {
pub(crate) fn with<T, F: for<'b> FnOnce(ModuleContext<'b>) -> T>(env: Env, exports: Handle<'a, JsObject>, f: F) -> T {
pub(crate) fn with<T, F: for<'b> FnOnce(ModuleContext<'b>) -> T>(
env: Env,
exports: Handle<'a, JsObject>,
f: F,
) -> T {
// These assertions ensure the proper amount of space is reserved on the rust stack
// This is only necessary in the legacy runtime.
#[cfg(feature = "legacy-runtime")]
{
debug_assert!(unsafe { neon_runtime::scope::size() } <= std::mem::size_of::<raw::HandleScope>());
debug_assert!(unsafe { neon_runtime::scope::alignment() } <= std::mem::align_of::<raw::HandleScope>());
debug_assert!(
unsafe { neon_runtime::scope::size() } <= std::mem::size_of::<raw::HandleScope>()
);
debug_assert!(
unsafe { neon_runtime::scope::alignment() }
<= std::mem::align_of::<raw::HandleScope>()
);
}
Scope::with(env, |scope| {
f(ModuleContext {
scope,
exports
})
})
Scope::with(env, |scope| f(ModuleContext { scope, exports }))
}
/// Convenience method for exporting a Neon function from a module.
pub fn export_function<T: Value>(&mut self, key: &str, f: fn(FunctionContext) -> JsResult<T>) -> NeonResult<()> {
pub fn export_function<T: Value>(
&mut self,
key: &str,
f: fn(FunctionContext) -> JsResult<T>,
) -> NeonResult<()> {
let value = JsFunction::new(self, f)?.upcast::<JsValue>();
self.exports.set(self, key, value)?;
Ok(())
}
#[cfg(feature = "legacy-runtime")]
/// Convenience method for exporting a Neon class constructor from a module.
pub fn export_class<T: Class>(&mut self, key: &str) -> NeonResult<()> {
let constructor = T::constructor(self)?;
@ -438,18 +622,19 @@ impl<'a> ContextInternal<'a> for ModuleContext<'a> {
}
}
impl<'a> Context<'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>
scope: Scope<'a, raw::HandleScope>,
}
impl<'a> ExecuteContext<'a> {
pub(crate) fn with<T, C: Context<'a>, F: for<'b> FnOnce(ExecuteContext<'b>) -> T>(cx: &C, f: F) -> T {
Scope::with(cx.env(), |scope| {
f(ExecuteContext { scope })
})
pub(crate) fn with<T, C: Context<'a>, F: for<'b> FnOnce(ExecuteContext<'b>) -> T>(
cx: &C,
f: F,
) -> T {
Scope::with(cx.env(), |scope| f(ExecuteContext { scope }))
}
}
@ -459,22 +644,25 @@ impl<'a> ContextInternal<'a> for ExecuteContext<'a> {
}
}
impl<'a> Context<'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 ()>,
phantom_outer: PhantomData<&'outer ()>
phantom_outer: PhantomData<&'outer ()>,
}
impl<'a, 'b> ComputeContext<'a, 'b> {
pub(crate) fn with<T, C: Context<'a>, F: for<'c, 'd> FnOnce(ComputeContext<'c, 'd>) -> T>(cx: &C, f: F) -> T {
pub(crate) fn with<T, C: Context<'a>, F: for<'c, 'd> FnOnce(ComputeContext<'c, 'd>) -> T>(
cx: &C,
f: F,
) -> T {
Scope::with(cx.env(), |scope| {
f(ComputeContext {
scope,
phantom_inner: PhantomData,
phantom_outer: PhantomData
phantom_outer: PhantomData,
})
})
}
@ -486,54 +674,67 @@ impl<'a, 'b> ContextInternal<'a> for ComputeContext<'a, 'b> {
}
}
impl<'a, 'b> Context<'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> {
scope: Scope<'a, raw::HandleScope>,
info: &'a CallbackInfo<'a>,
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
arguments: Option<SmallVec<[raw::Local; 8]>>,
phantom_type: PhantomData<T>
phantom_type: PhantomData<T>,
}
impl<'a, T: This> UnwindSafe for CallContext<'a, T> { }
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();
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
let kind = self.info.kind(self);
kind
}
pub(crate) fn with<U, F: for<'b> FnOnce(CallContext<'b, T>) -> U>(env: Env, info: &'a CallbackInfo<'a>, f: F) -> U {
pub(crate) fn with<U, F: for<'b> FnOnce(CallContext<'b, T>) -> U>(
env: Env,
info: &'a CallbackInfo<'a>,
f: F,
) -> U {
Scope::with(env, |scope| {
f(CallContext {
scope,
info,
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
arguments: None,
phantom_type: PhantomData
phantom_type: PhantomData,
})
})
}
/// Indicates the number of arguments that were passed to the function.
pub fn len(&self) -> i32 { self.info.len(self) }
pub fn len(&self) -> i32 {
self.info.len(self)
}
/// Indicates if no arguments were passed to the function.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Produces the `i`th argument, or `None` if `i` is greater than or equal to `self.len()`.
pub fn argument_opt(&mut self, i: i32) -> Option<Handle<'a, JsValue>> {
#[cfg(feature = "legacy-runtime")]
{ self.info.get(self, i) }
{
self.info.get(self, i)
}
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
{
let local = if let Some(arguments) = &self.arguments {
arguments.get(i as usize).cloned()
@ -553,7 +754,7 @@ impl<'a, T: This> CallContext<'a, T> {
pub fn argument<V: Value>(&mut self, i: i32) -> JsResult<'a, V> {
match self.argument_opt(i) {
Some(v) => v.downcast_or_throw(self),
None => self.throw_type_error("not enough arguments")
None => self.throw_type_error("not enough arguments"),
}
}
@ -561,7 +762,7 @@ impl<'a, T: This> CallContext<'a, T> {
pub fn this(&mut self) -> Handle<'a, T> {
#[cfg(feature = "legacy-runtime")]
let this = T::as_this(self.info.this(self));
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
let this = T::as_this(self.env(), self.info.this(self));
Handle::new_internal(this)
@ -574,27 +775,31 @@ impl<'a, T: This> ContextInternal<'a> for CallContext<'a, T> {
}
}
impl<'a, T: This> Context<'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.
scope: Scope<'a, raw::InheritedHandleScope>
scope: Scope<'a, raw::InheritedHandleScope>,
}
impl<'a> TaskContext<'a> {
#[cfg(feature = "legacy-runtime")]
pub(crate) fn with<T, F: for<'b> FnOnce(TaskContext<'b>) -> T>(f: F) -> T {
let env = Env::current();
Scope::with(env, |scope| {
f(TaskContext { scope })
})
Scope::with(env, |scope| f(TaskContext { scope }))
}
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
pub(crate) fn with_context<T, F: for<'b> FnOnce(TaskContext<'b>) -> T>(env: Env, f: F) -> T {
Scope::with(env, |scope| f(TaskContext { scope }))
}
}
@ -604,29 +809,27 @@ impl<'a> ContextInternal<'a> for TaskContext<'a> {
}
}
impl<'a> Context<'a> for TaskContext<'a> { }
impl<'a> Context<'a> for TaskContext<'a> {}
/// A view of the JS engine in the context of a finalize method on garbage collection
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
pub(crate) struct FinalizeContext<'a> {
scope: Scope<'a, raw::HandleScope>
scope: Scope<'a, raw::HandleScope>,
}
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
impl<'a> FinalizeContext<'a> {
pub(crate) fn with<T, F: for<'b> FnOnce(FinalizeContext<'b>) -> T>(env: Env, f: F) -> T {
Scope::with(env, |scope| {
f(FinalizeContext { scope })
})
Scope::with(env, |scope| f(FinalizeContext { scope }))
}
}
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
impl<'a> ContextInternal<'a> for FinalizeContext<'a> {
fn scope_metadata(&self) -> &ScopeMetadata {
&self.scope.metadata
}
}
#[cfg(feature = "napi-runtime")]
impl<'a> Context<'a> for FinalizeContext<'a> { }
#[cfg(feature = "napi-1")]
impl<'a> Context<'a> for FinalizeContext<'a> {}

View File

@ -0,0 +1,94 @@
//! Helper to run a callback in the libuv main thread.
use std::os::raw::c_void;
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;
type EventContext<'a> = crate::context::TaskContext<'a>;
struct EventHandlerInner(*mut c_void);
unsafe impl Send for EventHandlerInner {}
unsafe impl Sync for EventHandlerInner {}
impl Drop for EventHandlerInner {
fn drop(&mut self) {
unsafe {
neon_runtime::handler::delete(self.0);
}
}
}
#[derive(Clone)]
pub struct EventHandler(Arc<EventHandlerInner>);
impl EventHandler {
pub fn new<'a, C: Context<'a>, T: Value>(
cx: &C,
this: Handle<T>,
callback: Handle<JsFunction>,
) -> Self {
let cb = unsafe {
neon_runtime::handler::new(cx.env().to_raw(), this.to_raw(), callback.to_raw())
};
EventHandler(Arc::new(EventHandlerInner(cb)))
}
pub fn schedule<T, F>(&self, arg_cb: F)
where
T: Value,
F: for<'a> FnOnce(&mut EventContext<'a>) -> Vec<Handle<'a, T>>,
F: Send + 'static,
{
self.schedule_with(move |cx, this, callback| {
let args = arg_cb(cx);
let _result = callback.call(cx, this, args);
})
}
fn schedule_internal<F>(&self, cb: F)
where
F: FnOnce(&mut EventContext, Handle<JsValue>, Handle<JsFunction>),
F: Send + 'static,
{
let callback = Box::into_raw(Box::new(cb)) as *mut c_void;
unsafe {
neon_runtime::handler::schedule((*self.0).0, callback, handle_callback::<F>);
}
}
pub fn schedule_with<F>(&self, arg_cb: F)
where
F: FnOnce(&mut EventContext, Handle<JsValue>, Handle<JsFunction>),
F: Send + 'static,
{
// HACK: Work around for race condition in `close`. `EventHandler` cannot be
// dropped until all callbacks have executed.
// NOTE: This will still leak memory if the callback is never called
let cloned_cb = self.clone();
self.schedule_internal(move |cx, this, cb| {
arg_cb(cx, this, cb);
let _ = cloned_cb;
});
}
}
unsafe extern "C" fn handle_callback<F>(this: raw::Local, func: raw::Local, callback: *mut c_void)
where
F: FnOnce(&mut EventContext, Handle<JsValue>, Handle<JsFunction>),
F: Send + 'static,
{
EventContext::with(|mut cx: EventContext| {
let this = JsValue::new_internal(this);
let func: Handle<JsFunction> = Handle::new_internal(JsFunction::from_raw(cx.env(), func));
let callback: Box<F> = Box::from_raw(callback as *mut _);
callback(&mut cx, this, func);
})
}

156
src/event/event_queue.rs Normal file
View File

@ -0,0 +1,156 @@
use neon_runtime::raw::Env;
use neon_runtime::tsfn::ThreadsafeFunction;
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 the JavaScript main thread.
///
/// # Example
///
/// The following example spawns a standard Rust thread to complete a computation
/// and calls back to a JavaScript function asynchronously with the result.
///
/// ```
/// # use neon::prelude::*;
/// # fn fibonacci(_: f64) -> f64 { todo!() }
/// fn async_fibonacci(mut cx: FunctionContext) -> JsResult<JsUndefined> {
/// // These types (`f64`, `Root<JsFunction>`, `EventQueue`) may all be sent
/// // across threads.
/// let n = cx.argument::<JsNumber>(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 || {
/// let result = fibonacci(n);
///
/// // 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 = vec![
/// cx.null().upcast::<JsValue>(),
/// cx.number(result).upcast(),
/// ];
///
/// callback.call(&mut cx, this, args)?;
///
/// Ok(())
/// });
/// });
///
/// Ok(cx.undefined())
/// }
/// ```
pub struct EventQueue {
tsfn: ThreadsafeFunction<Callback>,
has_ref: bool,
}
impl std::fmt::Debug for EventQueue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("EventQueue")
}
}
impl EventQueue {
/// Creates an unbounded queue for scheduling closures on the JavaScript
/// main thread
pub fn new<'a, C: Context<'a>>(cx: &mut C) -> Self {
let tsfn = unsafe { ThreadsafeFunction::new(cx.env().to_raw(), Self::callback) };
Self {
tsfn,
has_ref: true,
}
}
/// Allow the Node event loop to exit while this `EventQueue` exists.
/// _Idempotent_
pub fn unref<'a, C: Context<'a>>(&mut self, cx: &mut C) -> &mut Self {
self.has_ref = false;
unsafe { self.tsfn.unref(cx.env().to_raw()) }
self
}
/// Prevent the Node event loop from exiting while this `EventQueue` exists. (Default)
/// _Idempotent_
pub fn reference<'a, C: Context<'a>>(&mut self, cx: &mut C) -> &mut Self {
self.has_ref = true;
unsafe { self.tsfn.reference(cx.env().to_raw()) }
self
}
/// Schedules a closure to execute on the JavaScript thread that created this EventQueue
/// Panics if there is a libuv error
pub fn send<F>(&self, f: F)
where
F: FnOnce(TaskContext) -> NeonResult<()> + Send + 'static,
{
self.try_send(f).unwrap()
}
/// Schedules a closure to execute on the JavaScript thread that created this EventQueue
/// Returns an `Error` if the task could not be scheduled.
pub fn try_send<F>(&self, f: F) -> Result<(), EventQueueError>
where
F: FnOnce(TaskContext) -> NeonResult<()> + Send + 'static,
{
let callback = Box::new(move |env| {
let env = unsafe { std::mem::transmute(env) };
// Note: It is sufficient to use `TaskContext`'s `InheritedHandleScope` because
// N-API creates a `HandleScope` before calling the callback.
TaskContext::with_context(env, move |cx| {
let _ = f(cx);
});
});
self.tsfn.call(callback, None).map_err(|_| EventQueueError)
}
/// Returns a boolean indicating if this `EventQueue` will prevent the Node event
/// queue from exiting.
pub fn has_ref(&self) -> bool {
self.has_ref
}
// Monomorphized trampoline funciton for calling the user provided closure
fn callback(env: Option<Env>, callback: Callback) {
if let Some(env) = env {
callback(env);
} else {
crate::context::internal::IS_RUNNING.with(|v| {
*v.borrow_mut() = false;
});
}
}
}
/// Error indicating that a closure was unable to be scheduled to execute on the event queue
pub struct EventQueueError;
impl std::fmt::Display for EventQueueError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "EventQueueError")
}
}
impl std::fmt::Debug for EventQueueError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
}
impl std::error::Error for EventQueueError {}

View File

@ -1,87 +1,142 @@
//! Helper to run a callback in the libuv main thread.
//! 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/
use std::os::raw::c_void;
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
mod event_queue;
use types::*;
use handle::{Handle, Managed};
use neon_runtime;
use neon_runtime::raw;
use std::sync::Arc;
use context::Context;
use context::internal::ContextInternal;
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
pub use self::event_queue::{EventQueue, EventQueueError};
type EventContext<'a> = crate::context::TaskContext<'a>;
#[cfg(all(not(feature = "napi-1"), feature = "event-handler-api"))]
mod event_handler;
struct EventHandlerInner(*mut c_void);
#[cfg(all(not(feature = "napi-1"), feature = "event-handler-api"))]
pub use self::event_handler::EventHandler;
unsafe impl Send for EventHandlerInner {}
unsafe impl Sync for EventHandlerInner {}
impl Drop for EventHandlerInner {
fn drop(&mut self) {
unsafe {
neon_runtime::handler::delete(self.0);
}
}
}
#[derive(Clone)]
pub struct EventHandler(Arc<EventHandlerInner>);
impl EventHandler {
#[cfg(feature = "napi-runtime")]
pub fn new<'a, C: Context<'a>, T: Value>(cx: &C, this: Handle<T>, callback: Handle<JsFunction>) -> Self {
unimplemented!()
}
#[cfg(feature = "legacy-runtime")]
pub fn new<'a, C: Context<'a>, T: Value>(cx: &C, this: Handle<T>, callback: Handle<JsFunction>) -> Self {
let cb = unsafe {
neon_runtime::handler::new(cx.env().to_raw(), this.to_raw(), callback.to_raw())
};
EventHandler(Arc::new(EventHandlerInner(cb)))
}
pub fn schedule<T, F>(&self, arg_cb: F)
where T: Value,
F: for<'a> FnOnce(&mut EventContext<'a>) -> Vec<Handle<'a, T>>,
F: Send + 'static {
self.schedule_with(move |cx, this, callback| {
let args = arg_cb(cx);
let _result = callback.call(cx, this, args);
})
}
fn schedule_internal<F>(&self, cb: F)
where F: FnOnce(&mut EventContext, Handle<JsValue>, Handle<JsFunction>),
F: Send + 'static {
let callback = Box::into_raw(Box::new(cb)) as *mut c_void;
unsafe {
neon_runtime::handler::schedule((*self.0).0, callback, handle_callback::<F>);
}
}
pub fn schedule_with<F>(&self, arg_cb: F)
where F: FnOnce(&mut EventContext, Handle<JsValue>, Handle<JsFunction>),
F: Send + 'static {
// HACK: Work around for race condition in `close`. `EventHandler` cannot be
// dropped until all callbacks have executed.
// NOTE: This will still leak memory if the callback is never called
let cloned_cb = self.clone();
self.schedule_internal(move |cx, this, cb| {
arg_cb(cx, this, cb);
let _ = cloned_cb;
});
}
}
unsafe extern "C" fn handle_callback<F>(this: raw::Local, func: raw::Local, callback: *mut c_void)
where F: FnOnce(&mut EventContext, Handle<JsValue>, Handle<JsFunction>), F: Send + 'static {
EventContext::with(|mut cx: EventContext| {
let this = JsValue::new_internal(this);
let func: Handle<JsFunction> = Handle::new_internal(JsFunction::from_raw(cx.env(), func));
let callback: Box<F> = Box::from_raw(callback as *mut _);
callback(&mut cx, this, func);
})
}
#[cfg(all(feature = "napi-1", feature = "event-handler-api"))]
compile_error!(
"The `EventHandler` API is not supported with the N-API \
backend. Use `EventQueue` instead."
);

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,47 +1,109 @@
//! 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;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::error::Error;
use std::fmt::{self, Debug, Display};
#[cfg(feature = "napi-1")]
pub(crate) mod root;
#[cfg(feature = "napi-1")]
pub use self::root::Root;
use self::internal::SuperType;
use crate::context::internal::Env;
use crate::context::Context;
use crate::result::{JsResult, JsResultExt};
use crate::types::Value;
use neon_runtime;
use neon_runtime::raw;
use types::Value;
use context::Context;
use context::internal::Env;
use result::{JsResult, JsResultExt};
use self::internal::SuperType;
use std::error::Error;
use std::fmt::{self, Debug, Display};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
/// 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)]
#[derive(Clone, Copy, Debug)]
pub struct Handle<'a, T: Managed + 'a> {
value: T,
phantom: PhantomData<&'a T>
phantom: PhantomData<&'a T>,
}
#[cfg(feature = "legacy-runtime")]
impl<'a, T: Managed + 'a> PartialEq for Handle<'a, T> {
fn eq(&self, other: &Self) -> bool {
unsafe { neon_runtime::mem::same_handle(self.to_raw(), other.to_raw()) }
}
}
impl<'a, T: Managed + 'a> Eq for Handle<'a, T> { }
#[cfg(feature = "legacy-runtime")]
impl<'a, T: Managed + 'a> Eq for Handle<'a, T> {}
impl<'a, T: Managed + 'a> Handle<'a, T> {
pub(crate) fn new_internal(value: T) -> Handle<'a, T> {
Handle {
value: value,
phantom: PhantomData
value,
phantom: PhantomData,
}
}
}
@ -76,22 +138,21 @@ 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> {
fn or_throw<'b, C: Context<'b>>(self, cx: &mut C) -> JsResult<'a, T> {
match self {
Ok(v) => Ok(v),
Err(e) => cx.throw_type_error(&e.to_string())
Err(e) => cx.throw_type_error(&e.to_string()),
}
}
}
impl<'a, T: Value> Handle<'a, T> {
/// Safely upcast a handle to a supertype.
///
///
/// This method does not require an execution context because it only copies a handle.
pub fn upcast<U: Value + SuperType<T>>(&self) -> Handle<'a, U> {
Handle::new_internal(SuperType::upcast_internal(self.value))
@ -99,9 +160,9 @@ impl<'a, T: Value> Handle<'a, T> {
#[cfg(feature = "legacy-runtime")]
/// Tests whether this value is an instance of the given type.
///
///
/// # Example:
///
///
/// ```no_run
/// # use neon::prelude::*;
/// # fn my_neon_function(mut cx: FunctionContext) -> JsResult<JsUndefined> {
@ -116,11 +177,11 @@ impl<'a, T: Value> Handle<'a, T> {
U::is_typeof(Env::current(), self.value)
}
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
/// Tests whether this value is an instance of the given type.
///
///
/// # Example:
///
///
/// ```no_run
/// # use neon::prelude::*;
/// # fn my_neon_function(mut cx: FunctionContext) -> JsResult<JsUndefined> {
@ -143,11 +204,11 @@ impl<'a, T: Value> Handle<'a, T> {
pub fn downcast<U: Value>(&self) -> DowncastResult<'a, T, U> {
match U::downcast(Env::current(), self.value) {
Some(v) => Ok(Handle::new_internal(v)),
None => Err(DowncastError::new())
None => Err(DowncastError::new()),
}
}
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
/// Attempts to downcast a handle to another type, which may fail. A failure
/// to downcast **does not** throw a JavaScript exception, so it's OK to
/// continue interacting with the JS engine if this method produces an `Err`
@ -155,7 +216,7 @@ impl<'a, T: Value> Handle<'a, T> {
pub fn downcast<'b, U: Value, C: Context<'b>>(&self, cx: &mut C) -> DowncastResult<'a, T, U> {
match U::downcast(cx.env(), self.value) {
Some(v) => Ok(Handle::new_internal(v)),
None => Err(DowncastError::new())
None => Err(DowncastError::new()),
}
}
@ -167,7 +228,7 @@ impl<'a, T: Value> Handle<'a, T> {
self.downcast().or_throw(cx)
}
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
/// Attempts to downcast a handle to another type, raising a JavaScript `TypeError`
/// exception on failure. This method is a convenient shorthand, equivalent to
/// `self.downcast::<U>().or_throw::<C>(cx)`.
@ -175,17 +236,27 @@ impl<'a, T: Value> Handle<'a, T> {
self.downcast(cx).or_throw(cx)
}
#[cfg(feature = "napi-1")]
pub fn strict_equals<'b, U: Value, C: Context<'b>>(
&self,
cx: &mut C,
other: Handle<'b, U>,
) -> bool {
unsafe {
neon_runtime::mem::strict_equals(cx.env().to_raw(), self.to_raw(), other.to_raw())
}
}
}
impl<'a, T: Managed> Deref for Handle<'a, T> {
type Target = T;
fn deref<'b>(&'b self) -> &'b T {
fn deref(&self) -> &T {
&self.value
}
}
impl<'a, T: Managed> DerefMut for Handle<'a, T> {
fn deref_mut<'b>(&'b mut self) -> &'b mut T {
fn deref_mut(&mut self) -> &mut T {
&mut self.value
}
}

170
src/handle/root.rs Normal file
View File

@ -0,0 +1,170 @@
use std::ffi::c_void;
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::sync::Arc;
use neon_runtime::reference;
#[cfg(feature = "napi-6")]
use neon_runtime::tsfn::ThreadsafeFunction;
use crate::context::Context;
use crate::handle::Handle;
#[cfg(feature = "napi-6")]
use crate::lifecycle::InstanceData;
use crate::object::Object;
use crate::types::boxed::Finalize;
#[repr(transparent)]
#[derive(Clone)]
pub(crate) struct NapiRef(*mut c_void);
// # Safety
// `NapiRef` are reference counted types that allow references to JavaScript objects
// to outlive a `Context` (`napi_env`). Since access is serialized by obtaining a
// `Context`, they are both `Send` and `Sync`.
// https://nodejs.org/api/n-api.html#n_api_references_to_objects_with_a_lifespan_longer_than_that_of_the_native_method
unsafe impl Send for NapiRef {}
unsafe impl Sync for NapiRef {}
/// A thread-safe handle that holds a reference to a JavaScript object and
/// prevents it from being garbage collected.
///
/// A `Root<T>` may be sent across threads, but the referenced object may
/// only be accessed on the JavaScript thread that created it.
pub struct Root<T> {
internal: NapiRef,
#[cfg(feature = "napi-6")]
drop_queue: Arc<ThreadsafeFunction<NapiRef>>,
_phantom: PhantomData<T>,
}
impl<T> std::fmt::Debug for Root<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Root<{}>", std::any::type_name::<T>())
}
}
// `Root` are intended to be `Send` and `Sync`
// Safety: `Root` contains two types. A `NapiRef` which is `Send` and `Sync` and a
// `PhantomData` that does not impact the safety.
unsafe impl<T> Send for Root<T> {}
unsafe impl<T> Sync for Root<T> {}
impl<T: Object> Root<T> {
/// Create a reference to a JavaScript object. The object will not be
/// garbage collected until the `Root` is dropped. A `Root<T>` may only
/// be dropped on the JavaScript thread that created it.
///
/// The caller _should_ ensure `Root::into_inner` or `Root::drop` is called
/// to properly dispose of the `Root<T>`. If the value is dropped without
/// calling one of these methods:
/// * N-API < 6, Neon will `panic` to notify of the leak
/// * N-API >= 6, Neon will drop from a global queue at a runtime cost
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,
}
}
/// Clone a reference to the contained JavaScript object. This method can
/// be considered identical to the following:
/// ```
/// # use neon::prelude::*;
/// # fn my_neon_function(mut cx: FunctionContext) -> JsResult<JsUndefined> {
/// # let root = cx.argument::<JsObject>(0)?.root(&mut cx);
/// let inner = root.into_inner(&mut cx);
/// let cloned = inner.root(&mut cx);
/// let root = inner.root(&mut cx);
/// # Ok(cx.undefined())
/// # }
/// ```
pub fn clone<'a, C: Context<'a>>(&self, cx: &mut C) -> Self {
let env = cx.env();
let internal = self.internal.0 as *mut _;
unsafe {
reference::reference(env.to_raw(), internal);
};
Self {
internal: self.internal.clone(),
#[cfg(feature = "napi-6")]
drop_queue: Arc::clone(&self.drop_queue),
_phantom: PhantomData,
}
}
/// Safely drop a `Root<T>` without returning the referenced JavaScript
/// object.
pub fn drop<'a, C: Context<'a>>(self, cx: &mut C) {
let env = cx.env().to_raw();
let internal = ManuallyDrop::new(self).internal.0 as *mut _;
unsafe {
reference::unreference(env, internal);
}
}
/// Return the referenced JavaScript object and allow it to be garbage collected
pub fn into_inner<'a, C: Context<'a>>(self, cx: &mut C) -> Handle<'a, T> {
let env = cx.env();
let internal = ManuallyDrop::new(self).internal.0 as *mut _;
let local = unsafe { reference::get(env.to_raw(), internal) };
unsafe {
reference::unreference(env.to_raw(), internal);
}
Handle::new_internal(T::from_raw(env, local))
}
/// Access the inner JavaScript object without consuming the `Root`
/// This method aliases the reference without changing the reference count. It
/// can be used in place of a clone immediately followed by a call to `into_inner`.
pub fn to_inner<'a, C: Context<'a>>(&self, cx: &mut C) -> Handle<'a, T> {
let env = cx.env();
let local = unsafe { reference::get(env.to_raw(), self.internal.0 as *mut _) };
Handle::new_internal(T::from_raw(env, local))
}
}
// Allows putting `Root<T>` directly in a container that implements `Finalize`
// For example, `Vec<Root<T>>` or `JsBox`.
impl<T: Object> Finalize for Root<T> {
fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
self.drop(cx);
}
}
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.
if std::thread::panicking() {
eprintln!("Warning: neon::sync::Root leaked during a panic");
return;
}
// Only panic if the event loop is still running
if let Ok(true) = crate::context::internal::IS_RUNNING.try_with(|v| *v.borrow()) {
panic!(
"Must call `into_inner` or `drop` on `Root` \
https://docs.rs/neon/latest/neon/sync/index.html#drop-safety"
);
}
}
#[cfg(feature = "napi-6")]
fn drop(&mut self) {
let _ = self.drop_queue.call(self.internal.clone(), None);
}
}

View File

@ -1,28 +1,101 @@
//! The [Neon](https://www.neon-bindings.com/) crate provides bindings for writing Node.js plugins with a safe and fast Rust API.
//! 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
extern crate neon_runtime;
extern crate cslice;
extern crate semver;
extern crate smallvec;
#[cfg(feature = "proc-macros")]
extern crate neon_macros;
#[cfg(test)]
#[macro_use]
extern crate lazy_static;
pub mod context;
pub mod handle;
pub mod types;
pub mod object;
pub mod borrow;
pub mod result;
pub mod task;
#[cfg(feature = "event-handler-api")]
pub mod context;
#[cfg(any(
feature = "event-handler-api",
all(feature = "napi-4", feature = "event-queue-api")
))]
pub mod event;
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")]
pub mod task;
pub mod types;
#[doc(hidden)]
pub mod macro_internal;
@ -30,27 +103,14 @@ pub mod macro_internal;
#[cfg(feature = "proc-macros")]
pub use neon_macros::*;
#[cfg(all(feature = "legacy-runtime", feature = "napi-runtime"))]
compile_error!("Cannot enable both `legacy-runtime` and `napi-runtime` features.\n\nTo use `napi-runtime`, disable `legacy-runtime` by setting `default-features` to `false` in Cargo.toml\nor with cargo's --no-default-features flag.");
#[cfg(feature = "napi-6")]
mod lifecycle;
#[cfg(all(feature = "napi-runtime", 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(())
/// });
/// ```
#[cfg(all(feature = "legacy-runtime", feature = "napi-1"))]
compile_error!("Cannot enable both `legacy-runtime` and `napi-*` features.\n\nTo use `napi-*`, disable `legacy-runtime` by setting `default-features` to `false` in Cargo.toml\nor with cargo's --no-default-features flag.");
#[cfg(all(feature = "napi-1", not(feature = "legacy-runtime")))]
#[doc(hidden)]
#[macro_export]
macro_rules! register_module {
($module:pat, $init:block) => {
@ -91,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 {
@ -171,6 +237,7 @@ macro_rules! register_module {
}
}
#[cfg(feature = "legacy-runtime")]
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! class_definition {
@ -260,6 +327,7 @@ macro_rules! class_definition {
};
}
#[cfg(feature = "legacy-runtime")]
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_managed {
@ -277,15 +345,15 @@ macro_rules! impl_managed {
$cls(raw)
}
}
}
};
}
#[cfg(feature = "legacy-runtime")]
/// Declare custom native JavaScript types with Rust implementations.
///
/// Example:
///
/// ```rust
/// # #[macro_use] extern crate neon;
/// # use neon::prelude::*;
/// # fn main() {}
/// pub struct Greeter {
@ -299,7 +367,7 @@ macro_rules! impl_managed {
/// init(mut cx) {
/// # #[cfg(feature = "legacy-runtime")]
/// let greeting = cx.argument::<JsString>(0)?.to_string(&mut cx)?.value();
/// # #[cfg(feature = "napi-runtime")]
/// # #[cfg(feature = "napi-1")]
/// # let greeting = cx.argument::<JsString>(0)?.to_string(&mut cx)?.value(&mut cx);
/// Ok(Greeter {
/// greeting: greeting
@ -309,7 +377,7 @@ macro_rules! impl_managed {
/// method hello(mut cx) {
/// # #[cfg(feature = "legacy-runtime")]
/// let name = cx.argument::<JsString>(0)?.to_string(&mut cx)?.value();
/// # #[cfg(feature = "napi-runtime")]
/// # #[cfg(feature = "napi-1")]
/// # let name = cx.argument::<JsString>(0)?.to_string(&mut cx)?.value(&mut cx);
/// let this = cx.this();
/// let msg = {
@ -362,6 +430,7 @@ macro_rules! declare_types {
{ } => { };
}
#[cfg(feature = "legacy-runtime")]
#[doc(hidden)]
#[macro_export]
macro_rules! neon_stringify {
@ -372,11 +441,11 @@ 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;
use std::sync::Mutex;
use semver::Version;
// Create a mutex to enforce sequential running of the tests.
lazy_static! {
@ -403,11 +472,14 @@ mod tests {
eprintln!("Running Neon test: {} {} {}", shell, command_flag, cmd);
assert!(Command::new(&shell)
.current_dir(dir)
.args(&[&command_flag, cmd])
.status()
.expect(&format!("failed to execute test command: {} {} {}", shell, command_flag, cmd))
.success());
.current_dir(dir)
.args(&[&command_flag, cmd])
.status()
.unwrap_or_else(|_| panic!(
"failed to execute test command: {} {} {}",
shell, command_flag, cmd
))
.success());
}
fn cli_setup() {
@ -436,7 +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
@ -445,22 +517,30 @@ mod tests {
#[rustversion::beta]
#[cfg(feature = "enable-static-tests")]
#[test]
fn static_test() { static_test_impl() }
fn static_test() {
static_test_impl()
}
#[rustversion::beta]
#[cfg(not(feature = "enable-static-tests"))]
#[test]
#[ignore]
fn static_test() { static_test_impl() }
fn static_test() {
static_test_impl()
}
#[rustversion::not(beta)]
#[cfg(feature = "enable-static-tests")]
compile_error!("The `enable-static-tests` feature can only be enabled with the Rust beta toolchain.");
compile_error!(
"The `enable-static-tests` feature can only be enabled with the Rust beta toolchain."
);
#[rustversion::not(beta)]
#[test]
#[ignore]
fn static_test() { static_test_impl() }
fn static_test() {
static_test_impl()
}
#[test]
fn dynamic_test() {
@ -482,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,9 +1,12 @@
//! Internals needed by macros. These have to be exported for the macros to work
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::{AllocateCallback, ConstructCallback, ConstructorCallCallback, MethodCallback};
pub use context::internal::{initialize_module, Env};
pub use crate::object::class::internal::{
AllocateCallback, ConstructCallback, ConstructorCallCallback, MethodCallback,
};
// An alias for neon_runtime so macros can refer to it.
pub mod runtime {

View File

@ -1,18 +1,18 @@
//! Utilities exposing metadata about the Neon version and build.
//! Metadata about the Neon version and build.
use semver::Version;
/// The Neon version.
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
/// The Neon major version.
pub const MAJOR: &'static str = env!("CARGO_PKG_VERSION_MAJOR");
pub const MAJOR: &str = env!("CARGO_PKG_VERSION_MAJOR");
/// The Neon minor version.
pub const MINOR: &'static str = env!("CARGO_PKG_VERSION_MINOR");
pub const MINOR: &str = env!("CARGO_PKG_VERSION_MINOR");
/// The neon patch version.
pub const PATCH: &'static str = env!("CARGO_PKG_VERSION_PATCH");
/// The Neon patch version.
pub const PATCH: &str = env!("CARGO_PKG_VERSION_PATCH");
/// Produces a `semver::Version` data structure representing the Neon version.
pub fn version() -> Version {
@ -21,7 +21,7 @@ pub fn version() -> Version {
minor: MINOR.parse().unwrap(),
patch: PATCH.parse().unwrap(),
pre: vec![],
build: vec![]
build: vec![],
}
}
@ -29,8 +29,8 @@ pub fn version() -> Version {
/// The current build profile (either `"release"` or `"debug"`).
#[cfg(neon_profile = "release")]
pub const BUILD_PROFILE: &'static str = "release";
pub const BUILD_PROFILE: &str = "release";
/// The current build profile (either `"release"` or `"debug"`).
#[cfg(not(neon_profile = "release"))]
pub const BUILD_PROFILE: &'static str = "debug";
pub const BUILD_PROFILE: &str = "debug";

View File

@ -1,15 +1,15 @@
use super::{Callback, Class, ClassInternal};
use crate::context::internal::{ContextInternal, Env};
use crate::context::{CallContext, CallbackInfo, Context};
use crate::handle::{Handle, Managed};
use crate::result::{JsResult, NeonResult, Throw};
use crate::types::error::convert_panics;
use crate::types::{build, JsFunction, JsObject, JsUndefined, JsValue};
use neon_runtime;
use neon_runtime::raw;
use std::mem;
use std::os::raw::c_void;
use std::ptr::null_mut;
use neon_runtime;
use neon_runtime::raw;
use super::{Class, ClassInternal, Callback};
use handle::{Handle, Managed};
use context::{CallbackInfo, CallContext, Context};
use context::internal::{ContextInternal, Env};
use result::{NeonResult, JsResult, Throw};
use types::{JsValue, JsObject, JsFunction, JsUndefined, build};
use types::error::convert_panics;
#[repr(C)]
pub struct MethodCallback<T: Class>(pub fn(CallContext<T>) -> JsResult<JsValue>);
@ -19,29 +19,34 @@ impl<T: Class> Callback<()> for MethodCallback<T> {
unsafe {
info.with_cx::<T, _, _>(env, |mut cx| {
let data = info.data(cx.env());
let this: Handle<JsValue> = Handle::new_internal(JsValue::from_raw(env, info.this(&mut cx)));
let this: Handle<JsValue> =
Handle::new_internal(JsValue::from_raw(env, info.this(&mut cx)));
#[cfg(feature = "legacy-runtime")]
let is_a_t = this.is_a::<T>();
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
let is_a_t = this.is_a::<T, _>(&mut cx);
if !is_a_t {
if let Ok(metadata) = T::metadata(&mut cx) {
neon_runtime::class::throw_this_error(mem::transmute(cx.env()), metadata.pointer);
neon_runtime::class::throw_this_error(
mem::transmute(cx.env()),
metadata.pointer,
);
}
return;
};
let dynamic_callback: fn(CallContext<T>) -> JsResult<JsValue> =
mem::transmute(neon_runtime::fun::get_dynamic_callback(cx.env().to_raw(), data));
if let Ok(value) = convert_panics(env, || { dynamic_callback(cx) }) {
let dynamic_callback: fn(CallContext<T>) -> JsResult<JsValue> = mem::transmute(
neon_runtime::fun::get_dynamic_callback(cx.env().to_raw(), data),
);
if let Ok(value) = convert_panics(env, || dynamic_callback(cx)) {
info.set_return(value);
}
})
}
}
fn as_ptr(self) -> *mut c_void {
fn into_ptr(self) -> *mut c_void {
self.0 as *mut c_void
}
}
@ -54,7 +59,10 @@ impl ConstructorCallCallback {
fn callback<T: Class>(mut cx: CallContext<JsValue>) -> JsResult<JsValue> {
unsafe {
if let Ok(metadata) = T::metadata(&mut cx) {
neon_runtime::class::throw_call_error(mem::transmute(cx.env()), metadata.pointer);
neon_runtime::class::throw_call_error(
mem::transmute(cx.env()),
metadata.pointer,
);
}
}
Err(Throw)
@ -71,14 +79,14 @@ impl Callback<()> for ConstructorCallCallback {
let data = info.data(cx.env());
let kernel: fn(CallContext<JsValue>) -> JsResult<JsValue> =
mem::transmute(neon_runtime::class::get_call_kernel(data));
if let Ok(value) = convert_panics(env, || { kernel(cx) }) {
if let Ok(value) = convert_panics(env, || kernel(cx)) {
info.set_return(value);
}
})
}
}
fn as_ptr(self) -> *mut c_void {
fn into_ptr(self) -> *mut c_void {
self.0 as *mut c_void
}
}
@ -93,9 +101,9 @@ impl<T: Class> Callback<*mut c_void> for AllocateCallback<T> {
let data = info.data(cx.env());
let kernel: fn(CallContext<JsUndefined>) -> NeonResult<T::Internals> =
mem::transmute(neon_runtime::class::get_allocate_kernel(data));
if let Ok(value) = convert_panics(env, || { kernel(cx) }) {
if let Ok(value) = convert_panics(env, || kernel(cx)) {
let p = Box::into_raw(Box::new(value));
mem::transmute(p)
p.cast()
} else {
null_mut()
}
@ -103,13 +111,15 @@ impl<T: Class> Callback<*mut c_void> for AllocateCallback<T> {
}
}
fn as_ptr(self) -> *mut c_void {
fn into_ptr(self) -> *mut c_void {
self.0 as *mut c_void
}
}
#[repr(C)]
pub struct ConstructCallback<T: Class>(pub fn(CallContext<T>) -> NeonResult<Option<Handle<JsObject>>>);
pub struct ConstructCallback<T: Class>(
pub fn(CallContext<T>) -> NeonResult<Option<Handle<JsObject>>>,
);
impl<T: Class> Callback<bool> for ConstructCallback<T> {
extern "C" fn invoke(env: Env, info: CallbackInfo<'_>) -> bool {
@ -118,19 +128,19 @@ impl<T: Class> Callback<bool> for ConstructCallback<T> {
let data = info.data(cx.env());
let kernel: fn(CallContext<T>) -> NeonResult<Option<Handle<JsObject>>> =
mem::transmute(neon_runtime::class::get_construct_kernel(data));
match convert_panics(env, || { kernel(cx) }) {
match convert_panics(env, || kernel(cx)) {
Ok(None) => true,
Ok(Some(obj)) => {
info.set_return(obj);
true
}
_ => false
_ => false,
}
})
}
}
fn as_ptr(self) -> *mut c_void {
fn into_ptr(self) -> *mut c_void {
self.0 as *mut c_void
}
}
@ -138,13 +148,20 @@ impl<T: Class> Callback<bool> for ConstructCallback<T> {
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ClassMetadata {
pub(crate) pointer: *mut c_void
pub(crate) pointer: *mut c_void,
}
impl ClassMetadata {
pub unsafe fn constructor<'a, T: Class, C: Context<'a>>(&self, cx: &mut C) -> JsResult<'a, JsFunction<T>> {
pub unsafe fn constructor<'a, T: Class, C: Context<'a>>(
&self,
cx: &mut C,
) -> JsResult<'a, JsFunction<T>> {
build(cx.env(), |out| {
neon_runtime::class::metadata_to_constructor(out, mem::transmute(cx.env()), self.pointer)
neon_runtime::class::metadata_to_constructor(
out,
mem::transmute(cx.env()),
self.pointer,
)
})
}

View File

@ -2,32 +2,33 @@
pub(crate) mod internal;
use self::internal::{
AllocateCallback, ClassMetadata, ConstructCallback, ConstructorCallCallback, MethodCallback,
};
use crate::borrow::{Borrow, BorrowMut, LoanError, Ref, RefMut};
use crate::context::internal::Env;
use crate::context::{Context, Lock};
use crate::handle::{Handle, Managed};
use crate::object::{Object, This};
use crate::result::{JsResult, NeonResult, Throw};
use crate::types::internal::{Callback, ValueInternal};
use crate::types::{build, JsFunction, JsValue, Value};
use neon_runtime;
use neon_runtime::raw;
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::mem;
use std::os::raw::c_void;
use std::slice;
use std::collections::HashMap;
use neon_runtime;
use neon_runtime::raw;
use neon_runtime::call::CCallback;
use context::{Context, Lock, CallbackInfo};
use context::internal::Env;
use result::{NeonResult, JsResult, Throw};
use borrow::{Borrow, BorrowMut, Ref, RefMut, LoanError};
use handle::{Handle, Managed};
use types::{Value, JsFunction, JsValue, build};
use types::internal::ValueInternal;
use object::{Object, This};
use self::internal::{ClassMetadata, MethodCallback, ConstructorCallCallback, AllocateCallback, ConstructCallback};
pub(crate) struct ClassMap {
map: HashMap<TypeId, ClassMetadata>
map: HashMap<TypeId, ClassMetadata>,
}
impl ClassMap {
pub(crate) fn new() -> ClassMap {
ClassMap {
map: HashMap::new()
map: HashMap::new(),
}
}
@ -46,19 +47,18 @@ pub struct ClassDescriptor<'a, T: Class> {
allocate: AllocateCallback<T>,
call: Option<ConstructorCallCallback>,
construct: Option<ConstructCallback<T>>,
methods: Vec<(&'a str, MethodCallback<T>)>
methods: Vec<(&'a str, MethodCallback<T>)>,
}
impl<'a, T: Class> ClassDescriptor<'a, T> {
/// Constructs a new minimal `ClassDescriptor` with a name and allocator.
pub fn new<'b, U: Class>(name: &'b str, allocate: AllocateCallback<U>) -> ClassDescriptor<'b, U> {
ClassDescriptor {
name: name,
allocate: allocate,
pub fn new<'b: 'a>(name: &'b str, allocate: AllocateCallback<T>) -> Self {
Self {
name,
allocate,
call: None,
construct: None,
methods: Vec::new()
methods: Vec::new(),
}
}
@ -79,16 +79,15 @@ impl<'a, T: Class> ClassDescriptor<'a, T> {
self.methods.push((name, callback));
self
}
}
extern "C" fn drop_internals<T>(internals: *mut c_void) {
let p: Box<T> = unsafe { Box::from_raw(mem::transmute(internals)) };
let p: Box<T> = unsafe { Box::from_raw(internals.cast()) };
mem::drop(p);
}
/// The trait implemented by Neon classes.
///
///
/// This trait is not intended to be implemented manually; it is implemented automatically by
/// creating a class with the `class` syntax of the `declare_types!` macro.
pub trait Class: Managed + Any {
@ -105,15 +104,16 @@ pub trait Class: Managed + Any {
/// Convenience method for constructing new instances of this class without having to extract the constructor function.
fn new<'a, 'b, C: Context<'a>, A, AS>(cx: &mut C, args: AS) -> JsResult<'a, Self>
where A: Value + 'b,
AS: IntoIterator<Item=Handle<'b, A>>
where
A: Value + 'b,
AS: IntoIterator<Item = Handle<'b, A>>,
{
let constructor = Self::constructor(cx)?;
constructor.construct(cx, args)
}
#[doc(hidden)]
fn describe<'a>(name: &'a str, allocate: AllocateCallback<Self>) -> ClassDescriptor<'a, Self> {
fn describe(name: &str, allocate: AllocateCallback<Self>) -> ClassDescriptor<Self> {
ClassDescriptor::<Self>::new(name, allocate)
}
}
@ -124,26 +124,23 @@ unsafe impl<T: Class> This for T {
Self::from_raw(Env::current(), h)
}
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
fn as_this(env: Env, h: raw::Local) -> Self {
Self::from_raw(env, h)
}
}
impl<T: Class> Object for T { }
impl<T: Class> Object for T {}
pub(crate) trait ClassInternal: Class {
fn metadata_opt<'a, C: Context<'a>>(cx: &mut C) -> Option<ClassMetadata> {
cx.env()
.class_map()
.get(&TypeId::of::<Self>())
.map(|m| m.clone())
cx.env().class_map().get(&TypeId::of::<Self>()).copied()
}
fn metadata<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<ClassMetadata> {
match Self::metadata_opt(cx) {
Some(metadata) => Ok(metadata),
None => Self::create(cx)
None => Self::create(cx),
}
}
@ -153,14 +150,22 @@ pub(crate) trait ClassInternal: Class {
let env = cx.env().to_raw();
let allocate = descriptor.allocate.into_c_callback();
let construct = descriptor.construct.map(|callback| callback.into_c_callback()).unwrap_or_default();
let call = descriptor.call.unwrap_or_else(ConstructorCallCallback::default::<Self>).into_c_callback();
let construct = descriptor
.construct
.map(|callback| callback.into_c_callback())
.unwrap_or_default();
let call = descriptor
.call
.unwrap_or_else(ConstructorCallCallback::default::<Self>)
.into_c_callback();
let metadata_pointer = neon_runtime::class::create_base(env,
allocate,
construct,
call,
drop_internals::<Self::Internals>);
let metadata_pointer = neon_runtime::class::create_base(
env,
allocate,
construct,
call,
drop_internals::<Self::Internals>,
);
if metadata_pointer.is_null() {
return Err(Throw);
@ -170,7 +175,12 @@ pub(crate) trait ClassInternal: Class {
// v8::FunctionTemplate has a finalizer that will delete it.
let class_name = descriptor.name;
if !neon_runtime::class::set_name(env, metadata_pointer, class_name.as_ptr(), class_name.len() as u32) {
if !neon_runtime::class::set_name(
env,
metadata_pointer,
class_name.as_ptr(),
class_name.len() as u32,
) {
return Err(Throw);
}
@ -179,13 +189,19 @@ pub(crate) trait ClassInternal: Class {
let callback = method.into_c_callback();
neon_runtime::fun::new_template(out, env, callback)
})?;
if !neon_runtime::class::add_method(env, metadata_pointer, name.as_ptr(), name.len() as u32, method.to_raw()) {
if !neon_runtime::class::add_method(
env,
metadata_pointer,
name.as_ptr(),
name.len() as u32,
method.to_raw(),
) {
return Err(Throw);
}
}
let metadata = ClassMetadata {
pointer: metadata_pointer
pointer: metadata_pointer,
};
cx.env().class_map().set(TypeId::of::<Self>(), metadata);
@ -195,13 +211,11 @@ pub(crate) trait ClassInternal: Class {
}
}
impl<T: Class> ClassInternal for T { }
impl<T: Class> ClassInternal for T {}
impl<T: Class> ValueInternal for T {
fn name() -> String {
let mut isolate: Env = unsafe {
mem::transmute(neon_runtime::call::current_isolate())
};
let mut isolate: Env = unsafe { mem::transmute(neon_runtime::call::current_isolate()) };
let raw_isolate = unsafe { mem::transmute(isolate) };
let map = isolate.class_map();
match map.get(&TypeId::of::<T>()) {
@ -210,7 +224,8 @@ impl<T: Class> ValueInternal for T {
let mut chars = std::ptr::null_mut();
let buf = unsafe {
let len = neon_runtime::class::get_name(&mut chars, raw_isolate, metadata.pointer);
let len =
neon_runtime::class::get_name(&mut chars, raw_isolate, metadata.pointer);
slice::from_raw_parts_mut(chars, len)
};
@ -224,14 +239,12 @@ impl<T: Class> ValueInternal for T {
let map = env.class_map();
match map.get(&TypeId::of::<T>()) {
None => false,
Some(ref metadata) => unsafe {
metadata.has_instance(value.to_raw())
}
Some(ref metadata) => unsafe { metadata.has_instance(value.to_raw()) },
}
}
}
impl<T: Class> Value for T { }
impl<T: Class> Value for T {}
impl<'a, T: Class> Borrow for &'a T {
type Target = &'a mut T::Internals;
@ -239,7 +252,7 @@ impl<'a, T: Class> Borrow for &'a T {
fn try_borrow<'b>(self, lock: &'b Lock<'b>) -> Result<Ref<'b, Self::Target>, LoanError> {
unsafe {
let ptr: *mut c_void = neon_runtime::class::get_instance_internals(self.to_raw());
Ref::new(lock, mem::transmute(ptr))
Ref::new(lock, &mut *ptr.cast())
}
}
}
@ -256,42 +269,7 @@ impl<'a, T: Class> BorrowMut for &'a mut T {
fn try_borrow_mut<'b>(self, lock: &'b Lock<'b>) -> Result<RefMut<'b, Self::Target>, LoanError> {
unsafe {
let ptr: *mut c_void = neon_runtime::class::get_instance_internals(self.to_raw());
RefMut::new(lock, mem::transmute(ptr))
}
}
}
/// A dynamically computed callback that can be passed through C to the engine.
/// This type makes it possible to export a dynamically computed Rust function
/// as a pair of 1) a raw pointer to the dynamically computed function, and 2)
/// a static function that knows how to transmute that raw pointer and call it.
pub(crate) trait Callback<T: Clone + Copy + Sized>: Sized {
/// Extracts the computed Rust function and invokes it. The Neon runtime
/// ensures that the computed function is provided as the extra data field,
/// wrapped as a V8 External, in the `CallbackInfo` argument.
extern "C" fn invoke(env: Env, info: CallbackInfo<'_>) -> T;
/// See `invoke`. This is used by the non-n-api implementation, so that every impl for this
/// trait doesn't need to provide two versions of `invoke`.
#[cfg(feature = "legacy-runtime")]
#[doc(hidden)]
extern "C" fn invoke_compat(info: CallbackInfo<'_>) -> T {
Self::invoke(Env::current(), info)
}
/// Converts the callback to a raw void pointer.
fn as_ptr(self) -> *mut c_void;
/// Exports the callback as a pair consisting of the static `Self::invoke`
/// method and the computed callback, both converted to raw void pointers.
fn into_c_callback(self) -> CCallback {
#[cfg(feature = "napi-runtime")]
let invoke = Self::invoke;
#[cfg(feature = "legacy-runtime")]
let invoke = Self::invoke_compat;
CCallback {
static_callback: unsafe { mem::transmute(invoke as usize) },
dynamic_callback: self.as_ptr()
RefMut::new(lock, &mut *ptr.cast())
}
}
}

View File

@ -1,18 +1,20 @@
//! Traits for working with JavaScript objects.
#[cfg(feature = "legacy-runtime")]
pub(crate) mod class;
#[cfg(feature = "legacy-runtime")]
pub use self::class::{Class, ClassDescriptor};
pub use self::traits::*;
#[cfg(feature = "legacy-runtime")]
mod traits {
use crate::context::Context;
use crate::handle::{Handle, Managed};
use crate::result::{JsResult, NeonResult, Throw};
use crate::types::utf8::Utf8;
use crate::types::{build, JsArray, JsValue, Value};
use neon_runtime::raw;
use handle::{Handle, Managed};
use types::{Value, JsValue, JsArray, build};
use types::utf8::Utf8;
use context::Context;
use result::{NeonResult, JsResult, Throw};
/// A property key in a JavaScript object.
pub trait PropertyKey {
@ -54,18 +56,27 @@ mod traits {
/// The trait of all object types.
pub trait Object: Value {
fn get<'a, C: Context<'a>, K: PropertyKey>(self, cx: &mut C, key: K) -> NeonResult<Handle<'a, JsValue>> {
build(cx.env(), |out| { unsafe { key.get_from(out, self.to_raw()) } })
fn get<'a, C: Context<'a>, K: PropertyKey>(
self,
cx: &mut C,
key: K,
) -> NeonResult<Handle<'a, JsValue>> {
build(cx.env(), |out| unsafe { key.get_from(out, self.to_raw()) })
}
fn get_own_property_names<'a, C: Context<'a>>(self, cx: &mut C) -> JsResult<'a, JsArray> {
let env = cx.env();
build(env, |out| {
unsafe { neon_runtime::object::get_own_property_names(out, env.to_raw(), self.to_raw()) }
build(env, |out| unsafe {
neon_runtime::object::get_own_property_names(out, env.to_raw(), self.to_raw())
})
}
fn set<'a, C: Context<'a>, K: PropertyKey, W: Value>(self, _: &mut C, key: K, val: Handle<W>) -> NeonResult<bool> {
fn set<'a, C: Context<'a>, K: PropertyKey, W: Value>(
self,
_: &mut C,
key: K,
val: Handle<W>,
) -> NeonResult<bool> {
let mut result = false;
if unsafe { key.set_from(&mut result, self.to_raw(), val.to_raw()) } {
Ok(result)
@ -77,19 +88,25 @@ mod traits {
/// The trait of types that can be a function's `this` binding.
pub unsafe trait This: Managed {
#[allow(clippy::wrong_self_convention)]
fn as_this(h: raw::Local) -> Self;
}
}
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
mod traits {
use crate::context::internal::Env;
use crate::context::Context;
use crate::handle::{Handle, Managed, Root};
use crate::result::{NeonResult, Throw};
use crate::types::utf8::Utf8;
use crate::types::{build, JsValue, Value};
use neon_runtime::raw;
use handle::{Handle, Managed};
use types::{Value, JsValue, JsArray, build};
use types::utf8::Utf8;
use context::Context;
use context::internal::Env;
use result::{NeonResult, JsResult, Throw};
#[cfg(feature = "napi-6")]
use crate::result::JsResult;
#[cfg(feature = "napi-6")]
use crate::types::JsArray;
/// A property key in a JavaScript object.
pub trait PropertyKey {
@ -97,7 +114,7 @@ mod traits {
self,
cx: &mut C,
out: &mut raw::Local,
obj: raw::Local
obj: raw::Local,
) -> bool;
unsafe fn set_from<'c, C: Context<'c>>(
@ -114,7 +131,7 @@ mod traits {
self,
cx: &mut C,
out: &mut raw::Local,
obj: raw::Local
obj: raw::Local,
) -> bool {
neon_runtime::object::get_index(out, cx.env().to_raw(), obj, self)
}
@ -135,7 +152,7 @@ mod traits {
self,
cx: &mut C,
out: &mut raw::Local,
obj: raw::Local
obj: raw::Local,
) -> bool {
let env = cx.env().to_raw();
@ -160,7 +177,7 @@ mod traits {
self,
cx: &mut C,
out: &mut raw::Local,
obj: raw::Local
obj: raw::Local,
) -> bool {
let (ptr, len) = Utf8::from(self).into_small_unwrap().lower();
let env = cx.env().to_raw();
@ -184,19 +201,31 @@ mod traits {
/// The trait of all object types.
pub trait Object: Value {
fn get<'a, C: Context<'a>, K: PropertyKey>(self, cx: &mut C, key: K) -> NeonResult<Handle<'a, JsValue>> {
build(cx.env(), |out| { unsafe { key.get_from(cx, out, self.to_raw()) } })
}
fn get_own_property_names<'a, C: Context<'a>>(self, cx: &mut C) -> JsResult<'a, JsArray> {
let env = cx.env();
build(cx.env(), |out| {
unsafe { neon_runtime::object::get_own_property_names(out, env.to_raw(), self.to_raw()) }
fn get<'a, C: Context<'a>, K: PropertyKey>(
self,
cx: &mut C,
key: K,
) -> NeonResult<Handle<'a, JsValue>> {
build(cx.env(), |out| unsafe {
key.get_from(cx, out, self.to_raw())
})
}
fn set<'a, C: Context<'a>, K: PropertyKey, W: Value>(self, cx: &mut C, key: K, val: Handle<W>) -> NeonResult<bool> {
#[cfg(feature = "napi-6")]
fn get_own_property_names<'a, C: Context<'a>>(self, cx: &mut C) -> JsResult<'a, JsArray> {
let env = cx.env();
build(cx.env(), |out| unsafe {
neon_runtime::object::get_own_property_names(out, env.to_raw(), self.to_raw())
})
}
fn set<'a, C: Context<'a>, K: PropertyKey, W: Value>(
self,
cx: &mut C,
key: K,
val: Handle<W>,
) -> NeonResult<bool> {
let mut result = false;
if unsafe { key.set_from(cx, &mut result, self.to_raw(), val.to_raw()) } {
Ok(result)
@ -204,10 +233,15 @@ mod traits {
Err(Throw)
}
}
fn root<'a, C: Context<'a>>(&self, cx: &mut C) -> Root<Self> {
Root::new(cx, self)
}
}
/// The trait of types that can be a function's `this` binding.
pub unsafe trait This: Managed {
#[allow(clippy::wrong_self_convention)]
fn as_this(env: Env, h: raw::Local) -> Self;
}
}

View File

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

16
src/reflect.rs Normal file
View File

@ -0,0 +1,16 @@
//! 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};
pub fn eval<'a, 'b, C: Context<'a>>(
cx: &mut C,
script: Handle<'b, JsString>,
) -> JsResult<'a, JsValue> {
let env = cx.env().to_raw();
build(cx.env(), |out| unsafe {
neon_runtime::string::run_script(out, env, script.to_raw())
})
}

View File

@ -1,16 +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
use crate::context::Context;
use crate::handle::Handle;
use crate::types::Value;
use std::fmt::{Display, Formatter, Result as FmtResult};
use handle::Handle;
use types::Value;
use context::Context;
/// An error sentinel type used by `NeonResult` (and `JsResult`) to indicate that the JavaScript engine
/// has entered into a throwing state.
///
/// `Throw` deliberately does not implement `std::error::Error`, 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.
/// A [unit type][unit] indicating that the JavaScript thread is throwing an exception.
///
/// `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;
@ -20,14 +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>;
}

View File

@ -1,17 +1,16 @@
//! Asynchronous background _tasks_ that run in the Node thread pool.
//! Utilities for scheduling tasks to be executed by the Node.js runtime
use std::marker::{Send, Sized};
use std::mem;
use std::os::raw::c_void;
use types::{Value, JsFunction};
use result::JsResult;
use handle::{Handle, Managed};
use context::TaskContext;
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;
/// A Rust task that can be executed in a background thread.
/// A Rust task that can be executed in the background on the Node thread pool.
pub trait Task: Send + Sized + 'static {
/// The task's result type, which is sent back to the main thread to communicate a successful result back to JavaScript.
type Output: Send + 'static;
@ -26,7 +25,11 @@ pub trait Task: Send + Sized + 'static {
fn perform(&self) -> Result<Self::Output, Self::Error>;
/// Convert the result of the task to a JavaScript value to be passed to the asynchronous callback. This method is executed on the main thread at some point after the background task is completed.
fn complete<'a>(self, cx: TaskContext<'a>, result: Result<Self::Output, Self::Error>) -> JsResult<Self::JsEvent>;
fn complete(
self,
cx: TaskContext,
result: Result<Self::Output, Self::Error>,
) -> JsResult<Self::JsEvent>;
/// Schedule a task to be executed on a background thread.
///
@ -40,24 +43,30 @@ pub trait Task: Send + Sized + 'static {
let self_raw = Box::into_raw(boxed_self);
let callback_raw = callback.to_raw();
unsafe {
neon_runtime::task::schedule(mem::transmute(self_raw),
perform_task::<Self>,
complete_task::<Self>,
callback_raw);
neon_runtime::task::schedule(
self_raw.cast(),
perform_task::<Self>,
complete_task::<Self>,
callback_raw,
);
}
}
}
unsafe extern "C" fn perform_task<T: Task>(task: *mut c_void) -> *mut c_void {
let task: Box<T> = Box::from_raw(mem::transmute(task));
let task: Box<T> = Box::from_raw(task.cast());
let result = task.perform();
Box::into_raw(task);
mem::transmute(Box::into_raw(Box::new(result)))
Box::into_raw(Box::new(result)).cast()
}
unsafe extern "C" fn complete_task<T: Task>(task: *mut c_void, result: *mut c_void, out: &mut raw::Local) {
let result: Result<T::Output, T::Error> = *Box::from_raw(mem::transmute(result));
let task: Box<T> = Box::from_raw(mem::transmute(task));
unsafe extern "C" fn complete_task<T: Task>(
task: *mut c_void,
result: *mut c_void,
out: &mut raw::Local,
) {
let result: Result<T::Output, T::Error> = *Box::from_raw(result.cast());
let task: Box<T> = Box::from_raw(task.cast());
TaskContext::with(|cx| {
if let Ok(result) = task.complete(cx, result) {
*out = result.to_raw();

View File

@ -1,19 +1,21 @@
//! Types and traits representing binary JavaScript data.
use crate::borrow::internal::Pointer;
use crate::borrow::{Borrow, BorrowMut, LoanError, Ref, RefMut};
use crate::context::internal::Env;
use crate::context::{Context, Lock};
#[cfg(feature = "napi-1")]
use crate::handle::Handle;
use crate::handle::Managed;
use crate::result::JsResult;
use crate::types::internal::ValueInternal;
use crate::types::{build, Object, Value};
use neon_runtime;
use neon_runtime::raw;
use std::marker::PhantomData;
use std::mem::{self, MaybeUninit};
use std::os::raw::c_void;
use std::slice;
use context::{Context, Lock};
use context::internal::Env;
use borrow::{Borrow, BorrowMut, Ref, RefMut, LoanError};
use borrow::internal::Pointer;
use handle::Managed;
use types::{Value, Object, build};
use types::internal::ValueInternal;
use result::JsResult;
use neon_runtime;
use neon_runtime::raw;
/// The Node [`Buffer`](https://nodejs.org/api/buffer.html) type.
#[repr(C)]
@ -21,37 +23,62 @@ use neon_runtime::raw;
pub struct JsBuffer(raw::Local);
impl JsBuffer {
/// Constructs a new `Buffer` object, safely zero-filled.
pub fn new<'a, C: Context<'a>>(cx: &mut C, size: u32) -> JsResult<'a, JsBuffer> {
let env = cx.env();
build(env, |out| { unsafe { neon_runtime::buffer::new(env.to_raw(), out, size) } })
build(env, |out| unsafe {
neon_runtime::buffer::new(env.to_raw(), out, size)
})
}
/// Constructs a new `Buffer` object, safely zero-filled.
pub unsafe fn uninitialized<'a, C: Context<'a>>(cx: &mut C, size: u32) -> JsResult<'a, JsBuffer> {
build(cx.env(), |out| { neon_runtime::buffer::uninitialized(out, size) })
pub unsafe fn uninitialized<'a, C: Context<'a>>(
cx: &mut C,
size: u32,
) -> JsResult<'a, JsBuffer> {
let env = cx.env();
build(env, |out| {
neon_runtime::buffer::uninitialized(env.to_raw(), out, size)
})
}
#[cfg(feature = "napi-1")]
/// Construct a new `Buffer` from bytes allocated by Rust
pub fn external<'a, C, T>(cx: &mut C, data: T) -> Handle<'a, JsBuffer>
where
C: Context<'a>,
T: AsMut<[u8]> + Send,
{
let env = cx.env().to_raw();
let value = unsafe { neon_runtime::buffer::new_external(env, data) };
Handle::new_internal(JsBuffer(value))
}
}
impl Managed for JsBuffer {
fn to_raw(self) -> raw::Local { self.0 }
fn to_raw(self) -> raw::Local {
self.0
}
fn from_raw(_env: Env, h: raw::Local) -> Self { JsBuffer(h) }
fn from_raw(_env: Env, h: raw::Local) -> Self {
JsBuffer(h)
}
}
impl ValueInternal for JsBuffer {
fn name() -> String { "Buffer".to_string() }
fn name() -> String {
"Buffer".to_string()
}
fn is_typeof<Other: Value>(env: Env, other: Other) -> bool {
unsafe { neon_runtime::tag::is_buffer(env.to_raw(), other.to_raw()) }
}
}
impl Value for JsBuffer { }
impl Value for JsBuffer {}
impl Object for JsBuffer { }
impl Object for JsBuffer {}
/// The standard JS [`ArrayBuffer`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) type.
#[repr(C)]
@ -59,31 +86,50 @@ impl Object for JsBuffer { }
pub struct JsArrayBuffer(raw::Local);
impl JsArrayBuffer {
/// Constructs a new `ArrayBuffer` object with the given size, in bytes.
pub fn new<'a, C: Context<'a>>(cx: &mut C, size: u32) -> JsResult<'a, JsArrayBuffer> {
build(cx.env(), |out| { unsafe { neon_runtime::arraybuffer::new(out, mem::transmute(cx.env()), size) } })
build(cx.env(), |out| unsafe {
neon_runtime::arraybuffer::new(out, mem::transmute(cx.env()), size)
})
}
#[cfg(feature = "napi-1")]
/// Construct a new `ArrayBuffer` from bytes allocated by Rust
pub fn external<'a, C, T>(cx: &mut C, data: T) -> Handle<'a, JsArrayBuffer>
where
C: Context<'a>,
T: AsMut<[u8]> + Send,
{
let env = cx.env().to_raw();
let value = unsafe { neon_runtime::arraybuffer::new_external(env, data) };
Handle::new_internal(JsArrayBuffer(value))
}
}
impl Managed for JsArrayBuffer {
fn to_raw(self) -> raw::Local { self.0 }
fn to_raw(self) -> raw::Local {
self.0
}
fn from_raw(_env: Env, h: raw::Local) -> Self { JsArrayBuffer(h) }
fn from_raw(_env: Env, h: raw::Local) -> Self {
JsArrayBuffer(h)
}
}
impl ValueInternal for JsArrayBuffer {
fn name() -> String { "ArrayBuffer".to_string() }
fn name() -> String {
"ArrayBuffer".to_string()
}
fn is_typeof<Other: Value>(env: Env, other: Other) -> bool {
unsafe { neon_runtime::tag::is_arraybuffer(env.to_raw(), other.to_raw()) }
}
}
impl Value for JsArrayBuffer { }
impl Value for JsArrayBuffer {}
impl Object for JsArrayBuffer { }
impl Object for JsArrayBuffer {}
/// A reference to the internal backing buffer data of a `Buffer` or `ArrayBuffer` object, which can be accessed via the `Borrow` and `BorrowMut` traits.
#[derive(Clone, Copy)]
@ -91,7 +137,7 @@ impl Object for JsArrayBuffer { }
pub struct BinaryData<'a> {
base: *mut c_void,
size: usize,
phantom: PhantomData<&'a ()>
phantom: PhantomData<&'a ()>,
}
unsafe impl<'a> Pointer for BinaryData<'a> {
@ -105,25 +151,24 @@ unsafe impl<'a> Pointer for BinaryData<'a> {
}
/// The trait for element types by which a buffer's binary data can be indexed.
pub trait BinaryViewType: Sized { }
pub trait BinaryViewType: Sized {}
impl BinaryViewType for u8 { }
impl BinaryViewType for i8 { }
impl BinaryViewType for u16 { }
impl BinaryViewType for i16 { }
impl BinaryViewType for u32 { }
impl BinaryViewType for i32 { }
impl BinaryViewType for u64 { }
impl BinaryViewType for i64 { }
impl BinaryViewType for f32 { }
impl BinaryViewType for f64 { }
impl BinaryViewType for u8 {}
impl BinaryViewType for i8 {}
impl BinaryViewType for u16 {}
impl BinaryViewType for i16 {}
impl BinaryViewType for u32 {}
impl BinaryViewType for i32 {}
impl BinaryViewType for u64 {}
impl BinaryViewType for i64 {}
impl BinaryViewType for f32 {}
impl BinaryViewType for f64 {}
impl<'a> BinaryData<'a> {
/// Produces an immutable slice as a view into the contents of this buffer.
///
///
/// # Example:
///
///
/// ```no_run
/// # use neon::prelude::*;
/// # fn get_x_and_y(mut cx: FunctionContext) -> JsResult<JsUndefined> {
@ -137,15 +182,19 @@ impl<'a> BinaryData<'a> {
/// # }
/// ```
pub fn as_slice<T: BinaryViewType>(self) -> &'a [T] {
let base = unsafe { mem::transmute(self.base) };
let len = self.size / mem::size_of::<T>();
unsafe { slice::from_raw_parts(base, len) }
if self.size == 0 {
&[]
} else {
let base = self.base.cast();
let len = self.size / mem::size_of::<T>();
unsafe { slice::from_raw_parts(base, len) }
}
}
/// Produces a mutable slice as a view into the contents of this buffer.
///
///
/// # Example:
///
///
/// ```no_run
/// # use neon::prelude::*;
/// # fn modify_buffer(mut cx: FunctionContext) -> JsResult<JsUndefined> {
@ -159,15 +208,24 @@ impl<'a> BinaryData<'a> {
/// # }
/// ```
pub fn as_mut_slice<T: BinaryViewType>(self) -> &'a mut [T] {
let base = unsafe { mem::transmute(self.base) };
let len = self.size / mem::size_of::<T>();
unsafe { slice::from_raw_parts_mut(base, len) }
if self.size == 0 {
&mut []
} else {
let base = self.base.cast();
let len = self.size / mem::size_of::<T>();
unsafe { slice::from_raw_parts_mut(base, len) }
}
}
/// Produces the length of the buffer, in bytes.
pub fn len(self) -> usize {
self.size
}
/// Returns `true` if the buffer is empty
pub fn is_empty(self) -> bool {
self.len() == 0
}
}
impl<'a> Borrow for &'a JsBuffer {
@ -179,13 +237,12 @@ impl<'a> Borrow for &'a JsBuffer {
// Initialize pointer
unsafe {
let pointer = data.as_mut_ptr();
(*pointer).size = neon_runtime::buffer::data(guard.env.to_raw(), &mut (*pointer).base, self.to_raw());
(*pointer).size =
neon_runtime::buffer::data(guard.env.to_raw(), &mut (*pointer).base, self.to_raw());
}
// UB if pointer is not initialized!
unsafe {
Ref::new(guard, data.assume_init())
}
unsafe { Ref::new(guard, data.assume_init()) }
}
}
@ -198,19 +255,21 @@ impl<'a> Borrow for &'a mut JsBuffer {
}
impl<'a> BorrowMut for &'a mut JsBuffer {
fn try_borrow_mut<'b>(self, guard: &'b Lock<'b>) -> Result<RefMut<'b, Self::Target>, LoanError> {
fn try_borrow_mut<'b>(
self,
guard: &'b Lock<'b>,
) -> Result<RefMut<'b, Self::Target>, LoanError> {
let mut data = MaybeUninit::<BinaryData>::uninit();
// Initialize pointer
unsafe {
let pointer = data.as_mut_ptr();
(*pointer).size = neon_runtime::buffer::data(guard.env.to_raw(), &mut (*pointer).base, self.to_raw());
(*pointer).size =
neon_runtime::buffer::data(guard.env.to_raw(), &mut (*pointer).base, self.to_raw());
}
// UB if pointer is not initialized!
unsafe {
RefMut::new(guard, data.assume_init())
}
unsafe { RefMut::new(guard, data.assume_init()) }
}
}
@ -223,13 +282,15 @@ impl<'a> Borrow for &'a JsArrayBuffer {
// Initialize pointer
unsafe {
let pointer = data.as_mut_ptr();
(*pointer).size = neon_runtime::arraybuffer::data(guard.env.to_raw(), &mut (*pointer).base, self.to_raw());
(*pointer).size = neon_runtime::arraybuffer::data(
guard.env.to_raw(),
&mut (*pointer).base,
self.to_raw(),
);
}
// UB if pointer is not initialized!
unsafe {
Ref::new(guard, data.assume_init())
}
unsafe { Ref::new(guard, data.assume_init()) }
}
}
@ -242,18 +303,23 @@ impl<'a> Borrow for &'a mut JsArrayBuffer {
}
impl<'a> BorrowMut for &'a mut JsArrayBuffer {
fn try_borrow_mut<'b>(self, guard: &'b Lock<'b>) -> Result<RefMut<'b, Self::Target>, LoanError> {
fn try_borrow_mut<'b>(
self,
guard: &'b Lock<'b>,
) -> Result<RefMut<'b, Self::Target>, LoanError> {
let mut data = MaybeUninit::<BinaryData>::uninit();
// Initialize pointer
unsafe {
let pointer = data.as_mut_ptr();
(*pointer).size = neon_runtime::arraybuffer::data(guard.env.to_raw(), &mut (*pointer).base, self.to_raw());
(*pointer).size = neon_runtime::arraybuffer::data(
guard.env.to_raw(),
&mut (*pointer).base,
self.to_raw(),
);
}
// UB if pointer is not initialized!
unsafe {
RefMut::new(guard, data.assume_init())
}
unsafe { RefMut::new(guard, data.assume_init()) }
}
}

View File

@ -1,12 +1,13 @@
use std::any::{self, Any};
use std::ops::Deref;
use neon_runtime::raw;
use neon_runtime::external;
use neon_runtime::raw;
use crate::context::{Context, FinalizeContext};
use crate::context::internal::Env;
use crate::handle::{Managed, Handle};
use crate::context::{Context, FinalizeContext};
use crate::handle::{Handle, Managed};
use crate::object::Object;
use crate::types::internal::ValueInternal;
use crate::types::Value;
@ -89,38 +90,38 @@ type BoxAny = Box<dyn Any + Send + 'static>;
/// pub fn new(name: String) -> Self {
/// Person { name }
/// }
///
///
/// pub fn set_name(&mut self, name: String) {
/// self.name = name;
/// }
///
///
/// pub fn greet(&self) -> String {
/// format!("Hello, {}!", self.name)
/// }
/// }
///
///
/// fn person_new(mut cx: FunctionContext) -> JsResult<BoxedPerson> {
/// let name = cx.argument::<JsString>(0)?.value(&mut cx);
/// let person = RefCell::new(Person::new(name));
///
///
/// Ok(cx.boxed(person))
/// }
///
///
/// fn person_set_name(mut cx: FunctionContext) -> JsResult<JsUndefined> {
/// let person = cx.argument::<BoxedPerson>(0)?;
/// let mut person = person.borrow_mut();
/// let name = cx.argument::<JsString>(1)?.value(&mut cx);
///
///
/// person.set_name(name);
///
///
/// Ok(cx.undefined())
/// }
///
///
/// fn person_greet(mut cx: FunctionContext) -> JsResult<JsString> {
/// let person = cx.argument::<BoxedPerson>(0)?;
/// let person = person.borrow();
/// let greeting = person.greet();
///
///
/// Ok(cx.string(greeting))
/// }
pub struct JsBox<T: Send + 'static> {
@ -137,11 +138,16 @@ pub struct JsBox<T: Send + 'static> {
raw_data: *const T,
}
impl<T: Send + 'static> std::fmt::Debug for JsBox<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "JsBox<{}>", std::any::type_name::<T>())
}
}
// Attempt to use a `napi_value` as a `napi_external` to unwrap a `BoxAny>
/// Safety: `local` must be a `napi_value` that is valid for the lifetime `'a`.
unsafe fn maybe_external_deref<'a>(env: Env, local: raw::Local) -> Option<&'a BoxAny> {
external::deref::<BoxAny>(env.to_raw(), local)
.map(|v| &*v)
external::deref::<BoxAny>(env.to_raw(), local).map(|v| &*v)
}
// Custom `Clone` implementation since `T` might not be `Clone`
@ -154,9 +160,11 @@ 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> { }
impl<T: Send + 'static> Value for JsBox<T> {}
impl<T: Send + 'static> Managed for JsBox<T> {
fn to_raw(self) -> raw::Local {
@ -169,10 +177,7 @@ impl<T: Send + 'static> Managed for JsBox<T> {
.downcast_ref()
.expect("Failed to downcast Any");
Self {
local,
raw_data,
}
Self { local, raw_data }
}
}
@ -193,10 +198,7 @@ impl<T: Send + 'static> ValueInternal for JsBox<T> {
// Attempt to downcast the `Option<&BoxAny>` to `Option<*const T>`
data.and_then(|v| v.downcast_ref())
.map(|raw_data| Self {
local,
raw_data,
})
.map(|raw_data| Self { local, raw_data })
}
}
@ -233,23 +235,15 @@ impl<T: Finalize + Send + 'static> JsBox<T> {
let data = *data.downcast::<U>().unwrap();
let env = unsafe { std::mem::transmute(env) };
FinalizeContext::with(
env,
move |mut cx| data.finalize(&mut cx),
);
FinalizeContext::with(env, move |mut cx| data.finalize(&mut cx));
}
let v = Box::new(value) as BoxAny;
// Since this value was just constructed, we know it is `T`
let raw_data = &*v as *const dyn Any as *const T;
let local = unsafe {
external::create(cx.env().to_raw(), v, finalizer::<T>)
};
let local = unsafe { external::create(cx.env().to_raw(), v, finalizer::<T>) };
Handle::new_internal(Self {
local,
raw_data,
})
Handle::new_internal(Self { local, raw_data })
}
}
@ -364,7 +358,7 @@ impl<T: Finalize> Finalize for Vec<T> {
}
}
// Smart Pointers
// Smart pointers and other wrappers
impl<T: Finalize> Finalize for std::boxed::Box<T> {
fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
@ -372,6 +366,14 @@ impl<T: Finalize> Finalize for std::boxed::Box<T> {
}
}
impl<T: Finalize> Finalize for Option<T> {
fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
if let Some(v) = self {
v.finalize(cx);
}
}
}
impl<T: Finalize> Finalize for std::rc::Rc<T> {
fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
if let Ok(v) = std::rc::Rc::try_unwrap(self) {

129
src/types/date.rs Normal file
View File

@ -0,0 +1,129 @@
use super::{Value, ValueInternal};
use crate::context::internal::Env;
use crate::context::Context;
use crate::handle::{Handle, Managed};
use crate::object::Object;
use crate::result::{JsResult, JsResultExt};
use neon_runtime;
use neon_runtime::raw;
use std::error::Error;
use std::fmt;
use std::fmt::Debug;
/// A JavaScript Date object
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct JsDate(raw::Local);
impl Value for JsDate {}
impl Managed for JsDate {
fn to_raw(self) -> raw::Local {
self.0
}
fn from_raw(_: Env, h: raw::Local) -> Self {
JsDate(h)
}
}
/// The Error struct for a Date
#[derive(Debug)]
pub struct DateError(DateErrorKind);
impl DateError {
pub fn kind(&self) -> DateErrorKind {
self.0
}
}
impl fmt::Display for DateError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str(self.0.as_str())
}
}
impl Error for DateError {}
/// The error kinds corresponding to `DateError`
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum DateErrorKind {
Overflow,
Underflow,
}
impl DateErrorKind {
fn as_str(&self) -> &'static str {
match *self {
DateErrorKind::Overflow => "Date overflow",
DateErrorKind::Underflow => "Date underflow",
}
}
}
impl<'a, T: Value> JsResultExt<'a, T> for Result<Handle<'a, T>, DateError> {
/// Creates an `Error` on error
fn or_throw<'b, C: Context<'b>>(self, cx: &mut C) -> JsResult<'a, T> {
self.or_else(|e| cx.throw_range_error(e.0.as_str()))
}
}
impl JsDate {
/// The smallest possible Date value, defined by ECMAScript. See https://www.ecma-international.org/ecma-262/5.1/#sec-15.7.3.3
pub const MIN_VALUE: f64 = -8.64e15;
/// The largest possible Date value, defined by ECMAScript. See https://www.ecma-international.org/ecma-262/5.1/#sec-15.7.3.2
pub const MAX_VALUE: f64 = 8.64e15;
/// Creates a new Date. It errors when `value` is outside the range of valid JavaScript Date values. When `value`
/// is `NaN`, the operation will succeed but with an invalid Date
pub fn new<'a, C: Context<'a>, T: Into<f64>>(
cx: &mut C,
value: T,
) -> Result<Handle<'a, JsDate>, DateError> {
let env = cx.env().to_raw();
let time = value.into();
if time > JsDate::MAX_VALUE {
return Err(DateError(DateErrorKind::Overflow));
} else if time < JsDate::MIN_VALUE {
return Err(DateError(DateErrorKind::Underflow));
}
let local = unsafe { neon_runtime::date::new_date(env, time) };
let date = Handle::new_internal(JsDate(local));
Ok(date)
}
/// Creates a new Date with lossy conversion for out of bounds Date values. Out of bounds
/// values will be treated as NaN
pub fn new_lossy<'a, C: Context<'a>, V: Into<f64>>(cx: &mut C, value: V) -> Handle<'a, JsDate> {
let env = cx.env().to_raw();
let local = unsafe { neon_runtime::date::new_date(env, value.into()) };
Handle::new_internal(JsDate(local))
}
/// Gets the Date's value. An invalid Date will return `std::f64::NaN`
pub fn value<'a, C: Context<'a>>(self, cx: &mut C) -> f64 {
let env = cx.env().to_raw();
unsafe { neon_runtime::date::value(env, self.to_raw()) }
}
/// Checks if the Date's value is valid. A Date is valid if its value is between
/// `JsDate::MIN_VALUE` and `JsDate::MAX_VALUE` or if it is `NaN`
pub fn is_valid<'a, C: Context<'a>>(self, cx: &mut C) -> bool {
let value = self.value(cx);
(JsDate::MIN_VALUE..=JsDate::MAX_VALUE).contains(&value)
}
}
impl ValueInternal for JsDate {
fn name() -> String {
"object".to_string()
}
fn is_typeof<Other: Value>(env: Env, other: Other) -> bool {
unsafe { neon_runtime::tag::is_date(env.to_raw(), other.to_raw()) }
}
}
impl Object for JsDate {}

View File

@ -1,16 +1,16 @@
//! Types and traits representing JavaScript error values.
use std::panic::{UnwindSafe, catch_unwind};
use std::panic::{catch_unwind, UnwindSafe};
use neon_runtime;
use neon_runtime::raw;
use context::Context;
use context::internal::Env;
use result::{NeonResult, Throw};
use types::{Value, Object, Handle, Managed, build};
use types::internal::ValueInternal;
use types::utf8::Utf8;
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)]
@ -18,26 +18,35 @@ use types::utf8::Utf8;
pub struct JsError(raw::Local);
impl Managed for JsError {
fn to_raw(self) -> raw::Local { self.0 }
fn to_raw(self) -> raw::Local {
self.0
}
fn from_raw(_: Env, h: raw::Local) -> Self { JsError(h) }
fn from_raw(_: Env, h: raw::Local) -> Self {
JsError(h)
}
}
impl ValueInternal for JsError {
fn name() -> String { "Error".to_string() }
fn name() -> String {
"Error".to_string()
}
fn is_typeof<Other: Value>(env: Env, other: Other) -> bool {
unsafe { neon_runtime::tag::is_error(env.to_raw(), other.to_raw()) }
}
}
impl Value for JsError { }
impl Value for JsError {}
impl Object for JsError { }
impl Object for JsError {}
impl JsError {
/// Creates a direct instance of the [`Error`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error) class.
pub fn error<'a, C: Context<'a>, S: AsRef<str>>(cx: &mut C, msg: S) -> NeonResult<Handle<'a, JsError>> {
pub fn error<'a, C: Context<'a>, S: AsRef<str>>(
cx: &mut C,
msg: S,
) -> NeonResult<Handle<'a, JsError>> {
let msg = cx.string(msg.as_ref());
build(cx.env(), |out| unsafe {
neon_runtime::error::new_error(cx.env().to_raw(), out, msg.to_raw());
@ -46,7 +55,10 @@ impl JsError {
}
/// Creates an instance of the [`TypeError`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/TypeError) class.
pub fn type_error<'a, C: Context<'a>, S: AsRef<str>>(cx: &mut C, msg: S) -> NeonResult<Handle<'a, JsError>> {
pub fn type_error<'a, C: Context<'a>, S: AsRef<str>>(
cx: &mut C,
msg: S,
) -> NeonResult<Handle<'a, JsError>> {
let msg = cx.string(msg.as_ref());
build(cx.env(), |out| unsafe {
neon_runtime::error::new_type_error(cx.env().to_raw(), out, msg.to_raw());
@ -55,7 +67,10 @@ impl JsError {
}
/// Creates an instance of the [`RangeError`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RangeError) class.
pub fn range_error<'a, C: Context<'a>, S: AsRef<str>>(cx: &mut C, msg: S) -> NeonResult<Handle<'a, JsError>> {
pub fn range_error<'a, C: Context<'a>, S: AsRef<str>>(
cx: &mut C,
msg: S,
) -> NeonResult<Handle<'a, JsError>> {
let msg = cx.string(msg.as_ref());
build(cx.env(), |out| unsafe {
neon_runtime::error::new_range_error(cx.env().to_raw(), out, msg.to_raw());
@ -64,8 +79,11 @@ impl JsError {
}
}
pub(crate) fn convert_panics<T, F: UnwindSafe + FnOnce() -> NeonResult<T>>(env: Env, f: F) -> NeonResult<T> {
match catch_unwind(|| { f() }) {
pub(crate) fn convert_panics<T, F: UnwindSafe + FnOnce() -> NeonResult<T>>(
env: Env,
f: F,
) -> NeonResult<T> {
match catch_unwind(|| f()) {
Ok(result) => result,
Err(panic) => {
let msg = if let Some(string) = panic.downcast_ref::<String>() {
@ -77,7 +95,7 @@ pub(crate) fn convert_panics<T, F: UnwindSafe + FnOnce() -> NeonResult<T>>(env:
};
let (data, len) = Utf8::from(&msg[..]).truncate().lower();
unsafe {
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
neon_runtime::error::clear_exception(env.to_raw());
neon_runtime::error::throw_error_from_utf8(env.to_raw(), data, len);
Err(Throw)

View File

@ -1,14 +1,14 @@
use super::Value;
use crate::context::internal::Env;
use crate::context::{CallbackInfo, FunctionContext};
use crate::result::JsResult;
use crate::types::error::convert_panics;
use crate::types::{Handle, JsObject, Managed};
use neon_runtime;
use neon_runtime::call::CCallback;
use neon_runtime::raw;
use std::mem;
use std::os::raw::c_void;
use neon_runtime;
use neon_runtime::raw;
use context::{CallbackInfo, FunctionContext};
use context::internal::Env;
use types::error::convert_panics;
use types::{JsObject, Handle, Managed};
use result::JsResult;
use object::class::Callback;
use super::Value;
pub trait ValueInternal: Managed + 'static {
fn name() -> String;
@ -39,19 +39,19 @@ impl<T: Value> Callback<()> for FunctionCallback<T> {
let data = info.data(env);
let dynamic_callback: fn(FunctionContext) -> JsResult<T> =
mem::transmute(neon_runtime::fun::get_dynamic_callback(env.to_raw(), data));
if let Ok(value) = convert_panics(env, || { dynamic_callback(cx) }) {
if let Ok(value) = convert_panics(env, || dynamic_callback(cx)) {
info.set_return(value);
}
})
}
}
fn as_ptr(self) -> *mut c_void {
unsafe { mem::transmute(self.0) }
fn into_ptr(self) -> *mut c_void {
self.0 as *mut _
}
}
#[cfg(feature = "napi-runtime")]
#[cfg(feature = "napi-1")]
impl<T: Value> Callback<raw::Local> for FunctionCallback<T> {
extern "C" fn invoke(env: Env, info: CallbackInfo<'_>) -> raw::Local {
unsafe {
@ -59,7 +59,7 @@ impl<T: Value> Callback<raw::Local> for FunctionCallback<T> {
let data = info.data(env);
let dynamic_callback: fn(FunctionContext) -> JsResult<T> =
mem::transmute(neon_runtime::fun::get_dynamic_callback(env.to_raw(), data));
if let Ok(value) = convert_panics(env, || { dynamic_callback(cx) }) {
if let Ok(value) = convert_panics(env, || dynamic_callback(cx)) {
value.to_raw()
} else {
// We do not have a Js Value to return, most likely due to an exception.
@ -74,7 +74,42 @@ impl<T: Value> Callback<raw::Local> for FunctionCallback<T> {
}
}
fn as_ptr(self) -> *mut c_void {
unsafe { mem::transmute(self.0) }
fn into_ptr(self) -> *mut c_void {
self.0 as *mut _
}
}
/// A dynamically computed callback that can be passed through C to the engine.
/// This type makes it possible to export a dynamically computed Rust function
/// as a pair of 1) a raw pointer to the dynamically computed function, and 2)
/// a static function that knows how to transmute that raw pointer and call it.
pub(crate) trait Callback<T: Clone + Copy + Sized>: Sized {
/// Extracts the computed Rust function and invokes it. The Neon runtime
/// ensures that the computed function is provided as the extra data field,
/// wrapped as a V8 External, in the `CallbackInfo` argument.
extern "C" fn invoke(env: Env, info: CallbackInfo<'_>) -> T;
/// See `invoke`. This is used by the non-n-api implementation, so that every impl for this
/// trait doesn't need to provide two versions of `invoke`.
#[cfg(feature = "legacy-runtime")]
#[doc(hidden)]
extern "C" fn invoke_compat(info: CallbackInfo<'_>) -> T {
Self::invoke(Env::current(), info)
}
/// Converts the callback to a raw void pointer.
fn into_ptr(self) -> *mut c_void;
/// Exports the callback as a pair consisting of the static `Self::invoke`
/// method and the computed callback, both converted to raw void pointers.
fn into_c_callback(self) -> CCallback {
#[cfg(feature = "napi-1")]
let invoke = Self::invoke;
#[cfg(feature = "legacy-runtime")]
let invoke = Self::invoke_compat;
CCallback {
static_callback: unsafe { mem::transmute(invoke as usize) },
dynamic_callback: self.into_ptr(),
}
}
}

Some files were not shown because too many files have changed in this diff Show More