1 | use std::cell::{Cell, RefCell}; |
2 | use std::collections::HashMap; |
3 | |
4 | use napi_derive_backend::{bail_span, BindgenResult, Diagnostic}; |
5 | use proc_macro2::{Delimiter, Ident, Span, TokenTree}; |
6 | use quote::ToTokens; |
7 | use syn::parse::{Parse, ParseStream}; |
8 | use syn::spanned::Spanned; |
9 | use syn::Attribute; |
10 | |
11 | use crate::parser::AnyIdent; |
12 | |
13 | thread_local! { |
14 | static ATTRS: AttributeParseState = Default::default(); |
15 | static STRUCTS: StructParseState = Default::default(); |
16 | } |
17 | |
18 | #[derive (Default)] |
19 | struct StructParseState { |
20 | parsed: RefCell<HashMap<String, ParsedStruct>>, |
21 | } |
22 | |
23 | struct ParsedStruct { |
24 | js_name: String, |
25 | ctor_defined: bool, |
26 | } |
27 | |
28 | #[derive (Default)] |
29 | struct AttributeParseState { |
30 | parsed: Cell<usize>, |
31 | #[allow (unused)] |
32 | checks: Cell<usize>, |
33 | } |
34 | |
35 | #[derive (Debug)] |
36 | /// Parsed attributes from a `#[napi(..)]`. |
37 | pub struct BindgenAttrs { |
38 | /// Whether `#[napi]` attribute exists |
39 | pub exists: bool, |
40 | /// List of parsed attributes |
41 | pub attrs: Vec<(Cell<bool>, BindgenAttr)>, |
42 | /// Span of original attribute |
43 | pub span: Span, |
44 | } |
45 | |
46 | // NOTE: borrowed from wasm-bindgen |
47 | // some of them may useless in #[napi] macro |
48 | macro_rules! attrgen { |
49 | ($mac:ident) => { |
50 | $mac! { |
51 | (catch_unwind, CatchUnwind(Span)), |
52 | (js_name, JsName(Span, String, Span)), |
53 | (constructor, Constructor(Span)), |
54 | (factory, Factory(Span)), |
55 | (getter, Getter(Span, Option<Ident>)), |
56 | (setter, Setter(Span, Option<Ident>)), |
57 | (readonly, Readonly(Span)), |
58 | (enumerable, Enumerable(Span, Option<bool>), true), |
59 | (writable, Writable(Span, Option<bool>), true), |
60 | (configurable, Configurable(Span, Option<bool>), true), |
61 | (skip, Skip(Span)), |
62 | (strict, Strict(Span)), |
63 | (return_if_invalid, ReturnIfInvalid(Span)), |
64 | (object, Object(Span)), |
65 | (object_from_js, ObjectFromJs(Span, Option<bool>), true), |
66 | (object_to_js, ObjectToJs(Span, Option<bool>), true), |
67 | (custom_finalize, CustomFinalize(Span)), |
68 | (namespace, Namespace(Span, String, Span)), |
69 | (iterator, Iterator(Span)), |
70 | (ts_args_type, TsArgsType(Span, String, Span)), |
71 | (ts_return_type, TsReturnType(Span, String, Span)), |
72 | (ts_type, TsType(Span, String, Span)), |
73 | (ts_generic_types, TsGenericTypes(Span, String, Span)), |
74 | (string_enum, StringEnum(Span)), |
75 | (use_nullable, UseNullable(Span, Option<bool>), false), |
76 | |
77 | // impl later |
78 | // (inspectable, Inspectable(Span)), |
79 | // (typescript_custom_section, TypescriptCustomSection(Span)), |
80 | (skip_typescript, SkipTypescript(Span)), |
81 | // (getter_with_clone, GetterWithClone(Span)), |
82 | |
83 | // For testing purposes only. |
84 | // (assert_no_shim, AssertNoShim(Span)), |
85 | } |
86 | }; |
87 | } |
88 | |
89 | macro_rules! methods { |
90 | ($(($name:ident, $variant:ident($($contents:tt)*) $($extra_tokens:tt)*),)*) => { |
91 | $(methods!(@method $name, $variant($($contents)*) $($extra_tokens)*);)* |
92 | |
93 | #[cfg(feature = "strict" )] |
94 | #[allow(unused)] |
95 | pub fn check_used(&self) -> Result<(), Diagnostic> { |
96 | // Account for the fact this method was called |
97 | ATTRS.with(|state| state.checks.set(state.checks.get() + 1)); |
98 | |
99 | let mut errors = Vec::new(); |
100 | for (used, attr) in self.attrs.iter() { |
101 | if used.get() { |
102 | continue |
103 | } |
104 | let span = match attr { |
105 | $(BindgenAttr::$variant(span, ..) => span,)* |
106 | }; |
107 | errors.push(Diagnostic::span_error(*span, "unused #[napi] attribute" )); |
108 | } |
109 | Diagnostic::from_vec(errors) |
110 | } |
111 | |
112 | #[cfg(not(feature = "strict" ))] |
113 | pub fn check_used(&self) -> Result<(), Diagnostic> { |
114 | // Account for the fact this method was called |
115 | ATTRS.with(|state| state.checks.set(state.checks.get() + 1)); |
116 | Ok(()) |
117 | } |
118 | }; |
119 | |
120 | (@method $name:ident, $variant:ident(Span, String, Span)) => { |
121 | pub fn $name(&self) -> Option<(&str, Span)> { |
122 | self.attrs |
123 | .iter() |
124 | .filter_map(|a| match &a.1 { |
125 | BindgenAttr::$variant(_, s, span) => { |
126 | a.0.set(true); |
127 | Some((&s[..], *span)) |
128 | } |
129 | _ => None, |
130 | }) |
131 | .next() |
132 | } |
133 | }; |
134 | |
135 | (@method $name:ident, $variant:ident(Span, Option<bool>), $default_value:literal) => { |
136 | pub fn $name(&self) -> bool { |
137 | self.attrs |
138 | .iter() |
139 | .filter_map(|a| match &a.1 { |
140 | BindgenAttr::$variant(_, s) => { |
141 | a.0.set(true); |
142 | *s |
143 | } |
144 | _ => None, |
145 | }) |
146 | .next() |
147 | .unwrap_or($default_value) |
148 | } |
149 | }; |
150 | |
151 | (@method $name:ident, $variant:ident(Span, Vec<String>, Vec<Span>)) => { |
152 | pub fn $name(&self) -> Option<(&[String], &[Span])> { |
153 | self.attrs |
154 | .iter() |
155 | .filter_map(|a| match &a.1 { |
156 | BindgenAttr::$variant(_, ss, spans) => { |
157 | a.0.set(true); |
158 | Some((&ss[..], &spans[..])) |
159 | } |
160 | _ => None, |
161 | }) |
162 | .next() |
163 | } |
164 | }; |
165 | |
166 | (@method $name:ident, $variant:ident(Span, $($other:tt)*)) => { |
167 | #[allow(unused)] |
168 | pub fn $name(&self) -> Option<&$($other)*> { |
169 | self.attrs |
170 | .iter() |
171 | .filter_map(|a| match &a.1 { |
172 | BindgenAttr::$variant(_, s) => { |
173 | a.0.set(true); |
174 | Some(s) |
175 | } |
176 | _ => None, |
177 | }) |
178 | .next() |
179 | } |
180 | }; |
181 | |
182 | (@method $name:ident, $variant:ident($($other:tt)*)) => { |
183 | #[allow(unused)] |
184 | pub fn $name(&self) -> Option<&$($other)*> { |
185 | self.attrs |
186 | .iter() |
187 | .filter_map(|a| match &a.1 { |
188 | BindgenAttr::$variant(s) => { |
189 | a.0.set(true); |
190 | Some(s) |
191 | } |
192 | _ => None, |
193 | }) |
194 | .next() |
195 | } |
196 | }; |
197 | } |
198 | |
199 | impl BindgenAttrs { |
200 | /// Find and parse the napi attributes. |
201 | pub fn find(attrs: &mut Vec<syn::Attribute>) -> Result<BindgenAttrs, Diagnostic> { |
202 | for (index: usize, attr: &Attribute) in attrs.iter().enumerate() { |
203 | let attr: BindgenAttrs = BindgenAttrs::try_from(attr)?; |
204 | if attr.exists { |
205 | attrs.remove(index); |
206 | |
207 | return Ok(attr); |
208 | } |
209 | } |
210 | |
211 | Ok(BindgenAttrs::default()) |
212 | } |
213 | |
214 | attrgen!(methods); |
215 | } |
216 | |
217 | impl TryFrom<&Attribute> for BindgenAttrs { |
218 | type Error = Diagnostic; |
219 | |
220 | fn try_from(attr: &Attribute) -> Result<Self, Self::Error> { |
221 | let mut ret = BindgenAttrs { |
222 | exists: false, |
223 | attrs: vec![], |
224 | span: Span::call_site(), |
225 | }; |
226 | |
227 | if attr.path().is_ident("napi" ) { |
228 | ret.exists = true; |
229 | ret.span = attr.span(); |
230 | |
231 | let tts = attr.meta.to_token_stream().into_iter(); |
232 | let group = match tts.last() { |
233 | // #[napi(xxx)] |
234 | // ^^^^^^^^^ |
235 | Some(TokenTree::Group(d)) => d, |
236 | // #[napi] |
237 | // ^^^^ |
238 | Some(TokenTree::Ident(_)) => parse_quote!(()), |
239 | _ => bail_span!(attr, "invalid #[napi] attribute" ), |
240 | }; |
241 | |
242 | if group.delimiter() != Delimiter::Parenthesis { |
243 | bail_span!(attr, "malformed #[napi] attribute" ); |
244 | } |
245 | |
246 | let mut attrs: BindgenAttrs = syn::parse2(group.stream())?; |
247 | ret.attrs.append(&mut attrs.attrs); |
248 | } |
249 | |
250 | Ok(ret) |
251 | } |
252 | } |
253 | |
254 | impl Default for BindgenAttrs { |
255 | fn default() -> BindgenAttrs { |
256 | // Add 1 to the list of parsed attribute sets. We'll use this counter to |
257 | // sanity check that we call `check_used` an appropriate number of |
258 | // times. |
259 | ATTRS.with(|state: &AttributeParseState| state.parsed.set(val:state.parsed.get() + 1)); |
260 | BindgenAttrs { |
261 | span: Span::call_site(), |
262 | attrs: Vec::new(), |
263 | exists: false, |
264 | } |
265 | } |
266 | } |
267 | |
268 | macro_rules! gen_bindgen_attr { |
269 | ($( ($method:ident, $variant:ident($($associated_data:tt)*) $($extra_tokens:tt)*) ,)*) => { |
270 | /// The possible attributes in the `#[napi]`. |
271 | #[derive(Debug)] |
272 | pub enum BindgenAttr { |
273 | $($variant($($associated_data)*)),* |
274 | } |
275 | } |
276 | } |
277 | |
278 | attrgen!(gen_bindgen_attr); |
279 | |
280 | pub fn record_struct(ident: &Ident, js_name: String, opts: &BindgenAttrs) { |
281 | STRUCTS.with(|state: &StructParseState| { |
282 | let struct_name: String = ident.to_string(); |
283 | |
284 | let mut map: RefMut<'_, HashMap> = state.parsed.borrow_mut(); |
285 | |
286 | map.insert( |
287 | k:struct_name, |
288 | v:ParsedStruct { |
289 | js_name, |
290 | ctor_defined: opts.constructor().is_some(), |
291 | }, |
292 | ); |
293 | }); |
294 | } |
295 | |
296 | pub fn check_recorded_struct_for_impl(ident: &Ident, opts: &BindgenAttrs) -> BindgenResult<String> { |
297 | STRUCTS.with(|state| { |
298 | let struct_name = ident.to_string(); |
299 | let mut map = state.parsed.borrow_mut(); |
300 | if let Some(parsed) = map.get_mut(&struct_name) { |
301 | if opts.constructor().is_some() && !cfg!(debug_assertions) { |
302 | if parsed.ctor_defined { |
303 | bail_span!( |
304 | ident, |
305 | "Constructor has already been defined for struct ` {}`" , |
306 | &struct_name |
307 | ); |
308 | } else { |
309 | parsed.ctor_defined = true; |
310 | } |
311 | } |
312 | |
313 | Ok(parsed.js_name.clone()) |
314 | } else { |
315 | bail_span!( |
316 | ident, |
317 | "Did not find struct ` {}` parsed before expand #[napi] for impl" , |
318 | &struct_name, |
319 | ) |
320 | } |
321 | }) |
322 | } |
323 | |
324 | impl Parse for BindgenAttrs { |
325 | fn parse(input: ParseStream) -> syn::Result<Self> { |
326 | let mut attrs: BindgenAttrs = BindgenAttrs::default(); |
327 | if input.is_empty() { |
328 | return Ok(attrs); |
329 | } |
330 | |
331 | let opts: Punctuated = syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated(input)?; |
332 | attrs.attrs = opts.into_iter().map(|c: BindgenAttr| (Cell::new(false), c)).collect(); |
333 | Ok(attrs) |
334 | } |
335 | } |
336 | |
337 | impl Parse for BindgenAttr { |
338 | fn parse(input: ParseStream) -> syn::Result<Self> { |
339 | let original = input.fork(); |
340 | let attr: AnyIdent = input.parse()?; |
341 | let attr = attr.0; |
342 | let attr_span = attr.span(); |
343 | let attr_string = attr.to_string(); |
344 | let raw_attr_string = format!("r# {}" , attr_string); |
345 | |
346 | macro_rules! parsers { |
347 | ($(($name:ident, $($contents:tt)*),)*) => { |
348 | $( |
349 | if attr_string == stringify!($name) || raw_attr_string == stringify!($name) { |
350 | parsers!( |
351 | @parser |
352 | $($contents)* |
353 | ); |
354 | } |
355 | )* |
356 | }; |
357 | |
358 | (@parser $variant:ident(Span)) => ({ |
359 | return Ok(BindgenAttr::$variant(attr_span)); |
360 | }); |
361 | |
362 | (@parser $variant:ident(Span, Ident)) => ({ |
363 | input.parse::<Token![=]>()?; |
364 | let ident = input.parse::<AnyIdent>()?.0; |
365 | return Ok(BindgenAttr::$variant(attr_span, ident)) |
366 | }); |
367 | |
368 | (@parser $variant:ident(Span, Option<Ident>)) => ({ |
369 | if input.parse::<Token![=]>().is_ok() { |
370 | let ident = input.parse::<AnyIdent>()?.0; |
371 | return Ok(BindgenAttr::$variant(attr_span, Some(ident))) |
372 | } else { |
373 | return Ok(BindgenAttr::$variant(attr_span, None)); |
374 | } |
375 | }); |
376 | |
377 | (@parser $variant:ident(Span, syn::Path)) => ({ |
378 | input.parse::<Token![=]>()?; |
379 | return Ok(BindgenAttr::$variant(attr_span, input.parse()?)); |
380 | }); |
381 | |
382 | (@parser $variant:ident(Span, syn::Expr)) => ({ |
383 | input.parse::<Token![=]>()?; |
384 | return Ok(BindgenAttr::$variant(attr_span, input.parse()?)); |
385 | }); |
386 | |
387 | (@parser $variant:ident(Span, String, Span)) => ({ |
388 | input.parse::<Token![=]>()?; |
389 | let (val, span) = match input.parse::<syn::LitStr>() { |
390 | Ok(str) => (str.value(), str.span()), |
391 | Err(_) => { |
392 | let ident = input.parse::<AnyIdent>()?.0; |
393 | (ident.to_string(), ident.span()) |
394 | } |
395 | }; |
396 | return Ok(BindgenAttr::$variant(attr_span, val, span)) |
397 | }); |
398 | |
399 | (@parser $variant:ident(Span, Option<bool>), $default_value:literal) => ({ |
400 | if let Ok(_) = input.parse::<Token![=]>() { |
401 | let (val, _) = match input.parse::<syn::LitBool>() { |
402 | Ok(str) => (str.value(), str.span()), |
403 | Err(_) => { |
404 | let ident = input.parse::<AnyIdent>()?.0; |
405 | (true, ident.span()) |
406 | } |
407 | }; |
408 | return Ok::<BindgenAttr, syn::Error>(BindgenAttr::$variant(attr_span, Some(val))) |
409 | } else { |
410 | return Ok(BindgenAttr::$variant(attr_span, Some($default_value))) |
411 | } |
412 | }); |
413 | |
414 | (@parser $variant:ident(Span, Vec<String>, Vec<Span>)) => ({ |
415 | input.parse::<Token![=]>()?; |
416 | let (vals, spans) = match input.parse::<syn::ExprArray>() { |
417 | Ok(exprs) => { |
418 | let mut vals = vec![]; |
419 | let mut spans = vec![]; |
420 | |
421 | for expr in exprs.elems.iter() { |
422 | if let syn::Expr::Lit(syn::ExprLit { |
423 | lit: syn::Lit::Str(ref str), |
424 | .. |
425 | }) = expr { |
426 | vals.push(str.value()); |
427 | spans.push(str.span()); |
428 | } else { |
429 | return Err(syn::Error::new(expr.span(), "expected string literals" )); |
430 | } |
431 | } |
432 | |
433 | (vals, spans) |
434 | }, |
435 | Err(_) => { |
436 | let ident = input.parse::<AnyIdent>()?.0; |
437 | (vec![ident.to_string()], vec![ident.span()]) |
438 | } |
439 | }; |
440 | return Ok(BindgenAttr::$variant(attr_span, vals, spans)) |
441 | }); |
442 | } |
443 | |
444 | attrgen!(parsers); |
445 | |
446 | Err(original.error("unknown attribute" )) |
447 | } |
448 | } |
449 | |