michael@0: // Two Environments nested in the same runtime scope share the correct tail of their parent chains. michael@0: michael@0: // The compiler must be allowed to elide empty scopes and so forth, so this michael@0: // test does not check the number of unshared Environments. Instead, each test michael@0: // case identifies the expected innermost shared scope by the name of a michael@0: // variable in it. michael@0: michael@0: var g = newGlobal(); michael@0: g.eval("function h() { debugger; }"); michael@0: var dbg = Debugger(g); michael@0: var hits, name, shared, unshared; michael@0: dbg.onDebuggerStatement = function (hframe) { michael@0: var frame = hframe.older; michael@0: michael@0: // Find name in frame.environment. michael@0: var env, child = null; michael@0: for (env = frame.environment; env !== null; env = env.parent) { michael@0: if (env.names().indexOf(name) != -1) michael@0: break; michael@0: child = env; michael@0: } michael@0: assertEq(env !== null, true, "expected '" + name + "' to be in scope"); michael@0: assertEq(env, frame.environment.find(name), michael@0: "env.find should find the same frame as the written out search"); michael@0: michael@0: if (hits === 0) { michael@0: // First hit. michael@0: shared = env; michael@0: unshared = child; michael@0: } else { michael@0: // Subsequent hit. michael@0: assertEq(env, shared, "the environment containing '" + name + "' should be shared"); michael@0: assertEq(child === null || unshared === null || unshared !== child, true, michael@0: "environments nested within the one containing '" + name + "' should not be shared"); michael@0: } michael@0: hits++; michael@0: }; michael@0: michael@0: function test(sharedName, expectedHits, code) { michael@0: hits = 0; michael@0: name = sharedName; michael@0: shared = unshared = undefined; michael@0: g.eval(code); michael@0: assertEq(hits, expectedHits); michael@0: } michael@0: michael@0: // Basic test cases. michael@0: // michael@0: // (The stray "a = b" assignments in these tests are to inhibit the flat closure michael@0: // optimization, which Environments expose. There's nothing really wrong with michael@0: // the optimization or with the debugger exposing it, but that's not what we michael@0: // want to test here.) michael@0: michael@0: test("q", 2, "var q = function (a) { h(); }; q(1); q(2);"); michael@0: test("a", 2, "q = function (a) { (function (b) { h(); a = b; })(2); h(); }; q(1);"); michael@0: test("a", 2, "q = function (a) { h(); return function (b) { h(); a = b; }; }; q(1)(2);"); michael@0: test("n", 3, "q = function (n) { for (var i = 0; i < n; i++) { let (j = i) { h(); } } }; q(3);"); michael@0: michael@0: // A function with long dynamic and static chains. michael@0: var N = 80; michael@0: michael@0: var code = "function f" + N + "(a" + N + ") {\neval('a0 + a1'); h();\n}\n"; michael@0: for (var i = N; --i >= 0;) { michael@0: var call = "f" + (i + 1) + "(a" + i + " - 1);\n"; michael@0: code = ("function f" + i + "(a" + i + ") {\n" + michael@0: code + michael@0: call + michael@0: "if (a" + i + " === 0) " + call + michael@0: "}\n"); michael@0: } michael@0: michael@0: g.eval(code); michael@0: test("a0", 2, "f0(0);"); michael@0: test("a17", 2, "f0(17);"); michael@0: test("a" + (N-2), 2, "f0(" + (N-2) + ");"); michael@0: test("a" + (N-1), 2, "f0(" + (N-1) + ");"); michael@0: michael@0: // A function with a short dynamic chain and a long static chain. michael@0: N = 60; michael@0: michael@0: function DeepStaticShallowDynamic(i, n) { michael@0: var code = "function f" + i + "(a" + i + ") {\n"; michael@0: if (i >= n) michael@0: code += "eval('a1 + a2'); h();\n"; michael@0: else michael@0: code += "return " + DeepStaticShallowDynamic(i+1, n) + ";\n"; michael@0: code += "}"; michael@0: return code; michael@0: } michael@0: g.eval(DeepStaticShallowDynamic(1, N)); michael@0: michael@0: function range(start, stop) { michael@0: for (var i = start; i < stop; i++) michael@0: yield i; michael@0: } michael@0: michael@0: function DSSDsplit(s) { michael@0: return ("var mid = f1" + ["(" + i + ")" for (i in range(0, s))].join("") + ";\n" + michael@0: "mid" + ["(" + i + ")" for (i in range(s, N))].join("") + ";\n" + michael@0: "mid" + ["(" + i + ")" for (i in range(s, N))].join("") + ";\n"); michael@0: } michael@0: michael@0: test("a1", 2, DSSDsplit(1)); michael@0: test("a17", 2, DSSDsplit(17)); michael@0: test("a" + (N-2), 2, DSSDsplit(N-2)); michael@0: test("a" + (N-1), 2, DSSDsplit(N-1));