Environment-identity-03.js (3720B)
1 // |jit-test| skip-if: getBuildConfiguration("wasi") 2 // 3 // Two Environments nested in the same runtime scope share the correct tail of their parent chains. 4 5 // The compiler must be allowed to elide empty scopes and so forth, so this 6 // test does not check the number of unshared Environments. Instead, each test 7 // case identifies the expected innermost shared scope by the name of a 8 // variable in it. 9 10 var g = newGlobal({newCompartment: true}); 11 g.eval("function h() { debugger; }"); 12 var dbg = Debugger(g); 13 var hits, name, shared, unshared; 14 dbg.onDebuggerStatement = function (hframe) { 15 var frame = hframe.older; 16 17 // Find name in frame.environment. 18 var env, child = null; 19 for (env = frame.environment; env !== null; env = env.parent) { 20 if (env.names().indexOf(name) != -1) 21 break; 22 child = env; 23 } 24 assertEq(env !== null, true, "expected '" + name + "' to be in scope"); 25 assertEq(env, frame.environment.find(name), 26 "env.find should find the same frame as the written out search"); 27 28 if (hits === 0) { 29 // First hit. 30 shared = env; 31 unshared = child; 32 } else { 33 // Subsequent hit. 34 assertEq(env, shared, "the environment containing '" + name + "' should be shared"); 35 assertEq(child === null || unshared === null || unshared !== child, true, 36 "environments nested within the one containing '" + name + "' should not be shared"); 37 } 38 hits++; 39 }; 40 41 function test(sharedName, expectedHits, code) { 42 hits = 0; 43 name = sharedName; 44 shared = unshared = undefined; 45 g.eval(code); 46 assertEq(hits, expectedHits); 47 } 48 49 // Basic test cases. 50 // 51 // (The stray "a = b" assignments in these tests are to inhibit the flat closure 52 // optimization, which Environments expose. There's nothing really wrong with 53 // the optimization or with the debugger exposing it, but that's not what we 54 // want to test here.) 55 56 test("q", 2, "let q = function (a) { h(); }; q(1); q(2);"); 57 test("a", 2, "q = function (a) { (function (b) { h(); a = b; })(2); h(); }; q(1);"); 58 test("a", 2, "q = function (a) { h(); return function (b) { h(); a = b; }; }; q(1)(2);"); 59 test("n", 3, "q = function (n) { for (var i = 0; i < n; i++) { { let j = i; h(); } } }; q(3);"); 60 61 // A function with long dynamic and static chains. 62 var N = 80; 63 64 var code = "function f" + N + "(a" + N + ") {\neval('a0 + a1'); h();\n}\n"; 65 for (var i = N; --i >= 0;) { 66 var call = "f" + (i + 1) + "(a" + i + " - 1);\n"; 67 code = ("function f" + i + "(a" + i + ") {\n" + 68 code + 69 call + 70 "if (a" + i + " === 0) " + call + 71 "}\n"); 72 } 73 74 g.eval(code); 75 test("a0", 2, "f0(0);"); 76 test("a17", 2, "f0(17);"); 77 test("a" + (N-2), 2, "f0(" + (N-2) + ");"); 78 test("a" + (N-1), 2, "f0(" + (N-1) + ");"); 79 80 // A function with a short dynamic chain and a long static chain. 81 N = 60; 82 83 function DeepStaticShallowDynamic(i, n) { 84 var code = "function f" + i + "(a" + i + ") {\n"; 85 if (i >= n) 86 code += "eval('a1 + a2'); h();\n"; 87 else 88 code += "return " + DeepStaticShallowDynamic(i+1, n) + ";\n"; 89 code += "}"; 90 return code; 91 } 92 g.eval(DeepStaticShallowDynamic(1, N)); 93 94 function* range(start, stop) { 95 for (var i = start; i < stop; i++) 96 yield i; 97 } 98 99 function DSSDsplit(s) { 100 return ("var mid = f1" + [...range(0, s)].map((i) => "(" + i + ")").join("") + ";\n" + 101 "mid" + [...range(s, N)].map((i) => "(" + i + ")").join("") + ";\n" + 102 "mid" + [...range(s, N)].map((i) => "(" + i + ")").join("") + ";\n"); 103 } 104 105 test("a1", 2, DSSDsplit(1)); 106 test("a17", 2, DSSDsplit(17)); 107 test("a" + (N-2), 2, DSSDsplit(N-2)); 108 test("a" + (N-1), 2, DSSDsplit(N-1));