wasm_bindgen_webidl/
lib.rs

1/*!
2# `wasm_bindgen_webidl`
3
4Converts WebIDL into wasm-bindgen's internal AST form, so that bindings can be
5emitted for the types and methods described in the WebIDL.
6 */
7
8#![deny(missing_docs)]
9#![deny(missing_debug_implementations)]
10#![doc(html_root_url = "https://docs.rs/wasm-bindgen-webidl/0.2")]
11
12mod constants;
13mod first_pass;
14mod generator;
15mod idl_type;
16mod traverse;
17mod util;
18
19use crate::first_pass::{CallbackInterfaceData, OperationData};
20use crate::first_pass::{FirstPass, FirstPassRecord, InterfaceData, OperationId};
21use crate::generator::{
22    Dictionary, DictionaryField, Enum, EnumVariant, Function, Interface, InterfaceAttribute,
23    InterfaceAttributeKind, InterfaceConst, InterfaceMethod, Namespace,
24};
25use crate::idl_type::ToIdlType;
26use crate::traverse::TraverseType;
27use crate::util::{
28    camel_case_ident, is_structural, is_type_unstable, read_dir, shouty_snake_case_ident,
29    snake_case_ident, throws, webidl_const_v_to_backend_const_v, TypePosition,
30};
31use anyhow::Context;
32use anyhow::Result;
33use proc_macro2::{Ident, TokenStream};
34use quote::ToTokens;
35use sourcefile::SourceFile;
36use std::collections::{BTreeMap, BTreeSet, HashSet};
37use std::ffi::OsStr;
38use std::fmt;
39use std::fs;
40use std::path::{Path, PathBuf};
41use std::process::Command;
42use wasm_bindgen_backend::util::rust_ident;
43use weedle::attribute::ExtendedAttributeList;
44use weedle::common::Identifier;
45use weedle::dictionary::DictionaryMember;
46use weedle::interface::InterfaceMember;
47use weedle::Parse;
48
49/// Options to configure the conversion process
50#[derive(Debug)]
51pub struct Options {
52    /// Whether to generate cfg features or not
53    pub features: bool,
54}
55
56#[derive(Default)]
57struct Program {
58    tokens: TokenStream,
59    required_features: BTreeSet<String>,
60}
61
62impl Program {
63    fn to_string(&self) -> Option<String> {
64        if self.tokens.is_empty() {
65            None
66        } else {
67            Some(self.tokens.to_string())
68        }
69    }
70}
71
72/// A parse error indicating where parsing failed
73#[derive(Debug)]
74pub struct WebIDLParseError(pub usize);
75
76impl fmt::Display for WebIDLParseError {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        write!(f, "failed to parse webidl at byte position {}", self.0)
79    }
80}
81
82impl std::error::Error for WebIDLParseError {}
83
84#[derive(Clone, Copy, PartialEq)]
85pub(crate) enum ApiStability {
86    Stable,
87    Unstable,
88}
89
90impl ApiStability {
91    pub(crate) fn is_unstable(self) -> bool {
92        self == Self::Unstable
93    }
94}
95
96impl Default for ApiStability {
97    fn default() -> Self {
98        Self::Stable
99    }
100}
101
102fn parse_source(source: &str) -> Result<Vec<weedle::Definition>> {
103    match weedle::Definitions::parse(source) {
104        Ok(("", parsed)) => Ok(parsed),
105
106        Ok((remaining, _))
107        | Err(weedle::Err::Error((remaining, _)))
108        | Err(weedle::Err::Failure((remaining, _))) => {
109            Err(WebIDLParseError(source.len() - remaining.len()).into())
110        }
111
112        Err(weedle::Err::Incomplete(needed)) => {
113            Err(anyhow::anyhow!("needed {:?} more bytes", needed))
114        }
115    }
116}
117
118/// Parse a string of WebIDL source text into a wasm-bindgen AST.
119fn parse(
120    webidl_source: &str,
121    unstable_source: &str,
122    options: Options,
123) -> Result<BTreeMap<String, Program>> {
124    let mut first_pass_record: FirstPassRecord = Default::default();
125
126    let definitions = parse_source(webidl_source)?;
127    definitions.first_pass(&mut first_pass_record, ApiStability::Stable)?;
128
129    let unstable_definitions = parse_source(unstable_source)?;
130
131    // Gather unstable type Identifiers so that stable APIs can be downgraded
132    // to unstable if they accept one of these types
133    let unstable_types: HashSet<Identifier> = unstable_definitions
134        .iter()
135        .flat_map(|definition| {
136            use weedle::Definition::*;
137            match definition {
138                Dictionary(v) => Some(v.identifier),
139                Enum(v) => Some(v.identifier),
140                Interface(v) => Some(v.identifier),
141                _ => None,
142            }
143        })
144        .collect();
145
146    unstable_definitions.first_pass(&mut first_pass_record, ApiStability::Unstable)?;
147
148    let mut types: BTreeMap<String, Program> = BTreeMap::new();
149
150    for (js_name, e) in first_pass_record.enums.iter() {
151        let name = rust_ident(&camel_case_ident(js_name));
152        let program = types.entry(name.to_string()).or_default();
153        first_pass_record.append_enum(&options, program, name, js_name, e);
154    }
155    for (js_name, d) in first_pass_record.dictionaries.iter() {
156        let name = rust_ident(&camel_case_ident(js_name));
157        let program = types.entry(name.to_string()).or_default();
158        first_pass_record.append_dictionary(
159            &options,
160            program,
161            name,
162            js_name.to_string(),
163            d,
164            &unstable_types,
165        );
166    }
167    for (js_name, n) in first_pass_record.namespaces.iter() {
168        let name = rust_ident(&snake_case_ident(js_name));
169        let program = types.entry(name.to_string()).or_default();
170        first_pass_record.append_ns(&options, program, name, js_name.to_string(), n);
171    }
172    for (js_name, d) in first_pass_record.interfaces.iter() {
173        let name = rust_ident(&camel_case_ident(js_name));
174        let program = types.entry(name.to_string()).or_default();
175        first_pass_record.append_interface(
176            &options,
177            program,
178            name,
179            js_name.to_string(),
180            &unstable_types,
181            d,
182        );
183    }
184    for (js_name, d) in first_pass_record.callback_interfaces.iter() {
185        let name = rust_ident(&camel_case_ident(js_name));
186        let program = types.entry(name.to_string()).or_default();
187        first_pass_record.append_callback_interface(
188            &options,
189            program,
190            name,
191            js_name.to_string(),
192            d,
193        );
194    }
195
196    Ok(types)
197}
198
199/// Data for a single feature
200#[derive(Debug)]
201pub struct Feature {
202    /// Generated code
203    pub code: String,
204
205    /// Required features
206    pub required_features: Vec<String>,
207}
208
209/// Compile the given WebIDL source text into Rust source text containing
210/// `wasm-bindgen` bindings to the things described in the WebIDL.
211pub fn compile(
212    webidl_source: &str,
213    experimental_source: &str,
214    options: Options,
215) -> Result<BTreeMap<String, Feature>> {
216    let ast = parse(webidl_source, experimental_source, options)?;
217
218    let features = ast
219        .into_iter()
220        .filter_map(|(name, program)| {
221            let code = program.to_string()?;
222            let required_features = program.required_features.into_iter().collect();
223            Some((
224                name,
225                Feature {
226                    required_features,
227                    code,
228                },
229            ))
230        })
231        .collect();
232
233    Ok(features)
234}
235
236impl<'src> FirstPassRecord<'src> {
237    fn append_enum(
238        &self,
239        options: &Options,
240        program: &mut Program,
241        name: Ident,
242        js_name: &str,
243        data: &first_pass::EnumData<'src>,
244    ) {
245        let enum_ = data.definition;
246        let unstable = data.stability.is_unstable();
247
248        assert_eq!(js_name, enum_.identifier.0);
249
250        let variants = enum_
251            .values
252            .body
253            .list
254            .iter()
255            .map(|v| {
256                let name = if !v.0.is_empty() {
257                    rust_ident(camel_case_ident(&v.0).as_str())
258                } else {
259                    rust_ident("None")
260                };
261
262                let value = v.0.to_string();
263
264                EnumVariant { name, value }
265            })
266            .collect::<Vec<_>>();
267
268        Enum {
269            name,
270            variants,
271            unstable,
272        }
273        .generate(options)
274        .to_tokens(&mut program.tokens);
275    }
276
277    // tons more data for what's going on here at
278    // https://www.w3.org/TR/WebIDL-1/#idl-dictionaries
279    fn append_dictionary(
280        &self,
281        options: &Options,
282        program: &mut Program,
283        name: Ident,
284        js_name: String,
285        data: &first_pass::DictionaryData<'src>,
286        unstable_types: &HashSet<Identifier>,
287    ) {
288        let def = match data.definition {
289            Some(def) => def,
290            None => return,
291        };
292
293        assert_eq!(js_name, def.identifier.0);
294
295        let unstable = data.stability.is_unstable();
296
297        let mut fields = Vec::new();
298
299        if !self.append_dictionary_members(&js_name, &mut fields, unstable, unstable_types) {
300            return;
301        }
302
303        Dictionary {
304            name,
305            js_name,
306            fields,
307            unstable,
308        }
309        .generate(options)
310        .to_tokens(&mut program.tokens);
311    }
312
313    fn append_dictionary_members(
314        &self,
315        dict: &'src str,
316        dst: &mut Vec<DictionaryField>,
317        unstable: bool,
318        unstable_types: &HashSet<Identifier>,
319    ) -> bool {
320        let dict_data = &self.dictionaries[&dict];
321        let definition = dict_data.definition.unwrap();
322
323        // > The order of the dictionary members on a given dictionary is
324        // > such that inherited dictionary members are ordered before
325        // > non-inherited members ...
326        if let Some(parent) = &definition.inheritance {
327            if !self.append_dictionary_members(parent.identifier.0, dst, unstable, unstable_types) {
328                return false;
329            }
330        }
331
332        // > ... and the dictionary members on the one dictionary
333        // > definition (including any partial dictionary definitions) are
334        // > ordered lexicographically by the Unicode codepoints that
335        // > comprise their identifiers.
336        let start = dst.len();
337        let members = definition.members.body.iter();
338        let partials = dict_data.partials.iter().flat_map(|d| &d.members.body);
339        for member in members.chain(partials) {
340            match self.dictionary_field(member, unstable, unstable_types) {
341                Some(f) => dst.push(f),
342                None => {
343                    log::warn!(
344                        "unsupported dictionary field {:?}",
345                        (dict, member.identifier.0),
346                    );
347                    // If this is required then we can't support the
348                    // dictionary at all, but if it's not required we can
349                    // avoid generating bindings for the field and keep
350                    // going otherwise.
351                    if member.required.is_some() {
352                        return false;
353                    }
354                }
355            }
356        }
357        dst[start..].sort_by_key(|f| f.js_name.clone());
358
359        return true;
360    }
361
362    fn dictionary_field(
363        &self,
364        field: &'src DictionaryMember<'src>,
365        unstable: bool,
366        unstable_types: &HashSet<Identifier>,
367    ) -> Option<DictionaryField> {
368        let unstable_override = match unstable {
369            true => true,
370            false => is_type_unstable(&field.type_, unstable_types),
371        };
372
373        // use argument position now as we're just binding setters
374        let ty = field
375            .type_
376            .to_idl_type(self)
377            .to_syn_type(TypePosition::Argument)
378            .unwrap_or(None)?;
379
380        // Slice types aren't supported because they don't implement
381        // `Into<JsValue>`
382        match ty {
383            syn::Type::Reference(ref i) => match &*i.elem {
384                syn::Type::Slice(_) => return None,
385                _ => (),
386            },
387            syn::Type::Path(ref path, ..) =>
388            // check that our inner don't contains slices either
389            {
390                for seg in path.path.segments.iter() {
391                    if let syn::PathArguments::AngleBracketed(ref arg) = seg.arguments {
392                        for elem in &arg.args {
393                            if let syn::GenericArgument::Type(syn::Type::Reference(ref i)) = elem {
394                                match &*i.elem {
395                                    syn::Type::Slice(_) => return None,
396                                    _ => (),
397                                }
398                            }
399                        }
400                    }
401                }
402            }
403            _ => (),
404        };
405
406        // Similarly i64/u64 aren't supported because they don't
407        // implement `Into<JsValue>`
408        let mut any_64bit = false;
409
410        ty.traverse_type(&mut |ident| {
411            if !any_64bit {
412                if ident == "u64" || ident == "i64" {
413                    any_64bit = true;
414                }
415            }
416        });
417
418        if any_64bit {
419            return None;
420        }
421
422        Some(DictionaryField {
423            required: field.required.is_some(),
424            name: rust_ident(&snake_case_ident(field.identifier.0)),
425            js_name: field.identifier.0.to_string(),
426            ty,
427            unstable: unstable_override,
428        })
429    }
430
431    fn append_ns(
432        &'src self,
433        options: &Options,
434        program: &mut Program,
435        name: Ident,
436        js_name: String,
437        ns: &'src first_pass::NamespaceData<'src>,
438    ) {
439        let mut functions = vec![];
440
441        for (id, data) in ns.operations.iter() {
442            self.append_ns_member(&mut functions, &js_name, id, data);
443        }
444
445        if !functions.is_empty() {
446            Namespace {
447                name,
448                js_name,
449                functions,
450            }
451            .generate(options)
452            .to_tokens(&mut program.tokens);
453        }
454    }
455
456    fn append_ns_member(
457        &self,
458        functions: &mut Vec<Function>,
459        js_name: &'src str,
460        id: &OperationId<'src>,
461        data: &OperationData<'src>,
462    ) {
463        match id {
464            OperationId::Operation(Some(_)) => {}
465            OperationId::Constructor(_)
466            | OperationId::NamedConstructor(_)
467            | OperationId::Operation(None)
468            | OperationId::IndexingGetter
469            | OperationId::IndexingSetter
470            | OperationId::IndexingDeleter => {
471                log::warn!("Unsupported unnamed operation: on {:?}", js_name);
472                return;
473            }
474        }
475
476        for x in self.create_imports(None, id, data, false, &HashSet::new()) {
477            functions.push(Function {
478                name: x.name,
479                js_name: x.js_name,
480                arguments: x.arguments,
481                ret_ty: x.ret_ty,
482                catch: x.catch,
483                variadic: x.variadic,
484                unstable: false,
485            });
486        }
487    }
488
489    fn append_const(
490        &self,
491        consts: &mut Vec<InterfaceConst>,
492        member: &'src weedle::interface::ConstMember<'src>,
493        unstable: bool,
494    ) {
495        let idl_type = member.const_type.to_idl_type(self);
496        let ty = idl_type.to_syn_type(TypePosition::Return).unwrap().unwrap();
497
498        let js_name = member.identifier.0;
499        let name = rust_ident(shouty_snake_case_ident(js_name).as_str());
500        let value = webidl_const_v_to_backend_const_v(&member.const_value);
501
502        consts.push(InterfaceConst {
503            name,
504            js_name: js_name.to_string(),
505            ty,
506            value,
507            unstable,
508        });
509    }
510
511    fn append_interface(
512        &self,
513        options: &Options,
514        program: &mut Program,
515        name: Ident,
516        js_name: String,
517        unstable_types: &HashSet<Identifier>,
518        data: &InterfaceData<'src>,
519    ) {
520        let unstable = data.stability.is_unstable();
521        let has_interface = data.has_interface;
522
523        let deprecated = data.deprecated.clone();
524
525        let parents = self
526            .all_superclasses(&js_name)
527            .map(|parent| {
528                let ident = rust_ident(&camel_case_ident(&parent));
529                program.required_features.insert(parent);
530                ident
531            })
532            .collect::<Vec<_>>();
533
534        let mut consts = vec![];
535        let mut attributes = vec![];
536        let mut methods = vec![];
537
538        for member in data.consts.iter() {
539            self.append_const(&mut consts, member, unstable);
540        }
541
542        for member in data.attributes.iter() {
543            let unstable = unstable || member.stability.is_unstable();
544            let member = member.definition;
545            self.member_attribute(
546                &mut attributes,
547                member.modifier,
548                member.readonly.is_some(),
549                &member.type_,
550                member.identifier.0.to_string(),
551                &member.attributes,
552                data.definition_attributes,
553                unstable,
554            );
555        }
556
557        for (id, op_data) in data.operations.iter() {
558            self.member_operation(&mut methods, data, id, op_data, unstable_types);
559        }
560
561        for mixin_data in self.all_mixins(&js_name) {
562            for member in &mixin_data.consts {
563                self.append_const(&mut consts, member, unstable);
564            }
565
566            for member in &mixin_data.attributes {
567                let unstable = unstable || member.stability.is_unstable();
568                let member = member.definition;
569                self.member_attribute(
570                    &mut attributes,
571                    if let Some(s) = member.stringifier {
572                        Some(weedle::interface::StringifierOrInheritOrStatic::Stringifier(s))
573                    } else {
574                        None
575                    },
576                    member.readonly.is_some(),
577                    &member.type_,
578                    member.identifier.0.to_string(),
579                    &member.attributes,
580                    data.definition_attributes,
581                    unstable,
582                );
583            }
584
585            for (id, op_data) in mixin_data.operations.iter() {
586                self.member_operation(&mut methods, data, id, op_data, unstable_types);
587            }
588        }
589
590        Interface {
591            name,
592            js_name,
593            deprecated,
594            has_interface,
595            parents,
596            consts,
597            attributes,
598            methods,
599            unstable,
600        }
601        .generate(options)
602        .to_tokens(&mut program.tokens);
603    }
604
605    fn member_attribute(
606        &self,
607        attributes: &mut Vec<InterfaceAttribute>,
608        modifier: Option<weedle::interface::StringifierOrInheritOrStatic>,
609        readonly: bool,
610        type_: &'src weedle::types::AttributedType<'src>,
611        js_name: String,
612        attrs: &'src Option<ExtendedAttributeList<'src>>,
613        container_attrs: Option<&'src ExtendedAttributeList<'src>>,
614        unstable: bool,
615    ) {
616        use weedle::interface::StringifierOrInheritOrStatic::*;
617
618        let is_static = match modifier {
619            Some(Stringifier(_)) => unreachable!(), // filtered out earlier
620            Some(Inherit(_)) => false,
621            Some(Static(_)) => true,
622            None => false,
623        };
624
625        let structural = is_structural(attrs.as_ref(), container_attrs);
626
627        let catch = throws(attrs);
628
629        let ty = type_
630            .type_
631            .to_idl_type(self)
632            .to_syn_type(TypePosition::Return)
633            .unwrap_or(None);
634
635        // Skip types which can't be converted
636        if let Some(ty) = ty {
637            let kind = InterfaceAttributeKind::Getter;
638            attributes.push(InterfaceAttribute {
639                is_static,
640                structural,
641                catch,
642                ty,
643                js_name: js_name.clone(),
644                kind,
645                unstable,
646            });
647        }
648
649        if !readonly {
650            let ty = type_
651                .type_
652                .to_idl_type(self)
653                .to_syn_type(TypePosition::Argument)
654                .unwrap_or(None);
655
656            // Skip types which can't be converted
657            if let Some(ty) = ty {
658                let kind = InterfaceAttributeKind::Setter;
659                attributes.push(InterfaceAttribute {
660                    is_static,
661                    structural,
662                    catch,
663                    ty,
664                    js_name,
665                    kind,
666                    unstable,
667                });
668            }
669        }
670    }
671
672    fn member_operation(
673        &self,
674        methods: &mut Vec<InterfaceMethod>,
675        data: &InterfaceData<'src>,
676        id: &OperationId<'src>,
677        op_data: &OperationData<'src>,
678        unstable_types: &HashSet<Identifier>,
679    ) {
680        let attrs = data.definition_attributes;
681        let unstable = data.stability.is_unstable();
682
683        for method in self.create_imports(attrs, id, op_data, unstable, unstable_types) {
684            methods.push(method);
685        }
686    }
687
688    fn append_callback_interface(
689        &self,
690        options: &Options,
691        program: &mut Program,
692        name: Ident,
693        js_name: String,
694        item: &CallbackInterfaceData<'src>,
695    ) {
696        assert_eq!(js_name, item.definition.identifier.0);
697
698        let mut fields = Vec::new();
699
700        for member in item.definition.members.body.iter() {
701            match member {
702                InterfaceMember::Operation(op) => {
703                    let identifier = match op.identifier {
704                        Some(i) => i.0,
705                        None => continue,
706                    };
707                    let pos = TypePosition::Argument;
708
709                    fields.push(DictionaryField {
710                        required: false,
711                        name: rust_ident(&snake_case_ident(identifier)),
712                        js_name: identifier.to_string(),
713                        ty: idl_type::IdlType::Callback
714                            .to_syn_type(pos)
715                            .unwrap()
716                            .unwrap(),
717                        unstable: false,
718                    })
719                }
720                _ => {
721                    log::warn!(
722                        "skipping callback interface member on {}",
723                        item.definition.identifier.0
724                    );
725                }
726            }
727        }
728
729        Dictionary {
730            name,
731            js_name,
732            fields,
733            unstable: false,
734        }
735        .generate(options)
736        .to_tokens(&mut program.tokens);
737    }
738}
739
740/// Generates Rust source code with #[wasm_bindgen] annotations.
741///
742/// * Reads WebIDL files in `from`
743/// * Generates Rust source code in the directory `to`
744/// * `options.features` indicates whether everything is gated by features or
745///   not
746///
747/// If features are enabled, returns a string that should be appended to
748/// `Cargo.toml` which lists all the known features.
749pub fn generate(from: &Path, to: &Path, options: Options) -> Result<String> {
750    let generate_features = options.features;
751
752    let source = read_source_from_path(&from.join("enabled"))?;
753    let unstable_source = read_source_from_path(&from.join("unstable"))?;
754
755    let features = parse_webidl(generate_features, source, unstable_source)?;
756
757    if to.exists() {
758        fs::remove_dir_all(&to).context("Removing features directory")?;
759    }
760
761    fs::create_dir_all(&to).context("Creating features directory")?;
762
763    for (name, feature) in features.iter() {
764        let out_file_path = to.join(format!("gen_{}.rs", name));
765
766        fs::write(&out_file_path, &feature.code)?;
767
768        rustfmt(&out_file_path, name)?;
769    }
770
771    let binding_file = features.keys().map(|name| {
772        if generate_features {
773            format!("#[cfg(feature = \"{name}\")] #[allow(non_snake_case)] mod gen_{name};\n#[cfg(feature = \"{name}\")] pub use gen_{name}::*;", name = name)
774        } else {
775            format!("#[allow(non_snake_case)] mod gen_{name};\npub use gen_{name}::*;", name = name)
776        }
777    }).collect::<Vec<_>>().join("\n\n");
778
779    fs::write(to.join("mod.rs"), binding_file)?;
780
781    rustfmt(&to.join("mod.rs"), "mod")?;
782
783    return if generate_features {
784        let features = features
785            .iter()
786            .map(|(name, feature)| {
787                let features = feature
788                    .required_features
789                    .iter()
790                    .map(|x| format!("\"{}\"", x))
791                    .collect::<Vec<_>>()
792                    .join(", ");
793                format!("{} = [{}]", name, features)
794            })
795            .collect::<Vec<_>>()
796            .join("\n");
797        Ok(features)
798    } else {
799        Ok(String::new())
800    };
801
802    /// Read all WebIDL files in a directory into a single `SourceFile`
803    fn read_source_from_path(dir: &Path) -> Result<SourceFile> {
804        let entries = read_dir(dir).context("reading webidls directory")?;
805        let mut source = SourceFile::default();
806        for path in entries {
807            if path.extension() != Some(OsStr::new("webidl")) {
808                continue;
809            }
810            source = source
811                .add_file(&path)
812                .with_context(|| format!("reading contents of file \"{}\"", path.display()))?;
813        }
814
815        Ok(source)
816    }
817
818    fn rustfmt(path: &PathBuf, name: &str) -> Result<()> {
819        // run rustfmt on the generated file - really handy for debugging
820        let result = Command::new("rustfmt")
821            .arg("--edition")
822            .arg("2018")
823            .arg(&path)
824            .status()
825            .context(format!("rustfmt on file {}", name))?;
826
827        assert!(result.success(), "rustfmt on file {}", name);
828
829        Ok(())
830    }
831
832    fn parse_webidl(
833        generate_features: bool,
834        enabled: SourceFile,
835        unstable: SourceFile,
836    ) -> Result<BTreeMap<String, Feature>> {
837        let options = Options {
838            features: generate_features,
839        };
840
841        match compile(&enabled.contents, &unstable.contents, options) {
842            Ok(features) => Ok(features),
843            Err(e) => {
844                if let Some(err) = e.downcast_ref::<WebIDLParseError>() {
845                    if let Some(pos) = enabled.resolve_offset(err.0) {
846                        let ctx = format!(
847                            "compiling WebIDL into wasm-bindgen bindings in file \
848                             \"{}\", line {} column {}",
849                            pos.filename,
850                            pos.line + 1,
851                            pos.col + 1
852                        );
853                        return Err(e.context(ctx));
854                    } else {
855                        return Err(e.context("compiling WebIDL into wasm-bindgen bindings"));
856                    }
857                }
858                return Err(e.context("compiling WebIDL into wasm-bindgen bindings"));
859            }
860        }
861    }
862}