feat: export registers in wasm32 target (#1529)
This commit is contained in:
parent
5605bdf7fc
commit
550ef7c3cc
21 changed files with 278 additions and 131 deletions
27
.github/workflows/check-all-features.yml
vendored
27
.github/workflows/check-all-features.yml
vendored
|
@ -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 }}
|
||||
|
|
|
@ -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 intermediateTypeFile = join(
|
||||
tmpdir(),
|
||||
`${cargoArtifactName}-${createHash('sha256')
|
||||
const cwdSha = createHash('sha256')
|
||||
.update(process.cwd())
|
||||
.digest('hex')
|
||||
.substring(0, 8)}.napi_type_def.tmp`,
|
||||
.substring(0, 8)
|
||||
const intermediateTypeFile = join(
|
||||
tmpdir(),
|
||||
`${cargoArtifactName}-${cwdSha}.napi_type_def.tmp`,
|
||||
)
|
||||
const intermediateWasiRegisterFile = join(
|
||||
tmpdir(),
|
||||
`${cargoArtifactName}-${cwdSha}.napi_wasi_register.tmp`,
|
||||
)
|
||||
debug(`intermediate type def file: ${intermediateTypeFile}`)
|
||||
|
||||
try {
|
||||
execSync(cargoCommand, {
|
||||
env: {
|
||||
const commandEnv = {
|
||||
...process.env,
|
||||
...additionalEnv,
|
||||
TYPE_DEF_TMP_PATH: intermediateTypeFile,
|
||||
},
|
||||
WASI_REGISTER_TMP_PATH: intermediateWasiRegisterFile,
|
||||
}
|
||||
|
||||
try {
|
||||
execSync(cargoCommand, {
|
||||
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'),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),*]);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(|| {
|
||||
#[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)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue