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, Option<(String, 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<(String, Span)>)) => { |
136 | pub fn $name(&self) -> Option<Option<&(String, Span)>> { |
137 | self.attrs |
138 | .iter() |
139 | .filter_map(|a| match &a.1 { |
140 | BindgenAttr::$variant(_, s) => { |
141 | a.0.set(true); |
142 | Some(s.as_ref()) |
143 | } |
144 | _ => None, |
145 | }) |
146 | .next() |
147 | } |
148 | }; |
149 | |
150 | (@method $name:ident, $variant:ident(Span, Option<bool>), $default_value:literal) => { |
151 | pub fn $name(&self) -> bool { |
152 | self.attrs |
153 | .iter() |
154 | .filter_map(|a| match &a.1 { |
155 | BindgenAttr::$variant(_, s) => { |
156 | a.0.set(true); |
157 | *s |
158 | } |
159 | _ => None, |
160 | }) |
161 | .next() |
162 | .unwrap_or($default_value) |
163 | } |
164 | }; |
165 | |
166 | (@method $name:ident, $variant:ident(Span, Vec<String>, Vec<Span>)) => { |
167 | pub fn $name(&self) -> Option<(&[String], &[Span])> { |
168 | self.attrs |
169 | .iter() |
170 | .filter_map(|a| match &a.1 { |
171 | BindgenAttr::$variant(_, ss, spans) => { |
172 | a.0.set(true); |
173 | Some((&ss[..], &spans[..])) |
174 | } |
175 | _ => None, |
176 | }) |
177 | .next() |
178 | } |
179 | }; |
180 | |
181 | (@method $name:ident, $variant:ident(Span, $($other:tt)*)) => { |
182 | #[allow(unused)] |
183 | pub fn $name(&self) -> Option<&$($other)*> { |
184 | self.attrs |
185 | .iter() |
186 | .filter_map(|a| match &a.1 { |
187 | BindgenAttr::$variant(_, s) => { |
188 | a.0.set(true); |
189 | Some(s) |
190 | } |
191 | _ => None, |
192 | }) |
193 | .next() |
194 | } |
195 | }; |
196 | |
197 | (@method $name:ident, $variant:ident($($other:tt)*)) => { |
198 | #[allow(unused)] |
199 | pub fn $name(&self) -> Option<&$($other)*> { |
200 | self.attrs |
201 | .iter() |
202 | .filter_map(|a| match &a.1 { |
203 | BindgenAttr::$variant(s) => { |
204 | a.0.set(true); |
205 | Some(s) |
206 | } |
207 | _ => None, |
208 | }) |
209 | .next() |
210 | } |
211 | }; |
212 | } |
213 | |
214 | impl BindgenAttrs { |
215 | /// Find and parse the napi attributes. |
216 | pub fn find(attrs: &mut Vec<syn::Attribute>) -> Result<BindgenAttrs, Diagnostic> { |
217 | for (index: usize, attr: &Attribute) in attrs.iter().enumerate() { |
218 | let attr: BindgenAttrs = BindgenAttrs::try_from(attr)?; |
219 | if attr.exists { |
220 | attrs.remove(index); |
221 | |
222 | return Ok(attr); |
223 | } |
224 | } |
225 | |
226 | Ok(BindgenAttrs::default()) |
227 | } |
228 | |
229 | attrgen!(methods); |
230 | } |
231 | |
232 | impl TryFrom<&Attribute> for BindgenAttrs { |
233 | type Error = Diagnostic; |
234 | |
235 | fn try_from(attr: &Attribute) -> Result<Self, Self::Error> { |
236 | let mut ret = BindgenAttrs { |
237 | exists: false, |
238 | attrs: vec![], |
239 | span: Span::call_site(), |
240 | }; |
241 | |
242 | if attr.path().is_ident("napi" ) { |
243 | ret.exists = true; |
244 | ret.span = attr.span(); |
245 | |
246 | let tts = attr.meta.to_token_stream().into_iter(); |
247 | let group = match tts.last() { |
248 | // #[napi(xxx)] |
249 | // ^^^^^^^^^ |
250 | Some(TokenTree::Group(d)) => d, |
251 | // #[napi] |
252 | // ^^^^ |
253 | Some(TokenTree::Ident(_)) => parse_quote!(()), |
254 | _ => bail_span!(attr, "invalid #[napi] attribute" ), |
255 | }; |
256 | |
257 | if group.delimiter() != Delimiter::Parenthesis { |
258 | bail_span!(attr, "malformed #[napi] attribute" ); |
259 | } |
260 | |
261 | let mut attrs: BindgenAttrs = syn::parse2(group.stream())?; |
262 | ret.attrs.append(&mut attrs.attrs); |
263 | } |
264 | |
265 | Ok(ret) |
266 | } |
267 | } |
268 | |
269 | impl Default for BindgenAttrs { |
270 | fn default() -> BindgenAttrs { |
271 | // Add 1 to the list of parsed attribute sets. We'll use this counter to |
272 | // sanity check that we call `check_used` an appropriate number of |
273 | // times. |
274 | ATTRS.with(|state: &AttributeParseState| state.parsed.set(val:state.parsed.get() + 1)); |
275 | BindgenAttrs { |
276 | span: Span::call_site(), |
277 | attrs: Vec::new(), |
278 | exists: false, |
279 | } |
280 | } |
281 | } |
282 | |
283 | macro_rules! gen_bindgen_attr { |
284 | ($( ($method:ident, $variant:ident($($associated_data:tt)*) $($extra_tokens:tt)*) ,)*) => { |
285 | /// The possible attributes in the `#[napi]`. |
286 | #[derive(Debug)] |
287 | pub enum BindgenAttr { |
288 | $($variant($($associated_data)*)),* |
289 | } |
290 | } |
291 | } |
292 | |
293 | attrgen!(gen_bindgen_attr); |
294 | |
295 | pub fn record_struct(ident: &Ident, js_name: String, opts: &BindgenAttrs) { |
296 | STRUCTS.with(|state: &StructParseState| { |
297 | let struct_name: String = ident.to_string(); |
298 | |
299 | let mut map: RefMut<'_, HashMap> = state.parsed.borrow_mut(); |
300 | |
301 | map.insert( |
302 | k:struct_name, |
303 | v:ParsedStruct { |
304 | js_name, |
305 | ctor_defined: opts.constructor().is_some(), |
306 | }, |
307 | ); |
308 | }); |
309 | } |
310 | |
311 | pub fn check_recorded_struct_for_impl(ident: &Ident, opts: &BindgenAttrs) -> BindgenResult<String> { |
312 | STRUCTS.with(|state| { |
313 | let struct_name = ident.to_string(); |
314 | let mut map = state.parsed.borrow_mut(); |
315 | if let Some(parsed) = map.get_mut(&struct_name) { |
316 | if opts.constructor().is_some() && !cfg!(debug_assertions) { |
317 | if parsed.ctor_defined { |
318 | bail_span!( |
319 | ident, |
320 | "Constructor has already been defined for struct ` {}`" , |
321 | &struct_name |
322 | ); |
323 | } else { |
324 | parsed.ctor_defined = true; |
325 | } |
326 | } |
327 | |
328 | Ok(parsed.js_name.clone()) |
329 | } else { |
330 | bail_span!( |
331 | ident, |
332 | "Did not find struct ` {}` parsed before expand #[napi] for impl" , |
333 | &struct_name, |
334 | ) |
335 | } |
336 | }) |
337 | } |
338 | |
339 | impl Parse for BindgenAttrs { |
340 | fn parse(input: ParseStream) -> syn::Result<Self> { |
341 | let mut attrs: BindgenAttrs = BindgenAttrs::default(); |
342 | if input.is_empty() { |
343 | return Ok(attrs); |
344 | } |
345 | |
346 | let opts: Punctuated = syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated(input)?; |
347 | attrs.attrs = opts.into_iter().map(|c: BindgenAttr| (Cell::new(false), c)).collect(); |
348 | Ok(attrs) |
349 | } |
350 | } |
351 | |
352 | impl Parse for BindgenAttr { |
353 | fn parse(input: ParseStream) -> syn::Result<Self> { |
354 | let original = input.fork(); |
355 | let attr: AnyIdent = input.parse()?; |
356 | let attr = attr.0; |
357 | let attr_span = attr.span(); |
358 | let attr_string = attr.to_string(); |
359 | let raw_attr_string = format!("r# {}" , attr_string); |
360 | |
361 | macro_rules! parsers { |
362 | ($(($name:ident, $($contents:tt)*),)*) => { |
363 | $( |
364 | if attr_string == stringify!($name) || raw_attr_string == stringify!($name) { |
365 | parsers!( |
366 | @parser |
367 | $($contents)* |
368 | ); |
369 | } |
370 | )* |
371 | }; |
372 | |
373 | (@parser $variant:ident(Span)) => ({ |
374 | return Ok(BindgenAttr::$variant(attr_span)); |
375 | }); |
376 | |
377 | (@parser $variant:ident(Span, Ident)) => ({ |
378 | input.parse::<Token![=]>()?; |
379 | let ident = input.parse::<AnyIdent>()?.0; |
380 | return Ok(BindgenAttr::$variant(attr_span, ident)) |
381 | }); |
382 | |
383 | (@parser $variant:ident(Span, Option<Ident>)) => ({ |
384 | if input.parse::<Token![=]>().is_ok() { |
385 | let ident = input.parse::<AnyIdent>()?.0; |
386 | return Ok(BindgenAttr::$variant(attr_span, Some(ident))) |
387 | } else { |
388 | return Ok(BindgenAttr::$variant(attr_span, None)); |
389 | } |
390 | }); |
391 | |
392 | (@parser $variant:ident(Span, syn::Path)) => ({ |
393 | input.parse::<Token![=]>()?; |
394 | return Ok(BindgenAttr::$variant(attr_span, input.parse()?)); |
395 | }); |
396 | |
397 | (@parser $variant:ident(Span, syn::Expr)) => ({ |
398 | input.parse::<Token![=]>()?; |
399 | return Ok(BindgenAttr::$variant(attr_span, input.parse()?)); |
400 | }); |
401 | |
402 | (@parser $variant:ident(Span, String, Span)) => ({ |
403 | input.parse::<Token![=]>()?; |
404 | let (val, span) = match input.parse::<syn::LitStr>() { |
405 | Ok(str) => (str.value(), str.span()), |
406 | Err(_) => { |
407 | let ident = input.parse::<AnyIdent>()?.0; |
408 | (ident.to_string(), ident.span()) |
409 | } |
410 | }; |
411 | return Ok(BindgenAttr::$variant(attr_span, val, span)) |
412 | }); |
413 | |
414 | (@parser $variant:ident(Span, Option<(String, Span)>)) => ({ |
415 | if let Ok(_) = input.parse::<Token![=]>() { |
416 | let val = match input.parse::<syn::LitStr>() { |
417 | Ok(str) => Some((str.value(), str.span())), |
418 | Err(_) => { |
419 | let ident = input.parse::<AnyIdent>()?.0; |
420 | Some((ident.to_string(), ident.span())) |
421 | } |
422 | }; |
423 | return Ok(BindgenAttr::$variant(attr_span, val)) |
424 | } else { |
425 | return Ok(BindgenAttr::$variant(attr_span, None)) |
426 | } |
427 | }); |
428 | |
429 | (@parser $variant:ident(Span, Option<bool>), $default_value:literal) => ({ |
430 | if let Ok(_) = input.parse::<Token![=]>() { |
431 | let (val, _) = match input.parse::<syn::LitBool>() { |
432 | Ok(str) => (str.value(), str.span()), |
433 | Err(_) => { |
434 | let ident = input.parse::<AnyIdent>()?.0; |
435 | (true, ident.span()) |
436 | } |
437 | }; |
438 | return Ok::<BindgenAttr, syn::Error>(BindgenAttr::$variant(attr_span, Some(val))) |
439 | } else { |
440 | return Ok(BindgenAttr::$variant(attr_span, Some($default_value))) |
441 | } |
442 | }); |
443 | |
444 | (@parser $variant:ident(Span, Vec<String>, Vec<Span>)) => ({ |
445 | input.parse::<Token![=]>()?; |
446 | let (vals, spans) = match input.parse::<syn::ExprArray>() { |
447 | Ok(exprs) => { |
448 | let mut vals = vec![]; |
449 | let mut spans = vec![]; |
450 | |
451 | for expr in exprs.elems.iter() { |
452 | if let syn::Expr::Lit(syn::ExprLit { |
453 | lit: syn::Lit::Str(ref str), |
454 | .. |
455 | }) = expr { |
456 | vals.push(str.value()); |
457 | spans.push(str.span()); |
458 | } else { |
459 | return Err(syn::Error::new(expr.span(), "expected string literals" )); |
460 | } |
461 | } |
462 | |
463 | (vals, spans) |
464 | }, |
465 | Err(_) => { |
466 | let ident = input.parse::<AnyIdent>()?.0; |
467 | (vec![ident.to_string()], vec![ident.span()]) |
468 | } |
469 | }; |
470 | return Ok(BindgenAttr::$variant(attr_span, vals, spans)) |
471 | }); |
472 | } |
473 | |
474 | attrgen!(parsers); |
475 | |
476 | Err(original.error("unknown attribute" )) |
477 | } |
478 | } |
479 | |