From 0f14799776e0335a08f1c0b1cce973bf7c0fd353 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Sat, 6 Aug 2022 21:54:58 +0800 Subject: [PATCH] feat(napi-derive): support set property attribute in napi macro (#1257) --- crates/backend/src/ast.rs | 6 ++++ crates/backend/src/codegen.rs | 5 ++++ crates/backend/src/codegen/struct.rs | 27 ++++++++++++++++-- crates/macro/src/parser/attrs.rs | 19 +++++++++++++ crates/macro/src/parser/mod.rs | 24 ++++++++++++++++ crates/napi/Cargo.toml | 1 + crates/napi/src/js_values/object_property.rs | 28 ++++++++----------- crates/napi/src/lib.rs | 1 + examples/napi/__test__/object-attr.spec.ts | 15 ++++++++++ examples/napi/__test__/typegen.spec.ts.md | 5 ++++ examples/napi/__test__/typegen.spec.ts.snap | Bin 3418 -> 3436 bytes examples/napi/__test__/values.spec.ts | 7 +++++ examples/napi/index.d.ts | 5 ++++ examples/napi/src/class.rs | 14 ++++++++++ 14 files changed, 139 insertions(+), 18 deletions(-) create mode 100644 examples/napi/__test__/object-attr.spec.ts diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 6e1be828..f82d7e3e 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -23,6 +23,9 @@ pub struct NapiFn { pub skip_typescript: bool, pub comments: Vec, pub parent_is_generator: bool, + pub writable: bool, + pub enumerable: bool, + pub configurable: bool, } #[derive(Debug, Clone)] @@ -94,6 +97,9 @@ pub struct NapiStructField { pub ty: syn::Type, pub getter: bool, pub setter: bool, + pub writable: bool, + pub enumerable: bool, + pub configurable: bool, pub comments: Vec, pub skip_typescript: bool, pub ts_type: Option, diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 66c7f6ba..3cbfcda9 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -7,6 +7,11 @@ mod r#enum; mod r#fn; mod r#struct; +pub const PROPERTY_ATTRIBUTE_DEFAULT: i32 = 0; +pub const PROPERTY_ATTRIBUTE_WRITABLE: i32 = 1 << 0; +pub const PROPERTY_ATTRIBUTE_ENUMERABLE: i32 = 1 << 1; +pub const PROPERTY_ATTRIBUTE_CONFIGURABLE: i32 = 1 << 2; + pub trait TryToTokens { fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()>; diff --git a/crates/backend/src/codegen/struct.rs b/crates/backend/src/codegen/struct.rs index 9a63ca3d..38a82fe6 100644 --- a/crates/backend/src/codegen/struct.rs +++ b/crates/backend/src/codegen/struct.rs @@ -695,9 +695,21 @@ impl NapiStruct { } let js_name = &field.js_name; + let mut attribute = super::PROPERTY_ATTRIBUTE_DEFAULT; + if field.writable { + attribute |= super::PROPERTY_ATTRIBUTE_WRITABLE; + } + if field.enumerable { + attribute |= super::PROPERTY_ATTRIBUTE_ENUMERABLE; + } + if field.configurable { + attribute |= super::PROPERTY_ATTRIBUTE_CONFIGURABLE; + } + let mut prop = quote! { napi::bindgen_prelude::Property::new(#js_name) .unwrap() + .with_property_attributes(napi::bindgen_prelude::PropertyAttributes::from_bits(#attribute).unwrap()) }; if field.getter { @@ -705,7 +717,7 @@ impl NapiStruct { (quote! { .with_getter(#getter_name) }).to_tokens(&mut prop); } - if field.setter { + if field.writable && field.setter { let setter_name = Ident::new(&format!("set_{}", field_name), Span::call_site()); (quote! { .with_setter(#setter_name) }).to_tokens(&mut prop); } @@ -757,9 +769,20 @@ impl NapiImpl { let intermediate_name = get_intermediate_ident(&item_str); methods.push(item.try_to_token_stream()?); + let mut attribute = super::PROPERTY_ATTRIBUTE_DEFAULT; + if item.writable { + attribute |= super::PROPERTY_ATTRIBUTE_WRITABLE; + } + if item.enumerable { + attribute |= super::PROPERTY_ATTRIBUTE_ENUMERABLE; + } + if item.configurable { + attribute |= super::PROPERTY_ATTRIBUTE_CONFIGURABLE; + } + let prop = props.entry(&item.js_name).or_insert_with(|| { quote! { - napi::bindgen_prelude::Property::new(#js_name).unwrap() + napi::bindgen_prelude::Property::new(#js_name).unwrap().with_property_attributes(napi::bindgen_prelude::PropertyAttributes::from_bits(#attribute).unwrap()) } }); diff --git a/crates/macro/src/parser/attrs.rs b/crates/macro/src/parser/attrs.rs index 54598ba1..3b3b0418 100644 --- a/crates/macro/src/parser/attrs.rs +++ b/crates/macro/src/parser/attrs.rs @@ -48,6 +48,9 @@ macro_rules! attrgen { (getter, Getter(Span, Option)), (setter, Setter(Span, Option)), (readonly, Readonly(Span)), + (enumerable, Enumerable(Span, Option)), + (writable, Writable(Span, Option)), + (configurable, Configurable(Span, Option)), (skip, Skip(Span)), (strict, Strict(Span)), (return_if_invalid, ReturnIfInvalid(Span)), @@ -116,6 +119,22 @@ macro_rules! methods { } }; + (@method $name:ident, $variant:ident(Span, Option)) => { + pub fn $name(&self) -> bool { + self.attrs + .iter() + .filter_map(|a| match &a.1 { + BindgenAttr::$variant(_, s) => { + a.0.set(true); + *s + } + _ => None, + }) + .next() + .unwrap_or(true) + } + }; + (@method $name:ident, $variant:ident(Span, Vec, Vec)) => { pub fn $name(&self) -> Option<(&[String], &[Span])> { self.attrs diff --git a/crates/macro/src/parser/mod.rs b/crates/macro/src/parser/mod.rs index 58f2ccd3..5450ff1b 100644 --- a/crates/macro/src/parser/mod.rs +++ b/crates/macro/src/parser/mod.rs @@ -111,6 +111,21 @@ impl Parse for BindgenAttr { return Ok(BindgenAttr::$variant(attr_span, val, span)) }); + (@parser $variant:ident(Span, Option)) => ({ + if let Ok(_) = input.parse::() { + let (val, _) = match input.parse::() { + Ok(str) => (str.value(), str.span()), + Err(_) => { + let ident = input.parse::()?.0; + (true, ident.span()) + } + }; + return Ok::(BindgenAttr::$variant(attr_span, Some(val))) + } else { + return Ok(BindgenAttr::$variant(attr_span, Some(true))) + } + }); + (@parser $variant:ident(Span, Vec, Vec)) => ({ input.parse::()?; let (vals, spans) = match input.parse::() { @@ -677,6 +692,9 @@ fn napi_fn_from_decl( ts_return_type: opts.ts_return_type().map(|(m, _)| m.to_owned()), skip_typescript: opts.skip_typescript().is_some(), parent_is_generator, + writable: opts.writable(), + enumerable: opts.enumerable(), + configurable: opts.configurable(), } }) } @@ -868,6 +886,9 @@ impl ConvertToAST for syn::ItemStruct { let ignored = field_opts.skip().is_some(); let readonly = field_opts.readonly().is_some(); + let writable = field_opts.writable(); + let enumerable = field_opts.enumerable(); + let configurable = field_opts.configurable(); let skip_typescript = field_opts.skip_typescript().is_some(); let ts_type = field_opts.ts_type().map(|e| e.0.to_string()); @@ -877,6 +898,9 @@ impl ConvertToAST for syn::ItemStruct { ty: field.ty.clone(), getter: !ignored, setter: !(ignored || readonly), + writable, + enumerable, + configurable, comments: extract_doc_comments(&field.attrs), skip_typescript, ts_type, diff --git a/crates/napi/Cargo.toml b/crates/napi/Cargo.toml index 3b508696..74160f43 100644 --- a/crates/napi/Cargo.toml +++ b/crates/napi/Cargo.toml @@ -51,6 +51,7 @@ tokio_time = ["tokio/time"] ctor = "0.1" once_cell = "1" thread_local = "1" +bitflags = "1" [dependencies.napi-sys] version = "2.2.2" diff --git a/crates/napi/src/js_values/object_property.rs b/crates/napi/src/js_values/object_property.rs index 9e89d949..4a777694 100644 --- a/crates/napi/src/js_values/object_property.rs +++ b/crates/napi/src/js_values/object_property.rs @@ -2,6 +2,8 @@ use std::convert::From; use std::ffi::CString; use std::ptr; +use bitflags::bitflags; + use crate::{sys, Callback, NapiRaw, Result}; #[derive(Clone)] @@ -29,31 +31,25 @@ impl Default for Property { } } -#[repr(i32)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum PropertyAttributes { - Default = sys::PropertyAttributes::default, - Writable = sys::PropertyAttributes::writable, - Enumerable = sys::PropertyAttributes::enumerable, - Configurable = sys::PropertyAttributes::configurable, - Static = sys::PropertyAttributes::static_, +bitflags! { + pub struct PropertyAttributes: i32 { + const Default = sys::PropertyAttributes::default; + const Writable = sys::PropertyAttributes::writable; + const Enumerable = sys::PropertyAttributes::enumerable; + const Configurable = sys::PropertyAttributes::configurable; + const Static = sys::PropertyAttributes::static_; + } } impl Default for PropertyAttributes { fn default() -> Self { - PropertyAttributes::Default + PropertyAttributes::Configurable | PropertyAttributes::Enumerable | PropertyAttributes::Writable } } impl From for sys::napi_property_attributes { fn from(value: PropertyAttributes) -> Self { - match value { - PropertyAttributes::Default => sys::PropertyAttributes::default, - PropertyAttributes::Writable => sys::PropertyAttributes::writable, - PropertyAttributes::Enumerable => sys::PropertyAttributes::enumerable, - PropertyAttributes::Configurable => sys::PropertyAttributes::configurable, - PropertyAttributes::Static => sys::PropertyAttributes::static_, - } + value.bits() } } diff --git a/crates/napi/src/lib.rs b/crates/napi/src/lib.rs index 476096fc..7892567b 100644 --- a/crates/napi/src/lib.rs +++ b/crates/napi/src/lib.rs @@ -1,5 +1,6 @@ #![deny(clippy::all)] #![forbid(unsafe_op_in_unsafe_fn)] +#![allow(non_upper_case_globals)] //! High level Node.js [N-API](https://nodejs.org/api/n-api.html) binding //! diff --git a/examples/napi/__test__/object-attr.spec.ts b/examples/napi/__test__/object-attr.spec.ts new file mode 100644 index 00000000..81769ffe --- /dev/null +++ b/examples/napi/__test__/object-attr.spec.ts @@ -0,0 +1,15 @@ +import test from 'ava' + +import { NotWritableClass } from '../index' + +test('Not Writable Class', (t) => { + const obj = new NotWritableClass('1') + t.throws(() => { + obj.name = '2' + }) + obj.setName('2') + t.is(obj.name, '2') + t.throws(() => { + obj.setName = () => {} + }) +}) diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 5e3dee5f..3ac6a9f6 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -277,6 +277,11 @@ Generated by [AVA](https://avajs.dev). static optionStartEnd(optional1: string | undefined | null, required: string, optional2?: string | undefined | null): string␊ static optionOnly(optional?: string | undefined | null): string␊ }␊ + export class NotWritableClass {␊ + name: string␊ + constructor(name: string)␊ + setName(name: string): void␊ + }␊ export class ClassWithFactory {␊ name: string␊ static withName(name: string): ClassWithFactory␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index 31385caf9201c91330806182ce19ee1381e1871b..197cd296a56b03d0e61acc180b6b135b14e57064 100644 GIT binary patch literal 3436 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 O-upl69=&qbFaQACO`Qh- literal 3418 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 diff --git a/examples/napi/__test__/values.spec.ts b/examples/napi/__test__/values.spec.ts index 72dc4cd5..5465716b 100644 --- a/examples/napi/__test__/values.spec.ts +++ b/examples/napi/__test__/values.spec.ts @@ -162,6 +162,13 @@ test('class', (t) => { t.is(dog.kind, Kind.Dog) t.is(dog.whoami(), 'Dog: 旺财') + t.notThrows(() => { + const rawMethod = dog.whoami + dog.whoami = function (...args) { + return rawMethod.apply(this, args) + } + }) + dog.name = '可乐' t.is(dog.name, '可乐') t.deepEqual(dog.returnOtherClass(), new Dog('Doge')) diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index b2500499..4a0f1e55 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -267,6 +267,11 @@ export class Optional { static optionStartEnd(optional1: string | undefined | null, required: string, optional2?: string | undefined | null): string static optionOnly(optional?: string | undefined | null): string } +export class NotWritableClass { + name: string + constructor(name: string) + setName(name: string): void +} export class ClassWithFactory { name: string static withName(name: string): ClassWithFactory diff --git a/examples/napi/src/class.rs b/examples/napi/src/class.rs index e01fc00b..a1ae310c 100644 --- a/examples/napi/src/class.rs +++ b/examples/napi/src/class.rs @@ -347,3 +347,17 @@ pub fn receive_object_with_class_field( ) -> Result> { Ok(object.bird) } + +#[napi(constructor)] +pub struct NotWritableClass { + #[napi(writable = false)] + pub name: String, +} + +#[napi] +impl NotWritableClass { + #[napi(writable = false)] + pub fn set_name(&mut self, name: String) { + self.name = name; + } +}