summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/environments
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/environments')
-rw-r--r--js/src/jit-test/tests/environments/bug1671563-strict.js25
-rw-r--r--js/src/jit-test/tests/environments/bug1671563.js24
-rw-r--r--js/src/jit-test/tests/environments/bug1671762.js20
-rw-r--r--js/src/jit-test/tests/environments/bug1710089.js38
-rw-r--r--js/src/jit-test/tests/environments/delete-global-var.js13
-rw-r--r--js/src/jit-test/tests/environments/evaluate_envChainObject.js32
-rw-r--r--js/src/jit-test/tests/environments/lexical-shadows-global-var.js9
-rw-r--r--js/src/jit-test/tests/environments/replace-global-var-with-getter.js13
-rw-r--r--js/src/jit-test/tests/environments/strict-eval-bindings.js221
9 files changed, 395 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/environments/bug1671563-strict.js b/js/src/jit-test/tests/environments/bug1671563-strict.js
new file mode 100644
index 0000000000..5b9e8112ee
--- /dev/null
+++ b/js/src/jit-test/tests/environments/bug1671563-strict.js
@@ -0,0 +1,25 @@
+// A `var` is `undefined` on entering a function body in strict mode too.
+
+"use strict";
+
+load(libdir + "asserts.js");
+
+function f(a = class C{}) {
+ var x;
+ return x;
+}
+assertEq(f(), undefined);
+
+function* g1(a = class C {}) {
+ var x;
+ assertEq(x, undefined);
+}
+g1().next();
+
+function* g2(a = class C {}) {
+ x;
+ let x;
+}
+assertThrowsInstanceOf(() => g2().next(), ReferenceError);
+
+
diff --git a/js/src/jit-test/tests/environments/bug1671563.js b/js/src/jit-test/tests/environments/bug1671563.js
new file mode 100644
index 0000000000..29a24fbcd9
--- /dev/null
+++ b/js/src/jit-test/tests/environments/bug1671563.js
@@ -0,0 +1,24 @@
+// The value of a `var` on entering a function is `undefined`, even if a
+// default expression uses the same stack slot for something else.
+
+load(libdir + "asserts.js");
+
+function f(a = class C{}) {
+ var x;
+ return x;
+}
+assertEq(f(), undefined);
+
+function* g1(a = class C {}) {
+ var x;
+ assertEq(x, undefined);
+}
+g1().next();
+
+function* g2(a = class C {}) {
+ x;
+ let x;
+}
+assertThrowsInstanceOf(() => g2().next(), ReferenceError);
+
+
diff --git a/js/src/jit-test/tests/environments/bug1671762.js b/js/src/jit-test/tests/environments/bug1671762.js
new file mode 100644
index 0000000000..3d7a452474
--- /dev/null
+++ b/js/src/jit-test/tests/environments/bug1671762.js
@@ -0,0 +1,20 @@
+(function() {
+ var v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v0a, v0b, v0c, v0d, v0e, v0f;
+ var v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v1a, v1b, v1c, v1d, v1e, v1f;
+ var v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v2a, v2b, v2c, v2d, v2e, v2f;
+ var v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v3a, v3b, v3c, v3d, v3e, v3f;
+ var v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v4a, v4b, v4c, v4d, v4e, v4f;
+ var v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v5a, v5b, v5c, v5d, v5e, v5f;
+ var v60, v61, v62, v63, v64, v65, v66, v67, v68, v69, v6a, v6b, v6c, v6d, v6e, v6f;
+ var v70, v71, v72, v73, v74, v75, v76, v77, v78, v79, v7a, v7b, v7c, v7d, v7e, v7f;
+ try { a1; } catch {} finally {}
+ var v80, v81, v82, v83, v84, v85, v86, v87, v88, v89, v8a, v8b, v8c, v8d, v8e, v8f;
+ var v90, v91, v92, v93, v94, v95, v96, v97, v98, v99, v9a, v9b, v9c, v9d, v9e, v9f;
+ var va0, va1, va2, va3, va4, va5, va6, va7, va8, va9, vaa, vab, vac, vad, vae, vaf;
+ var vb0, vb1, vb2, vb3, vb4, vb5, vb6, vb7, vb8, vb9, vba, vbb, vbc, vbd, vbe, vbf;
+ var vc0, vc1, vc2, vc3, vc4, vc5, vc6, vc7, vc8, vc9, vca, vcb, vcc, vcd, vce, vcf;
+ var vd0, vd1, vd2, vd3, vd4, vd5, vd6, vd7, vd8, vd9, vda, vdb, vdc, vdd, vde, vdf;
+ var ve0, ve1, ve2, ve3, ve4, ve5, ve6, ve7, ve8, ve9, vea, veb, vec, ved, vee, vef;
+ var vf0, vf1, vf2, vf3, vf4, vf5, vf6, vf7, vf8, vf9, vfa, vfb, vfc, vfd, vfe, vff;
+ var v100;
+})();
diff --git a/js/src/jit-test/tests/environments/bug1710089.js b/js/src/jit-test/tests/environments/bug1710089.js
new file mode 100644
index 0000000000..3bd78a14d3
--- /dev/null
+++ b/js/src/jit-test/tests/environments/bug1710089.js
@@ -0,0 +1,38 @@
+// |jit-test| skip-if: getBuildConfiguration("wasi")
+
+// Recursion depth reduced to allow PBL with debug build (hence larger
+// frames) to work.
+var iters = 75;
+
+// Generate a deeply nested version of:
+// function outer() {
+// var top_level_var = 42;
+// var x3 = 0;
+// function f2() {
+// var x2 = x3;
+// function f1() {
+// var x1 = x2;
+// function f0() {
+// var x0 = x1;
+// return top_level_var + x0;
+// }
+// return f0();
+// }
+// return f1();
+// }
+// return f2();
+// }
+
+var src = "return top_level_var + x0; "
+
+for (var i = 0; i < iters; i++) {
+ var def = "var x" + i + " = x" + (i+1) + "; ";
+ src = "function f" + i + "() { " + def + src + "} return f" + i + "();"
+}
+src = "var x" + iters + " = 0;" + src;
+src = "var top_level_var = 42; " + src;
+
+var outer = Function(src);
+for (var i = 0; i < 2; i++) {
+ assertEq(outer(), 42);
+}
diff --git a/js/src/jit-test/tests/environments/delete-global-var.js b/js/src/jit-test/tests/environments/delete-global-var.js
new file mode 100644
index 0000000000..03b128affe
--- /dev/null
+++ b/js/src/jit-test/tests/environments/delete-global-var.js
@@ -0,0 +1,13 @@
+let assert = assertEq;
+
+this.x = "OK";
+this.y = "FAIL";
+
+for (let i = 0; i < 50; i++) {
+ assert(x, "OK");
+ if (i === 40) {
+ this.x = "FAIL";
+ delete this.x;
+ this.x = "OK";
+ }
+}
diff --git a/js/src/jit-test/tests/environments/evaluate_envChainObject.js b/js/src/jit-test/tests/environments/evaluate_envChainObject.js
new file mode 100644
index 0000000000..d6edb1f43c
--- /dev/null
+++ b/js/src/jit-test/tests/environments/evaluate_envChainObject.js
@@ -0,0 +1,32 @@
+load(libdir + "asserts.js");
+
+// Test with envChainObject in current global.
+{
+ let global = newGlobal();
+ let envChainObject = { a: "test1" };
+ assertEq(evaluate("a", { global, envChainObject }), "test1");
+}
+
+// Test with envChainObject in target global.
+{
+ let global = newGlobal();
+ var envChainObject = global.evaluate('({a: "test2"})');
+ assertEq(envChainObject.a, "test2");
+ assertEq(evaluate("a", { global, envChainObject }), "test2");
+}
+
+// Unqualified variables objects are not allowed.
+
+if (!isProxy(evalcx(""))) {
+ // if --more-compartments option is not give, evalcx returns sandbox,
+ // which is unqualified variables object.
+ assertThrowsInstanceOf(() => {
+ evaluate("10", { envChainObject: evalcx("") });
+ }, Error);
+}
+
+// evalReturningScope returns NonSyntacticVariablesObject, which is unqualified
+// variables object.
+assertThrowsInstanceOf(() => {
+ evaluate("10", { envChainObject: evalReturningScope("1") });
+}, Error);
diff --git a/js/src/jit-test/tests/environments/lexical-shadows-global-var.js b/js/src/jit-test/tests/environments/lexical-shadows-global-var.js
new file mode 100644
index 0000000000..9a8a8792b6
--- /dev/null
+++ b/js/src/jit-test/tests/environments/lexical-shadows-global-var.js
@@ -0,0 +1,9 @@
+this.x = "OK";
+
+for (var i = 0; i < 50; i++) {
+ assertEq(x, "OK");
+ if (i === 40) {
+ this.x = "FAIL";
+ evaluate("let x = 'OK';");
+ }
+}
diff --git a/js/src/jit-test/tests/environments/replace-global-var-with-getter.js b/js/src/jit-test/tests/environments/replace-global-var-with-getter.js
new file mode 100644
index 0000000000..f79948933a
--- /dev/null
+++ b/js/src/jit-test/tests/environments/replace-global-var-with-getter.js
@@ -0,0 +1,13 @@
+let assert = assertEq;
+
+this.x = "OK";
+
+for (let i = 0; i < 50; i++) {
+ assert(x, "OK");
+ if (i === 40) {
+ this.x = "FAIL";
+ Object.defineProperty(this, "x", {
+ get() { return "OK"; }
+ });
+ }
+}
diff --git a/js/src/jit-test/tests/environments/strict-eval-bindings.js b/js/src/jit-test/tests/environments/strict-eval-bindings.js
new file mode 100644
index 0000000000..8e60574d6b
--- /dev/null
+++ b/js/src/jit-test/tests/environments/strict-eval-bindings.js
@@ -0,0 +1,221 @@
+// |jit-test| skip-if: !('disassemble' in this)
+// Strict direct eval supports static binding of identifiers.
+
+"use strict";
+
+// Check that a script contains a particular bytecode sequence.
+//
+// `actual` is the output of the `disassemble()` shell builtin.
+// `expected` is a semicolon-separated string of opcodes.
+// Can include regular expression syntax, e.g. "GetLocal .* x$"
+// to match a GetLocal instruction with ` x` at the end of the line.
+// `message` is a string to include in the error message if the test fails.
+//
+function assertBytecode(actual, expected, message) {
+ // Grab the opcode name and everything after to the end of the line. This
+ // intentionally includes the expression stack, as that is what makes the
+ // `GetLocal .* y$` trick work. The disassemble() output is like this:
+ //
+ // 00016: 10 GetLocal 0 # x y
+ //
+ let actualOps =
+ actual.split('\n')
+ .map(s => /^\d{5}: +\d+ +(.*)$/.exec(s)?.[1])
+ .filter(x => x !== undefined);
+
+ // Turn the expectations into regular expressions.
+ let expectedOps =
+ expected.split(';')
+ .map(s => {
+ s = s.trim();
+ // If the op is a single word, like `Dup`, add `\b` to rule out
+ // similarly named ops like `Dup2`.
+ if (/^\w+$/.test(s)) {
+ s += "\\b";
+ }
+ return new RegExp("^" + s);
+ });
+
+ // The condition on this for-loop is saying, "continue as long as the range
+ // [i..i+expectedOps.length] is entirely within in the actualOps array".
+ // Hence the rare use of `<=` in a for-loop!
+ for (let i = 0; i + expectedOps.length <= actualOps.length; i++) {
+ if (expectedOps.every((expectRegExp, j) => expectRegExp.test(actualOps[i + j]))) {
+ // Found a complete match.
+ return;
+ }
+ }
+ throw new Error(`Assertion failed: ${message}\nexpected ${uneval(expected)}, got:\n${actual}`);
+}
+
+
+// --- Tests
+
+var bytecode;
+
+// `var`s in strict eval code are statically bound as locals.
+eval(`
+ var pet = "ostrich";
+ bytecode = disassemble();
+ pet
+`);
+assertEq(globalThis.hasOwnProperty('pet'), false);
+assertBytecode(bytecode, 'String "ostrich"; SetLocal; Pop',
+ "`pet` is stored in a stack local");
+assertBytecode(bytecode, "GetLocal; SetRval; RetRval",
+ "`pet` is loaded from the local at the end of the eval code");
+
+// Same for top-level `function`s.
+eval(`
+ function banana() { return "potassium"; }
+ bytecode = disassemble();
+`);
+assertEq(globalThis.hasOwnProperty('banana'), false);
+assertBytecode(bytecode, 'Lambda .* banana; SetLocal; Pop',
+ "`banana` is stored in a stack local");
+
+// Same for let/const.
+eval(`
+ let a = "ushiko-san";
+ const b = "umao-san";
+ bytecode = disassemble();
+ [a, b]
+`);
+assertBytecode(bytecode, 'String "ushiko-san"; InitLexical; Pop',
+ "`let a` is stored in a stack local");
+assertBytecode(bytecode, 'String "umao-san"; InitLexical; Pop',
+ "`const b` is stored in a stack local");
+assertBytecode(bytecode, 'GetLocal .* a$; InitElemArray; GetLocal .* b$; InitElemArray',
+ "lexical variables are loaded from stack locals");
+
+// Same for arguments and locals in functions declared in strict eval code.
+let g = eval(`
+ function f(a) {
+ let x = 'x';
+ function g(b) {
+ let y = "wye";
+ return [f, a, x, g, b, y];
+ }
+ return g;
+ }
+ f();
+`);
+bytecode = disassemble(g);
+assertBytecode(bytecode, 'GetAliasedVar "f"',
+ "closed-over eval-scope `function` is accessed via aliased op");
+assertBytecode(bytecode, 'GetAliasedVar "a"',
+ "closed-over argument is accessed via aliased op");
+assertBytecode(bytecode, 'GetAliasedVar "x"',
+ "closed-over local `let` variable is accessed via aliased op");
+assertBytecode(bytecode, 'GetAliasedVar "g"',
+ "closed-over local `function` is accessed via aliased op");
+assertBytecode(bytecode, 'GetArg .* b$',
+ "non-closed-over arguments are optimized");
+assertBytecode(bytecode, 'GetLocal .* y$',
+ "non-closed-over locals are optimized");
+
+// Closed-over bindings declared in strict eval code are statically bound.
+var fac = eval(`
+ bytecode = disassemble();
+ function fac(x) { return x <= 1 ? 1 : x * fac(x - 1); }
+ fac
+`);
+assertBytecode(bytecode, 'SetAliasedVar "fac"',
+ "strict eval code accesses closed-over top-level function using aliased ops");
+assertBytecode(disassemble(fac), 'GetAliasedVar "fac"',
+ "function in strict eval accesses itself using aliased ops");
+
+// References to `this` in an enclosing method are statically bound.
+let obj = {
+ m(s) { return eval(s); }
+};
+let result = obj.m(`
+ bytecode = disassemble();
+ this;
+`);
+assertEq(result, obj);
+assertBytecode(bytecode, 'GetAliasedVar ".this"',
+ "strict eval in a method can access `this` using aliased ops");
+
+// Same for `arguments`.
+function fn_with_args() {
+ return eval(`
+ bytecode = disassemble();
+ arguments[0];
+ `);
+}
+assertEq(fn_with_args(117), 117);
+assertBytecode(bytecode, 'GetAliasedVar "arguments"',
+ "strict eval in a function can access `arguments` using aliased ops");
+
+// The frontend can emit GName ops in strict eval.
+result = eval(`
+ bytecode = disassemble();
+ fn_with_args;
+`);
+assertEq(result, fn_with_args);
+assertBytecode(bytecode, 'GetGName "fn_with_args"',
+ "strict eval code can optimize access to globals");
+
+// Even within a function.
+function test_globals_in_function() {
+ result = eval(`
+ bytecode = disassemble();
+ fn_with_args;
+ `);
+ assertEq(result, fn_with_args);
+ assertBytecode(bytecode, 'GetGName "fn_with_args"',
+ "strict eval code in a function can optimize access to globals");
+}
+test_globals_in_function();
+
+// Nested eval is no obstacle.
+{
+ let outer = "outer";
+ const f = function (code, a, b) {
+ return eval(code);
+ };
+ let result = f(`
+ eval("bytecode = disassemble();\\n" +
+ "outer += a + b;\\n");
+ `, 3, 4);
+ assertEq(outer, "outer7");
+ assertBytecode(bytecode, 'GetAliasedVar "outer"',
+ "access to outer bindings is optimized even through nested strict evals");
+ assertBytecode(bytecode, 'GetAliasedVar "a"',
+ "access to outer bindings is optimized even through nested strict evals");
+ assertBytecode(bytecode, 'SetAliasedVar "outer"',
+ "assignment to outer bindings is optimized even through nested strict evals");
+}
+
+// Assignment to an outer const is handled correctly.
+{
+ const doNotSetMe = "i already have a value, thx";
+ let f = eval(`() => { doNotSetMe = 34; }`);
+ assertBytecode(disassemble(f), 'ThrowSetConst "doNotSetMe"',
+ "assignment to outer const in strict eval code emits ThrowSetConst");
+}
+
+// OK, there are other scopes but let's just do one more: the
+// computed-property-name scope.
+{
+ let stashed;
+ (class C {
+ [(
+ eval(`
+ var secret = () => C;
+ stashed = () => secret;
+ `),
+ "method"
+ )]() {
+ return "ok";
+ }
+ });
+
+ bytecode = disassemble(stashed());
+ assertBytecode(bytecode, 'GetAliasedVar "C"',
+ "access to class name uses aliased ops");
+ let C = stashed()();
+ assertEq(new C().method(), "ok");
+}
+