diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /js/src/jit-test/tests/xdr | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit-test/tests/xdr')
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 }); |