-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Copy pathmodule-functionality.js
375 lines (358 loc) · 19.7 KB
/
module-functionality.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
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
// ES6 Module functionality tests -- verifies functionality of import and export statements
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
function testModuleScript(source, message, shouldFail = false) {
let testfunc = () => testRunner.LoadModule(source, 'samethread', shouldFail);
if (shouldFail) {
let caught = false;
// We can't use assert.throws here because the SyntaxError used to construct the thrown error
// is from a different context so it won't be strictly equal to our SyntaxError.
try {
testfunc();
} catch(e) {
caught = true;
// Compare toString output of SyntaxError and other context SyntaxError constructor.
assert.areEqual(e.constructor.toString(), SyntaxError.toString(), message);
}
assert.isTrue(caught, `Expected error not thrown: ${message}`);
} else {
assert.doesNotThrow(testfunc, message);
}
}
var tests = [
{
name: "Validate a simple module export",
body: function () {
let functionBody =
`import { ModuleSimpleExport_foo } from 'ModuleSimpleExport.js';
assert.areEqual('ModuleSimpleExport', ModuleSimpleExport_foo(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleExport.js');`;
testModuleScript(functionBody, "Test importing a simple exported function", false);
}
},
{
name: "Validate importing from multiple modules",
body: function () {
let functionBody =
`import { ModuleSimpleExport_foo } from 'ModuleSimpleExport.js';
assert.areEqual('ModuleSimpleExport', ModuleSimpleExport_foo(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleExport.js');
import { foo2 } from 'ModuleComplexExports.js';
assert.areEqual('foo', foo2(), 'Failed to import foo2 from ModuleComplexExports.js');`;
testModuleScript(functionBody, "Test importing from multiple modules", false);
}
},
{
name: "Validate a variety of more complex exports",
body: function () {
let functionBody =
`import { foo, foo2 } from 'ModuleComplexExports.js';
assert.areEqual('foo', foo(), 'Failed to import foo from ModuleComplexExports.js');
assert.areEqual('foo', foo2(), 'Failed to import foo2 from ModuleComplexExports.js');
import { bar, bar2 } from 'ModuleComplexExports.js';
assert.areEqual('bar', bar(), 'Failed to import bar from ModuleComplexExports.js');
assert.areEqual('bar', bar2(), 'Failed to import bar2 from ModuleComplexExports.js');
import { let2, let3, let4, let5 } from 'ModuleComplexExports.js';
assert.areEqual('let2', let2, 'Failed to import let2 from ModuleComplexExports.js');
assert.areEqual('let3', let3, 'Failed to import let3 from ModuleComplexExports.js');
assert.areEqual('let2', let4, 'Failed to import let4 from ModuleComplexExports.js');
assert.areEqual('let3', let5, 'Failed to import let5 from ModuleComplexExports.js');
import { const2, const3, const4, const5 } from 'ModuleComplexExports.js';
assert.areEqual('const2', const2, 'Failed to import const2 from ModuleComplexExports.js');
assert.areEqual('const3', const3, 'Failed to import const3 from ModuleComplexExports.js');
assert.areEqual('const2', const4, 'Failed to import const4 from ModuleComplexExports.js');
assert.areEqual('const3', const5, 'Failed to import const5 from ModuleComplexExports.js');
import { var2, var3, var4, var5 } from 'ModuleComplexExports.js';
assert.areEqual('var2', var2, 'Failed to import var2 from ModuleComplexExports.js');
assert.areEqual('var3', var3, 'Failed to import var3 from ModuleComplexExports.js');
assert.areEqual('var2', var4, 'Failed to import var4 from ModuleComplexExports.js');
assert.areEqual('var3', var5, 'Failed to import var5 from ModuleComplexExports.js');
import { class2, class3, class4, class5 } from 'ModuleComplexExports.js';
assert.areEqual('class2', class2.static_member(), 'Failed to import class2 from ModuleComplexExports.js');
assert.areEqual('class2', new class2().member(), 'Failed to create intance of class2 from ModuleComplexExports.js');
assert.areEqual('class2', class3.static_member(), 'Failed to import class3 from ModuleComplexExports.js');
assert.areEqual('class2', new class3().member(), 'Failed to create intance of class3 from ModuleComplexExports.js');
assert.areEqual('class4', class4.static_member(), 'Failed to import class4 from ModuleComplexExports.js');
assert.areEqual('class4', new class4().member(), 'Failed to create intance of class4 from ModuleComplexExports.js');
assert.areEqual('class4', class5.static_member(), 'Failed to import class4 from ModuleComplexExports.js');
assert.areEqual('class4', new class5().member(), 'Failed to create intance of class4 from ModuleComplexExports.js');
import _default from 'ModuleComplexExports.js';
assert.areEqual('default', _default(), 'Failed to import default from ModuleComplexExports.js');
`;
testModuleScript(functionBody, "Test importing a variety of exports", false);
}
},
{
name: "Import an export as a different binding identifier",
body: function () {
let functionBody =
`import { ModuleSimpleExport_foo as foo3 } from 'ModuleSimpleExport.js';
assert.areEqual('ModuleSimpleExport', foo3(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleExport.js');
import { foo2 as foo4 } from 'ModuleComplexExports.js';
assert.areEqual('foo', foo4(), 'Failed to import foo4 from ModuleComplexExports.js');`;
testModuleScript(functionBody, "Test importing as different binding identifiers", false);
}
},
{
name: "Import the same export under multiple local binding identifiers",
body: function () {
let functionBody =
`import { ModuleSimpleExport_foo as foo3, ModuleSimpleExport_foo as foo4 } from 'ModuleSimpleExport.js';
assert.areEqual('ModuleSimpleExport', foo3(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleExport.js');
assert.areEqual('ModuleSimpleExport', foo4(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleExport.js');
assert.isTrue(foo3 === foo4, 'Export has the same value even if rebound');`;
testModuleScript(functionBody, "Test importing the same export under multiple binding identifier", false);
}
},
{
name: "Exporting module changes exported value",
body: function () {
let functionBody =
`import { target, changeTarget } from 'ModuleComplexExports.js';
assert.areEqual('before', target(), 'Failed to import target from ModuleComplexExports.js');
assert.areEqual('ok', changeTarget(), 'Failed to import changeTarget from ModuleComplexExports.js');
assert.areEqual('after', target(), 'changeTarget failed to change export value');`;
testModuleScript(functionBody, "Changing exported value", false);
}
},
{
name: "Simple re-export forwards import to correct slot",
body: function () {
let functionBody =
`import { ModuleSimpleExport_foo } from 'ModuleSimpleReexport.js';
assert.areEqual('ModuleSimpleExport', ModuleSimpleExport_foo(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleReexport.js');`;
testModuleScript(functionBody, "Simple re-export from one module to another", false);
}
},
{
name: "Import of renamed re-export forwards import to correct slot",
body: function () {
let functionBody =
`import { ModuleSimpleExport_foo as ModuleSimpleExport_baz } from 'ModuleSimpleReexport.js';
assert.areEqual('ModuleSimpleExport', ModuleSimpleExport_baz(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleReexport.js');`;
testModuleScript(functionBody, "Rename simple re-export", false);
}
},
{
name: "Renamed re-export and renamed import",
body: function () {
let functionBody =
`import { ModuleComplexReexports_foo as ModuleComplexReexports_baz } from 'ModuleComplexReexports.js';
assert.areEqual('bar', ModuleComplexReexports_baz(), 'Failed to import ModuleComplexReexports_foo from ModuleComplexReexports.js');`;
testModuleScript(functionBody, "Rename already renamed re-export", false);
}
},
{
name: "Explicit export/import to default binding",
body: function () {
let functionBody =
`import { default as baz } from 'ModuleDefaultExport1.js';
assert.areEqual('ModuleDefaultExport1', baz(), 'Failed to import default from ModuleDefaultExport1.js');`;
testModuleScript(functionBody, "Explicitly export and import a local name to the default binding", false);
}
},
{
name: "Explicit import of default binding",
body: function () {
let functionBody =
`import { default as baz } from 'ModuleDefaultExport2.js';
assert.areEqual('ModuleDefaultExport2', baz(), 'Failed to import default from ModuleDefaultExport2.js');`;
testModuleScript(functionBody, "Explicitly import the default export binding", false);
}
},
{
name: "Implicitly re-export default export",
body: function () {
let functionBody =
`import baz from 'ModuleDefaultReexport.js';
assert.areEqual('ModuleDefaultExport1', baz(), 'Failed to import default from ModuleDefaultReexport.js');`;
testModuleScript(functionBody, "Implicitly re-export the default export binding", false);
}
},
{
name: "Implicitly re-export default export and rename the imported binding",
body: function () {
let functionBody =
`import { default as baz } from 'ModuleDefaultReexport.js';
assert.areEqual('ModuleDefaultExport1', baz(), 'Failed to import default from ModuleDefaultReexport.js');
import { not_default as bat } from 'ModuleDefaultReexport.js';
assert.areEqual('ModuleDefaultExport2', bat(), 'Failed to import not_default from ModuleDefaultReexport.js');`;
testModuleScript(functionBody, "Implicitly re-export the default export binding and rename the import binding", false);
}
},
{
name: "Exporting module changes value of default export",
body: function () {
let functionBody =
`import ModuleDefaultExport3_default from 'ModuleDefaultExport3.js';
assert.areEqual(2, ModuleDefaultExport3_default, 'Failed to import default from ModuleDefaultExport3.js');
import ModuleDefaultExport4_default from 'ModuleDefaultExport4.js';
assert.areEqual(1, ModuleDefaultExport4_default, 'Failed to import not_default from ModuleDefaultExport4.js');`;
testModuleScript(functionBody, "Exported value incorrectly bound", false);
}
},
{
name: "Import bindings used in a nested function",
body: function () {
let functionBody =
`function test() {
assert.areEqual('ModuleDefaultExport2', foo(), 'Failed to import default from ModuleDefaultExport2.js');
}
test();
import foo from 'ModuleDefaultExport2.js';
test();`;
testModuleScript(functionBody, "Failed to find imported name correctly in nested function", false);
}
},
{
name: "Exported name may be any keyword",
body: function () {
let functionBody =
`import { export as baz } from 'ModuleComplexExports.js';
assert.areEqual('ModuleComplexExports', baz, 'Failed to import export from ModuleDefaultExport2.js');
import { function as bat } from 'ModuleComplexExports.js';
assert.areEqual('ModuleComplexExports', bat, 'Failed to import function from ModuleDefaultExport2.js');`;
testModuleScript(functionBody, "Exported name may be a keyword (import binding must be binding identifier)", false);
}
},
{
name: "Import binding of a keyword-named export may not be a keyword unless it is bound to a different binding identifier",
body: function () {
let functionBody = `import { export } from 'ModuleComplexExports.js';`;
testModuleScript(functionBody, "Import binding must be binding identifier even if export name is not (export)", true);
functionBody = `import { function } from 'ModuleComplexExports.js';`;
testModuleScript(functionBody, "Import binding must be binding identifier even if export name is not (function)", true);
functionBody = `import { switch } from 'ModuleComplexReexports.js';`;
testModuleScript(functionBody, "Import binding must be binding identifier even if re-export name is not (switch)", true);
}
},
{
name: "Exported name may be any keyword testing re-exports",
body: function () {
let functionBody =
`import { switch as baz } from 'ModuleComplexReexports.js';
assert.areEqual('ModuleComplexExports', baz, 'Failed to import switch from ModuleComplexReexports.js');`;
testModuleScript(functionBody, "Exported name may be a keyword including re-epxort chains", false);
}
},
{
name: "Odd case of 'export { as as as }; import { as as as };'",
body: function () {
let functionBody =
`import { as as as } from 'ModuleComplexExports.js';
assert.areEqual('as', as(), 'String "as" is not reserved word');`;
testModuleScript(functionBody, "Test 'import { as as as}'", false);
}
},
{
name: "Typeof a module export",
body: function () {
let functionBody =
`import _default from 'ModuleDefaultExport2.js';
assert.areEqual('function', typeof _default, 'typeof default export from ModuleDefaultExport2.js is function');`;
testRunner.LoadModule(functionBody, 'samethread');
}
},
{
name: "Circular module dependency",
body: function () {
let functionBody =
`import { circular_foo } from 'ModuleCircularFoo.js';
assert.areEqual(2, circular_foo(), 'This function calls between both modules in the circular dependency incrementing a counter in each');
import { circular_bar } from 'ModuleCircularBar.js';
assert.areEqual(4, circular_bar(), 'Second call originates in the other module but still increments the counter twice');`;
testRunner.LoadModule(functionBody, 'samethread');
}
},
{
name: "Circular module dependency: starExportList should allow multiple/recursive visits to re-resolve different names",
body: function () {
let functionBody =
`import './module-3250-bug-dep.js';`;
testModuleScript(functionBody);
}
},
{
name: "Circular module dependency: non-root GetExportNames should not be cached",
body: function () {
let functionBody =
`import './module-3250-ext-a.js';`;
testModuleScript(functionBody);
}
},
{
name: "Implicitly re-exporting an import binding (import { foo } from ''; export { foo };)",
body: function () {
let functionBody =
`import { foo, baz, localfoo, bar, localfoo2, bar2, bar2 as bar3 } from 'ModuleComplexReexports.js';
assert.areEqual('foo', foo(), 'Simple implicit re-export');
assert.areEqual('foo', baz(), 'Renamed export imported and renamed during implicit re-export');
assert.areEqual('foo', localfoo(), 'Export renamed as import and implicitly re-exported');
assert.areEqual('foo', bar(), 'Renamed export renamed as import and renamed again during implicit re-exported');
assert.areEqual('foo', localfoo2(), 'Renamed export renamed as import and implicitly re-exported');
assert.areEqual('foo', bar2(), 'Renamed export renamed as import and renamed again during implicit re-export');
assert.areEqual('foo', bar3(), 'Renamed export renamed as import renamed during implicit re-export and renamed in final import');`;
testRunner.LoadModule(functionBody, 'samethread');
}
},
{
name: "Nested function in module function body which captures exported symbol doesn't create empty frame object",
body: function() {
let functionBody =
`function foo() { };
export { foo };
function bar() { foo(); };`;
testRunner.LoadModule(functionBody, 'samethread');
}
},
{
name: "try/catch at top level at module source should not throw syntax error",
body: function() {
let functionBody =
`try {
var k = 1;
} catch(e) {
}
export var x = 1; `;
testRunner.LoadModule(functionBody, 'samethread');
}
},
{
name: "OS13924587 - should export only top level vars",
body: function() {
let functionBody =
`
export {a as baz};
function foo(a) {
function bar() {
a;
}
}
var a = 1;
foo(30);
assert.areEqual(1, a);
`;
testRunner.LoadModule(functionBody, 'samethread');
}
},
{
name: "OS18171347 - Module's parser leaks temporary Guest Arena allocator when module has a regex pattern.",
body: function() {
testRunner.LoadModule(` /x/ ;`, 'samethread');
try
{
// syntax error
testRunner.LoadModule(` /x/ ; for(i=0);`, 'samethread', {shouldFail:true});
}
catch(e){}
try
{
// import non-existent module
testRunner.LoadModule(` /x/ ;import 'funfuzz_moduleScript3';`, 'samethread', {shouldFail:true});
}
catch(e){}
}
},
];
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });