summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/xdr
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/xdr')
-rw-r--r--js/src/jit-test/tests/xdr/asm.js19
-rw-r--r--js/src/jit-test/tests/xdr/async-lazy.js24
-rw-r--r--js/src/jit-test/tests/xdr/async.js35
-rw-r--r--js/src/jit-test/tests/xdr/bigint.js10
-rw-r--r--js/src/jit-test/tests/xdr/bug1186973.js17
-rw-r--r--js/src/jit-test/tests/xdr/bug1390856.js9
-rw-r--r--js/src/jit-test/tests/xdr/bug1427860.js12
-rw-r--r--js/src/jit-test/tests/xdr/bug1585158.js12
-rw-r--r--js/src/jit-test/tests/xdr/bug1607895.js7
-rw-r--r--js/src/jit-test/tests/xdr/bug1790615.js19
-rw-r--r--js/src/jit-test/tests/xdr/class-relazify.js15
-rw-r--r--js/src/jit-test/tests/xdr/classes.js36
-rw-r--r--js/src/jit-test/tests/xdr/debug-hook.js20
-rw-r--r--js/src/jit-test/tests/xdr/debug-lazy.js19
-rw-r--r--js/src/jit-test/tests/xdr/decode-off-thread.js54
-rw-r--r--js/src/jit-test/tests/xdr/delazifications-atoms.js14
-rw-r--r--js/src/jit-test/tests/xdr/delazifications-nest.js70
-rw-r--r--js/src/jit-test/tests/xdr/delazify-findScript-lineCount.js54
-rw-r--r--js/src/jit-test/tests/xdr/delazify-findScript-parameterNames.js54
-rw-r--r--js/src/jit-test/tests/xdr/force-full-parse.js19
-rw-r--r--js/src/jit-test/tests/xdr/function-flags.js40
-rw-r--r--js/src/jit-test/tests/xdr/incremental-encoder.js66
-rw-r--r--js/src/jit-test/tests/xdr/incremental-oom.js10
-rw-r--r--js/src/jit-test/tests/xdr/lazy-class-definition.js51
-rw-r--r--js/src/jit-test/tests/xdr/lazy.js164
-rw-r--r--js/src/jit-test/tests/xdr/load-nonsyntactic.js55
-rw-r--r--js/src/jit-test/tests/xdr/module-exports.js19
-rw-r--r--js/src/jit-test/tests/xdr/module-imports.js25
-rw-r--r--js/src/jit-test/tests/xdr/module-oom.js26
-rw-r--r--js/src/jit-test/tests/xdr/module.js92
-rw-r--r--js/src/jit-test/tests/xdr/off-thread-inner-fcn.js11
-rw-r--r--js/src/jit-test/tests/xdr/option-mismatch.js23
-rw-r--r--js/src/jit-test/tests/xdr/private-fields.js9
-rw-r--r--js/src/jit-test/tests/xdr/relazify.js31
-rw-r--r--js/src/jit-test/tests/xdr/runonce.js6
-rw-r--r--js/src/jit-test/tests/xdr/scope.js19
-rw-r--r--js/src/jit-test/tests/xdr/script-source-fields.js23
-rw-r--r--js/src/jit-test/tests/xdr/share-bytecode.js12
-rw-r--r--js/src/jit-test/tests/xdr/stencil-arg.js25
-rw-r--r--js/src/jit-test/tests/xdr/stencil-can-lazily-parse-mismatch.js71
-rw-r--r--js/src/jit-test/tests/xdr/stencil-oom.js12
-rw-r--r--js/src/jit-test/tests/xdr/stencil.js99
-rw-r--r--js/src/jit-test/tests/xdr/tagged-template-literals-2.js4
-rw-r--r--js/src/jit-test/tests/xdr/tagged-template-literals.js4
-rw-r--r--js/src/jit-test/tests/xdr/trivial.js46
45 files changed, 1462 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/xdr/asm.js b/js/src/jit-test/tests/xdr/asm.js
new file mode 100644
index 0000000000..5e28e9da00
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/asm.js
@@ -0,0 +1,19 @@
+load(libdir + 'bytecode-cache.js');
+
+var test = (function () {
+ function f() {
+ var x = function inner() {
+ "use asm";
+ function g() {}
+ return g;
+ };
+ };
+ return f.toString();
+})();
+
+try {
+ evalWithCache(test, {});
+} catch (x) {
+ assertEq(x.message.includes("Asm.js is not supported by XDR") ||
+ x.message.includes("XDR encoding failure"), true);
+}
diff --git a/js/src/jit-test/tests/xdr/async-lazy.js b/js/src/jit-test/tests/xdr/async-lazy.js
new file mode 100644
index 0000000000..8f8c5acccc
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/async-lazy.js
@@ -0,0 +1,24 @@
+async function f1(a, b) {
+ let x = await 10;
+ return x;
+};
+var toStringResult = f1.toString();
+
+async function f2(a, b) {
+ // arguments.callee gets wrapped function from unwrapped function.
+ return arguments.callee;
+};
+
+relazifyFunctions();
+
+// toString gets unwrapped function from wrapped function.
+assertEq(f1.toString(), toStringResult);
+
+var ans = 0;
+f1().then(x => { ans = x; });
+drainJobQueue();
+assertEq(ans, 10);
+
+f2().then(x => { ans = x; });
+drainJobQueue();
+assertEq(ans, f2);
diff --git a/js/src/jit-test/tests/xdr/async.js b/js/src/jit-test/tests/xdr/async.js
new file mode 100644
index 0000000000..2c75c89a98
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/async.js
@@ -0,0 +1,35 @@
+load(libdir + 'bytecode-cache.js');
+
+// Prevent relazification triggered by some zeal modes.
+gczeal(0);
+
+async function f1(a, b) {
+ let x = await 10;
+ return x;
+};
+var toStringResult = f1.toString();
+
+var test = `
+async function f1(a, b) {
+ let x = await 10;
+ return x;
+};
+// toString gets unwrapped function from wrapped function.
+assertEq(f1.toString(), \`${toStringResult}\`);
+
+var ans = 0;
+f1().then(x => { ans = x; });
+drainJobQueue();
+assertEq(ans, 10);
+
+async function f2(a, b) {
+ // arguments.callee gets wrapped function from unwrapped function.
+ return arguments.callee;
+};
+
+f2().then(x => { ans = x; });
+drainJobQueue();
+assertEq(ans, f2);
+`;
+
+evalWithCache(test, { assertEqBytecode: true, checkFrozen: true});
diff --git a/js/src/jit-test/tests/xdr/bigint.js b/js/src/jit-test/tests/xdr/bigint.js
new file mode 100644
index 0000000000..f15d0f317a
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/bigint.js
@@ -0,0 +1,10 @@
+// |jit-test| skip-if: !('BigInt' in this)
+load(libdir + 'bytecode-cache.js');
+
+let test = `
+ assertEq(2n**64n - 1n, BigInt("0xffffFFFFffffFFFF"));
+`;
+evalWithCache(test, {
+ assertEqBytecode: true,
+ assertEqResult : true
+});
diff --git a/js/src/jit-test/tests/xdr/bug1186973.js b/js/src/jit-test/tests/xdr/bug1186973.js
new file mode 100644
index 0000000000..b6949799c1
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/bug1186973.js
@@ -0,0 +1,17 @@
+// |jit-test| error: cache does not have the same size; skip-if: isLcovEnabled()
+
+
+load(libdir + 'bytecode-cache.js');
+
+var test = (function () {
+ function f(x) {
+ function ifTrue() {};
+ function ifFalse() {};
+
+ if (generation % 2 == 0)
+ return ifTrue();
+ return ifFalse();
+ }
+ return f.toString() + "; f()";
+})();
+evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });
diff --git a/js/src/jit-test/tests/xdr/bug1390856.js b/js/src/jit-test/tests/xdr/bug1390856.js
new file mode 100644
index 0000000000..b1dbbffdfa
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/bug1390856.js
@@ -0,0 +1,9 @@
+// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0
+
+// Test main thread encode/decode OOM
+oomTest(function() {
+ let t = cacheEntry(`function f() { function g() { }; return 3; };`);
+
+ evaluate(t, { sourceIsLazy: true, saveIncrementalBytecode: true });
+ evaluate(t, { sourceIsLazy: true, readBytecode: true });
+});
diff --git a/js/src/jit-test/tests/xdr/bug1427860.js b/js/src/jit-test/tests/xdr/bug1427860.js
new file mode 100644
index 0000000000..fd6d6f0411
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/bug1427860.js
@@ -0,0 +1,12 @@
+// |jit-test| --code-coverage; skip-if: !('oomAtAllocation' in this)
+
+let x = cacheEntry("function inner() { return 3; }; inner()");
+evaluate(x, { saveIncrementalBytecode: true });
+
+try {
+ // Fail XDR decode with partial script
+ oomAtAllocation(20);
+ evaluate(x, { loadBytecode: true });
+} catch { }
+
+getLcovInfo();
diff --git a/js/src/jit-test/tests/xdr/bug1585158.js b/js/src/jit-test/tests/xdr/bug1585158.js
new file mode 100644
index 0000000000..a64d5cf7aa
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/bug1585158.js
@@ -0,0 +1,12 @@
+gczeal(4);
+evalWithCache(`
+ var obj = { a: 1, b: 2 };
+ obj.a++;
+ assertEq(obj.a, 2);
+`);
+function evalWithCache(code) {
+ code = cacheEntry(code);
+ ctx_save = Object.create({}, { saveIncrementalBytecode: { value: true } });
+ var res1 = evaluate(code, ctx_save);
+ var res2 = evaluate(code, Object.create(ctx_save, {}));
+}
diff --git a/js/src/jit-test/tests/xdr/bug1607895.js b/js/src/jit-test/tests/xdr/bug1607895.js
new file mode 100644
index 0000000000..74050bddbd
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/bug1607895.js
@@ -0,0 +1,7 @@
+code = cacheEntry(`
+ function f() {
+ (function () {})
+ };
+ f()
+ `);
+evaluate(code, { saveIncrementalBytecode: { value: true } });
diff --git a/js/src/jit-test/tests/xdr/bug1790615.js b/js/src/jit-test/tests/xdr/bug1790615.js
new file mode 100644
index 0000000000..39a914345a
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/bug1790615.js
@@ -0,0 +1,19 @@
+// |jit-test| --fuzzing-safe; --cpu-count=2; --ion-offthread-compile=off; skip-if: !isAsmJSCompilationAvailable()
+
+load(libdir + "asserts.js");
+
+function asmCompile() {
+ var f = Function.apply(null, arguments);
+ return f;
+}
+var code = asmCompile('glob', 'imp', 'b', `
+"use asm";
+function f(i,j) {
+ i=i|0;
+ j=j|0;
+}
+return f
+`);
+let g80 = newGlobal({newCompartment: true});
+// Compiling to a Stencil XDR should fail because XDR encoding does not support asm.js
+assertThrowsInstanceOf(() => g80.compileToStencilXDR(code, {}), g80.Error); \ No newline at end of file
diff --git a/js/src/jit-test/tests/xdr/class-relazify.js b/js/src/jit-test/tests/xdr/class-relazify.js
new file mode 100644
index 0000000000..290202208f
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/class-relazify.js
@@ -0,0 +1,15 @@
+var code = cacheEntry(`
+function f() {
+ class A {
+ constructor() {}
+ }
+ new A();
+ relazifyFunctions();
+ new A();
+ relazifyFunctions();
+}
+f();
+`);
+
+evaluate(code, {saveIncrementalBytecode: { value: true } });
+evaluate(code, {loadBytecode: { value: true } });
diff --git a/js/src/jit-test/tests/xdr/classes.js b/js/src/jit-test/tests/xdr/classes.js
new file mode 100644
index 0000000000..3b83e41a48
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/classes.js
@@ -0,0 +1,36 @@
+load(libdir + 'bytecode-cache.js');
+
+// Prevent relazification triggered by some zeal modes. Relazification can cause
+// test failures because lazy functions are XDR'd differently.
+gczeal(0);
+
+var test = "new class extends class { } { constructor() { super(); } }()";
+evalWithCache(test, { assertEqBytecode : true });
+
+var test = "new class { method() { super.toString(); } }().method()";
+evalWithCache(test, { assertEqBytecode : true });
+
+// Test class constructor in lazy function
+var test = `
+ function f(delazify) {
+ function inner1() {
+ class Y {
+ constructor() {}
+ }
+ }
+
+ function inner2() {
+ class Y {
+ constructor() {}
+ field1 = "";
+ }
+ }
+
+ if (delazify) {
+ inner1();
+ inner2();
+ }
+ }
+ f(generation > 0);
+`;
+evalWithCache(test, {});
diff --git a/js/src/jit-test/tests/xdr/debug-hook.js b/js/src/jit-test/tests/xdr/debug-hook.js
new file mode 100644
index 0000000000..3abb138a80
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/debug-hook.js
@@ -0,0 +1,20 @@
+var g = newGlobal({ newCompartment: true });
+var dbg = new Debugger(g);
+var count = 0;
+
+dbg.onNewScript = script => {
+ count++;
+};
+
+var stencil = g.compileToStencil(`"a"`);
+g.evalStencil(stencil);
+assertEq(count, 1);
+
+count = 0;
+dbg.onNewScript = script => {
+ count++;
+};
+
+var stencilXDR = g.compileToStencilXDR(`"b"`);
+g.evalStencilXDR(stencilXDR);
+assertEq(count, 1);
diff --git a/js/src/jit-test/tests/xdr/debug-lazy.js b/js/src/jit-test/tests/xdr/debug-lazy.js
new file mode 100644
index 0000000000..8c0405742d
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/debug-lazy.js
@@ -0,0 +1,19 @@
+load(libdir + 'bytecode-cache.js');
+
+// Ensure that if a function is encoded when non-lazy but relazifiable, then
+// decoded, the resulting script is marked as being non-lazy so that when the
+// debugger tries to delazify things it doesn't get all confused. We just use
+// findScripts() to trigger debugger delazification; we don't really care about
+// the scripts themselves.
+function checkAfter(ctx) {
+ var dbg = new Debugger(ctx.global);
+ var allScripts = dbg.findScripts();
+ assertEq(allScripts.length == 0, false);
+}
+
+test = `
+ function f() { return true; };
+ f();
+ `
+evalWithCache(test, { assertEqBytecode: true, assertEqResult: true,
+ checkAfter: checkAfter, newCompartment: true});
diff --git a/js/src/jit-test/tests/xdr/decode-off-thread.js b/js/src/jit-test/tests/xdr/decode-off-thread.js
new file mode 100644
index 0000000000..cacef62e46
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/decode-off-thread.js
@@ -0,0 +1,54 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+function evalWithCacheLoadOffThread(code, ctx) {
+ ctx = ctx || {};
+ ctx = Object.create(ctx, {
+ fileName: { value: "evalWithCacheCode.js" },
+ lineNumber: { value: 0 }
+ });
+ code = code instanceof Object ? code : cacheEntry(code);
+
+ // We create a new global ...
+ if (!("global" in ctx))
+ ctx.global = newGlobal();
+
+ var ctx_save = Object.create(ctx, {
+ saveIncrementalBytecode: { value: true }
+ });
+
+ ctx.global.generation = 0;
+ evaluate(code, ctx_save);
+
+ offThreadDecodeStencil(code, ctx);
+ ctx.global.eval(`
+stencil = finishOffThreadStencil();
+evalStencil(stencil);
+`);
+}
+
+var test;
+
+// Decode a constant.
+test = `
+ 1;
+`;
+evalWithCacheLoadOffThread(test, {});
+
+// Decode object literals.
+test = `
+ var obj = { a: 1, b: 2 };
+ obj.a++;
+ assertEq(obj.a, 2);
+`;
+evalWithCacheLoadOffThread(test, {});
+
+// Decode functions.
+test = `
+ function g() {
+ return function f() { return 1; };
+ };
+ assertEq(g()(), 1);
+`;
+evalWithCacheLoadOffThread(test, {});
+
+
diff --git a/js/src/jit-test/tests/xdr/delazifications-atoms.js b/js/src/jit-test/tests/xdr/delazifications-atoms.js
new file mode 100644
index 0000000000..91b9603a5b
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/delazifications-atoms.js
@@ -0,0 +1,14 @@
+let s = `
+function f1() { return "Atom_f1"; }
+function f2() { return "Atom_f2"; }
+function f3() { return "Atom_f3"; }
+
+assertEq(f1(), "Atom_f1");
+assertEq(f2(), "Atom_f2");
+assertEq(f3(), "Atom_f3");
+`;
+
+let c = cacheEntry(s);
+let g = newGlobal();
+evaluate(c, {saveIncrementalBytecode:true});
+evaluate(c, {global:g, loadBytecode:true});
diff --git a/js/src/jit-test/tests/xdr/delazifications-nest.js b/js/src/jit-test/tests/xdr/delazifications-nest.js
new file mode 100644
index 0000000000..0a8fc23a58
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/delazifications-nest.js
@@ -0,0 +1,70 @@
+load(libdir + 'bytecode-cache.js');
+
+let test = `
+// Put some unused atoms in the initial stencil, to verify that atoms are
+// correctly mapped while merging stencils.
+if (false) {
+ "unused text1";
+ "unused text2";
+ "unused text3";
+ "unused text4";
+ "unused text5";
+}
+
+var result = 0;
+
+function func() {
+ var anonFunc = function() {
+ // Method/accessor as inner-inner function.
+ var obj = {
+ method(name) {
+ // Test object literal.
+ var object = {
+ propA: 9,
+ propB: 10,
+ propC: 11,
+ };
+
+ // Test object property access.
+ return object["prop" + name];
+ },
+ get prop1() {
+ return 200;
+ },
+ set prop2(value) {
+ result += value;
+ }
+ };
+ result += obj.prop1;
+ obj.prop2 = 3000;
+ result += obj.method("B");
+ };
+
+ function anotherFunc() {
+ return 40000;
+ }
+
+ function unused() {
+ }
+
+ class MyClass {
+ constructor() {
+ result += 500000;
+ }
+ }
+
+ // Functions inside arrow parameters, that are parsed multiple times.
+ const arrow = (a = (b = c => { result += 6000000 }) => b) => a()();
+
+ // Delazify in the different order as definition.
+ new MyClass();
+ anonFunc();
+ result += anotherFunc();
+ arrow();
+}
+func();
+
+result;
+`;
+
+evalWithCache(test, { incremental: true, oassertEqResult : true });
diff --git a/js/src/jit-test/tests/xdr/delazify-findScript-lineCount.js b/js/src/jit-test/tests/xdr/delazify-findScript-lineCount.js
new file mode 100644
index 0000000000..7846f1128b
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/delazify-findScript-lineCount.js
@@ -0,0 +1,54 @@
+function testDelazify(i) {
+ var g = newGlobal({ newCompartment: true });
+ var dbg = new Debugger(g);
+
+ dbg.onDebuggerStatement = (frame) => {
+ var allScripts = dbg.findScripts();
+ var j = 0;
+ for (let script of allScripts) {
+ if (i == -1 || i == j) {
+ // Delazify.
+ try {
+ script.lineCount;
+ } catch (e) {
+ }
+ }
+ j++;
+ }
+ };
+
+ var cache = cacheEntry(`
+function f(
+ a = (
+ b = (
+ c = function g() {
+ },
+ ) => {
+ },
+ d = (
+ e = (
+ f = (
+ ) => {
+ },
+ ) => {
+ },
+ ) => {
+ },
+ ) => {
+ },
+) {
+}
+debugger;
+`);
+
+ evaluate(cache, { global: g, saveIncrementalBytecode: true });
+ evaluate(cache, { global: g, loadBytecode: true });
+}
+
+// Delazify all.
+testDelazify(-1);
+
+// Delazify specific function and its ancestor.
+for (var i = 0; i < 30; i++) {
+ testDelazify(i);
+}
diff --git a/js/src/jit-test/tests/xdr/delazify-findScript-parameterNames.js b/js/src/jit-test/tests/xdr/delazify-findScript-parameterNames.js
new file mode 100644
index 0000000000..5ee955a40b
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/delazify-findScript-parameterNames.js
@@ -0,0 +1,54 @@
+function testDelazify(i) {
+ var g = newGlobal({ newCompartment: true });
+ var dbg = new Debugger(g);
+
+ dbg.onDebuggerStatement = (frame) => {
+ var allScripts = dbg.findScripts();
+ var j = 0;
+ for (let script of allScripts) {
+ if (i == -1 || i == j) {
+ // Delazify.
+ try {
+ script.parameterNames;
+ } catch (e) {
+ }
+ }
+ j++;
+ }
+ };
+
+ var cache = cacheEntry(`
+function f(
+ a = (
+ b = (
+ c = function g() {
+ },
+ ) => {
+ },
+ d = (
+ e = (
+ f = (
+ ) => {
+ },
+ ) => {
+ },
+ ) => {
+ },
+ ) => {
+ },
+) {
+}
+debugger;
+`);
+
+ evaluate(cache, { global: g, saveIncrementalBytecode: true });
+ evaluate(cache, { global: g, loadBytecode: true });
+}
+
+// Delazify all.
+testDelazify(-1);
+
+// Delazify specific function and its ancestor.
+for (var i = 0; i < 30; i++) {
+ testDelazify(i);
+}
diff --git a/js/src/jit-test/tests/xdr/force-full-parse.js b/js/src/jit-test/tests/xdr/force-full-parse.js
new file mode 100644
index 0000000000..4012677055
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/force-full-parse.js
@@ -0,0 +1,19 @@
+load(libdir + 'bytecode-cache.js');
+
+var test = `
+function f() {
+ function f1() {
+ return 1;
+ }
+ function f2() {
+ return 20;
+ }
+ return f1() + f2();
+}
+function g() {
+ return 300;
+}
+f() + g();
+`;
+evalWithCache(test, { assertEqBytecode: true, assertEqResult : true,
+ forceFullParse: true, });
diff --git a/js/src/jit-test/tests/xdr/function-flags.js b/js/src/jit-test/tests/xdr/function-flags.js
new file mode 100644
index 0000000000..62c69752b7
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/function-flags.js
@@ -0,0 +1,40 @@
+load(libdir + 'bytecode-cache.js');
+
+var test;
+
+// Ensure that if a function is encoded we don't encode its "name
+// resolved" flag.
+test = `
+ function f() { delete f.name; return f.hasOwnProperty('name'); }
+ f();
+ `
+evalWithCache(test, { assertEqBytecode: true, assertEqResult: true });
+
+test = `
+ function f() { return f.hasOwnProperty('name'); }
+ f();
+ `
+evalWithCache(test, { assertEqBytecode: true, assertEqResult: true });
+
+// Ensure that if a function is encoded we don't encode its "length
+// resolved" flag.
+test = `
+ function f() { delete f.length; return f.hasOwnProperty('length'); }
+ f();
+ `
+evalWithCache(test, { assertEqBytecode: true, assertEqResult: true });
+
+test = `
+ function f() { return f.hasOwnProperty('length'); }
+ f();
+ `
+evalWithCache(test, { assertEqBytecode: true, assertEqResult: true });
+
+// And make sure our bytecode is actually not reflecting the flags,
+// not that we ignore them on decode.
+test = `
+ function f() { return f.hasOwnProperty('length') || f.hasOwnProperty('name'); }
+ f();
+ `
+evalWithCache(test, { assertEqBytecode: true, assertEqResult: true });
+
diff --git a/js/src/jit-test/tests/xdr/incremental-encoder.js b/js/src/jit-test/tests/xdr/incremental-encoder.js
new file mode 100644
index 0000000000..d8a97f6327
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/incremental-encoder.js
@@ -0,0 +1,66 @@
+// |jit-test| skip-if: isLcovEnabled()
+
+load(libdir + 'bytecode-cache.js');
+var test = "";
+
+// Check that without cloneSingleton, we can safely encode object literal which
+// have mutations.
+test = `
+ var obj = { a: 1, b: 2 };
+ obj.a++;
+ assertEq(obj.a, 2);
+`;
+evalWithCache(test, {
+ incremental: true,
+ assertEqResult : true
+});
+
+
+// Encode lazy functions.
+test = `
+ function f() { return 1; };
+ 1;
+`;
+evalWithCache(test, {
+ incremental: true,
+ assertEqResult : true
+});
+
+
+// Encode delazified functions.
+test = `
+ function f() { return 1; };
+ f();
+`;
+evalWithCache(test, {
+ incremental: true,
+ assertEqResult : true
+});
+
+// Encode inner functions.
+test = `
+ function g() {
+ return function f() { return 1; };
+ };
+ g()();
+`;
+evalWithCache(test, {
+ incremental: true,
+ assertEqResult : true
+});
+
+// Extra zeal GCs can cause isRelazifiableFunction() to become true after we
+// record its value by throwing away JIT code for the function.
+gczeal(0);
+
+// Relazified functions are encoded as non-lazy-script, when the encoding is
+// incremental.
+test = `
+ assertEq(isLazyFunction(f), generation == 0 || generation == 3);
+ function f() { return isRelazifiableFunction(f); };
+ expect = f();
+ assertEq(isLazyFunction(f), false);
+ relazifyFunctions(f);
+ assertEq(isLazyFunction(f), expect);
+`;
+evalWithCache(test, { incremental: true });
diff --git a/js/src/jit-test/tests/xdr/incremental-oom.js b/js/src/jit-test/tests/xdr/incremental-oom.js
new file mode 100644
index 0000000000..ef202d112d
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/incremental-oom.js
@@ -0,0 +1,10 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+// Delazify a function while encoding bytecode.
+oomTest(() => {
+ let code = cacheEntry(`
+ function f() { }
+ f();
+ `);
+ evaluate(code, { saveIncrementalBytecode: true });
+});
diff --git a/js/src/jit-test/tests/xdr/lazy-class-definition.js b/js/src/jit-test/tests/xdr/lazy-class-definition.js
new file mode 100644
index 0000000000..ac29c9acf0
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/lazy-class-definition.js
@@ -0,0 +1,51 @@
+load(libdir + 'bytecode-cache.js');
+
+// Bury the class definition deep in a lazy function to hit edge cases of lazy
+// script handling.
+
+test = `
+ function f1() {
+ function f2() {
+ function f3() {
+ class C {
+ constructor() {
+ this.x = 42;
+ }
+ }
+ return new C;
+ }
+ return f3();
+ }
+ return f2();
+ }
+ if (generation >= 2) {
+ assertEq(f1().x, 42);
+ }
+`;
+evalWithCache(test, {});
+
+// NOTE: Fields currently force full parsing, but this test may be more
+// interesting in future.
+test = `
+ function f1() {
+ function f2() {
+ function f3() {
+ class C {
+ y = 12;
+
+ constructor() {
+ this.x = 42;
+ }
+ }
+ return new C;
+ }
+ return f3();
+ }
+ return f2();
+ }
+ if (generation >= 2) {
+ assertEq(f1().x, 42);
+ assertEq(f1().y, 12);
+ }
+`;
+evalWithCache(test, {});
diff --git a/js/src/jit-test/tests/xdr/lazy.js b/js/src/jit-test/tests/xdr/lazy.js
new file mode 100644
index 0000000000..00bcf1e306
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/lazy.js
@@ -0,0 +1,164 @@
+load(libdir + 'bytecode-cache.js');
+var test = "";
+var checkAfter;
+
+// code a function which has both used and unused inner functions.
+test = (function () {
+ function f(x) {
+ function ifTrue() {
+ return true;
+ };
+ function ifFalse() {
+ return false;
+ };
+
+ if (x) return ifTrue();
+ else return ifFalse();
+ }
+
+ return f.toString() + "; f(true)";
+})();
+evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });
+
+// code a function which uses different inner functions based on the generation.
+test = (function () {
+ function f(x) {
+ function ifTrue() {
+ return true;
+ };
+ function ifFalse() {
+ return false;
+ };
+
+ if (x) return ifTrue();
+ else return ifFalse();
+ }
+
+ return f.toString() + "; f((generation % 2) == 0)";
+})();
+evalWithCache(test, { });
+
+// Code a function which has an enclosing scope.
+test = (function () {
+ function f() {
+ var upvar = "";
+ function g() { upvar += ""; return upvar; }
+ return g;
+ }
+
+ return f.toString() + "; f()();";
+})();
+evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });
+
+// Code a lazy function which has an enclosing scope.
+test = (function () {
+ function f() {
+ var upvar = "";
+ function g() { upvar += ""; return upvar; }
+ return g;
+ }
+
+ return f.toString() + "; f();";
+})();
+evalWithCache(test, { assertEqBytecode: true });
+
+// (basic/bug535930) Code an enclosing scope which is a Call object.
+test = (function () {
+ return "(" + (function () {
+ p = function () {
+ Set()
+ };
+ var Set = function () {};
+ for (var x = 0; x < 5; x++) {
+ Set = function (z) {
+ return function () {
+ [z]
+ }
+ } (x)
+ }
+ }).toString() + ")()";
+})();
+evalWithCache(test, { assertEqBytecode: true });
+
+// Code an arrow function, and execute it.
+test = (function () {
+ function f() {
+ var g = (a) => a + a;
+ return g;
+ }
+
+ return f.toString() + "; f()(1);";
+})();
+evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });
+
+// Code an arrow function, and do not execute it.
+test = (function () {
+ function f() {
+ var g = (a) => a + a;
+ return g;
+ }
+
+ return f.toString() + "; f();";
+})();
+evalWithCache(test, { assertEqBytecode: true });
+
+// Extra zeal GCs can cause isRelazifiableFunction() to become true after we
+// record its value by throwing away JIT code for the function.
+gczeal(0);
+
+// Ensure that decoded functions can be relazified.
+test = "function f() { }; f();"
+ + "assertEq(isLazyFunction(f), false);"
+ + "var expect = isRelazifiableFunction(f);";
+checkAfter = function (ctx) {
+ relazifyFunctions(); // relazify f, if possible.
+ evaluate("assertEq(isLazyFunction(f), expect);", ctx);
+};
+evalWithCache(test, {
+ assertEqBytecode: true, // Check that we re-encode the same thing.
+ assertEqResult: true, // The function should remain relazifiable, if it was
+ // during the first run.
+ checkAfter: checkAfter // Check that relazifying the restored function works
+ // if the original was relazifiable.
+});
+
+// Ensure that decoded functions can be relazified, even if they have free
+// variables.
+test = "function f() { return isRelazifiableFunction(f) }; var expect = f();"
+ + "assertEq(isLazyFunction(f), false);"
+ + "expect";
+checkAfter = function (ctx) {
+ relazifyFunctions(); // relazify f, if possible.
+ evaluate("assertEq(isLazyFunction(f), expect);", ctx);
+};
+evalWithCache(test, {
+ assertEqBytecode: true, // Check that we re-encode the same thing.
+ assertEqResult: true, // The function should remain relazifiable, if it was
+ // during the first run.
+ checkAfter: checkAfter // Check that relazifying the restored function works
+ // if the original was relazifiable.
+});
+
+// Ensure that if a function is encoded when non-lazy but relazifiable, then
+// decoded, relazified, and then delazified, the result actually works.
+test = `
+ function f() { return true; };
+ var canBeLazy = isRelazifiableFunction(f) || isLazyFunction(f);
+ relazifyFunctions();
+ assertEq(isLazyFunction(f), canBeLazy);
+ f()`
+evalWithCache(test, { assertEqBytecode: true, assertEqResult: true });
+
+// And more of the same, in a slightly different way
+var g1 = newGlobal({ cloneSingletons: true });
+var g2 = newGlobal();
+var res = "function f(){}";
+var code = cacheEntry(res + "; f();");
+evaluate(code, {global:g1, saveIncrementalBytecode: {value: true}});
+evaluate(code, {global:g2, loadBytecode: true});
+gc();
+assertEq(g2.f.toString(), res);
+
+// Another relazification case.
+var src = "function f() { return 3; }; f(); relazifyFunctions(); 4";
+evalWithCache(src, {assertEqBytecode: true, assertEqResult: true});
diff --git a/js/src/jit-test/tests/xdr/load-nonsyntactic.js b/js/src/jit-test/tests/xdr/load-nonsyntactic.js
new file mode 100644
index 0000000000..a5a2e838ce
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/load-nonsyntactic.js
@@ -0,0 +1,55 @@
+var nonSyntacticEnvironment = { field: 10 };
+
+var source = `
+function f() {
+ function g() {
+ function h() {
+ // Load out of non syntactic environment.
+ return field;
+ }
+ return h();
+ }
+ return g();
+}
+
+f()`
+
+function check(before, after) {
+ var code = cacheEntry(source);
+ var res = evaluate(code, before);
+ assertEq(res, 10);
+ res = evaluate(code, after);
+ assertEq(res, 10);
+}
+
+
+check({ envChainObject: nonSyntacticEnvironment, saveIncrementalBytecode: true, },
+ { envChainObject: nonSyntacticEnvironment, loadBytecode: true })
+
+
+try {
+ var global = newGlobal();
+ global.field = 10;
+ check({ envChainObject: nonSyntacticEnvironment, saveIncrementalBytecode: true, },
+ { global: global, loadBytecode: true })
+
+ // Should have thrown
+ assertEq(false, true)
+} catch (e) {
+ assertEq(/Incompatible cache contents/.test(e.message), true);
+}
+
+try {
+ check({ global: global, saveIncrementalBytecode: true },
+ { envChainObject: nonSyntacticEnvironment, loadBytecode: true })
+
+ // Should have thrown
+ assertEq(false, true)
+} catch (e) {
+ assertEq(/Incompatible cache contents/.test(e.message), true);
+}
+
+
+var nonSyntacticEnvironmentTwo = { field: 10 };
+check({ envChainObject: nonSyntacticEnvironment, saveIncrementalBytecode: true, },
+ { envChainObject: nonSyntacticEnvironmentTwo, loadBytecode: true })
diff --git a/js/src/jit-test/tests/xdr/module-exports.js b/js/src/jit-test/tests/xdr/module-exports.js
new file mode 100644
index 0000000000..2c1bff3172
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/module-exports.js
@@ -0,0 +1,19 @@
+const count = 10;
+
+let s = "";
+for (let i = 0; i < count; i++)
+ s += "export let e" + i + " = " + (i * i) + ";\n";
+let stencil = compileToStencilXDR(s, {module: true});
+let m = instantiateModuleStencilXDR(stencil);
+let a = registerModule('a', m);
+
+stencil = compileToStencilXDR("import * as ns from 'a'", {module: true});
+m = instantiateModuleStencilXDR(stencil);
+let b = registerModule('b', m);
+
+moduleLink(b);
+moduleEvaluate(b);
+
+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/xdr/module-imports.js b/js/src/jit-test/tests/xdr/module-imports.js
new file mode 100644
index 0000000000..2e77268917
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/module-imports.js
@@ -0,0 +1,25 @@
+const count = 10;
+
+let stencil = compileToStencilXDR("export let a = 1;", {module: true});
+let m = instantiateModuleStencilXDR(stencil);
+let a = registerModule('a', m);
+
+let s = "";
+for (let i = 0; i < count; i++) {
+ s += "import { a as i" + i + " } from 'a';\n";
+ s += "assertEq(i" + i + ", 1);\n";
+}
+
+stencil = compileToStencilXDR(s, {module: true});
+m = instantiateModuleStencilXDR(stencil);
+let b = registerModule('b', m);
+
+moduleLink(b);
+moduleEvaluate(b);
+
+
+stencil = compileToStencilXDR("import * as nsa from 'a'; import * as nsb from 'b';", {module: true});
+m = instantiateModuleStencilXDR(stencil);
+
+moduleLink(m);
+moduleEvaluate(m);
diff --git a/js/src/jit-test/tests/xdr/module-oom.js b/js/src/jit-test/tests/xdr/module-oom.js
new file mode 100644
index 0000000000..14aef8e0af
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/module-oom.js
@@ -0,0 +1,26 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+// OOM tests for xdr 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 stencil = compileToStencilXDR(sa, {module: true});
+ let m = instantiateModuleStencilXDR(stencil);
+ let a = registerModule('a', m);
+
+ stencil = compileToStencilXDR(sb, {module: true});
+ m = instantiateModuleStencilXDR(stencil);
+ let b = registerModule('b', m);
+});
diff --git a/js/src/jit-test/tests/xdr/module.js b/js/src/jit-test/tests/xdr/module.js
new file mode 100644
index 0000000000..ef518bcf85
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/module.js
@@ -0,0 +1,92 @@
+// |jit-test|
+
+load(libdir + "asserts.js");
+
+async function parseAndEvaluate(source) {
+ let stencil = compileToStencilXDR(source, {module: true});
+ let m = instantiateModuleStencilXDR(stencil);
+ moduleLink(m);
+ await moduleEvaluate(m);
+ return m;
+}
+
+(async () => {
+ // Check the evaluation of an empty module succeeds.
+ await parseAndEvaluate("");
+})();
+
+(async () => {
+ // Check that evaluation returns evaluation promise,
+ // and promise is always the same.
+ let stencil = compileToStencilXDR("1", {module: true});
+ let m = instantiateModuleStencilXDR(stencil);
+ moduleLink(m);
+ assertEq(typeof moduleEvaluate(m), "object");
+ assertEq(moduleEvaluate(m) instanceof Promise, true);
+ assertEq(moduleEvaluate(m), moduleEvaluate(m));
+})();
+
+(async () => {
+ // Check top level variables are initialized by evaluation.
+ let stencil = compileToStencilXDR("export var x = 2 + 2;", {module: true});
+ let m = instantiateModuleStencilXDR(stencil);
+ assertEq(typeof getModuleEnvironmentValue(m, "x"), "undefined");
+ moduleLink(m);
+ await moduleEvaluate(m);
+ assertEq(getModuleEnvironmentValue(m, "x"), 4);
+})();
+
+(async () => {
+ let m = await parseAndEvaluate("export let x = 2 * 3;");
+ assertEq(getModuleEnvironmentValue(m, "x"), 6);
+
+ // Set up a module to import from.
+ let stencil = compileToStencilXDR(
+ `var x = 1;
+ export { x };
+ export default 2;
+ export function f(x) { return x + 1; }`,
+ {module: true});
+ let mod = instantiateModuleStencilXDR(stencil);
+ let a = registerModule('a', mod);
+
+ // 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() {} };");
+
+ // Test default import
+ m = await parseAndEvaluate("import a from 'a'; export { a };")
+ assertEq(getModuleEnvironmentValue(m, "a"), 2);
+
+ // Test named import
+ m = await parseAndEvaluate("import { x as y } from 'a'; export { y };")
+ assertEq(getModuleEnvironmentValue(m, "y"), 1);
+
+ // Call exported function
+ m = await parseAndEvaluate("import { f } from 'a'; export let x = f(3);")
+ assertEq(getModuleEnvironmentValue(m, "x"), 4);
+
+ // Import access in functions
+ m = await parseAndEvaluate("import { x } from 'a'; function f() { return x; }")
+ let f = getModuleEnvironmentValue(m, "f");
+ assertEq(f(), 1);
+})();
+drainJobQueue();
diff --git a/js/src/jit-test/tests/xdr/off-thread-inner-fcn.js b/js/src/jit-test/tests/xdr/off-thread-inner-fcn.js
new file mode 100644
index 0000000000..4112428092
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/off-thread-inner-fcn.js
@@ -0,0 +1,11 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+offThreadCompileToStencil(`function outer() {
+ // enough inner functions to make JS::Vector realloc
+ let inner1 = () => {};
+ let inner2 = () => {};
+ let inner3 = () => {};
+ let inner4 = () => {};
+ let inner5 = () => {};
+} `);
+const stencil = finishOffThreadStencil();
diff --git a/js/src/jit-test/tests/xdr/option-mismatch.js b/js/src/jit-test/tests/xdr/option-mismatch.js
new file mode 100644
index 0000000000..b6b27fffe5
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/option-mismatch.js
@@ -0,0 +1,23 @@
+var source = `
+function f() {
+ function g() {
+ return 10;
+ }
+ return g();
+}
+
+f()`
+
+var code = cacheEntry(source);
+
+
+var res = evaluate(code, { saveIncrementalBytecode: true, })
+assertEq(res, 10)
+
+try {
+ // We should throw here because we have incompatible options!
+ res = evaluate(code, { loadBytecode: true, sourceIsLazy: true })
+ assertEq(true, false);
+} catch (e) {
+ assertEq(/Incompatible cache contents/.test(e.message), true);
+} \ No newline at end of file
diff --git a/js/src/jit-test/tests/xdr/private-fields.js b/js/src/jit-test/tests/xdr/private-fields.js
new file mode 100644
index 0000000000..52b7bdd5aa
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/private-fields.js
@@ -0,0 +1,9 @@
+load(libdir + 'bytecode-cache.js');
+
+function test() {
+ class A {
+ #x;
+ }
+};
+
+evalWithCache(test.toString(), { assertEqBytecode: true, assertEqResult: true });
diff --git a/js/src/jit-test/tests/xdr/relazify.js b/js/src/jit-test/tests/xdr/relazify.js
new file mode 100644
index 0000000000..f867071d88
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/relazify.js
@@ -0,0 +1,31 @@
+// |jit-test| skip-if: isLcovEnabled()
+
+load(libdir + 'bytecode-cache.js');
+var test = "";
+gczeal(0);
+
+// Check that a GC can relazify decoded functions.
+//
+// Generations 0 and 3 are executed from the source, thus f is not executed yet.
+// Generations 1 and 2 are decoded, thus we recorded the delazified f function.
+test = `
+ function f() { return 1; };
+ assertEq(isLazyFunction(f), generation == 0 || generation == 3);
+ f();
+ expect = isRelazifiableFunction(f);
+ assertEq(isLazyFunction(f), false);
+`;
+evalWithCache(test, {
+ checkAfter: function (ctx) {
+ relazifyFunctions(); // relazify f, if possible.
+ evaluate("assertEq(isLazyFunction(f), expect);", ctx);
+ }
+});
+
+evalWithCache(test, {
+ incremental: true,
+ checkAfter: function (ctx) {
+ relazifyFunctions(); // relazify f, if possible.
+ evaluate("assertEq(isLazyFunction(f), expect);", ctx);
+ }
+});
diff --git a/js/src/jit-test/tests/xdr/runonce.js b/js/src/jit-test/tests/xdr/runonce.js
new file mode 100644
index 0000000000..b9e154ba0b
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/runonce.js
@@ -0,0 +1,6 @@
+load(libdir + "asserts.js")
+
+// Incremental XDR doesn't have run-once script restrictions.
+evaluate(cacheEntry(""), { saveIncrementalBytecode: true });
+evaluate(cacheEntry(""), { saveIncrementalBytecode: true, isRunOnce: false });
+evaluate(cacheEntry(""), { saveIncrementalBytecode: true, isRunOnce: true });
diff --git a/js/src/jit-test/tests/xdr/scope.js b/js/src/jit-test/tests/xdr/scope.js
new file mode 100644
index 0000000000..02a135509a
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/scope.js
@@ -0,0 +1,19 @@
+load(libdir + 'bytecode-cache.js');
+var test = "";
+
+// code a function which has both used and unused inner functions.
+test = (function () {
+ function f() {
+ var x = 3;
+ (function() {
+ with(obj) {
+ (function() {
+ assertEq(x, 2);
+ })();
+ }
+ })();
+ };
+
+ return "var obj = { x : 2 };" + f.toString() + "; f()";
+})();
+evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });
diff --git a/js/src/jit-test/tests/xdr/script-source-fields.js b/js/src/jit-test/tests/xdr/script-source-fields.js
new file mode 100644
index 0000000000..68d4f3d2a6
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/script-source-fields.js
@@ -0,0 +1,23 @@
+const code = `
+debugger;
+`;
+let g = newGlobal({newCompartment: true});
+let dbg = new Debugger(g);
+const bytes = g.compileToStencilXDR(code, {
+ fileName: "test-filename.js",
+ lineNumber: 12,
+ forceFullParse: true,
+ displayURL: "display-url.js",
+ sourceMapURL: "source-map-url.js",
+});
+let hit = false;
+dbg.onDebuggerStatement = frame => {
+ hit = true;
+ const source = frame.script.source;
+ assertEq(source.url, "test-filename.js");
+ assertEq(source.displayURL, "display-url.js");
+ assertEq(source.sourceMapURL, "source-map-url.js");
+ assertEq(source.startLine, 12);
+};
+g.evalStencilXDR(bytes, {});
+assertEq(hit, true);
diff --git a/js/src/jit-test/tests/xdr/share-bytecode.js b/js/src/jit-test/tests/xdr/share-bytecode.js
new file mode 100644
index 0000000000..d139c94cfa
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/share-bytecode.js
@@ -0,0 +1,12 @@
+
+let entry = cacheEntry(`
+ function f() { }
+ f() // Delazify
+ f // Return function object
+`);
+
+let f1 = evaluate(entry, {saveIncrementalBytecode: true});
+let f2 = evaluate(entry, {loadBytecode: true});
+
+// Reading from XDR should still deduplicate to same bytecode
+assertEq(hasSameBytecodeData(f1, f2), true)
diff --git a/js/src/jit-test/tests/xdr/stencil-arg.js b/js/src/jit-test/tests/xdr/stencil-arg.js
new file mode 100644
index 0000000000..94ec8f3edc
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/stencil-arg.js
@@ -0,0 +1,25 @@
+const tests = [
+ () => evalStencil(1),
+ () => evalStencil({}),
+ () => evalStencil([]),
+ () => evalStencilXDR(1),
+ () => evalStencilXDR({}),
+ () => evalStencilXDR([]),
+ () => instantiateModuleStencil(1),
+ () => instantiateModuleStencil({}),
+ () => instantiateModuleStencil([]),
+ () => instantiateModuleStencilXDR(1),
+ () => instantiateModuleStencilXDR({}),
+ () => instantiateModuleStencilXDR([]),
+];
+
+for (const test of tests) {
+ let caught = false;
+ try {
+ test();
+ } catch (e) {
+ assertEq(/Stencil( XDR)? object expected/.test(e.message), true);
+ caught = true;
+ }
+ assertEq(caught, true);
+}
diff --git a/js/src/jit-test/tests/xdr/stencil-can-lazily-parse-mismatch.js b/js/src/jit-test/tests/xdr/stencil-can-lazily-parse-mismatch.js
new file mode 100644
index 0000000000..351dbc9648
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/stencil-can-lazily-parse-mismatch.js
@@ -0,0 +1,71 @@
+// |jit-test| skip-if: isLcovEnabled()
+// Code coverage forces full-parse.
+
+load(libdir + 'asserts.js');
+
+const code = `var a = 10;`;
+
+function testCompile(sourceIsLazy1, sourceIsLazy2,
+ forceFullParse1, forceFullParse2) {
+ const stencil = compileToStencil(code, { sourceIsLazy: sourceIsLazy1,
+ forceFullParse: forceFullParse1 });
+ // The laziness options are ignored for instantiation, and no error is thrown.
+ evalStencil(stencil, { sourceIsLazy: sourceIsLazy2,
+ forceFullParse: forceFullParse2 });
+}
+
+function testOffThreadCompile(sourceIsLazy1, sourceIsLazy2,
+ forceFullParse1, forceFullParse2) {
+ offThreadCompileToStencil(code, { sourceIsLazy: sourceIsLazy1,
+ forceFullParse: forceFullParse1 });
+ const stencil = finishOffThreadStencil();
+ // The laziness options are ignored for instantiation, and no error is thrown.
+ evalStencil(stencil, { sourceIsLazy: sourceIsLazy2,
+ forceFullParse: forceFullParse2 });
+}
+
+function testXDR(sourceIsLazy1, sourceIsLazy2,
+ forceFullParse1, forceFullParse2) {
+ const xdr = compileToStencilXDR(code, { sourceIsLazy: sourceIsLazy1,
+ forceFullParse: forceFullParse1 });
+ // The compile options are ignored when decoding, and no error is thrown.
+ evalStencilXDR(xdr, { sourceIsLazy: sourceIsLazy2,
+ forceFullParse: forceFullParse2 });
+}
+
+function testOffThreadXDR(sourceIsLazy1, sourceIsLazy2,
+ forceFullParse1, forceFullParse2) {
+ const t = cacheEntry(code);
+ evaluate(t, { sourceIsLazy: sourceIsLazy1,
+ forceFullParse: forceFullParse1,
+ saveIncrementalBytecode: true });
+
+ // The compile options are ignored when decoding, and no error is thrown.
+ offThreadDecodeStencil(t, { sourceIsLazy: sourceIsLazy2,
+ forceFullParse: forceFullParse2 });
+ const stencil = finishOffThreadStencil();
+
+ // The laziness options are ignored for instantiation, and no error is thrown.
+ evalStencil(stencil, { sourceIsLazy: sourceIsLazy2,
+ forceFullParse: forceFullParse2 });
+}
+
+const optionsList = [
+ [true, false, false, false],
+ [false, true, false, false],
+ [false, false, true, false],
+ [false, false, false, true],
+ [true, false, true, false],
+ [false, true, false, true],
+];
+
+for (const options of optionsList) {
+ testCompile(...options);
+ if (helperThreadCount() > 0) {
+ testOffThreadCompile(...options);
+ }
+ testXDR(...options);
+ if (helperThreadCount() > 0) {
+ testOffThreadXDR(...options);
+ }
+}
diff --git a/js/src/jit-test/tests/xdr/stencil-oom.js b/js/src/jit-test/tests/xdr/stencil-oom.js
new file mode 100644
index 0000000000..f57e8f82f8
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/stencil-oom.js
@@ -0,0 +1,12 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+const sa = `
+function f(x, y) { return x + y }
+let a = 10, b = 20;
+f(a, b);
+`;
+
+oomTest(() => {
+ let stencil = compileToStencilXDR(sa);
+ evalStencilXDR(stencil);
+});
diff --git a/js/src/jit-test/tests/xdr/stencil.js b/js/src/jit-test/tests/xdr/stencil.js
new file mode 100644
index 0000000000..f7ac1c4012
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/stencil.js
@@ -0,0 +1,99 @@
+
+load(libdir + "asserts.js");
+
+/*
+ * This exercises stencil XDR encoding and decoding using a broad
+ * smoke testing.
+ *
+ * A set of scripts exercising various codepaths are XDR-encoded,
+ * then decoded, and then executed. Their output is compared to
+ * the execution of the scripts through a normal path and the
+ * outputs checked.
+ */
+
+/*
+ * Exercises global scope access and object literals, as well as some
+ * simple object destructuring.
+ */
+const testGlobal0 = 13;
+let testGlobal1 = undefined;
+var testGlobal2 = undefined;
+
+const SCRIPT_0 = `
+testGlobal1 = 123456789012345678901234567890n;
+testGlobal2 = {'foo':3, 'bar': [1, 2, 3],
+ '4': 'zing'};
+var testGlobal3 = /NewlyDefinedGlobal/;
+function testGlobal4(a, {b}) {
+ return a + b.foo + b['bar'].reduce((a,b) => (a+b), 0);
+ + b[4].length +
+ + testGlobal3.toString().length;
+};
+testGlobal4(Number(testGlobal1), {b:testGlobal2})
+`;
+
+/*
+ * Exercises function scopes, lexical scopes, var and let
+ * within them, and some longer identifiers, and array destructuring in
+ * arguments. Also contains some tiny atoms and globls access.
+ */
+const SCRIPT_1 = `
+function foo(a, b, c) {
+ var q = a * (b + c);
+ let bix = function (d, e) {
+ let x = a + d;
+ var y = e * b;
+ const a0 = q + x + y;
+ for (let i = 0; i < 3; i++) {
+ y = a0 + Math.PI + y;
+ }
+ return y;
+ };
+ function bang(d, [e, f]) {
+ let reallyLongIdentifierName = a + d;
+ var y = e * b;
+ const z = reallyLongIdentifierName + f;
+ return z;
+ }
+ return bix(1, 2) + bang(3, [4, 5, 6]);
+}
+foo(1, 2, 3)
+`;
+
+/*
+ * Exercises eval and with scopes, object destructuring, function rest
+ * arguments.
+ */
+const SCRIPT_2 = `
+function foo(with_obj, ...xs) {
+ const [x0, x1, ...xrest] = xs;
+ eval('var x2 = x0 + x1');
+ var sum = [];
+ with (with_obj) {
+ sum.push(x2 + xrest.length);
+ }
+ sum.push(x2 + xrest.length);
+ return sum;
+}
+foo({x2: 99}, 1, 2, 3, 4, 5, 6)
+`;
+
+function test_script(script_str) {
+ const eval_f = eval;
+ const options = {
+ fileName: "compileToStencilXDR-DATA.js",
+ lineNumber: 1,
+ forceFullParse: true,
+ };
+ const bytes = compileToStencilXDR(script_str, options);
+ const result = evalStencilXDR(bytes, options);
+ assertDeepEq(result, eval_f(script_str));
+}
+
+function tests() {
+ test_script(SCRIPT_0);
+ test_script(SCRIPT_1);
+ test_script(SCRIPT_2);
+}
+
+tests()
diff --git a/js/src/jit-test/tests/xdr/tagged-template-literals-2.js b/js/src/jit-test/tests/xdr/tagged-template-literals-2.js
new file mode 100644
index 0000000000..901d20017c
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/tagged-template-literals-2.js
@@ -0,0 +1,4 @@
+var code = cacheEntry("(x => JSON.stringify(x))`bar`;");
+var g = newGlobal({ cloneSingletons: true });
+assertEq("[\"bar\"]", evaluate(code, { global: g, saveIncrementalBytecode: true }));
+assertEq("[\"bar\"]", evaluate(code, { global: g, saveIncrementalBytecode: true }));
diff --git a/js/src/jit-test/tests/xdr/tagged-template-literals.js b/js/src/jit-test/tests/xdr/tagged-template-literals.js
new file mode 100644
index 0000000000..b37f3bbbf3
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/tagged-template-literals.js
@@ -0,0 +1,4 @@
+var code = cacheEntry("assertEq('bar', String.raw`bar`);");
+var g = newGlobal({ cloneSingletons: true });
+evaluate(code, { global: g, saveIncrementalBytecode: true });
+evaluate(code, { global: g, loadBytecode: true })
diff --git a/js/src/jit-test/tests/xdr/trivial.js b/js/src/jit-test/tests/xdr/trivial.js
new file mode 100644
index 0000000000..52ec42f397
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/trivial.js
@@ -0,0 +1,46 @@
+load(libdir + 'bytecode-cache.js');
+var test = "";
+
+// code a constant.
+test = "1;";
+evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });
+
+// code a string constant.
+test = "'string';";
+evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });
+
+// code a global variable access.
+test = "generation;";
+evalWithCache(test, { assertEqBytecode: true });
+
+// code an object constant.
+test = "var obj = { a: 1, b: 2 };";
+evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });
+
+// code object literal with mutations.
+test = "var obj = { a: 1, b: 2 }; obj.a++; assertEq(obj.a, 2);";
+evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });
+
+// code object literals with mutations.
+test = "var obj = { a: 1, b: { c: 3, d: 4 } }; obj.b.c++; assertEq(obj.b.c, 4);";
+evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });
+
+// code a function which is used.
+test = "function f() { return 1; }; f();";
+evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });
+
+// code a function which is not used.
+test = "function f() { return 1; }; 1;";
+evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });
+
+// code a function which has an object literal.
+test = "function f() { return { x: 2 }; }; f();";
+evalWithCache(test, { assertEqBytecode: true });
+
+// code call site object
+test = "function f(a) { return a; }; f`a${4}b`;";
+evalWithCache(test, { assertEqBytecode: true, checkFrozen: true});
+
+// code RegExp constants.
+test = "'fooooooo'.match(/(f.+)/)[1].replace(/o/g, 'O')";
+evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });