diff options
Diffstat (limited to 'js/src/jit-test/tests/modules')
129 files changed, 3168 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/modules/add-to-namespace-import.js b/js/src/jit-test/tests/modules/add-to-namespace-import.js new file mode 100644 index 0000000000..b5a6626bfc --- /dev/null +++ b/js/src/jit-test/tests/modules/add-to-namespace-import.js @@ -0,0 +1,3 @@ +// |jit-test| module; error: TypeError +import * as ns from "module1.js"; +ns.b = 2; diff --git a/js/src/jit-test/tests/modules/ambiguous-import.js b/js/src/jit-test/tests/modules/ambiguous-import.js new file mode 100644 index 0000000000..6a91f9537b --- /dev/null +++ b/js/src/jit-test/tests/modules/ambiguous-import.js @@ -0,0 +1,2 @@ +// |jit-test| module; error: SyntaxError +import { a } from "ambiguous.js"; diff --git a/js/src/jit-test/tests/modules/ambiguous-indirect-export.js b/js/src/jit-test/tests/modules/ambiguous-indirect-export.js new file mode 100644 index 0000000000..17949955ea --- /dev/null +++ b/js/src/jit-test/tests/modules/ambiguous-indirect-export.js @@ -0,0 +1,2 @@ +// |jit-test| module; error: SyntaxError +export { a } from "ambiguous.js"; diff --git a/js/src/jit-test/tests/modules/ambiguous-star-export.js b/js/src/jit-test/tests/modules/ambiguous-star-export.js new file mode 100644 index 0000000000..2542714061 --- /dev/null +++ b/js/src/jit-test/tests/modules/ambiguous-star-export.js @@ -0,0 +1,43 @@ +// Test ambigious export * statements. + +"use strict"; + +load(libdir + "asserts.js"); + +function checkModuleEval(source) { + let m = parseModule(source); + m.declarationInstantiation(); + m.evaluation(); + return m; +} + +function checkModuleSyntaxError(source) { + let m = parseModule(source); + assertThrowsInstanceOf(() => m.declarationInstantiation(), SyntaxError); +} + +let a = registerModule('a', parseModule("export var a = 1; export var b = 2;")); +let b = registerModule('b', parseModule("export var b = 3; export var c = 4;")); +let c = registerModule('c', parseModule("export * from 'a'; export * from 'b';")); +c.declarationInstantiation(); +c.evaluation(); + +// Check importing/exporting non-ambiguous name works. +let d = checkModuleEval("import { a } from 'c';"); +assertEq(getModuleEnvironmentValue(d, "a"), 1); +checkModuleEval("export { a } from 'c';"); + +// Check importing/exporting ambiguous name is a syntax error. +checkModuleSyntaxError("import { b } from 'c';"); +checkModuleSyntaxError("export { b } from 'c';"); + +// Check that namespace objects include only non-ambiguous names. +let m = parseModule("import * as ns from 'c';"); +m.declarationInstantiation(); +m.evaluation(); +let ns = c.namespace; +let names = Object.keys(ns); +assertEq(names.length, 2); +assertEq('a' in ns, true); +assertEq('b' in ns, false); +assertEq('c' in ns, true); diff --git a/js/src/jit-test/tests/modules/assign-to-import.js b/js/src/jit-test/tests/modules/assign-to-import.js new file mode 100644 index 0000000000..42abd66c50 --- /dev/null +++ b/js/src/jit-test/tests/modules/assign-to-import.js @@ -0,0 +1,3 @@ +// |jit-test| module; error: TypeError +import { a } from "module1.js"; +a = 2; diff --git a/js/src/jit-test/tests/modules/assign-to-namespace-import.js b/js/src/jit-test/tests/modules/assign-to-namespace-import.js new file mode 100644 index 0000000000..faa55b6198 --- /dev/null +++ b/js/src/jit-test/tests/modules/assign-to-namespace-import.js @@ -0,0 +1,3 @@ +// |jit-test| module; error: TypeError +import * as ns from "module1.js"; +ns.a = 2; diff --git a/js/src/jit-test/tests/modules/assign-to-namespace.js b/js/src/jit-test/tests/modules/assign-to-namespace.js new file mode 100644 index 0000000000..396ec1b525 --- /dev/null +++ b/js/src/jit-test/tests/modules/assign-to-namespace.js @@ -0,0 +1,3 @@ +// |jit-test| module; error: TypeError +import * as ns from "module1.js"; +ns = 2; diff --git a/js/src/jit-test/tests/modules/bad-namespace-created.js b/js/src/jit-test/tests/modules/bad-namespace-created.js new file mode 100644 index 0000000000..cc4a6e46a8 --- /dev/null +++ b/js/src/jit-test/tests/modules/bad-namespace-created.js @@ -0,0 +1,15 @@ +// Prior to https://github.com/tc39/ecma262/pull/916 it was possible for a +// module namespace object to be successfully created that was later found to be +// erroneous. Test that this is no longer the case. + +"use strict"; + +load(libdir + "asserts.js"); + +let a = registerModule('A', parseModule('import "B"; export {x} from "C"')); +registerModule('B', parseModule('import * as a from "A"')); +registerModule('C', parseModule('export * from "D"; export * from "E"')); +registerModule('D', parseModule('export let x')); +registerModule('E', parseModule('export let x')); + +assertThrowsInstanceOf(() => a.declarationInstantiation(), SyntaxError); diff --git a/js/src/jit-test/tests/modules/bug-1168666.js b/js/src/jit-test/tests/modules/bug-1168666.js new file mode 100644 index 0000000000..32aea2983b --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1168666.js @@ -0,0 +1,2 @@ +// |jit-test| error: SyntaxError +export * diff --git a/js/src/jit-test/tests/modules/bug-1217593.js b/js/src/jit-test/tests/modules/bug-1217593.js new file mode 100644 index 0000000000..ebf210b38e --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1217593.js @@ -0,0 +1,6 @@ +enableOsiPointRegisterChecks(); +function f() { + return this; +} +f(); +f(); diff --git a/js/src/jit-test/tests/modules/bug-1219044.js b/js/src/jit-test/tests/modules/bug-1219044.js new file mode 100644 index 0000000000..3917d7ca9c --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1219044.js @@ -0,0 +1,4 @@ +// |jit-test| skip-if: !('oomTest' in this) + +oomTest(() => parseModule('import v from "mod";')); +fullcompartmentchecks(true); diff --git a/js/src/jit-test/tests/modules/bug-1219408.js b/js/src/jit-test/tests/modules/bug-1219408.js new file mode 100644 index 0000000000..85d7092894 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1219408.js @@ -0,0 +1,2 @@ +// |jit-test| error: Error +parseModule("").evaluation(); diff --git a/js/src/jit-test/tests/modules/bug-1225346.js b/js/src/jit-test/tests/modules/bug-1225346.js new file mode 100644 index 0000000000..6d8908e981 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1225346.js @@ -0,0 +1,2 @@ +// |jit-test| error: Error: expected filename string, got number +parseModule("", 3); diff --git a/js/src/jit-test/tests/modules/bug-1233117.js b/js/src/jit-test/tests/modules/bug-1233117.js new file mode 100644 index 0000000000..a5a7ee872e --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1233117.js @@ -0,0 +1,2 @@ +// |jit-test| module; --enable-top-level-await +eval("1"); diff --git a/js/src/jit-test/tests/modules/bug-1233179.js b/js/src/jit-test/tests/modules/bug-1233179.js new file mode 100644 index 0000000000..80e742b319 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1233179.js @@ -0,0 +1,8 @@ +// |jit-test| --code-coverage + +let c = parseModule(` + function a(x) { return x; } + function b(x) { return i<40; } + function d(x) { return x + 3; } +`); +getLcovInfo(); diff --git a/js/src/jit-test/tests/modules/bug-1233915.js b/js/src/jit-test/tests/modules/bug-1233915.js new file mode 100644 index 0000000000..e3f2b2788c --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1233915.js @@ -0,0 +1,11 @@ +g = newGlobal({newCompartment: true}); +g.parent = this; +g.eval("(" + function() { + Debugger(parent) + .onExceptionUnwind = function(frame) { + return frame.eval(""); + }; +} + ")()"); +m = parseModule(` s1 `); +m.declarationInstantiation(); +m.evaluation(); diff --git a/js/src/jit-test/tests/modules/bug-1236875.js b/js/src/jit-test/tests/modules/bug-1236875.js new file mode 100644 index 0000000000..41751f9476 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1236875.js @@ -0,0 +1,2 @@ +let m = parseModule(`{ function x() {} }`); +m.declarationInstantiation(); diff --git a/js/src/jit-test/tests/modules/bug-1245518.js b/js/src/jit-test/tests/modules/bug-1245518.js new file mode 100644 index 0000000000..0247453b23 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1245518.js @@ -0,0 +1,15 @@ +evalInFrame = function(global) { + dbgGlobal = newGlobal({newCompartment: true}); + dbg = new dbgGlobal.Debugger(); + return function(upCount, code) { + dbg.addDebuggee(global); + frame = dbg.getNewestFrame().older; + frame.eval(code); + } +}(this); +m = parseModule(` + function g() { return this.hours = 0; } + evalInFrame.call(0, 0, "g()") +`); +m.declarationInstantiation(); +m.evaluation(); diff --git a/js/src/jit-test/tests/modules/bug-1247934.js b/js/src/jit-test/tests/modules/bug-1247934.js new file mode 100644 index 0000000000..666607d1f5 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1247934.js @@ -0,0 +1,5 @@ +setJitCompilerOption("ion.warmup.trigger", 50); +s = ""; +for (i = 0; i < 1024; i++) s += "export let e" + i + "\n"; +registerModule('a', parseModule(s)); +parseModule("import * as ns from 'a'").declarationInstantiation(); diff --git a/js/src/jit-test/tests/modules/bug-1251090.js b/js/src/jit-test/tests/modules/bug-1251090.js new file mode 100644 index 0000000000..5c98f20c03 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1251090.js @@ -0,0 +1,3 @@ +// |jit-test| error: Error +offThreadCompileScript(""); +finishOffThreadModule(); diff --git a/js/src/jit-test/tests/modules/bug-1258097.js b/js/src/jit-test/tests/modules/bug-1258097.js new file mode 100644 index 0000000000..c7f8770436 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1258097.js @@ -0,0 +1,3 @@ +// |jit-test| module; error:SyntaxError +import x from 'y'; +function x() {} diff --git a/js/src/jit-test/tests/modules/bug-1283448.js b/js/src/jit-test/tests/modules/bug-1283448.js new file mode 100644 index 0000000000..9be2828ea4 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1283448.js @@ -0,0 +1,6 @@ +// |jit-test| error: TypeError + +let a = registerModule('a', parseModule("var x = 1; export { x };")); +let b = registerModule('b', parseModule("import { x as y } from 'a';")); +a.__proto__ = {15: 1337}; +b.declarationInstantiation(); diff --git a/js/src/jit-test/tests/modules/bug-1284486-2.js b/js/src/jit-test/tests/modules/bug-1284486-2.js new file mode 100644 index 0000000000..54270caa59 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1284486-2.js @@ -0,0 +1,34 @@ +// This tests that attempting to perform ModuleDeclarationInstantation a +// second time after a failure still fails. (It no longer stores and rethrows +// the same error; the spec changed in that regard and the implementation was +// updated in bug 1420420). +// +// The attempts fails becuase module 'a' is not available. +// +// This test exercises the path where the previously instantiated module is +// re-instantiated directly. + +let b = registerModule('b', parseModule("export var b = 3; export var c = 4;")); +let c = registerModule('c', parseModule("export * from 'a'; export * from 'b';")); + +let e1; +let threw = false; +try { + c.declarationInstantiation(); +} catch (exc) { + threw = true; + e1 = exc; +} +assertEq(threw, true); +assertEq(typeof e1 === "undefined", false); + +threw = false; +let e2; +try { + c.declarationInstantiation(); +} catch (exc) { + threw = true; + e2 = exc; +} +assertEq(threw, true); +assertEq(e1.toString(), e2.toString()); diff --git a/js/src/jit-test/tests/modules/bug-1284486.js b/js/src/jit-test/tests/modules/bug-1284486.js new file mode 100644 index 0000000000..7489b5eb56 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1284486.js @@ -0,0 +1,33 @@ +// This tests that module instantiation can succeed when executed a second +// time after a failure. +// +// The first attempt fails becuase module 'a' is not available. The second +// attempt succeeds as 'a' is now available. +// +// This test exercises the path where the previously instantiated module is +// encountered as an import. + +let b = registerModule('b', parseModule("export var b = 3; export var c = 4;")); +let c = registerModule('c', parseModule("export * from 'a'; export * from 'b';")); + +let e1; +let threw = false; +try { + c.declarationInstantiation(); +} catch (exc) { + threw = true; + e1 = exc; +} +assertEq(threw, true); +assertEq(typeof e1 === "undefined", false); + +let a = registerModule('a', parseModule("export var a = 1; export var b = 2;")); +let d = registerModule('d', parseModule("import { a } from 'c'; a;")); + +threw = false; +try { + d.declarationInstantiation(); +} catch (exc) { + threw = true; +} +assertEq(threw, false); diff --git a/js/src/jit-test/tests/modules/bug-1287406.js b/js/src/jit-test/tests/modules/bug-1287406.js new file mode 100644 index 0000000000..8eef163886 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1287406.js @@ -0,0 +1 @@ +parseModule("export default () => 1"); diff --git a/js/src/jit-test/tests/modules/bug-1287410.js b/js/src/jit-test/tests/modules/bug-1287410.js new file mode 100644 index 0000000000..14df1b14e4 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1287410.js @@ -0,0 +1,17 @@ +// |jit-test| error: InternalError + +let a = registerModule('a', parseModule("export var a = 1; export var b = 2;")); +let b = registerModule('b', parseModule("export var b = 3; export var c = 4;")); +let c = registerModule('c', parseModule("export * from 'a'; export * from 'b';")); +c.declarationInstantiation(); + +// Module 'a' is replaced with another module that has not been instantiated. +// This should not happen and would be a bug in the module loader. +a = registerModule('a', parseModule("export var a = 1; export var b = 2;")); + +let d = registerModule('d', parseModule("import { a } from 'c'; a;")); + +// Attempting to instantiate 'd' throws an error because depdency 'a' of +// instantiated module 'c' is not instantiated. +d.declarationInstantiation(); +d.evaluation(); diff --git a/js/src/jit-test/tests/modules/bug-1320993.js b/js/src/jit-test/tests/modules/bug-1320993.js new file mode 100644 index 0000000000..bece5731a3 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1320993.js @@ -0,0 +1,2 @@ +parseModule("export default (class {})"); +parseModule("export default (class A {})"); diff --git a/js/src/jit-test/tests/modules/bug-1372258.js b/js/src/jit-test/tests/modules/bug-1372258.js new file mode 100644 index 0000000000..2764d38075 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1372258.js @@ -0,0 +1,25 @@ +// |jit-test| skip-if: helperThreadCount() === 0 + +// Overwrite built-in parseModule with off-thread module parser. +function parseModule(source) { + offThreadCompileModule(source); + return finishOffThreadModule(); +} + +// Test case derived from: js/src/jit-test/tests/modules/many-imports.js + +// Test importing an import many times. + +const count = 1024; + +let a = registerModule('a', parseModule("export let a = 1;")); + +let s = ""; +for (let i = 0; i < count; i++) { + s += "import { a as i" + i + " } from 'a';\n"; + s += "assertEq(i" + i + ", 1);\n"; +} +let b = registerModule('b', parseModule(s)); + +b.declarationInstantiation(); +b.evaluation(); diff --git a/js/src/jit-test/tests/modules/bug-1402535.js b/js/src/jit-test/tests/modules/bug-1402535.js new file mode 100644 index 0000000000..1d689f2aa4 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1402535.js @@ -0,0 +1,6 @@ +// |jit-test| skip-if: !('stackTest' in this) + +stackTest(function() { + let m = parseModule(``); + m.declarationInstantiation(); +}); diff --git a/js/src/jit-test/tests/modules/bug-1402649.js b/js/src/jit-test/tests/modules/bug-1402649.js new file mode 100644 index 0000000000..cae48d3558 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1402649.js @@ -0,0 +1,14 @@ +// |jit-test| skip-if: !('oomTest' in this) + +loadFile(` +function parseAndEvaluate(source) { + let m = parseModule(source); + m.declarationInstantiation(); +} +parseAndEvaluate("async function a() { await 2 + 3; }") +`); +function loadFile(lfVarx) { + oomTest(function() { + eval(lfVarx); + }); +} diff --git a/js/src/jit-test/tests/modules/bug-1406452.js b/js/src/jit-test/tests/modules/bug-1406452.js new file mode 100644 index 0000000000..7b8325aad7 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1406452.js @@ -0,0 +1,5 @@ +// |jit-test| error: Error +let m = parseModule(`for (var x of iterator) {}`); +m.declarationInstantiation(); +try { m.evaluation(); } catch (e) {} +getModuleEnvironmentValue(m, "r"); diff --git a/js/src/jit-test/tests/modules/bug-1420420-2.js b/js/src/jit-test/tests/modules/bug-1420420-2.js new file mode 100644 index 0000000000..e67882300b --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1420420-2.js @@ -0,0 +1,17 @@ +// Test re-instantiation module after failure. + +load(libdir + "asserts.js"); + +registerModule("good", parseModule(`export let x`)); + +registerModule("y1", parseModule(`export let y`)); +registerModule("y2", parseModule(`export let y`)); +registerModule("bad", parseModule(`export* from "y1"; export* from "y2";`)); + +registerModule("a", parseModule(`import* as ns from "good"; import {y} from "bad";`)); + +let b = registerModule("b", parseModule(`import "a";`)); +let c = registerModule("c", parseModule(`import "a";`)); + +assertThrowsInstanceOf(() => b.declarationInstantiation(), SyntaxError); +assertThrowsInstanceOf(() => c.declarationInstantiation(), SyntaxError); diff --git a/js/src/jit-test/tests/modules/bug-1420420-3.js b/js/src/jit-test/tests/modules/bug-1420420-3.js new file mode 100644 index 0000000000..bc0c5fdaca --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1420420-3.js @@ -0,0 +1,7 @@ +// |jit-test| skip-if: !('stackTest' in this) + +let a = parseModule(`throw new Error`); +a.declarationInstantiation(); +stackTest(function() { + a.evaluation(); +}); diff --git a/js/src/jit-test/tests/modules/bug-1420420-4.js b/js/src/jit-test/tests/modules/bug-1420420-4.js new file mode 100644 index 0000000000..91f1784c1d --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1420420-4.js @@ -0,0 +1,18 @@ +load(libdir + "asserts.js"); + +registerModule("a", parseModule(`throw undefined`)); + +let b = registerModule("b", parseModule(`import "a";`)); +let c = registerModule("c", parseModule(`import "a";`)); + +b.declarationInstantiation(); +c.declarationInstantiation(); + +(async () => { + let count = 0; + try { await b.evaluation() } catch (e) { count++; } + try { await c.evaluation() } catch (e) { count++; } + assertEq(count, 2); +})(); + +drainJobQueue(); diff --git a/js/src/jit-test/tests/modules/bug-1420420.js b/js/src/jit-test/tests/modules/bug-1420420.js new file mode 100644 index 0000000000..3764d71a7a --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1420420.js @@ -0,0 +1,17 @@ +// Test re-instantiation module after failure. + +load(libdir + "asserts.js"); + +registerModule("good", parseModule(`export let x`)); + +registerModule("y1", parseModule(`export let y`)); +registerModule("y2", parseModule(`export let y`)); +registerModule("bad", parseModule(`export* from "y1"; export* from "y2";`)); + +registerModule("a", parseModule(`import {x} from "good"; import {y} from "bad";`)); + +let b = registerModule("b", parseModule(`import "a";`)); +let c = registerModule("c", parseModule(`import "a";`)); + +assertThrowsInstanceOf(() => b.declarationInstantiation(), SyntaxError); +assertThrowsInstanceOf(() => c.declarationInstantiation(), SyntaxError); diff --git a/js/src/jit-test/tests/modules/bug-1435327.js b/js/src/jit-test/tests/modules/bug-1435327.js new file mode 100644 index 0000000000..356ccab31e --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1435327.js @@ -0,0 +1,27 @@ +// |jit-test| skip-if: !('oomTest' in this) + +lfLogBuffer = ` + let c = registerModule('c', parseModule("")); + let d = registerModule('d', parseModule("import { a } from 'c'; a;")); + d.declarationInstantiation(); +`; +lfLogBuffer = lfLogBuffer.split('\n'); +var lfCodeBuffer = ""; +while (true) { + var line = lfLogBuffer.shift(); + if (line == null) { + break; + } else { + lfCodeBuffer += line + "\n"; + } +} +if (lfCodeBuffer) loadFile(lfCodeBuffer); +function loadFile(lfVarx) { + try { + oomTest(function() { + let m = parseModule(lfVarx); + m.declarationInstantiation(); + m.evaluation(); + }); + } catch (lfVare) {} +} diff --git a/js/src/jit-test/tests/modules/bug-1439416-2.js b/js/src/jit-test/tests/modules/bug-1439416-2.js new file mode 100644 index 0000000000..b9202c112c --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1439416-2.js @@ -0,0 +1,10 @@ +function parseAsModule(source) { + return Reflect.parse(source, { + target: "module", + line: 0x7FFFFFFF + 1, + }); +} +parseAsModule(` + import {a} from ""; + export {a}; +`); diff --git a/js/src/jit-test/tests/modules/bug-1439416.js b/js/src/jit-test/tests/modules/bug-1439416.js new file mode 100644 index 0000000000..9dfe55b49a --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1439416.js @@ -0,0 +1,10 @@ +// Test that zero-based line numbers supplied by Reflect.parse don't cause +// assertions. + +function parseAsModule(source) { + return Reflect.parse(source, { + target: "module", + line: 0 + }); +} +parseAsModule("import d from 'a'"); diff --git a/js/src/jit-test/tests/modules/bug-1443555.js b/js/src/jit-test/tests/modules/bug-1443555.js new file mode 100644 index 0000000000..cbb1c5f5dd --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1443555.js @@ -0,0 +1,29 @@ +// |jit-test| error: TypeError + +"use strict"; + +setJitCompilerOption("baseline.warmup.trigger", 0); + +let mainSrc = ` +import A from "A"; + +const a = A; + +function requestAnimationFrame(f) { Promise.resolve().then(f); }; + +requestAnimationFrame(loopy); +a = 2; +function loopy() { + A; +} +`; + +let ASrc = ` +export default 1; +`; + +registerModule('A', parseModule(ASrc)); + +let m = parseModule(mainSrc); +m.declarationInstantiation() +m.evaluation(); diff --git a/js/src/jit-test/tests/modules/bug-1462286.js b/js/src/jit-test/tests/modules/bug-1462286.js new file mode 100644 index 0000000000..312d2f34b0 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1462286.js @@ -0,0 +1,8 @@ +let a = registerModule('a', parseModule(` + export var { ... get } = { x: "foo" }; +`)); + +let m = parseModule("import { get } from 'a'; export { get };"); +m.declarationInstantiation(); +m.evaluation() +assertEq(getModuleEnvironmentValue(m, "get").x, "foo"); diff --git a/js/src/jit-test/tests/modules/bug-1462326.js b/js/src/jit-test/tests/modules/bug-1462326.js new file mode 100644 index 0000000000..1c43f160be --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1462326.js @@ -0,0 +1,6 @@ +// |jit-test| error: Error + +let m = parseModule(` + import A from "A"; +`); +m.declarationInstantiation(); diff --git a/js/src/jit-test/tests/modules/bug-1466487.js b/js/src/jit-test/tests/modules/bug-1466487.js new file mode 100644 index 0000000000..f9c25fa4d3 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1466487.js @@ -0,0 +1,7 @@ +// |jit-test| skip-if: helperThreadCount() === 0 + +evalInWorker(` + let m = parseModule("import.meta;"); + m.declarationInstantiation(); + m.evaluation(); +`); diff --git a/js/src/jit-test/tests/modules/bug-1476921.js b/js/src/jit-test/tests/modules/bug-1476921.js new file mode 100644 index 0000000000..ce355078b0 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1476921.js @@ -0,0 +1,31 @@ +// |jit-test| --enable-top-level-await; +"use strict"; + +load(libdir + "asserts.js"); + +class UniqueError extends Error {} + +let a = registerModule('a', parseModule(` + throw new UniqueError(); +`)); + +let b = registerModule('b', parseModule(` + import * as ns0 from "a"; +`)); + +a.declarationInstantiation(); +a.evaluation() + .then(r => { + // We should not reach here, as we expect an error to be thrown. + assertEq(false, true); + }) + .catch(e => assertEq(e instanceof UniqueError, true)); +b.declarationInstantiation(); +b.evaluation() + .then(r => { + // We should not reach here, as we expect an error to be thrown. + assertEq(false, true); + }) + .catch(e => assertEq(e instanceof UniqueError, true)); + +drainJobQueue(); diff --git a/js/src/jit-test/tests/modules/bug-1498980.js b/js/src/jit-test/tests/modules/bug-1498980.js new file mode 100644 index 0000000000..753b174deb --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1498980.js @@ -0,0 +1,29 @@ +// |jit-test| --enable-top-level-await; +dbgGlobal = newGlobal({newCompartment: true}); +dbg = new dbgGlobal.Debugger; +dbg.addDebuggee(this); + +function f() { + dbg.getNewestFrame().older.eval(""); +} + +function execModule(source) { + m = parseModule(source); + m.declarationInstantiation(); + return m.evaluation(); +} + +execModule("f();").then(() => { + gc(); + + execModule("throw 'foo'") + .then(r => { + // We should not reach here. + assertEq(false, true); + }) + .catch(e => { + assertEq(e, 'foo'); + }); +}) + +drainJobQueue(); diff --git a/js/src/jit-test/tests/modules/bug-1501154.js b/js/src/jit-test/tests/modules/bug-1501154.js new file mode 100644 index 0000000000..48a506db2b --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1501154.js @@ -0,0 +1,25 @@ +// Test using an empty string as a module specifier fails. +let result = null; +let error = null; +let promise = import(""); +promise.then((ns) => { + result = ns; +}).catch((e) => { + error = e; +}); + +drainJobQueue(); +assertEq(result, null); +assertEq(error instanceof Error, true); + +// Test reading a directory as a file fails. +result = null; +error = null; +try { + result = os.file.readFile("."); +} catch (e) { + error = e; +} + +assertEq(result, null); +assertEq(error instanceof Error, true); diff --git a/js/src/jit-test/tests/modules/bug-1501157.js b/js/src/jit-test/tests/modules/bug-1501157.js new file mode 100644 index 0000000000..14885e9fde --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1501157.js @@ -0,0 +1,2 @@ +// |jit-test| skip-if: helperThreadCount() === 0 +offThreadCompileScript('import("")', {}); diff --git a/js/src/jit-test/tests/modules/bug-1502669.js b/js/src/jit-test/tests/modules/bug-1502669.js new file mode 100644 index 0000000000..4ac6e262cf --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1502669.js @@ -0,0 +1,5 @@ +// |jit-test| error: ReferenceError +var g = newGlobal({newCompartment: true}); +g.parent = this; +g.eval("new Debugger(parent).onExceptionUnwind = function () { hits++; };"); +import('')(); diff --git a/js/src/jit-test/tests/modules/bug-1503009.js b/js/src/jit-test/tests/modules/bug-1503009.js new file mode 100644 index 0000000000..f8dd8d384b --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1503009.js @@ -0,0 +1 @@ +new Function("if (0) import('')")(); diff --git a/js/src/jit-test/tests/modules/bug-1510598.js b/js/src/jit-test/tests/modules/bug-1510598.js new file mode 100644 index 0000000000..0701474f1a --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1510598.js @@ -0,0 +1,9 @@ +g = newGlobal({newCompartment: true}); +g.parent = this; +g.eval(`new Debugger(parent).onExceptionUnwind = () => null;`); +let exc = "fail"; +// Module evaluation throws so we fire the onExceptionUnwind hook. +import("javascript: throw 'foo'").catch(e => { exc = e; }); +drainJobQueue(); +assertEq(exc, undefined); + diff --git a/js/src/jit-test/tests/modules/bug-1519140.js b/js/src/jit-test/tests/modules/bug-1519140.js new file mode 100644 index 0000000000..29871bd1a4 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1519140.js @@ -0,0 +1,3 @@ +// |jit-test| --more-compartments; +fullcompartmentchecks(true); +newGlobal().eval(`import("javascript:")`).catch(() => {}); diff --git a/js/src/jit-test/tests/modules/bug-1604792.js b/js/src/jit-test/tests/modules/bug-1604792.js new file mode 100644 index 0000000000..ce0ef2b750 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1604792.js @@ -0,0 +1,7 @@ +var lfLogBuffer = ` + eval("function f(){}; f();"); +`; + +let lfMod = parseModule(lfLogBuffer); +lfMod.declarationInstantiation(); +lfMod.evaluation(); diff --git a/js/src/jit-test/tests/modules/bug-1657066.js b/js/src/jit-test/tests/modules/bug-1657066.js new file mode 100644 index 0000000000..22c828ca68 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1657066.js @@ -0,0 +1,3 @@ +let g = newGlobal({newCompartment: true}); +new Debugger(g).onExceptionUnwind = () => null; +g.eval(`import("javascript: throw 1")`).catch(() => 0); diff --git a/js/src/jit-test/tests/modules/bug-1680878-a.js b/js/src/jit-test/tests/modules/bug-1680878-a.js new file mode 100644 index 0000000000..e86b67d261 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1680878-a.js @@ -0,0 +1,8 @@ +// |jit-test| error: SyntaxError +// Remove once Top-level await is enabled by default: Bug #1681046 + +r = parseModule(` + for await (var x of this) {} +`); +r.declarationInstantiation(); +r.evaluation(); diff --git a/js/src/jit-test/tests/modules/bug-1680878.js b/js/src/jit-test/tests/modules/bug-1680878.js new file mode 100644 index 0000000000..47dcf4736f --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1680878.js @@ -0,0 +1,7 @@ +// |jit-test| --enable-top-level-await; error: TypeError + +r = parseModule(` + for await (var x of this) {} +`); +r.declarationInstantiation(); +r.evaluation(); diff --git a/js/src/jit-test/tests/modules/bug-1681256.js b/js/src/jit-test/tests/modules/bug-1681256.js new file mode 100644 index 0000000000..577060543f --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1681256.js @@ -0,0 +1,20 @@ +// |jit-test| --enable-top-level-await;--more-compartments; +let lfCode = ` + var g = newGlobal(); + g.debuggeeGlobal = this; + g.eval("(" + function () { + dbg = new Debugger(debuggeeGlobal); + dbg.onExceptionUnwind = function (frame, exc) {}; + } + ")();"); +`; +loadFile(lfCode); +// use "await" so the module is marked as TLA +loadFile(lfCode + " await ''"); +async function loadFile(lfVarx) { + try { + try { evaluate(lfVarx); } catch(exc) {} + let lfMod = parseModule(lfVarx); + lfMod.declarationInstantiation(); + await lfMod.evaluation(); + } catch (lfVare) {} +} diff --git a/js/src/jit-test/tests/modules/bug1105608.js b/js/src/jit-test/tests/modules/bug1105608.js new file mode 100644 index 0000000000..98e6aded08 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug1105608.js @@ -0,0 +1,9 @@ +// export-from should throw SyntaxError until it's implemented. + +var caught = false; +try { + eval("export { a } from 'b';"); +} catch (e) { + caught = true; +} +assertEq(caught, true); diff --git a/js/src/jit-test/tests/modules/bug1169850.js b/js/src/jit-test/tests/modules/bug1169850.js new file mode 100644 index 0000000000..3f6fbeef93 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug1169850.js @@ -0,0 +1,9 @@ +// export-default should throw SyntaxError until it's implemented. + +var caught = false; +try { + eval("export default 1;"); +} catch (e) { + caught = true; +} +assertEq(caught, true); diff --git a/js/src/jit-test/tests/modules/bug1198673.js b/js/src/jit-test/tests/modules/bug1198673.js new file mode 100644 index 0000000000..509adb8e59 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug1198673.js @@ -0,0 +1,2 @@ +// |jit-test| error: expected string to compile +parseModule((1)); diff --git a/js/src/jit-test/tests/modules/bug1204857.js b/js/src/jit-test/tests/modules/bug1204857.js new file mode 100644 index 0000000000..451da2e286 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug1204857.js @@ -0,0 +1,2 @@ +// |jit-test| error: SyntaxError: unexpected garbage after module +parseModule(("}")); diff --git a/js/src/jit-test/tests/modules/bug1210391.js b/js/src/jit-test/tests/modules/bug1210391.js new file mode 100644 index 0000000000..e9006bf16f --- /dev/null +++ b/js/src/jit-test/tests/modules/bug1210391.js @@ -0,0 +1,7 @@ +let a = registerModule('a', parseModule("export var a = 1; export var b = 2;")); +let b = registerModule('b', parseModule("export var b = 3; export var c = 4;")); +let c = registerModule('c', parseModule("export * from 'a'; export * from 'b';")); +let d = registerModule('d', parseModule("import { a } from 'c'; a;")); +d.declarationInstantiation(); +d.evaluation(); + diff --git a/js/src/jit-test/tests/modules/bug1394492.js b/js/src/jit-test/tests/modules/bug1394492.js new file mode 100644 index 0000000000..a0e5d2ac39 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug1394492.js @@ -0,0 +1,6 @@ +// |jit-test| error: NaN +let m = parseModule(` + throw i => { return 5; }, m-1; +`); +m.declarationInstantiation(); +m.evaluation(); diff --git a/js/src/jit-test/tests/modules/bug1394493.js b/js/src/jit-test/tests/modules/bug1394493.js new file mode 100644 index 0000000000..1fc1899602 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug1394493.js @@ -0,0 +1,4 @@ +// |jit-test| error: ReferenceError + +let m = parseModule("export let r = y; y = 4;"); +getModuleEnvironmentValue(m, "r").toString() diff --git a/js/src/jit-test/tests/modules/bug1429031.js b/js/src/jit-test/tests/modules/bug1429031.js new file mode 100644 index 0000000000..95e0d4e375 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug1429031.js @@ -0,0 +1,16 @@ +// |jit-test| error: ReferenceError + +assertEq = function(a, b) {} +evaluate(` +let a = registerModule('a', parseModule( + 'export var a = 1;' +)); +let b = registerModule('b', parseModule( + \`import * as ns from 'a'; + export var x = ns.a + ns.b;\` +)); +b.declarationInstantiation(); +let ns = getModuleEnvironmentValue(b, "ns"); +assertEq(ns.a, 1); +while ( t.ArrayType() ) 1; +`); diff --git a/js/src/jit-test/tests/modules/bug1449153.js b/js/src/jit-test/tests/modules/bug1449153.js new file mode 100644 index 0000000000..1e2c3b0d99 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug1449153.js @@ -0,0 +1,33 @@ +// Test performing GetModuleNamespace on an errored module. + +class MyError {} + +async function assertThrowsMyError(f) +{ + let caught = false; + try { + await f(); + } catch (e) { + caught = true; + assertEq(e.constructor, MyError); + } + assertEq(caught, true); +} + +registerModule("a", parseModule(` + throw new MyError(); +`)); + +let c = registerModule("c", parseModule(` + import "a"; +`)); +c.declarationInstantiation(); +assertThrowsMyError(() => c.evaluation()); + +let b = registerModule('b', parseModule(` + import * as ns0 from 'a' +`)); +b.declarationInstantiation(); +assertThrowsMyError(() => b.evaluation(b)); + +drainJobQueue(); diff --git a/js/src/jit-test/tests/modules/bug1485698.js b/js/src/jit-test/tests/modules/bug1485698.js new file mode 100644 index 0000000000..7c03b402cf --- /dev/null +++ b/js/src/jit-test/tests/modules/bug1485698.js @@ -0,0 +1,9 @@ +let m = parseModule(` + function f(x,y,z) { + delete arguments[2]; + import.meta[2] + } + f(1,2,3) +`); +m.declarationInstantiation(); +m.evaluation(); diff --git a/js/src/jit-test/tests/modules/bug1584034.js b/js/src/jit-test/tests/modules/bug1584034.js new file mode 100644 index 0000000000..65597effc4 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug1584034.js @@ -0,0 +1 @@ +import('./asdf.js').then(() => {}, inJit) diff --git a/js/src/jit-test/tests/modules/bug1584309.js b/js/src/jit-test/tests/modules/bug1584309.js new file mode 100644 index 0000000000..3e5906ea64 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug1584309.js @@ -0,0 +1 @@ +import('./asdf.js').then(() => {}, inIon) diff --git a/js/src/jit-test/tests/modules/bug1586599.js b/js/src/jit-test/tests/modules/bug1586599.js new file mode 100644 index 0000000000..a38f53037f --- /dev/null +++ b/js/src/jit-test/tests/modules/bug1586599.js @@ -0,0 +1,30 @@ +let m1 = parseModule(` +function x() { + return 1; +} +function y() { + x = function() { return 2; }; +} +export { x, y }; +`); +m1.declarationInstantiation(); +m1.evaluation(); + +registerModule('m1', m1); + +let m2 = parseModule(` +import {x, y} from "m1"; + +function test(expected) { + for (var i = 0; i < 2000; i++) { + if (i > 1900) { + assertEq(x(), expected); + } + } +} +test(1); +y(); +test(2); +`); +m2.declarationInstantiation(); +m2.evaluation(); diff --git a/js/src/jit-test/tests/modules/bug1670236.js b/js/src/jit-test/tests/modules/bug1670236.js new file mode 100644 index 0000000000..35192c2b58 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug1670236.js @@ -0,0 +1,6 @@ +// |jit-test| skip-if: !('oomTest' in this) +o0=r=/x/; +this.toString=(function() { + evaluate("",({ element:o0 })); +}) +oomTest(String.prototype.charCodeAt,{ keepFailing:true }) diff --git a/js/src/jit-test/tests/modules/bug1685992.js b/js/src/jit-test/tests/modules/bug1685992.js new file mode 100644 index 0000000000..ea709965fd --- /dev/null +++ b/js/src/jit-test/tests/modules/bug1685992.js @@ -0,0 +1,12 @@ +// |jit-test| --ion-offthread-compile=off; --enable-private-methods; skip-if: !('oomTest' in this) + +function oomModule(lfMod) { + oomTest(function () { + parseModule(lfMod); + }); +} +oomModule(` + class B50 { + #priv() {} + } +`)
\ No newline at end of file diff --git a/js/src/jit-test/tests/modules/cyclic-function-import.js b/js/src/jit-test/tests/modules/cyclic-function-import.js new file mode 100644 index 0000000000..a7d61e9bdf --- /dev/null +++ b/js/src/jit-test/tests/modules/cyclic-function-import.js @@ -0,0 +1,7 @@ +// |jit-test| module + +import { isOdd } from "isOdd.js" +import { isEven } from "isEven.js" + +assertEq(isEven(4), true); +assertEq(isOdd(5), true); diff --git a/js/src/jit-test/tests/modules/cyclic-import.js b/js/src/jit-test/tests/modules/cyclic-import.js new file mode 100644 index 0000000000..3ca7bf123e --- /dev/null +++ b/js/src/jit-test/tests/modules/cyclic-import.js @@ -0,0 +1,3 @@ +// |jit-test| module; error: ReferenceError + +import { a } from "cyclicImport1.js"; diff --git a/js/src/jit-test/tests/modules/debugger-frames.js b/js/src/jit-test/tests/modules/debugger-frames.js new file mode 100644 index 0000000000..5619df5e65 --- /dev/null +++ b/js/src/jit-test/tests/modules/debugger-frames.js @@ -0,0 +1,83 @@ +// Test debugger access to frames and environments work as expected inside a module. + +load(libdir + "asserts.js"); + +function assertArrayEq(actual, expected) +{ + var eq = actual.length == expected.length; + if (eq) { + for (var i = 0; i < actual.length; i++) { + if (actual[i] !== expected[i]) { + eq = false; + break; + } + } + } + if (!eq) { + print("Assertion failed: got " + JSON.stringify(actual) + + ", expected " + JSON.stringify(expected)); + quit(3); + } +} + +var g2 = newGlobal({newCompartment: true}); + +var dbg = Debugger(g2); +dbg.onDebuggerStatement = function (frame) { + // The current frame is a module frame. + assertEq(frame.type, 'module'); + assertEq(frame.this, undefined); + + // The frame's environement is a module environment. + let env = frame.environment; + assertEq(env.type, 'declarative'); + assertEq(env.calleeScript, null); + + // Top level module definitions and imports are visible. + assertArrayEq(env.names().sort(), ['a', 'b', 'c', 'x', 'y', 'z']); + assertArrayEq(['a', 'b', 'c'].map(env.getVariable, env), [1, 2, 3]); + assertArrayEq(['x', 'y', 'z'].map(env.getVariable, env), [4, 5, 6]); + + // Cannot set imports or const bindings. + assertThrowsInstanceOf(() => env.setVariable('a', 10), TypeError); + assertThrowsInstanceOf(() => env.setVariable('b', 11), TypeError); + assertThrowsInstanceOf(() => env.setVariable('c', 12), TypeError); + env.setVariable('x', 7); + env.setVariable('y', 8); + assertThrowsInstanceOf(() => env.setVariable('z', 9), TypeError); + assertArrayEq(['a', 'b', 'c'].map(env.getVariable, env), [1, 2, 3]); + assertArrayEq(['x', 'y', 'z'].map(env.getVariable, env), [7, 8, 6]); + + // The global lexical is the next thing after the module on the scope chain, + // followed by the global. + assertEq(env.parent.type, 'declarative'); + assertEq(env.parent.parent.type, 'object'); + assertEq(env.parent.parent.parent, null); +}; + +f = g2.eval( +` + // Set up a module to import from. + a = registerModule('a', parseModule( + \` + export var a = 1; + export let b = 2; + export const c = 3; + export function f(x) { return x + 1; } + \`)); + a.declarationInstantiation(); + a.evaluation(); + + let m = parseModule( + \` + import { a, b, c } from 'a'; + var x = 4; + let y = 5; + const z = 6; + + eval(""); + debugger; + \`); + m.declarationInstantiation(); + m.evaluation(); +`); diff --git a/js/src/jit-test/tests/modules/debugger-vars-function.js b/js/src/jit-test/tests/modules/debugger-vars-function.js new file mode 100644 index 0000000000..215953295c --- /dev/null +++ b/js/src/jit-test/tests/modules/debugger-vars-function.js @@ -0,0 +1,30 @@ +// Test debugger access to aliased and unaliased bindings work correctly. + +load(libdir + "asserts.js"); + +var g = newGlobal({newCompartment: true}); +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (frame) { + let env = frame.environment.parent; + assertEq(env.getVariable('a'), 1); + assertEq(env.getVariable('b'), 2); + assertEq(env.getVariable('c'), 3); + assertEq(env.getVariable('d'), 4); + assertEq(env.getVariable('e'), 5); +}; + +g.eval( +` + let m = parseModule( + \` + var a = 1; + let b = 2; + export var c = 3; + export let d = 4; + let e = 5; + function f() { debugger; return e; } + \`); + m.declarationInstantiation(); + m.evaluation(); +`); + diff --git a/js/src/jit-test/tests/modules/debugger-vars-toplevel.js b/js/src/jit-test/tests/modules/debugger-vars-toplevel.js new file mode 100644 index 0000000000..5d657ca5a1 --- /dev/null +++ b/js/src/jit-test/tests/modules/debugger-vars-toplevel.js @@ -0,0 +1,31 @@ +// Test debugger access to aliased and unaliased bindings work correctly. + +load(libdir + "asserts.js"); + +var g = newGlobal({newCompartment: true}); +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (frame) { + let env = frame.environment; + assertEq(env.getVariable('a'), 1); + assertEq(env.getVariable('b'), 2); + assertEq(env.getVariable('c'), 3); + assertEq(env.getVariable('d'), 4); + assertEq(env.getVariable('e'), 5); +}; + +g.eval( +` + let m = parseModule( + \` + var a = 1; + let b = 2; + export var c = 3; + export let d = 4; + let e = 5; + function f() { return e; } + debugger; + \`); + m.declarationInstantiation(); + m.evaluation(); +`); + diff --git a/js/src/jit-test/tests/modules/delete-import.js b/js/src/jit-test/tests/modules/delete-import.js new file mode 100644 index 0000000000..b76c028f6f --- /dev/null +++ b/js/src/jit-test/tests/modules/delete-import.js @@ -0,0 +1,3 @@ +// |jit-test| module; error: SyntaxError +import { a } from "module1.js"; +delete a; diff --git a/js/src/jit-test/tests/modules/delete-namespace-import.js b/js/src/jit-test/tests/modules/delete-namespace-import.js new file mode 100644 index 0000000000..4b5f2d1c9d --- /dev/null +++ b/js/src/jit-test/tests/modules/delete-namespace-import.js @@ -0,0 +1,3 @@ +// |jit-test| module; error: TypeError +import * as ns from "module1.js"; +delete ns.a; diff --git a/js/src/jit-test/tests/modules/delete-namespace.js b/js/src/jit-test/tests/modules/delete-namespace.js new file mode 100644 index 0000000000..ef23e2afcb --- /dev/null +++ b/js/src/jit-test/tests/modules/delete-namespace.js @@ -0,0 +1,3 @@ +// |jit-test| module; error: SyntaxError +import * as ns from "module1.js"; +delete ns; diff --git a/js/src/jit-test/tests/modules/duplicate-exports.js b/js/src/jit-test/tests/modules/duplicate-exports.js new file mode 100644 index 0000000000..7e45ac6f37 --- /dev/null +++ b/js/src/jit-test/tests/modules/duplicate-exports.js @@ -0,0 +1,32 @@ +// Test errors due to duplicate exports +load(libdir + "asserts.js"); + +function testSyntaxError(source) { + assertThrowsInstanceOf(function () { + parseModule(source); + }, SyntaxError); +} + +// SyntexError due to duplicate exports +testSyntaxError("export var v; export var v;"); +testSyntaxError("export var x, y, z; export var y;"); +testSyntaxError("export let v; var w; export {w as v};"); +testSyntaxError("export const v; var w; export {w as v};"); +testSyntaxError("export var v; let w; export {w as v};"); +testSyntaxError("export var v; const w; export {w as v};"); +testSyntaxError("export default 1; export default 2;"); +testSyntaxError("export default 1; export default function() {};"); +testSyntaxError("export default 1; export default function foo() {};"); +testSyntaxError("var v; export {v}; export {v};"); +testSyntaxError("var v, x; export {v}; export {x as v};"); +testSyntaxError("export default 1; export default export class { constructor() {} };"); +testSyntaxError("export default 1; export default export class foo { constructor() {} };"); + +// SyntaxError due to redeclared binding +testSyntaxError("export let v; export let v;"); +testSyntaxError("export let x, y, z; export let y;"); +testSyntaxError("export const v = 0; export const v = 0;"); +testSyntaxError("export const x = 0, y = 0, z = 0; export const y = 0;"); +testSyntaxError("export var v; export let v;"); +testSyntaxError("export var v; export const v = 0;"); +testSyntaxError("export let v; export const v;"); diff --git a/js/src/jit-test/tests/modules/duplicate-imports.js b/js/src/jit-test/tests/modules/duplicate-imports.js new file mode 100644 index 0000000000..fa87ba8ebd --- /dev/null +++ b/js/src/jit-test/tests/modules/duplicate-imports.js @@ -0,0 +1,27 @@ +// Test errors due to duplicate lexically declared names. + +load(libdir + "asserts.js"); + +function testNoError(source) { + parseModule(source); +} + +function testSyntaxError(source) { + assertThrowsInstanceOf(() => parseModule(source), SyntaxError); +} + +testNoError("import { a } from 'm';"); +testNoError("import { a as b } from 'm';"); +testNoError("import * as a from 'm';"); +testNoError("import a from 'm';"); + +testSyntaxError("import { a } from 'm'; let a = 1;"); +testSyntaxError("let a = 1; import { a } from 'm';"); +testSyntaxError("import { a } from 'm'; var a = 1;"); +testSyntaxError("var a = 1; import { a } from 'm';"); +testSyntaxError("import { a, b } from 'm'; const b = 1;"); +testSyntaxError("import { a } from 'm'; import { a } from 'm2';"); +testSyntaxError("import { a } from 'm'; import { b as a } from 'm2';"); +testSyntaxError("import { a } from 'm'; import * as a from 'm2';"); +testSyntaxError("import { a } from 'm'; import a from 'm2';"); + diff --git a/js/src/jit-test/tests/modules/dynamic-import-error.js b/js/src/jit-test/tests/modules/dynamic-import-error.js new file mode 100644 index 0000000000..98a6af75d0 --- /dev/null +++ b/js/src/jit-test/tests/modules/dynamic-import-error.js @@ -0,0 +1,14 @@ +// |jit-test| module + +let result = null; +let error = null; +let promise = import("nonexistent.js"); +promise.then((ns) => { + result = ns; +}).catch((e) => { + error = e; +}); + +drainJobQueue(); +assertEq(result, null); +assertEq(error instanceof Error, true); diff --git a/js/src/jit-test/tests/modules/dynamic-import-expression.js b/js/src/jit-test/tests/modules/dynamic-import-expression.js new file mode 100644 index 0000000000..9cee4dd6e1 --- /dev/null +++ b/js/src/jit-test/tests/modules/dynamic-import-expression.js @@ -0,0 +1,79 @@ +load(libdir + "match.js"); +load(libdir + "asserts.js"); + +var { Pattern, MatchError } = Match; + +program = (elts) => Pattern({ + type: "Program", + body: elts +}); +expressionStatement = (expression) => Pattern({ + type: "ExpressionStatement", + expression: expression +}); +assignmentExpression = (left, operator, right) => Pattern({ + type: "AssignmentExpression", + operator: operator, + left: left, + right: right +}); +ident = (name) => Pattern({ + type: "Identifier", + name: name +}); +importCall = (ident, singleArg) => Pattern({ + type: "CallImport", + ident: ident, + arg: singleArg +}); + +function parseAsClassicScript(source) +{ + return Reflect.parse(source); +} + +function parseAsModuleScript(source) +{ + return Reflect.parse(source, {target: "module"}); +} + +for (let parse of [parseAsModuleScript, parseAsClassicScript]) { + program([ + expressionStatement( + importCall( + ident("import"), + ident("foo") + ) + ) + ]).assert(parse("import(foo);")); + + program([ + expressionStatement( + assignmentExpression( + ident("x"), + "=", + importCall( + ident("import"), + ident("foo") + ) + ) + ) + ]).assert(parse("x = import(foo);")); +} + +function assertParseThrowsSyntaxError(source) +{ + assertThrowsInstanceOf(() => parseAsClassicScript(source), SyntaxError); + assertThrowsInstanceOf(() => parseAsModuleScript(source), SyntaxError); +} + +assertParseThrowsSyntaxError("import"); +assertParseThrowsSyntaxError("import("); +assertParseThrowsSyntaxError("import(1,"); +assertParseThrowsSyntaxError("import(1, 2"); +assertParseThrowsSyntaxError("import(1, 2)"); +assertParseThrowsSyntaxError("x = import"); +assertParseThrowsSyntaxError("x = import("); +assertParseThrowsSyntaxError("x = import(1,"); +assertParseThrowsSyntaxError("x = import(1, 2"); +assertParseThrowsSyntaxError("x = import(1, 2)"); diff --git a/js/src/jit-test/tests/modules/dynamic-import-ion.js b/js/src/jit-test/tests/modules/dynamic-import-ion.js new file mode 100644 index 0000000000..9c8f5e0e40 --- /dev/null +++ b/js/src/jit-test/tests/modules/dynamic-import-ion.js @@ -0,0 +1,16 @@ +// Even with --ion-eager, this needs to be run twice before it executes the +// ion-compiled version. +for (let i = 0; i < 2; i++) { + let result = null; + let error = null; + let promise = import("../../modules/module1.js"); + promise.then((ns) => { + result = ns; + }).catch((e) => { + error = e; + }); + + drainJobQueue(); + assertEq(error, null); + assertEq(result.a, 1); +} diff --git a/js/src/jit-test/tests/modules/dynamic-import-lazy.js b/js/src/jit-test/tests/modules/dynamic-import-lazy.js new file mode 100644 index 0000000000..ccbf90989d --- /dev/null +++ b/js/src/jit-test/tests/modules/dynamic-import-lazy.js @@ -0,0 +1,4 @@ +function lazyilyParsedFunction() +{ + return import("/module1.js"); +} diff --git a/js/src/jit-test/tests/modules/dynamic-import-module.js b/js/src/jit-test/tests/modules/dynamic-import-module.js new file mode 100644 index 0000000000..3c004258a3 --- /dev/null +++ b/js/src/jit-test/tests/modules/dynamic-import-module.js @@ -0,0 +1,39 @@ +// |jit-test| module + +function testImport(path, name, value) { + let result = null; + let error = null; + let promise = import(path); + promise.then((ns) => { + result = ns; + }).catch((e) => { + error = e; + }); + + drainJobQueue(); + assertEq(error, null); + assertEq(result[name], value); +} + +// Resolved via module load path. +testImport("module1.js", "a", 1); + +// Relative path resolved relative to this script. +testImport("../../modules/module1a.js", "a", 2); + +// Import inside function. +function f() { + testImport("../../modules/module2.js", "b", 2); +} +f(); + +// Import inside direct eval. +eval(`testImport("../../modules/module3.js", "c", 3)`); + +// Import inside indirect eval. +const indirect = eval; +const defineTestFunc = testImport.toString(); +indirect(defineTestFunc + `testImport("../../modules/module3.js");`); + +// Import inside dynamic function. +Function(defineTestFunc + `testImport("../../modules/module3.js");`)(); diff --git a/js/src/jit-test/tests/modules/dynamic-import-oom.js b/js/src/jit-test/tests/modules/dynamic-import-oom.js new file mode 100644 index 0000000000..9682c7560a --- /dev/null +++ b/js/src/jit-test/tests/modules/dynamic-import-oom.js @@ -0,0 +1,9 @@ +// |jit-test| skip-if: !('oomTest' in this); --ion-offthread-compile=off +// +// Note: without --ion-offthread-compile=off this test takes a long time and +// may timeout on some platforms. See bug 1507721. + +ignoreUnhandledRejections(); + +oomTest(() => import("module1.js")); +oomTest(() => import("cyclicImport1.js")); diff --git a/js/src/jit-test/tests/modules/dynamic-import-script.js b/js/src/jit-test/tests/modules/dynamic-import-script.js new file mode 100644 index 0000000000..e6986a42ec --- /dev/null +++ b/js/src/jit-test/tests/modules/dynamic-import-script.js @@ -0,0 +1,45 @@ +function testImport(path, name, value) { + let result = null; + let error = null; + let promise = import(path); + promise.then((ns) => { + result = ns; + }).catch((e) => { + error = e; + }); + + drainJobQueue(); + assertEq(error, null); + assertEq(result[name], value); +} + +// Resolved via module load path. +testImport("module1.js", "a", 1); + +// Relative path resolved relative to this script. +testImport("../../modules/module1a.js", "a", 2); + +// Import inside function. +function f() { + testImport("../../modules/module2.js", "b", 2); +} +f(); + +// Import inside eval. +eval(`testImport("../../modules/module3.js", "c", 3)`); + +// Import inside indirect eval. +const indirect = eval; +const defineTestFunc = testImport.toString(); +indirect(defineTestFunc + `testImport("../../modules/module3.js");`); + +// Import inside dynamic function. +Function(defineTestFunc + `testImport("../../modules/module3.js");`)(); + +// Import in eval in promise handler. +let ran = false; +Promise + .resolve(`import("../../modules/module3.js").then(() => { ran = true; })`) + .then(eval) +drainJobQueue(); +assertEq(ran, true); diff --git a/js/src/jit-test/tests/modules/eval-module-oom.js b/js/src/jit-test/tests/modules/eval-module-oom.js new file mode 100644 index 0000000000..cd693105e1 --- /dev/null +++ b/js/src/jit-test/tests/modules/eval-module-oom.js @@ -0,0 +1,23 @@ +// |jit-test| skip-if: !('oomTest' in this) + +// OOM tests for module parsing. + +const sa = +`export default 20; + export let a = 22; + export function f(x, y) { return x + y } +`; + +const sb = +`import x from "a"; + import { a as y } from "a"; + import * as ns from "a"; + ns.f(x, y); +`; + +oomTest(() => { + let a = registerModule('a', parseModule(sa)); + let b = registerModule('b', parseModule(sb)); + b.declarationInstantiation(); + assertEq(b.evaluation(), 42); +}); diff --git a/js/src/jit-test/tests/modules/export-circular-nonexisting-binding.js b/js/src/jit-test/tests/modules/export-circular-nonexisting-binding.js new file mode 100644 index 0000000000..387c7c369a --- /dev/null +++ b/js/src/jit-test/tests/modules/export-circular-nonexisting-binding.js @@ -0,0 +1,3 @@ +// |jit-test| module; error:SyntaxError + +import "export-circular-nonexisting-binding-1.js"; diff --git a/js/src/jit-test/tests/modules/export-declaration.js b/js/src/jit-test/tests/modules/export-declaration.js new file mode 100644 index 0000000000..dc49b82ee6 --- /dev/null +++ b/js/src/jit-test/tests/modules/export-declaration.js @@ -0,0 +1,480 @@ +load(libdir + "match.js"); +load(libdir + "asserts.js"); + +var { Pattern, MatchError } = Match; + +program = (elts) => Pattern({ + type: "Program", + body: elts +}) +exportDeclaration = (declaration, specifiers, source, isDefault) => Pattern({ + type: "ExportDeclaration", + declaration: declaration, + specifiers: specifiers, + source: source, + isDefault: isDefault +}); +exportSpecifier = (id, name) => Pattern({ + type: "ExportSpecifier", + id: id, + name: name +}); +exportBatchSpecifier = () => Pattern({ + type: "ExportBatchSpecifier" +}); +blockStatement = (body) => Pattern({ + type: "BlockStatement", + body: body +}); +functionDeclaration = (id, params, body) => Pattern({ + type: "FunctionDeclaration", + id: id, + params: params, + defaults: [], + body: body, + rest: null, + generator: false +}); +classDeclaration = (name) => Pattern({ + type: "ClassStatement", + id: name +}); +variableDeclaration = (decls) => Pattern({ + type: "VariableDeclaration", + kind: "var", + declarations: decls +}); +constDeclaration = (decls) => Pattern({ + type: "VariableDeclaration", + kind: "const", + declarations: decls +}); +letDeclaration = (decls) => Pattern({ + type: "VariableDeclaration", + kind: "let", + declarations: decls +}); +ident = (name) => Pattern({ + type: "Identifier", + name: name +}); +lit = (val) => Pattern({ + type: "Literal", + value: val +}); + +function parseAsModule(source) +{ + return Reflect.parse(source, {target: "module"}); +} + +program([ + exportDeclaration( + null, + [], + null, + false + ) +]).assert(parseAsModule("export {}")); + +program([ + letDeclaration([ + { + id: ident("a"), + init: lit(1) + } + ]), + exportDeclaration( + null, + [ + exportSpecifier( + ident("a"), + ident("a") + ) + ], + null, + false + ) +]).assert(parseAsModule("let a = 1; export { a }")); + +program([ + letDeclaration([ + { + id: ident("a"), + init: lit(1) + } + ]), + exportDeclaration( + null, + [ + exportSpecifier( + ident("a"), + ident("b") + ) + ], + null, + false + ) +]).assert(parseAsModule("let a = 1; export { a as b }")); + +program([ + letDeclaration([ + { + id: ident("as"), + init: lit(1) + } + ]), + exportDeclaration( + null, + [ + exportSpecifier( + ident("as"), + ident("as") + ) + ], + null, + false + ) +]).assert(parseAsModule("let as = 1; export { as as as }")); + +program([ + letDeclaration([ + { + id: ident("a"), + init: lit(1) + } + ]), + exportDeclaration( + null, + [ + exportSpecifier( + ident("a"), + ident("true") + ) + ], + null, + false + ) +]).assert(parseAsModule("let a = 1; export { a as true }")); + +program([ + letDeclaration([ + { + id: ident("a"), + init: lit(1) + }, + { + id: ident("b"), + init: lit(2) + } + ]), + exportDeclaration( + null, + [ + exportSpecifier( + ident("a"), + ident("a") + ), + exportSpecifier( + ident("b"), + ident("b") + ), + ], + null, + false + ) +]).assert(parseAsModule("let a = 1, b = 2; export { a, b }")); + +program([ + letDeclaration([ + { + id: ident("a"), + init: lit(1) + }, + { + id: ident("c"), + init: lit(2) + } + ]), + exportDeclaration( + null, + [ + exportSpecifier( + ident("a"), + ident("b") + ), + exportSpecifier( + ident("c"), + ident("d") + ), + ], + null, + false + ) +]).assert(parseAsModule("let a = 1, c = 2; export { a as b, c as d }")); + +program([ + exportDeclaration( + null, + [ + exportSpecifier( + ident("a"), + ident("a") + ) + ], + lit("b"), + false + ) +]).assert(parseAsModule("export { a } from 'b'")); + +program([ + exportDeclaration( + null, + [ + exportBatchSpecifier() + ], + lit("a"), + false + ) +]).assert(parseAsModule("export * from 'a'")); + +program([ + exportDeclaration( + functionDeclaration( + ident("f"), + [], + blockStatement([]) + ), + null, + null, + false + ) +]).assert(parseAsModule("export function f() {}")); + +program([ + exportDeclaration( + classDeclaration( + ident("Foo") + ), + null, + null, + false + ) +]).assert(parseAsModule("export class Foo { constructor() {} }")); + +program([ + exportDeclaration( + variableDeclaration([ + { + id: ident("a"), + init: lit(1) + }, { + id: ident("b"), + init: lit(2) + } + ]), + null, + null, + false + ) +]).assert(parseAsModule("export var a = 1, b = 2;")); + +program([ + exportDeclaration( + constDeclaration([ + { + id: ident("a"), + init: lit(1) + }, { + id: ident("b"), + init: lit(2) + } + ]), + null, + null, + false + ) +]).assert(parseAsModule("export const a = 1, b = 2;")); + +program([ + exportDeclaration( + letDeclaration([ + { + id: ident("a"), + init: lit(1) + }, { + id: ident("b"), + init: lit(2) + } + ]), + null, + null, + false + ) +]).assert(parseAsModule("export let a = 1, b = 2;")); + +program([ + exportDeclaration( + functionDeclaration( + ident("default"), + [], + blockStatement([]) + ), + null, + null, + true + ) +]).assert(parseAsModule("export default function() {}")); + +program([ + exportDeclaration( + functionDeclaration( + ident("foo"), + [], + blockStatement([]) + ), + null, + null, + true + ) +]).assert(parseAsModule("export default function foo() {}")); + +program([ + exportDeclaration( + classDeclaration( + ident("default") + ), + null, + null, + true + ) +]).assert(parseAsModule("export default class { constructor() {} }")); + +program([ + exportDeclaration( + classDeclaration( + ident("Foo") + ), + null, + null, + true + ) +]).assert(parseAsModule("export default class Foo { constructor() {} }")); + +program([ + exportDeclaration( + lit(1234), + null, + null, + true + ) +]).assert(parseAsModule("export default 1234")); + +assertThrowsInstanceOf(function () { + parseAsModule("export default 1234 5678"); +}, SyntaxError); + +var loc = parseAsModule("export { a as b } from 'c'", { + loc: true +}).body[0].loc; + +assertEq(loc.start.line, 1); +assertEq(loc.start.column, 0); +assertEq(loc.start.line, 1); +assertEq(loc.end.column, 26); + +assertThrowsInstanceOf(function () { + parseAsModule("function f() { export a }"); +}, SyntaxError); + +assertThrowsInstanceOf(function () { + parseAsModule("if (true) export a"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("export {"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("export {} from"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("export {,} from 'a'"); +}, SyntaxError); + +program([ + exportDeclaration( + null, + [ + exportSpecifier( + ident("true"), + ident("true") + ), + ], + lit("b"), + false + ) +]).assert(parseAsModule("export { true } from 'b'")); + +program([ + exportDeclaration( + null, + [ + exportSpecifier( + ident("true"), + ident("name") + ), + ], + lit("b"), + false + ) +]).assert(parseAsModule("export { true as name } from 'b'")); + +assertThrowsInstanceOf(function() { + parseAsModule("export { true }"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("export { true as name }"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("export { static }"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("export { static as name }"); +}, SyntaxError); + +assertThrowsInstanceOf(function () { + parseAsModule("export { name } from 'b' f();"); +}, SyntaxError); + +assertThrowsInstanceOf(function () { + parseAsModule("export *"); +}, SyntaxError); + +assertThrowsInstanceOf(function () { + parseAsModule("export * from 'b' f();"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("export {}\nfrom ()"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("function() {}"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("class() { constructor() {} }"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("export x"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("export foo = 5"); +}, SyntaxError); diff --git a/js/src/jit-test/tests/modules/export-destructuring.js b/js/src/jit-test/tests/modules/export-destructuring.js new file mode 100644 index 0000000000..115cec41a9 --- /dev/null +++ b/js/src/jit-test/tests/modules/export-destructuring.js @@ -0,0 +1,104 @@ +function assertArrayEq(value, expected) +{ + assertEq(value instanceof Array, true); + assertEq(value.length, expected.length); + for (let i = 0; i < value.length; i++) + assertEq(value[i], expected[i]); +} + +registerModule('a', parseModule(` + export const [] = []; + export const [a=0] = []; + export const [b] = [1]; + export const [c, d, e] = [2, 3, 4]; + export const [, f, g] = [5, 6, 7]; + export const [h,, i] = [8, 9, 10]; + export const [,, j] = [11, 12, 13]; + export const [...k] = [14, 15, 16]; + export const [l, ...m] = [17, 18, 19]; + export const [,, ...n] = [20, 21, 22]; +`)); + +m = parseModule(` + import * as a from 'a'; + + assertEq(a.a, 0); + assertEq(a.b, 1); + assertEq(a.c, 2); + assertEq(a.d, 3); + assertEq(a.e, 4); + assertEq(a.f, 6); + assertEq(a.g, 7); + assertEq(a.h, 8); + assertEq(a.i, 10); + assertEq(a.j, 13); + assertArrayEq(a.k, [14, 15, 16]); + assertEq(a.l, 17); + assertArrayEq(a.m, [18, 19]); + assertArrayEq(a.n, [22]); +`); + +m.declarationInstantiation(); +m.evaluation(); + +registerModule('o', parseModule(` + export const {} = {}; + export const {x: a=0} = {}; + export const {x: b=0} = {x: 1}; + export const {c, d, e} = {c: 2, d: 3, e: 4}; + export const {x: f} = {x: 5}; + export const {g} = {}; + export const {h=6} = {}; +`)); + +m = parseModule(` + import * as o from 'o'; + + assertEq(o.a, 0); + assertEq(o.b, 1); + assertEq(o.c, 2); + assertEq(o.d, 3); + assertEq(o.e, 4); + assertEq(o.f, 5); + assertEq(o.g, undefined); + assertEq(o.h, 6); +`); + +m.declarationInstantiation(); +m.evaluation(); + +registerModule('ao', parseModule(` + export const [{x: a}, {x: b}] = [{x: 1}, {x: 2}]; + export const [{c}, {d}] = [{c: 3}, {d: 4}]; + export const [,, {e}, ...f] = [5, 6, {e: 7}, 8, 9]; + + export const {x: [g, h, i]} = {x: [10, 11, 12]}; + + export const [[], [...j], [, k, l]] = [[], [13, 14], [15, 16, 17]]; + + export const {x: {m, n, o=20}, y: {z: p}} = {x: {m: 18, n: 19}, y: {z: 21}}; +`)); + +m = parseModule(` + import * as ao from 'ao'; + + assertEq(ao.a, 1); + assertEq(ao.b, 2); + assertEq(ao.c, 3); + assertEq(ao.d, 4); + assertEq(ao.e, 7); + assertArrayEq(ao.f, [8, 9]); + assertEq(ao.g, 10); + assertEq(ao.h, 11); + assertEq(ao.i, 12); + assertArrayEq(ao.j, [13, 14]); + assertEq(ao.k, 16); + assertEq(ao.l, 17); + assertEq(ao.m, 18); + assertEq(ao.n, 19); + assertEq(ao.o, 20); + assertEq(ao.p, 21); +`); + +m.declarationInstantiation(); +m.evaluation(); diff --git a/js/src/jit-test/tests/modules/export-entries.js b/js/src/jit-test/tests/modules/export-entries.js new file mode 100644 index 0000000000..b383d9e1e9 --- /dev/null +++ b/js/src/jit-test/tests/modules/export-entries.js @@ -0,0 +1,121 @@ +// Test localExportEntries property + +function testArrayContents(actual, expected) { + assertEq(actual.length, expected.length); + for (var i = 0; i < actual.length; i++) { + for (var property in expected[i]) { + assertEq(actual[i][property], expected[i][property]); + } + } +} + +function testLocalExportEntries(source, expected) { + var module = parseModule(source); + testArrayContents(module.localExportEntries, expected); +} + +testLocalExportEntries( + 'export var v;', + [{exportName: 'v', moduleRequest: null, importName: null, localName: 'v'}]); + +testLocalExportEntries( + 'export var v = 0;', + [{exportName: 'v', moduleRequest: null, importName: null, localName: 'v'}]); + +testLocalExportEntries( + 'export let x = 1;', + [{exportName: 'x', moduleRequest: null, importName: null, localName: 'x'}]); + +testLocalExportEntries( + 'export const x = 1;', + [{exportName: 'x', moduleRequest: null, importName: null, localName: 'x'}]); + +testLocalExportEntries( + 'export class foo { constructor() {} };', + [{exportName: 'foo', moduleRequest: null, importName: null, localName: 'foo'}]); + +testLocalExportEntries( + 'export default function f() {};', + [{exportName: 'default', moduleRequest: null, importName: null, localName: 'f'}]); + +testLocalExportEntries( + 'export default function() {};', + [{exportName: 'default', moduleRequest: null, importName: null, localName: 'default'}]); + +testLocalExportEntries( + 'export default 42;', + [{exportName: 'default', moduleRequest: null, importName: null, localName: 'default'}]); + +testLocalExportEntries( + 'let x = 1; export {x};', + [{exportName: 'x', moduleRequest: null, importName: null, localName: 'x'}]); + +testLocalExportEntries( + 'let v = 1; export {v as x};', + [{exportName: 'x', moduleRequest: null, importName: null, localName: 'v'}]); + +testLocalExportEntries( + 'export {x} from "mod";', + []); + +testLocalExportEntries( + 'export {v as x} from "mod";', + []); + +testLocalExportEntries( + 'export * from "mod";', + []); + +// Test indirectExportEntries property + +function testIndirectExportEntries(source, expected) { + var module = parseModule(source); + testArrayContents(module.indirectExportEntries, expected); +} + +testIndirectExportEntries( + 'export default function f() {};', + []); + +testIndirectExportEntries( + 'let x = 1; export {x};', + []); + +testIndirectExportEntries( + 'export {x} from "mod";', + [{exportName: 'x', moduleRequest: 'mod', importName: 'x', localName: null}]); + +testIndirectExportEntries( + 'export {v as x} from "mod";', + [{exportName: 'x', moduleRequest: 'mod', importName: 'v', localName: null}]); + +testIndirectExportEntries( + 'export * from "mod";', + []); + +testIndirectExportEntries( + 'import {v as x} from "mod"; export {x as y};', + [{exportName: 'y', moduleRequest: 'mod', importName: 'v', localName: null}]); + +// Test starExportEntries property + +function testStarExportEntries(source, expected) { + var module = parseModule(source); + testArrayContents(module.starExportEntries, expected); +} + +testStarExportEntries( + 'export default function f() {};', + []); + +testStarExportEntries( + 'let x = 1; export {x};', + []); + +testStarExportEntries( + 'export {x} from "mod";', + []); + +testStarExportEntries( + 'export * from "mod";', + [{exportName: null, moduleRequest: 'mod', importName: '*', localName: null}]); diff --git a/js/src/jit-test/tests/modules/export-ns-from.js b/js/src/jit-test/tests/modules/export-ns-from.js new file mode 100644 index 0000000000..2e79d77f42 --- /dev/null +++ b/js/src/jit-test/tests/modules/export-ns-from.js @@ -0,0 +1,10 @@ +// |jit-test| module + +import { ns } from "export-ns.js"; + +load(libdir + 'asserts.js'); + +assertEq(isProxy(ns), true); +assertEq(ns.a, 1); +assertThrowsInstanceOf(function() { eval("delete ns"); }, SyntaxError); +assertThrowsInstanceOf(function() { ns = null; }, TypeError); diff --git a/js/src/jit-test/tests/modules/export-star-cannot-rescue-missing-export.js b/js/src/jit-test/tests/modules/export-star-cannot-rescue-missing-export.js new file mode 100644 index 0000000000..f87829d89b --- /dev/null +++ b/js/src/jit-test/tests/modules/export-star-cannot-rescue-missing-export.js @@ -0,0 +1,4 @@ +// |jit-test| module; error:SyntaxError + +export { a } from "empty.js"; +export* from "module1.js"; diff --git a/js/src/jit-test/tests/modules/export-star-circular-dependencies.js b/js/src/jit-test/tests/modules/export-star-circular-dependencies.js new file mode 100644 index 0000000000..9aa612f087 --- /dev/null +++ b/js/src/jit-test/tests/modules/export-star-circular-dependencies.js @@ -0,0 +1,6 @@ +// |jit-test| module + +import { x, y } from "export-star-circular-1.js"; + +assertEq(x, "pass"); +assertEq(y, "pass"); diff --git a/js/src/jit-test/tests/modules/function-redeclaration.js b/js/src/jit-test/tests/modules/function-redeclaration.js new file mode 100644 index 0000000000..b84704641d --- /dev/null +++ b/js/src/jit-test/tests/modules/function-redeclaration.js @@ -0,0 +1,94 @@ +load(libdir + "asserts.js"); + +var functionDeclarations = [ + "function f(){}", + "function* f(){}", + "async function f(){}", +]; + +var varDeclarations = [ + "var f", + "{ var f; }", + "for (var f in null);", + "for (var f of null);", + "for (var f; ;);", +]; + +var lexicalDeclarations = [ + "let f;", + "const f = 0;", + "class f {};", +]; + +var imports = [ + "import f from '';", + "import f, {} from '';", + "import d, {f} from '';", + "import d, {f as f} from '';", + "import d, {foo as f} from '';", + "import f, * as d from '';", + "import d, * as f from '';", + "import {f} from '';", + "import {f as f} from '';", + "import {foo as f} from '';", + "import* as f from '';", +]; + +var exports = [ + "export var f;", + ...functionDeclarations.map(fn => `export ${fn};`), + ...lexicalDeclarations.map(ld => `export ${ld};`), + ...functionDeclarations.map(fn => `export default ${fn};`), + "export default class f {};", +]; + +var redeclarations = [ + ...functionDeclarations, + ...varDeclarations, + ...lexicalDeclarations, + ...imports, + ...exports, +]; + +var noredeclarations = [ + ...functionDeclarations.map(fn => `{ ${fn} }`), + ...lexicalDeclarations.map(ld => `{ ${ld} }`), + ...["let", "const"].map(ld => `for (${ld} f in null);`), + ...["let", "const"].map(ld => `for (${ld} f of null);`), + ...["let", "const"].map(ld => `for (${ld} f = 0; ;);`), + "export {f};", + "export {f as f};", + "export {foo as f}; var foo;", + "export {f} from '';", + "export {f as f} from '';", + "export {foo as f} from '';", +]; + +for (var decl of functionDeclarations) { + for (var redecl of redeclarations) { + assertThrowsInstanceOf(() => { + parseModule(` + ${decl} + ${redecl} + `); + }, SyntaxError); + + assertThrowsInstanceOf(() => { + parseModule(` + ${redecl} + ${decl} + `); + }, SyntaxError); + } + + for (var redecl of noredeclarations) { + parseModule(` + ${decl} + ${redecl} + `); + parseModule(` + ${redecl} + ${decl} + `); + } +} diff --git a/js/src/jit-test/tests/modules/global-scope.js b/js/src/jit-test/tests/modules/global-scope.js new file mode 100644 index 0000000000..b99019fa86 --- /dev/null +++ b/js/src/jit-test/tests/modules/global-scope.js @@ -0,0 +1,34 @@ +// Test interaction with global object and global lexical scope. + +function evalModuleAndCheck(source, expected) { + let m = parseModule(source); + m.declarationInstantiation(); + m.evaluation(); + assertEq(getModuleEnvironmentValue(m, "r"), expected); +} + +var x = 1; +evalModuleAndCheck("export let r = x; x = 2;", 1); +assertEq(x, 2); + +let y = 3; +evalModuleAndCheck("export let r = y; y = 4;", 3); +assertEq(y, 4); + +if (helperThreadCount() == 0) + quit(); + +function offThreadEvalModuleAndCheck(source, expected) { + offThreadCompileModule(source); + let m = finishOffThreadModule(); + print("compiled"); + m.declarationInstantiation(); + m.evaluation(); + assertEq(getModuleEnvironmentValue(m, "r"), expected); +} + +offThreadEvalModuleAndCheck("export let r = x; x = 5;", 2); +assertEq(x, 5); + +offThreadEvalModuleAndCheck("export let r = y; y = 6;", 4); +assertEq(y, 6); diff --git a/js/src/jit-test/tests/modules/import-declaration.js b/js/src/jit-test/tests/modules/import-declaration.js new file mode 100644 index 0000000000..fd0881e4df --- /dev/null +++ b/js/src/jit-test/tests/modules/import-declaration.js @@ -0,0 +1,343 @@ +load(libdir + "match.js"); +load(libdir + "asserts.js"); + +var { Pattern, MatchError } = Match; + +program = (elts) => Pattern({ + type: "Program", + body: elts +}) +importDeclaration = (specifiers, source) => Pattern({ + type: "ImportDeclaration", + specifiers: specifiers, + source: source +}); +importSpecifier = (id, name) => Pattern({ + type: "ImportSpecifier", + id: id, + name: name +}); +ident = (name) => Pattern({ + type: "Identifier", + name: name +}) +lit = (val) => Pattern({ + type: "Literal", + value: val +}) + +function parseAsModule(source) +{ + return Reflect.parse(source, {target: "module"}); +} + +program([ + importDeclaration( + [ + importSpecifier( + ident("default"), + ident("a") + ) + ], + lit("b") + ) +]).assert(parseAsModule("import a from 'b'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("*"), + ident("a") + ) + ], + lit("b") + ) +]).assert(parseAsModule("import * as a from 'b'")); + +program([ + importDeclaration( + [], + lit("a") + ) +]).assert(parseAsModule("import {} from 'a'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("a"), + ident("a") + ) + ], + lit("b") + ) +]).assert(parseAsModule("import { a } from 'b'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("a"), + ident("a") + ) + ], + lit("b") + ) +]).assert(parseAsModule("import { a, } from 'b'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("a"), + ident("b") + ) + ], + lit("c") + ) +]).assert(parseAsModule("import { a as b } from 'c'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("as"), + ident("as") + ) + ], + lit("a") + ) +]).assert(parseAsModule("import { as as as } from 'a'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("default"), + ident("a") + ), + importSpecifier( + ident("*"), + ident("b") + ) + ], + lit("c") + ) +]).assert(parseAsModule("import a, * as b from 'c'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("default"), + ident("d") + ) + ], + lit("a") + ) +]).assert(parseAsModule("import d, {} from 'a'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("default"), + ident("d") + ), + importSpecifier( + ident("a"), + ident("a") + ) + ], + lit("b") + ) +]).assert(parseAsModule("import d, { a } from 'b'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("default"), + ident("d") + ), + importSpecifier( + ident("a"), + ident("b") + ) + ], + lit("c") + ) +]).assert(parseAsModule("import d, { a as b } from 'c'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("default"), + ident("d") + ), + importSpecifier( + ident("a"), + ident("a") + ), + importSpecifier( + ident("b"), + ident("b") + ), + ], + lit("c") + ) +]).assert(parseAsModule("import d, { a, b } from 'c'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("default"), + ident("d") + ), + importSpecifier( + ident("a"), + ident("b") + ), + importSpecifier( + ident("c"), + ident("f") + ), + ], + lit("e") + ) +]).assert(parseAsModule("import d, { a as b, c as f } from 'e'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("true"), + ident("a") + ) + ], + lit("b") + ) +]).assert(parseAsModule("import { true as a } from 'b'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("a"), + ident("a") + ), + importSpecifier( + ident("b"), + ident("b") + ), + ], + lit("c") + ) +]).assert(parseAsModule("import { a, b } from 'c'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("a"), + ident("b") + ), + importSpecifier( + ident("c"), + ident("d") + ), + ], + lit("e") + ) +]).assert(parseAsModule("import { a as b, c as d } from 'e'")); + +program([ + importDeclaration( + [], + lit("a") + ) +]).assert(parseAsModule("import 'a'")); + +var loc = parseAsModule("import { a as b } from 'c'", { + loc: true +}).body[0].loc; + +assertEq(loc.start.line, 1); +assertEq(loc.start.column, 0); +assertEq(loc.start.line, 1); +assertEq(loc.end.column, 26); + +assertThrowsInstanceOf(function () { + parseAsModule("function f() { import a from 'b' }"); +}, SyntaxError); + +assertThrowsInstanceOf(function () { + parseAsModule("if (true) import a from 'b'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("import {"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("import {}"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("import {} from"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("import {,} from 'a'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("import { a as true } from 'b'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("import { true } from 'a'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("import a,"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("import a, b from 'a'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("import * as a,"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("import * as a, {} from 'a'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("import as a from 'a'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("import * a from 'a'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("import * as from 'a'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("import , {} from 'a'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("import d, from 'a'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("import * as true from 'b'"); +}, SyntaxError); diff --git a/js/src/jit-test/tests/modules/import-default-async-asi.js b/js/src/jit-test/tests/modules/import-default-async-asi.js new file mode 100644 index 0000000000..96a4ea155f --- /dev/null +++ b/js/src/jit-test/tests/modules/import-default-async-asi.js @@ -0,0 +1,7 @@ +// |jit-test| module + +import v from "export-default-async-asi.js"; + +assertEq(typeof v, "function"); +assertEq(v.name, "async"); +assertEq(v(), 17); diff --git a/js/src/jit-test/tests/modules/import-default-async-regexpy.js b/js/src/jit-test/tests/modules/import-default-async-regexpy.js new file mode 100644 index 0000000000..10a769cf2c --- /dev/null +++ b/js/src/jit-test/tests/modules/import-default-async-regexpy.js @@ -0,0 +1,5 @@ +// |jit-test| module + +import v from "export-default-async-regexpy.js"; + +assertEq(v, 7); diff --git a/js/src/jit-test/tests/modules/import-default-class.js b/js/src/jit-test/tests/modules/import-default-class.js new file mode 100644 index 0000000000..ff87649ac6 --- /dev/null +++ b/js/src/jit-test/tests/modules/import-default-class.js @@ -0,0 +1,5 @@ +// |jit-test| module + +import c from "defaultClass.js"; +let o = new c(); +assertEq(o.triple(14), 42); diff --git a/js/src/jit-test/tests/modules/import-default-function.js b/js/src/jit-test/tests/modules/import-default-function.js new file mode 100644 index 0000000000..95e962448f --- /dev/null +++ b/js/src/jit-test/tests/modules/import-default-function.js @@ -0,0 +1,4 @@ +// |jit-test| module + +import f from "defaultFunction.js"; +assertEq(f(21), 42); diff --git a/js/src/jit-test/tests/modules/import-entries.js b/js/src/jit-test/tests/modules/import-entries.js new file mode 100644 index 0000000000..e5f7066f10 --- /dev/null +++ b/js/src/jit-test/tests/modules/import-entries.js @@ -0,0 +1,49 @@ +// Test importEntries property + +function importEntryEq(a, b) +{ + return a['moduleRequest'] === b['moduleRequest'] && + a['importName'] === b['importName'] && + a['localName'] === b['localName']; +} + +function findImportEntry(array, target) +{ + for (let i = 0; i < array.length; i++) { + if (importEntryEq(array[i], target)) + return i; + } + return -1; +} + +function testImportEntries(source, expected) { + var module = parseModule(source); + var actual = module.importEntries.slice(0); + assertEq(actual.length, expected.length); + for (var i = 0; i < expected.length; i++) { + let index = findImportEntry(actual, expected[i]); + assertEq(index >= 0, true); + actual.splice(index, 1); + } +} + +testImportEntries('', []); + +testImportEntries('import v from "mod";', + [{moduleRequest: 'mod', importName: 'default', localName: 'v'}]); + +testImportEntries('import * as ns from "mod";', + [{moduleRequest: 'mod', importName: '*', localName: 'ns'}]); + +testImportEntries('import {x} from "mod";', + [{moduleRequest: 'mod', importName: 'x', localName: 'x'}]); + +testImportEntries('import {x as v} from "mod";', + [{moduleRequest: 'mod', importName: 'x', localName: 'v'}]); + +testImportEntries('import "mod";', + []); + +testImportEntries('import {x} from "a"; import {y} from "b";', + [{moduleRequest: 'a', importName: 'x', localName: 'x'}, + {moduleRequest: 'b', importName: 'y', localName: 'y'}]); diff --git a/js/src/jit-test/tests/modules/import-in-lazy-function.js b/js/src/jit-test/tests/modules/import-in-lazy-function.js new file mode 100644 index 0000000000..cddacb1428 --- /dev/null +++ b/js/src/jit-test/tests/modules/import-in-lazy-function.js @@ -0,0 +1,11 @@ +// |jit-test| module + +// Test that accessing imports in lazily parsed functions works. + +import { a } from "module1.js"; + +function add1(x) { + return x + a; +} + +assertEq(add1(2), 3); diff --git a/js/src/jit-test/tests/modules/import-meta-expression.js b/js/src/jit-test/tests/modules/import-meta-expression.js new file mode 100644 index 0000000000..e7562afaed --- /dev/null +++ b/js/src/jit-test/tests/modules/import-meta-expression.js @@ -0,0 +1,91 @@ +load(libdir + "match.js"); +load(libdir + "asserts.js"); + +var { Pattern, MatchError } = Match; + +program = (elts) => Pattern({ + type: "Program", + body: elts +}); +expressionStatement = (expression) => Pattern({ + type: "ExpressionStatement", + expression: expression +}); +assignmentExpression = (left, operator, right) => Pattern({ + type: "AssignmentExpression", + operator: operator, + left: left, + right: right +}); +ident = (name) => Pattern({ + type: "Identifier", + name: name +}); +metaProperty = (meta, property) => Pattern({ + type: "MetaProperty", + meta: meta, + property: property +}); +memberExpression = (object, property) => Pattern({ + type: "MemberExpression", + object: object, + property: property +}); + +function parseAsModule(source) +{ + return Reflect.parse(source, {target: "module"}); +} + +program([ + expressionStatement( + metaProperty( + ident("import"), + ident("meta") + ) + ) +]).assert(parseAsModule("import.meta;")); + +program([ + expressionStatement( + assignmentExpression( + ident("x"), + "=", + metaProperty( + ident("import"), + ident("meta") + ) + ) + ) +]).assert(parseAsModule("x = import.meta;")); + +program([ + expressionStatement( + assignmentExpression( + memberExpression( + metaProperty( + ident("import"), + ident("meta") + ), + ident("foo") + ), + "=", + ident("x"), + ) + ) +]).assert(parseAsModule("import.meta.foo = x;")); + +function assertModuleParseThrowsSyntaxError(source) +{ + assertThrowsInstanceOf(() => parseAsModule(source), SyntaxError); +} + +assertModuleParseThrowsSyntaxError("import"); +assertModuleParseThrowsSyntaxError("import."); +assertModuleParseThrowsSyntaxError("import.met"); +assertModuleParseThrowsSyntaxError("import.metaa"); +assertModuleParseThrowsSyntaxError("x = import"); +assertModuleParseThrowsSyntaxError("x = import."); +assertModuleParseThrowsSyntaxError("x = import.met"); +assertModuleParseThrowsSyntaxError("x = import.metaa"); +assertModuleParseThrowsSyntaxError("import.meta = x"); diff --git a/js/src/jit-test/tests/modules/import-meta-oom.js b/js/src/jit-test/tests/modules/import-meta-oom.js new file mode 100644 index 0000000000..168f1102a1 --- /dev/null +++ b/js/src/jit-test/tests/modules/import-meta-oom.js @@ -0,0 +1,3 @@ +// |jit-test| module; skip-if: !('oomTest' in this) + +oomTest(() => import.meta); diff --git a/js/src/jit-test/tests/modules/import-meta.js b/js/src/jit-test/tests/modules/import-meta.js new file mode 100644 index 0000000000..aab134cd0b --- /dev/null +++ b/js/src/jit-test/tests/modules/import-meta.js @@ -0,0 +1,58 @@ +// |jit-test| module + +// import.meta is an object. +assertEq(typeof import.meta, "object"); +assertEq(import.meta !== null, true); + +// import.meta always returns the same object. +let obj = import.meta; +assertEq(import.meta, obj); + +// Test calling from lazily compile function. +function get() { + return import.meta; +} +assertEq(get(), import.meta); + +// import.meta.url: This property will contain the module script's base URL, +// serialized. + +assertEq("url" in import.meta, true); +assertEq(import.meta.url.endsWith("import-meta.js"), true); + +import getOtherMetaObject from "exportImportMeta.js"; + +let otherImportMeta = getOtherMetaObject(); +assertEq(otherImportMeta.url.endsWith("exportImportMeta.js"), true); + +// By default the import.meta object will be extensible, and its properties will +// be writable, configurable, and enumerable. + +assertEq(Object.isExtensible(import.meta), true); + +var desc = Object.getOwnPropertyDescriptor(import.meta, "url"); +assertEq(desc.writable, true); +assertEq(desc.enumerable, true); +assertEq(desc.configurable, true); +assertEq(desc.value, import.meta.url); + +// The import.meta object's prototype is null. +assertEq(Object.getPrototypeOf(import.meta), null); + +import.meta.url = 0; +assertEq(import.meta.url, 0); + +import.meta.newProp = 42; +assertEq(import.meta.newProp, 42); + +let found = new Set(Reflect.ownKeys(import.meta)); + +assertEq(found.size, 2); +assertEq(found.has("url"), true); +assertEq(found.has("newProp"), true); + +delete import.meta.url; +delete import.meta.newProp; + +found = new Set(Reflect.ownKeys(import.meta)); +assertEq(found.size, 0); diff --git a/js/src/jit-test/tests/modules/import-namespace.js b/js/src/jit-test/tests/modules/import-namespace.js new file mode 100644 index 0000000000..c5eee00fd1 --- /dev/null +++ b/js/src/jit-test/tests/modules/import-namespace.js @@ -0,0 +1,114 @@ +// |jit-test| --enable-top-level-await; +// Test importing module namespaces + +"use strict"; + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +function parseAndEvaluate(source) { + let m = parseModule(source); + m.declarationInstantiation(); + return m.evaluation(); +} + +function testHasNames(names, expected) { + assertEq(names.length, expected.length); + expected.forEach(function(name) { + assertEq(names.includes(name), true); + }); +} + +function testEqualArrays(actual, expected) { + assertEq(Array.isArray(actual), true); + assertEq(Array.isArray(expected), true); + assertEq(actual.length, expected.length); + for (let i = 0; i < expected.length; i++) { + assertEq(actual[i], expected[i]); + } +} + +let a = registerModule('a', parseModule( + `// Reflection methods should return these exports alphabetically sorted. + export var b = 2; + export var a = 1;` +)); + +let b = registerModule('b', parseModule( + `import * as ns from 'a'; + export { ns }; + export var x = ns.a + ns.b;` +)); + +b.declarationInstantiation(); +b.evaluation(); +testHasNames(getModuleEnvironmentNames(b), ["ns", "x"]); +let ns = getModuleEnvironmentValue(b, "ns"); +testHasNames(Object.keys(ns), ["a", "b"]); +assertEq(ns.a, 1); +assertEq(ns.b, 2); +assertEq(ns.c, undefined); +assertEq(getModuleEnvironmentValue(b, "x"), 3); + +// Test module namespace internal methods as defined in 9.4.6 +assertEq(Object.getPrototypeOf(ns), null); +assertEq(Reflect.setPrototypeOf(ns, null), true); +assertEq(Reflect.setPrototypeOf(ns, Object.prototype), false); +assertThrowsInstanceOf(() => Object.setPrototypeOf(ns, {}), TypeError); +assertThrowsInstanceOf(function() { ns.foo = 1; }, TypeError); +assertEq(Object.isExtensible(ns), false); +Object.preventExtensions(ns); +let desc = Object.getOwnPropertyDescriptor(ns, "a"); +assertEq(desc.value, 1); +assertEq(desc.writable, true); +assertEq(desc.enumerable, true); +assertEq(desc.configurable, false); +assertEq(typeof desc.get, "undefined"); +assertEq(typeof desc.set, "undefined"); +assertThrowsInstanceOf(function() { ns.a = 1; }, TypeError); +delete ns.foo; +assertThrowsInstanceOf(function() { delete ns.a; }, TypeError); + +// Test @@toStringTag property +desc = Object.getOwnPropertyDescriptor(ns, Symbol.toStringTag); +assertEq(desc.value, "Module"); +assertEq(desc.writable, false); +assertEq(desc.enumerable, false); +assertEq(desc.configurable, false); +assertEq(typeof desc.get, "undefined"); +assertEq(typeof desc.set, "undefined"); +assertEq(Object.prototype.toString.call(ns), "[object Module]"); + +// Test [[OwnPropertyKeys]] internal method. +testEqualArrays(Reflect.ownKeys(ns), ["a", "b", Symbol.toStringTag]); +testEqualArrays(Object.getOwnPropertyNames(ns), ["a", "b"]); +testEqualArrays(Object.getOwnPropertySymbols(ns), [Symbol.toStringTag]); + +// Test cyclic namespace import and access in module evaluation. +let c = registerModule('c', + parseModule("export let c = 1; import * as ns from 'd'; let d = ns.d;")); +let d = registerModule('d', + parseModule("export let d = 2; import * as ns from 'c'; let c = ns.c;")); +c.declarationInstantiation(); +d.declarationInstantiation(); +c.evaluation() + .then(r => { + // We expect the evaluation to throw, so we should not reach this. + assertEq(false, true) + }) + .catch(e => { + assertEq(e instanceof ReferenceError, true) + }); + +// Test cyclic namespace import. +let e = registerModule('e', + parseModule("export let e = 1; import * as ns from 'f'; export function f() { return ns.f }")); +let f = registerModule('f', + parseModule("export let f = 2; import * as ns from 'e'; export function e() { return ns.e }")); +e.declarationInstantiation(); +f.declarationInstantiation(); +e.evaluation(); +f.evaluation(); +assertEq(e.namespace.f(), 2); +assertEq(f.namespace.e(), 1); +drainJobQueue(); diff --git a/js/src/jit-test/tests/modules/import-not-found.js b/js/src/jit-test/tests/modules/import-not-found.js new file mode 100644 index 0000000000..05e6e1e8d5 --- /dev/null +++ b/js/src/jit-test/tests/modules/import-not-found.js @@ -0,0 +1,2 @@ +// |jit-test| module; error: SyntaxError +import { foo } from "module1.js"; diff --git a/js/src/jit-test/tests/modules/inline-data.js b/js/src/jit-test/tests/modules/inline-data.js new file mode 100644 index 0000000000..9c56856f8d --- /dev/null +++ b/js/src/jit-test/tests/modules/inline-data.js @@ -0,0 +1,17 @@ +// |jit-test| module + +import { a } from "javascript: export let a = 42;"; +assertEq(a, 42); + +let result = null; +let error = null; +let promise = import("javascript: export let b = 100;"); +promise.then((ns) => { + result = ns; +}).catch((e) => { + error = e; +}); + +drainJobQueue(); +assertEq(error, null); +assertEq(result.b, 100); diff --git a/js/src/jit-test/tests/modules/instanceof-error-message.js b/js/src/jit-test/tests/modules/instanceof-error-message.js new file mode 100644 index 0000000000..d78eb333b3 --- /dev/null +++ b/js/src/jit-test/tests/modules/instanceof-error-message.js @@ -0,0 +1,14 @@ +function getInstanceOfErrorMessage(x) { + try { + var result = {} instanceof x; + } + catch (e) { + return e.message; + } +} + +// Error message for a Module Namespace Exotic Object should be same as normal +// non-callable when using 'instanceof' +import('empty.js').then( + m => assertEq(getInstanceOfErrorMessage(m), + getInstanceOfErrorMessage({}))); diff --git a/js/src/jit-test/tests/modules/let-tdz.js b/js/src/jit-test/tests/modules/let-tdz.js new file mode 100644 index 0000000000..b426794340 --- /dev/null +++ b/js/src/jit-test/tests/modules/let-tdz.js @@ -0,0 +1,3 @@ +// |jit-test| module; error:ReferenceError +foo = 1; +let foo; diff --git a/js/src/jit-test/tests/modules/many-exports.js b/js/src/jit-test/tests/modules/many-exports.js new file mode 100644 index 0000000000..4ade0d957b --- /dev/null +++ b/js/src/jit-test/tests/modules/many-exports.js @@ -0,0 +1,17 @@ +// Test many exports. + +const count = 1024; + +let s = ""; +for (let i = 0; i < count; i++) + s += "export let e" + i + " = " + (i * i) + ";\n"; +let a = registerModule('a', parseModule(s)); + +let b = registerModule('b', parseModule("import * as ns from 'a'")); + +b.declarationInstantiation(); +b.evaluation(); + +let ns = a.namespace; +for (let i = 0; i < count; i++) + assertEq(ns["e" + i], i * i); diff --git a/js/src/jit-test/tests/modules/many-imports.js b/js/src/jit-test/tests/modules/many-imports.js new file mode 100644 index 0000000000..d8ff18d311 --- /dev/null +++ b/js/src/jit-test/tests/modules/many-imports.js @@ -0,0 +1,15 @@ +// Test importing an import many times. + +const count = 1024; + +let a = registerModule('a', parseModule("export let a = 1;")); + +let s = ""; +for (let i = 0; i < count; i++) { + s += "import { a as i" + i + " } from 'a';\n"; + s += "assertEq(i" + i + ", 1);\n"; +} +let b = registerModule('b', parseModule(s)); + +b.declarationInstantiation(); +b.evaluation(); diff --git a/js/src/jit-test/tests/modules/many-namespace-imports.js b/js/src/jit-test/tests/modules/many-namespace-imports.js new file mode 100644 index 0000000000..d6b23f7cd4 --- /dev/null +++ b/js/src/jit-test/tests/modules/many-namespace-imports.js @@ -0,0 +1,15 @@ +// Test importing a namespace many times. + +const count = 1024; + +let a = registerModule('a', parseModule("export let a = 1;")); + +let s = ""; +for (let i = 0; i < count; i++) { + s += "import * as ns" + i + " from 'a';\n"; + s += "assertEq(ns" + i + ".a, 1);\n"; +} +let b = registerModule('b', parseModule(s)); + +b.declarationInstantiation(); +b.evaluation(); diff --git a/js/src/jit-test/tests/modules/missing-export-offthread.js b/js/src/jit-test/tests/modules/missing-export-offthread.js new file mode 100644 index 0000000000..faaca860a9 --- /dev/null +++ b/js/src/jit-test/tests/modules/missing-export-offthread.js @@ -0,0 +1,7 @@ +// |jit-test| skip-if: helperThreadCount() === 0 + +load(libdir + "asserts.js") + +// Don't assert. +offThreadCompileModule("export { x };"); +assertThrowsInstanceOf(() => finishOffThreadModule(), SyntaxError); diff --git a/js/src/jit-test/tests/modules/missing-indirect-export.js b/js/src/jit-test/tests/modules/missing-indirect-export.js new file mode 100644 index 0000000000..207e49ba05 --- /dev/null +++ b/js/src/jit-test/tests/modules/missing-indirect-export.js @@ -0,0 +1,2 @@ +// |jit-test| module; error: SyntaxError +export { foo } from "module1.js"; diff --git a/js/src/jit-test/tests/modules/module-declaration-instantiation.js b/js/src/jit-test/tests/modules/module-declaration-instantiation.js new file mode 100644 index 0000000000..8fb557d58a --- /dev/null +++ b/js/src/jit-test/tests/modules/module-declaration-instantiation.js @@ -0,0 +1,37 @@ +// Exercise ModuleDeclarationInstantiation() operation. + +function testModuleEnvironment(module, expected) { + var actual = getModuleEnvironmentNames(module).sort(); + assertEq(actual.length, expected.length); + for (var i = 0; i < actual.length; i++) { + assertEq(actual[i], expected[i]); + } +} + +// Check the environment of an empty module. +let m = parseModule(""); +m.declarationInstantiation(); +testModuleEnvironment(m, []); + +let a = registerModule('a', parseModule("var x = 1; export { x };")); +let b = registerModule('b', parseModule("import { x as y } from 'a';")); + +a.declarationInstantiation(); +b.declarationInstantiation(); + +testModuleEnvironment(a, ['x']); +testModuleEnvironment(b, ['y']); + +// Function bindings are initialized as well as instantiated. +let c = parseModule(`function a(x) { return x; } + function b(x) { return x + 1; } + function c(x) { return x + 2; } + function d(x) { return x + 3; }`); +const names = ['a', 'b', 'c', 'd']; +testModuleEnvironment(c, names); +names.forEach((n) => assertEq(typeof getModuleEnvironmentValue(c, n), "undefined")); +c.declarationInstantiation(); +for (let i = 0; i < names.length; i++) { + let f = getModuleEnvironmentValue(c, names[i]); + assertEq(f(21), 21 + i); +} diff --git a/js/src/jit-test/tests/modules/module-environment.js b/js/src/jit-test/tests/modules/module-environment.js new file mode 100644 index 0000000000..fc526cfdd7 --- /dev/null +++ b/js/src/jit-test/tests/modules/module-environment.js @@ -0,0 +1,34 @@ +// Test top-level module environment + +function testInitialEnvironment(source, expected) { + let module = parseModule(source); + let names = getModuleEnvironmentNames(module); + assertEq(names.length, expected.length); + expected.forEach(function(name) { + assertEq(names.includes(name), true); + }); +} + +// Non-exported bindings: only top-level functions are present in the +// environment. +testInitialEnvironment('', []); +testInitialEnvironment('var x = 1;', []); +testInitialEnvironment('let x = 1;', []); +testInitialEnvironment("if (true) { let x = 1; }", []); +testInitialEnvironment("if (true) { var x = 1; }", []); +testInitialEnvironment('function x() {}', ['x']); +testInitialEnvironment("class x { constructor() {} }", []); + +// Exported bindings must be present in the environment. +testInitialEnvironment('export var x = 1;', ['x']); +testInitialEnvironment('export let x = 1;', ['x']); +testInitialEnvironment('export default function x() {};', ['x']); +testInitialEnvironment('export default 1;', ['default']); +testInitialEnvironment('export default function() {};', ['default']); +testInitialEnvironment("export class x { constructor() {} }", ['x']); +testInitialEnvironment('export default class x { constructor() {} };', ['x']); +testInitialEnvironment('export default class { constructor() {} };', ['default']); + +// Imports: namespace imports are present. +testInitialEnvironment('import { x } from "m";', []); +testInitialEnvironment('import * as x from "m";', ['x']); diff --git a/js/src/jit-test/tests/modules/module-evaluation.js b/js/src/jit-test/tests/modules/module-evaluation.js new file mode 100644 index 0000000000..64b64c51a2 --- /dev/null +++ b/js/src/jit-test/tests/modules/module-evaluation.js @@ -0,0 +1,125 @@ +// |jit-test| --enable-top-level-await; +// Exercise ModuleEvaluation() concrete method. + +load(libdir + "asserts.js"); + +async function parseAndEvaluate(source) { + let m = parseModule(source); + m.declarationInstantiation(); + await m.evaluation(); + return m; +} + +// Check the evaluation of an empty module succeeds. +(async () => { + await parseAndEvaluate(""); +})(); + +(async () => { + // Check that evaluation returns evaluation promise, + // and promise is always the same. + let m = parseModule("1"); + m.declarationInstantiation(); + assertEq(typeof m.evaluation(), "object"); + assertEq(m.evaluation() instanceof Promise, true); + assertEq(m.evaluation(), m.evaluation()); + await m.evaluation(); +})(); + +(async () => { + // Check top level variables are initialized by evaluation. + let m = parseModule("export var x = 2 + 2;"); + assertEq(typeof getModuleEnvironmentValue(m, "x"), "undefined"); + m.declarationInstantiation(); + await m.evaluation(); + assertEq(getModuleEnvironmentValue(m, "x"), 4); +})(); + +(async () => { + let m = parseModule("export let x = 2 * 3;"); + m.declarationInstantiation(); + await m.evaluation(); + assertEq(getModuleEnvironmentValue(m, "x"), 6); +})(); + +// Set up a module to import from. +let a = registerModule('a', + parseModule(`var x = 1; + export { x }; + export default 2; + export function f(x) { return x + 1; }`)); + +(async () => { + // Check we can evaluate top level definitions. + await parseAndEvaluate("var foo = 1;"); + await parseAndEvaluate("let foo = 1;"); + await parseAndEvaluate("const foo = 1"); + await parseAndEvaluate("function foo() {}"); + await parseAndEvaluate("class foo { constructor() {} }"); + + // Check we can evaluate all module-related syntax. + await parseAndEvaluate("export var foo = 1;"); + await parseAndEvaluate("export let foo = 1;"); + await parseAndEvaluate("export const foo = 1;"); + await parseAndEvaluate("var x = 1; export { x };"); + await parseAndEvaluate("export default 1"); + await parseAndEvaluate("export default function() {};"); + await parseAndEvaluate("export default function foo() {};"); + await parseAndEvaluate("import a from 'a';"); + await parseAndEvaluate("import { x } from 'a';"); + await parseAndEvaluate("import * as ns from 'a';"); + await parseAndEvaluate("export * from 'a'"); + await parseAndEvaluate("export default class { constructor() {} };"); + await parseAndEvaluate("export default class foo { constructor() {} };"); +})(); + +(async () => { + // Test default import + let m = parseModule("import a from 'a'; export { a };") + m.declarationInstantiation(); + await m.evaluation() + assertEq(getModuleEnvironmentValue(m, "a"), 2); +})(); + +(async () => { + // Test named import + let m = parseModule("import { x as y } from 'a'; export { y };") + m.declarationInstantiation(); + await m.evaluation(); + assertEq(getModuleEnvironmentValue(m, "y"), 1); +})(); + +(async () => { + // Call exported function + let m = parseModule("import { f } from 'a'; export let x = f(3);") + m.declarationInstantiation(); + await m.evaluation(); + assertEq(getModuleEnvironmentValue(m, "x"), 4); +})(); + +(async () => { + // Test importing an indirect export + registerModule('b', parseModule("export { x as z } from 'a';")); + let m = await parseAndEvaluate("import { z } from 'b'; export { z }"); + assertEq(getModuleEnvironmentValue(m, "z"), 1); +})(); + +(async () => { + // Test cyclic dependencies + registerModule('c1', parseModule("export var x = 1; export {y} from 'c2'")); + registerModule('c2', parseModule("export var y = 2; export {x} from 'c1'")); + let m = await parseAndEvaluate(`import { x as x1, y as y1 } from 'c1'; + import { x as x2, y as y2 } from 'c2'; + export let z = [x1, y1, x2, y2]`); + assertDeepEq(getModuleEnvironmentValue(m, "z"), [1, 2, 1, 2]); +})(); + +(async () => { + // Import access in functions + let m = await parseModule("import { x } from 'a'; function f() { return x; }") + m.declarationInstantiation(); + m.evaluation(); + let f = getModuleEnvironmentValue(m, "f"); + assertEq(f(), 1); +})(); +drainJobQueue(); diff --git a/js/src/jit-test/tests/modules/module-this.js b/js/src/jit-test/tests/modules/module-this.js new file mode 100644 index 0000000000..9721ef4d95 --- /dev/null +++ b/js/src/jit-test/tests/modules/module-this.js @@ -0,0 +1,25 @@ +// |jit-test| --enable-top-level-await; +// Test 'this' is undefined in modules. + +function parseAndEvaluate(source) { + let m = parseModule(source); + m.declarationInstantiation(); + return m.evaluation(); +} + +parseAndEvaluate("this") + .then(value => assertEq(typeof(value), "undefined")) + .catch(error => { + // We shouldn't throw in this case. + assertEq(false, true) + }); + +let m = parseModule("export function getThis() { return this; }"); +m.declarationInstantiation(); +m.evaluation() + .then(() => { + let f = getModuleEnvironmentValue(m, "getThis"); + assertEq(typeof(f()), "undefined"); + }); + +drainJobQueue(); diff --git a/js/src/jit-test/tests/modules/namespace-import-compilation-2.js b/js/src/jit-test/tests/modules/namespace-import-compilation-2.js new file mode 100644 index 0000000000..ae322246dd --- /dev/null +++ b/js/src/jit-test/tests/modules/namespace-import-compilation-2.js @@ -0,0 +1,17 @@ +// |jit-test| module + +import * as ns from 'module1.js'; + +let other = ns; + +const iterations = 2000; + +let x = 0; +for (let i = 0; i < iterations; i++) { + if (i == iterations / 2) + other = 1; + x += other.a; +} + +assertEq(other.a, undefined); +assertEq(x, NaN); diff --git a/js/src/jit-test/tests/modules/namespace-import-compilation.js b/js/src/jit-test/tests/modules/namespace-import-compilation.js new file mode 100644 index 0000000000..42595b7095 --- /dev/null +++ b/js/src/jit-test/tests/modules/namespace-import-compilation.js @@ -0,0 +1,17 @@ +// |jit-test| module + +import * as ns from 'module1.js'; + +let other = ns; + +const iterations = 10000; + +let x = 0; +for (let i = 0; i < iterations; i++) { + if (i == iterations / 2) + other = { a: 0 }; + x += other.a; +} + +assertEq(other.a, 0); +assertEq(x, iterations / 2); diff --git a/js/src/jit-test/tests/modules/off-thread-compile.js b/js/src/jit-test/tests/modules/off-thread-compile.js new file mode 100644 index 0000000000..65d85fe8fe --- /dev/null +++ b/js/src/jit-test/tests/modules/off-thread-compile.js @@ -0,0 +1,14 @@ +// |jit-test| skip-if: helperThreadCount() === 0 + +// Test off thread module compilation. + +load(libdir + "asserts.js"); + +function offThreadParseAndEvaluate(source) { + offThreadCompileModule(source); + let m = finishOffThreadModule(); + m.declarationInstantiation(); + return m.evaluation(); +} + +offThreadParseAndEvaluate("export let x = 2 * 3;"); diff --git a/js/src/jit-test/tests/modules/recursive-star-export.js b/js/src/jit-test/tests/modules/recursive-star-export.js new file mode 100644 index 0000000000..0313321c59 --- /dev/null +++ b/js/src/jit-test/tests/modules/recursive-star-export.js @@ -0,0 +1,2 @@ +// |jit-test| module; +import * as ns from "recursiveStarExport.js"; diff --git a/js/src/jit-test/tests/modules/requested-modules.js b/js/src/jit-test/tests/modules/requested-modules.js new file mode 100644 index 0000000000..acf58e5a6e --- /dev/null +++ b/js/src/jit-test/tests/modules/requested-modules.js @@ -0,0 +1,30 @@ +// Test requestedModules property + +function testRequestedModules(source, expected) { + var module = parseModule(source); + var actual = module.requestedModules; + assertEq(actual.length, expected.length); + for (var i = 0; i < actual.length; i++) { + assertEq(actual[i].moduleSpecifier, expected[i]); + } +} + +testRequestedModules("", []); + +testRequestedModules("import a from 'foo'", + ['foo']); + +testRequestedModules("import a from 'foo'; import b from 'bar'", + ['foo', 'bar']); + +testRequestedModules("import a from 'foo'; import b from 'bar'; import c from 'foo'", + ['foo', 'bar']); + +testRequestedModules("export {} from 'foo'", + ['foo']); + +testRequestedModules("export * from 'bar'", + ['bar']); + +testRequestedModules("import a from 'foo'; export {} from 'bar'; export * from 'baz'", + ['foo', 'bar', 'baz']); diff --git a/js/src/jit-test/tests/modules/shell-parse.js b/js/src/jit-test/tests/modules/shell-parse.js new file mode 100644 index 0000000000..d01f10aaa2 --- /dev/null +++ b/js/src/jit-test/tests/modules/shell-parse.js @@ -0,0 +1,30 @@ +// Exercise shell parseModule function. + +function testEvalError(source) { + // Test |source| throws when passed to eval. + var caught = false; + try { + eval(source); + } catch (e) { + caught = true; + } + assertEq(caught, true); +} + +function testModuleSource(source) { + // Test |source| parses as a module, but throws when passed to eval. + testEvalError(source); + parseModule(source); +} + +parseModule(""); +parseModule("const foo = 1;"); +parseModule("var foo = 1;"); +parseModule("let foo = 1; var bar = 2; const baz = 3"); + +testModuleSource("import * as ns from 'bar';"); +testModuleSource("export { a } from 'b';"); +testModuleSource("export * from 'b';"); +testModuleSource("export const foo = 1;"); +testModuleSource("export default function() {};"); +testModuleSource("export default 1;"); diff --git a/js/src/jit-test/tests/modules/simple-imports.js b/js/src/jit-test/tests/modules/simple-imports.js new file mode 100644 index 0000000000..a0c894ef63 --- /dev/null +++ b/js/src/jit-test/tests/modules/simple-imports.js @@ -0,0 +1,11 @@ +// |jit-test| module + +import { a } from "module1.js"; +import { b } from "module2.js"; +import { c } from "module3.js"; +import d from "module4.js"; + +assertEq(a, 1); +assertEq(b, 2); +assertEq(c, 3); +assertEq(d, 4); diff --git a/js/src/jit-test/tests/modules/unbound-export.js b/js/src/jit-test/tests/modules/unbound-export.js new file mode 100644 index 0000000000..f70405d56f --- /dev/null +++ b/js/src/jit-test/tests/modules/unbound-export.js @@ -0,0 +1,2 @@ +// |jit-test| module; error: SyntaxError: local binding for export 'b' not found +export { b }; |