feat(napi): support export rust mod as ts namespace

This commit is contained in:
LongYinan 2021-11-23 19:00:31 +08:00
parent e4ca46f32b
commit 1fe39ff66d
No known key found for this signature in database
GPG key ID: C3666B7FC82ADAD7
29 changed files with 639 additions and 336 deletions

View file

@ -15,6 +15,7 @@ pub struct NapiFn {
pub vis: syn::Visibility,
pub parent: Option<Ident>,
pub strict: bool,
pub js_mod: Option<String>,
}
#[derive(Debug, Clone)]
@ -54,6 +55,7 @@ pub struct NapiStruct {
pub fields: Vec<NapiStructField>,
pub is_tuple: bool,
pub kind: NapiStructKind,
pub js_mod: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
@ -78,6 +80,7 @@ pub struct NapiImpl {
pub js_name: String,
pub items: Vec<NapiFn>,
pub task_output_type: Option<Type>,
pub js_mod: Option<String>,
}
#[derive(Debug, Clone)]
@ -85,6 +88,7 @@ pub struct NapiEnum {
pub name: Ident,
pub js_name: String,
pub variants: Vec<NapiEnumVariant>,
pub js_mod: Option<String>,
}
#[derive(Debug, Clone)]
@ -100,4 +104,11 @@ pub struct NapiConst {
pub js_name: String,
pub type_name: Type,
pub value: Expr,
pub js_mod: Option<String>,
}
#[derive(Debug, Clone)]
pub struct NapiMod {
pub name: Ident,
pub js_name: String,
}

View file

@ -5,6 +5,7 @@ use crate::BindgenResult;
mod r#const;
mod r#enum;
mod r#fn;
mod js_mod;
mod r#struct;
pub trait TryToTokens {
@ -27,3 +28,12 @@ fn get_register_ident(name: &str) -> Ident {
let new_name = format!("__napi_register__{}", name);
Ident::new(&new_name, Span::call_site())
}
fn js_mod_to_token_stream(js_mod: Option<&String>) -> TokenStream {
js_mod
.map(|i| {
let i = format!("{}\0", i);
quote! { Some(#i) }
})
.unwrap_or_else(|| quote! { None })
}

View file

@ -1,7 +1,10 @@
use proc_macro2::{Ident, Literal, TokenStream};
use quote::ToTokens;
use crate::{codegen::get_register_ident, BindgenResult, NapiConst, TryToTokens};
use crate::{
codegen::{get_register_ident, js_mod_to_token_stream},
BindgenResult, NapiConst, TryToTokens,
};
impl TryToTokens for NapiConst {
fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
@ -26,6 +29,7 @@ impl NapiConst {
&format!("__register__const__{}_callback__", register_name),
self.name.span(),
);
let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
quote! {
#[allow(non_snake_case)]
#[allow(clippy::all)]
@ -36,7 +40,7 @@ impl NapiConst {
#[allow(clippy::all)]
#[napi::bindgen_prelude::ctor]
fn #register_name() {
napi::bindgen_prelude::register_module_export(#js_name_lit, #cb_name);
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #cb_name);
}
}
}

View file

@ -1,7 +1,10 @@
use proc_macro2::{Ident, Literal, Span, TokenStream};
use quote::ToTokens;
use crate::{codegen::get_register_ident, BindgenResult, NapiEnum, TryToTokens};
use crate::{
codegen::{get_register_ident, js_mod_to_token_stream},
BindgenResult, NapiEnum, TryToTokens,
};
impl TryToTokens for NapiEnum {
fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
@ -124,6 +127,8 @@ impl NapiEnum {
Span::call_site(),
);
let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
quote! {
unsafe fn #callback_name(env: napi::bindgen_prelude::sys::napi_env) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
use std::ffi::CString;
@ -144,7 +149,7 @@ impl NapiEnum {
#[allow(clippy::all)]
#[napi::bindgen_prelude::ctor]
fn #register_name() {
napi::bindgen_prelude::register_module_export(#js_name_lit, #callback_name);
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #callback_name);
}
}
}

View file

@ -2,7 +2,7 @@ use proc_macro2::{Ident, Span, TokenStream};
use quote::ToTokens;
use crate::{
codegen::{get_intermediate_ident, get_register_ident},
codegen::{get_intermediate_ident, get_register_ident, js_mod_to_token_stream},
BindgenResult, CallbackArg, FnKind, FnSelf, NapiFn, NapiFnArgKind, TryToTokens,
};
@ -294,12 +294,11 @@ impl NapiFn {
let name_len = 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!("__register__fn__{}_callback__", name_str),
Span::call_site(),
);
quote! {
unsafe fn #cb_name(env: napi::bindgen_prelude::sys::napi_env) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
let mut fn_ptr = std::ptr::null_mut();
@ -324,7 +323,7 @@ impl NapiFn {
#[allow(non_snake_case)]
#[napi::bindgen_prelude::ctor]
fn #module_register_name() {
napi::bindgen_prelude::register_module_export(#js_name, #cb_name);
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name);
}
}
}

View file

@ -0,0 +1,9 @@
use proc_macro2::TokenStream;
use crate::{NapiMod, TryToTokens};
impl TryToTokens for NapiMod {
fn try_to_tokens(&self, _tokens: &mut TokenStream) -> crate::BindgenResult<()> {
Ok(())
}
}

View file

@ -4,7 +4,7 @@ use proc_macro2::{Ident, Literal, Span, TokenStream};
use quote::ToTokens;
use crate::{
codegen::{get_intermediate_ident, get_register_ident},
codegen::{get_intermediate_ident, get_register_ident, js_mod_to_token_stream},
BindgenResult, FnKind, NapiImpl, NapiStruct, NapiStructKind, TryToTokens,
};
@ -413,13 +413,13 @@ impl NapiStruct {
props.push(prop);
}
let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
quote! {
#[allow(non_snake_case)]
#[allow(clippy::all)]
#[napi::bindgen_prelude::ctor]
fn #struct_register_name() {
napi::bindgen_prelude::register_class(#name_str, #js_name, vec![#(#props),*]);
napi::bindgen_prelude::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
}
}
}
@ -478,7 +478,7 @@ 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 js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
Ok(quote! {
#[allow(non_snake_case)]
#[allow(clippy::all)]
@ -488,7 +488,7 @@ impl NapiImpl {
#[napi::bindgen_prelude::ctor]
fn #register_name() {
napi::bindgen_prelude::register_class(#name_str, #js_name, vec![#(#props),*]);
napi::bindgen_prelude::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
}
}
})

View file

@ -54,4 +54,5 @@ napi_ast_impl! {
(Impl, NapiImpl),
(Enum, NapiEnum),
(Const, NapiConst),
(Mod, NapiMod),
}

View file

@ -1,6 +1,7 @@
mod r#const;
mod r#enum;
mod r#fn;
mod js_mod;
pub(crate) mod r#struct;
use std::collections::HashMap;
@ -13,13 +14,19 @@ pub struct TypeDef {
pub kind: String,
pub name: String,
pub def: String,
pub js_mod: Option<String>,
}
impl ToString for TypeDef {
fn to_string(&self) -> String {
let js_mod = if let Some(js_mod) = &self.js_mod {
format!(", \"js_mod\": \"{}\"", js_mod)
} else {
"".to_owned()
};
format!(
r#"{{"kind": "{}", "name": "{}", "def": "{}"}}"#,
self.kind, self.name, self.def,
r#"{{"kind": "{}", "name": "{}", "def": "{}"{}}}"#,
self.kind, self.name, self.def, js_mod,
)
}
}

View file

@ -12,6 +12,7 @@ impl ToTypeDef for NapiConst {
&self.js_name,
ty_to_ts_type(&self.type_name, false).0
),
js_mod: self.js_mod.to_owned(),
}
}
}

View file

@ -11,6 +11,7 @@ impl ToTypeDef for NapiEnum {
js_name = &self.js_name,
variants = self.gen_ts_variants()
),
js_mod: self.js_mod.to_owned(),
}
}
}

View file

@ -19,6 +19,7 @@ impl ToTypeDef for NapiFn {
kind: "fn".to_owned(),
name: self.js_name.clone(),
def,
js_mod: self.js_mod.to_owned(),
}
}
}

View file

@ -0,0 +1,12 @@
use crate::{NapiMod, ToTypeDef, TypeDef};
impl ToTypeDef for NapiMod {
fn to_type_def(&self) -> TypeDef {
TypeDef {
kind: "mod".to_owned(),
name: self.js_name.clone(),
def: "".to_owned(),
js_mod: None,
}
}
}

View file

@ -23,6 +23,7 @@ impl ToTypeDef for NapiStruct {
}),
name: self.js_name.to_owned(),
def: self.gen_ts_class(),
js_mod: self.js_mod.to_owned(),
}
}
}
@ -44,6 +45,7 @@ impl ToTypeDef for NapiImpl {
.map(|f| f.to_type_def().def)
.collect::<Vec<_>>()
.join("\\n"),
js_mod: self.js_mod.to_owned(),
}
}
}

View file

@ -12,9 +12,10 @@ use napi_derive_backend::{BindgenResult, TryToTokens};
#[cfg(feature = "type-def")]
use napi_derive_backend::{ToTypeDef, TypeDef};
use parser::ParseNapi;
use parser::{attrs::BindgenAttrs, ParseNapi};
use proc_macro::TokenStream as RawStream;
use proc_macro2::TokenStream;
use proc_macro2::{TokenStream, TokenTree};
use quote::ToTokens;
use std::env;
#[cfg(feature = "type-def")]
use std::{
@ -23,6 +24,7 @@ use std::{
};
#[cfg(feature = "compat-mode")]
use syn::{fold::Fold, parse_macro_input, ItemFn};
use syn::{Attribute, Item};
/// ```ignore
/// #[napi]
@ -49,21 +51,78 @@ pub fn napi(attr: RawStream, input: RawStream) -> RawStream {
fn expand(attr: TokenStream, input: TokenStream) -> BindgenResult<TokenStream> {
let mut item = syn::parse2::<syn::Item>(input)?;
let opts = syn::parse2(attr)?;
let opts: BindgenAttrs = syn::parse2(attr)?;
let mut tokens = proc_macro2::TokenStream::new();
let napi = item.parse_napi(&mut tokens, opts)?;
napi.try_to_tokens(&mut tokens)?;
#[cfg(feature = "type-def")]
if let Ok(type_def_file) = env::var("TYPE_DEF_TMP_PATH") {
if let Err(e) = output_type_def(type_def_file, napi.to_type_def()) {
println!("Failed to write type def file: {:?}", e);
if let Item::Mod(mut js_mod) = item {
let js_name = opts.js_name().map_or_else(
|| js_mod.ident.to_string(),
|(js_name, _)| js_name.to_owned(),
);
if let Some((_, mut items)) = js_mod.content.clone() {
for item in items.iter_mut() {
let mut empty_attrs = vec![];
if let Some(item_opts) = replace_napi_attr_in_mod(
js_name.clone(),
match item {
syn::Item::Fn(ref mut function) => &mut function.attrs,
syn::Item::Struct(ref mut struct_) => &mut struct_.attrs,
syn::Item::Enum(ref mut enum_) => &mut enum_.attrs,
syn::Item::Const(ref mut const_) => &mut const_.attrs,
syn::Item::Impl(ref mut impl_) => &mut impl_.attrs,
syn::Item::Mod(mod_) => {
let mod_in_mod = mod_
.attrs
.iter()
.enumerate()
.find(|(_, m)| m.path.segments[0].ident == "napi");
if mod_in_mod.is_some() {
bail_span!(
mod_,
"napi module cannot be nested under another napi module"
);
} else {
&mut empty_attrs
}
}
_ => &mut empty_attrs,
},
) {
let napi = item.parse_napi(&mut tokens, item_opts)?;
napi.try_to_tokens(&mut tokens)?;
#[cfg(feature = "type-def")]
if let Ok(type_def_file) = env::var("TYPE_DEF_TMP_PATH") {
if let Err(e) = output_type_def(type_def_file, napi.to_type_def()) {
println!("Failed to write type def file: {:?}", e);
};
}
} else {
item.to_tokens(&mut tokens);
};
}
js_mod.content = None;
};
}
let js_mod_attrs: Vec<Attribute> = js_mod
.attrs
.clone()
.into_iter()
.filter(|attr| attr.path.segments[0].ident != "napi")
.collect();
let mod_name = js_mod.ident;
let visible = js_mod.vis;
let mod_tokens = quote! { #(#js_mod_attrs)* #visible mod #mod_name { #tokens } };
Ok(mod_tokens)
} else {
let napi = item.parse_napi(&mut tokens, opts)?;
napi.try_to_tokens(&mut tokens)?;
Ok(tokens)
#[cfg(feature = "type-def")]
if let Ok(type_def_file) = env::var("TYPE_DEF_TMP_PATH") {
if let Err(e) = output_type_def(type_def_file, napi.to_type_def()) {
println!("Failed to write type def file: {:?}", e);
};
}
Ok(tokens)
}
}
#[cfg(feature = "type-def")]
@ -213,3 +272,49 @@ pub fn module_exports(_attr: RawStream, input: RawStream) -> RawStream {
})
.into()
}
fn replace_napi_attr_in_mod(
js_namespace: String,
attrs: &mut Vec<syn::Attribute>,
) -> Option<BindgenAttrs> {
let napi_attr = attrs.clone();
let napi_attr = napi_attr
.iter()
.enumerate()
.find(|(_, m)| m.path.segments[0].ident == "napi");
if let Some((index, napi_attr)) = napi_attr {
let attr_token_stream = napi_attr.tokens.clone();
let raw_attr_stream = attr_token_stream.to_string();
let raw_attr_stream = if !raw_attr_stream.is_empty() {
raw_attr_stream
.strip_prefix('(')
.unwrap()
.strip_suffix(')')
.unwrap()
.to_string()
} else {
raw_attr_stream
};
let raw_attr_token_stream = syn::parse_str::<TokenStream>(raw_attr_stream.as_str()).unwrap();
let new_attr: syn::Attribute = if !raw_attr_stream.is_empty() {
syn::parse_quote!(
#[napi(#raw_attr_token_stream, namespace = #js_namespace)]
)
} else {
syn::parse_quote!(
#[napi(namespace = #js_namespace)]
)
};
let struct_opts: BindgenAttrs;
if let Some(TokenTree::Group(g)) = new_attr.tokens.into_iter().next() {
struct_opts = syn::parse2(g.stream()).ok()?;
} else {
struct_opts = syn::parse2(quote! {}).ok()?;
}
attrs.remove(index);
Some(struct_opts)
} else {
None
}
}

View file

@ -27,6 +27,7 @@ struct AttributeParseState {
checks: Cell<usize>,
}
#[derive(Debug)]
/// Parsed attributes from a `#[napi(..)]`.
pub struct BindgenAttrs {
/// Whether `#[napi]` attribute exists
@ -51,7 +52,7 @@ macro_rules! attrgen {
(skip, Skip(Span)),
(strict, Strict(Span)),
(object, Object(Span)),
(task, Task(Span)),
(namespace, Namespace(Span, String, Span)),
// impl later
// (inspectable, Inspectable(Span)),
@ -169,11 +170,11 @@ impl BindgenAttrs {
.enumerate()
.find(|&(_, m)| m.path.segments[0].ident == "napi");
let pos = match &napi_attr {
let pos = match napi_attr {
Some((pos, raw_attr)) => {
ret.exists = true;
ret.span = raw_attr.tokens.span();
*pos
pos
}
None => return Ok(ret),
};
@ -216,6 +217,7 @@ impl Default for BindgenAttrs {
macro_rules! gen_bindgen_attr {
($( ($method:ident, $($variants:tt)*) ,)*) => {
/// The possible attributes in the `#[napi]`.
#[derive(Debug)]
pub enum BindgenAttr {
$($($variants)*,)*
}
@ -243,7 +245,6 @@ pub fn record_struct(ident: &Ident, js_name: String, opts: &BindgenAttrs) {
pub fn check_recorded_struct_for_impl(ident: &Ident, opts: &BindgenAttrs) -> BindgenResult<String> {
STRUCTS.with(|state| {
let struct_name = ident.to_string();
let mut map = state.parsed.borrow_mut();
if let Some(parsed) = map.get_mut(&struct_name) {
if opts.constructor().is_some() && !cfg!(debug_assertions) {

View file

@ -574,6 +574,7 @@ fn napi_fn_from_decl(
parent: parent.cloned(),
attrs,
strict: opts.strict().is_some(),
js_mod: opts.namespace().map(|(m, _)| m.to_owned()),
}
})
}
@ -588,7 +589,7 @@ impl ParseNapi for syn::Item {
syn::Item::Const(c) => c.parse_napi(tokens, opts),
_ => bail_span!(
self,
"#[napi] can only be applied to a function, struct, enum or impl."
"#[napi] can only be applied to a function, struct, enum, const, mod or impl."
),
}
}
@ -735,7 +736,6 @@ impl ConvertToAST for syn::ItemStruct {
setter: !(ignored || readonly),
})
}
record_struct(&struct_name, js_name.clone(), &opts);
Diagnostic::from_vec(errors).map(|()| Napi {
@ -747,13 +747,14 @@ impl ConvertToAST for syn::ItemStruct {
fields,
is_tuple,
kind: struct_kind,
js_mod: opts.namespace().map(|(m, _)| m.to_owned()),
}),
})
}
}
impl ConvertToAST for syn::ItemImpl {
fn convert_to_ast(&mut self, _opts: BindgenAttrs) -> BindgenResult<Napi> {
fn convert_to_ast(&mut self, impl_opts: BindgenAttrs) -> BindgenResult<Napi> {
let struct_name = match get_ty(&self.self_ty) {
syn::Type::Path(syn::TypePath {
ref path,
@ -823,6 +824,7 @@ impl ConvertToAST for syn::ItemImpl {
js_name: struct_js_name,
items,
task_output_type,
js_mod: impl_opts.namespace().map(|(m, _)| m.to_owned()),
}),
})
}
@ -916,6 +918,7 @@ impl ConvertToAST for syn::ItemEnum {
name: self.ident.clone(),
js_name,
variants,
js_mod: opts.namespace().map(|(m, _)| m.to_owned()),
}),
})
}
@ -933,6 +936,7 @@ impl ConvertToAST for syn::ItemConst {
.map_or_else(|| self.ident.to_string(), |(s, _)| s.to_string()),
type_name: *self.ty.clone(),
value: *self.expr.clone(),
js_mod: opts.namespace().map(|(m, _)| m.to_owned()),
}),
}),
_ => bail_span!(self, "only public const allowed"),

View file

@ -21,6 +21,7 @@ pub struct i64n(pub i64);
/// <https://nodejs.org/api/n-api.html#napi_create_bigint_words>
/// The resulting BigInt is calculated as: (1)^sign_bit (words\[0\] × (2^64)^0 + words\[1\] × (2^64)^1 + …)
#[derive(Debug, Clone)]
pub struct BigInt {
/// true for negative numbers
pub sign_bit: bool,

View file

@ -9,9 +9,15 @@ pub type ExportRegisterCallback = unsafe fn(sys::napi_env) -> Result<sys::napi_v
pub type ModuleExportsCallback =
unsafe fn(env: sys::napi_env, exports: sys::napi_value) -> Result<()>;
type ModuleRegisterCallback =
RefCell<Vec<(Option<&'static str>, (&'static str, ExportRegisterCallback))>>;
type ModuleClassProperty =
RefCell<HashMap<&'static str, HashMap<Option<&'static str>, (&'static str, Vec<Property>)>>>;
thread_local! {
static MODULE_REGISTER_CALLBACK: RefCell<Vec<(&'static str, ExportRegisterCallback)>> = Default::default();
static MODULE_CLASS_PROPERTIES: RefCell<HashMap<&'static str, (&'static str, Vec<Property>)>> = Default::default();
static MODULE_REGISTER_CALLBACK: ModuleRegisterCallback = Default::default();
static MODULE_CLASS_PROPERTIES: ModuleClassProperty = Default::default();
static REGISTERED_CLASSES: RefCell<HashMap<
/* export name */ &'static str,
/* constructor */ sys::napi_ref,
@ -34,17 +40,27 @@ pub fn register_module_exports(callback: ModuleExportsCallback) {
MODULE_EXPORTS.with(|cell| cell.set(vec![callback]));
}
pub fn register_module_export(name: &'static str, cb: ExportRegisterCallback) {
pub fn register_module_export(
js_mod: Option<&'static str>,
name: &'static str,
cb: ExportRegisterCallback,
) {
MODULE_REGISTER_CALLBACK.with(|exports| {
let mut list = exports.borrow_mut();
list.push((name, cb));
list.push((js_mod, (name, cb)));
});
}
pub fn register_class(rust_name: &'static str, js_name: &'static str, props: Vec<Property>) {
pub fn register_class(
rust_name: &'static str,
js_mod: Option<&'static str>,
js_name: &'static str,
props: Vec<Property>,
) {
MODULE_CLASS_PROPERTIES.with(|map| {
let mut map = map.borrow_mut();
let val = map.entry(rust_name).or_default();
let val = val.entry(js_mod).or_default();
val.0 = js_name;
val.1.extend(props.into_iter());
@ -56,73 +72,158 @@ unsafe extern "C" fn napi_register_module_v1(
env: sys::napi_env,
exports: sys::napi_value,
) -> sys::napi_value {
let mut exports_objects: HashMap<Option<&'static str>, sys::napi_value> = HashMap::default();
MODULE_REGISTER_CALLBACK.with(|to_register_exports| {
to_register_exports
.take()
.iter_mut()
.fold(
HashMap::<Option<&'static str>, Vec<(&'static str, ExportRegisterCallback)>>::new(),
|mut acc, (js_mod, item)| {
if let Some(k) = acc.get_mut(js_mod) {
k.push(*item);
} else {
acc.insert(*js_mod, vec![*item]);
}
acc
},
)
.iter()
.for_each(|(name, callback)| {
let js_name = CStr::from_bytes_with_nul_unchecked(name.as_bytes());
unsafe {
if let Err(e) = callback(env).and_then(|v| {
check_status!(
sys::napi_set_named_property(env, exports, js_name.as_ptr(), v),
"Failed to register export `{}`",
name,
)
}) {
JsError::from(e).throw_into(env)
.for_each(|(js_mod, items)| {
let mut exports_js_mod = ptr::null_mut();
if let Some(js_mod_str) = js_mod {
if let Some(exports_object) = exports_objects.get(js_mod) {
exports_js_mod = *exports_object;
} else {
check_status_or_throw!(
env,
sys::napi_create_object(env, &mut exports_js_mod),
"Create export JavaScript Object [{}] failed",
js_mod_str
);
check_status_or_throw!(
env,
sys::napi_set_named_property(
env,
exports,
js_mod_str.as_ptr() as *const _,
exports_js_mod
),
"Set exports Object [{}] into exports object failed",
js_mod_str
);
exports_objects.insert(*js_mod, exports_js_mod);
}
}
for (name, callback) in items {
let js_name = CStr::from_bytes_with_nul_unchecked(name.as_bytes());
unsafe {
if let Err(e) = callback(env).and_then(|v| {
check_status!(
sys::napi_set_named_property(
env,
if exports_js_mod.is_null() {
exports
} else {
exports_js_mod
},
js_name.as_ptr(),
v
),
"Failed to register export `{}`",
name,
)
}) {
JsError::from(e).throw_into(env)
}
}
}
})
});
MODULE_CLASS_PROPERTIES.with(|to_register_classes| {
for (rust_name, (js_name, props)) in to_register_classes.take().into_iter() {
unsafe {
let (ctor, props): (Vec<_>, Vec<_>) = props.into_iter().partition(|prop| prop.is_ctor);
// one or more or zero?
// zero is for `#[napi(task)]`
if ctor.is_empty() && props.is_empty() {
continue;
}
let ctor = ctor.get(0).map(|c| c.raw().method.unwrap()).unwrap_or(noop);
let raw_props: Vec<_> = props.iter().map(|prop| prop.raw()).collect();
for (rust_name, js_mods) in to_register_classes.take().iter() {
for (js_mod, (js_name, props)) in js_mods {
let mut exports_js_mod = ptr::null_mut();
unsafe {
if let Some(js_mod_str) = js_mod {
if let Some(exports_object) = exports_objects.get(js_mod) {
exports_js_mod = *exports_object;
} else {
check_status_or_throw!(
env,
sys::napi_create_object(env, &mut exports_js_mod),
"Create export JavaScript Object [{}] failed",
js_mod_str
);
check_status_or_throw!(
env,
sys::napi_set_named_property(
env,
exports,
js_mod_str.as_ptr() as *const _,
exports_js_mod
),
"Set exports Object [{}] into exports object failed",
js_mod_str
);
exports_objects.insert(*js_mod, exports_js_mod);
}
}
let (ctor, props): (Vec<_>, Vec<_>) = props.iter().partition(|prop| prop.is_ctor);
// one or more or zero?
// zero is for `#[napi(task)]`
if ctor.is_empty() && props.is_empty() {
continue;
}
let ctor = ctor.get(0).map(|c| c.raw().method.unwrap()).unwrap_or(noop);
let raw_props: Vec<_> = props.iter().map(|prop| prop.raw()).collect();
let js_class_name = CStr::from_bytes_with_nul_unchecked(js_name.as_bytes());
let mut class_ptr = ptr::null_mut();
let js_class_name = CStr::from_bytes_with_nul_unchecked(js_name.as_bytes());
let mut class_ptr = ptr::null_mut();
check_status_or_throw!(
env,
sys::napi_define_class(
check_status_or_throw!(
env,
js_class_name.as_ptr(),
js_name.len(),
Some(ctor),
ptr::null_mut(),
raw_props.len(),
raw_props.as_ptr(),
&mut class_ptr,
),
"Failed to register class `{}` generate by struct `{}`",
&js_name,
&rust_name
);
sys::napi_define_class(
env,
js_class_name.as_ptr(),
js_name.len(),
Some(ctor),
ptr::null_mut(),
raw_props.len(),
raw_props.as_ptr(),
&mut class_ptr,
),
"Failed to register class `{}` generate by struct `{}`",
&js_name,
&rust_name
);
let mut ctor_ref = ptr::null_mut();
sys::napi_create_reference(env, class_ptr, 1, &mut ctor_ref);
let mut ctor_ref = ptr::null_mut();
sys::napi_create_reference(env, class_ptr, 1, &mut ctor_ref);
REGISTERED_CLASSES.with(|registered_classes| {
let mut registered_class = registered_classes.borrow_mut();
registered_class.insert(js_name, ctor_ref);
});
REGISTERED_CLASSES.with(|registered_classes| {
let mut registered_class = registered_classes.borrow_mut();
registered_class.insert(js_name, ctor_ref);
});
check_status_or_throw!(
env,
sys::napi_set_named_property(env, exports, js_class_name.as_ptr(), class_ptr),
"Failed to register class `{}` generate by struct `{}`",
&js_name,
&rust_name
);
check_status_or_throw!(
env,
sys::napi_set_named_property(
env,
if exports_js_mod.is_null() {
exports
} else {
exports_js_mod
},
js_class_name.as_ptr(),
class_ptr
),
"Failed to register class `{}` generate by struct `{}`",
&js_name,
&rust_name
);
}
}
}
});