-
Notifications
You must be signed in to change notification settings - Fork 1k
/
mod.rs
1739 lines (1586 loc) · 65.5 KB
/
mod.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//! The polyfill for the WebIDL bindings proposal in wasm-bindgen.
//!
//! This module contains the polyfill (or at least the current state of and as
//! closely as we can match) the WebIDL bindings proposal. The module exports
//! one main function, `process`, which takes a `walrus::Module`. This module is
//! expected to have two items:
//!
//! * First it contains all of the raw wasm-bindgen modules emitted by the Rust
//! compiler. These raw custom sections are extracted, removed, decoded, and
//! handled here. They contain information such as what's exported where,
//! what's imported, comments, etc.
//! * Second, the `descriptors.rs` pass must have run previously to execute all
//! the descriptor functions in the wasm module. Through the synthesized
//! custom section there we learn the type information of all
//! functions/imports/exports in the module.
//!
//! The output of this function is then a new `walrus::Module` with the previous
//! custom sections all removed and two new ones inserted. One is the webidl
//! bindings custom section (or at least a close approximate) and the second is
//! an auxiliary section for wasm-bindgen itself. The goal is for this auxiliary
//! section to eventually be empty or inconsequential, allowing us to emit
//! something that doesn't even need a JS shim one day. For now we're still
//! pretty far away from that, so we'll settle for using webidl bindings as
//! aggressively as possible!
use crate::decode;
use crate::descriptor::{Descriptor, Function};
use crate::descriptors::WasmBindgenDescriptorsSection;
use crate::intrinsic::Intrinsic;
use failure::{bail, format_err, Error};
use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use std::str;
use walrus::{ExportId, FunctionId, ImportId, Module, TypedCustomSectionId};
use wasm_bindgen_shared::struct_function_export_name;
use wasm_webidl_bindings::ast;
const PLACEHOLDER_MODULE: &str = "__wbindgen_placeholder__";
mod bindings;
mod incoming;
mod outgoing;
pub mod standard;
pub use self::incoming::NonstandardIncoming;
pub use self::outgoing::NonstandardOutgoing;
/// A nonstandard wasm-bindgen-specific WebIDL custom section.
///
/// This nonstandard section is intended to convey all information that
/// wasm-bindgen itself needs to know about binding functions. This means that
/// it basically uses `NonstandardIncoming` instead of
/// `IncomingBindingExpression` and such. It's also in a bit easier to work with
/// format than the official WebIDL bindings custom section.
///
/// Note that this is intended to be consumed during generation of JS shims and
/// bindings. There it can be transformed, however, into an actual WebIDL
/// binding section using all of the values it has internally.
#[derive(Default, Debug)]
pub struct NonstandardWebidlSection {
/// Store of all WebIDL types. Used currently to store all function types
/// specified in `Bindings`. This is intended to be passed through verbatim
/// to a final WebIDL bindings section.
pub types: ast::WebidlTypes,
/// A mapping from all bound exported functions to the binding that we have
/// listed for them. This is the master list of every binding that will be
/// bound and have a shim generated for it in the wasm module.
pub exports: HashMap<ExportId, Binding>,
/// Similar to `exports` above, except for imports. This will describe all
/// imports from the wasm module to indicate what the JS shim is expected to
/// do.
pub imports: HashMap<ImportId, Binding>,
/// For closures and such we'll be calling entries in the function table
/// with rich arguments (just like we call exports) so to do that we
/// describe all the elem indices that we need to modify here as well.
///
/// This is a list of pairs where the first element in the list is the
/// element index in the function table being described and the `Binding`
/// describes the signature that it's supposed to have.
///
/// The index within this table itself is then used to call actually
/// transformed functions.
pub elems: Vec<(u32, Binding)>,
}
pub type NonstandardWebidlSectionId = TypedCustomSectionId<NonstandardWebidlSection>;
/// A non-standard wasm-bindgen-specifi WebIDL binding. This is meant to vaguely
/// resemble a `FuctionBinding` in the official WebIDL bindings proposal, or at
/// least make it very easy to manufacture an official value from this one.
#[derive(Debug, Clone)]
pub struct Binding {
/// The WebAssembly type that the function is expected to have. Note that
/// this may not match the actual bound function's type! That's because this
/// type includes `anyref` but the Rust compiler never emits anyref. This
/// is, however, used for the `anyref` pass to know what to transform to
/// `anyref`.
pub wasm_ty: walrus::TypeId,
/// The WebIDL type of this binding, which is an index into the webidl
/// binding section's `types` field.
pub webidl_ty: ast::WebidlFunctionId,
/// A list of incoming bindings. For exports this is the list of arguments,
/// and for imports this is the return value.
pub incoming: Vec<NonstandardIncoming>,
/// A list of outgoing bindings. For exports this is the return value and
/// for imports this is the list of arguments.
pub outgoing: Vec<NonstandardOutgoing>,
/// An unfortunate necessity of today's implementation. Ideally WebIDL
/// bindings are used with multi-value support in wasm everywhere, but today
/// few engines support multi-value and LLVM certainly doesn't. Aggregates
/// are then always returned through an out-ptr, so this indicates that if
/// an out-ptr is present what wasm types are being transmitted through it.
pub return_via_outptr: Option<Vec<walrus::ValType>>,
}
impl Binding {
/// Does this binding's wasm function signature have any `anyref`s?
pub fn contains_anyref(&self, module: &walrus::Module) -> bool {
let ty = module.types.get(self.wasm_ty);
ty.params()
.iter()
.chain(ty.results())
.any(|ty| *ty == walrus::ValType::Anyref)
}
}
/// A synthetic custom section which is not standardized, never will be, and
/// cannot be serialized or parsed. This is synthesized from all of the
/// compiler-emitted wasm-bindgen sections and then immediately removed to be
/// processed in the JS generation pass.
#[derive(Default, Debug)]
pub struct WasmBindgenAux {
/// Extra typescript annotations that should be appended to the generated
/// TypeScript file. This is provided via a custom attribute in Rust code.
pub extra_typescript: String,
/// A map from identifier to the contents of each local module defined via
/// the `#[wasm_bindgen(module = "/foo.js")]` import options.
pub local_modules: HashMap<String, String>,
/// A map from unique crate identifier to the list of inline JS snippets for
/// that crate identifier.
pub snippets: HashMap<String, Vec<String>>,
/// A list of all `package.json` files that are intended to be included in
/// the final build.
pub package_jsons: HashSet<PathBuf>,
/// A map from exported function id to where it's expected to be exported
/// to.
pub export_map: HashMap<ExportId, AuxExport>,
/// A map from imported function id to what it's expected to import.
pub import_map: HashMap<ImportId, AuxImport>,
/// Small bits of metadata about imports.
pub imports_with_catch: HashSet<ImportId>,
pub imports_with_variadic: HashSet<ImportId>,
pub imports_with_assert_no_shim: HashSet<ImportId>,
/// Auxiliary information to go into JS/TypeScript bindings describing the
/// exported enums from Rust.
pub enums: Vec<AuxEnum>,
/// Auxiliary information to go into JS/TypeScript bindings describing the
/// exported structs from Rust and their fields they've got exported.
pub structs: Vec<AuxStruct>,
}
pub type WasmBindgenAuxId = TypedCustomSectionId<WasmBindgenAux>;
#[derive(Debug)]
pub struct AuxExport {
/// When generating errors about this export, a helpful name to remember it
/// by.
pub debug_name: String,
/// Comments parsed in Rust and forwarded here to show up in JS bindings.
pub comments: String,
/// Argument names in Rust forwarded here to configure the names that show
/// up in TypeScript bindings.
pub arg_names: Option<Vec<String>>,
/// What kind of function this is and where it shows up
pub kind: AuxExportKind,
}
/// All possible kinds of exports from a wasm module.
///
/// This `enum` says where to place an exported wasm function. For example it
/// may want to get hooked up to a JS class, or it may want to be exported as a
/// free function (etc).
///
/// TODO: it feels like this should not really be here per se. We probably want
/// to either construct the JS object itself from within wasm or somehow move
/// more of this information into some other section. Really what this is is
/// sort of an "export map" saying how to wire up all the free functions from
/// the wasm module into the output expected JS module. All our functions here
/// currently take integer parameters and require a JS wrapper, but ideally
/// we'd change them one day to taking/receiving `anyref` which then use some
/// sort of webidl import to customize behavior or something like that. In any
/// case this doesn't feel quite right in terms of priviledge separation, so
/// we'll want to work on this. For now though it works.
#[derive(Debug)]
pub enum AuxExportKind {
/// A free function that's just listed on the exported module
Function(String),
/// A function that's used to create an instane of a class. The function
/// actually return just an integer which is put on an JS object currently.
Constructor(String),
/// This function is intended to be a getter for a field on a class. The
/// first argument is the internal pointer and the returned value is
/// expected to be the field.
Getter { class: String, field: String },
/// This function is intended to be a setter for a field on a class. The
/// first argument is the internal pointer and the second argument is
/// expected to be the field's new value.
Setter { class: String, field: String },
/// This is a free function (ish) but scoped inside of a class name.
StaticFunction { class: String, name: String },
/// This is a member function of a class where the first parameter is the
/// implicit integer stored in the class instance.
Method {
class: String,
name: String,
/// Whether or not this is calling a by-value method in Rust and should
/// clear the internal pointer in JS automatically.
consumed: bool,
},
}
#[derive(Debug)]
pub struct AuxEnum {
/// The name of this enum
pub name: String,
/// The copied Rust comments to forward to JS
pub comments: String,
/// A list of variants with their name and value
pub variants: Vec<(String, u32)>,
}
#[derive(Debug)]
pub struct AuxStruct {
/// The name of this struct
pub name: String,
/// The copied Rust comments to forward to JS
pub comments: String,
}
/// All possible types of imports that can be imported by a wasm module.
///
/// This `enum` is intended to map out what an imported value is. For example
/// this contains a ton of shims and various ways you can call a function. The
/// base variant here is `Value` which simply means "hook this up to the import"
/// and the signatures will match up.
///
/// Note that this is *not* the same as the webidl bindings section. This is
/// intended to be coupled with that to map out what actually gets hooked up to
/// an import in the wasm module. The two work in tandem.
///
/// Some of these items here are native to JS (like `Value`, indexing
/// operations, etc). Others are shims generated by wasm-bindgen (like `Closure`
/// or `Instanceof`).
#[derive(Debug)]
pub enum AuxImport {
/// This import is expected to simply be whatever is the value that's
/// imported
Value(AuxValue),
/// A static method on a class is being imported, and the `this` of the
/// function call is expected to always be the class.
ValueWithThis(JsImport, String),
/// This import is expected to be a function that takes an `anyref` and
/// returns a `bool`. It's expected that it tests if the argument is an
/// instance of (using `instanceof`) the name specified.
///
/// TODO: can we use `Reflect` or something like that to avoid an extra kind
/// of import here?
Instanceof(JsImport),
/// This import is expected to be a shim that returns the JS value named by
/// `JsImport`.
Static(JsImport),
/// This import is intended to manufacture a JS closure with the given
/// signature and then return that back to Rust.
Closure {
mutable: bool, // whether or not this was a `FnMut` closure
dtor: u32, // table element index of the destructor function
binding_idx: u32,
nargs: usize,
},
/// This import is expected to be a shim that simply calls the `foo` method
/// on the first object, passing along all other parameters and returning
/// the resulting value.
StructuralMethod(String),
/// This import is a "structural getter" which simply returns the `.field`
/// value of the first argument as an object.
///
/// e.g. `function(x) { return x.foo; }`
StructuralGetter(String),
/// This import is a "structural getter" which simply returns the `.field`
/// value of the specified class
///
/// e.g. `function() { return TheClass.foo; }`
StructuralClassGetter(JsImport, String),
/// This import is a "structural setter" which simply sets the `.field`
/// value of the first argument to the second argument.
///
/// e.g. `function(x, y) { x.foo = y; }`
StructuralSetter(String),
/// This import is a "structural setter" which simply sets the `.field`
/// value of the specified class to the first argument of the function.
///
/// e.g. `function(x) { TheClass.foo = x; }`
StructuralClassSetter(JsImport, String),
/// This import is expected to be a shim that is an indexing getter of the
/// JS class here, where the first argument of the function is the field to
/// look up. The return value is the value of the field.
///
/// e.g. `function(x) { return TheClass[x]; }`
///
/// TODO: can we use `Reflect` or something like that to avoid an extra kind
/// of import here?
IndexingGetterOfClass(JsImport),
/// This import is expected to be a shim that is an indexing getter of the
/// first argument interpreted as an object where the field to look up is
/// the second argument.
///
/// e.g. `function(x, y) { return x[y]; }`
///
/// TODO: can we use `Reflect` or something like that to avoid an extra kind
/// of import here?
IndexingGetterOfObject,
/// This import is expected to be a shim that is an indexing setter of the
/// JS class here, where the first argument of the function is the field to
/// set and the second is the value to set it to.
///
/// e.g. `function(x, y) { TheClass[x] = y; }`
///
/// TODO: can we use `Reflect` or something like that to avoid an extra kind
/// of import here?
IndexingSetterOfClass(JsImport),
/// This import is expected to be a shim that is an indexing setter of the
/// first argument interpreted as an object where the next two fields are
/// the field to set and the value to set it to.
///
/// e.g. `function(x, y, z) { x[y] = z; }`
///
/// TODO: can we use `Reflect` or something like that to avoid an extra kind
/// of import here?
IndexingSetterOfObject,
/// This import is expected to be a shim that is an indexing deleter of the
/// JS class here, where the first argument of the function is the field to
/// delete.
///
/// e.g. `function(x) { delete TheClass[x]; }`
///
/// TODO: can we use `Reflect` or something like that to avoid an extra kind
/// of import here?
IndexingDeleterOfClass(JsImport),
/// This import is expected to be a shim that is an indexing deleter of the
/// first argument interpreted as an object where the second argument is
/// the field to delete.
///
/// e.g. `function(x, y) { delete x[y]; }`
///
/// TODO: can we use `Reflect` or something like that to avoid an extra kind
/// of import here?
IndexingDeleterOfObject,
/// This import is a generated shim which will wrap the provided pointer in
/// a JS object corresponding to the Class name given here. The class name
/// is one that is exported from the Rust/wasm.
///
/// TODO: sort of like the export map below we should ideally create the
/// `anyref` from within Rust itself and then return it directly rather than
/// requiring an intrinsic here to do so.
WrapInExportedClass(String),
/// This is an intrinsic function expected to be implemented with a JS glue
/// shim. Each intrinsic has its own expected signature and implementation.
Intrinsic(Intrinsic),
}
/// Values that can be imported verbatim to hook up to an import.
#[derive(Debug)]
pub enum AuxValue {
/// A bare JS value, no transformations, just put it in the slot.
Bare(JsImport),
/// A getter function for the class listed for the field, acquired using
/// `getOwnPropertyDescriptor`.
Getter(JsImport, String),
/// Like `Getter`, but accesses a field of a class instead of an instance
/// of the class.
ClassGetter(JsImport, String),
/// Like `Getter`, except the `set` property.
Setter(JsImport, String),
/// Like `Setter`, but for class fields instead of instance fields.
ClassSetter(JsImport, String),
}
/// What can actually be imported and typically a value in each of the variants
/// above of `AuxImport`
///
/// A `JsImport` is intended to indicate what exactly is being imported for a
/// particular operation.
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub struct JsImport {
/// The base of whatever is being imported, either from a module, the global
/// namespace, or similar.
pub name: JsImportName,
/// Various field accesses (like `.foo.bar.baz`) to hang off the `name`
/// above.
pub fields: Vec<String>,
}
/// Return value of `determine_import` which is where we look at an imported
/// function AST and figure out where it's actually being imported from
/// (performing some validation checks and whatnot).
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub enum JsImportName {
/// An item is imported from the global scope. The `name` is what's
/// imported.
Global { name: String },
/// Same as `Global`, except the `name` is imported via an ESM import from
/// the specified `module` path.
Module { module: String, name: String },
/// Same as `Module`, except we're importing from a local module defined in
/// a local JS snippet.
LocalModule { module: String, name: String },
/// Same as `Module`, except we're importing from an `inline_js` attribute
InlineJs {
unique_crate_identifier: String,
snippet_idx_in_crate: usize,
name: String,
},
/// A global import which may have a number of vendor prefixes associated
/// with it, like `webkitAudioPrefix`. The `name` is the name to test
/// whether it's prefixed.
VendorPrefixed { name: String, prefixes: Vec<String> },
}
struct Context<'a> {
start_found: bool,
module: &'a mut Module,
bindings: NonstandardWebidlSection,
aux: WasmBindgenAux,
function_exports: HashMap<String, (ExportId, FunctionId)>,
function_imports: HashMap<String, (ImportId, FunctionId)>,
vendor_prefixes: HashMap<String, Vec<String>>,
unique_crate_identifier: &'a str,
descriptors: HashMap<String, Descriptor>,
anyref_enabled: bool,
wasm_interface_types: bool,
support_start: bool,
}
pub fn process(
module: &mut Module,
anyref_enabled: bool,
wasm_interface_types: bool,
support_start: bool,
) -> Result<(NonstandardWebidlSectionId, WasmBindgenAuxId), Error> {
let mut storage = Vec::new();
let programs = extract_programs(module, &mut storage)?;
let mut cx = Context {
bindings: Default::default(),
aux: Default::default(),
function_exports: Default::default(),
function_imports: Default::default(),
vendor_prefixes: Default::default(),
descriptors: Default::default(),
unique_crate_identifier: "",
module,
start_found: false,
anyref_enabled,
wasm_interface_types,
support_start,
};
cx.init()?;
for program in programs {
cx.program(program)?;
}
if let Some(standard) = cx.module.customs.delete_typed::<ast::WebidlBindings>() {
cx.standard(&standard)?;
}
cx.verify()?;
let bindings = cx.module.customs.add(cx.bindings);
let aux = cx.module.customs.add(cx.aux);
Ok((bindings, aux))
}
impl<'a> Context<'a> {
fn init(&mut self) -> Result<(), Error> {
// Make a map from string name to ids of all exports
for export in self.module.exports.iter() {
if let walrus::ExportItem::Function(f) = export.item {
self.function_exports
.insert(export.name.clone(), (export.id(), f));
}
}
// Make a map from string name to ids of all imports from our
// placeholder module name which we'll want to be sure that we've got a
// location listed of what to import there for each item.
let mut intrinsics = Vec::new();
for import in self.module.imports.iter() {
if import.module != PLACEHOLDER_MODULE {
continue;
}
if let walrus::ImportKind::Function(f) = import.kind {
self.function_imports
.insert(import.name.clone(), (import.id(), f));
if let Some(intrinsic) = Intrinsic::from_symbol(&import.name) {
intrinsics.push((import.id(), intrinsic));
}
}
}
for (id, intrinsic) in intrinsics {
self.bind_intrinsic(id, intrinsic)?;
}
self.inject_anyref_initialization()?;
if let Some(custom) = self
.module
.customs
.delete_typed::<WasmBindgenDescriptorsSection>()
{
let WasmBindgenDescriptorsSection {
descriptors,
closure_imports,
} = *custom;
// Store all the executed descriptors in our own field so we have
// access to them while processing programs.
self.descriptors.extend(descriptors);
// Register all the injected closure imports as that they're expected
// to manufacture a particular type of closure.
//
// First we register the imported function shim which returns a
// `JsValue` for the closure. We manufacture this signature's
// binding since it's not listed anywhere.
//
// Next we register the corresponding table element's binding in
// the webidl bindings section. This binding will later be used to
// generate a shim (if necessary) for the table element.
//
// Finally we store all this metadata in the import map which we've
// learned so when a binding for the import is generated we can
// generate all the appropriate shims.
for (id, descriptor) in closure_imports {
let binding = Function {
shim_idx: 0,
arguments: vec![Descriptor::I32; 3],
ret: Descriptor::Anyref,
};
bindings::register_import(
self.module,
&mut self.bindings,
id,
binding,
ast::WebidlFunctionKind::Static,
)?;
// Synthesize the two integer pointers we pass through which
// aren't present in the signature but are present in the wasm
// signature.
let mut function = descriptor.function.clone();
let nargs = function.arguments.len();
function.arguments.insert(0, Descriptor::I32);
function.arguments.insert(0, Descriptor::I32);
let binding_idx = bindings::register_table_element(
self.module,
&mut self.bindings,
descriptor.shim_idx,
function,
)?;
self.aux.import_map.insert(
id,
AuxImport::Closure {
dtor: descriptor.dtor_idx,
mutable: descriptor.mutable,
binding_idx,
nargs,
},
);
}
}
Ok(())
}
// Ensure that the `start` function for this module calls the
// `__wbindgen_init_anyref_table` function. This'll ensure that all
// instances of this module have the initial slots of the anyref table
// initialized correctly.
//
// Note that this is disabled if WebAssembly interface types are enabled
// since that's a slightly different environment for now which doesn't have
// quite the same initialization.
fn inject_anyref_initialization(&mut self) -> Result<(), Error> {
if !self.anyref_enabled || self.wasm_interface_types {
return Ok(());
}
let ty = self.module.types.add(&[], &[]);
let (import, import_id) =
self.module
.add_import_func(PLACEHOLDER_MODULE, "__wbindgen_init_anyref_table", ty);
self.module.start = Some(match self.module.start {
Some(prev_start) => {
let mut builder = walrus::FunctionBuilder::new(&mut self.module.types, &[], &[]);
builder.func_body().call(import).call(prev_start);
builder.finish(Vec::new(), &mut self.module.funcs)
}
None => import,
});
self.bind_intrinsic(import_id, Intrinsic::InitAnyrefTable)?;
Ok(())
}
fn bind_intrinsic(&mut self, id: ImportId, intrinsic: Intrinsic) -> Result<(), Error> {
bindings::register_import(
self.module,
&mut self.bindings,
id,
intrinsic.binding(),
ast::WebidlFunctionKind::Static,
)?;
self.aux
.import_map
.insert(id, AuxImport::Intrinsic(intrinsic));
Ok(())
}
fn program(&mut self, program: decode::Program<'a>) -> Result<(), Error> {
self.unique_crate_identifier = program.unique_crate_identifier;
let decode::Program {
exports,
enums,
imports,
structs,
typescript_custom_sections,
local_modules,
inline_js,
unique_crate_identifier,
package_json,
} = program;
for module in local_modules {
// All local modules we find should be unique, but the same module
// may have showed up in a few different blocks. If that's the case
// all the same identifiers should have the same contents.
if let Some(prev) = self
.aux
.local_modules
.insert(module.identifier.to_string(), module.contents.to_string())
{
assert_eq!(prev, module.contents);
}
}
if let Some(s) = package_json {
self.aux.package_jsons.insert(s.into());
}
for export in exports {
self.export(export)?;
}
// Register vendor prefixes for all types before we walk over all the
// imports to ensure that if a vendor prefix is listed somewhere it'll
// apply to all the imports.
for import in imports.iter() {
if let decode::ImportKind::Type(ty) = &import.kind {
if ty.vendor_prefixes.len() == 0 {
continue;
}
self.vendor_prefixes
.entry(ty.name.to_string())
.or_insert(Vec::new())
.extend(ty.vendor_prefixes.iter().map(|s| s.to_string()));
}
}
for import in imports {
self.import(import)?;
}
for enum_ in enums {
self.enum_(enum_)?;
}
for struct_ in structs {
self.struct_(struct_)?;
}
for section in typescript_custom_sections {
self.aux.extra_typescript.push_str(section);
self.aux.extra_typescript.push_str("\n\n");
}
self.aux
.snippets
.entry(unique_crate_identifier.to_string())
.or_insert(Vec::new())
.extend(inline_js.iter().map(|s| s.to_string()));
Ok(())
}
fn export(&mut self, export: decode::Export<'_>) -> Result<(), Error> {
let wasm_name = match &export.class {
Some(class) => struct_function_export_name(class, export.function.name),
None => export.function.name.to_string(),
};
let mut descriptor = match self.descriptors.remove(&wasm_name) {
None => return Ok(()),
Some(d) => d.unwrap_function(),
};
let (export_id, id) = self.function_exports[&wasm_name];
if export.start {
self.add_start_function(id)?;
}
let kind = match export.class {
Some(class) => {
let class = class.to_string();
match export.method_kind {
decode::MethodKind::Constructor => AuxExportKind::Constructor(class),
decode::MethodKind::Operation(op) => match op.kind {
decode::OperationKind::Getter(f) => {
descriptor.arguments.insert(0, Descriptor::I32);
AuxExportKind::Getter {
class,
field: f.to_string(),
}
}
decode::OperationKind::Setter(f) => {
descriptor.arguments.insert(0, Descriptor::I32);
AuxExportKind::Setter {
class,
field: f.to_string(),
}
}
_ if op.is_static => AuxExportKind::StaticFunction {
class,
name: export.function.name.to_string(),
},
_ => {
descriptor.arguments.insert(0, Descriptor::I32);
AuxExportKind::Method {
class,
name: export.function.name.to_string(),
consumed: export.consumed,
}
}
},
}
}
None => AuxExportKind::Function(export.function.name.to_string()),
};
self.aux.export_map.insert(
export_id,
AuxExport {
debug_name: wasm_name,
comments: concatenate_comments(&export.comments),
arg_names: Some(export.function.arg_names),
kind,
},
);
bindings::register_export(self.module, &mut self.bindings, export_id, descriptor)?;
Ok(())
}
fn add_start_function(&mut self, id: FunctionId) -> Result<(), Error> {
if self.start_found {
bail!("cannot specify two `start` functions");
}
self.start_found = true;
// Skip this when we're generating tests
if !self.support_start {
return Ok(());
}
let prev_start = match self.module.start {
Some(f) => f,
None => {
self.module.start = Some(id);
return Ok(());
}
};
// Note that we call the previous start function, if any, first. This is
// because the start function currently only shows up when it's injected
// through thread/anyref transforms. These injected start functions need
// to happen before user code, so we always schedule them first.
let mut builder = walrus::FunctionBuilder::new(&mut self.module.types, &[], &[]);
builder.func_body().call(prev_start).call(id);
let new_start = builder.finish(Vec::new(), &mut self.module.funcs);
self.module.start = Some(new_start);
Ok(())
}
fn import(&mut self, import: decode::Import<'_>) -> Result<(), Error> {
match &import.kind {
decode::ImportKind::Function(f) => self.import_function(&import, f),
decode::ImportKind::Static(s) => self.import_static(&import, s),
decode::ImportKind::Type(t) => self.import_type(&import, t),
decode::ImportKind::Enum(_) => Ok(()),
}
}
fn import_function(
&mut self,
import: &decode::Import<'_>,
function: &decode::ImportFunction<'_>,
) -> Result<(), Error> {
let decode::ImportFunction {
shim,
catch,
variadic,
method,
structural,
function,
assert_no_shim,
} = function;
let (import_id, _id) = match self.function_imports.get(*shim) {
Some(pair) => *pair,
None => return Ok(()),
};
let descriptor = match self.descriptors.remove(*shim) {
None => return Ok(()),
Some(d) => d.unwrap_function(),
};
// Record this for later as it affects JS binding generation, but note
// that this doesn't affect the WebIDL interface at all.
if *variadic {
self.aux.imports_with_variadic.insert(import_id);
}
if *catch {
self.aux.imports_with_catch.insert(import_id);
}
if *assert_no_shim {
self.aux.imports_with_assert_no_shim.insert(import_id);
}
// Perform two functions here. First we're saving off our WebIDL
// bindings signature, indicating what we think our import is going to
// be. Next we're saving off other metadata indicating where this item
// is going to be imported from. The `import_map` table will record, for
// each import, what is getting hooked up to that slot of the import
// table to the WebAssembly instance.
let import = match method {
Some(data) => {
let class = self.determine_import(import, &data.class)?;
match &data.kind {
// NB: `structural` is ignored for constructors since the
// js type isn't expected to change anyway.
decode::MethodKind::Constructor => {
bindings::register_import(
self.module,
&mut self.bindings,
import_id,
descriptor,
ast::WebidlFunctionKind::Constructor,
)?;
AuxImport::Value(AuxValue::Bare(class))
}
decode::MethodKind::Operation(op) => {
let (import, method) =
self.determine_import_op(class, function, *structural, op)?;
let kind = if method {
let kind = ast::WebidlFunctionKindMethod {
// TODO: what should this actually be?
ty: ast::WebidlScalarType::Any.into(),
};
ast::WebidlFunctionKind::Method(kind)
} else {
ast::WebidlFunctionKind::Static
};
bindings::register_import(
self.module,
&mut self.bindings,
import_id,
descriptor,
kind,
)?;
import
}
}
}
// NB: `structural` is ignored for free functions since it's
// expected that the binding isn't changing anyway.
None => {
bindings::register_import(
self.module,
&mut self.bindings,
import_id,
descriptor,
ast::WebidlFunctionKind::Static,
)?;
let name = self.determine_import(import, function.name)?;
AuxImport::Value(AuxValue::Bare(name))
}
};
self.aux.import_map.insert(import_id, import);
Ok(())
}
/// The `bool` returned indicates whether the imported value should be
/// invoked as a method (first arg is implicitly `this`) or if the imported
/// value is a simple function-like shim
fn determine_import_op(
&mut self,
mut class: JsImport,
function: &decode::Function<'_>,
structural: bool,
op: &decode::Operation<'_>,
) -> Result<(AuxImport, bool), Error> {
match op.kind {
decode::OperationKind::Regular => {
if op.is_static {
Ok((AuxImport::ValueWithThis(class, function.name.to_string()), false))
} else if structural {
Ok((
AuxImport::StructuralMethod(function.name.to_string()),
false,
))
} else {
class.fields.push("prototype".to_string());
class.fields.push(function.name.to_string());
Ok((AuxImport::Value(AuxValue::Bare(class)), true))
}
}
decode::OperationKind::Getter(field) => {
if structural {
if op.is_static {
Ok((
AuxImport::StructuralClassGetter(class, field.to_string()),
false,
))
} else {
Ok((AuxImport::StructuralGetter(field.to_string()), false))
}
} else {
let val = if op.is_static {
AuxValue::ClassGetter(class, field.to_string())
} else {
AuxValue::Getter(class, field.to_string())
};
Ok((AuxImport::Value(val), true))
}
}
decode::OperationKind::Setter(field) => {
if structural {
if op.is_static {
Ok((
AuxImport::StructuralClassSetter(class, field.to_string()),
false,
))
} else {
Ok((AuxImport::StructuralSetter(field.to_string()), false))
}
} else {