refactor(napi): async and lifetime span

This commit is contained in:
LongYinan 2020-05-05 23:13:23 +08:00 committed by LongYinan
parent 25296d8d84
commit 1a280aa30f
No known key found for this signature in database
GPG key ID: A3FFE134A3E20881
24 changed files with 1645 additions and 462 deletions

2
.prettierignore Normal file
View file

@ -0,0 +1,2 @@
target
node_modules

View file

@ -27,6 +27,7 @@ This library depends on N-API and requires Node 8.9 or later. It is still pretty
One nice feature is that this crate allows you to build add-ons purely with the Rust toolchain and without involving `node-gyp`. One nice feature is that this crate allows you to build add-ons purely with the Rust toolchain and without involving `node-gyp`.
## Taste ## Taste
```rust ```rust
#[js_function(1)] // ------> arguments length, omit for zero #[js_function(1)] // ------> arguments length, omit for zero
fn fibonacci(ctx: CallContext) -> Result<Value<Number>> { fn fibonacci(ctx: CallContext) -> Result<Value<Number>> {
@ -62,7 +63,7 @@ napi-build = "0.1"
And create `build.rs` in your own project: And create `build.rs` in your own project:
```rs ```rust
// build.rs // build.rs
extern crate napi_build; extern crate napi_build;
@ -78,7 +79,6 @@ See the included [test_module](./test_module) for an example add-on.
Run `cargo build` to produce the `Dynamic lib` file. And install the `napi-rs` to help you copy `Dynamic lib` file to `.node` file in case you can `require` it in your program. Run `cargo build` to produce the `Dynamic lib` file. And install the `napi-rs` to help you copy `Dynamic lib` file to `.node` file in case you can `require` it in your program.
```json ```json
{ {
"package": "your pkg", "package": "your pkg",
"dependencies": { "dependencies": {
@ -128,7 +128,7 @@ npm test
### Create JavaScript values ### Create JavaScript values
| NAPI | NAPI Version | Minimal Node version | Status | | NAPI | NAPI Version | Minimal Node version | Status |
| ------------------------------------------------------------ | ------------ | -------------------- | ------ | | ------------------------------------------------------------------------------------------------------------ | ------------ | -------------------- | ------ |
| [Enum types](https://nodejs.org/api/n-api.html#n_api_enum_types) | 6 | v13.7.0 | ⛔️ | | [Enum types](https://nodejs.org/api/n-api.html#n_api_enum_types) | 6 | v13.7.0 | ⛔️ |
| [napi_create_array](https://nodejs.org/api/n-api.html#n_api_napi_create_array) | 1 | v8.0.0 | ✅ | | [napi_create_array](https://nodejs.org/api/n-api.html#n_api_napi_create_array) | 1 | v8.0.0 | ✅ |
| [napi_create_array_with_length](https://nodejs.org/api/n-api.html#n_api_napi_create_array_with_length) | 1 | v8.0.0 | ✅ | | [napi_create_array_with_length](https://nodejs.org/api/n-api.html#n_api_napi_create_array_with_length) | 1 | v8.0.0 | ✅ |
@ -157,48 +157,48 @@ npm test
### [Functions to convert from N-API to C types](https://nodejs.org/api/n-api.html#n_api_functions_to_convert_from_n_api_to_c_types) ### [Functions to convert from N-API to C types](https://nodejs.org/api/n-api.html#n_api_functions_to_convert_from_n_api_to_c_types)
| NAPI | NAPI Version | Minimal Node Version | Status | | NAPI | NAPI Version | Minimal Node Version | Status |
| ---- | ------------ | -------------------- | ------ | | ---------------------------------------------------------------------------------------------------- | ------------ | -------------------- | ------ |
|[napi_get_array_length](https://nodejs.org/api/n-api.html#n_api_napi_get_array_length)| 1 | v8.0.0 | ✅ | | [napi_get_array_length](https://nodejs.org/api/n-api.html#n_api_napi_get_array_length) | 1 | v8.0.0 | ✅ |
|[napi_get_arraybuffer_info](https://nodejs.org/api/n-api.html#n_api_napi_get_arraybuffer_info)| 1 | v8.0.0 | ✅ | | [napi_get_arraybuffer_info](https://nodejs.org/api/n-api.html#n_api_napi_get_arraybuffer_info) | 1 | v8.0.0 | ✅ |
|[napi_get_buffer_info](https://nodejs.org/api/n-api.html#n_api_napi_get_buffer_info)| 1 | v8.0.0 | ✅ | | [napi_get_buffer_info](https://nodejs.org/api/n-api.html#n_api_napi_get_buffer_info) | 1 | v8.0.0 | ✅ |
|[napi_get_prototype](https://nodejs.org/api/n-api.html#n_api_napi_get_prototype)| 1 | v8.0.0 | ⛔️ | | [napi_get_prototype](https://nodejs.org/api/n-api.html#n_api_napi_get_prototype) | 1 | v8.0.0 | ⛔️ |
|[napi_get_typedarray_info](https://nodejs.org/api/n-api.html#n_api_napi_get_typedarray_info)| 1 | v8.0.0 | ⛔️ | | [napi_get_typedarray_info](https://nodejs.org/api/n-api.html#n_api_napi_get_typedarray_info) | 1 | v8.0.0 | ⛔️ |
|[napi_get_dataview_info](https://nodejs.org/api/n-api.html#n_api_napi_get_dataview_info)| 1 | v8.3.0 | ⛔️ | | [napi_get_dataview_info](https://nodejs.org/api/n-api.html#n_api_napi_get_dataview_info) | 1 | v8.3.0 | ⛔️ |
|[napi_get_date_value](https://nodejs.org/api/n-api.html#n_api_napi_get_date_value)| 5 | v11.11.0 | ⛔️ | | [napi_get_date_value](https://nodejs.org/api/n-api.html#n_api_napi_get_date_value) | 5 | v11.11.0 | ⛔️ |
|[napi_get_value_bool](https://nodejs.org/api/n-api.html#n_api_napi_get_value_bool)| 1 | v8.0.0 | ✅ | | [napi_get_value_bool](https://nodejs.org/api/n-api.html#n_api_napi_get_value_bool) | 1 | v8.0.0 | ✅ |
|[napi_get_value_double](https://nodejs.org/api/n-api.html#n_api_napi_get_value_double)| 1 | v8.0.0 | ✅ | | [napi_get_value_double](https://nodejs.org/api/n-api.html#n_api_napi_get_value_double) | 1 | v8.0.0 | ✅ |
|[napi_get_value_bigint_int64](https://nodejs.org/api/n-api.html#n_api_napi_get_value_bigint_int64)| 6 | v10.7.0 | ⛔️ | | [napi_get_value_bigint_int64](https://nodejs.org/api/n-api.html#n_api_napi_get_value_bigint_int64) | 6 | v10.7.0 | ⛔️ |
|[napi_get_value_bigint_uint64](https://nodejs.org/api/n-api.html#n_api_napi_get_value_bigint_uint64)| 6 | v10.7.0 | ⛔️ | | [napi_get_value_bigint_uint64](https://nodejs.org/api/n-api.html#n_api_napi_get_value_bigint_uint64) | 6 | v10.7.0 | ⛔️ |
|[napi_get_value_bigint_words](https://nodejs.org/api/n-api.html#n_api_napi_get_value_bigint_words)| 6 | v10.7.0 | ⛔️ | | [napi_get_value_bigint_words](https://nodejs.org/api/n-api.html#n_api_napi_get_value_bigint_words) | 6 | v10.7.0 | ⛔️ |
|[napi_get_value_external](https://nodejs.org/api/n-api.html#n_api_napi_get_value_external)| 1 | v8.0.0 | ⛔️ | | [napi_get_value_external](https://nodejs.org/api/n-api.html#n_api_napi_get_value_external) | 1 | v8.0.0 | ⛔️ |
|[napi_get_value_int32](https://nodejs.org/api/n-api.html#n_api_napi_get_value_int32)| 1 | v8.0.0 | ✅ | | [napi_get_value_int32](https://nodejs.org/api/n-api.html#n_api_napi_get_value_int32) | 1 | v8.0.0 | ✅ |
|[napi_get_value_int64](https://nodejs.org/api/n-api.html#n_api_napi_get_value_int64)| 1 | v8.0.0 | ✅ | | [napi_get_value_int64](https://nodejs.org/api/n-api.html#n_api_napi_get_value_int64) | 1 | v8.0.0 | ✅ |
|[napi_get_value_string_latin1](https://nodejs.org/api/n-api.html#n_api_napi_get_value_string_latin1)| 1 | v8.0.0 | ⛔️ | | [napi_get_value_string_latin1](https://nodejs.org/api/n-api.html#n_api_napi_get_value_string_latin1) | 1 | v8.0.0 | ⛔️ |
|[napi_get_value_string_utf8](https://nodejs.org/api/n-api.html#n_api_napi_get_value_string_utf8)| 1 | v8.0.0 | ✅ | | [napi_get_value_string_utf8](https://nodejs.org/api/n-api.html#n_api_napi_get_value_string_utf8) | 1 | v8.0.0 | ✅ |
|[napi_get_value_string_utf16](https://nodejs.org/api/n-api.html#n_api_napi_get_value_string_utf16)| 1 | v8.0.0 | ✅ | | [napi_get_value_string_utf16](https://nodejs.org/api/n-api.html#n_api_napi_get_value_string_utf16) | 1 | v8.0.0 | ✅ |
|[napi_get_value_uint32](https://nodejs.org/api/n-api.html#n_api_napi_get_value_uint32)| 1 | v8.0.0 | ✅ | | [napi_get_value_uint32](https://nodejs.org/api/n-api.html#n_api_napi_get_value_uint32) | 1 | v8.0.0 | ✅ |
|[napi_get_boolean](https://nodejs.org/api/n-api.html#n_api_napi_get_boolean)| 1 | v8.0.0 | ✅ | | [napi_get_boolean](https://nodejs.org/api/n-api.html#n_api_napi_get_boolean) | 1 | v8.0.0 | ✅ |
|[napi_get_global](https://nodejs.org/api/n-api.html#n_api_napi_get_global)| 1 | v8.0.0 | ⛔️ | | [napi_get_global](https://nodejs.org/api/n-api.html#n_api_napi_get_global) | 1 | v8.0.0 | ⛔️ |
|[napi_get_null](https://nodejs.org/api/n-api.html#n_api_napi_get_null)| 1 | v8.0.0 | ✅ | | [napi_get_null](https://nodejs.org/api/n-api.html#n_api_napi_get_null) | 1 | v8.0.0 | ✅ |
|[napi_get_undefined](https://nodejs.org/api/n-api.html#n_api_napi_get_undefined)| 1 | v8.0.0 | ✅ | | [napi_get_undefined](https://nodejs.org/api/n-api.html#n_api_napi_get_undefined) | 1 | v8.0.0 | ✅ |
### [Working with JavaScript Values and Abstract Operations](https://nodejs.org/api/n-api.html#n_api_working_with_javascript_values_and_abstract_operations) ### [Working with JavaScript Values and Abstract Operations](https://nodejs.org/api/n-api.html#n_api_working_with_javascript_values_and_abstract_operations)
| NAPI | NAPI Version | Minimal Node Version | Status | | NAPI | NAPI Version | Minimal Node Version | Status |
| ---- | ------------ | -------------------- | ------ | | ---------------------------------------------------------------------------------------------------- | ------------ | -------------------- | ------ |
|[napi_coerce_to_bool](https://nodejs.org/api/n-api.html#n_api_napi_coerce_to_bool)| 1 | v8.0.0 | ✅ | | [napi_coerce_to_bool](https://nodejs.org/api/n-api.html#n_api_napi_coerce_to_bool) | 1 | v8.0.0 | ✅ |
|[napi_coerce_to_number](https://nodejs.org/api/n-api.html#n_api_napi_coerce_to_number)| 1 | v8.0.0 | ✅ | | [napi_coerce_to_number](https://nodejs.org/api/n-api.html#n_api_napi_coerce_to_number) | 1 | v8.0.0 | ✅ |
|[napi_coerce_to_object](https://nodejs.org/api/n-api.html#n_api_napi_coerce_to_object)| 1 | v8.0.0 | ✅ | | [napi_coerce_to_object](https://nodejs.org/api/n-api.html#n_api_napi_coerce_to_object) | 1 | v8.0.0 | ✅ |
|[napi_coerce_to_string](https://nodejs.org/api/n-api.html#n_api_napi_coerce_to_string)| 1 | v8.0.0 | ✅ | | [napi_coerce_to_string](https://nodejs.org/api/n-api.html#n_api_napi_coerce_to_string) | 1 | v8.0.0 | ✅ |
|[napi_typeof](https://nodejs.org/api/n-api.html#n_api_napi_typeof)| 1 | v8.0.0 | ✅ | | [napi_typeof](https://nodejs.org/api/n-api.html#n_api_napi_typeof) | 1 | v8.0.0 | ✅ |
|[napi_instanceof](https://nodejs.org/api/n-api.html#n_api_napi_instanceof)| 1 | v8.0.0 | ⛔️ | | [napi_instanceof](https://nodejs.org/api/n-api.html#n_api_napi_instanceof) | 1 | v8.0.0 | ⛔️ |
|[napi_is_array](https://nodejs.org/api/n-api.html#n_api_napi_is_array)| 1 | v8.0.0 | ✅ | | [napi_is_array](https://nodejs.org/api/n-api.html#n_api_napi_is_array) | 1 | v8.0.0 | ✅ |
|[napi_is_arraybuffer](https://nodejs.org/api/n-api.html#n_api_napi_is_arraybuffer)| 1 | v8.0.0 | ✅ | | [napi_is_arraybuffer](https://nodejs.org/api/n-api.html#n_api_napi_is_arraybuffer) | 1 | v8.0.0 | ✅ |
|[napi_is_buffer](https://nodejs.org/api/n-api.html#n_api_napi_is_buffer)| 1 | v8.0.0 | ✅ | | [napi_is_buffer](https://nodejs.org/api/n-api.html#n_api_napi_is_buffer) | 1 | v8.0.0 | ✅ |
|[napi_is_date](https://nodejs.org/api/n-api.html#n_api_napi_is_date)| 1 | v8.0.0 | ⛔️ | | [napi_is_date](https://nodejs.org/api/n-api.html#n_api_napi_is_date) | 1 | v8.0.0 | ⛔️ |
|[napi_is_error](https://nodejs.org/api/n-api.html#n_api_napi_is_error_1)| 1 | v8.0.0 | ⛔️ | | [napi_is_error](https://nodejs.org/api/n-api.html#n_api_napi_is_error_1) | 1 | v8.0.0 | ⛔️ |
|[napi_is_typedarray](https://nodejs.org/api/n-api.html#n_api_napi_is_typedarray)| 1 | v8.0.0 | ⛔️ | | [napi_is_typedarray](https://nodejs.org/api/n-api.html#n_api_napi_is_typedarray) | 1 | v8.0.0 | ⛔️ |
|[napi_is_dataview](https://nodejs.org/api/n-api.html#n_api_napi_is_dataview)| 1 | v8.3.0 | ⛔️ | | [napi_is_dataview](https://nodejs.org/api/n-api.html#n_api_napi_is_dataview) | 1 | v8.3.0 | ⛔️ |
|[napi_strict_equals](https://nodejs.org/api/n-api.html#n_api_napi_strict_equals)| 1 | v8.0.0 | ⛔️ | | [napi_strict_equals](https://nodejs.org/api/n-api.html#n_api_napi_strict_equals) | 1 | v8.0.0 | ⛔️ |
|[napi_detach_arraybuffer](https://nodejs.org/api/n-api.html#n_api_napi_detach_arraybuffer)| Experimental | v13.3.0 | ⛔️ | | [napi_detach_arraybuffer](https://nodejs.org/api/n-api.html#n_api_napi_detach_arraybuffer) | Experimental | v13.3.0 | ⛔️ |
|[napi_is_detached_arraybuffer](https://nodejs.org/api/n-api.html#n_api_napi_is_detached_arraybuffer)| Experimental | v13.3.0 | ⛔️ | | [napi_is_detached_arraybuffer](https://nodejs.org/api/n-api.html#n_api_napi_is_detached_arraybuffer) | Experimental | v13.3.0 | ⛔️ |

View file

@ -1,4 +1,5 @@
# napi-build # napi-build
> Build support for napi-rs > Build support for napi-rs
Setup `N-API` build in your `build.rs`: Setup `N-API` build in your `build.rs`:

View file

@ -8,10 +8,7 @@ use std::convert::TryInto;
register_module!(test_module, init); register_module!(test_module, init);
fn init<'env>( fn init(env: &Env, exports: &mut Value<Object>) -> Result<Option<Value<Object>>> {
env: &'env Env,
exports: &'env mut Value<'env, Object>,
) -> Result<Option<Value<'env, Object>>> {
exports.set_named_property("testThrow", env.create_function("testThrow", test_throw)?)?; exports.set_named_property("testThrow", env.create_function("testThrow", test_throw)?)?;
exports.set_named_property("fibonacci", env.create_function("fibonacci", fibonacci)?)?; exports.set_named_property("fibonacci", env.create_function("fibonacci", fibonacci)?)?;
@ -20,7 +17,7 @@ fn init<'env>(
#[js_function] #[js_function]
fn test_throw(_ctx: CallContext) -> Result<Value<Any>> { fn test_throw(_ctx: CallContext) -> Result<Value<Any>> {
Err(Error::new(Status::GenericFailure)) Err(Error::from_status(Status::GenericFailure))
} }
#[js_function(1)] #[js_function(1)]

View file

@ -102,16 +102,13 @@ pub fn js_function(attr: TokenStream, input: TokenStream) -> TokenStream {
} }
let env = Env::from_raw(raw_env); let env = Env::from_raw(raw_env);
let call_ctx = CallContext::new(&env, raw_this, raw_args, #arg_len_span); let call_ctx = CallContext::new(env, raw_this, raw_args, #arg_len_span);
let result = call_ctx.and_then(|ctx| #new_fn_name(ctx)); let result = call_ctx.and_then(|ctx| #new_fn_name(ctx));
has_error = has_error && result.is_err(); has_error = has_error && result.is_err();
match result { match result {
Ok(result) => result.into_raw(), Ok(result) => result.into_raw(),
Err(e) => { Err(e) => {
if !cfg!(windows) {
let _ = writeln!(::std::io::stderr(), "Error calling function: {:?}", e);
}
let message = format!("{:?}", e); let message = format!("{:?}", e);
unsafe { unsafe {
napi_rs::sys::napi_throw_error(raw_env, ptr::null(), message.as_ptr() as *const c_char); napi_rs::sys::napi_throw_error(raw_env, ptr::null(), message.as_ptr() as *const c_char);

View file

@ -7,6 +7,7 @@ description = "N-API bindings"
readme = "README.md" readme = "README.md"
repository = "https://github.com/Brooooooklyn/napi-rs" repository = "https://github.com/Brooooooklyn/napi-rs"
keywords = ["NodeJS", "FFI", "NAPI", "n-api"] keywords = ["NodeJS", "FFI", "NAPI", "n-api"]
edition = "2018"
[dependencies] [dependencies]
futures = { version = "0.3", features = ["default", "thread-pool"] } futures = { version = "0.3", features = ["default", "thread-pool"] }
@ -25,7 +26,6 @@ tar = "0.4"
[build-dependencies] [build-dependencies]
bindgen = "0.53" bindgen = "0.53"
cc = "1.0"
glob = "0.3" glob = "0.3"
napi-build = { version = "0.1", path = "../build" } napi-build = { version = "0.1", path = "../build" }
regex = "1.3" regex = "1.3"

View file

@ -1,5 +1,4 @@
extern crate bindgen; extern crate bindgen;
extern crate cc;
#[cfg(windows)] #[cfg(windows)]
extern crate flate2; extern crate flate2;
extern crate glob; extern crate glob;
@ -36,8 +35,6 @@ fn main() {
); );
} }
env::set_var("CARGO_RUSTC_FLAGS", "-Clink-args=-export_dynamic");
if node_major_version < 10 { if node_major_version < 10 {
panic!("node version is too low") panic!("node version is too low")
} }
@ -60,31 +57,6 @@ fn main() {
.expect("Unable to generate napi bindings") .expect("Unable to generate napi bindings")
.write_to_file(out_path.join("bindings.rs")) .write_to_file(out_path.join("bindings.rs"))
.expect("Unable to write napi bindings"); .expect("Unable to write napi bindings");
let mut bindings_path = PathBuf::from("src");
bindings_path.push("sys");
bindings_path.push("bindings.cc");
let mut cc_builder = cc::Build::new();
cc_builder
.cpp(true)
.include(&node_include_path)
.file(bindings_path);
if !cfg!(windows) {
cc_builder.flag("-Wno-unused-parameter");
};
if cfg!(target_os = "macos") {
cc_builder.flag("-std=c++0x");
} else if cfg!(linux) || cfg!(target_env = "gnu") {
cc_builder.flag("-std=c++14");
}
cc_builder
.cargo_metadata(true)
.out_dir(&out_path)
.compile("napi-bindings");
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]

View file

@ -1,32 +1,35 @@
use crate::{sys, Any, Env, Error, Result, Status, Value, ValueType}; use crate::{sys, Any, Env, Error, Result, Status, Value, ValueType};
pub struct CallContext<'env, T: ValueType = Any> { pub struct CallContext<T: ValueType = Any> {
pub env: &'env Env, pub env: Env,
pub this: Value<'env, T>, pub this: Value<T>,
args: [sys::napi_value; 8], args: [sys::napi_value; 8],
arg_len: usize, arg_len: usize,
} }
impl<'env, T: ValueType> CallContext<'env, T> { impl<T: ValueType> CallContext<T> {
pub fn new( pub fn new(
env: &'env Env, env: Env,
this: sys::napi_value, this: sys::napi_value,
args: [sys::napi_value; 8], args: [sys::napi_value; 8],
arg_len: usize, arg_len: usize,
) -> Result<Self> { ) -> Result<Self> {
Ok(Self { Ok(Self {
env, env,
this: Value::<'env, T>::from_raw(env, this)?, this: Value::<T>::from_raw(env.0, this)?,
args, args,
arg_len, arg_len,
}) })
} }
pub fn get<ArgType: ValueType>(&'env self, index: usize) -> Result<Value<'env, ArgType>> { pub fn get<ArgType: ValueType>(&self, index: usize) -> Result<Value<ArgType>> {
if index + 1 > self.arg_len { if index + 1 > self.arg_len {
Err(Error::new(Status::GenericFailure)) Err(Error {
status: Status::GenericFailure,
reason: Some("Arguments index out of range".to_owned()),
})
} else { } else {
Value::<'env, ArgType>::from_raw(&self.env, self.args[index]) Value::<ArgType>::from_raw(self.env.0, self.args[index])
} }
} }
} }

View file

@ -1,4 +1,3 @@
use super::sys;
use futures::task::Poll; use futures::task::Poll;
use std::future::Future; use std::future::Future;
use std::mem; use std::mem;
@ -7,16 +6,7 @@ use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::task::{Context, RawWaker, RawWakerVTable, Waker}; use std::task::{Context, RawWaker, RawWakerVTable, Waker};
pub struct LibuvExecutor { use crate::{sys, Error, Result, Status};
event_loop: *mut sys::uv_loop_s,
}
#[derive(Clone, Debug)]
struct LibuvWaker(*mut sys::uv_async_t);
unsafe impl Send for LibuvWaker {}
unsafe impl Sync for LibuvWaker {}
const UV_ASYNC_V_TABLE: RawWakerVTable = RawWakerVTable::new( const UV_ASYNC_V_TABLE: RawWakerVTable = RawWakerVTable::new(
clone_executor, clone_executor,
@ -39,27 +29,28 @@ unsafe fn wake_uv_async_by_ref(uv_async_t: *const ()) {
assert!(status == 0, "wake_uv_async_by_ref failed"); assert!(status == 0, "wake_uv_async_by_ref failed");
} }
unsafe fn drop_uv_async(uv_async_t_ptr: *const ()) { unsafe fn drop_uv_async(_uv_async_t_ptr: *const ()) {}
sys::uv_unref(uv_async_t_ptr as *mut sys::uv_handle_t);
}
struct Task<'a> { struct Task<'a> {
future: Pin<Box<dyn Future<Output = ()>>>, future: Pin<Box<dyn Future<Output = ()>>>,
context: Context<'a>, context: Context<'a>,
} }
impl LibuvExecutor { pub fn execute<F: 'static + Future<Output = ()>>(
pub fn new(event_loop: *mut sys::uv_loop_s) -> Self { event_loop: *mut sys::uv_loop_s,
Self { event_loop } future: F,
} ) -> Result<()> {
pub fn execute<F: 'static + Future<Output = ()>>(&self, future: F) {
let uninit = mem::MaybeUninit::<sys::uv_async_t>::uninit(); let uninit = mem::MaybeUninit::<sys::uv_async_t>::uninit();
let uv_async_t: Box<sys::uv_async_t> = unsafe { Box::new(uninit.assume_init()) }; let uv_async_t: Box<sys::uv_async_t> = unsafe { Box::new(uninit.assume_init()) };
let uv_async_t_ref = Box::leak(uv_async_t); let uv_async_t_ref = Box::leak(uv_async_t);
unsafe { unsafe {
let status = sys::uv_async_init(self.event_loop, uv_async_t_ref, Some(poll_future)); let status = sys::uv_async_init(event_loop, uv_async_t_ref, Some(poll_future));
assert!(status == 0, "Non-zero status returned from uv_async_init"); if status != 0 {
return Err(Error {
status: Status::Unknown,
reason: Some("Non-zero status returned from uv_async_init".to_owned()),
});
}
}; };
unsafe { unsafe {
let waker = Waker::from_raw(RawWaker::new( let waker = Waker::from_raw(RawWaker::new(
@ -77,8 +68,8 @@ impl LibuvExecutor {
uv_async_t_ref as *mut _ as *mut sys::uv_handle_t, uv_async_t_ref as *mut _ as *mut sys::uv_handle_t,
Arc::into_raw(arc_task) as *mut c_void, Arc::into_raw(arc_task) as *mut c_void,
); );
} };
} Ok(())
} }
} }
@ -96,10 +87,7 @@ unsafe extern "C" fn poll_future(handle: *mut sys::uv_async_t) {
let mut task = Arc::from_raw(data_ptr); let mut task = Arc::from_raw(data_ptr);
if let Some(mut_task) = Arc::get_mut(&mut task) { if let Some(mut_task) = Arc::get_mut(&mut task) {
if mut_task.poll_future() { if mut_task.poll_future() {
sys::uv_close( sys::uv_close(handle as *mut sys::uv_handle_t, None);
handle as *mut sys::uv_handle_t,
Some(drop_handle_after_close),
);
} else { } else {
Arc::into_raw(task); Arc::into_raw(task);
}; };
@ -107,7 +95,3 @@ unsafe extern "C" fn poll_future(handle: *mut sys::uv_async_t) {
Arc::into_raw(task); Arc::into_raw(task);
} }
} }
unsafe extern "C" fn drop_handle_after_close(handle: *mut sys::uv_handle_t) {
Box::from_raw(handle);
}

View file

@ -1,5 +1,7 @@
extern crate futures; extern crate futures;
use core::fmt::Debug;
use futures::prelude::*;
use std::any::TypeId; use std::any::TypeId;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::ffi::CString; use std::ffi::CString;
@ -14,17 +16,21 @@ use std::string::String as RustString;
mod call_context; mod call_context;
mod executor; mod executor;
mod promise;
pub mod sys; pub mod sys;
mod version;
pub use call_context::CallContext; pub use call_context::CallContext;
pub use sys::{napi_valuetype, Status}; pub use sys::{napi_valuetype, Status};
pub use version::NodeVersion;
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
pub type Callback = extern "C" fn(sys::napi_env, sys::napi_callback_info) -> sys::napi_value; pub type Callback = extern "C" fn(sys::napi_env, sys::napi_callback_info) -> sys::napi_value;
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Error { pub struct Error {
status: Status, pub status: Status,
pub reason: Option<String>,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@ -75,8 +81,8 @@ pub struct ArrayBuffer {
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct Value<'env, T> { pub struct Value<T> {
env: &'env Env, env: sys::napi_env,
raw_value: sys::napi_value, raw_value: sys::napi_value,
value: T, value: T,
} }
@ -87,14 +93,6 @@ pub struct Ref<T> {
_marker: PhantomData<T>, _marker: PhantomData<T>,
} }
pub struct AsyncContext {
raw_env: sys::napi_env,
raw_context: sys::napi_async_context,
raw_resource: sys::napi_ref,
}
pub struct Deferred(sys::napi_deferred);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Property { pub struct Property {
name: RustString, name: RustString,
@ -141,7 +139,7 @@ macro_rules! register_module {
raw_exports: sys::napi_value, raw_exports: sys::napi_value,
) -> sys::napi_value { ) -> sys::napi_value {
let env = Env::from_raw(raw_env); let env = Env::from_raw(raw_env);
let mut exports: Value<Object> = Value::from_raw(&env, raw_exports).unwrap(); let mut exports: Value<Object> = Value::from_raw(raw_env, raw_exports).unwrap();
let result = $init(&env, &mut exports); let result = $init(&env, &mut exports);
@ -162,15 +160,19 @@ macro_rules! register_module {
} }
impl Error { impl Error {
pub fn new(status: Status) -> Self { pub fn from_status(status: Status) -> Self {
Error { status: status } Error {
status: status,
reason: None,
}
} }
} }
impl From<std::ffi::NulError> for Error { impl From<std::ffi::NulError> for Error {
fn from(_error: std::ffi::NulError) -> Self { fn from(error: std::ffi::NulError) -> Self {
Error { Error {
status: Status::StringExpected, status: Status::StringExpected,
reason: Some(format!("{:?}", error)),
} }
} }
} }
@ -180,14 +182,14 @@ impl Env {
Env(env) Env(env)
} }
pub fn get_undefined<'a>(&'a self) -> Result<Value<'a, Undefined>> { pub fn get_undefined(&self) -> Result<Value<Undefined>> {
let mut raw_value = ptr::null_mut(); let mut raw_value = ptr::null_mut();
let status = unsafe { sys::napi_get_undefined(self.0, &mut raw_value) }; let status = unsafe { sys::napi_get_undefined(self.0, &mut raw_value) };
check_status(status)?; check_status(status)?;
Ok(Value::from_raw_value(self, raw_value, Undefined)) Ok(Value::from_raw_value(self, raw_value, Undefined))
} }
pub fn get_null<'a>(&'a self) -> Result<Value<'a, Null>> { pub fn get_null(&self) -> Result<Value<Null>> {
let mut raw_value = ptr::null_mut(); let mut raw_value = ptr::null_mut();
let status = unsafe { sys::napi_get_null(self.0, &mut raw_value) }; let status = unsafe { sys::napi_get_null(self.0, &mut raw_value) };
check_status(status)?; check_status(status)?;
@ -201,7 +203,7 @@ impl Env {
Ok(Value::from_raw_value(self, raw_value, Boolean { value })) Ok(Value::from_raw_value(self, raw_value, Boolean { value }))
} }
pub fn create_int32<'a>(&'a self, int: i32) -> Result<Value<'a, Number>> { pub fn create_int32(&self, int: i32) -> Result<Value<Number>> {
let mut raw_value = ptr::null_mut(); let mut raw_value = ptr::null_mut();
let status = let status =
unsafe { sys::napi_create_int32(self.0, int, (&mut raw_value) as *mut sys::napi_value) }; unsafe { sys::napi_create_int32(self.0, int, (&mut raw_value) as *mut sys::napi_value) };
@ -209,7 +211,7 @@ impl Env {
Ok(Value::from_raw_value(self, raw_value, Number::Int32(int))) Ok(Value::from_raw_value(self, raw_value, Number::Int32(int)))
} }
pub fn create_int64<'a>(&'a self, int: i64) -> Result<Value<'a, Number>> { pub fn create_int64(&self, int: i64) -> Result<Value<Number>> {
let mut raw_value = ptr::null_mut(); let mut raw_value = ptr::null_mut();
let status = let status =
unsafe { sys::napi_create_int64(self.0, int, (&mut raw_value) as *mut sys::napi_value) }; unsafe { sys::napi_create_int64(self.0, int, (&mut raw_value) as *mut sys::napi_value) };
@ -217,7 +219,7 @@ impl Env {
Ok(Value::from_raw_value(self, raw_value, Number::Int(int))) Ok(Value::from_raw_value(self, raw_value, Number::Int(int)))
} }
pub fn create_uint32<'a>(&'a self, number: u32) -> Result<Value<'a, Number>> { pub fn create_uint32(&self, number: u32) -> Result<Value<Number>> {
let mut raw_value = ptr::null_mut(); let mut raw_value = ptr::null_mut();
let status = let status =
unsafe { sys::napi_create_uint32(self.0, number, (&mut raw_value) as *mut sys::napi_value) }; unsafe { sys::napi_create_uint32(self.0, number, (&mut raw_value) as *mut sys::napi_value) };
@ -225,7 +227,7 @@ impl Env {
Ok(Value::from_raw_value(self, raw_value, Number::U32(number))) Ok(Value::from_raw_value(self, raw_value, Number::U32(number)))
} }
pub fn create_double<'a>(&'a self, double: f64) -> Result<Value<'a, Number>> { pub fn create_double(&self, double: f64) -> Result<Value<Number>> {
let mut raw_value = ptr::null_mut(); let mut raw_value = ptr::null_mut();
let status = let status =
unsafe { sys::napi_create_double(self.0, double, (&mut raw_value) as *mut sys::napi_value) }; unsafe { sys::napi_create_double(self.0, double, (&mut raw_value) as *mut sys::napi_value) };
@ -237,7 +239,7 @@ impl Env {
)) ))
} }
pub fn create_string<'a, 'b>(&'a self, s: &'b str) -> Result<Value<'a, JsString>> { pub fn create_string<'a, 'b>(&'a self, s: &'b str) -> Result<Value<JsString>> {
let mut raw_value = ptr::null_mut(); let mut raw_value = ptr::null_mut();
let status = unsafe { let status = unsafe {
sys::napi_create_string_utf8( sys::napi_create_string_utf8(
@ -260,7 +262,7 @@ impl Env {
Ok(Value::from_raw_value(self, raw_value, JsString)) Ok(Value::from_raw_value(self, raw_value, JsString))
} }
pub fn create_object<'a>(&'a self) -> Result<Value<'a, Object>> { pub fn create_object(&self) -> Result<Value<Object>> {
let mut raw_value = ptr::null_mut(); let mut raw_value = ptr::null_mut();
let status = unsafe { sys::napi_create_object(self.0, &mut raw_value) }; let status = unsafe { sys::napi_create_object(self.0, &mut raw_value) };
check_status(status)?; check_status(status)?;
@ -365,11 +367,7 @@ impl Env {
)) ))
} }
pub fn create_function<'a, 'b>( pub fn create_function(&self, name: &str, callback: Callback) -> Result<Value<Function>> {
&'a self,
name: &'b str,
callback: Callback,
) -> Result<Value<'a, Function>> {
let mut raw_result = ptr::null_mut(); let mut raw_result = ptr::null_mut();
let status = unsafe { let status = unsafe {
sys::napi_create_function( sys::napi_create_function(
@ -414,7 +412,7 @@ impl Env {
check_status(status)?; check_status(status)?;
}; };
Value::from_raw(self, raw_value) Value::from_raw(self.0, raw_value)
} }
pub fn define_class<'a, 'b>( pub fn define_class<'a, 'b>(
@ -422,7 +420,7 @@ impl Env {
name: &'b str, name: &'b str,
constructor_cb: Callback, constructor_cb: Callback,
properties: Vec<Property>, properties: Vec<Property>,
) -> Result<Value<'a, Function>> { ) -> Result<Value<Function>> {
let mut raw_result = ptr::null_mut(); let mut raw_result = ptr::null_mut();
let raw_properties = properties let raw_properties = properties
.into_iter() .into_iter()
@ -473,10 +471,14 @@ impl Env {
let tagged_object: *mut TaggedObject<T> = mem::transmute(unknown_tagged_object); let tagged_object: *mut TaggedObject<T> = mem::transmute(unknown_tagged_object);
(*tagged_object).object.as_mut().ok_or(Error { (*tagged_object).object.as_mut().ok_or(Error {
status: Status::InvalidArg, status: Status::InvalidArg,
reason: Some("Invalid argument, nothing attach to js_object".to_owned()),
}) })
} else { } else {
Err(Error { Err(Error {
status: Status::InvalidArg, status: Status::InvalidArg,
reason: Some(
"Invalid argument, T on unrwap is not the type of wrapped object".to_owned(),
),
}) })
} }
} }
@ -496,35 +498,40 @@ impl Env {
} else { } else {
Err(Error { Err(Error {
status: Status::InvalidArg, status: Status::InvalidArg,
reason: Some(
"Invalid argument, T on drop_wrapped is not the type of wrapped object".to_owned(),
),
}) })
} }
} }
} }
pub fn async_init(&self, resource: Option<Value<Object>>, name: &str) -> Result<AsyncContext> { pub fn create_error(&self, e: Error) -> Result<Value<Object>> {
let raw_resource = resource let reason = e.reason.unwrap_or("".to_owned());
.map(|r| Ok(r.into_raw())) let reason_string = self.create_string(reason.as_str())?;
.unwrap_or_else(|| self.create_object().map(|o| o.into_raw()))?; let mut result = ptr::null_mut();
let raw_name = self.create_string(name)?.into_raw(); let status = unsafe {
sys::napi_create_error(
let mut raw_context = ptr::null_mut(); self.0,
let mut raw_resource_ref = ptr::null_mut(); ptr::null_mut(),
unsafe { reason_string.into_raw(),
let status = sys::napi_async_init(self.0, raw_resource, raw_name, &mut raw_context); &mut result,
check_status(status)?; )
};
let status = sys::napi_create_reference(self.0, raw_resource, 1, &mut raw_resource_ref);
check_status(status)?; check_status(status)?;
Ok(Value::from_raw_value(self, result, Object))
} }
Ok(AsyncContext { pub fn perform_async_operation<
raw_env: self.0, T: 'static,
raw_resource: raw_resource_ref, V: 'static + ValueType,
raw_context, F: 'static + Future<Output = Result<T>>,
}) R: 'static + FnOnce(&mut Env, T) -> Result<Value<V>>,
} >(
&self,
pub fn create_promise(&self) -> Result<(Value<Object>, Deferred)> { deferred: F,
resolver: R,
) -> Result<Value<Object>> {
let mut raw_promise = ptr::null_mut(); let mut raw_promise = ptr::null_mut();
let mut raw_deferred = ptr::null_mut(); let mut raw_deferred = ptr::null_mut();
@ -533,28 +540,41 @@ impl Env {
check_status(status)?; check_status(status)?;
} }
Ok((
Value::from_raw_value(self, raw_promise, Object),
Deferred(raw_deferred),
))
}
pub fn resolve_deferred<T: ValueType>(&self, deferred: Deferred, value: Value<T>) -> Result<()> {
unsafe {
let status = sys::napi_resolve_deferred(self.0, deferred.0, value.into_raw());
check_status(status)
}
}
pub fn create_executor(&self) -> executor::LibuvExecutor {
let event_loop = unsafe { sys::uv_default_loop() }; let event_loop = unsafe { sys::uv_default_loop() };
executor::LibuvExecutor::new(event_loop) let raw_env = self.0;
executor::execute(
event_loop,
promise::resolve(self.0, deferred, resolver, raw_deferred).map(move |v| match v {
Ok(value) => value,
Err(e) => {
let cloned_error = e.clone();
unsafe {
sys::napi_throw_error(
raw_env,
ptr::null(),
e.reason.unwrap_or(format!("{:?}", e.status)).as_ptr() as *const _,
);
};
eprintln!("{:?}", &cloned_error);
panic!(cloned_error);
}
}),
)?;
Ok(Value::from_raw_value(self, raw_promise, Object))
}
pub fn get_node_version(&self) -> Result<NodeVersion> {
let mut result = ptr::null();
check_status(unsafe { sys::napi_get_node_version(self.0, &mut result) })?;
let version = unsafe { *result };
version.try_into()
} }
} }
pub trait ValueType: Copy { pub trait ValueType: Copy + Debug {
fn from_raw(env: sys::napi_env, raw: sys::napi_value) -> Result<Self>; fn from_raw(env: sys::napi_env, raw: sys::napi_value) -> Result<Self>;
fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> bool; fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> Result<bool>;
} }
impl ValueType for Any { impl ValueType for Any {
@ -562,8 +582,8 @@ impl ValueType for Any {
Ok(Any) Ok(Any)
} }
fn matches_raw_type(_env: sys::napi_env, _raw: sys::napi_value) -> bool { fn matches_raw_type(_env: sys::napi_env, _raw: sys::napi_value) -> Result<bool> {
true Ok(true)
} }
} }
@ -572,8 +592,8 @@ impl ValueType for Undefined {
Ok(Undefined) Ok(Undefined)
} }
fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> bool { fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> Result<bool> {
get_raw_type(env, raw) == sys::napi_valuetype::napi_undefined Ok(get_raw_type(env, raw)? == sys::napi_valuetype::napi_undefined)
} }
} }
@ -582,8 +602,8 @@ impl ValueType for Null {
Ok(Null) Ok(Null)
} }
fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> bool { fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> Result<bool> {
get_raw_type(env, raw) == sys::napi_valuetype::napi_null Ok(get_raw_type(env, raw)? == sys::napi_valuetype::napi_null)
} }
} }
@ -595,8 +615,8 @@ impl ValueType for Boolean {
Ok(Boolean { value }) Ok(Boolean { value })
} }
fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> bool { fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> Result<bool> {
get_raw_type(env, raw) == sys::napi_valuetype::napi_boolean Ok(get_raw_type(env, raw)? == sys::napi_valuetype::napi_boolean)
} }
} }
@ -608,8 +628,8 @@ impl ValueType for Number {
Ok(Number::Double(double)) Ok(Number::Double(double))
} }
fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> bool { fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> Result<bool> {
get_raw_type(env, raw) == sys::napi_valuetype::napi_number Ok(get_raw_type(env, raw)? == sys::napi_valuetype::napi_number)
} }
} }
@ -618,8 +638,8 @@ impl ValueType for JsString {
Ok(JsString {}) Ok(JsString {})
} }
fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> bool { fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> Result<bool> {
get_raw_type(env, raw) == sys::napi_valuetype::napi_string Ok(get_raw_type(env, raw)? == sys::napi_valuetype::napi_string)
} }
} }
@ -628,8 +648,8 @@ impl ValueType for Object {
Ok(Object {}) Ok(Object {})
} }
fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> bool { fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> Result<bool> {
get_raw_type(env, raw) == sys::napi_valuetype::napi_object Ok(get_raw_type(env, raw)? == sys::napi_valuetype::napi_object)
} }
} }
@ -645,13 +665,13 @@ impl ValueType for Buffer {
}) })
} }
fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> bool { fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> Result<bool> {
let mut result = false; let mut result = false;
unsafe { unsafe {
let status = sys::napi_is_buffer(env, raw, &mut result); let status = sys::napi_is_buffer(env, raw, &mut result);
debug_assert!(Status::from(status) == Status::Ok); check_status(status)?;
} }
result Ok(result)
} }
} }
@ -667,21 +687,21 @@ impl ValueType for ArrayBuffer {
}) })
} }
fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> bool { fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> Result<bool> {
let mut result = false; let mut result = false;
unsafe { unsafe {
let status = sys::napi_is_arraybuffer(env, raw, &mut result); let status = sys::napi_is_arraybuffer(env, raw, &mut result);
debug_assert!(Status::from(status) == Status::Ok); check_status(status)?;
} }
result Ok(result)
} }
} }
impl<'env> Value<'env, Buffer> { impl Value<Buffer> {
#[inline] #[inline]
pub fn from_value(env: &'env Env, value: &Value<'env, Any>) -> Result<Value<'env, Buffer>> { pub fn from_value(env: &Env, value: &Value<Any>) -> Result<Value<Buffer>> {
Ok(Value { Ok(Value {
env, env: env.0,
raw_value: value.raw_value, raw_value: value.raw_value,
value: Buffer::from_raw(env.0, value.into_raw())?, value: Buffer::from_raw(env.0, value.into_raw())?,
}) })
@ -693,25 +713,25 @@ impl ValueType for Function {
Ok(Function {}) Ok(Function {})
} }
fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> bool { fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> Result<bool> {
get_raw_type(env, raw) == sys::napi_valuetype::napi_function Ok(get_raw_type(env, raw)? == sys::napi_valuetype::napi_function)
} }
} }
impl<'env, T: ValueType> Value<'env, T> { impl<T: ValueType> Value<T> {
pub fn from_raw_value(env: &'env Env, raw_value: sys::napi_value, value: T) -> Self { pub fn from_raw_value(env: &Env, raw_value: sys::napi_value, value: T) -> Self {
Self { Self {
env, env: env.0,
raw_value, raw_value,
value, value,
} }
} }
pub fn from_raw(env: &'env Env, raw_value: sys::napi_value) -> Result<Self> { pub fn from_raw(env: sys::napi_env, raw_value: sys::napi_value) -> Result<Self> {
Ok(Self { Ok(Self {
env, env,
raw_value, raw_value,
value: T::from_raw(env.0, raw_value)?, value: T::from_raw(env, raw_value)?,
}) })
} }
@ -719,22 +739,22 @@ impl<'env, T: ValueType> Value<'env, T> {
self.raw_value self.raw_value
} }
pub fn coerce_to_number(self) -> Result<Value<'env, Number>> { pub fn coerce_to_number(self) -> Result<Value<Number>> {
let mut new_raw_value = ptr::null_mut(); let mut new_raw_value = ptr::null_mut();
let status = let status =
unsafe { sys::napi_coerce_to_number(self.env.0, self.raw_value, &mut new_raw_value) }; unsafe { sys::napi_coerce_to_number(self.env, self.raw_value, &mut new_raw_value) };
check_status(status)?; check_status(status)?;
Ok(Value { Ok(Value {
env: self.env, env: self.env,
raw_value: self.raw_value, raw_value: self.raw_value,
value: Number::from_raw(self.env.0, self.raw_value)?, value: Number::from_raw(self.env, self.raw_value)?,
}) })
} }
pub fn coerce_to_string(self) -> Result<Value<'env, JsString>> { pub fn coerce_to_string(self) -> Result<Value<JsString>> {
let mut new_raw_value = ptr::null_mut(); let mut new_raw_value = ptr::null_mut();
let status = let status =
unsafe { sys::napi_coerce_to_string(self.env.0, self.raw_value, &mut new_raw_value) }; unsafe { sys::napi_coerce_to_string(self.env, self.raw_value, &mut new_raw_value) };
check_status(status)?; check_status(status)?;
Ok(Value { Ok(Value {
env: self.env, env: self.env,
@ -743,11 +763,11 @@ impl<'env, T: ValueType> Value<'env, T> {
}) })
} }
pub fn coerce_to_object(self) -> Result<Value<'env, Object>> { pub fn coerce_to_object(self) -> Result<Value<Object>> {
let mut new_raw_value = ptr::null_mut(); let mut new_raw_value = ptr::null_mut();
let status = unsafe { let status = unsafe {
sys::napi_coerce_to_object( sys::napi_coerce_to_object(
self.env.0, self.env,
self.raw_value, self.raw_value,
(&mut new_raw_value) as *mut sys::napi_value, (&mut new_raw_value) as *mut sys::napi_value,
) )
@ -761,7 +781,7 @@ impl<'env, T: ValueType> Value<'env, T> {
} }
#[inline] #[inline]
pub fn into_any(self) -> Value<'env, Any> { pub fn into_any(self) -> Value<Any> {
Value { Value {
env: self.env, env: self.env,
raw_value: self.raw_value, raw_value: self.raw_value,
@ -771,27 +791,26 @@ impl<'env, T: ValueType> Value<'env, T> {
} }
#[inline] #[inline]
fn get_raw_type(env: sys::napi_env, raw_value: sys::napi_value) -> sys::napi_valuetype { fn get_raw_type(env: sys::napi_env, raw_value: sys::napi_value) -> Result<sys::napi_valuetype> {
unsafe { unsafe {
let value_type = ptr::null_mut(); let value_type = ptr::null_mut();
let status = sys::napi_typeof(env, raw_value, value_type); check_status(sys::napi_typeof(env, raw_value, value_type))?;
debug_assert!(Status::from(status) == Status::Ok); Ok(*value_type)
*value_type
} }
} }
impl<'env> Value<'env, Boolean> { impl Value<Boolean> {
pub fn get_value(&self) -> bool { pub fn get_value(&self) -> bool {
self.value.value self.value.value
} }
} }
impl<'env> Value<'env, JsString> { impl Value<JsString> {
pub fn len(&self) -> Result<usize> { pub fn len(&self) -> Result<usize> {
let mut raw_length = ptr::null_mut(); let mut raw_length = ptr::null_mut();
unsafe { unsafe {
let status = sys::napi_get_named_property( let status = sys::napi_get_named_property(
self.env.0, self.env,
self.raw_value, self.raw_value,
"length\0".as_ptr() as *const c_char, "length\0".as_ptr() as *const c_char,
&mut raw_length, &mut raw_length,
@ -803,7 +822,7 @@ impl<'env> Value<'env, JsString> {
} }
} }
impl<'env> Value<'env, JsString> { impl Value<JsString> {
#[inline] #[inline]
pub fn get_ref(&self) -> Result<&[u8]> { pub fn get_ref(&self) -> Result<&[u8]> {
let mut written_char_count: u64 = 0; let mut written_char_count: u64 = 0;
@ -811,7 +830,7 @@ impl<'env> Value<'env, JsString> {
let mut result = Vec::with_capacity(len); let mut result = Vec::with_capacity(len);
unsafe { unsafe {
let status = sys::napi_get_value_string_utf8( let status = sys::napi_get_value_string_utf8(
self.env.0, self.env,
self.raw_value, self.raw_value,
result.as_mut_ptr(), result.as_mut_ptr(),
len as u64, len as u64,
@ -829,7 +848,10 @@ impl<'env> Value<'env, JsString> {
} }
pub fn as_str(&self) -> Result<&str> { pub fn as_str(&self) -> Result<&str> {
str::from_utf8(self.get_ref()?).map_err(|_| Error::new(Status::GenericFailure)) str::from_utf8(self.get_ref()?).map_err(|e| Error {
status: Status::GenericFailure,
reason: Some(format!("{:?}", e)),
})
} }
pub fn get_ref_mut(&mut self) -> Result<&mut [u8]> { pub fn get_ref_mut(&mut self) -> Result<&mut [u8]> {
@ -838,7 +860,7 @@ impl<'env> Value<'env, JsString> {
let mut result = Vec::with_capacity(len); let mut result = Vec::with_capacity(len);
unsafe { unsafe {
let status = sys::napi_get_value_string_utf8( let status = sys::napi_get_value_string_utf8(
self.env.0, self.env,
self.raw_value, self.raw_value,
result.as_mut_ptr(), result.as_mut_ptr(),
len as u64, len as u64,
@ -856,16 +878,16 @@ impl<'env> Value<'env, JsString> {
} }
} }
impl<'env> TryFrom<Value<'env, JsString>> for Vec<u16> { impl TryFrom<Value<JsString>> for Vec<u16> {
type Error = Error; type Error = Error;
fn try_from(value: Value<'env, JsString>) -> Result<Vec<u16>> { fn try_from(value: Value<JsString>) -> Result<Vec<u16>> {
let mut result = Vec::with_capacity(value.len()? + 1); // Leave room for trailing null byte let mut result = Vec::with_capacity(value.len()? + 1); // Leave room for trailing null byte
unsafe { unsafe {
let mut written_char_count = 0; let mut written_char_count = 0;
let status = sys::napi_get_value_string_utf16( let status = sys::napi_get_value_string_utf16(
value.env.0, value.env,
value.raw_value, value.raw_value,
result.as_mut_ptr(), result.as_mut_ptr(),
result.capacity() as u64, result.capacity() as u64,
@ -879,63 +901,63 @@ impl<'env> TryFrom<Value<'env, JsString>> for Vec<u16> {
} }
} }
impl<'env> TryFrom<Value<'env, Number>> for usize { impl TryFrom<Value<Number>> for usize {
type Error = Error; type Error = Error;
fn try_from(value: Value<'env, Number>) -> Result<usize> { fn try_from(value: Value<Number>) -> Result<usize> {
let mut result = 0; let mut result = 0;
let status = unsafe { sys::napi_get_value_int64(value.env.0, value.raw_value, &mut result) }; let status = unsafe { sys::napi_get_value_int64(value.env, value.raw_value, &mut result) };
check_status(status)?; check_status(status)?;
Ok(result as usize) Ok(result as usize)
} }
} }
impl<'env> TryFrom<Value<'env, Number>> for u32 { impl TryFrom<Value<Number>> for u32 {
type Error = Error; type Error = Error;
fn try_from(value: Value<'env, Number>) -> Result<u32> { fn try_from(value: Value<Number>) -> Result<u32> {
let mut result = 0; let mut result = 0;
let status = unsafe { sys::napi_get_value_uint32(value.env.0, value.raw_value, &mut result) }; let status = unsafe { sys::napi_get_value_uint32(value.env, value.raw_value, &mut result) };
check_status(status)?; check_status(status)?;
Ok(result) Ok(result)
} }
} }
impl<'env> TryFrom<Value<'env, Number>> for i32 { impl TryFrom<Value<Number>> for i32 {
type Error = Error; type Error = Error;
fn try_from(value: Value<'env, Number>) -> Result<i32> { fn try_from(value: Value<Number>) -> Result<i32> {
let mut result = 0; let mut result = 0;
let status = unsafe { sys::napi_get_value_int32(value.env.0, value.raw_value, &mut result) }; let status = unsafe { sys::napi_get_value_int32(value.env, value.raw_value, &mut result) };
check_status(status)?; check_status(status)?;
Ok(result) Ok(result)
} }
} }
impl<'env> TryFrom<Value<'env, Number>> for i64 { impl TryFrom<Value<Number>> for i64 {
type Error = Error; type Error = Error;
fn try_from(value: Value<'env, Number>) -> Result<i64> { fn try_from(value: Value<Number>) -> Result<i64> {
let mut result = 0; let mut result = 0;
let status = unsafe { sys::napi_get_value_int64(value.env.0, value.raw_value, &mut result) }; let status = unsafe { sys::napi_get_value_int64(value.env, value.raw_value, &mut result) };
check_status(status)?; check_status(status)?;
Ok(result) Ok(result)
} }
} }
impl<'env> TryFrom<Value<'env, Number>> for f64 { impl TryFrom<Value<Number>> for f64 {
type Error = Error; type Error = Error;
fn try_from(value: Value<'env, Number>) -> Result<f64> { fn try_from(value: Value<Number>) -> Result<f64> {
let mut result = 0_f64; let mut result = 0_f64;
let status = unsafe { sys::napi_get_value_double(value.env.0, value.raw_value, &mut result) }; let status = unsafe { sys::napi_get_value_double(value.env, value.raw_value, &mut result) };
check_status(status)?; check_status(status)?;
Ok(result) Ok(result)
} }
} }
impl<'env> Value<'env, Object> { impl Value<Object> {
pub fn set_property<'a, K, V>(&mut self, key: Value<K>, value: Value<V>) -> Result<()> { pub fn set_property<K, V>(&mut self, key: Value<K>, value: Value<V>) -> Result<()> {
let status = unsafe { let status = unsafe {
sys::napi_set_property( sys::napi_set_property(
self.raw_env(), self.raw_env(),
@ -948,11 +970,7 @@ impl<'env> Value<'env, Object> {
Ok(()) Ok(())
} }
pub fn set_named_property<'a, T, V: Into<Value<'a, T>>>( pub fn set_named_property<T, V: Into<Value<T>>>(&mut self, name: &str, value: V) -> Result<()> {
&mut self,
name: &'a str,
value: V,
) -> Result<()> {
let key = CString::new(name)?; let key = CString::new(name)?;
let status = unsafe { let status = unsafe {
sys::napi_set_named_property( sys::napi_set_named_property(
@ -990,7 +1008,7 @@ impl<'env> Value<'env, Object> {
} }
pub fn set_index<'a, T>(&mut self, index: usize, value: Value<T>) -> Result<()> { pub fn set_index<'a, T>(&mut self, index: usize, value: Value<T>) -> Result<()> {
self.set_property(self.env.create_int64(index as i64)?, value) self.set_property(Env::from_raw(self.env).create_int64(index as i64)?, value)
} }
pub fn get_index<T: ValueType>(&self, index: u32) -> Result<Value<T>> { pub fn get_index<T: ValueType>(&self, index: u32) -> Result<Value<T>> {
@ -1015,7 +1033,7 @@ impl<'env> Value<'env, Object> {
Ok(is_buffer) Ok(is_buffer)
} }
pub fn to_buffer(&self) -> Result<Value<'env, Buffer>> { pub fn to_buffer(&self) -> Result<Value<Buffer>> {
Value::from_raw(self.env, self.raw_value) Value::from_raw(self.env, self.raw_value)
} }
@ -1023,6 +1041,7 @@ impl<'env> Value<'env, Object> {
if self.is_array()? != true { if self.is_array()? != true {
return Err(Error { return Err(Error {
status: Status::ArrayExpected, status: Status::ArrayExpected,
reason: Some("Object is not array".to_owned()),
}); });
} }
let mut length: u32 = 0; let mut length: u32 = 0;
@ -1037,17 +1056,17 @@ impl<'env> Value<'env, Object> {
} }
fn raw_env(&self) -> sys::napi_env { fn raw_env(&self) -> sys::napi_env {
self.env.0 self.env
} }
} }
impl<'env> AsRef<[u8]> for Value<'env, Buffer> { impl AsRef<[u8]> for Value<Buffer> {
fn as_ref(&self) -> &[u8] { fn as_ref(&self) -> &[u8] {
self.deref() self.deref()
} }
} }
impl<'env> Deref for Value<'env, Buffer> { impl Deref for Value<Buffer> {
type Target = [u8]; type Target = [u8];
fn deref(&self) -> &[u8] { fn deref(&self) -> &[u8] {
@ -1055,13 +1074,13 @@ impl<'env> Deref for Value<'env, Buffer> {
} }
} }
impl<'env> DerefMut for Value<'env, Buffer> { impl DerefMut for Value<Buffer> {
fn deref_mut(&mut self) -> &mut [u8] { fn deref_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.value.data as *mut _, self.value.size as usize) } unsafe { slice::from_raw_parts_mut(self.value.data as *mut _, self.value.size as usize) }
} }
} }
impl<'env> Deref for Value<'env, ArrayBuffer> { impl Deref for Value<ArrayBuffer> {
type Target = [u8]; type Target = [u8];
fn deref(&self) -> &[u8] { fn deref(&self) -> &[u8] {
@ -1069,22 +1088,26 @@ impl<'env> Deref for Value<'env, ArrayBuffer> {
} }
} }
impl<'env> DerefMut for Value<'env, ArrayBuffer> { impl DerefMut for Value<ArrayBuffer> {
fn deref_mut(&mut self) -> &mut [u8] { fn deref_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.value.data as *mut _, self.value.size as usize) } unsafe { slice::from_raw_parts_mut(self.value.data as *mut _, self.value.size as usize) }
} }
} }
impl<'env> Value<'env, Function> { impl Value<Function> {
pub fn call( pub fn call(&self, this: Option<&Value<Object>>, args: &[Value<Any>]) -> Result<Value<Any>> {
&self,
this: Option<&Value<'env, Object>>,
args: &[Value<'env, Any>],
) -> Result<Value<'env, Any>> {
let raw_this = this let raw_this = this
.map(|v| v.into_raw()) .map(|v| v.into_raw())
.or_else(|| self.env.get_undefined().ok().map(|u| u.into_raw())) .or_else(|| {
.ok_or(Error::new(Status::Unknown))?; Env::from_raw(self.env)
.get_undefined()
.ok()
.map(|u| u.into_raw())
})
.ok_or(Error {
status: Status::Unknown,
reason: Some("Get raw this failed".to_owned()),
})?;
let mut raw_args = unsafe { mem::MaybeUninit::<[sys::napi_value; 8]>::uninit().assume_init() }; let mut raw_args = unsafe { mem::MaybeUninit::<[sys::napi_value; 8]>::uninit().assume_init() };
for (i, arg) in args.into_iter().enumerate() { for (i, arg) in args.into_iter().enumerate() {
raw_args[i] = arg.raw_value; raw_args[i] = arg.raw_value;
@ -1092,7 +1115,7 @@ impl<'env> Value<'env, Function> {
let mut return_value = ptr::null_mut(); let mut return_value = ptr::null_mut();
let status = unsafe { let status = unsafe {
sys::napi_call_function( sys::napi_call_function(
self.env.0, self.env,
raw_this, raw_this,
self.raw_value, self.raw_value,
args.len() as u64, args.len() as u64,
@ -1106,9 +1129,9 @@ impl<'env> Value<'env, Function> {
} }
} }
impl<'env> Value<'env, Any> { impl Value<Any> {
pub fn get_type(&self) -> sys::napi_valuetype { pub fn get_type(&self) -> Result<sys::napi_valuetype> {
get_raw_type(self.env.0, self.raw_value) get_raw_type(self.env, self.raw_value)
} }
} }
@ -1127,34 +1150,6 @@ impl<T> Drop for Ref<T> {
} }
} }
impl AsyncContext {
pub fn enter<'a, F: 'a + FnOnce(&mut Env)>(&'a self, run_in_context: F) {
let mut env = Env::from_raw(self.raw_env);
let mut handle_scope = ptr::null_mut();
let mut callback_scope = ptr::null_mut();
let mut raw_resource = ptr::null_mut();
unsafe {
sys::napi_open_handle_scope(env.0, &mut handle_scope);
sys::napi_get_reference_value(env.0, self.raw_resource, &mut raw_resource);
sys::extras_open_callback_scope(self.raw_context, raw_resource, &mut callback_scope);
}
run_in_context(&mut env);
unsafe {
sys::extras_close_callback_scope(callback_scope);
sys::napi_close_handle_scope(env.0, handle_scope);
}
}
}
impl Drop for AsyncContext {
fn drop(&mut self) {
unsafe {
sys::napi_delete_reference(self.raw_env, self.raw_resource);
}
}
}
impl Property { impl Property {
pub fn new(name: &str) -> Self { pub fn new(name: &str) -> Self {
Property { Property {
@ -1202,11 +1197,12 @@ impl<T: 'static> TaggedObject<T> {
} }
} }
#[inline]
fn check_status(code: sys::napi_status) -> Result<()> { fn check_status(code: sys::napi_status) -> Result<()> {
let status = Status::from(code); let status = Status::from(code);
match status { match status {
Status::Ok => Ok(()), Status::Ok => Ok(()),
_ => Err(Error { status }), _ => Err(Error::from_status(status)),
} }
} }

65
napi/src/promise.rs Normal file
View file

@ -0,0 +1,65 @@
use futures::prelude::*;
use std::os::raw::c_char;
use std::ptr;
use crate::{check_status, sys, Env, Result, Value, ValueType};
#[inline]
pub async fn resolve<
T,
V: ValueType,
R: FnOnce(&mut Env, T) -> Result<Value<V>>,
F: Future<Output = Result<T>>,
>(
env: sys::napi_env,
fut: F,
resolver: R,
raw_deferred: sys::napi_deferred,
) -> Result<()> {
let mut raw_resource = ptr::null_mut();
let status = unsafe { sys::napi_create_object(env, &mut raw_resource) };
check_status(status)?;
let mut raw_name = ptr::null_mut();
let s = "napi_async_context";
let status = unsafe {
sys::napi_create_string_utf8(
env,
s.as_ptr() as *const c_char,
s.len() as u64,
&mut raw_name,
)
};
check_status(status)?;
let mut raw_context = ptr::null_mut();
unsafe {
let status = sys::napi_async_init(env, raw_resource, raw_name, &mut raw_context);
check_status(status)?;
}
let mut handle_scope = ptr::null_mut();
match fut.await {
Ok(v) => unsafe {
check_status(sys::napi_open_handle_scope(env, &mut handle_scope))?;
let mut tmp_env = Env::from_raw(env);
let js_value = resolver(&mut tmp_env, v)?;
check_status(sys::napi_resolve_deferred(
env,
raw_deferred,
js_value.raw_value,
))?;
check_status(sys::napi_close_handle_scope(env, handle_scope))?;
},
Err(e) => unsafe {
check_status(sys::napi_open_handle_scope(env, &mut handle_scope))?;
check_status(sys::napi_reject_deferred(
env,
raw_deferred,
Env::from_raw(env)
.create_error(e)
.map(|e| e.into_raw())
.unwrap_or(ptr::null_mut()),
))?;
check_status(sys::napi_close_handle_scope(env, handle_scope))?;
},
};
Ok(())
}

View file

@ -1,31 +0,0 @@
#include "bindings.h"
#include <stdio.h>
#include <node.h>
#include <v8.h>
static v8::Local<v8::Value> V8LocalValueFromJsValue(napi_value v)
{
v8::Local<v8::Value> local;
memcpy(&local, &v, sizeof(v));
return local;
}
EXTERN_C_START
NAPI_EXTERN void extras_open_callback_scope(napi_async_context napi_async_context,
napi_value napi_resource_object,
extras_callback_scope *result)
{
v8::Isolate *isolate = v8::Isolate::GetCurrent();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Object> resource_object = V8LocalValueFromJsValue(napi_resource_object)->ToObject(context).ToLocalChecked();
node::async_context *node_async_context = reinterpret_cast<node::async_context *>(napi_async_context);
*result = reinterpret_cast<extras_callback_scope>(new node::CallbackScope(isolate, resource_object, *node_async_context));
}
NAPI_EXTERN void extras_close_callback_scope(extras_callback_scope callback_scope)
{
delete reinterpret_cast<node::CallbackScope *>(callback_scope);
}
EXTERN_C_END

View file

@ -1,15 +1,2 @@
#include <node_api.h> #include <node_api.h>
#include <uv.h> #include <uv.h>
#include <string.h>
typedef struct extras_callback_scope__ *extras_callback_scope;
EXTERN_C_START
NAPI_EXTERN void extras_open_callback_scope(napi_async_context napi_async_context,
napi_value napi_resource_object,
extras_callback_scope *result);
NAPI_EXTERN void extras_close_callback_scope(extras_callback_scope callback_scope);
EXTERN_C_END

View file

@ -1,6 +1,6 @@
use super::napi_status; use super::napi_status;
#[derive(Eq, PartialEq, Debug)] #[derive(Eq, PartialEq, Debug, Clone, Copy)]
pub enum Status { pub enum Status {
Ok, Ok,
InvalidArg, InvalidArg,

29
napi/src/version.rs Normal file
View file

@ -0,0 +1,29 @@
use crate::{sys, Error, Status};
use std::convert::TryFrom;
use std::ffi::CStr;
#[derive(Debug, Clone, Copy)]
pub struct NodeVersion {
pub major: u32,
pub minor: u32,
pub patch: u32,
pub release: &'static str,
}
impl TryFrom<sys::napi_node_version> for NodeVersion {
type Error = Error;
fn try_from(value: sys::napi_node_version) -> Result<NodeVersion, Error> {
Ok(NodeVersion {
major: value.major,
minor: value.minor,
patch: value.patch,
release: unsafe {
CStr::from_ptr(value.release).to_str().map_err(|_| Error {
status: Status::StringExpected,
reason: Some("Invalid release name".to_owned()),
})?
},
})
}
}

View file

@ -17,6 +17,13 @@
} }
], ],
"license": "MIT", "license": "MIT",
"scripts": {
"format": "run-p format:md format:json format:yaml format:source",
"format:md": "prettier --parser markdown --write './**/*.md'",
"format:json": "prettier --parser json --write './**/*.json'",
"format:source": "prettier --config ./package.json --write './**/*.js'",
"format:yaml": "prettier --parser yaml --write './**/*.{yml,yaml}'"
},
"bugs": { "bugs": {
"url": "https://github.com/Brooooooklyn/napi-rs/issues" "url": "https://github.com/Brooooooklyn/napi-rs/issues"
}, },
@ -32,12 +39,23 @@
"trailingComma": "all", "trailingComma": "all",
"arrowParens": "always" "arrowParens": "always"
}, },
"files": [ "files": ["scripts/napi.js", "LICENSE"],
"scripts/napi.js", "lint-staged": {
"LICENSE" "*.js": ["prettier --write"],
], "*.@(yml|yaml)": ["prettier --parser yaml --write"],
"*.json": ["prettier --parser json --write"],
"*.md": ["prettier --parser markdown --write"]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged && cargo fmt --all"
}
},
"devDependencies": { "devDependencies": {
"@types/node": "^13.13.5", "@types/node": "^13.13.5",
"husky": "^4.2.5",
"lint-staged": "^10.2.2",
"npm-run-all": "^4.1.5",
"prettier": "^2.0.5" "prettier": "^2.0.5"
} }
} }

View file

@ -11,7 +11,10 @@ let tomlContent
let moduleName let moduleName
try { try {
tomlContentString = fs.readFileSync(path.join(process.cwd(), 'Cargo.toml'), 'utf-8') tomlContentString = fs.readFileSync(
path.join(process.cwd(), 'Cargo.toml'),
'utf-8',
)
} catch { } catch {
throw new TypeError('Can not find Cargo.toml in process.cwd') throw new TypeError('Can not find Cargo.toml in process.cwd')
} }
@ -60,7 +63,9 @@ const targetDir = argv.release ? 'release' : 'debug'
const platformName = argv.platform ? `.${platform}` : '' const platformName = argv.platform ? `.${platform}` : ''
let subcommand = argv._[0] || path.join('target', targetDir, `${moduleName}${platformName}.node`) let subcommand =
argv._[0] ||
path.join('target', targetDir, `${moduleName}${platformName}.node`)
const parsedDist = path.parse(subcommand) const parsedDist = path.parse(subcommand)
if (!parsedDist.name || parsedDist.name === '.') { if (!parsedDist.name || parsedDist.name === '.') {
@ -73,11 +78,13 @@ if (!parsedDist.ext) {
const pos = __dirname.indexOf('node_modules') const pos = __dirname.indexOf('node_modules')
const dylibContent = fs.readFileSync(path.join( const dylibContent = fs.readFileSync(
path.join(
__dirname.substring(0, pos), __dirname.substring(0, pos),
'target', 'target',
targetDir, targetDir,
`${dylibName}${libExt}`, `${dylibName}${libExt}`,
)) ),
)
fs.writeFileSync(subcommand, dylibContent) fs.writeFileSync(subcommand, dylibContent)

View file

@ -1,4 +1,4 @@
const testModule = require(`./target/debug/test_module.node`) const testModule = require('./index.node')
function testSpawn() { function testSpawn() {
console.log('=== Test spawning a future on libuv event loop') console.log('=== Test spawning a future on libuv event loop')
@ -9,19 +9,21 @@ function testThrow() {
console.log('=== Test throwing from Rust') console.log('=== Test throwing from Rust')
try { try {
testModule.testThrow() testModule.testThrow()
} catch (e) {
return
}
console.error('Expected function to throw an error') console.error('Expected function to throw an error')
process.exit(1) process.exit(1)
} catch (e) {
console.error(e)
}
} }
const future = testSpawn() const future = testSpawn()
// https://github.com/nodejs/node/issues/29355 future
setTimeout(() => { .then((value) => {
future.then(testThrow).catch((e) => { console.info(`${value} from napi`)
testThrow()
})
.catch((e) => {
console.error(e) console.error(e)
process.exit(1) process.exit(1)
}) })
}, 1000)

View file

@ -2,8 +2,8 @@
"name": "test-module", "name": "test-module",
"version": "1.0.0", "version": "1.0.0",
"scripts": { "scripts": {
"build": "cargo build && node ../scripts/napi.js", "build": "cargo build && node ../scripts/napi.js ./index",
"build-release": "cargo build --release && node ../scripts/napi.js --release", "build-release": "cargo build --release && node ../scripts/napi.js --release ./index",
"test": "node ./index.js" "test": "node ./index.js"
} }
} }

View file

@ -9,49 +9,36 @@ use napi::{Any, CallContext, Env, Error, Object, Result, Status, Value};
register_module!(test_module, init); register_module!(test_module, init);
fn init<'env>( fn init(env: &Env, exports: &mut Value<Object>) -> Result<Option<Value<Object>>> {
env: &'env Env,
exports: &'env mut Value<'env, Object>,
) -> Result<Option<Value<'env, Object>>> {
exports.set_named_property("testSpawn", env.create_function("testSpawn", test_spawn)?)?; exports.set_named_property("testSpawn", env.create_function("testSpawn", test_spawn)?)?;
exports.set_named_property("testThrow", env.create_function("testThrow", test_throw)?)?; exports.set_named_property("testThrow", env.create_function("testThrow", test_throw)?)?;
Ok(None) Ok(None)
} }
#[js_function] #[js_function]
fn test_spawn<'a>(ctx: CallContext<'a>) -> Result<Value<'a, Object>> { fn test_spawn(ctx: CallContext) -> Result<Value<Object>> {
use futures::executor::ThreadPool; use futures::executor::ThreadPool;
use futures::StreamExt; use futures::StreamExt;
let env = ctx.env; let env = ctx.env;
let async_context = env.async_init(None, "test_spawn")?;
let pool = ThreadPool::new().expect("Failed to build pool"); let pool = ThreadPool::new().expect("Failed to build pool");
let (promise, deferred) = env.create_promise()?;
let (tx, rx) = futures::channel::mpsc::unbounded::<i32>(); let (tx, rx) = futures::channel::mpsc::unbounded::<i32>();
let fut_values = async move { let fut_values = async move {
let fut_tx_result = async move { let fut_tx_result = async move {
(0..20).for_each(|v| { (0..200).for_each(|v| {
tx.unbounded_send(v).expect("Failed to send"); tx.unbounded_send(v).expect("Failed to send");
}) })
}; };
pool.spawn_ok(fut_tx_result); pool.spawn_ok(fut_tx_result);
let fut_values = rx.map(|v| v * 2).collect::<Vec<i32>>(); let fut = rx.map(|v| v * 2).collect::<Vec<i32>>();
let results = fut_values.await; let results = fut.await;
if !cfg!(windows) {
println!("Collected result lenght {}", results.len()); println!("Collected result lenght {}", results.len());
}; Ok(results.len() as u32)
async_context.enter(|env| {
env
.resolve_deferred(deferred, env.get_undefined().unwrap())
.unwrap();
});
}; };
env.create_executor().execute(fut_values); env.perform_async_operation(fut_values, |&mut env, len| env.create_uint32(len))
Ok(promise)
} }
#[js_function] #[js_function]
fn test_throw<'a>(_ctx: CallContext<'a>) -> Result<Value<'a, Any>> { fn test_throw(_ctx: CallContext) -> Result<Value<Any>> {
Err(Error::new(Status::GenericFailure)) Err(Error::from_status(Status::GenericFailure))
} }

1167
yarn.lock

File diff suppressed because it is too large Load diff