This repository has been archived by the owner on Oct 7, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathLoader.js
3260 lines (2886 loc) · 123 KB
/
Loader.js
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
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// # Loader.js - ES6 module loaders illustrated
//
// This is a sample implementation of the ES6 module loader. The code is
// interleaved with comments containing draft specification language for the
// ES6 module system.
//
// Source code is on github:
// [jorendorff/js-loaders](https://github.com/jorendorff/js-loaders).
// ## Current status
//
// This code does not work yet, though it passes [a few very basic
// tests](https://github.com/jorendorff/js-loaders/tree/master/test). We're
// focusing on producing a coherent spec document. I'm also very interested in
// standing the system up and running tests, but that will have to wait a week
// or two.
//
// This is not an introduction to the ES6 module system. It's not a tutorial or
// a rationale document. It's mainly specification text with a few explanatory
// comments.
// ## Prelude
//
// This section is not exactly riveting reading. It's safe to skip down to
// “Primitives”.
//
(function (global) {
"use strict";
// This implementation uses some ES builtins. User scripts may mutate or delete
// those builtins, so we capture everything we need up front.
//
var std_Function_call = Function.prototype.call;
var std_Function_bind = Function.prototype.bind;
var bind = std_Function_call.bind(std_Function_bind);
var callFunction = bind(std_Function_call, std_Function_call);
var std_Object_create = Object.create;
var std_Object_defineProperty = Object.defineProperty;
var std_Object_keys = Object.keys;
var std_Object_preventExtensions = Object.preventExtensions;
var std_Array_push = Array.prototype.push;
var std_Array_sort = Array.prototype.sort;
var std_Set = Set;
var std_Set_get_size = Object.getOwnPropertyDescriptor(Set.prototype, "size").get;
var std_Set_has = Set.prototype.has;
var std_Set_add = Set.prototype.add;
var std_Set_delete = Set.prototype.delete;
var std_Set_clear = Set.prototype.clear;
var std_Set_iterator = Set.prototype["@@iterator"];
var std_Set_iterator_next = new Set()["@@iterator"]().next;
var std_Map = Map;
var std_Map_has = Map.prototype.has;
var std_Map_get = Map.prototype.get;
var std_Map_set = Map.prototype.set;
var std_Map_delete = Map.prototype.delete;
var std_Map_entries = Map.prototype.entries;
var std_Map_keys = Map.prototype.keys;
var std_Map_values = Map.prototype.values;
var std_Map_iterator_next = new Map().keys().next;
var std_WeakMap = WeakMap;
var std_WeakMap_has = WeakMap.prototype.has;
var std_WeakMap_get = WeakMap.prototype.get;
var std_WeakMap_set = WeakMap.prototype.set;
var std_Promise = Promise;
var std_Promise_all = Promise.all;
var std_Promise_resolve = Promise.resolve;
var std_Promise_then = Promise.prototype.then;
var std_Promise_catch = Promise.prototype.catch;
var std_TypeError = TypeError;
// A handful of utility functions built from ES standard facilities.
// ES6 ToBoolean abstract operation.
function ToBoolean(v) {
return !!v;
}
// ES6 ToString abstract operation.
function ToString(v) {
return "" + v;
}
// **IsObject(v)** – Return true if Type(v) is Object.
// Perhaps surprisingly, process of elimination is the only correct way to
// implement this. See [ES5 11.4.3, "The `typeof`
// Operator"](https://people.mozilla.com/~jorendorff/es5.1-final.html#sec-11.4.3).
//
function IsObject(v) {
return v !== null &&
v !== undefined &&
typeof v !== "boolean" &&
typeof v !== "number" &&
typeof v !== "string" &&
typeof v !== "symbol";
}
// ES6 IsCallable abstract operation.
function IsCallable(v) {
return typeof v === "function";
}
// This implementation uses ES6 Set, Map, and WeakMap objects in some places
// where the spec text refers to Lists and internal slots.
//
// Bug: In implementations that support @@create, the `CreateSet()` function
// given here would be affected by modifying the @@create method of the Set
// builtin (which is a configurable property). This implementation will change
// whenever @@create is implemented. The same is true for `CreateMap()` and
// `CreateWeakMap()`.
//
function CreateSet() {
return new std_Set;
}
function CreateMap() {
return new std_Map;
}
function CreateWeakMap() {
return new std_WeakMap;
}
function IteratorToArray(iter, next) {
var a = [];
for (var x = callFunction(next, iter); !x.done; x = callFunction(next, iter))
callFunction(std_Array_push, a, x.value);
return a;
}
function SetToArray(set) {
return IteratorToArray(callFunction(std_Set_iterator, set), std_Set_iterator_next);
}
function MapValuesToArray(map) {
return IteratorToArray(callFunction(std_Map_values, map), std_Map_iterator_next);
}
// The Loader spec uses a few operations that will likely be provided by the
// Promise spec.
function PromiseOf(value) {
// Implementation note: Calling `Promise.resolve()` here is user-observable
// since `Promise[Symbol.create]` may have been mutated. This is
// considered a bug. User code should not be able to observe whether the
// implementation uses Promises internally or not.
//
// Every use of `new Promise`, `Promise.all`, `Promise.prototype.then`, and
// `Promise.prototype.catch` in this spec has the same problem.
//
// The plan is for the Promise spec to address this by exposing primitives
// that perform these operations but are immune to user tampering. This is
// [issue #73](https://github.com/domenic/promises-unwrapping/issues/73) in
// the GitHub repository where the Promise spec is being developed.
//
return callFunction(std_Promise_resolve, std_Promise, value);
}
// `Assert(condition)` is your bog-standard assert function. In theory, it does
// nothing. The given `condition` is always true.
function Assert(condition) {
if (typeof assert === "function")
assert(condition);
else if (typeof assertEq === "function")
assertEq(condition, true);
if (condition !== true)
throw "assertion failed";
}
// ## Primitives
//
// We rely on the JavaScript implementation to provide a few primitives. You
// can skip over this stuff too. On the other hand, it tells what sort of
// thing we'll be doing here.
// The first two primitives parse ECMAScript code.
//
// * `$ParseModule(loader, source, moduleName, address)` parses the string
// `source` as an ES6 Module. Returns a ModuleBody object.
//
// * `$ParseScript(source)` parses the string `source` as an ES6 Script.
// Returns a StatementList object.
//
// Both primitives detect ES "early errors" and throw `SyntaxError` or
// `ReferenceError`.
//
// Note that neither primitive runs any of the code in `source`.
//
// ModuleBody and StatementList objects are never exposed to user code. They
// are for use with the primitives below only. These two parsing primitives are
// the only way objects of these types are created.
//
// The following primitive extracts information from a ModuleBody object.
//
// * `$ModuleRequests(body)` returns an Array of strings, the module
// specifiers as they appear in import declarations and module declarations
// in the given module body, with duplicates removed. (This corresponds to
// the ModuleRequests static semantics operation.)
//
// The following primitives operate on Module objects.
// * `$CreateModule()` returns a new `Module` object. The object is
// extensible. It must not be exposed to scripts until it has been
// populated and frozen.
//
var moduleInternalDataMap = CreateWeakMap();
function GetModuleInternalData(module) {
return callFunction(std_WeakMap_get, moduleInternalDataMap, module);
}
function $CreateModule() {
var module = std_Object_create(null);
var moduleData = {
dependencies: undefined,
evaluated: false
};
callFunction(std_WeakMap_set, moduleInternalDataMap, module, moduleData);
return module;
}
// * `$IsModule(v)` returns true if `v` is a `Module` object.
//
function $IsModule(module) {
return GetModuleInternalData(module) !== undefined;
}
// * `$GetDependencies(module)` returns module.[[Dependencies]]. This is
// either undefined or an array of Module objects, the modules whose bodies
// are to be evaluated before the given module's body. A return value of
// undefined means the same thing as returning an empty array to the sole
// caller, EnsureEvaluated().
//
function $GetDependencies(module) {
return GetModuleInternalData(module).dependencies;
}
// * `$SetDependencies(module, deps)` sets module.[[Dependencies]].
//
function $SetDependencies(module, deps) {
GetModuleInternalData(module).dependencies = deps;
}
// * `$EvaluateModuleBody(realm, mod)` runs the body of the given module in
// the context of a given realm. Returns undefined.
//
// * `$HasBeenEvaluated(mod)` returns true if mod has ever been passed to
// $EvaluateModuleBody.
//
function $HasBeenEvaluated(module) {
return GetModuleInternalData(module).evaluated;
}
// Loader iterators require a little private state.
//
// * `$SetLoaderIteratorPrivate(iter, value)` stores `value` in an internal
// slot of `iter`.
//
var loaderIteratorInternalDataMap = CreateWeakMap();
function $SetLoaderIteratorPrivate(iter, value) {
callFunction(std_WeakMap_set, loaderIteratorInternalDataMap, iter, value);
}
// * `$GetLoaderIteratorPrivate(iter)` retrieves the value previously stored
// using $SetLoaderIteratorPrivate. If no value was previously stored,
// throw a TypeError.
//
function $GetLoaderIteratorPrivate(iter) {
if (!IsObject(iter)) {
throw std_TypeError(
"Loader Iterator method called on an incompatible " + typeof iter);
}
if (!callFunction(std_WeakMap_has, loaderIteratorInternalDataMap, iter)) {
throw std_TypeError(
"Loader Iterator method called on an incompatible object");
}
return callFunction(std_WeakMap_get, loaderIteratorInternalDataMap, iter);
}
// The following primitives deal with realms.
//
// * `$CreateRealm(realmObject)` creates a new realm for evaluating module
// and script code. This can be polyfilled in the browser using an iframe,
// [as shown in this sample
// code](https://gist.github.com/wycats/8f5263a0bcc8e818b8e5).
//
// * `$IndirectEval(realm, source)` performs an indirect eval in the given
// realm for the given script source.
//
//> # Modules: Semantics
//>
//> ## Module Loading
//>
//> ### Load Records
//>
//> The Load Record type represents an attempt to locate, fetch, translate, and
//> parse a single module.
//>
//> Each Load Record has the following fields:
//>
//> * load.[[Status]] – One of: `"loading"`, `"loaded"`, `"linked"`, or
//> `"failed"`.
//>
//> * load.[[Name]] – The normalized name of the module being loaded,
//> or **undefined** if loading an anonymous module.
//>
//> * load.[[LinkSets]] – A List of all LinkSets that require this load
//> to succeed. There is a many-to-many relation between Loads and
//> LinkSets. A single `import()` call can have a large dependency tree,
//> involving many Loads. Many `import()` calls can be waiting for a
//> single Load, if they depend on the same module.
//>
//> * load.[[Metadata]] – An object which loader hooks may use for any
//> purpose. See Loader.prototype.locate.
//>
//> * load.[[Address]] – The result of the locate hook.
//>
//> * load.[[Source]] – The result of the translate hook.
//>
//> * load.[[Kind]] – Once the Load reaches the `"loaded"` state,
//> either **declarative** or **dynamic**. If the `instantiate` hook
//> returned undefined, the module is declarative, and load.[[Body]]
//> contains a Module parse. Otherwise, the `instantiate` hook returned a
//> ModuleFactory object; load.[[Execute]] contains the `.execute` callable
//> object.
//>
//> * load.[[Body]] – A Module parse, if load.[[Kind]] is
//> **declarative**. Otherwise undefined.
//>
//> * load.[[Execute]] – The value of `factory.execute`, if
//> load.[[Kind]] is **dynamic**. Otherwise undefined.
//>
//> * load.[[Dependencies]] – Once the Load reaches the `"loaded"`
//> state, a List of pairs. Each pair consists of two strings: a module
//> name as it appears in a `module`, `import`, or `export from`
//> declaration in load.[[Body]], and the corresponding normalized module
//> name.
//>
//> * load.[[Exception]] – If load.[[Status]] is `"failed"`, the
//> exception value that was thrown, causing the load to fail. Otherwise,
//> **null**.
//>
//> * load.[[Module]] – The Module object produced by this load, or
//> undefined.
//>
//> If the `instantiate` hook returns undefined, load.[[Module]] is
//> populated at that point, if parsing succeeds and there are no early
//> errors.
//>
//> Otherwise the `instantiate` hook returns a factory object, and
//> load.[[Module]] is set during the link phase, when the
//> `factory.execute()` method returns a Module.
//>
// A Load is in one of four states:
//
// 1. Loading: Source is not available yet.
//
// .status === "loading"
// .linkSets is a Set of LinkSets
//
// The load leaves this state when (a) the source is successfully parsed;
// (b) an error causes the load to fail; or (c) the `instantiate` loader
// hook returns a Module object.
//
// 2. Loaded: Source is available and has been translated and parsed.
// Dependencies have been identified. But the module hasn't been linked or
// evaluated yet. We are waiting for dependencies.
//
// This implementation treats the `Module` object as already existing at
// this point (except for factory-made modules). But it has not been
// linked and thus must not be exposed to script yet.
//
// The `"loaded"` state says nothing about the status of the dependencies;
// they may all be linked and evaluated and yet there may not be any
// `LinkSet` that's ready to link and evaluate this module. The `LinkSet`
// may be waiting for unrelated dependencies to load.
//
// .status === "loaded"
// .body is a ModuleBody, or null
// .dependencies is a Map of strings (module requests)
// to strings (full module names)
// .factory is a callable object or null
//
// Exactly one of `[.body, .factory]` is non-null.
// If .body is null, then .dependencies is null.
//
// The load leaves this state when a LinkSet successfully links the module
// and moves it into the loader's module registry.
//
// 3. Linked: The module has been linked and added to the loader's module
// registry. Its body may or may not have been evaluated yet (see
// `EnsureEvaluated`).
//
// .status === "linked"
// .module is a Module object
//
// (TODO: this is not true in the case of the `instantiate` loader hook
// returning a Module object; may want a separate status for that) Loads
// that enter this state are removed from the `loader.loads` table and from
// all LinkSets; they become garbage.
//
// 4. Failed: The load failed. The load never leaves this state.
//
// .status === "failed"
// .exception is an exception value
//
//> #### CreateLoad(name) Abstract Operation
//>
//> The abstract operation CreateLoad creates and returns a new Load Record.
//> The argument name is either `undefined`, indicating an anonymous module, or
//> a normalized module name.
//>
//> The following steps are taken:
//>
function CreateLoad(name) {
//> 1. Let load be a new Load Record.
return {
//> 2. Set the [[Status]] field of load to `"loading"`.
status: "loading",
//> 3. Set the [[Name]] field of load to name.
name: name,
//> 4. Set the [[LinkSets]] field of load to a new empty List.
linkSets: CreateSet(),
//> 5. Let metadata be the result of ObjectCreate(%ObjectPrototype%).
//> 6. Set the [[Metadata]] field of load to metadata.
metadata: {},
//> 7. Set the [[Address]] field of load to undefined.
address: undefined,
//> 8. Set the [[Source]] field of load to undefined.
source: undefined,
//> 9. Set the [[Kind]] field of load to undefined.
kind: undefined,
//> 10. Set the [[Body]] field of load to undefined.
body: undefined,
//> 11. Set the [[Execute]] field of load to undefined.
execute: undefined,
//> 12. Set the [[Exception]] field of load to undefined.
exception: undefined,
//> 13. Set the [[Module]] field of load to undefined.
module: null,
then: undefined
};
//> 14. Return load.
}
//>
//
// In this implementation, Load objects have an extra property `then:
// undefined` to prevent them from appearing thenable even if the user assigns
// to Object.prototype.then. An alternative would be to use
// Object.create(null) here.
//> #### LoadFailed Functions
//>
//> A LoadFailed function is an anonymous function that marks a Load Record as
//> having failed. All LinkSets that depend on the Load also fail.
//>
//> Each LoadFailed function has a [[Load]] internal slot.
//>
//> When a LoadFailed function F is called with argument exc, the following
//> steps are taken:
//>
function MakeClosure_LoadFailed(load) {
return function (exc) {
//> 1. Let load be F.[[Load]].
//> 2. Assert: load.[[Status]] is `"loading"`.
Assert(load.status === "loading");
//> 3. Set load.[[Status]] to `"failed".
load.status = "failed";
//> 4. Set load.[[Exception]] to exc.
load.exception = exc;
//> 5. Let linkSets be a copy of the List load.[[LinkSets]].
//> 6. For each linkSet in linkSets, in the order in which the LinkSet
//> Records were created,
let linkSets = SetToArray(load.linkSets);
callFunction(std_Array_sort, linkSets,
(a, b) => b.timestamp - a.timestamp);
for (let i = 0; i < linkSets.length; i++) {
//> 1. Call LinkSetFailed(linkSet, exc).
LinkSetFailed(linkSets[i], exc);
}
//> 7. Assert: load.[[LinkSets]] is empty.
Assert(callFunction(std_Set_get_size, load.linkSets) === 0);
};
}
//
// The attached LinkSets fail in timestamp order. *Rationale*: Any
// deterministic order would do.
// ## The loader pipeline
//> #### RequestLoad(loader, request, referrerName, referrerAddress) Abstract Operation
//>
//> The RequestLoad abstract operation normalizes the given module name,
//> request, and returns a promise that resolves to the value of a Load object
//> for the given module.
//>
//> The loader argument is a Loader object.
//>
//> request is the (non-normalized) name of the module to be imported, as it
//> appears in the import-declaration or as the argument to `loader.load()` or
//> `loader.import()`.
//>
//> referrerName and referrerAddress provide information about the context of
//> the `import()` call or import-declaration. This information is passed to
//> all the loader hooks.
//>
//> If the requested module is already in the loader's module registry,
//> RequestLoad returns a promise for a Load with the [[Status]] field set to
//> `"linked"`. If the requested module is loading or loaded but not yet
//> linked, RequestLoad returns a promise for an existing Load object from
//> loader.[[Loads]]. Otherwise, RequestLoad starts loading the module and
//> returns a promise for a new Load Record.
//>
//> The following steps are taken:
//>
function RequestLoad(loader, request, referrerName, referrerAddress) {
//> 1. Let F be a new anonymous function as defined by CallNormalize.
//> 2. Set the [[Loader]] internal slot of F to loader.
//> 3. Set the [[Request]] internal slot of F to request.
//> 4. Set the [[ReferrerName]] internal slot of F to referrerName.
//> 5. Set the [[ReferrerAddress]] internal slot of F to referrerAddress.
var F = MakeClosure_CallNormalize(loader, request, referrerName,
referrerAddress);
//> 6. Let p be the result of calling OrdinaryConstruct(%Promise%, (F)).
var p = new std_Promise(F);
//> 7. Let G be a new anonymous function as defined by GetOrCreateLoad.
//> 8. Set the [[Loader]] internal slot of G to loader.
//> 9. Let p be the result of calling PromiseThen(p, G).
p = callFunction(std_Promise_then, p,
MakeClosure_GetOrCreateLoad(loader));
//> 10. Return p.
return p;
}
//>
//> #### CallNormalize Functions
//>
//> A CallNormalize function is an anonymous function that calls a loader's
//> normalize hook.
//>
//> Each CallNormalize function has internal slots [[Loader]], [[Request]],
//> [[ReferrerName]], and [[ReferrerAddress]].
//>
//> When a CallNormalize function F is called with arguments resolve and
//> reject, the following steps are taken.
//>
function MakeClosure_CallNormalize(loader, request, referrerName, referrerAddress) {
return function (resolve, reject) {
//> 1. Let loader be F.[[Loader]].
//> 2. Let request be F.[[Request]].
//> 3. Let referrerName be F.[[ReferrerName]].
//> 4. Let referrerAddress be F.[[ReferrerAddress]].
//> 5. Let normalizeHook be the result of Get(loader, `"normalize"`).
//> 6. Let name be the result of calling the [[Call]] internal method
//> of normalizeHook passing loader and (request, referrerName,
//> referrerAddress) as arguments.
//> 7. ReturnIfAbrupt(name).
//> 8. Call the [[Call]] internal method of resolve passing undefined
//> and (name) as arguments.
resolve(loader.normalize(request, referrerName, referrerAddress));
};
}
//> #### GetOrCreateLoad Functions
//>
//> A GetOrCreateLoad function is an anonymous function that gets or creates a Load
//> Record for a given module name.
//>
//> Each GetOrCreateLoad function has a [[Loader]] internal slot.
//>
//> When a GetOrCreateLoad function F is called with argument name, the following
//> steps are taken:
//>
function MakeClosure_GetOrCreateLoad(loader) {
return function (name) {
var loaderData = GetLoaderInternalData(loader);
//> 1. Let loader be F.[[Loader]].
//> 2. Let name be ToString(name).
//> 3. ReturnIfAbrupt(name).
name = ToString(name);
//> 4. If there is a Record in loader.[[Modules]] whose [[key]] field
//> is equal to name, then
var existingModule = callFunction(std_Map_get, loaderData.modules, name);
if (existingModule !== undefined) {
//> 1. Let existingModule be the [[value]] field of that Record.
//> 2. Let load be the result of CreateLoad(name).
//> 3. Set the [[Status]] field of load to `"linked"`.
//> 4. Set the [[Module]] field of load to existingModule.
//> 5. Return load.
var load = CreateLoad(name);
load.status = "linked";
load.module = existingModule;
return load;
}
//> 5. Else, if there is a Load Record in the List loader.[[Loads]]
//> whose [[Name]] field is equal to name, then
var load = callFunction(std_Map_get, loaderData.loads, name);
if (load !== undefined) {
//> 1. Let load be that Load Record.
//> 2. Assert: load.status is either `"loading"` or `"loaded"`.
Assert(load.status === "loading" || load.status === "loaded");
//> 3. Return load.
return load;
}
//> 6. Let load be the result of CreateLoad(name).
load = CreateLoad(name);
//> 7. Add load to the List loader.[[Loads]].
callFunction(std_Map_set, loaderData.loads, name, load);
//> 8. Call ProceedToLocate(loader, load).
ProceedToLocate(loader, load);
//> 9. Return load.
return load;
};
}
//> #### ProceedToLocate(loader, load, p) Abstract Operation
//>
//> The ProceedToLocate abstract operation continues the asynchronous loading
//> process at the `locate` hook.
//>
//> ProceedToLocate performs the following steps:
//>
function ProceedToLocate(loader, load) {
//> 1. Let p be the result of PromiseResolve(undefined).
var p = PromiseOf(undefined);
//> 1. Let F be a new anonymous function object as defined in
//> CallLocate.
//> 1. Set F.[[Loader]] to loader.
//> 1. Set F.[[Load]] to load.
//> 1. Let p be the result of calling PromiseThen(p, F).
p = callFunction(std_Promise_then, p,
MakeClosure_CallLocate(loader, load));
//> 1. Return ProceedToFetch(loader, load, p).
return ProceedToFetch(loader, load, p);
}
//> #### ProceedToFetch(loader, load, p) Abstract Operation
//>
//> The ProceedToFetch abstract operation continues the asynchronous loading
//> process at the `fetch` hook.
//>
//> ProceedToFetch performs the following steps:
//>
function ProceedToFetch(loader, load, p) {
//> 1. Let F be a new anonymous function object as defined in
//> CallFetch.
//> 1. Set F.[[Loader]] to loader.
//> 1. Set F.[[Load]] to load.
//> 1. Let p be the result of calling PromiseThen(p, F).
p = callFunction(std_Promise_then, p,
MakeClosure_CallFetch(loader, load));
//> 1. Return ProceedToTranslate(loader, load, p).
return ProceedToTranslate(loader, load, p);
}
//> #### ProceedToTranslate(loader, load, p) Abstract Operation
//>
//> The ProceedToTranslate abstract operation continues the asynchronous loading
//> process at the `translate` hook.
//>
//> ProceedToTranslate performs the following steps:
//>
function ProceedToTranslate(loader, load, p) {
//> 1. Let F be a new anonymous function object as defined in
//> CallTranslate.
//> 1. Set F.[[Loader]] to loader.
//> 1. Set F.[[Load]] to load.
//> 1. Let p be the result of calling PromiseThen(p, F).
p = callFunction(std_Promise_then, p,
MakeClosure_CallTranslate(loader, load));
//> 1. Let F be a new anonymous function object as defined in
//> CallInstantiate.
//> 1. Set F.[[Loader]] to loader.
//> 1. Set F.[[Load]] to load.
//> 1. Let p be the result of calling PromiseThen(p, F).
p = callFunction(std_Promise_then, p,
MakeClosure_CallInstantiate(loader, load));
//> 1. Let F be a new anonymous function object as defined in
//> InstantiateSucceeded.
//> 1. Set F.[[Loader]] to loader.
//> 1. Set F.[[Load]] to load.
//> 1. Let p be the result of calling PromiseThen(p, F).
p = callFunction(std_Promise_then, p,
MakeClosure_InstantiateSucceeded(loader, load));
//> 1. Let F be a new anonymous function object as defined in
//> LoadFailed.
//> 1. Set F.[[Load]] to load.
//> 1. Let p be the result of calling PromiseCatch(p, F).
callFunction(std_Promise_catch, p,
MakeClosure_LoadFailed(load));
}
//> #### SimpleDefine(obj, name, value) Abstract Operation
//>
//> The SimpleDefine operation defines a writable, configurable, enumerable
//> data property on an ordinary object by taking the following steps:
//>
//> 1. Return the result of calling OrdinaryDefineOwnProperty with arguments
//> obj, name, and PropertyDescriptor{[[Value]]: value, [[Writable]]: true,
//> [[Enumerable]]: true, [[Configurable]]: true}.
//>
//> #### CallLocate Functions
//>
//> A CallLocate function is an anonymous function that calls the `locate`
//> loader hook.
//>
//> Each CallLocate function has [[Loader]] and [[Load]] internal slots.
//>
//> When a CallLocate function F is called, the following steps are taken:
//>
function MakeClosure_CallLocate(loader, load) {
return function (_) {
//> 1. Let loader be F.[[Loader]].
//> 2. Let load be F.[[Load]].
//> 3. Let hook be the result of Get(loader, `"locate"`).
//> 4. ReturnIfAbrupt(hook).
//> 5. If IsCallable(hook) is false, throw a TypeError exception.
//> 6. Let obj be the result of calling
//> ObjectCreate(%ObjectPrototype%, ()).
//> 7. Call SimpleDefine(obj, `"name"`, load.[[Name]]).
//> 8. Call SimpleDefine(obj, `"metadata"`, load.[[Metadata]]).
//> 9. Return the result of calling the [[Call]] internal method of
//> hook with loader and (obj) as arguments.
return loader.locate({
name: load.name,
metadata: load.metadata
});
};
}
//>
//> #### CallFetch Functions
//>
//> A CallFetch function is an anonymous function that calls the `fetch`
//> loader hook.
//>
//> Each CallFetch function has [[Loader]] and [[Load]] internal slots.
//>
//> When a CallFetch function F is called with argument address, the following
//> steps are taken:
//>
function MakeClosure_CallFetch(loader, load) {
return function (address) {
//> 1. Let loader be F.[[Loader]].
//> 2. Let load be F.[[Load]].
//> 3. If load.[[LinkSets]] is an empty List, return undefined.
if (callFunction(std_Set_get_size, load.linkSets) === 0)
return;
//> 4. Set the [[Address]] field of load to address.
load.address = address;
//> 5. Let hook be the result of Get(loader, `"fetch"`).
//> 6. ReturnIfAbrupt(hook).
//> 7. If IsCallable(hook) is false, throw a TypeError exception.
//> 8. Let obj be the result of calling
//> ObjectCreate(%ObjectPrototype%, ()).
//> 9. Call SimpleDefine(obj, `"name"`, load.[[Name]]).
//> 10. Call SimpleDefine(obj, `"metadata"`, load.[[Metadata]]).
//> 11. Call SimpleDefine(obj, `"address"`, address).
//> 12. Return the result of calling the [[Call]] internal method of
//> hook with loader and (obj) as arguments.
return loader.fetch({
name: load.name,
metadata: load.metadata,
address: address
});
};
}
//>
//> #### CallTranslate Functions
//>
//> A CallTranslate function is an anonymous function that calls the `translate`
//> loader hook.
//>
//> Each CallTranslate function has [[Loader]] and [[Load]] internal slots.
//>
//> When a CallTranslate function F is called with argument source, the following
//> steps are taken:
//>
function MakeClosure_CallTranslate(loader, load) {
return function (source) {
//> 1. Let loader be F.[[Loader]].
//> 2. Let load be F.[[Load]].
//> 3. If load.[[LinkSets]] is an empty List, return undefined.
if (callFunction(std_Set_get_size, load.linkSets) === 0)
return;
//> 5. Let hook be the result of Get(loader, `"translate"`).
//> 6. ReturnIfAbrupt(hook).
//> 7. If IsCallable(hook) is false, throw a TypeError exception.
//> 8. Let obj be the result of calling
//> ObjectCreate(%ObjectPrototype%, ()).
//> 9. Call SimpleDefine(obj, `"name"`, load.[[Name]]).
//> 10. Call SimpleDefine(obj, `"metadata"`, load.[[Metadata]]).
//> 11. Call SimpleDefine(obj, `"address"`, load.[[Address]]).
//> 12. Call SimpleDefine(obj, `"source"`, source).
//> 13. Return the result of calling the [[Call]] internal method of
//> hook with loader and (obj) as arguments.
return loader.translate({
name: load.name,
metadata: load.metadata,
address: load.address,
source: source
});
};
}
//>
//> #### CallInstantiate Functions
//>
//> A CallInstantiate function is an anonymous function that calls the
//> `instantiate` loader hook.
//>
//> Each CallInstantiate function has [[Loader]] and [[Load]] internal slots.
//>
//> When a CallInstantiate function F is called with argument source, the
//> following steps are taken:
//>
function MakeClosure_CallInstantiate(loader, load) {
return function (source) {
//> 1. Let loader be F.[[Loader]].
//> 2. Let load be F.[[Load]].
//> 3. If load.[[LinkSets]] is an empty List, return undefined.
if (callFunction(std_Set_get_size, load.linkSets) === 0)
return;
//> 4. Set the [[Source]] internal slot of load to source.
load.source = source;
//> 5. Let hook be the result of Get(loader, `"instantiate"`).
//> 6. ReturnIfAbrupt(hook).
//> 7. If IsCallable(hook) is false, throw a TypeError exception.
//> 8. Let obj be the result of calling
//> ObjectCreate(%ObjectPrototype%, ()).
//> 9. Call SimpleDefine(obj, `"name"`, load.[[Name]]).
//> 10. Call SimpleDefine(obj, `"metadata"`, load.[[Metadata]]).
//> 11. Call SimpleDefine(obj, `"address"`, load.[[Address]]).
//> 12. Call SimpleDefine(obj, `"source"`, source).
//> 13. Return the result of calling the [[Call]] internal method of
//> hook with loader and (obj) as arguments.
return loader.instantiate({
name: load.name,
metadata: load.metadata,
address: load.address,
source: source
});
};
}
//>
//> #### InstantiateSucceeded Functions
//>
//> An InstantiateSucceeded function is an anonymous function that handles
//> the result of the `instantiate` hook.
//>
//> Each InstantiateSucceeded function has [[Loader]] and [[Load]] internal
//> slots.
//>
//> When an InstantiateSucceeded function F is called with argument
//> instantiateResult, the following steps are taken:
//>
function MakeClosure_InstantiateSucceeded(loader, load) {
return function (instantiateResult) {
//> 1. Let loader be F.[[Loader]].
//> 2. Let load be F.[[Load]].
//> 3. If load.[[LinkSets]] is an empty List, return undefined.
if (callFunction(std_Set_get_size, load.linkSets) === 0)
return;
var depsList;
//> 4. If instantiateResult is undefined, then
if (instantiateResult === undefined) {
//> 1. Let body be the result of parsing load.[[Source]],
//> interpreted as UTF-16 encoded Unicode text as described
//> in clause 10.1.1, using Module as the goal
//> symbol. Throw a SyntaxError exception if the parse
//> fails or if any static semantics errors are detected.
let body = $ParseModule(loader, load.source, load.name, load.address);
//> 2. Set the [[Body]] field of load to body.
load.body = body;
//> 3. Set the [[Kind]] field of load to **declarative**.
load.kind = "declarative";
//> 4. Let depsList be the ModuleRequests of body.
depsList = $ModuleRequests(body);
//> 5. Else if Type(instantiateResult) is Object, then
} else if (IsObject(instantiateResult)) {
//> 1. Let deps be the result of Get(instantiateResult, `"deps"`).
//> 2. ReturnIfAbrupt(deps).
var deps = instantiateResult.deps;
//> 3. If deps is undefined, then let depsList be a new empty List.
//> 4. Else:
//> 1. Let depsList be the result of calling the IterableToArray
//> abstract operation passing deps as the single argument.
//> 2. ReturnIfAbrupt(depsList).
depsList = deps === undefined ? [] : [...deps];
//> 5. Let execute be the result of Get(instantiateResult,
//> `"execute"`).
//> 6. ReturnIfAbrupt(execute).
var execute = instantiateResult.execute;
//> 7. Set the [[Execute]] field of load to execute.
load.execute = execute;
//> 8. Set the [[Kind]] field of load to **dynamic**.
load.kind = "dynamic";
//> 6. Else,
} else {
//> 1. Throw a TypeError exception.
throw std_TypeError("instantiate hook must return an object or undefined");
}
//> 7. Return the result of calling ProcessLoadDependencies(load,
//> loader, depsList).
;// ProcessLoadDependencies returns a promise. The only way to
;// propagate errors is to return it.
return ProcessLoadDependencies(load, loader, depsList);
};
}
//> #### ProcessLoadDependencies(load, loader, depsList) Abstract Operation
//>
//> The ProcessLoadDependencies abstract operation is called after one module
//> has nearly finished loading. It starts new loads as needed to load the
//> module's dependencies.
//>
//> ProcessLoadDependencies also arranges for LoadSucceeded to be called.
//>
//> The following steps are taken:
//>
function ProcessLoadDependencies(load, loader, depsList) {
//> 1. Let referrerName be load.[[Name]].
var referrerName = load.name;
//> 2. Set the [[Dependencies]] field of load to a new empty List.
load.dependencies = CreateMap();
//> 3. Let loadPromises be a new empty List.
var loadPromises = [];
//> 4. For each request in depsList, do
for (var i = 0; i < depsList.length; i++) {
var request = depsList[i];
//> 1. Let p be the result of RequestLoad(loader, request,
//> referrerName, load.[[Address]]).
var p = RequestLoad(loader, request, referrerName, load.address);
//> 2. Let F be a new anonymous function as defined by
//> AddDependencyLoad.
//> 3. Set the [[ParentLoad]] internal slot of F to load.
//> 4. Set the [[Request]] internal slot of F to request.
//> 5. Let p be the result of PromiseThen(p, F).
p = callFunction(std_Promise_then, p,
MakeClosure_AddDependencyLoad(load, request));
//> 6. Append p as the last element of loadPromises.