feat: export registers in wasm32 target (#1529)

This commit is contained in:
LongYinan 2023-03-20 18:42:27 +08:00 committed by GitHub
parent 5605bdf7fc
commit 550ef7c3cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 278 additions and 131 deletions

View file

@ -15,21 +15,11 @@ jobs:
strategy:
fail-fast: false
matrix:
features:
[
'napi3',
'napi4',
'napi5',
'napi6',
'napi7',
'napi8',
'experimental',
'async',
'chrono_date',
'latin1',
'full',
]
settings:
- features: 'napi1,napi2,napi3,napi4,napi5,napi6,napi7,napi8,experimental,async,chrono_date,latin1,full'
package: 'napi'
- features: 'compat-mode,strict,type-def,noop,full,default'
package: 'napi-derive'
name: stable - ubuntu-latest
runs-on: ubuntu-latest
@ -47,10 +37,7 @@ jobs:
path: |
~/.cargo/registry
~/.cargo/git
key: stable-check-ubuntu-latest-cargo-cache-features-${{ matrix.features }}
key: stable-check-ubuntu-latest-cargo-cache-features-${{ matrix.settings.package }}-${{ matrix.features }}
- name: Check build
uses: actions-rs/cargo@v1
with:
command: check
args: -p napi --no-default-features --features ${{ matrix.features }}
run: cargo check -p ${{ matrix.settings.package }} -F ${{ matrix.settings.features }}

View file

@ -311,10 +311,6 @@ export class BuildCommand extends Command {
rustflags.push('-C link-arg=-s')
}
if (rustflags.length > 0) {
additionalEnv['RUSTFLAGS'] = rustflags.join(' ')
}
let useZig = false
if (this.useZig || isCrossForLinux || isCrossForMacOS) {
try {
@ -452,6 +448,22 @@ export class BuildCommand extends Command {
tsConstEnum: tsConstEnumFromConfig,
} = getNapiConfig(this.configFileName)
const tsConstEnum = this.constEnum ?? tsConstEnumFromConfig
if (triple.platform === 'wasi') {
try {
const emnapiDir = require.resolve('emnapi')
const linkDir = join(emnapiDir, '..', 'lib', 'wasm32-wasi')
additionalEnv['EMNAPI_LINK_DIR'] = linkDir
rustflags.push('-Z wasi-exec-model=reactor')
} catch (e) {
const err = new Error(`Could not find emnapi, please install emnapi`)
err.cause = e
throw err
}
}
if (rustflags.length > 0) {
additionalEnv['RUSTFLAGS'] = rustflags.join(' ')
}
let cargoArtifactName = this.cargoName
if (!cargoArtifactName) {
if (this.bin) {
@ -479,23 +491,30 @@ export class BuildCommand extends Command {
} else {
debug(`Dylib name: ${chalk.greenBright(cargoArtifactName)}`)
}
const cwdSha = createHash('sha256')
.update(process.cwd())
.digest('hex')
.substring(0, 8)
const intermediateTypeFile = join(
tmpdir(),
`${cargoArtifactName}-${createHash('sha256')
.update(process.cwd())
.digest('hex')
.substring(0, 8)}.napi_type_def.tmp`,
`${cargoArtifactName}-${cwdSha}.napi_type_def.tmp`,
)
const intermediateWasiRegisterFile = join(
tmpdir(),
`${cargoArtifactName}-${cwdSha}.napi_wasi_register.tmp`,
)
debug(`intermediate type def file: ${intermediateTypeFile}`)
const commandEnv = {
...process.env,
...additionalEnv,
TYPE_DEF_TMP_PATH: intermediateTypeFile,
WASI_REGISTER_TMP_PATH: intermediateWasiRegisterFile,
}
try {
execSync(cargoCommand, {
env: {
...process.env,
...additionalEnv,
TYPE_DEF_TMP_PATH: intermediateTypeFile,
},
env: commandEnv,
stdio: 'inherit',
cwd,
})
@ -606,18 +625,6 @@ export class BuildCommand extends Command {
this.dts ?? 'index.d.ts',
)
if (this.pipe) {
const pipeCommand = `${this.pipe} ${dtsFilePath}`
console.info(`Run ${chalk.green(pipeCommand)}`)
try {
execSync(pipeCommand, { stdio: 'inherit', env: process.env })
} catch (e) {
console.warn(
chalk.bgYellowBright('Pipe the dts file to command failed'),
e,
)
}
}
const jsBindingFilePath =
this.jsBinding &&
this.jsBinding !== 'false' &&
@ -640,7 +647,7 @@ export class BuildCommand extends Command {
const pipeCommand = `${this.pipe} ${jsBindingFilePath}`
console.info(`Run ${chalk.green(pipeCommand)}`)
try {
execSync(pipeCommand, { stdio: 'inherit', env: process.env })
execSync(pipeCommand, { stdio: 'inherit', env: commandEnv })
} catch (e) {
console.warn(
chalk.bgYellowBright('Pipe the js binding file to command failed'),

View file

@ -1,7 +1,13 @@
export const createJsBinding = (
localName: string,
pkgName: string,
) => `const { existsSync, readFileSync } = require('fs')
) => `/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */
/* auto-generated by NAPI-RS */
const { existsSync, readFileSync } = require('fs')
const { join } = require('path')
const { platform, arch } = process

View file

@ -10,10 +10,10 @@ import { debugFactory } from '../debug'
import { DefaultPlatforms } from '../parse-triple'
import { spawn } from '../spawn'
import { GitIgnore } from './.gitignore-template'
import { createCargoContent } from './cargo'
import { createCargoConfig } from './cargo-config'
import { createGithubActionsCIYml } from './ci-yml'
import { GitIgnore } from './gitignore-template'
import { LibRs } from './lib-rs'
import { NPMIgnoreFiles } from './npmignore'
import { createPackageJson } from './package'

View file

@ -14,6 +14,7 @@ type NodeJSArch =
| 'x32'
| 'x64'
| 'universal'
| 'wasm32'
const CpuToNodeArch: { [index: string]: NodeJSArch } = {
x86_64: 'x64',
@ -41,7 +42,7 @@ export const UniArchsByPlatform: Record<string, NodeJSArch[]> = {
}
export interface PlatformDetail {
platform: NodeJS.Platform
platform: NodeJS.Platform | 'wasi'
platformArchABI: string
arch: NodeJSArch
raw: string

View file

@ -1,3 +1,5 @@
use std::sync::atomic::AtomicUsize;
use proc_macro2::{Ident, Span, TokenStream};
use crate::BindgenResult;
@ -12,6 +14,12 @@ pub const PROPERTY_ATTRIBUTE_WRITABLE: i32 = 1 << 0;
pub const PROPERTY_ATTRIBUTE_ENUMERABLE: i32 = 1 << 1;
pub const PROPERTY_ATTRIBUTE_CONFIGURABLE: i32 = 1 << 2;
static REGISTER_INDEX: AtomicUsize = AtomicUsize::new(0);
thread_local! {
pub static REGISTER_IDENTS: std::cell::RefCell<Vec<String>> = std::cell::RefCell::new(Vec::new());
}
pub trait TryToTokens {
fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()>;
@ -29,7 +37,11 @@ fn get_intermediate_ident(name: &str) -> Ident {
}
fn get_register_ident(name: &str) -> Ident {
let new_name = format!("__napi_register__{}", name);
let new_name = format!(
"__napi_register__{}_{}",
name,
REGISTER_INDEX.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
);
Ident::new(&new_name, Span::call_site())
}

View file

@ -30,6 +30,9 @@ impl NapiConst {
self.name.span(),
);
let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
crate::codegen::REGISTER_IDENTS.with(|c| {
c.borrow_mut().push(register_name.to_string());
});
quote! {
#[allow(non_snake_case)]
#[allow(clippy::all)]
@ -38,11 +41,19 @@ impl NapiConst {
}
#[allow(non_snake_case)]
#[allow(clippy::all)]
#[cfg(all(not(test), not(feature = "noop")))]
#[cfg(all(not(test), not(feature = "noop"), not(target_arch = "wasm32")))]
#[napi::bindgen_prelude::ctor]
fn #register_name() {
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #cb_name);
}
#[allow(non_snake_case)]
#[allow(clippy::all)]
#[cfg(all(not(test), not(feature = "noop"), target_arch = "wasm32"))]
#[no_mangle]
unsafe extern "C" fn #register_name() {
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #cb_name);
}
}
}
}

View file

@ -130,6 +130,10 @@ impl NapiEnum {
let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
crate::codegen::REGISTER_IDENTS.with(|c| {
c.borrow_mut().push(register_name.to_string());
});
quote! {
#[allow(non_snake_case)]
#[allow(clippy::all)]
@ -150,11 +154,18 @@ impl NapiEnum {
}
#[allow(non_snake_case)]
#[allow(clippy::all)]
#[cfg(all(not(test), not(feature = "noop")))]
#[cfg(all(not(test), not(feature = "noop"), not(target_arch = "wasm32")))]
#[napi::bindgen_prelude::ctor]
fn #register_name() {
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #callback_name);
}
#[allow(non_snake_case)]
#[allow(clippy::all)]
#[cfg(all(not(test), not(feature = "noop"), target_arch = "wasm32"))]
#[no_mangle]
extern "C" fn #register_name() {
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #callback_name);
}
}
}
}

View file

@ -474,7 +474,7 @@ impl NapiFn {
match self.fn_self {
Some(FnSelf::Value) => {
// impossible, panic! in parser
unimplemented!();
unreachable!();
}
Some(FnSelf::Ref) | Some(FnSelf::MutRef) => quote! { this.#name },
None => match &self.parent {
@ -552,11 +552,14 @@ impl NapiFn {
} else {
let name_str = self.name.to_string();
let js_name = format!("{}\0", &self.js_name);
let name_len = js_name.len();
let name_len = self.js_name.len();
let module_register_name = get_register_ident(&name_str);
let intermediate_ident = get_intermediate_ident(&name_str);
let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
let cb_name = Ident::new(&format!("{}_js_function", name_str), Span::call_site());
crate::codegen::REGISTER_IDENTS.with(|c| {
c.borrow_mut().push(module_register_name.to_string());
});
quote! {
#[allow(non_snake_case)]
#[allow(clippy::all)]
@ -566,7 +569,7 @@ impl NapiFn {
napi::bindgen_prelude::check_status!(
napi::bindgen_prelude::sys::napi_create_function(
env,
#js_name.as_ptr() as *const _,
#js_name.as_ptr().cast(),
#name_len,
Some(#intermediate_ident),
std::ptr::null_mut(),
@ -581,11 +584,19 @@ impl NapiFn {
#[allow(clippy::all)]
#[allow(non_snake_case)]
#[cfg(all(not(test), not(feature = "noop")))]
#[cfg(all(not(test), not(feature = "noop"), not(target_arch = "wasm32")))]
#[napi::bindgen_prelude::ctor]
fn #module_register_name() {
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name);
}
#[allow(clippy::all)]
#[allow(non_snake_case)]
#[cfg(all(not(test), not(feature = "noop"), target_arch = "wasm32"))]
#[no_mangle]
extern "C" fn #module_register_name() {
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name);
}
}
}
}

View file

@ -760,14 +760,25 @@ impl NapiStruct {
props.push(prop);
}
let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
crate::codegen::REGISTER_IDENTS.with(|c| {
c.borrow_mut().push(struct_register_name.to_string());
});
quote! {
#[allow(non_snake_case)]
#[allow(clippy::all)]
#[cfg(all(not(test), not(feature = "noop")))]
#[cfg(all(not(test), not(feature = "noop"), not(target_arch = "wasm32")))]
#[napi::bindgen_prelude::ctor]
fn #struct_register_name() {
napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
}
#[allow(non_snake_case)]
#[allow(clippy::all)]
#[cfg(all(not(test), not(feature = "noop"), target_arch = "wasm32"))]
#[no_mangle]
extern "C" fn #struct_register_name() {
napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
}
}
}
@ -866,7 +877,11 @@ impl NapiImpl {
let mut props: Vec<_> = props.into_iter().collect();
props.sort_by_key(|(_, prop)| prop.to_string());
let props = props.into_iter().map(|(_, prop)| prop);
let props_wasm = props.clone();
let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
crate::codegen::REGISTER_IDENTS.with(|c| {
c.borrow_mut().push(register_name.to_string());
});
Ok(quote! {
#[allow(non_snake_case)]
#[allow(clippy::all)]
@ -874,11 +889,17 @@ impl NapiImpl {
use super::*;
#(#methods)*
#[cfg(all(not(test), not(feature = "noop")))]
#[cfg(all(not(test), not(feature = "noop"), not(target_arch = "wasm32")))]
#[napi::bindgen_prelude::ctor]
fn #register_name() {
napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
}
#[cfg(all(not(test), not(feature = "noop"), target_arch = "wasm32"))]
#[no_mangle]
extern "C" fn #register_name() {
napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props_wasm),*]);
}
}
})
}

View file

@ -11,16 +11,20 @@ extern crate quote;
#[cfg(not(feature = "noop"))]
use std::env;
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(feature = "noop"))]
use std::fs;
#[cfg(not(feature = "noop"))]
use std::io::Write;
#[cfg(all(feature = "type-def", not(feature = "noop")))]
use std::{
fs,
io::{BufWriter, Result as IOResult, Write},
};
use std::io::{BufWriter, Result as IOResult};
#[cfg(not(feature = "noop"))]
use std::sync::atomic::{AtomicBool, Ordering};
use napi_derive_backend::BindgenResult;
#[cfg(not(feature = "noop"))]
use napi_derive_backend::TryToTokens;
#[cfg(not(feature = "noop"))]
use napi_derive_backend::REGISTER_IDENTS;
#[cfg(all(feature = "type-def", not(feature = "noop")))]
use napi_derive_backend::{ToTypeDef, TypeDef};
#[cfg(not(feature = "noop"))]
@ -36,6 +40,7 @@ use syn::Item;
#[cfg(feature = "compat-mode")]
use syn::{fold::Fold, parse_macro_input, ItemFn};
#[cfg(not(feature = "noop"))]
/// a flag indicate whether or never at least one `napi` macro has been expanded.
/// ```ignore
/// if BUILT_FLAG
@ -64,8 +69,18 @@ pub fn napi(attr: RawStream, input: RawStream) -> RawStream {
#[cfg(feature = "type-def")]
if let Ok(type_def_file) = env::var("TYPE_DEF_TMP_PATH") {
if let Err(_e) = fs::remove_file(type_def_file) {
// should only input in debug mode
// println!("Failed to manipulate type def file: {:?}", e);
#[cfg(debug_assertions)]
{
println!("Failed to manipulate type def file: {:?}", _e);
}
}
}
if let Ok(wasi_register_file) = env::var("WASI_REGISTER_TMP_PATH") {
if let Err(_e) = fs::remove_file(wasi_register_file) {
#[cfg(debug_assertions)]
{
println!("Failed to manipulate wasi register file: {:?}", _e);
}
}
}
}
@ -75,6 +90,15 @@ pub fn napi(attr: RawStream, input: RawStream) -> RawStream {
if env::var("DEBUG_GENERATED_CODE").is_ok() {
println!("{}", tokens);
}
REGISTER_IDENTS.with(|idents| {
if let Ok(wasi_register_file) = env::var("WASI_REGISTER_TMP_PATH") {
let mut file =
fs::File::create(wasi_register_file).expect("Create wasi register file failed");
file
.write_all(format!("{:?}", idents.borrow()).as_bytes())
.expect("Write wasi register file failed");
}
});
tokens.into()
}
Err(diagnostic) => {
@ -338,7 +362,7 @@ pub fn module_exports(_attr: RawStream, input: RawStream) -> RawStream {
};
let register = quote! {
#[napi::bindgen_prelude::ctor]
#[cfg_attr(not(target_arch = "wasm32"), napi::bindgen_prelude::ctor)]
fn __napi__explicit_module_register() {
unsafe fn register(raw_env: napi::sys::napi_env, raw_exports: napi::sys::napi_value) -> napi::Result<()> {
use napi::{Env, JsObject, NapiValue};

View file

@ -69,10 +69,16 @@ version = "0.8"
optional = true
version = "0.4"
[dependencies.tokio]
features = ["rt", "rt-multi-thread", "sync"]
optional = true
version = "1"
[target.'cfg(target_arch = "wasm32")'.dependencies]
tokio = { version = "1", optional = true, features = ["rt", "sync"] }
napi-derive = { path = "../macro", version = "2.10.1", default-features = false }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1", optional = true, features = [
"rt",
"rt-multi-thread",
"sync",
] }
[dependencies.serde]
optional = true

View file

@ -77,7 +77,7 @@ pub fn run<T: Task>(
complete::<T>
as unsafe extern "C" fn(env: sys::napi_env, status: sys::napi_status, data: *mut c_void),
),
result as *mut _ as *mut c_void,
(result as *mut AsyncWork<T>).cast(),
&mut result.napi_async_work,
)
})?;
@ -91,10 +91,8 @@ pub fn run<T: Task>(
})
}
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl<T: Task> Send for AsyncWork<T> {}
unsafe impl<T: Task> Sync for AsyncWork<T> {}
unsafe impl<T: Task + Send> Send for AsyncWork<T> {}
unsafe impl<T: Task + Sync> Sync for AsyncWork<T> {}
/// env here is the same with the one in `CallContext`.
/// So it actually could do nothing here, because `execute` function is called in the other thread mostly.

View file

@ -7,7 +7,7 @@ use std::sync::{
Arc,
};
#[cfg(feature = "napi4")]
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
use crate::bindgen_prelude::{CUSTOM_GC_TSFN, CUSTOM_GC_TSFN_CLOSED, MAIN_THREAD_ID};
pub use crate::js_values::TypedArrayType;
use crate::{check_status, sys, Error, Result, Status};
@ -66,7 +66,7 @@ macro_rules! impl_typed_array {
fn drop(&mut self) {
if Arc::strong_count(&self.drop_in_vm) == 1 {
if let Some((ref_, env)) = self.raw {
#[cfg(feature = "napi4")]
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
{
if CUSTOM_GC_TSFN_CLOSED.load(std::sync::atomic::Ordering::SeqCst) {
return;

View file

@ -9,7 +9,7 @@ use std::sync::Arc;
#[cfg(all(debug_assertions, not(windows)))]
use std::sync::Mutex;
#[cfg(feature = "napi4")]
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
use crate::bindgen_prelude::{CUSTOM_GC_TSFN, CUSTOM_GC_TSFN_CLOSED, MAIN_THREAD_ID};
use crate::{bindgen_prelude::*, check_status, sys, Result, ValueType};
@ -34,7 +34,7 @@ impl Drop for Buffer {
fn drop(&mut self) {
if Arc::strong_count(&self.ref_count) == 1 {
if let Some((ref_, env)) = self.raw {
#[cfg(feature = "napi4")]
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
{
if CUSTOM_GC_TSFN_CLOSED.load(std::sync::atomic::Ordering::SeqCst) {
return;

View file

@ -1,30 +1,33 @@
use std::ffi::CStr;
use std::ffi::{c_void, CStr};
use std::future;
use std::pin::Pin;
use std::ptr;
use std::sync::{
atomic::{AtomicBool, Ordering},
atomic::{AtomicBool, AtomicPtr, Ordering},
Arc,
};
use std::task::{Context, Poll};
use std::task::{Context, Poll, Waker};
use tokio::sync::oneshot::{channel, Receiver, Sender};
use crate::{check_status, sys, Error, JsUnknown, NapiValue, Result, Status};
use crate::{check_status, sys, Error, JsUnknown, NapiValue, Result};
use super::{FromNapiValue, TypeName, ValidateNapiValue};
pub struct Promise<T: FromNapiValue> {
value: Pin<Box<Receiver<*mut Result<T>>>>,
aborted: Arc<AtomicBool>,
struct PromiseInner<T: FromNapiValue> {
value: AtomicPtr<Result<T>>,
waker: AtomicPtr<Waker>,
aborted: AtomicBool,
}
impl<T: FromNapiValue> Drop for Promise<T> {
impl<T: FromNapiValue> Drop for PromiseInner<T> {
fn drop(&mut self) {
self.aborted.store(true, Ordering::SeqCst);
}
}
pub struct Promise<T: FromNapiValue> {
inner: Arc<PromiseInner<T>>,
}
impl<T: FromNapiValue> TypeName for Promise<T> {
fn type_name() -> &'static str {
"Promise"
@ -100,9 +103,13 @@ impl<T: FromNapiValue> FromNapiValue for Promise<T> {
)?;
let mut promise_after_then = ptr::null_mut();
let mut then_js_cb = ptr::null_mut();
let (tx, rx) = channel();
let aborted = Arc::new(AtomicBool::new(false));
let tx_ptr = Box::into_raw(Box::new((tx, aborted.clone())));
let promise_inner = PromiseInner {
value: AtomicPtr::new(ptr::null_mut()),
waker: AtomicPtr::new(ptr::null_mut()),
aborted: AtomicBool::new(false),
};
let shared_inner = Arc::new(promise_inner);
let context_ptr = Arc::into_raw(shared_inner.clone());
check_status!(
unsafe {
sys::napi_create_function(
@ -110,7 +117,7 @@ impl<T: FromNapiValue> FromNapiValue for Promise<T> {
then_c_string.as_ptr(),
4,
Some(then_callback::<T>),
tx_ptr.cast(),
context_ptr as *mut c_void,
&mut then_js_cb,
)
},
@ -145,7 +152,7 @@ impl<T: FromNapiValue> FromNapiValue for Promise<T> {
catch_c_string.as_ptr(),
5,
Some(catch_callback::<T>),
tx_ptr.cast(),
context_ptr as *mut c_void,
&mut catch_js_cb,
)
},
@ -165,8 +172,7 @@ impl<T: FromNapiValue> FromNapiValue for Promise<T> {
"Failed to call catch method"
)?;
Ok(Promise {
value: Box::pin(rx),
aborted,
inner: shared_inner,
})
}
}
@ -174,13 +180,19 @@ impl<T: FromNapiValue> FromNapiValue for Promise<T> {
impl<T: FromNapiValue> future::Future for Promise<T> {
type Output = Result<T>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.value.as_mut().poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(v) => Poll::Ready(
v.map_err(|e| Error::new(Status::GenericFailure, format!("{}", e)))
.and_then(|v| unsafe { *Box::from_raw(v) }.map_err(Error::from)),
),
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.inner.value.load(Ordering::Relaxed).is_null() {
if self.inner.waker.load(Ordering::Acquire).is_null() {
self.inner.waker.store(
Box::into_raw(Box::new(cx.waker().clone())),
Ordering::Release,
);
}
Poll::Pending
} else {
Poll::Ready(
unsafe { Box::from_raw(self.inner.value.load(Ordering::Relaxed)) }.map_err(Error::from),
)
}
}
}
@ -206,15 +218,20 @@ unsafe extern "C" fn then_callback<T: FromNapiValue>(
get_cb_status == sys::Status::napi_ok,
"Get callback info from Promise::then failed"
);
let (sender, aborted) =
*unsafe { Box::from_raw(data as *mut (Sender<*mut Result<T>>, Arc<AtomicBool>)) };
let PromiseInner {
value,
waker,
aborted,
} = &*unsafe { Arc::from_raw(data as *mut PromiseInner<T>) };
if aborted.load(Ordering::SeqCst) {
return this;
}
let resolve_value_t = Box::new(unsafe { T::from_napi_value(env, resolved_value[0]) });
sender
.send(Box::into_raw(resolve_value_t))
.expect("Send Promise resolved value error");
value.store(Box::into_raw(resolve_value_t), Ordering::Relaxed);
let waker = waker.load(Ordering::Acquire);
if !waker.is_null() {
unsafe { Box::from_raw(waker) }.wake();
}
this
}
@ -241,15 +258,23 @@ unsafe extern "C" fn catch_callback<T: FromNapiValue>(
"Get callback info from Promise::catch failed"
);
let rejected_value = rejected_value[0];
let (sender, aborted) =
*unsafe { Box::from_raw(data as *mut (Sender<*mut Result<T>>, Arc<AtomicBool>)) };
let PromiseInner {
value,
waker,
aborted,
} = &*unsafe { Arc::from_raw(data as *mut PromiseInner<T>) };
if aborted.load(Ordering::SeqCst) {
return this;
}
sender
.send(Box::into_raw(Box::new(Err(Error::from(unsafe {
value.store(
Box::into_raw(Box::new(Err(Error::from(unsafe {
JsUnknown::from_raw_unchecked(env, rejected_value)
})))))
.expect("Send Promise resolved value error");
})))),
Ordering::Relaxed,
);
let waker = waker.load(Ordering::Acquire);
if !waker.is_null() {
unsafe { Box::from_raw(waker) }.wake();
}
this
}

View file

@ -117,14 +117,14 @@ static IS_FIRST_MODULE: AtomicBool = AtomicBool::new(true);
static FIRST_MODULE_REGISTERED: AtomicBool = AtomicBool::new(false);
static REGISTERED_CLASSES: Lazy<RegisteredClassesMap> = Lazy::new(Default::default);
static FN_REGISTER_MAP: Lazy<FnRegisterMap> = Lazy::new(Default::default);
#[cfg(feature = "napi4")]
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
pub(crate) static CUSTOM_GC_TSFN: AtomicPtr<sys::napi_threadsafe_function__> =
AtomicPtr::new(ptr::null_mut());
#[cfg(feature = "napi4")]
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
// CustomGC ThreadsafeFunction may be deleted during the process exit.
// And there may still some Buffer alive after that.
pub(crate) static CUSTOM_GC_TSFN_CLOSED: AtomicBool = AtomicBool::new(false);
#[cfg(feature = "napi4")]
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
pub(crate) static MAIN_THREAD_ID: once_cell::sync::OnceCell<ThreadId> =
once_cell::sync::OnceCell::new();
@ -288,6 +288,15 @@ fn load_host() {
}
}
#[cfg(target_arch = "wasm32")]
#[no_mangle]
unsafe extern "C" fn napi_register_wasm_v1(
env: sys::napi_env,
exports: sys::napi_value,
) -> sys::napi_value {
unsafe { napi_register_module_v1(env, exports) }
}
#[no_mangle]
unsafe extern "C" fn napi_register_module_v1(
env: sys::napi_env,
@ -488,7 +497,7 @@ unsafe extern "C" fn napi_register_module_v1(
)
};
}
#[cfg(feature = "napi4")]
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
create_custom_gc(env);
FIRST_MODULE_REGISTERED.store(true, Ordering::SeqCst);
exports
@ -511,7 +520,7 @@ pub(crate) unsafe extern "C" fn noop(
ptr::null_mut()
}
#[cfg(feature = "napi4")]
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
fn create_custom_gc(env: sys::napi_env) {
use std::os::raw::c_char;
@ -572,13 +581,13 @@ fn create_custom_gc(env: sys::napi_env) {
MAIN_THREAD_ID.get_or_init(|| std::thread::current().id());
}
#[cfg(feature = "napi4")]
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
#[allow(unused)]
unsafe extern "C" fn empty(env: sys::napi_env, info: sys::napi_callback_info) -> sys::napi_value {
ptr::null_mut()
}
#[cfg(feature = "napi4")]
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
#[allow(unused)]
unsafe extern "C" fn custom_gc_finalize(
env: sys::napi_env,
@ -588,7 +597,7 @@ unsafe extern "C" fn custom_gc_finalize(
CUSTOM_GC_TSFN_CLOSED.store(true, Ordering::SeqCst);
}
#[cfg(feature = "napi4")]
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
// recycle the ArrayBuffer/Buffer Reference if the ArrayBuffer/Buffer is not dropped on the main thread
extern "C" fn custom_gc(
env: sys::napi_env,

View file

@ -567,7 +567,7 @@ impl Env {
pub fn create_function_from_closure<R, F>(&self, name: &str, callback: F) -> Result<JsFunction>
where
F: 'static + Fn(crate::CallContext<'_>) -> Result<R>,
R: NapiRaw,
R: ToNapiValue,
{
use crate::CallContext;
let boxed_callback = Box::new(callback);
@ -582,7 +582,7 @@ impl Env {
name.as_ptr(),
len,
Some({
unsafe extern "C" fn trampoline<R: NapiRaw, F: Fn(CallContext<'_>) -> Result<R>>(
unsafe extern "C" fn trampoline<R: ToNapiValue, F: Fn(CallContext<'_>) -> Result<R>>(
raw_env: sys::napi_env,
cb_info: sys::napi_callback_info,
) -> sys::napi_value {
@ -636,7 +636,8 @@ impl Env {
};
let env = &mut unsafe { Env::from_raw(raw_env) };
let ctx = CallContext::new(env, cb_info, raw_this, raw_args, raw_args.len());
closure(ctx).map(|ret: R| unsafe { ret.raw() })
closure(ctx)
.and_then(|ret: R| unsafe { <R as ToNapiValue>::to_napi_value(env.0, ret) })
}))
.map_err(|e| {
Error::from_reason(format!(

View file

@ -674,7 +674,7 @@ unsafe extern "C" fn call_js_cb<T: 'static, V: ToNapiValue, R, ES>(
let message_length = message.len();
unsafe {
sys::napi_fatal_error(
"threadsafe_function.rs:573\0".as_ptr().cast(),
"threadsafe_function.rs:642\0".as_ptr().cast(),
26,
CString::new(message).unwrap().into_raw(),
message_length,

View file

@ -6,8 +6,19 @@ use tokio::runtime::Runtime;
use crate::{sys, JsDeferred, JsUnknown, NapiValue, Result};
pub(crate) static mut RT: Lazy<Option<Runtime>> = Lazy::new(|| {
let runtime = tokio::runtime::Runtime::new().expect("Create tokio runtime failed");
Some(runtime)
#[cfg(not(target_arch = "wasm32"))]
{
let runtime = tokio::runtime::Runtime::new().expect("Create tokio runtime failed");
Some(runtime)
}
#[cfg(target_arch = "wasm32")]
{
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.ok()
}
});
#[cfg(windows)]
@ -74,14 +85,20 @@ pub fn execute_tokio_future<
) -> Result<sys::napi_value> {
let (deferred, promise) = JsDeferred::new(env)?;
spawn(async move {
let inner = async move {
match fut.await {
Ok(v) => deferred.resolve(|env| {
resolver(env.raw(), v).map(|v| unsafe { JsUnknown::from_raw_unchecked(env.raw(), v) })
}),
Err(e) => deferred.reject(e),
}
});
};
#[cfg(not(target_arch = "wasm32"))]
spawn(inner);
#[cfg(target_arch = "wasm32")]
block_on(inner);
Ok(promise.0.value)
}