| 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 | |