Compare commits
6 Commits
main
...
kv/experim
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e503caa2b9 | ||
|
|
77f1ffdf04 | ||
|
|
a70bc8b50c | ||
|
|
e67b1d1411 | ||
|
|
fad0f694f4 | ||
|
|
74b8914334 |
@ -1,6 +0,0 @@
|
||||
[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
39
.github/workflows/lint.yml
vendored
@ -1,39 +0,0 @@
|
||||
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
|
||||
11
.github/workflows/linux.yml
vendored
11
.github/workflows/linux.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x, 14.x, 16.x]
|
||||
node-version: [10.x, 12.x, 14.x, 15.x]
|
||||
rust-toolchain: [stable, beta, nightly]
|
||||
|
||||
steps:
|
||||
@ -29,10 +29,7 @@ jobs:
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: install build-essential
|
||||
run: sudo apt-get install -y build-essential
|
||||
- name: install g++-4.8
|
||||
run: sudo apt-get install -y g++-4.8
|
||||
- name: run cargo test
|
||||
run: xvfb-run --auto-servernum cargo neon-test -- --nocapture
|
||||
- name: run CLI test
|
||||
working-directory: ./create-neon
|
||||
run: npm test
|
||||
run: xvfb-run --auto-servernum cargo test --release -- --nocapture
|
||||
|
||||
7
.github/workflows/macos.yml
vendored
7
.github/workflows/macos.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x, 14.x, 16.x]
|
||||
node-version: [10.x, 12.x, 14.x, 15.x]
|
||||
rust-toolchain: [stable, beta, nightly]
|
||||
|
||||
steps:
|
||||
@ -34,7 +34,4 @@ jobs:
|
||||
# # https://github.com/nodejs/node-gyp/issues/1933#issuecomment-586915535
|
||||
# run: npm install -g node-gyp@latest
|
||||
- name: run cargo test
|
||||
run: cargo neon-test
|
||||
- name: run CLI test
|
||||
working-directory: ./create-neon
|
||||
run: npm test
|
||||
run: cargo test --release
|
||||
|
||||
11
.github/workflows/windows.yml
vendored
11
.github/workflows/windows.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x, 14.x, 16.x]
|
||||
node-version: [10.x, 12.x, 14.x, 15.x]
|
||||
rust-toolchain: [stable, beta, nightly]
|
||||
|
||||
steps:
|
||||
@ -31,10 +31,10 @@ jobs:
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Use npm v6
|
||||
if: ${{ matrix.node-version == '16.x' }}
|
||||
if: ${{ matrix.node-version == '15.x' }}
|
||||
run: npm install -g npm@6
|
||||
- name: Install libclang
|
||||
uses: KyleMayes/install-llvm-action@01144dc97b1e2693196c3056414a44f15180648b
|
||||
uses: KyleMayes/install-llvm-action@01144dc
|
||||
with:
|
||||
version: "10"
|
||||
directory: ${{ runner.temp }}/llvm
|
||||
@ -43,9 +43,6 @@ jobs:
|
||||
# run: npm install -g node-gyp@latest
|
||||
- run: npm config set msvs_version 2019
|
||||
- name: run cargo test
|
||||
run: cargo neon-test
|
||||
run: cargo test --release
|
||||
env:
|
||||
LIBCLANG_PATH: ${{ runner.temp }}/llvm/bin
|
||||
- name: run CLI test
|
||||
working-directory: ./create-neon
|
||||
run: npm test
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -7,9 +7,6 @@ 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
|
||||
|
||||
@ -17,9 +17,7 @@ Neon owes its existence to the contributions of these fine people.
|
||||
* [Nathaniel Daniel](https://github.com/adumbidiot)
|
||||
* [Joey Ezechiëls](https://github.com/jjpe)
|
||||
* [Cory Forsyth](https://github.com/bantic)
|
||||
* [Olivier Goffart](https://github.com/ogoffart)
|
||||
* [Dave Herman](https://github.com/dherman)
|
||||
* [Himself65](https://github.com/Himself65)
|
||||
* [Maciej Hirsz](https://github.com/maciejhirsz)
|
||||
* [Amal Hussein](https://github.com/nomadtechie)
|
||||
* [Usagi Ito](https://github.com/usagi)
|
||||
@ -29,7 +27,6 @@ Neon owes its existence to the contributions of these fine people.
|
||||
* [Aleksey Kladov](https://github.com/matklad)
|
||||
* [Adam Kloboucnik](https://github.com/akloboucnik)
|
||||
* [Renée Kooi](https://github.com/goto-bus-stop)
|
||||
* [Anton Lazarev](https://github.com/antonok-edm)
|
||||
* [Simon Liang](https://github.com/lhr0909)
|
||||
* [Terence Lee](https://github.com/hone)
|
||||
* [Milan Loveless](https://github.com/MilanLoveless)
|
||||
@ -40,7 +37,6 @@ Neon owes its existence to the contributions of these fine people.
|
||||
* [Robbie Pitts](https://github.com/robbiepitts)
|
||||
* [Thiago Pontes](https://github.com/thiagopnts)
|
||||
* [Sean Prashad](https://github.com/SeanPrashad)
|
||||
* [Jordan Rose](https://github.com/jrose-signal)
|
||||
* [Antonio Scandurra](https://github.com/as-cii)
|
||||
* [Edward Shaw](https://github.com/EdShaw)
|
||||
* [Nathan Sobo](https://github.com/nathansobo)
|
||||
@ -56,7 +52,6 @@ Neon owes its existence to the contributions of these fine people.
|
||||
* [Roberto Vidal](https://github.com/jrvidal)
|
||||
* [Georg Vienna](https://github.com/geovie)
|
||||
* [Daijiro Wachi](https://github.com/watilde)
|
||||
* [Chi Wang](https://github.com/patr0nus)
|
||||
* [wangcong](https://github.com/king6cong)
|
||||
* [Amila Welihinda](https://github.com/amilajack)
|
||||
* [Felix Yan](https://github.com/felixonmars)
|
||||
|
||||
37
Cargo.toml
37
Cargo.toml
@ -1,32 +1,29 @@
|
||||
[package]
|
||||
name = "neon"
|
||||
version = "0.8.2"
|
||||
version = "0.6.0"
|
||||
authors = ["Dave Herman <david.herman@gmail.com>"]
|
||||
description = "A safe abstraction layer for Node.js."
|
||||
readme = "README.md"
|
||||
homepage = "https://www.neon-bindings.com"
|
||||
repository = "https://github.com/neon-bindings/neon"
|
||||
license = "MIT/Apache-2.0"
|
||||
exclude = ["neon.jpg", "doc/**/*"]
|
||||
exclude = ["neon.jpg"]
|
||||
build = "build.rs"
|
||||
edition = "2018"
|
||||
|
||||
[build-dependencies]
|
||||
neon-build = { version = "=0.8.2", path = "crates/neon-build" }
|
||||
neon-build = { version = "=0.6.0", path = "crates/neon-build" }
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
rustversion = "0.1.4"
|
||||
semver = "0.9"
|
||||
psd = "0.1.9" # used for a doc example
|
||||
failure = "0.1.5" # used for a doc example
|
||||
|
||||
[dependencies]
|
||||
cslice = "0.2"
|
||||
semver = "0.9.0"
|
||||
smallvec = "1.4.2"
|
||||
neon-runtime = { version = "=0.8.2", path = "crates/neon-runtime" }
|
||||
neon-macros = { version = "=0.8.2", path = "crates/neon-macros", optional = true }
|
||||
neon-runtime = { version = "=0.6.0", path = "crates/neon-runtime" }
|
||||
neon-macros = { version = "=0.6.0", path = "crates/neon-macros", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["legacy-runtime"]
|
||||
@ -45,22 +42,9 @@ default-panic-hook = []
|
||||
# enabled by default.
|
||||
legacy-runtime = ["neon-runtime/neon-sys", "neon-build/neon-sys"]
|
||||
|
||||
# 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
|
||||
# Feature flag to enable the experimental N-API runtime. For now, this feature
|
||||
# is disabled by default.
|
||||
# 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"]
|
||||
napi-runtime = ["proc-macros", "neon-macros/napi", "neon-runtime/napi"]
|
||||
|
||||
# Feature flag to disable external dependencies on docs build
|
||||
docs-only = ["neon-runtime/docs-only"]
|
||||
@ -68,10 +52,6 @@ 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"]
|
||||
|
||||
@ -87,5 +67,6 @@ members = [
|
||||
"test/static",
|
||||
"test/electron",
|
||||
"test/dynamic/native",
|
||||
"test/napi"
|
||||
"test/napi",
|
||||
"test/tokio",
|
||||
]
|
||||
|
||||
@ -1,320 +1,14 @@
|
||||
# N-API Migration Guide
|
||||
# [WIP] N-API Migration Guide
|
||||
|
||||
## 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
|
||||
## 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.
|
||||
|
||||
#### Affected methods
|
||||
### Impacted 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())
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
17
README.md
17
README.md
@ -13,22 +13,15 @@ Rust bindings for writing safe and fast native Node.js modules.
|
||||
Once you have the [platform dependencies](https://neon-bindings.com/docs/getting-started#install-node-build-tools/) installed, getting started is as simple as:
|
||||
|
||||
```
|
||||
$ npm init neon my-project
|
||||
$ npm install -g neon-cli
|
||||
$ neon new my-project
|
||||
```
|
||||
|
||||
Then see the [Hello World guide](https://neon-bindings.com/docs/hello-world/) for writing your first Hello World in Neon!
|
||||
|
||||
_**Note:** This will create a new project with the `napi-backend` and some documentation may not be up to date._
|
||||
|
||||
# Docs
|
||||
|
||||
See our [Neon fundamentals docs](https://neon-bindings.com/docs/intro) and our [API docs](https://docs.rs/neon/latest/neon).
|
||||
|
||||
# N-API Migration Guide
|
||||
|
||||
We've ported Neon to a new backend based on [N-API](https://nodejs.org/api/n-api.html), which will be the basis for Neon 1.0.
|
||||
|
||||
**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!
|
||||
See our [Neon fundamentals docs](https://neon-bindings.com/docs/primitives) and our [API docs](https://neon-bindings.com/api/neon/).
|
||||
|
||||
# Platform Support
|
||||
|
||||
@ -40,11 +33,11 @@ We've ported Neon to a new backend based on [N-API](https://nodejs.org/api/n-api
|
||||
|
||||
### Node.js
|
||||
|
||||
| Node 12 | Node 14 | Node 16 |
|
||||
| Node 10 | Node 12 | Node 14 |
|
||||
| ------- | ------- | ------- |
|
||||
| ✓ | ✓ | ✓ |
|
||||
|
||||
Support for [LTS versions of Node](https://github.com/nodejs/LTS#release-schedule) and current are expected. If you're using a different version of Node and believe it should be supported, let us know.
|
||||
Support for [LTS versions of Node](https://github.com/nodejs/LTS#lts-schedule) and current are expected. If you're using a different version of Node and believe it should be supported, let us know.
|
||||
|
||||
### Rust
|
||||
|
||||
|
||||
65
RELEASES.md
65
RELEASES.md
@ -1,68 +1,3 @@
|
||||
# Version 0.8.2
|
||||
|
||||
* More docs improvements
|
||||
* Added a deprecation warning to `neon new` (https://github.com/neon-bindings/neon/pull/722)
|
||||
|
||||
# Version 0.8.1
|
||||
|
||||
* Fix `legacy-backend` for Node 16 (https://github.com/neon-bindings/neon/pull/715)
|
||||
* Various docs improvements
|
||||
|
||||
# Version 0.8.0
|
||||
|
||||
## Fixes
|
||||
|
||||
* `as_slice` and `as_mut_slice` properly handle a `null` pointer from an empty buffer (https://github.com/neon-bindings/neon/pull/681)
|
||||
* Global drop queue added to avoid panics on N-API 6+ when dropping a `Root` (https://github.com/neon-bindings/neon/pull/700)
|
||||
|
||||
## Features
|
||||
|
||||
* Added `neon::reflect::eval` (https://github.com/neon-bindings/neon/pull/692)
|
||||
* Added `create-neon` for creating an N-API project (https://github.com/neon-bindings/neon/pull/690)
|
||||
* Added details to the `README.md` generated by `create-neon` (https://github.com/neon-bindings/neon/pull/697)
|
||||
|
||||
## Improvements
|
||||
|
||||
* Switched N-API tests to `cargo-cp-artifact` (https://github.com/neon-bindings/neon/pull/687)
|
||||
* Added `impl<T: Finalize> Finalize for Option<T>` (https://github.com/neon-bindings/neon/pull/680)
|
||||
* Added a N-API migration guide (https://github.com/neon-bindings/neon/pull/685)
|
||||
|
||||
## Housekeeping
|
||||
|
||||
* Lint fixes (https://github.com/neon-bindings/neon/pull/609)
|
||||
* Lint CI enforcement and `cargo fmt` (https://github.com/neon-bindings/neon/pull/698)
|
||||
|
||||
# Version 0.7.1
|
||||
|
||||
### Features
|
||||
|
||||
* 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
26
cli/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "neon-cli",
|
||||
"version": "0.8.2",
|
||||
"version": "0.6.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -616,9 +616,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"handlebars": {
|
||||
"version": "4.7.7",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
|
||||
"integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
|
||||
"version": "4.7.6",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
|
||||
"integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
|
||||
"requires": {
|
||||
"minimist": "^1.2.5",
|
||||
"neo-async": "^2.6.0",
|
||||
@ -849,9 +849,9 @@
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
|
||||
},
|
||||
"lodash.camelcase": {
|
||||
"version": "4.3.0",
|
||||
@ -1365,9 +1365,9 @@
|
||||
"integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw=="
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.13.5",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.5.tgz",
|
||||
"integrity": "sha512-xtB8yEqIkn7zmOyS2zUNBsYCBRhDkvlNxMMY2smuJ/qA8NCHeQvKCF3i9Z4k8FJH4+PJvZRtMrPynfZ75+CSZw==",
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.0.tgz",
|
||||
"integrity": "sha512-Esj5HG5WAyrLIdYU74Z3JdG2PxdIusvj6IWHMtlyESxc7kcDz7zYlYjpnSokn1UbpV0d/QX9fan7gkCNd/9BQA==",
|
||||
"optional": true
|
||||
},
|
||||
"validate-npm-package-license": {
|
||||
@ -1552,9 +1552,9 @@
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
|
||||
"integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "neon-cli",
|
||||
"version": "0.8.2",
|
||||
"version": "0.6.0",
|
||||
"description": "Build and load native Rust/Neon modules.",
|
||||
"author": "Dave Herman <david.herman@gmail.com>",
|
||||
"repository": {
|
||||
|
||||
@ -99,11 +99,6 @@ interface Answers {
|
||||
}
|
||||
|
||||
export default async function wizard(pwd: string, name: string, neon: string | null, features: string | null, noDefaultFeatures: boolean) {
|
||||
let warning = "WARN: `neon new` is deprecated. To create a new project use `npm init neon my-project`.";
|
||||
let banner = "".padStart(warning.length + 4, "#");
|
||||
|
||||
console.warn(`${banner}\n# ${warning} #\n${banner}\n`);
|
||||
|
||||
let its = validateName(name);
|
||||
if (!its.validForNewPackages) {
|
||||
let errors = (its.errors || []).concat(its.warnings || []);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "neon-build"
|
||||
version = "0.8.2"
|
||||
version = "0.6.0"
|
||||
authors = ["Dave Herman <david.herman@gmail.com>"]
|
||||
description = "Build logic required for Neon projects."
|
||||
repository = "https://github.com/neon-bindings/neon"
|
||||
@ -9,4 +9,4 @@ edition = "2018"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
neon-sys = { version = "=0.8.2", path = "../neon-sys", optional = true }
|
||||
neon-sys = { version = "=0.6.0", path = "../neon-sys", optional = true }
|
||||
|
||||
@ -94,9 +94,7 @@ 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
|
||||
@ -147,7 +145,9 @@ 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,7 +155,9 @@ 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);
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "neon-macros"
|
||||
version = "0.8.2"
|
||||
version = "0.6.0"
|
||||
authors = ["Dave Herman <david.herman@gmail.com>"]
|
||||
description = "Procedural macros supporting Neon"
|
||||
repository = "https://github.com/neon-bindings/neon"
|
||||
@ -16,3 +16,8 @@ napi = []
|
||||
[dependencies]
|
||||
quote = "1"
|
||||
syn = { version = "1", features = ["full"] }
|
||||
|
||||
[dev-dependencies.neon]
|
||||
version = "*"
|
||||
path = "../.."
|
||||
features = ["proc-macros"]
|
||||
|
||||
@ -14,19 +14,18 @@ use legacy as macros;
|
||||
// Implementations are in the backend dependent module
|
||||
|
||||
#[proc_macro_attribute]
|
||||
/// Marks a function as the main entry point for initialization in
|
||||
/// a Neon module.
|
||||
///
|
||||
/// This attribute should only be used _once_ in a module and will
|
||||
/// Marks a method as the main entrypoint for initialization in a Neon
|
||||
/// module. This attribute should only be used _once_ in a module and will
|
||||
/// be called each time the module is initialized in a context.
|
||||
///
|
||||
/// ```ignore
|
||||
/// ```no_run
|
||||
/// # use neon::prelude::*;
|
||||
/// #[neon::main]
|
||||
/// fn main(mut cx: ModuleContext) -> NeonResult<()> {
|
||||
/// fn my_module(mut cx: ModuleContext) -> NeonResult<()> {
|
||||
/// let version = cx.string("1.0.0");
|
||||
///
|
||||
///
|
||||
/// cx.export_value("version", version)?;
|
||||
///
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@ -23,7 +23,7 @@ pub(crate) fn main(
|
||||
::std::mem::transmute(m),
|
||||
#name,
|
||||
);
|
||||
|
||||
|
||||
m
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "neon-runtime"
|
||||
version = "0.8.2"
|
||||
version = "0.6.0"
|
||||
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.8.2", path = "../neon-sys", optional = true }
|
||||
neon-sys = { version = "=0.6.0", path = "../neon-sys", optional = true }
|
||||
smallvec = "1.4.2"
|
||||
|
||||
[dev-dependencies]
|
||||
@ -19,12 +19,6 @@ 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]
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
#[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;
|
||||
|
||||
|
||||
@ -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 call;
|
||||
pub mod class;
|
||||
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 module;
|
||||
pub mod mem;
|
||||
pub mod fun;
|
||||
pub mod convert;
|
||||
pub mod class;
|
||||
pub mod task;
|
||||
pub mod handler;
|
||||
pub mod try_catch;
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
//! Fundamental definitions for mapping to the V8 memory space.
|
||||
|
||||
pub use neon_sys::{
|
||||
EscapableHandleScope, FunctionCallbackInfo, HandleScope, InheritedHandleScope, Isolate, Local,
|
||||
};
|
||||
pub use neon_sys::{Local, FunctionCallbackInfo, Isolate, HandleScope, EscapableHandleScope, InheritedHandleScope};
|
||||
|
||||
@ -1,23 +1,15 @@
|
||||
//! Facilities for working with `v8::HandleScope`s and `v8::EscapableHandleScope`s.
|
||||
|
||||
use crate::raw::{EscapableHandleScope, HandleScope, InheritedHandleScope, Isolate};
|
||||
use crate::raw::{HandleScope, EscapableHandleScope, 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)
|
||||
}
|
||||
@ -27,9 +19,7 @@ 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)
|
||||
}
|
||||
@ -39,11 +29,9 @@ 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.
|
||||
|
||||
@ -4,7 +4,7 @@ use crate::raw::{Env, Local};
|
||||
|
||||
use crate::napi::bindings as napi;
|
||||
|
||||
pub unsafe fn new(out: &mut Local, env: Env, length: u32) {
|
||||
pub unsafe extern "C" 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 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 fn len(env: Env, array: Local) -> u32 {
|
||||
pub unsafe extern "C" fn len(env: Env, array: Local) -> u32 {
|
||||
let mut len = 0;
|
||||
assert_eq!(
|
||||
napi::get_array_length(env, array, &mut len as *mut _),
|
||||
|
||||
@ -1,17 +1,16 @@
|
||||
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 fn new(out: &mut Local, env: Env, size: u32) -> bool {
|
||||
pub unsafe extern "C" 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 fn data(env: Env, base_out: &mut *mut c_void, obj: Local) -> usize {
|
||||
pub unsafe extern "C" fn data<'a, 'b>(env: Env, base_out: &'a 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 _),
|
||||
@ -19,32 +18,3 @@ pub unsafe fn data(env: Env, base_out: &mut *mut c_void, obj: Local) -> usize {
|
||||
);
|
||||
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 _);
|
||||
}
|
||||
|
||||
@ -1,327 +1,276 @@
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
#[cfg(windows)]
|
||||
use libloading::os::windows::Library;
|
||||
#[cfg(not(windows))]
|
||||
use libloading::os::unix::Library;
|
||||
|
||||
mod napi1 {
|
||||
use super::super::types::*;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use super::types::*;
|
||||
|
||||
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 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_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_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 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_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 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 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 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 get_element(env: Env, object: Value, index: u32, 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(())
|
||||
}
|
||||
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 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_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 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 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 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 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 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 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_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 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 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;
|
||||
|
||||
fn create_promise(
|
||||
env: Env,
|
||||
deferred: *mut Deferred,
|
||||
promise: *mut Value,
|
||||
) -> Status;
|
||||
|
||||
fn resolve_deferred(
|
||||
env: Env,
|
||||
deferred: Deferred,
|
||||
resolution: Value,
|
||||
) -> Status;
|
||||
|
||||
fn reject_deferred(
|
||||
env: Env,
|
||||
deferred: Deferred,
|
||||
rejection: Value,
|
||||
) -> Status;
|
||||
|
||||
fn is_promise(env: Env, value: Value, is_promise: *mut bool) -> Status;
|
||||
});
|
||||
|
||||
@ -77,17 +77,12 @@ macro_rules! napi_name {
|
||||
///
|
||||
/// // Load N-API symbols from the host process
|
||||
/// // # Safety: Must only be called once
|
||||
/// 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,
|
||||
/// );
|
||||
/// 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?;
|
||||
///
|
||||
/// NAPI = Napi {
|
||||
/// // Load each N-API symbol
|
||||
@ -136,17 +131,10 @@ macro_rules! generate {
|
||||
}
|
||||
};
|
||||
|
||||
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,
|
||||
);
|
||||
pub(crate) unsafe fn load() -> Result<(), libloading::Error> {
|
||||
let host = Library::this();
|
||||
#[cfg(windows)]
|
||||
let host = host?;
|
||||
|
||||
NAPI = Napi {
|
||||
$(
|
||||
@ -171,15 +159,16 @@ use std::sync::Once;
|
||||
pub(crate) use functions::*;
|
||||
pub(crate) use types::*;
|
||||
|
||||
mod functions;
|
||||
mod types;
|
||||
mod functions;
|
||||
|
||||
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.
|
||||
/// 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"));
|
||||
pub fn setup() {
|
||||
SETUP.call_once(|| unsafe {
|
||||
load().expect("Failed to load N-API symbols");
|
||||
});
|
||||
}
|
||||
|
||||
@ -47,24 +47,41 @@ pub struct Ref__ {
|
||||
|
||||
pub type Ref = *mut Ref__;
|
||||
|
||||
#[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>;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Deferred__ {
|
||||
_unused: [u8; 0],
|
||||
}
|
||||
|
||||
pub(crate) type Finalize =
|
||||
Option<unsafe extern "C" fn(env: Env, finalize_data: *mut c_void, finalize_hint: *mut c_void)>;
|
||||
pub type Deferred = *mut Deferred__;
|
||||
|
||||
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),
|
||||
unsafe extern "C" fn(
|
||||
env: Env,
|
||||
js_callback: Value,
|
||||
context: *mut c_void,
|
||||
data: *mut c_void,
|
||||
),
|
||||
>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -127,7 +144,6 @@ pub enum KeyConversion {
|
||||
NumbersToStrings = 1,
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi-4")]
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ThreadsafeFunctionCallMode {
|
||||
@ -135,7 +151,6 @@ pub enum ThreadsafeFunctionCallMode {
|
||||
Blocking = 1,
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi-4")]
|
||||
#[allow(dead_code)]
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
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 fn new(env: Env, out: &mut Local, size: u32) -> bool {
|
||||
pub unsafe extern "C" 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 {
|
||||
@ -18,38 +17,9 @@ pub unsafe fn new(env: Env, out: &mut Local, size: u32) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
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 uninitialized(_out: &mut Local, _size: u32) -> bool { unimplemented!() }
|
||||
|
||||
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 {
|
||||
pub unsafe extern "C" fn data<'a, 'b>(env: Env, base_out: &'a 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 _),
|
||||
@ -57,7 +27,3 @@ pub unsafe fn data(env: Env, base_out: &mut *mut c_void, obj: Local) -> usize {
|
||||
);
|
||||
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 _);
|
||||
}
|
||||
|
||||
@ -4,28 +4,41 @@ 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 fn is_construct(env: Env, info: FunctionCallbackInfo) -> bool {
|
||||
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 {
|
||||
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);
|
||||
|
||||
@ -38,14 +51,21 @@ pub unsafe fn is_construct(env: Env, info: FunctionCallbackInfo) -> bool {
|
||||
!target.is_null()
|
||||
}
|
||||
|
||||
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());
|
||||
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(),
|
||||
);
|
||||
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 fn data(env: Env, info: FunctionCallbackInfo, out: &mut *mut c_void) {
|
||||
pub unsafe extern "C" fn data(env: Env, info: FunctionCallbackInfo, out: &mut *mut c_void) {
|
||||
let mut data = null_mut();
|
||||
let status = napi::get_cb_info(
|
||||
env,
|
||||
@ -61,7 +81,7 @@ pub unsafe fn data(env: Env, info: FunctionCallbackInfo, out: &mut *mut c_void)
|
||||
}
|
||||
|
||||
/// Gets the number of arguments passed to the function.
|
||||
pub unsafe fn len(env: Env, info: FunctionCallbackInfo) -> i32 {
|
||||
pub unsafe extern "C" fn len(env: Env, info: FunctionCallbackInfo) -> i32 {
|
||||
let mut argc = 0usize;
|
||||
let status = napi::get_cb_info(
|
||||
env,
|
||||
@ -76,7 +96,7 @@ pub unsafe fn len(env: Env, info: FunctionCallbackInfo) -> i32 {
|
||||
}
|
||||
|
||||
/// Returns the function arguments as a `SmallVec<[Local; 8]>`
|
||||
pub unsafe fn argv(env: Env, info: FunctionCallbackInfo) -> SmallVec<[Local; 8]> {
|
||||
pub unsafe extern "C" 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();
|
||||
|
||||
39
crates/neon-runtime/src/napi/class.rs
Normal file
39
crates/neon-runtime/src/napi/class.rs
Normal file
@ -0,0 +1,39 @@
|
||||
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!() }
|
||||
@ -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 fn to_object(out: &mut Local, env: Env, value: Local) -> bool {
|
||||
pub unsafe extern "C" 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 fn to_string(out: &mut Local, env: Env, value: Local) -> bool {
|
||||
pub unsafe extern "C" 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
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
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
|
||||
}
|
||||
@ -50,7 +50,12 @@ 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);
|
||||
|
||||
@ -59,7 +64,12 @@ 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);
|
||||
|
||||
@ -68,7 +78,12 @@ 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);
|
||||
|
||||
@ -77,12 +92,22 @@ 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 _, len as usize, out.as_mut_ptr());
|
||||
let status = napi::create_string_utf8(
|
||||
env,
|
||||
msg as *const i8,
|
||||
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);
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
use crate::napi::bindings as napi;
|
||||
use crate::raw::{Env, Local};
|
||||
use crate::napi::bindings as napi;
|
||||
|
||||
/// `finalize_external` is invoked immediately before a `napi_external` is garbage collected
|
||||
extern "C" fn finalize_external<T: Send + 'static>(
|
||||
@ -25,9 +25,16 @@ 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);
|
||||
|
||||
@ -42,7 +49,11 @@ pub unsafe fn deref<T: Send + 'static>(env: Env, local: Local) -> Option<*const
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -50,7 +61,11 @@ pub unsafe fn deref<T: Send + 'static>(env: Env, local: Local) -> Option<*const
|
||||
}
|
||||
|
||||
/// 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();
|
||||
|
||||
|
||||
@ -4,12 +4,12 @@ use std::os::raw::c_void;
|
||||
use std::ptr::null;
|
||||
|
||||
use crate::call::CCallback;
|
||||
use crate::napi::bindings as napi;
|
||||
use crate::raw::{Env, Local};
|
||||
use crate::napi::bindings as napi;
|
||||
|
||||
/// Mutates the `out` argument provided to refer to a newly created `v8::Function`. Returns
|
||||
/// `false` if the value couldn't be created.
|
||||
pub unsafe fn new(out: &mut Local, env: Env, callback: CCallback) -> bool {
|
||||
pub unsafe extern "C" fn new(out: &mut Local, env: Env, callback: CCallback) -> bool {
|
||||
let status = napi::create_function(
|
||||
env,
|
||||
null(),
|
||||
@ -22,37 +22,21 @@ pub unsafe fn new(out: &mut Local, env: Env, callback: CCallback) -> bool {
|
||||
status == napi::Status::Ok
|
||||
}
|
||||
|
||||
pub unsafe fn get_dynamic_callback(_env: Env, data: *mut c_void) -> *mut c_void {
|
||||
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 {
|
||||
data
|
||||
}
|
||||
|
||||
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 _,
|
||||
);
|
||||
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 _);
|
||||
|
||||
status == napi::Status::Ok
|
||||
}
|
||||
|
||||
pub unsafe fn construct(
|
||||
out: &mut Local,
|
||||
env: Env,
|
||||
fun: Local,
|
||||
argc: i32,
|
||||
argv: *mut c_void,
|
||||
) -> bool {
|
||||
pub unsafe extern "C" 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
|
||||
|
||||
7
crates/neon-runtime/src/napi/handler.rs
Normal file
7
crates/neon-runtime/src/napi/handler.rs
Normal file
@ -0,0 +1,7 @@
|
||||
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!() }
|
||||
@ -1,49 +0,0 @@
|
||||
//! # Environment life cycle APIs
|
||||
//!
|
||||
//! These APIs map to the life cycle of a specific "Agent" or self-contained
|
||||
//! environment. If a Neon module is loaded multiple times (Web Workers, worker
|
||||
//! threads), these API will be handle data associated with a specific instance.
|
||||
//!
|
||||
//! See the [N-API Lifecycle][npai-docs] documentation for more details.
|
||||
//!
|
||||
//! [napi-docs]: https://nodejs.org/api/n-api.html#n_api_environment_life_cycle_apis
|
||||
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
use crate::napi::bindings as napi;
|
||||
use crate::raw::Env;
|
||||
|
||||
/// # Safety
|
||||
/// `env` must point to a valid `napi_env` for this thread
|
||||
pub unsafe fn set_instance_data<T: Send + 'static>(env: Env, data: T) -> *mut T {
|
||||
let data = Box::into_raw(Box::new(data));
|
||||
|
||||
assert_eq!(
|
||||
napi::set_instance_data(env, data.cast(), Some(drop_box::<T>), ptr::null_mut(),),
|
||||
napi::Status::Ok,
|
||||
);
|
||||
|
||||
data
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// * `T` must be the same type used in `set_instance_data`
|
||||
/// * Caller must ensure reference does not outlive `Env`
|
||||
/// * Return value may be `null`
|
||||
/// * `env` must point to a valid `napi_env` for this thread
|
||||
pub unsafe fn get_instance_data<T: Send + 'static>(env: Env) -> *mut T {
|
||||
let mut data = MaybeUninit::uninit();
|
||||
|
||||
assert_eq!(
|
||||
napi::get_instance_data(env, data.as_mut_ptr(),),
|
||||
napi::Status::Ok,
|
||||
);
|
||||
|
||||
data.assume_init().cast()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn drop_box<T>(_env: Env, data: *mut c_void, _hint: *mut c_void) {
|
||||
Box::<T>::from_raw(data.cast());
|
||||
}
|
||||
@ -1,11 +1,3 @@
|
||||
use crate::napi::bindings as napi;
|
||||
use crate::raw::{Env, Local};
|
||||
use crate::raw::Local;
|
||||
|
||||
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
|
||||
}
|
||||
pub unsafe extern "C" fn same_handle(_h1: Local, _h2: Local) -> bool { unimplemented!() }
|
||||
|
||||
@ -2,24 +2,23 @@ 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;
|
||||
#[cfg(feature = "napi-4")]
|
||||
pub mod task;
|
||||
pub mod handler;
|
||||
pub mod reference;
|
||||
pub mod tsfn;
|
||||
pub mod promise;
|
||||
|
||||
mod bindings;
|
||||
pub use bindings::*;
|
||||
|
||||
@ -4,14 +4,13 @@ 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 fn new(out: &mut Local, env: Env) {
|
||||
pub unsafe extern "C" 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 fn get_own_property_names(out: &mut Local, env: Env, object: Local) -> bool {
|
||||
pub unsafe extern "C" 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(
|
||||
@ -21,8 +20,7 @@ pub unsafe fn get_own_property_names(out: &mut Local, env: Env, object: Local) -
|
||||
napi::KeyFilter::ALL_PROPERTIES | napi::KeyFilter::SKIP_SYMBOLS,
|
||||
napi::KeyConversion::NumbersToStrings,
|
||||
property_names.as_mut_ptr(),
|
||||
) != napi::Status::Ok
|
||||
{
|
||||
) != napi::Status::Ok {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -31,8 +29,13 @@ pub unsafe fn get_own_property_names(out: &mut Local, env: Env, object: Local) -
|
||||
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 fn get_index(out: &mut Local, env: Env, object: Local, index: u32) -> bool {
|
||||
pub unsafe extern "C" 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
|
||||
@ -45,7 +48,7 @@ pub unsafe fn get_index(out: &mut Local, env: Env, object: Local, index: u32) ->
|
||||
/// see [discussion].
|
||||
///
|
||||
/// [discussion]: https://github.com/neon-bindings/neon/pull/458#discussion_r344827965
|
||||
pub unsafe fn set_index(out: &mut bool, env: Env, object: Local, index: u32, val: Local) -> bool {
|
||||
pub unsafe extern "C" 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;
|
||||
|
||||
@ -53,25 +56,21 @@ pub unsafe fn set_index(out: &mut bool, env: Env, object: Local, index: u32, val
|
||||
}
|
||||
|
||||
/// 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 fn get_string(
|
||||
env: Env,
|
||||
out: &mut Local,
|
||||
object: Local,
|
||||
key: *const u8,
|
||||
len: i32,
|
||||
) -> bool {
|
||||
pub unsafe extern "C" 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 _, len as usize, key_val.as_mut_ptr())
|
||||
if napi::create_string_utf8(env, key as *const i8, 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;
|
||||
}
|
||||
|
||||
@ -84,26 +83,21 @@ pub unsafe fn get_string(
|
||||
/// see [discussion].
|
||||
///
|
||||
/// [discussion]: https://github.com/neon-bindings/neon/pull/458#discussion_r344827965
|
||||
pub unsafe fn set_string(
|
||||
env: Env,
|
||||
out: &mut bool,
|
||||
object: Local,
|
||||
key: *const u8,
|
||||
len: i32,
|
||||
val: Local,
|
||||
) -> bool {
|
||||
pub unsafe extern "C" 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 _, len as usize, key_val.as_mut_ptr())
|
||||
if napi::create_string_utf8(env, key as *const i8, 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;
|
||||
}
|
||||
@ -113,7 +107,7 @@ pub unsafe fn set_string(
|
||||
|
||||
/// 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 fn get(out: &mut Local, env: Env, object: Local, key: Local) -> bool {
|
||||
pub unsafe extern "C" 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
|
||||
@ -125,7 +119,7 @@ pub unsafe fn get(out: &mut Local, env: Env, object: Local, key: Local) -> bool
|
||||
/// see [discussion].
|
||||
///
|
||||
/// [discussion]: https://github.com/neon-bindings/neon/pull/458#discussion_r344827965
|
||||
pub unsafe fn set(out: &mut bool, env: Env, object: Local, key: Local, val: Local) -> bool {
|
||||
pub unsafe extern "C" 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;
|
||||
|
||||
|
||||
@ -1,45 +1,49 @@
|
||||
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 fn undefined(out: &mut Local, env: Env) {
|
||||
pub unsafe extern "C" 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 fn null(out: &mut Local, env: Env) {
|
||||
pub unsafe extern "C" 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 fn boolean(out: &mut Local, env: Env, b: bool) {
|
||||
pub unsafe extern "C" 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 fn boolean_value(env: Env, p: Local) -> bool {
|
||||
pub unsafe extern "C" 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 fn number(out: &mut Local, env: Env, v: f64) {
|
||||
pub unsafe extern "C" 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 fn number_value(env: Env, p: Local) -> f64 {
|
||||
pub unsafe extern "C" 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
|
||||
);
|
||||
value
|
||||
assert_eq!(napi::get_value_double(env, p, &mut value as *mut f64), napi::Status::Ok);
|
||||
return value;
|
||||
}
|
||||
|
||||
153
crates/neon-runtime/src/napi/promise.rs
Normal file
153
crates/neon-runtime/src/napi/promise.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
use crate::napi::bindings as napi;
|
||||
use crate::raw::{Env, Local};
|
||||
|
||||
use super::CallbackInfo;
|
||||
|
||||
pub unsafe fn new(env: Env) -> (napi::Deferred, Local) {
|
||||
let mut deferred = MaybeUninit::uninit();
|
||||
let mut promise = MaybeUninit::uninit();
|
||||
|
||||
assert_eq!(
|
||||
napi::create_promise(env, deferred.as_mut_ptr(), promise.as_mut_ptr()),
|
||||
napi::Status::Ok,
|
||||
);
|
||||
|
||||
(deferred.assume_init(), promise.assume_init())
|
||||
}
|
||||
|
||||
pub unsafe fn resolve(env: Env, deferred: napi::Deferred, value: Local) {
|
||||
assert_eq!(napi::resolve_deferred(env, deferred, value), napi::Status::Ok);
|
||||
}
|
||||
|
||||
pub unsafe fn reject(env: Env, deferred: napi::Deferred, value: Local) {
|
||||
assert_eq!(napi::reject_deferred(env, deferred, value), napi::Status::Ok);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn callback_wrapper<F>(
|
||||
env: Env,
|
||||
info: CallbackInfo,
|
||||
) -> Local
|
||||
where
|
||||
F: FnOnce(Env, Local) + Send + 'static,
|
||||
{
|
||||
let mut argc = 1;
|
||||
let mut argv = [std::ptr::null_mut()];
|
||||
let mut this = MaybeUninit::uninit();
|
||||
let mut data = MaybeUninit::uninit();
|
||||
|
||||
assert_eq!(
|
||||
napi::get_cb_info(
|
||||
env,
|
||||
info,
|
||||
&mut argc,
|
||||
argv.as_mut_ptr(),
|
||||
this.as_mut_ptr(),
|
||||
data.as_mut_ptr(),
|
||||
),
|
||||
napi::Status::Ok,
|
||||
);
|
||||
|
||||
debug_assert_eq!(argc, 1);
|
||||
|
||||
let cb = Box::from_raw(data.assume_init() as *mut F);
|
||||
|
||||
cb(env, argv[0]);
|
||||
|
||||
let mut undefined = MaybeUninit::uninit();
|
||||
|
||||
assert_eq!(
|
||||
napi::get_undefined(env, undefined.as_mut_ptr()),
|
||||
napi::Status::Ok,
|
||||
);
|
||||
|
||||
undefined.assume_init()
|
||||
}
|
||||
|
||||
unsafe fn future_callback<F>(env: Env, f: F) -> Local
|
||||
where
|
||||
F: FnOnce(Env, Local) + Send + 'static,
|
||||
{
|
||||
let mut local = MaybeUninit::uninit();
|
||||
|
||||
assert_eq!(
|
||||
napi::create_function(
|
||||
env,
|
||||
std::ptr::null(),
|
||||
0,
|
||||
Some(callback_wrapper::<F>),
|
||||
Box::into_raw(Box::new(f)) as *mut _,
|
||||
local.as_mut_ptr(),
|
||||
),
|
||||
napi::Status::Ok,
|
||||
);
|
||||
|
||||
local.assume_init()
|
||||
}
|
||||
|
||||
unsafe fn get_key(env: Env, o: Local, k: &str) -> Local {
|
||||
let mut key = MaybeUninit::uninit();
|
||||
|
||||
assert_eq!(
|
||||
napi::create_string_utf8(
|
||||
env,
|
||||
k.as_ptr() as *const _,
|
||||
k.len(),
|
||||
key.as_mut_ptr(),
|
||||
),
|
||||
napi::Status::Ok,
|
||||
);
|
||||
|
||||
let mut prop = MaybeUninit::uninit();
|
||||
|
||||
assert_eq!(
|
||||
napi::get_property(env, o, key.assume_init(), prop.as_mut_ptr()),
|
||||
napi::Status::Ok,
|
||||
);
|
||||
|
||||
prop.assume_init()
|
||||
}
|
||||
|
||||
unsafe fn call_promise_method(
|
||||
env: Env,
|
||||
promise: Local,
|
||||
method: &str,
|
||||
arg: Local,
|
||||
) {
|
||||
let mut result = MaybeUninit::uninit();
|
||||
let argv = [arg];
|
||||
|
||||
assert_eq!(
|
||||
napi::call_function(
|
||||
env,
|
||||
promise,
|
||||
get_key(env, promise, method),
|
||||
1,
|
||||
argv.as_ptr(),
|
||||
result.as_mut_ptr(),
|
||||
),
|
||||
napi::Status::Ok,
|
||||
);
|
||||
}
|
||||
|
||||
pub unsafe fn adapter<Resolve, Reject>(
|
||||
env: Env,
|
||||
maybe_promise: Local,
|
||||
resolve: Resolve,
|
||||
reject: Reject,
|
||||
)
|
||||
where
|
||||
Resolve: FnOnce(Env, Local) + Send + 'static,
|
||||
Reject: FnOnce(Env, Local) + Send + 'static,
|
||||
{
|
||||
let (deferred, promise) = new(env);
|
||||
|
||||
self::resolve(env, deferred, maybe_promise);
|
||||
|
||||
let resolve = future_callback(env, resolve);
|
||||
let reject = future_callback(env, reject);
|
||||
|
||||
call_promise_method(env, promise, "then", resolve);
|
||||
call_promise_method(env, promise, "catch", reject);
|
||||
}
|
||||
@ -11,41 +11,21 @@ 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for HandleScope {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
pub fn new() -> Self { Self { word: ptr::null_mut() } }
|
||||
}
|
||||
|
||||
#[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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EscapableHandleScope {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
pub fn new() -> Self { Self { word: ptr::null_mut() } }
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
||||
@ -2,7 +2,7 @@ use std::mem::MaybeUninit;
|
||||
|
||||
use crate::napi::bindings as napi;
|
||||
|
||||
use crate::raw::{Env, Local};
|
||||
use crate::raw::{Local, Env};
|
||||
|
||||
pub unsafe fn new(env: Env, value: Local) -> napi::Ref {
|
||||
let mut result = MaybeUninit::uninit();
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
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.
|
||||
@ -12,9 +13,7 @@ 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());
|
||||
@ -31,9 +30,7 @@ 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());
|
||||
@ -50,22 +47,17 @@ 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 fn escape(env: Env, out: &mut Local, scope: *mut EscapableHandleScope, value: Local) {
|
||||
pub unsafe extern "C" 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 fn get_global(env: Env, out: &mut Local) {
|
||||
assert_eq!(
|
||||
crate::napi::bindings::get_global(env, out as *mut _),
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1,36 +1,46 @@
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr;
|
||||
|
||||
use crate::napi::bindings as napi;
|
||||
use crate::raw::{Env, Local};
|
||||
use crate::napi::bindings as napi;
|
||||
|
||||
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 _, len as usize, out);
|
||||
let status = napi::create_string_utf8(
|
||||
env,
|
||||
data as *const i8,
|
||||
len as usize,
|
||||
out,
|
||||
);
|
||||
|
||||
status == napi::Status::Ok
|
||||
}
|
||||
|
||||
pub unsafe fn utf8_len(env: Env, value: Local) -> isize {
|
||||
pub unsafe extern "C" 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 fn data(env: Env, out: *mut u8, len: isize, value: Local) -> isize {
|
||||
pub unsafe extern "C" 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 _, len as usize, read.as_mut_ptr());
|
||||
let status = napi::get_value_string_utf8(
|
||||
env,
|
||||
value,
|
||||
out as *mut i8,
|
||||
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
|
||||
}
|
||||
|
||||
@ -1,91 +1,72 @@
|
||||
use crate::napi::bindings as napi;
|
||||
use crate::raw::{Env, Local};
|
||||
use crate::napi::bindings as napi;
|
||||
|
||||
/// 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 fn is_undefined(env: Env, val: Local) -> bool {
|
||||
pub unsafe extern "C" fn is_undefined(env: Env, val: Local) -> bool {
|
||||
is_type(env, val, napi::ValueType::Undefined)
|
||||
}
|
||||
|
||||
pub unsafe fn is_null(env: Env, val: Local) -> bool {
|
||||
pub unsafe extern "C" fn is_null(env: Env, val: Local) -> bool {
|
||||
is_type(env, val, napi::ValueType::Null)
|
||||
}
|
||||
|
||||
/// Is `val` a JavaScript number?
|
||||
pub unsafe fn is_number(env: Env, val: Local) -> bool {
|
||||
pub unsafe extern "C" fn is_number(env: Env, val: Local) -> bool {
|
||||
is_type(env, val, napi::ValueType::Number)
|
||||
}
|
||||
|
||||
/// Is `val` a JavaScript boolean?
|
||||
pub unsafe fn is_boolean(env: Env, val: Local) -> bool {
|
||||
pub unsafe extern "C" fn is_boolean(env: Env, val: Local) -> bool {
|
||||
is_type(env, val, napi::ValueType::Boolean)
|
||||
}
|
||||
|
||||
/// Is `val` a JavaScript string?
|
||||
pub unsafe fn is_string(env: Env, val: Local) -> bool {
|
||||
pub unsafe extern "C" fn is_string(env: Env, val: Local) -> bool {
|
||||
is_type(env, val, napi::ValueType::String)
|
||||
}
|
||||
|
||||
pub unsafe fn is_object(env: Env, val: Local) -> bool {
|
||||
pub unsafe extern "C" fn is_object(env: Env, val: Local) -> bool {
|
||||
is_type(env, val, napi::ValueType::Object)
|
||||
}
|
||||
|
||||
pub unsafe fn is_array(env: Env, val: Local) -> bool {
|
||||
pub unsafe extern "C" 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 fn is_function(env: Env, val: Local) -> bool {
|
||||
pub unsafe extern "C" fn is_function(env: Env, val: Local) -> bool {
|
||||
is_type(env, val, napi::ValueType::Function)
|
||||
}
|
||||
|
||||
pub unsafe fn is_error(env: Env, val: Local) -> bool {
|
||||
pub unsafe extern "C" 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 fn is_buffer(env: Env, val: Local) -> bool {
|
||||
pub unsafe extern "C" 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 fn is_arraybuffer(env: Env, val: Local) -> bool {
|
||||
pub unsafe extern "C" 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 {
|
||||
pub unsafe fn is_promise(env: Env, val: Local) -> bool {
|
||||
let mut result = false;
|
||||
assert_eq!(
|
||||
napi::is_date(env, val, &mut result as *mut _),
|
||||
napi::Status::Ok
|
||||
);
|
||||
assert_eq!(napi::is_promise(env, val, &mut result as *mut _), napi::Status::Ok);
|
||||
result
|
||||
}
|
||||
|
||||
7
crates/neon-runtime/src/napi/task.rs
Normal file
7
crates/neon-runtime/src/napi/task.rs
Normal file
@ -0,0 +1,7 @@
|
||||
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!() }
|
||||
@ -1,10 +1,8 @@
|
||||
//! 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};
|
||||
use crate::raw::{Local, Env};
|
||||
|
||||
unsafe fn string(env: Env, s: impl AsRef<str>) -> Local {
|
||||
let s = s.as_ref();
|
||||
@ -30,50 +28,45 @@ 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),
|
||||
callback: fn(Env, T),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Callback<T> {
|
||||
callback: fn(Option<Env>, T),
|
||||
callback: fn(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 {
|
||||
// Caller must maintain that `Env` is valid for the current thread
|
||||
pub unsafe fn new(
|
||||
env: Env,
|
||||
callback: fn(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),
|
||||
callback: fn(Env, T),
|
||||
) -> Self {
|
||||
let mut result = MaybeUninit::uninit();
|
||||
|
||||
@ -95,28 +88,33 @@ impl<T: Send + 'static> ThreadsafeFunction<T> {
|
||||
),
|
||||
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 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) };
|
||||
let status = unsafe {
|
||||
napi::call_threadsafe_function(
|
||||
self.tsfn.0,
|
||||
callback as *mut _,
|
||||
is_blocking,
|
||||
)
|
||||
};
|
||||
|
||||
if status == napi::Status::Ok {
|
||||
Ok(())
|
||||
@ -131,35 +129,47 @@ impl<T: Send + 'static> ThreadsafeFunction<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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::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::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 may be terminated
|
||||
if data.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
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) };
|
||||
if env.is_null() {
|
||||
eprintln!("This is surprising");
|
||||
return;
|
||||
}
|
||||
|
||||
callback(env, data);
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "neon-sys"
|
||||
version = "0.8.2"
|
||||
version = "0.6.0"
|
||||
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"
|
||||
|
||||
@ -12,11 +12,11 @@ mod build {
|
||||
|
||||
#[cfg(not(feature = "docs-only"))]
|
||||
mod build {
|
||||
use regex::Regex;
|
||||
use std::process::Command;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use regex::Regex;
|
||||
|
||||
pub fn main() {
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
@ -94,8 +94,7 @@ 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"])
|
||||
}
|
||||
@ -110,10 +109,7 @@ 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]
|
||||
}
|
||||
|
||||
@ -135,10 +131,7 @@ 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]
|
||||
}
|
||||
|
||||
@ -154,70 +147,37 @@ mod build {
|
||||
}
|
||||
|
||||
// Ensure that all package.json dependencies and dev dependencies are installed.
|
||||
npm(native_dir)
|
||||
.args(&["install", "--silent"])
|
||||
.status()
|
||||
.expect("Failed to run \"npm install\" for neon-sys!");
|
||||
npm(native_dir).args(&["install", "--silent"]).status().ok().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!(
|
||||
panic!(format!(
|
||||
"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.
|
||||
@ -246,10 +206,7 @@ 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)]
|
||||
@ -280,7 +237,7 @@ mod build {
|
||||
fn debug() -> bool {
|
||||
match env::var("DEBUG") {
|
||||
Ok(s) => s == "true",
|
||||
Err(_) => false,
|
||||
Err(_) => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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::Isolate *isolate, v8::Local<v8::Object> *out, uint32_t size) {
|
||||
extern "C" bool Neon_Buffer_Uninitialized(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(node::GetCurrentEnvironment(isolate->GetCurrentContext()), cleanup_class_map, holder);
|
||||
node::AtExit(cleanup_class_map, holder);
|
||||
}
|
||||
|
||||
extern "C" void *Neon_Class_GetCallKernel(void *wrapper) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
use std::mem;
|
||||
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,19 +38,11 @@ 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() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for HandleScope {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
pub fn new() -> HandleScope { unsafe { mem::zeroed() } }
|
||||
}
|
||||
|
||||
const ESCAPABLE_HANDLE_SCOPE_SIZE: usize = 32;
|
||||
@ -64,32 +56,24 @@ 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() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EscapableHandleScope {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
pub fn new() -> EscapableHandleScope { unsafe { mem::zeroed() } }
|
||||
}
|
||||
|
||||
#[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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -99,7 +83,7 @@ pub enum TryCatchControl {
|
||||
Returned = 0,
|
||||
Threw = 1,
|
||||
Panicked = 2,
|
||||
UnexpectedErr = 3,
|
||||
UnexpectedErr = 3
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@ -111,12 +95,10 @@ 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 "C" 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 fn(rust_thunk: *mut c_void,
|
||||
cx: *mut c_void,
|
||||
result: *mut c_void,
|
||||
unwind_value: *mut *mut c_void) -> TryCatchControl;
|
||||
|
||||
extern "C" {
|
||||
|
||||
@ -124,19 +106,11 @@ 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(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_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_Call_SetReturn(info: FunctionCallbackInfo, value: Local);
|
||||
pub fn Neon_Call_GetIsolate(info: FunctionCallbackInfo) -> Isolate;
|
||||
@ -149,38 +123,17 @@ 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;
|
||||
@ -200,52 +153,22 @@ 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 "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_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_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;
|
||||
|
||||
@ -260,24 +183,9 @@ 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 "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_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_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);
|
||||
@ -304,19 +212,14 @@ 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 "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_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_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 "C" 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 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.
|
||||
@ -324,12 +227,10 @@ 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;
|
||||
}
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"extension": ["ts"],
|
||||
"spec": "test/**/*.ts",
|
||||
"require": "ts-node/register",
|
||||
"timeout": 5000
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
# 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
|
||||
```
|
||||
@ -1,5 +0,0 @@
|
||||
target
|
||||
index.node
|
||||
**/node_modules
|
||||
**/.DS_Store
|
||||
npm-debug.log*
|
||||
@ -1,22 +0,0 @@
|
||||
[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}}"]
|
||||
@ -1,109 +0,0 @@
|
||||
# {{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).
|
||||
@ -1,11 +0,0 @@
|
||||
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(())
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
{
|
||||
"neon": "0.8",
|
||||
"napi": "6",
|
||||
"cargo-cp-artifact": "0.1"
|
||||
}
|
||||
@ -1,104 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
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
1103
create-neon/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,49 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@ -1,70 +0,0 @@
|
||||
#!/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]);
|
||||
@ -1,4 +0,0 @@
|
||||
export default function die(message: string): never {
|
||||
console.error(`❌ ${message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
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' });
|
||||
}
|
||||
@ -1,76 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@ -1,100 +0,0 @@
|
||||
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>']);
|
||||
});
|
||||
});
|
||||
@ -1,31 +0,0 @@
|
||||
{
|
||||
"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
BIN
doc/types.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 29 KiB |
BIN
doc/types.pptx
BIN
doc/types.pptx
Binary file not shown.
@ -1,7 +1,7 @@
|
||||
use crate::borrow::LoanError;
|
||||
use std;
|
||||
use std::collections::HashSet;
|
||||
use std::os::raw::c_void;
|
||||
use std::collections::HashSet;
|
||||
use borrow::LoanError;
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,91 +1,66 @@
|
||||
//! Provides temporary access to JavaScript typed arrays.
|
||||
//!
|
||||
//! ## Typed Arrays
|
||||
//!
|
||||
//! JavaScript's [typed arrays][typed-arrays] are objects that allow reading and writing
|
||||
//! raw binary data in memory.
|
||||
//!
|
||||
//! Typed arrays are managed with the [`ArrayBuffer`][ArrayBuffer] type, which controls
|
||||
//! the storage of the underlying data buffer, and several typed views for managing access
|
||||
//! to the buffer. Neon provides access to the `ArrayBuffer` class with the
|
||||
//! [`JsArrayBuffer`](crate::types::JsArrayBuffer) type.
|
||||
//!
|
||||
//! Node also provides a [`Buffer`][Buffer] type, which is built on top of `ArrayBuffer`
|
||||
//! and provides additional functionality. Neon provides access to the `Buffer` class
|
||||
//! with the [`JsBuffer`](crate::types::JsBuffer) type.
|
||||
//!
|
||||
//! Many of Node's I/O APIs work with these types, and they can also be used for
|
||||
//! compact in-memory data structures, which can be shared efficiently between
|
||||
//! JavaScript and Rust without copying.
|
||||
//!
|
||||
//! ## Borrowing
|
||||
//!
|
||||
//! Neon makes it possible to [borrow][borrow] temporary access to the internal memory
|
||||
//! of a typed array by pausing execution of JavaScript with a
|
||||
//! [`Lock`](crate::context::Lock) and returning a reference to a
|
||||
//! [`BinaryData`](crate::types::BinaryData) struct. The [`Borrow`](Borrow) and
|
||||
//! [`BorrowMut`](BorrowMut) traits provide the methods for borrowing this typed array data.
|
||||
//!
|
||||
//! [typed-arrays]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays
|
||||
//! [borrow]: https://doc.rust-lang.org/beta/rust-by-example/scope/borrow.html
|
||||
//! [ArrayBuffer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
|
||||
//! [Buffer]: https://nodejs.org/api/buffer.html
|
||||
//! Types and traits for obtaining temporary access to the internals of JavaScript values.
|
||||
|
||||
pub(crate) mod internal;
|
||||
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut, Drop};
|
||||
use std::fmt;
|
||||
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 {
|
||||
@ -104,7 +79,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> {
|
||||
@ -133,7 +108,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> {
|
||||
|
||||
@ -1,39 +1,33 @@
|
||||
use super::ModuleContext;
|
||||
use crate::handle::Handle;
|
||||
use std;
|
||||
#[cfg(feature = "legacy-runtime")]
|
||||
use crate::object::class::ClassMap;
|
||||
use crate::result::NeonResult;
|
||||
use crate::types::{JsObject, JsValue};
|
||||
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 neon_runtime;
|
||||
use neon_runtime::raw;
|
||||
use neon_runtime::scope::Root;
|
||||
#[cfg(feature = "legacy-runtime")]
|
||||
use neon_runtime::try_catch::TryCatchControl;
|
||||
#[cfg(feature = "legacy-runtime")]
|
||||
use 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};
|
||||
use types::{JsObject, JsValue};
|
||||
use handle::Handle;
|
||||
use object::class::ClassMap;
|
||||
use result::NeonResult;
|
||||
use super::ModuleContext;
|
||||
|
||||
#[cfg(feature = "legacy-runtime")]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Env(raw::Isolate);
|
||||
|
||||
#[cfg(feature = "napi-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
#[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);
|
||||
}
|
||||
@ -45,41 +39,47 @@ impl Env {
|
||||
ptr
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
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 = raw.cast();
|
||||
ptr = unsafe { std::mem::transmute(raw) };
|
||||
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 { &mut *ptr.cast() }
|
||||
unsafe { std::mem::transmute(ptr) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
pub(crate) fn current() -> Env {
|
||||
panic!("Context::current() will not implemented with n-api")
|
||||
}
|
||||
|
||||
#[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,17 +122,12 @@ 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<T, F>(&mut self, f: F) -> Result<T, Handle<'a, JsValue>>
|
||||
where
|
||||
F: FnOnce(&mut Self) -> NeonResult<T>,
|
||||
fn try_catch_internal<'b: 'a, 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++.
|
||||
@ -143,14 +138,12 @@ 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 {
|
||||
@ -171,10 +164,9 @@ pub trait ContextInternal<'a>: Sized {
|
||||
}
|
||||
}
|
||||
|
||||
#[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>,
|
||||
#[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>
|
||||
{
|
||||
let result = f(self);
|
||||
let mut local: MaybeUninit<raw::Local> = MaybeUninit::zeroed();
|
||||
@ -191,18 +183,15 @@ 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 { &mut *cx.cast() };
|
||||
let cx: &mut C = unsafe { std::mem::transmute(cx) };
|
||||
|
||||
// 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,
|
||||
@ -214,16 +203,18 @@ where
|
||||
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
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,20 +227,9 @@ pub fn initialize_module(exports: Handle<JsObject>, init: fn(ModuleContext) -> N
|
||||
});
|
||||
}
|
||||
|
||||
#[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;
|
||||
});
|
||||
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
pub fn initialize_module(env: raw::Env, exports: Handle<JsObject>, init: fn(ModuleContext) -> NeonResult<()>) {
|
||||
neon_runtime::setup();
|
||||
ModuleContext::with(Env(env), exports, |cx| {
|
||||
let _ = init(cx);
|
||||
});
|
||||
|
||||
@ -1,184 +1,31 @@
|
||||
//! Provides runtime access to the JavaScript engine.
|
||||
//!
|
||||
//! An _execution context_ represents the current state of a thread of execution in the
|
||||
//! JavaScript engine. Internally, it tracks things like the set of pending function calls,
|
||||
//! whether the engine is currently throwing an exception or not, and whether the engine is
|
||||
//! in the process of shutting down. The context uses this internal state to manage what
|
||||
//! operations are safely available and when.
|
||||
//!
|
||||
//! The [`Context`](Context) trait provides an abstract interface to the JavaScript
|
||||
//! execution context. All interaction with the JavaScript engine in Neon code is mediated
|
||||
//! through instances of this trait.
|
||||
//!
|
||||
//! One particularly useful context type is [`CallContext`](CallContext), which is passed
|
||||
//! to all Neon functions as their initial execution context (or [`FunctionContext`](FunctionContext),
|
||||
//! a convenient shorthand for `CallContext<JsObject>`):
|
||||
//!
|
||||
//! ```
|
||||
//! # use neon::prelude::*;
|
||||
//! fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
|
||||
//! Ok(cx.string("hello Neon"))
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Another important context type is [`ModuleContext`](ModuleContext), which is provided
|
||||
//! to a Neon module's [`main`](crate::main) function to enable sharing Neon functions back
|
||||
//! with JavaScript:
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "neon-macros")] {
|
||||
//! # use neon::prelude::*;
|
||||
//! # fn hello(_: FunctionContext) -> JsResult<JsValue> { todo!() }
|
||||
//! #[neon::main]
|
||||
//! fn main(mut cx: ModuleContext) -> NeonResult<()> {
|
||||
//! cx.export_function("hello", hello)?;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Memory Management
|
||||
//!
|
||||
//! Because contexts represent the engine at a point in time, they are associated with a
|
||||
//! [_lifetime_][lifetime], which limits how long Rust code is allowed to access them. This
|
||||
//! is also used to determine the lifetime of [`Handle`](crate::handle::Handle)s, which
|
||||
//! provide safe references to JavaScript memory managed by the engine's garbage collector.
|
||||
//!
|
||||
//! For example, we can
|
||||
//! write a simple string scanner that counts whitespace in a JavaScript string and
|
||||
//! returns a [`JsNumber`](crate::types::JsNumber):
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "napi-1")] {
|
||||
//! # use neon::prelude::*;
|
||||
//! fn count_whitespace(mut cx: FunctionContext) -> JsResult<JsNumber> {
|
||||
//! let s: Handle<JsString> = cx.argument(0)?;
|
||||
//! let contents = s.value(&mut cx);
|
||||
//! let count = contents
|
||||
//! .chars() // iterate over the characters
|
||||
//! .filter(|c| c.is_whitespace()) // select the whitespace chars
|
||||
//! .count(); // count the resulting chars
|
||||
//! Ok(cx.number(count as f64))
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! In this example, `s` is assigned a handle to a string, which ensures that the string
|
||||
//! is _kept alive_ (i.e., prevented from having its storage reclaimed by the JavaScript
|
||||
//! engine's garbage collector) for the duration of the `count_whitespace` function. This
|
||||
//! is how Neon takes advantage of Rust's type system to allow your Rust code to safely
|
||||
//! interact with JavaScript values.
|
||||
//!
|
||||
//! ### Temporary Scopes
|
||||
//!
|
||||
//! Sometimes it can be useful to limit the scope of a handle's lifetime, to allow the
|
||||
//! engine to reclaim memory sooner. This can be important when, for example, an expensive inner loop generates
|
||||
//! temporary JavaScript values that are only needed inside the loop. In these cases,
|
||||
//! the [`execute_scoped`](Context::execute_scoped) and [`compute_scoped`](Context::compute_scoped)
|
||||
//! methods allow you to create temporary contexts in order to allocate temporary
|
||||
//! handles.
|
||||
//!
|
||||
//! For example, to extract the elements of a JavaScript [iterator][iterator] from Rust,
|
||||
//! a Neon function has to work with several temporary handles on each pass through
|
||||
//! the loop:
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "napi-1")] {
|
||||
//! # use neon::prelude::*;
|
||||
//! # fn iterate(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
||||
//! let iterator = cx.argument::<JsObject>(0)?; // iterator object
|
||||
//! let next = iterator.get(&mut cx, "next")? // iterator's `next` method
|
||||
//! .downcast_or_throw::<JsFunction, _>(&mut cx)?;
|
||||
//! let mut numbers = vec![]; // results vector
|
||||
//! let mut done = false; // loop controller
|
||||
//!
|
||||
//! while !done {
|
||||
//! done = cx.execute_scoped(|mut cx| { // temporary scope
|
||||
//! let args: Vec<Handle<JsValue>> = vec![];
|
||||
//! let obj = next.call(&mut cx, iterator, args)? // temporary object
|
||||
//! .downcast_or_throw::<JsObject, _>(&mut cx)?;
|
||||
//! let number = obj.get(&mut cx, "value")? // temporary number
|
||||
//! .downcast_or_throw::<JsNumber, _>(&mut cx)?
|
||||
//! .value(&mut cx);
|
||||
//! numbers.push(number);
|
||||
//! Ok(obj.get(&mut cx, "done")? // temporary boolean
|
||||
//! .downcast_or_throw::<JsBoolean, _>(&mut cx)?
|
||||
//! .value(&mut cx))
|
||||
//! })?;
|
||||
//! }
|
||||
//! # Ok(cx.undefined())
|
||||
//! # }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The temporary scope ensures that the temporary values are only kept alive
|
||||
//! during a single pass through the loop, since the temporary context is
|
||||
//! discarded (and all of its handles released) on the inside of the loop.
|
||||
//!
|
||||
//! ## Throwing Exceptions
|
||||
//!
|
||||
//! When a Neon API causes a JavaScript exception to be thrown, it returns an
|
||||
//! [`Err`](std::result::Result::Err) result, indicating that the thread associated
|
||||
//! with the context is now throwing. This allows Rust code to perform any
|
||||
//! cleanup before returning, but with an important restriction:
|
||||
//!
|
||||
//! > **While a JavaScript thread is throwing, its context cannot be used.**
|
||||
//!
|
||||
//! Unless otherwise documented, any Neon API that uses a context (as `self` or as
|
||||
//! a parameter) immediately panics if called while the context's thread is throwing.
|
||||
//!
|
||||
//! Typically, Neon code can manage JavaScript exceptions correctly and conveniently
|
||||
//! by using Rust's [question mark (`?`)][question-mark] operator. This ensures that
|
||||
//! Rust code "short-circuits" when an exception is thrown and returns back to
|
||||
//! JavaScript without calling any throwing APIs.
|
||||
//!
|
||||
//! Alternatively, to invoke a Neon API and catch any JavaScript exceptions, use the
|
||||
//! [`Context::try_catch`](Context::try_catch) method, which catches any thrown
|
||||
//! exception and restores the context to non-throwing state.
|
||||
//!
|
||||
//! ## See also
|
||||
//!
|
||||
//! 1. Ecma International. [Execution contexts](https://tc39.es/ecma262/#sec-execution-contexts), _ECMAScript Language Specification_.
|
||||
//! 2. Madhavan Nagarajan. [What is the Execution Context and Stack in JavaScript?](https://medium.com/@itIsMadhavan/what-is-the-execution-context-stack-in-javascript-e169812e851a)
|
||||
//! 3. Rupesh Mishra. [Execution context, Scope chain and JavaScript internals](https://medium.com/@happymishra66/execution-context-in-javascript-319dd72e8e2c).
|
||||
//!
|
||||
//! [lifetime]: https://doc.rust-lang.org/book/ch10-00-generics.html
|
||||
//! [iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
|
||||
//! [question-mark]: https://doc.rust-lang.org/edition-guide/rust-2018/error-handling-and-panics/the-question-mark-operator-for-easier-error-handling.html
|
||||
//! Node _execution contexts_, which manage access to the JavaScript engine at various points in the Node.js runtime lifecycle.
|
||||
|
||||
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};
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
use sync::EventQueue;
|
||||
use types::{JsValue, Value, JsObject, JsArray, JsFunction, JsBoolean, JsNumber, JsString, StringResult, JsNull, JsUndefined, JsPromise, Deferred};
|
||||
#[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)]
|
||||
@ -196,17 +43,14 @@ 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)
|
||||
}
|
||||
|
||||
#[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()) }
|
||||
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")]
|
||||
@ -218,7 +62,7 @@ impl CallbackInfo<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
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
|
||||
@ -228,7 +72,9 @@ 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")]
|
||||
@ -243,9 +89,11 @@ impl CallbackInfo<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
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 {
|
||||
@ -258,22 +106,20 @@ impl CallbackInfo<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates whether a function was called with `new`.
|
||||
/// Indicates whether a function call was called with JavaScript's `[[Call]]` or `[[Construct]]` semantics.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum CallKind {
|
||||
Construct,
|
||||
Call,
|
||||
Call
|
||||
}
|
||||
|
||||
/// A temporary lock of an execution context.
|
||||
/// An RAII implementation of a "scoped lock" of the JS engine. When this structure is dropped (falls out of scope), the engine will be unlocked.
|
||||
///
|
||||
/// While a lock is alive, no JavaScript code can be executed in the execution context.
|
||||
///
|
||||
/// Objects that support the `Borrow` and `BorrowMut` traits can be inspected while the context is locked by passing a reference to a `Lock` to their methods.
|
||||
/// Types of JS values that support the `Borrow` and `BorrowMut` traits can be inspected while the engine is locked by passing a reference to a `Lock` to their methods.
|
||||
pub struct Lock<'a> {
|
||||
pub(crate) ledger: RefCell<Ledger>,
|
||||
pub(crate) env: Env,
|
||||
phantom: PhantomData<&'a ()>,
|
||||
phantom: PhantomData<&'a ()>
|
||||
}
|
||||
|
||||
impl<'a> Lock<'a> {
|
||||
@ -286,14 +132,13 @@ impl<'a> Lock<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// An _execution context_, which represents the current state of a thread of execution in the JavaScript engine.
|
||||
///
|
||||
/// All interaction with the JavaScript engine in Neon code is mediated through instances of this trait.
|
||||
///
|
||||
/// An _execution context_, which provides context-sensitive access to the JavaScript engine. Most operations that interact with the engine require passing a reference to a context.
|
||||
///
|
||||
/// A context has a lifetime `'a`, which ensures the safety of handles managed by the JS garbage collector. All handles created during the lifetime of a context are kept alive for that duration and cannot outlive the context.
|
||||
pub trait Context<'a>: ContextInternal<'a> {
|
||||
|
||||
/// 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();
|
||||
@ -301,9 +146,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> {
|
||||
@ -313,17 +158,16 @@ 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);
|
||||
@ -331,9 +175,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> {
|
||||
@ -345,17 +189,16 @@ 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);
|
||||
@ -363,13 +206,12 @@ 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();
|
||||
@ -379,37 +221,32 @@ 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<T, F>(&mut self, f: F) -> Result<T, Handle<'a, JsValue>>
|
||||
where
|
||||
F: FnOnce(&mut Self) -> NeonResult<T>,
|
||||
fn try_catch<'b: 'a, T, F>(&mut self, f: F) -> Result<T, Handle<'a, JsValue>>
|
||||
where F: FnOnce(&mut Self) -> NeonResult<T>
|
||||
{
|
||||
self.try_catch_internal(f)
|
||||
}
|
||||
@ -425,14 +262,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)
|
||||
@ -442,7 +279,7 @@ pub trait Context<'a>: ContextInternal<'a> {
|
||||
fn null(&mut self) -> Handle<'a, JsNull> {
|
||||
#[cfg(feature = "legacy-runtime")]
|
||||
return JsNull::new();
|
||||
#[cfg(feature = "napi-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
return JsNull::new(self);
|
||||
}
|
||||
|
||||
@ -450,7 +287,7 @@ pub trait Context<'a>: ContextInternal<'a> {
|
||||
fn undefined(&mut self) -> Handle<'a, JsUndefined> {
|
||||
#[cfg(feature = "legacy-runtime")]
|
||||
return JsUndefined::new();
|
||||
#[cfg(feature = "napi-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
return JsUndefined::new(self);
|
||||
}
|
||||
|
||||
@ -474,21 +311,17 @@ 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<T: Value, U>(&mut self, v: Handle<T>) -> NeonResult<U> {
|
||||
fn throw<'b, T: Value, U>(&mut self, v: Handle<'b, T>) -> NeonResult<U> {
|
||||
unsafe {
|
||||
neon_runtime::error::throw(self.env().to_raw(), v.to_raw());
|
||||
}
|
||||
@ -528,11 +361,11 @@ pub trait Context<'a>: ContextInternal<'a> {
|
||||
self.throw(err)
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
/// Convenience method for wrapping a value in a `JsBox`.
|
||||
///
|
||||
/// # Example:
|
||||
///
|
||||
///
|
||||
/// ```rust
|
||||
/// # use neon::prelude::*;
|
||||
/// struct Point(usize, usize);
|
||||
@ -549,54 +382,50 @@ pub trait Context<'a>: ContextInternal<'a> {
|
||||
JsBox::new(self, v)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
/// Creates an unbounded queue of events to be executed on a JavaScript thread
|
||||
fn queue(&mut self) -> EventQueue {
|
||||
fn event_queue(&mut self) -> EventQueue {
|
||||
EventQueue::new(self)
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
fn promise(&mut self) -> (Handle<'a, JsPromise>, Deferred) {
|
||||
JsPromise::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// An execution context of module initialization.
|
||||
/// A view of the JS engine in the context of top-level initialization of a Neon module.
|
||||
pub struct ModuleContext<'a> {
|
||||
scope: Scope<'a, raw::HandleScope>,
|
||||
exports: Handle<'a, JsObject>,
|
||||
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)?;
|
||||
@ -622,19 +451,18 @@ impl<'a> ContextInternal<'a> for ModuleContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Context<'a> for ModuleContext<'a> {}
|
||||
impl<'a> Context<'a> for ModuleContext<'a> { }
|
||||
|
||||
/// An execution context of a scope created by [`Context::execute_scoped()`](Context::execute_scoped).
|
||||
/// A view of the JS engine in the context of a scoped computation started by `Context::execute_scoped()`.
|
||||
pub struct ExecuteContext<'a> {
|
||||
scope: Scope<'a, raw::HandleScope>,
|
||||
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 })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -644,25 +472,22 @@ impl<'a> ContextInternal<'a> for ExecuteContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Context<'a> for ExecuteContext<'a> {}
|
||||
impl<'a> Context<'a> for ExecuteContext<'a> { }
|
||||
|
||||
/// An execution context of a scope created by [`Context::compute_scoped()`](Context::compute_scoped).
|
||||
/// A view of the JS engine in the context of a scoped computation started by `Context::compute_scoped()`.
|
||||
pub struct ComputeContext<'a, 'outer> {
|
||||
scope: Scope<'a, raw::EscapableHandleScope>,
|
||||
phantom_inner: PhantomData<&'a ()>,
|
||||
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
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -674,67 +499,54 @@ 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> { }
|
||||
|
||||
/// An execution context of a function call.
|
||||
///
|
||||
/// A view of the JS engine in the context of a function call.
|
||||
///
|
||||
/// The type parameter `T` is the type of the `this`-binding.
|
||||
pub struct CallContext<'a, T: This> {
|
||||
scope: Scope<'a, raw::HandleScope>,
|
||||
info: &'a CallbackInfo<'a>,
|
||||
#[cfg(feature = "napi-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
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 with `new`.
|
||||
/// Indicates whether the function was called via the JavaScript `[[Call]]` or `[[Construct]]` semantics.
|
||||
pub fn kind(&self) -> CallKind {
|
||||
#[cfg(feature = "legacy-runtime")]
|
||||
let kind = self.info.kind();
|
||||
|
||||
#[cfg(feature = "napi-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
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-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
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)
|
||||
}
|
||||
|
||||
/// Indicates if no arguments were passed to the function.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
pub fn len(&self) -> i32 { self.info.len(self) }
|
||||
|
||||
/// 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-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
{
|
||||
let local = if let Some(arguments) = &self.arguments {
|
||||
arguments.get(i as usize).cloned()
|
||||
@ -754,7 +566,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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -762,7 +574,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-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
let this = T::as_this(self.env(), self.info.this(self));
|
||||
|
||||
Handle::new_internal(this)
|
||||
@ -775,31 +587,34 @@ 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`](CallContext) with `this`-type [`JsObject`](crate::types::JsObject).
|
||||
/// A shorthand for a `CallContext` with `this`-type `JsObject`.
|
||||
pub type FunctionContext<'a> = CallContext<'a, JsObject>;
|
||||
|
||||
/// An alias for [`CallContext`](CallContext), useful for indicating that the function is a method of a class.
|
||||
/// An alias for `CallContext`, useful for indicating that the function is a method of a class.
|
||||
pub type MethodContext<'a, T> = CallContext<'a, T>;
|
||||
|
||||
/// An execution context of a task completion callback.
|
||||
/// A view of the JS engine in the context of a task completion callback.
|
||||
pub struct TaskContext<'a> {
|
||||
/// We use an "inherited HandleScope" here because the C++ `neon::Task::complete`
|
||||
/// method sets up and tears down a `HandleScope` for us.
|
||||
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"))]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
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 }))
|
||||
Scope::with(env, |scope| {
|
||||
f(TaskContext { scope })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -809,27 +624,29 @@ 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-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
pub(crate) struct FinalizeContext<'a> {
|
||||
scope: Scope<'a, raw::HandleScope>,
|
||||
scope: Scope<'a, raw::HandleScope>
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
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-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
impl<'a> ContextInternal<'a> for FinalizeContext<'a> {
|
||||
fn scope_metadata(&self) -> &ScopeMetadata {
|
||||
&self.scope.metadata
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi-1")]
|
||||
impl<'a> Context<'a> for FinalizeContext<'a> {}
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
impl<'a> Context<'a> for FinalizeContext<'a> { }
|
||||
|
||||
@ -1,94 +0,0 @@
|
||||
//! 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);
|
||||
})
|
||||
}
|
||||
@ -1,156 +0,0 @@
|
||||
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 {}
|
||||
219
src/event/mod.rs
219
src/event/mod.rs
@ -1,142 +1,87 @@
|
||||
//! 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/
|
||||
//! Helper to run a callback in the libuv main thread.
|
||||
|
||||
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
|
||||
mod event_queue;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
|
||||
pub use self::event_queue::{EventQueue, EventQueueError};
|
||||
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(not(feature = "napi-1"), feature = "event-handler-api"))]
|
||||
mod event_handler;
|
||||
type EventContext<'a> = crate::context::TaskContext<'a>;
|
||||
|
||||
#[cfg(all(not(feature = "napi-1"), feature = "event-handler-api"))]
|
||||
pub use self::event_handler::EventHandler;
|
||||
struct EventHandlerInner(*mut c_void);
|
||||
|
||||
#[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."
|
||||
);
|
||||
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);
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use crate::types::Value;
|
||||
use types::Value;
|
||||
|
||||
pub trait SuperType<T: Value> {
|
||||
fn upcast_internal(v: T) -> Self;
|
||||
fn upcast_internal(T) -> Self;
|
||||
}
|
||||
|
||||
@ -1,109 +1,47 @@
|
||||
//! References to garbage-collected JavaScript values.
|
||||
//!
|
||||
//! A _handle_ is a safe reference to a JavaScript value that is owned and managed
|
||||
//! by the JavaScript engine's memory management system (the garbage collector).
|
||||
//!
|
||||
//! Neon APIs that accept and return JavaScript values never use raw pointer types
|
||||
//! ([`*T`](pointer)) or reference types ([`&T`](reference)). Instead they use the
|
||||
//! special Neon type [`Handle`](Handle), which encapsulates a JavaScript
|
||||
//! [`Value`](crate::types::Value) and ensures that Rust only maintains access to
|
||||
//! the value while it is guaranteed to be valid.
|
||||
//!
|
||||
//! ## Working with Handles
|
||||
//!
|
||||
//! The `Handle<T>` type automatically dereferences to `T` (via the standard
|
||||
//! [`Deref`](std::ops::Deref) trait), so you can call `T`'s methods on a value of
|
||||
//! type `Handle<T>`. For example, we can call
|
||||
//! [`JsNumber::value()`](crate::types::JsNumber::value) on a `Handle<JsNumber>`:
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "napi-1")] {
|
||||
//! # use neon::prelude::*;
|
||||
//! # fn run(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
||||
//! let n: Handle<JsNumber> = cx.argument(0)?;
|
||||
//! let v = n.value(&mut cx); // JsNumber::value()
|
||||
//! # Ok(cx.undefined())
|
||||
//! # }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! This Neon function takes an object as its argument, extracts two properties,
|
||||
//! `width` and `height`, and multiplies them together as numbers. Each JavaScript
|
||||
//! value in the calculation is stored locally in a `Handle`.
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "napi-1")] {
|
||||
//! # use neon::prelude::*;
|
||||
//! fn area(mut cx: FunctionContext) -> JsResult<JsNumber> {
|
||||
//! let rect: Handle<JsObject> = cx.argument(0)?;
|
||||
//!
|
||||
//! let width: Handle<JsNumber> = rect
|
||||
//! .get(&mut cx, "width")?
|
||||
//! .downcast_or_throw(&mut cx)?;
|
||||
//! let w: f64 = width.value(&mut cx);
|
||||
//!
|
||||
//! let height: Handle<JsNumber> = rect
|
||||
//! .get(&mut cx, "height")?
|
||||
//! .downcast_or_throw(&mut cx)?;
|
||||
//! let h: f64 = height.value(&mut cx);
|
||||
//!
|
||||
//! Ok(cx.number(w * h))
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
//! Safe _handles_ to managed JavaScript memory.
|
||||
|
||||
pub(crate) mod internal;
|
||||
|
||||
#[cfg(feature = "napi-1")]
|
||||
pub(crate) mod root;
|
||||
|
||||
#[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 std::error::Error;
|
||||
use std::fmt::{self, Debug, Display};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::error::Error;
|
||||
use std::fmt::{self, Debug, Display};
|
||||
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;
|
||||
|
||||
/// The trait of data owned by the JavaScript engine and that can only be accessed via handles.
|
||||
/// The trait of data that is managed by the JS garbage collector and can only be accessed via handles.
|
||||
pub trait Managed: Copy {
|
||||
fn to_raw(self) -> raw::Local;
|
||||
|
||||
fn from_raw(env: Env, h: raw::Local) -> Self;
|
||||
}
|
||||
|
||||
/// A handle to a JavaScript value that is owned by the JavaScript engine.
|
||||
/// A safely rooted _handle_ to a JS value in memory that is managed by the garbage collector.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
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()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "legacy-runtime")]
|
||||
impl<'a, T: Managed + 'a> Eq for Handle<'a, T> {}
|
||||
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,
|
||||
phantom: PhantomData,
|
||||
value: value,
|
||||
phantom: PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -138,21 +76,22 @@ impl<F: Value, T: Value> Display for DowncastError<F, T> {
|
||||
|
||||
impl<F: Value, T: Value> Error for DowncastError<F, T> {}
|
||||
|
||||
/// The result of a call to [`Handle::downcast()`](Handle::downcast).
|
||||
/// The result of a call to `Handle::downcast()`.
|
||||
pub type DowncastResult<'a, F, T> = Result<Handle<'a, T>, DowncastError<F, T>>;
|
||||
|
||||
impl<'a, F: Value, T: Value> JsResultExt<'a, T> for DowncastResult<'a, F, T> {
|
||||
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))
|
||||
@ -160,9 +99,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> {
|
||||
@ -177,11 +116,11 @@ impl<'a, T: Value> Handle<'a, T> {
|
||||
U::is_typeof(Env::current(), self.value)
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi-1")]
|
||||
#[cfg(feature = "napi-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> {
|
||||
@ -204,11 +143,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-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
/// 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`
|
||||
@ -216,7 +155,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())
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,7 +167,7 @@ impl<'a, T: Value> Handle<'a, T> {
|
||||
self.downcast().or_throw(cx)
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
/// 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)`.
|
||||
@ -236,27 +175,17 @@ 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(&self) -> &T {
|
||||
fn deref<'b>(&'b self) -> &'b T {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Managed> DerefMut for Handle<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
fn deref_mut<'b>(&'b mut self) -> &'b mut T {
|
||||
&mut self.value
|
||||
}
|
||||
}
|
||||
|
||||
200
src/lib.rs
200
src/lib.rs
@ -1,101 +1,30 @@
|
||||
//! The [Neon][neon] crate provides bindings for writing [Node.js addons][addons]
|
||||
//! (i.e., dynamically-loaded binary modules) with a safe and fast Rust API.
|
||||
//!
|
||||
//! ## Getting Started
|
||||
//!
|
||||
//! You can conveniently bootstrap a new Neon project with the Neon project
|
||||
//! generator. You don't need to install anything special on your machine as
|
||||
//! long as you have a [supported version of Node and Rust][supported] on
|
||||
//! your system.
|
||||
//!
|
||||
//! To start a new project, open a terminal in the directory where you would
|
||||
//! like to place the project, and run at the command prompt:
|
||||
//!
|
||||
//! ```text
|
||||
//! % npm init neon my-project
|
||||
//! ... answer the user prompts ...
|
||||
//! ✨ Created Neon project `my-project`. Happy 🦀 hacking! ✨
|
||||
//! ```
|
||||
//!
|
||||
//! where `my-project` can be any name you like for the project. This will
|
||||
//! run the Neon project generator, prompting you with a few questions and
|
||||
//! placing a simple but working Neon project in a subdirectory called
|
||||
//! `my-project` (or whatever name you chose).
|
||||
//!
|
||||
//! You can then install and build the project by changing into the project
|
||||
//! directory and running the standard Node installation command:
|
||||
//!
|
||||
//! ```text
|
||||
//! % cd my-project
|
||||
//! % npm install
|
||||
//! % node
|
||||
//! > require(".").hello()
|
||||
//! 'hello node'
|
||||
//! ```
|
||||
//!
|
||||
//! You can look in the project's generated `README.md` for more details on
|
||||
//! the project structure.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! The generated `src/lib.rs` contains a function annotated with the
|
||||
//! [`#[neon::main]`](main) attribute, marking it as the module's main entry
|
||||
//! point to be executed when the module is loaded. This function can have
|
||||
//! any name but is conventionally called `main`:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # #[cfg(feature = "neon-macros")] {
|
||||
//! # use neon::prelude::*;
|
||||
//! #
|
||||
//! # fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
|
||||
//! # Ok(cx.string("hello node"))
|
||||
//! # }
|
||||
//! #
|
||||
//! #[neon::main]
|
||||
//! fn main(mut cx: ModuleContext) -> NeonResult<()> {
|
||||
//! cx.export_function("hello", hello)?;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The example code generated by `npm init neon` exports a single
|
||||
//! function via [`ModuleContext::export_function`](context::ModuleContext::export_function).
|
||||
//! The `hello` function is defined just above `main` in `src/lib.rs`:
|
||||
//!
|
||||
//! ```
|
||||
//! # use neon::prelude::*;
|
||||
//! #
|
||||
//! fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
|
||||
//! Ok(cx.string("hello node"))
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The `hello` function takes a [`FunctionContext`](context::FunctionContext) and
|
||||
//! returns a JavaScript string. Because all Neon functions can potentially throw a
|
||||
//! JavaScript exception, the return type is wrapped in a [`JsResult`](result::JsResult).
|
||||
//!
|
||||
//! [neon]: https://www.neon-bindings.com/
|
||||
//! [addons]: https://nodejs.org/api/addons.html
|
||||
//! [supported]: https://github.com/neon-bindings/neon#platform-support
|
||||
//! The [Neon](https://www.neon-bindings.com/) crate provides bindings for writing Node.js plugins with a safe and fast Rust API.
|
||||
|
||||
extern crate 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 borrow;
|
||||
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;
|
||||
pub mod object;
|
||||
pub mod borrow;
|
||||
pub mod result;
|
||||
pub mod task;
|
||||
#[cfg(feature = "event-handler-api")]
|
||||
pub mod event;
|
||||
pub mod meta;
|
||||
pub mod prelude;
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
pub mod sync;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod macro_internal;
|
||||
@ -103,14 +32,27 @@ pub mod macro_internal;
|
||||
#[cfg(feature = "proc-macros")]
|
||||
pub use neon_macros::*;
|
||||
|
||||
#[cfg(feature = "napi-6")]
|
||||
mod lifecycle;
|
||||
#[cfg(all(feature = "legacy-runtime", feature = "napi-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(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)]
|
||||
#[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(())
|
||||
/// });
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! register_module {
|
||||
($module:pat, $init:block) => {
|
||||
@ -151,19 +93,13 @@ macro_rules! register_module {
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "n-api")] {
|
||||
/// # use neon::prelude::*;
|
||||
/// # fn foo(mut cx: FunctionContext) -> JsResult<JsUndefined> { Ok(cx.undefined()) }
|
||||
/// # fn bar(mut cx: FunctionContext) -> JsResult<JsUndefined> { Ok(cx.undefined()) }
|
||||
/// # fn baz(mut cx: FunctionContext) -> JsResult<JsUndefined> { Ok(cx.undefined()) }
|
||||
/// ```rust,ignore
|
||||
/// register_module!(mut cx, {
|
||||
/// cx.export_function("foo", foo)?;
|
||||
/// cx.export_function("bar", bar)?;
|
||||
/// cx.export_function("baz", baz)?;
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! register_module {
|
||||
@ -237,7 +173,6 @@ macro_rules! register_module {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "legacy-runtime")]
|
||||
#[doc(hidden)]
|
||||
#[macro_export(local_inner_macros)]
|
||||
macro_rules! class_definition {
|
||||
@ -327,7 +262,6 @@ macro_rules! class_definition {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "legacy-runtime")]
|
||||
#[doc(hidden)]
|
||||
#[macro_export(local_inner_macros)]
|
||||
macro_rules! impl_managed {
|
||||
@ -345,15 +279,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 {
|
||||
@ -367,7 +301,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-1")]
|
||||
/// # #[cfg(feature = "napi-runtime")]
|
||||
/// # let greeting = cx.argument::<JsString>(0)?.to_string(&mut cx)?.value(&mut cx);
|
||||
/// Ok(Greeter {
|
||||
/// greeting: greeting
|
||||
@ -377,7 +311,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-1")]
|
||||
/// # #[cfg(feature = "napi-runtime")]
|
||||
/// # let name = cx.argument::<JsString>(0)?.to_string(&mut cx)?.value(&mut cx);
|
||||
/// let this = cx.this();
|
||||
/// let msg = {
|
||||
@ -430,7 +364,6 @@ macro_rules! declare_types {
|
||||
{ } => { };
|
||||
}
|
||||
|
||||
#[cfg(feature = "legacy-runtime")]
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! neon_stringify {
|
||||
@ -441,11 +374,11 @@ macro_rules! neon_stringify {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use lazy_static::lazy_static;
|
||||
use semver::Version;
|
||||
extern crate rustversion;
|
||||
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! {
|
||||
@ -472,14 +405,11 @@ mod tests {
|
||||
eprintln!("Running Neon test: {} {} {}", shell, command_flag, cmd);
|
||||
|
||||
assert!(Command::new(&shell)
|
||||
.current_dir(dir)
|
||||
.args(&[&command_flag, cmd])
|
||||
.status()
|
||||
.unwrap_or_else(|_| panic!(
|
||||
"failed to execute test command: {} {} {}",
|
||||
shell, command_flag, cmd
|
||||
))
|
||||
.success());
|
||||
.current_dir(dir)
|
||||
.args(&[&command_flag, cmd])
|
||||
.status()
|
||||
.expect(&format!("failed to execute test command: {} {} {}", shell, command_flag, cmd))
|
||||
.success());
|
||||
}
|
||||
|
||||
fn cli_setup() {
|
||||
@ -508,7 +438,7 @@ mod tests {
|
||||
|
||||
log("static_test");
|
||||
|
||||
run("cargo test", &project_root().join("test").join("static"));
|
||||
run("cargo test --release", &project_root().join("test").join("static"));
|
||||
}
|
||||
|
||||
// Only run the static tests in Beta. This will catch changes to error reporting
|
||||
@ -517,30 +447,22 @@ 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() {
|
||||
@ -562,7 +484,7 @@ mod tests {
|
||||
log("dynamic_cargo_test");
|
||||
|
||||
let test_dynamic_cargo = project_root().join("test").join("dynamic").join("native");
|
||||
run("cargo test", &test_dynamic_cargo);
|
||||
run("cargo test --release", &test_dynamic_cargo);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -1,76 +0,0 @@
|
||||
//! # Environment life cycle APIs
|
||||
//!
|
||||
//! These APIs map to the life cycle of a specific "Agent" or self-contained
|
||||
//! environment. If a Neon module is loaded multiple times (Web Workers, worker
|
||||
//! threads), these API will be handle data associated with a specific instance.
|
||||
//!
|
||||
//! See the [N-API Lifecycle][npai-docs] documentation for more details.
|
||||
//!
|
||||
//! [napi-docs]: https://nodejs.org/api/n-api.html#n_api_environment_life_cycle_apis
|
||||
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
||||
use neon_runtime::raw::Env;
|
||||
use neon_runtime::reference;
|
||||
use neon_runtime::tsfn::ThreadsafeFunction;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::handle::root::NapiRef;
|
||||
|
||||
/// `InstanceData` holds Neon data associated with a particular instance of a
|
||||
/// native module. If a module is loaded multiple times (e.g., worker threads), this
|
||||
/// data will be unique per instance.
|
||||
pub(crate) struct InstanceData {
|
||||
/// Used to free `Root` in the same JavaScript environment that created it
|
||||
///
|
||||
/// _Design Note_: An `Arc` ensures the `ThreadsafeFunction` outlives the unloading
|
||||
/// of a module. Since it is unlikely that modules will be re-loaded frequently, this
|
||||
/// could be replaced with a leaked `&'static ThreadsafeFunction<NapiRef>`. However,
|
||||
/// given the cost of FFI, this optimization is omitted until the cost of an
|
||||
/// `Arc` is demonstrated as significant.
|
||||
drop_queue: Arc<ThreadsafeFunction<NapiRef>>,
|
||||
}
|
||||
|
||||
fn drop_napi_ref(env: Option<Env>, data: NapiRef) {
|
||||
if let Some(env) = env {
|
||||
unsafe {
|
||||
reference::unreference(env, mem::transmute(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InstanceData {
|
||||
/// Return the data associated with this module instance, lazily initializing if
|
||||
/// necessary.
|
||||
///
|
||||
/// # Safety
|
||||
/// No additional locking (e.g., `Mutex`) is necessary because holding a
|
||||
/// `Context` reference ensures serialized access.
|
||||
pub(crate) fn get<'a, C: Context<'a>>(cx: &mut C) -> &'a mut InstanceData {
|
||||
let env = cx.env().to_raw();
|
||||
let data =
|
||||
unsafe { neon_runtime::lifecycle::get_instance_data::<InstanceData>(env).as_mut() };
|
||||
|
||||
if let Some(data) = data {
|
||||
return data;
|
||||
}
|
||||
|
||||
let drop_queue = unsafe {
|
||||
let mut queue = ThreadsafeFunction::new(env, drop_napi_ref);
|
||||
queue.unref(env);
|
||||
queue
|
||||
};
|
||||
|
||||
let data = InstanceData {
|
||||
drop_queue: Arc::new(drop_queue),
|
||||
};
|
||||
|
||||
unsafe { &mut *neon_runtime::lifecycle::set_instance_data(env, data) }
|
||||
}
|
||||
|
||||
/// Helper to return a reference to the `drop_queue` field of `InstanceData`
|
||||
pub(crate) fn drop_queue<'a, C: Context<'a>>(cx: &mut C) -> Arc<ThreadsafeFunction<NapiRef>> {
|
||||
Arc::clone(&InstanceData::get(cx).drop_queue)
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,9 @@
|
||||
//! 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 crate::object::class::internal::{
|
||||
AllocateCallback, ConstructCallback, ConstructorCallCallback, MethodCallback,
|
||||
};
|
||||
pub use object::class::internal::{AllocateCallback, ConstructCallback, ConstructorCallCallback, MethodCallback};
|
||||
pub use context::internal::{initialize_module, Env};
|
||||
|
||||
// An alias for neon_runtime so macros can refer to it.
|
||||
pub mod runtime {
|
||||
|
||||
18
src/meta.rs
18
src/meta.rs
@ -1,18 +1,18 @@
|
||||
//! Metadata about the Neon version and build.
|
||||
//! Utilities exposing metadata about the Neon version and build.
|
||||
|
||||
use semver::Version;
|
||||
|
||||
/// The Neon version.
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
/// The Neon major version.
|
||||
pub const MAJOR: &str = env!("CARGO_PKG_VERSION_MAJOR");
|
||||
pub const MAJOR: &'static str = env!("CARGO_PKG_VERSION_MAJOR");
|
||||
|
||||
/// The Neon minor version.
|
||||
pub const MINOR: &str = env!("CARGO_PKG_VERSION_MINOR");
|
||||
pub const MINOR: &'static str = env!("CARGO_PKG_VERSION_MINOR");
|
||||
|
||||
/// The Neon patch version.
|
||||
pub const PATCH: &str = env!("CARGO_PKG_VERSION_PATCH");
|
||||
/// The neon patch version.
|
||||
pub const PATCH: &'static 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: &str = "release";
|
||||
pub const BUILD_PROFILE: &'static str = "release";
|
||||
|
||||
/// The current build profile (either `"release"` or `"debug"`).
|
||||
#[cfg(not(neon_profile = "release"))]
|
||||
pub const BUILD_PROFILE: &str = "debug";
|
||||
pub const BUILD_PROFILE: &'static str = "debug";
|
||||
|
||||
@ -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,34 +19,29 @@ 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-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
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 into_ptr(self) -> *mut c_void {
|
||||
fn as_ptr(self) -> *mut c_void {
|
||||
self.0 as *mut c_void
|
||||
}
|
||||
}
|
||||
@ -59,10 +54,7 @@ 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)
|
||||
@ -79,14 +71,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 into_ptr(self) -> *mut c_void {
|
||||
fn as_ptr(self) -> *mut c_void {
|
||||
self.0 as *mut c_void
|
||||
}
|
||||
}
|
||||
@ -101,9 +93,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));
|
||||
p.cast()
|
||||
mem::transmute(p)
|
||||
} else {
|
||||
null_mut()
|
||||
}
|
||||
@ -111,15 +103,13 @@ impl<T: Class> Callback<*mut c_void> for AllocateCallback<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn into_ptr(self) -> *mut c_void {
|
||||
fn as_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 {
|
||||
@ -128,19 +118,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 into_ptr(self) -> *mut c_void {
|
||||
fn as_ptr(self) -> *mut c_void {
|
||||
self.0 as *mut c_void
|
||||
}
|
||||
}
|
||||
@ -148,20 +138,13 @@ 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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -2,33 +2,32 @@
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,18 +46,19 @@ 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: 'a>(name: &'b str, allocate: AllocateCallback<T>) -> Self {
|
||||
Self {
|
||||
name,
|
||||
allocate,
|
||||
pub fn new<'b, U: Class>(name: &'b str, allocate: AllocateCallback<U>) -> ClassDescriptor<'b, U> {
|
||||
ClassDescriptor {
|
||||
name: name,
|
||||
allocate: allocate,
|
||||
call: None,
|
||||
construct: None,
|
||||
methods: Vec::new(),
|
||||
methods: Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,15 +79,16 @@ 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(internals.cast()) };
|
||||
let p: Box<T> = unsafe { Box::from_raw(mem::transmute(internals)) };
|
||||
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 {
|
||||
@ -104,16 +105,15 @@ 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(name: &str, allocate: AllocateCallback<Self>) -> ClassDescriptor<Self> {
|
||||
fn describe<'a>(name: &'a str, allocate: AllocateCallback<Self>) -> ClassDescriptor<'a, Self> {
|
||||
ClassDescriptor::<Self>::new(name, allocate)
|
||||
}
|
||||
}
|
||||
@ -124,23 +124,26 @@ unsafe impl<T: Class> This for T {
|
||||
Self::from_raw(Env::current(), h)
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
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>()).copied()
|
||||
cx.env()
|
||||
.class_map()
|
||||
.get(&TypeId::of::<Self>())
|
||||
.map(|m| m.clone())
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,22 +153,14 @@ 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);
|
||||
@ -175,12 +170,7 @@ 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);
|
||||
}
|
||||
|
||||
@ -189,19 +179,13 @@ 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);
|
||||
@ -211,11 +195,13 @@ 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>()) {
|
||||
@ -224,8 +210,7 @@ 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)
|
||||
};
|
||||
@ -239,12 +224,14 @@ 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;
|
||||
@ -252,7 +239,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, &mut *ptr.cast())
|
||||
Ref::new(lock, mem::transmute(ptr))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -269,7 +256,42 @@ 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, &mut *ptr.cast())
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,18 @@
|
||||
//! 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 {
|
||||
@ -56,27 +54,18 @@ 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)
|
||||
@ -88,25 +77,20 @@ 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-1")]
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
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;
|
||||
|
||||
#[cfg(feature = "napi-6")]
|
||||
use crate::result::JsResult;
|
||||
#[cfg(feature = "napi-6")]
|
||||
use crate::types::JsArray;
|
||||
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};
|
||||
use sync::Root;
|
||||
|
||||
/// A property key in a JavaScript object.
|
||||
pub trait PropertyKey {
|
||||
@ -114,7 +98,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>>(
|
||||
@ -131,7 +115,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)
|
||||
}
|
||||
@ -152,7 +136,7 @@ mod traits {
|
||||
self,
|
||||
cx: &mut C,
|
||||
out: &mut raw::Local,
|
||||
obj: raw::Local,
|
||||
obj: raw::Local
|
||||
) -> bool {
|
||||
let env = cx.env().to_raw();
|
||||
|
||||
@ -177,7 +161,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();
|
||||
@ -201,31 +185,19 @@ 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<'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()) } })
|
||||
}
|
||||
|
||||
#[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())
|
||||
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> {
|
||||
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)
|
||||
@ -241,7 +213,6 @@ 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(env: Env, h: raw::Local) -> Self;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,30 +1,16 @@
|
||||
//! Convenience module for the most common Neon imports.
|
||||
//! A convenience module that re-exports the most commonly-used Neon APIs.
|
||||
|
||||
pub use crate::borrow::{Borrow, BorrowMut};
|
||||
pub use crate::context::{
|
||||
CallContext, CallKind, ComputeContext, Context, ExecuteContext, FunctionContext, MethodContext,
|
||||
ModuleContext, TaskContext,
|
||||
};
|
||||
#[cfg(feature = "legacy-runtime")]
|
||||
pub use crate::declare_types;
|
||||
#[cfg(all(not(feature = "napi-1"), feature = "event-handler-api"))]
|
||||
pub use crate::event::EventHandler;
|
||||
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
|
||||
pub use crate::event::{EventQueue, EventQueueError};
|
||||
pub use crate::handle::Handle;
|
||||
#[cfg(feature = "legacy-runtime")]
|
||||
pub use crate::object::Class;
|
||||
pub use crate::object::Object;
|
||||
pub use crate::register_module;
|
||||
pub use crate::result::{JsResult, JsResultExt, NeonResult};
|
||||
#[cfg(feature = "legacy-runtime")]
|
||||
pub use crate::task::Task;
|
||||
pub use crate::types::{
|
||||
BinaryData, JsArray, JsArrayBuffer, JsBoolean, JsBuffer, JsError, JsFunction, JsNull, JsNumber,
|
||||
JsObject, JsString, JsUndefined, JsValue, Value,
|
||||
};
|
||||
#[cfg(feature = "napi-1")]
|
||||
pub use crate::{
|
||||
handle::Root,
|
||||
types::boxed::{Finalize, JsBox},
|
||||
};
|
||||
pub use handle::Handle;
|
||||
pub use types::{JsBuffer, JsArrayBuffer, BinaryData, JsError, Value, JsValue, JsUndefined, JsNull, JsBoolean, JsString, JsNumber, JsObject, JsArray, JsFunction, JsPromise};
|
||||
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};
|
||||
#[cfg(feature = "napi-runtime")]
|
||||
pub use sync::{EventQueue, Root};
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
//! 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())
|
||||
})
|
||||
}
|
||||
@ -1,51 +1,16 @@
|
||||
//! Represents JavaScript exceptions as a Rust [`Result`](std::result) type.
|
||||
//!
|
||||
//! Most interactions with the JavaScript engine can throw a JavaScript exception. Neon APIs
|
||||
//! that can throw an exception are called _throwing APIs_ and return the type
|
||||
//! [`NeonResult`](NeonResult) (or its shorthand [`JsResult`](JsResult)).
|
||||
//!
|
||||
//! When a throwing API triggers a JavaScript exception, it returns an [Err](std::result::Result::Err)
|
||||
//! result. This indicates that the thread associated with the [`Context`](crate::context::Context)
|
||||
//! is now throwing, and allows Rust code to perform any cleanup. See the
|
||||
//! [`neon::context`](crate::context) module documentation for more about
|
||||
//! [contexts and exceptions](crate::context#throwing-exceptions).
|
||||
//!
|
||||
//! Typically, Neon code can manage JavaScript exceptions correctly and conveniently by
|
||||
//! using Rust's [question mark (`?`)][question-mark] operator. This ensures that Rust code
|
||||
//! "short-circuits" when an exception is thrown and returns back to JavaScript without
|
||||
//! calling any throwing APIs.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! Neon functions typically use [`JsResult`](JsResult) for their return type. This
|
||||
//! example defines a function that extracts a property called `"message"` from an object,
|
||||
//! throwing an exception if the argument is not of the right type or extracting the property
|
||||
//! fails:
|
||||
//!
|
||||
//! ```
|
||||
//! # use neon::prelude::*;
|
||||
//! fn get_message(mut cx: FunctionContext) -> JsResult<JsValue> {
|
||||
//! let obj: Handle<JsObject> = cx.argument(0)?;
|
||||
//! let prop: Handle<JsValue> = obj.get(&mut cx, "message")?;
|
||||
//! Ok(prop)
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [question-mark]: https://doc.rust-lang.org/edition-guide/rust-2018/error-handling-and-panics/the-question-mark-operator-for-easier-error-handling.html
|
||||
//! Types and traits for working with JavaScript exceptions.
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::handle::Handle;
|
||||
use crate::types::Value;
|
||||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||
use handle::Handle;
|
||||
use types::Value;
|
||||
use context::Context;
|
||||
|
||||
/// 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
|
||||
/// 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.
|
||||
#[derive(Debug)]
|
||||
pub struct Throw;
|
||||
|
||||
@ -55,14 +20,14 @@ impl Display for Throw {
|
||||
}
|
||||
}
|
||||
|
||||
/// The result type for throwing APIs.
|
||||
/// The result of a computation that might send the JS engine into a throwing state.
|
||||
pub type NeonResult<T> = Result<T, Throw>;
|
||||
|
||||
/// Shorthand for a [`NeonResult`](NeonResult) that produces JavaScript values.
|
||||
/// The result of a computation that produces a JavaScript value and might send the JS engine into a throwing state.
|
||||
pub type JsResult<'b, T> = NeonResult<Handle<'b, T>>;
|
||||
|
||||
/// Extension trait for converting Rust [`Result`](std::result::Result) values
|
||||
/// into [`JsResult`](JsResult) values by throwing JavaScript exceptions.
|
||||
/// An extension trait for `Result` values that can be converted into `JsResult` values by throwing a JavaScript
|
||||
/// exception in the error case.
|
||||
pub trait JsResultExt<'a, V: Value> {
|
||||
fn or_throw<'b, C: Context<'b>>(self, cx: &mut C) -> JsResult<'a, V>;
|
||||
}
|
||||
|
||||
120
src/sync/event_queue.rs
Normal file
120
src/sync/event_queue.rs
Normal file
@ -0,0 +1,120 @@
|
||||
use neon_runtime::raw::Env;
|
||||
use neon_runtime::tsfn::ThreadsafeFunction;
|
||||
|
||||
use context::{Context, TaskContext};
|
||||
use result::JsResult;
|
||||
use types::Value;
|
||||
|
||||
type Callback = Box<dyn FnOnce(Env) + Send + 'static>;
|
||||
|
||||
/// Queue for scheduling Rust closures to execute on tge JavaScript main thread
|
||||
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, T>(&self, f: F)
|
||||
where
|
||||
F: FnOnce(TaskContext) -> JsResult<T> + Send + 'static,
|
||||
T: Value,
|
||||
{
|
||||
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, T>(&self, f: F) -> Result<(), EventQueueError>
|
||||
where
|
||||
F: FnOnce(TaskContext) -> JsResult<T> + Send + 'static,
|
||||
T: Value,
|
||||
{
|
||||
let callback = Box::new(move |env| {
|
||||
let env = unsafe { std::mem::transmute(env) };
|
||||
|
||||
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: Env, callback: Callback) {
|
||||
callback(env)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {}
|
||||
186
src/sync/mod.rs
Normal file
186
src/sync/mod.rs
Normal file
@ -0,0 +1,186 @@
|
||||
//! `neon::sync` provides utilities for building multi-threaded Neon modules.
|
||||
//!
|
||||
//! ## `Root<T>`
|
||||
//! In Neon, typically references to JavaScript values are bound by the lifetime
|
||||
//! of a [`Context`](../context/trait.Context.html).
|
||||
//! [`Root<T>`](struct.Root.html) allows Rust to maintain a reference to a
|
||||
//! JavaScript object. The JavaScript object cannot be garbage collected
|
||||
//! until the `Root<T>` is dropped.
|
||||
//!
|
||||
//! The following example demonstrates how `Root` can be used to create a
|
||||
//! globally accesible callback.
|
||||
//!
|
||||
//! ```
|
||||
//! use neon::prelude::*;
|
||||
//! # struct OnceCell<T>(Option<T>);
|
||||
//! # impl<T> OnceCell<T> {
|
||||
//! # const fn new() -> Self { Self(None) }
|
||||
//! # fn set(&self, v: T) -> Result<(), T> { todo!() }
|
||||
//! # fn get(&self) -> Option<T> { todo!() }
|
||||
//! # }
|
||||
//!
|
||||
//! static CALLBACK: OnceCell<Root<JsFunction>> = OnceCell::new();
|
||||
//!
|
||||
//! fn init(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
||||
//! let callback = cx.argument::<JsFunction>(0)?.root(&mut cx);
|
||||
//!
|
||||
//! CALLBACK.set(callback).unwrap();
|
||||
//!
|
||||
//! Ok(cx.undefined())
|
||||
//! }
|
||||
//!
|
||||
//! fn invoke(mut cx: FunctionContext) -> JsResult<JsValue> {
|
||||
//! let this = cx.undefined();
|
||||
//! let arg = cx.argument::<JsString>(0)?;
|
||||
//! let callback = CALLBACK.get().unwrap().to_inner(&mut cx);
|
||||
//!
|
||||
//! callback.call(&mut cx, this, vec![arg])
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Drop Safety
|
||||
//!
|
||||
//! `Root<T>` may only be dropped from the main JavaScript thread. To prevent
|
||||
//! accidental leaks of JavaScript objects, `Root<T>` provides a `Drop`
|
||||
//! implementation that panics if `drop` or `into_inner` is not called. Users
|
||||
//! must be careful to ensure that `Root<T>` are properly disposed.
|
||||
//!
|
||||
//! The [`Finalize`](../prelude/trait.Finalize.html) trait provides an ergonomic
|
||||
//! way to ensure `Root<T>` contained in a [`JsBox`](../types/struct.JsBox.html)
|
||||
//! are dropped safely.
|
||||
//!
|
||||
//! In the following example, the callback will be safely dropped when the
|
||||
//! client is garbage collected.
|
||||
//!
|
||||
//! ```
|
||||
//! use neon::prelude::*;
|
||||
//!
|
||||
//! struct Client {
|
||||
//! callback: Root<JsFunction>,
|
||||
//! }
|
||||
//!
|
||||
//! impl Finalize for Client {
|
||||
//! fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
|
||||
//! self.callback.drop(cx);
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn create_server(mut cx: FunctionContext) -> JsResult<JsBox<Client>> {
|
||||
//! let callback = cx.argument::<JsFunction>(0)?.root(&mut cx);
|
||||
//! let server = cx.boxed(Client { callback });
|
||||
//!
|
||||
//! Ok(server)
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## `EventQueue`
|
||||
//!
|
||||
//! Most Neon functions may only be called from the JavaScript main thread.
|
||||
//! [`EventQueue`](struct.EventQueue.html) provides a method of
|
||||
//! synchronization by allowing any thread to schedule a closure to execute
|
||||
//! on the main thread.
|
||||
//!
|
||||
//! In the previous example using [`Root<T>`](#roott), a persistent reference to a
|
||||
//! JavaScript callback was created. In this example, the reference is held
|
||||
//! while work is performed on another thread and called when it has completed.
|
||||
//!
|
||||
//! ```
|
||||
//! use neon::prelude::*;
|
||||
//! # fn long_running_task() -> f64 { 42.0 }
|
||||
//!
|
||||
//! fn perform_async(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
||||
//! let callback = cx.argument::<JsFunction>(0)?.root(&mut cx);
|
||||
//! let queue = cx.event_queue();
|
||||
//!
|
||||
//! // Spawn a background thread to perform our task
|
||||
//! std::thread::spawn(move || {
|
||||
//! // Perform any number of computations on a separate thread without
|
||||
//! // blocking the JavaScript queue.
|
||||
//! let result = long_running_task();
|
||||
//!
|
||||
//! // Once complete, an event can be scheduled back on the main
|
||||
//! // JavaScript thread.
|
||||
//! queue.send(move |mut cx| {
|
||||
//! // Neon functions can be called and the event queue is blocked
|
||||
//! // inside this closure.
|
||||
//! let callback = callback.into_inner(&mut cx);
|
||||
//! let this = cx.undefined();
|
||||
//! let arg = cx.number(result);
|
||||
//!
|
||||
//! callback.call(&mut cx, this, vec![arg])
|
||||
//! });
|
||||
//! });
|
||||
//!
|
||||
//! // When this function returns, the event queue is no longer blocked
|
||||
//! // but, the thread may still be executing in the background.
|
||||
//! Ok(cx.undefined())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### `Arc<EventQueue>`
|
||||
//!
|
||||
//! `EventQueue` are somewhat expensive to create. Ideally, code will re-use an
|
||||
//! `EventQueue` for scheduling similar events instead of creating a new queue
|
||||
//! for each event. `EventQueue` is `Sync` and can be called from any thread,
|
||||
//! but, is not `Clone`. It can be useful to wrap an `EventQueue` in an
|
||||
//! [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) to ensure it
|
||||
//! is not dropped while it might still be used.
|
||||
//!
|
||||
//! The following example uses `EventQueue` to callback to JavaScript after
|
||||
//! performing a Rust async operation.
|
||||
//!
|
||||
//! ```edition2018
|
||||
//! use neon::prelude::*;
|
||||
//!
|
||||
//! use std::sync::Arc;
|
||||
//! # struct Runtime;
|
||||
//! # impl Runtime { fn spawn<F>(&self, _: F) {} }
|
||||
//! # impl Client { fn new<T>(_: T) -> Self { todo!() } }
|
||||
//! # impl Finalize for Client {}
|
||||
//!
|
||||
//! struct Client {
|
||||
//! runtime: Runtime,
|
||||
//! queue: Arc<EventQueue>,
|
||||
//! }
|
||||
//!
|
||||
//! async fn get_user_name(id: f64) -> String {
|
||||
//! String::from("Username")
|
||||
//! }
|
||||
//!
|
||||
//! fn create_client(mut cx: FunctionContext) -> JsResult<JsBox<Client>> {
|
||||
//! let queue = cx.event_queue();
|
||||
//! let client = Client::new(queue);
|
||||
//!
|
||||
//! Ok(cx.boxed(client))
|
||||
//! }
|
||||
//!
|
||||
//! fn get_user_name_js(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
||||
//! let client = cx.argument::<JsBox<Client>>(0)?;
|
||||
//! let user_id = cx.argument::<JsNumber>(1)?.value(&mut cx);
|
||||
//! let callback = cx.argument::<JsFunction>(2)?.root(&mut cx);
|
||||
//! let queue = Arc::clone(&client.queue);
|
||||
//!
|
||||
//! client.runtime.spawn(async move {
|
||||
//! let username = get_user_name(user_id).await;
|
||||
//!
|
||||
//! queue.send(move |mut cx| {
|
||||
//! let this = cx.undefined();
|
||||
//! let callback = callback.into_inner(&mut cx);
|
||||
//! let args = vec![
|
||||
//! cx.null().upcast::<JsValue>(),
|
||||
//! cx.string(username).upcast(),
|
||||
//! ];
|
||||
//!
|
||||
//! callback.call(&mut cx, this, args)
|
||||
//! });
|
||||
//! });
|
||||
//!
|
||||
//! Ok(cx.undefined())
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
mod event_queue;
|
||||
mod root;
|
||||
|
||||
pub use self::event_queue::EventQueue;
|
||||
pub use self::root::Root;
|
||||
@ -1,41 +1,36 @@
|
||||
use std::ffi::c_void;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::sync::Arc;
|
||||
|
||||
use neon_runtime::reference;
|
||||
#[cfg(feature = "napi-6")]
|
||||
use neon_runtime::tsfn::ThreadsafeFunction;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::handle::Handle;
|
||||
#[cfg(feature = "napi-6")]
|
||||
use crate::lifecycle::InstanceData;
|
||||
use crate::object::Object;
|
||||
use crate::types::boxed::Finalize;
|
||||
use context::Context;
|
||||
use handle::Handle;
|
||||
use object::Object;
|
||||
use types::boxed::Finalize;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct NapiRef(*mut c_void);
|
||||
struct NapiRef(*mut c_void);
|
||||
|
||||
// # Safety
|
||||
// `NapiRef` are reference counted types that allow references to JavaScript objects
|
||||
// to outlive a `Context` (`napi_env`). Since access is serialized by obtaining a
|
||||
// `Context`, they are both `Send` and `Sync`.
|
||||
// https://nodejs.org/api/n-api.html#n_api_references_to_objects_with_a_lifespan_longer_than_that_of_the_native_method
|
||||
unsafe impl Send for NapiRef {}
|
||||
unsafe impl Sync for NapiRef {}
|
||||
// Provides unsafe `Send` and `Sync` on a `PhantomData`
|
||||
struct UnsafePhantom<T>(PhantomData<T>);
|
||||
|
||||
/// 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.
|
||||
impl<T> UnsafePhantom<T> {
|
||||
// Safety: Caller must ensure `T` is `Send` and `Sync`
|
||||
unsafe fn new() -> Self {
|
||||
UnsafePhantom(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
/// `Root<T>` holds a reference to a `JavaScript` object and prevents it from
|
||||
/// being garbage collected. `Root<T>` may be sent across threads, but the
|
||||
/// referenced objected may only be accessed on the JavaScript thread that
|
||||
/// created it.
|
||||
#[repr(transparent)]
|
||||
pub struct Root<T> {
|
||||
internal: NapiRef,
|
||||
#[cfg(feature = "napi-6")]
|
||||
drop_queue: Arc<ThreadsafeFunction<NapiRef>>,
|
||||
_phantom: PhantomData<T>,
|
||||
_phantom: UnsafePhantom<T>,
|
||||
}
|
||||
|
||||
impl<T> std::fmt::Debug for Root<T> {
|
||||
@ -44,31 +39,32 @@ impl<T> std::fmt::Debug for Root<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> {}
|
||||
// `napi_ref` are intended to be `Send` and `Sync`.
|
||||
unsafe impl Send for NapiRef {}
|
||||
unsafe impl Sync for NapiRef {}
|
||||
|
||||
// Treat `T` as though it were `Send` and `Sync`
|
||||
// Safety: Caller must ensure `T` is only used on the main thread
|
||||
unsafe impl<T> Send for UnsafePhantom<T> {}
|
||||
unsafe impl<T> Sync for UnsafePhantom<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
|
||||
/// The caller _must_ ensure `Root::into_inner` or `Root::drop` is called
|
||||
/// to properly dispose of the `Root<T>`. If the value is dropped without
|
||||
/// calling one of these methods:
|
||||
/// * N-API < 6, Neon will `panic` to notify of the leak
|
||||
/// * N-API >= 6, Neon will drop from a global queue at a runtime cost
|
||||
/// calling one of these methods, it will *panic*.
|
||||
pub fn new<'a, C: Context<'a>>(cx: &mut C, value: &T) -> Self {
|
||||
let env = cx.env().to_raw();
|
||||
let internal = unsafe { reference::new(env, value.to_raw()) };
|
||||
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,
|
||||
_phantom: unsafe { UnsafePhantom::new() },
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,9 +90,7 @@ impl<T: Object> Root<T> {
|
||||
|
||||
Self {
|
||||
internal: self.internal.clone(),
|
||||
#[cfg(feature = "napi-6")]
|
||||
drop_queue: Arc::clone(&self.drop_queue),
|
||||
_phantom: PhantomData,
|
||||
_phantom: unsafe { UnsafePhantom::new() },
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +110,9 @@ impl<T: Object> Root<T> {
|
||||
let env = cx.env();
|
||||
let internal = ManuallyDrop::new(self).internal.0 as *mut _;
|
||||
|
||||
let local = unsafe { reference::get(env.to_raw(), internal) };
|
||||
let local = unsafe {
|
||||
reference::get(env.to_raw(), internal)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
reference::unreference(env.to_raw(), internal);
|
||||
@ -130,7 +126,9 @@ impl<T: Object> Root<T> {
|
||||
/// 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 _) };
|
||||
let local = unsafe {
|
||||
reference::get(env.to_raw(), self.internal.0 as *mut _)
|
||||
};
|
||||
|
||||
Handle::new_internal(T::from_raw(env, local))
|
||||
}
|
||||
@ -138,33 +136,21 @@ impl<T: Object> Root<T> {
|
||||
|
||||
// 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> {
|
||||
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;
|
||||
} else {
|
||||
panic!("Must call `into_inner` or `drop` on `Root` \
|
||||
https://docs.rs/neon/latest/neon/sync/index.html#drop-safety");
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@ -1,16 +1,17 @@
|
||||
//! Utilities for scheduling tasks to be executed by the Node.js runtime
|
||||
//! Asynchronous background _tasks_ that run in the Node thread pool.
|
||||
|
||||
use std::marker::{Send, Sized};
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use crate::context::TaskContext;
|
||||
use crate::handle::{Handle, Managed};
|
||||
use crate::result::JsResult;
|
||||
use crate::types::{JsFunction, Value};
|
||||
use types::{Value, JsFunction};
|
||||
use result::JsResult;
|
||||
use handle::{Handle, Managed};
|
||||
use context::TaskContext;
|
||||
use neon_runtime;
|
||||
use neon_runtime::raw;
|
||||
|
||||
/// A Rust task that can be executed in the background on the Node thread pool.
|
||||
/// A Rust task that can be executed in a background thread.
|
||||
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;
|
||||
@ -25,11 +26,7 @@ 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(
|
||||
self,
|
||||
cx: TaskContext,
|
||||
result: Result<Self::Output, Self::Error>,
|
||||
) -> JsResult<Self::JsEvent>;
|
||||
fn complete<'a>(self, cx: TaskContext<'a>, result: Result<Self::Output, Self::Error>) -> JsResult<Self::JsEvent>;
|
||||
|
||||
/// Schedule a task to be executed on a background thread.
|
||||
///
|
||||
@ -43,30 +40,24 @@ 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(
|
||||
self_raw.cast(),
|
||||
perform_task::<Self>,
|
||||
complete_task::<Self>,
|
||||
callback_raw,
|
||||
);
|
||||
neon_runtime::task::schedule(mem::transmute(self_raw),
|
||||
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(task.cast());
|
||||
let task: Box<T> = Box::from_raw(mem::transmute(task));
|
||||
let result = task.perform();
|
||||
Box::into_raw(task);
|
||||
Box::into_raw(Box::new(result)).cast()
|
||||
mem::transmute(Box::into_raw(Box::new(result)))
|
||||
}
|
||||
|
||||
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());
|
||||
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));
|
||||
TaskContext::with(|cx| {
|
||||
if let Ok(result) = task.complete(cx, result) {
|
||||
*out = result.to_raw();
|
||||
|
||||
@ -1,21 +1,19 @@
|
||||
//! 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)]
|
||||
@ -23,62 +21,37 @@ use std::slice;
|
||||
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> {
|
||||
let env = cx.env();
|
||||
build(env, |out| {
|
||||
neon_runtime::buffer::uninitialized(env.to_raw(), out, size)
|
||||
})
|
||||
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) })
|
||||
}
|
||||
|
||||
#[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)]
|
||||
@ -86,50 +59,31 @@ 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)]
|
||||
@ -137,7 +91,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> {
|
||||
@ -151,24 +105,25 @@ 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> {
|
||||
@ -182,19 +137,15 @@ impl<'a> BinaryData<'a> {
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn as_slice<T: BinaryViewType>(self) -> &'a [T] {
|
||||
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) }
|
||||
}
|
||||
let base = unsafe { mem::transmute(self.base) };
|
||||
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> {
|
||||
@ -208,24 +159,15 @@ impl<'a> BinaryData<'a> {
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn as_mut_slice<T: BinaryViewType>(self) -> &'a mut [T] {
|
||||
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) }
|
||||
}
|
||||
let base = unsafe { mem::transmute(self.base) };
|
||||
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 {
|
||||
@ -237,12 +179,13 @@ 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,21 +198,19 @@ 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,15 +223,13 @@ 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,23 +242,18 @@ 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
use std::any::{self, Any};
|
||||
use std::ops::Deref;
|
||||
|
||||
use neon_runtime::external;
|
||||
use neon_runtime::raw;
|
||||
use neon_runtime::external;
|
||||
|
||||
use crate::context::internal::Env;
|
||||
use crate::context::{Context, FinalizeContext};
|
||||
use crate::handle::{Handle, Managed};
|
||||
use crate::object::Object;
|
||||
use crate::context::internal::Env;
|
||||
use crate::handle::{Managed, Handle};
|
||||
use crate::types::internal::ValueInternal;
|
||||
use crate::types::Value;
|
||||
|
||||
@ -90,38 +89,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> {
|
||||
@ -147,7 +146,8 @@ impl<T: Send + 'static> std::fmt::Debug for JsBox<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`
|
||||
@ -160,11 +160,9 @@ 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 {
|
||||
@ -177,7 +175,10 @@ impl<T: Send + 'static> Managed for JsBox<T> {
|
||||
.downcast_ref()
|
||||
.expect("Failed to downcast Any");
|
||||
|
||||
Self { local, raw_data }
|
||||
Self {
|
||||
local,
|
||||
raw_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,7 +199,10 @@ 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,15 +239,23 @@ 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -358,7 +370,7 @@ impl<T: Finalize> Finalize for Vec<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// Smart pointers and other wrappers
|
||||
// Smart Pointers
|
||||
|
||||
impl<T: Finalize> Finalize for std::boxed::Box<T> {
|
||||
fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
|
||||
@ -366,14 +378,6 @@ 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) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user