fix(napi-derive,cli): register function cross crates (#1848)

This commit is contained in:
LongYinan 2023-12-11 00:36:26 +08:00 committed by GitHub
parent 589d95994a
commit dab4ce7fe0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 532 additions and 763 deletions

View file

@ -609,18 +609,27 @@ class Builder {
const intermediateWasiRegisterFile = this.envs.WASI_REGISTER_TMP_PATH
const wasiRegisterFunctions =
this.target.arch === 'wasm32'
? JSON.parse(
await readFileAsync(intermediateWasiRegisterFile, 'utf8').catch(
(err) => {
console.warn(
`Read ${colors.yellowBright(
intermediateWasiRegisterFile,
)} failed, reason: ${err.message}`,
)
return `[]`
},
),
)
? await (async function readIntermediateWasiRegisterFile() {
const fileContent = await readFileAsync(
intermediateWasiRegisterFile,
'utf8',
).catch((err) => {
console.warn(
`Read ${colors.yellowBright(
intermediateWasiRegisterFile,
)} failed, reason: ${err.message}`,
)
return ``
})
return fileContent
.split('\n')
.map((l) => l.trim())
.filter((l) => l.length)
.map((line) => {
const [_, fn] = line.split(':')
return fn.trim()
})
})()
: []
const jsOutput = await this.writeJsBinding(idents)
const wasmOutput = await this.writeWasiBinding(
@ -780,18 +789,14 @@ class Builder {
const { name, dir } = parse(distFileName)
const newPath = join(dir, `${this.config.binaryName}.wasi.cjs`)
const workerPath = join(dir, 'wasi-worker.mjs')
const declareCodes = `const { ${idents.join(
', ',
)} } = __napiModule.exports\n`
const exportsCode = idents
.map((ident) => `module.exports.${ident} = ${ident}`)
.map(
(ident) => `module.exports.${ident} = __napiModule.exports.${ident}`,
)
.join(',\n')
await writeFileAsync(
newPath,
createWasiBinding(name, wasiRegisterFunctions) +
declareCodes +
exportsCode +
'\n',
createWasiBinding(name, wasiRegisterFunctions) + exportsCode + '\n',
'utf8',
)
await writeFileAsync(workerPath, WASI_WORKER_TEMPLATE, 'utf8')

View file

@ -56,7 +56,7 @@ const { instance: __napiInstance, module: __wasiModule, napiModule: __napiModule
function __napi_rs_initialize_modules(__napiInstance) {
${wasiRegisterFunctions
.map((name) => ` __napiInstance.exports['${name}']()`)
.map((name) => ` __napiInstance.exports['${name}']?.()`)
.join('\n')}
}
`

View file

@ -28,6 +28,7 @@ pub struct NapiFn {
pub configurable: bool,
pub catch_unwind: bool,
pub unsafe_: bool,
pub register_name: Ident,
}
#[derive(Debug, Clone)]
@ -86,6 +87,7 @@ pub struct NapiStruct {
pub comments: Vec<String>,
pub implement_iterator: bool,
pub use_custom_finalize: bool,
pub register_name: Ident,
}
#[derive(Debug, Clone, PartialEq, Eq)]
@ -121,6 +123,7 @@ pub struct NapiImpl {
pub iterator_return_type: Option<Type>,
pub js_mod: Option<String>,
pub comments: Vec<String>,
pub register_name: Ident,
}
#[derive(Debug, Clone)]
@ -131,6 +134,7 @@ pub struct NapiEnum {
pub js_mod: Option<String>,
pub comments: Vec<String>,
pub skip_typescript: bool,
pub register_name: Ident,
}
#[derive(Debug, Clone)]
@ -164,6 +168,7 @@ pub struct NapiConst {
pub js_mod: Option<String>,
pub comments: Vec<String>,
pub skip_typescript: bool,
pub register_name: Ident,
}
#[derive(Debug, Clone)]

View file

@ -1,5 +1,3 @@
use std::sync::atomic::AtomicUsize;
use proc_macro2::{Ident, Span, TokenStream};
use crate::BindgenResult;
@ -14,12 +12,6 @@ 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<()>;
@ -36,15 +28,6 @@ fn get_intermediate_ident(name: &str) -> Ident {
Ident::new(&new_name, Span::call_site())
}
fn get_register_ident(name: &str) -> Ident {
let new_name = format!(
"__napi_register__{}_{}",
name,
REGISTER_INDEX.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
);
Ident::new(&new_name, Span::call_site())
}
fn js_mod_to_token_stream(js_mod: Option<&String>) -> TokenStream {
js_mod
.map(|i| {

View file

@ -1,10 +1,7 @@
use proc_macro2::{Ident, Literal, TokenStream};
use quote::ToTokens;
use crate::{
codegen::{get_register_ident, js_mod_to_token_stream},
BindgenResult, NapiConst, TryToTokens,
};
use crate::{codegen::js_mod_to_token_stream, BindgenResult, NapiConst, TryToTokens};
impl TryToTokens for NapiConst {
fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
@ -20,19 +17,16 @@ impl TryToTokens for NapiConst {
impl NapiConst {
fn gen_module_register(&self) -> TokenStream {
let name_str = self.name.to_string();
let name_ident = self.name.clone();
let name_ident = &self.name;
let js_name_lit = Literal::string(&format!("{}\0", self.name));
let register_name = get_register_ident(&name_str);
let register_name = &self.register_name;
let type_name = &self.type_name;
let cb_name = Ident::new(
&format!("__register__const__{}_callback__", register_name),
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)]

View file

@ -1,10 +1,7 @@
use proc_macro2::{Ident, Literal, Span, TokenStream};
use quote::ToTokens;
use crate::{
codegen::{get_register_ident, js_mod_to_token_stream},
BindgenResult, NapiEnum, TryToTokens,
};
use crate::{codegen::js_mod_to_token_stream, BindgenResult, NapiEnum, TryToTokens};
impl TryToTokens for NapiEnum {
fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
@ -103,7 +100,7 @@ impl NapiEnum {
fn gen_module_register(&self) -> TokenStream {
let name_str = self.name.to_string();
let js_name_lit = Literal::string(&format!("{}\0", &self.js_name));
let register_name = get_register_ident(&name_str);
let register_name = &self.register_name;
let mut define_properties = vec![];
@ -134,10 +131,6 @@ 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)]

View file

@ -3,7 +3,7 @@ use quote::ToTokens;
use syn::spanned::Spanned;
use crate::{
codegen::{get_intermediate_ident, get_register_ident, js_mod_to_token_stream},
codegen::{get_intermediate_ident, js_mod_to_token_stream},
BindgenResult, CallbackArg, Diagnostic, FnKind, FnSelf, NapiFn, NapiFnArgKind, TryToTokens,
};
@ -567,13 +567,11 @@ impl NapiFn {
let name_str = self.name.to_string();
let js_name = format!("{}\0", &self.js_name);
let name_len = self.js_name.len();
let module_register_name = get_register_ident(&name_str);
let module_register_name = &self.register_name;
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)]

View file

@ -5,7 +5,7 @@ use proc_macro2::{Ident, Literal, Span, TokenStream};
use quote::ToTokens;
use crate::{
codegen::{get_intermediate_ident, get_register_ident, js_mod_to_token_stream},
codegen::{get_intermediate_ident, js_mod_to_token_stream},
BindgenResult, FnKind, NapiImpl, NapiStruct, NapiStructKind, TryToTokens,
};
@ -717,7 +717,7 @@ impl NapiStruct {
fn gen_register(&self) -> TokenStream {
let name_str = self.name.to_string();
let struct_register_name = get_register_ident(&format!("{}_struct", name_str));
let struct_register_name = &self.register_name;
let js_name = format!("{}\0", self.js_name);
let mut props = vec![];
@ -772,9 +772,6 @@ 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)]
@ -842,7 +839,7 @@ impl NapiImpl {
Span::call_site(),
);
let register_name = get_register_ident(&format!("{}_impl", name_str));
let register_name = &self.register_name;
let mut methods = vec![];
let mut props = HashMap::new();
@ -891,9 +888,6 @@ impl NapiImpl {
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)]

View file

@ -47,6 +47,14 @@ macro_rules! napi_ast_impl {
}
}
}
impl Napi {
pub fn register_name(&self) -> String {
match self.item {
$( NapiItem::$v(ref ast) => ast.register_name.to_string() ),*
}
}
}
};
}

View file

@ -1,14 +1,13 @@
use std::env;
use std::fs;
#[cfg(feature = "type-def")]
use std::io::BufWriter;
use std::io::Write;
use std::sync::atomic::{AtomicBool, Ordering};
use crate::parser::{attrs::BindgenAttrs, ParseNapi};
use napi_derive_backend::{BindgenResult, TryToTokens, REGISTER_IDENTS};
#[cfg(feature = "type-def")]
use napi_derive_backend::{Napi, ToTypeDef};
use napi_derive_backend::ToTypeDef;
use napi_derive_backend::{BindgenResult, Napi, TryToTokens};
use proc_macro2::{TokenStream, TokenTree};
use quote::ToTokens;
use syn::{Attribute, Item};
@ -34,7 +33,7 @@ pub fn expand(attr: TokenStream, input: TokenStream) -> BindgenResult<TokenStrea
prepare_type_def_file();
if let Ok(wasi_register_file) = env::var("WASI_REGISTER_TMP_PATH") {
if let Err(_e) = fs::remove_file(wasi_register_file) {
if let Err(_e) = remove_existed_def_file(&wasi_register_file) {
#[cfg(debug_assertions)]
{
println!("Failed to manipulate wasi register file: {:?}", _e);
@ -85,6 +84,7 @@ pub fn expand(attr: TokenStream, input: TokenStream) -> BindgenResult<TokenStrea
#[cfg(feature = "type-def")]
output_type_def(&napi);
output_wasi_register_def(&napi);
} else {
item.to_tokens(&mut tokens);
};
@ -108,21 +108,29 @@ pub fn expand(attr: TokenStream, input: TokenStream) -> BindgenResult<TokenStrea
#[cfg(feature = "type-def")]
output_type_def(&napi);
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");
}
});
output_wasi_register_def(&napi);
Ok(tokens)
}
}
fn output_wasi_register_def(napi: &Napi) {
if let Ok(wasi_register_file) = env::var("WASI_REGISTER_TMP_PATH") {
fs::OpenOptions::new()
.append(true)
.create(true)
.open(wasi_register_file)
.and_then(|file| {
let mut writer = BufWriter::<fs::File>::new(file);
let pkg_name: String = std::env::var("CARGO_PKG_NAME").expect("CARGO_PKG_NAME is not set");
writer.write_all(format!("{pkg_name}: {}", napi.register_name()).as_bytes())?;
writer.write_all("\n".as_bytes())
})
.unwrap_or_else(|e| {
println!("Failed to write wasi register file: {:?}", e);
});
}
}
#[cfg(feature = "type-def")]
fn output_type_def(napi: &Napi) {
if let Ok(type_def_file) = env::var("TYPE_DEF_TMP_PATH") {
@ -194,7 +202,7 @@ fn prepare_type_def_file() {
if let Ok(ref type_def_file) = env::var("TYPE_DEF_TMP_PATH") {
use napi_derive_backend::{NAPI_RS_CLI_VERSION, NAPI_RS_CLI_VERSION_WITH_SHARED_CRATES_FIX};
if let Err(_e) = if *NAPI_RS_CLI_VERSION >= *NAPI_RS_CLI_VERSION_WITH_SHARED_CRATES_FIX {
remove_existed_type_def(type_def_file)
remove_existed_def_file(type_def_file)
} else {
fs::remove_file(type_def_file)
} {
@ -206,12 +214,11 @@ fn prepare_type_def_file() {
}
}
#[cfg(feature = "type-def")]
fn remove_existed_type_def(type_def_file: &str) -> std::io::Result<()> {
fn remove_existed_def_file(def_file: &str) -> std::io::Result<()> {
use std::io::{BufRead, BufReader};
let pkg_name = std::env::var("CARGO_PKG_NAME").expect("CARGO_PKG_NAME is not set");
if let Ok(content) = std::fs::File::open(type_def_file) {
if let Ok(content) = std::fs::File::open(def_file) {
let reader = BufReader::new(content);
let cleaned_content = reader
.lines()
@ -229,14 +236,7 @@ fn remove_existed_type_def(type_def_file: &str) -> std::io::Result<()> {
})
.collect::<Vec<String>>()
.join("\n");
let mut content = std::fs::OpenOptions::new()
.read(true)
.write(true)
.truncate(true)
.open(type_def_file)?;
content.write_all(cleaned_content.as_bytes())?;
content.write_all(b"\n")?;
std::fs::write(def_file, format!("{cleaned_content}\n"))?;
}
Ok(())
}

View file

@ -4,6 +4,7 @@ pub mod attrs;
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::str::Chars;
use std::sync::atomic::AtomicUsize;
use attrs::{BindgenAttr, BindgenAttrs};
@ -25,6 +26,17 @@ thread_local! {
static GENERATOR_STRUCT: RefCell<HashMap<String, bool>> = Default::default();
}
static REGISTER_INDEX: AtomicUsize = AtomicUsize::new(0);
fn get_register_ident(name: &str) -> Ident {
let new_name = format!(
"__napi_register__{}_{}",
name,
REGISTER_INDEX.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
);
Ident::new(&new_name, Span::call_site())
}
struct AnyIdent(Ident);
impl Parse for AnyIdent {
@ -672,7 +684,7 @@ fn napi_fn_from_decl(
};
NapiFn {
name: ident,
name: ident.clone(),
js_name,
args,
ret,
@ -697,6 +709,7 @@ fn napi_fn_from_decl(
configurable: opts.configurable(),
catch_unwind: opts.catch_unwind().is_some(),
unsafe_: sig.unsafety.is_some(),
register_name: get_register_ident(ident.to_string().as_str()),
}
})
}
@ -990,7 +1003,7 @@ impl ConvertToAST for syn::ItemStruct {
Diagnostic::from_vec(errors).map(|()| Napi {
item: NapiItem::Struct(NapiStruct {
js_name,
name: struct_name,
name: struct_name.clone(),
vis,
fields,
is_tuple,
@ -1001,6 +1014,7 @@ impl ConvertToAST for syn::ItemStruct {
comments: extract_doc_comments(&self.attrs),
implement_iterator,
use_custom_finalize: opts.custom_finalize().is_some(),
register_name: get_register_ident(format!("{struct_name}_struct").as_str()),
}),
})
}
@ -1089,7 +1103,7 @@ impl ConvertToAST for syn::ItemImpl {
Ok(Napi {
item: NapiItem::Impl(NapiImpl {
name: struct_name,
name: struct_name.clone(),
js_name: struct_js_name,
items,
task_output_type,
@ -1098,6 +1112,7 @@ impl ConvertToAST for syn::ItemImpl {
iterator_return_type,
js_mod: namespace,
comments: extract_doc_comments(&self.attrs),
register_name: get_register_ident(format!("{struct_name}_impl").as_str()),
}),
})
}
@ -1212,6 +1227,7 @@ impl ConvertToAST for syn::ItemEnum {
js_mod: opts.namespace().map(|(m, _)| m.to_owned()),
comments: extract_doc_comments(&self.attrs),
skip_typescript: opts.skip_typescript().is_some(),
register_name: get_register_ident(self.ident.to_string().as_str()),
}),
})
}
@ -1231,6 +1247,7 @@ impl ConvertToAST for syn::ItemConst {
js_mod: opts.namespace().map(|(m, _)| m.to_owned()),
comments: extract_doc_comments(&self.attrs),
skip_typescript: opts.skip_typescript().is_some(),
register_name: get_register_ident(self.ident.to_string().as_str()),
}),
}),
_ => bail_span!(self, "only public const allowed"),

View file

@ -5,9 +5,6 @@ name = "napi-shared"
publish = false
version = "0.1.0"
[lib]
crate-type = ["cdylib", "lib"]
[dependencies]
napi = { path = "../../crates/napi", default-features = false, features = [
"napi8",

View file

@ -1,5 +0,0 @@
fn main() {
use napi_build::setup;
setup();
}

File diff suppressed because it is too large Load diff