| // Copyright 2016 the V8 project authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // The functions used for testing backtraces. They are at the top to make the | |
| // testing of source line/column easier. | |
| var Debug = debug.Debug; | |
| var test_name; | |
| var exception; | |
| var begin_test_count = 0; | |
| var end_test_count = 0; | |
| // Initialize for a new test. | |
| function BeginTest(name) { | |
| test_name = name; | |
| exception = null; | |
| begin_test_count++; | |
| } | |
| // Check result of a test. | |
| function EndTest() { | |
| assertNull(exception, test_name + " / " + exception); | |
| end_test_count++; | |
| } | |
| // Check that two scope are the same. | |
| function assertScopeMirrorEquals(scope1, scope2) { | |
| assertEquals(scope1.scopeType(), scope2.scopeType()); | |
| assertEquals(scope1.scopeIndex(), scope2.scopeIndex()); | |
| assertPropertiesEqual(scope1.scopeObject().value(), | |
| scope2.scopeObject().value()); | |
| } | |
| // Check that the scope chain contains the expected types of scopes. | |
| function CheckScopeChain(scopes, gen) { | |
| var all_scopes = Debug.generatorScopes(gen); | |
| assertEquals(scopes.length, Debug.generatorScopeCount(gen)); | |
| assertEquals(scopes.length, all_scopes.length, | |
| "FrameMirror.allScopes length"); | |
| for (var i = 0; i < scopes.length; i++) { | |
| var scope = all_scopes[i]; | |
| assertEquals(scopes[i], scope.scopeType(), | |
| `Scope ${i} has unexpected type`); | |
| // Check the global object when hitting the global scope. | |
| if (scopes[i] == debug.ScopeType.Global) { | |
| // Objects don't have same class (one is "global", other is "Object", | |
| // so just check the properties directly. | |
| assertPropertiesEqual(this, scope.scopeObject().value()); | |
| } | |
| } | |
| } | |
| // Check that the content of the scope is as expected. For functions just check | |
| // that there is a function. | |
| function CheckScopeContent(content, number, gen) { | |
| var scope = Debug.generatorScope(gen, number); | |
| var count = 0; | |
| for (var p in content) { | |
| var property_mirror = scope.scopeObject().property(p); | |
| if (content[p] === undefined) { | |
| assertTrue(property_mirror === undefined); | |
| } else { | |
| assertFalse(property_mirror === undefined, | |
| 'property ' + p + ' not found in scope'); | |
| } | |
| if (typeof(content[p]) === 'function') { | |
| assertTrue(typeof property_mirror == "function"); | |
| } else { | |
| assertEquals(content[p], property_mirror, | |
| 'property ' + p + ' has unexpected value'); | |
| } | |
| count++; | |
| } | |
| // 'arguments' and might be exposed in the local and closure scope. Just | |
| // ignore this. | |
| var scope_size = scope.scopeObject().properties().length; | |
| if (scope.scopeObject().property('arguments') !== undefined) { | |
| scope_size--; | |
| } | |
| // Ditto for 'this'. | |
| if (scope.scopeObject().property('this') !== undefined) { | |
| scope_size--; | |
| } | |
| // Temporary variables introduced by the parser have not been materialized. | |
| assertTrue(scope.scopeObject().property('') === undefined); | |
| if (count != scope_size) { | |
| print('Names found in scope:'); | |
| var names = scope.scopeObject().propertyNames(); | |
| for (var i = 0; i < names.length; i++) { | |
| print(names[i]); | |
| } | |
| } | |
| assertEquals(count, scope_size); | |
| } | |
| // Simple empty closure scope. | |
| function *gen1() { | |
| yield 1; | |
| return 2; | |
| } | |
| var g = gen1(); | |
| CheckScopeChain([debug.ScopeType.Local, | |
| debug.ScopeType.Script, | |
| debug.ScopeType.Global], g); | |
| CheckScopeContent({}, 0, g); | |
| // Closure scope with a parameter. | |
| function *gen2(a) { | |
| yield a; | |
| return 2; | |
| } | |
| g = gen2(42); | |
| CheckScopeChain([debug.ScopeType.Local, | |
| debug.ScopeType.Script, | |
| debug.ScopeType.Global], g); | |
| CheckScopeContent({a: 42}, 0, g); | |
| // Closure scope with a parameter. | |
| function *gen3(a) { | |
| var b = 1 | |
| yield a; | |
| return b; | |
| } | |
| g = gen3(0); | |
| CheckScopeChain([debug.ScopeType.Local, | |
| debug.ScopeType.Script, | |
| debug.ScopeType.Global], g); | |
| CheckScopeContent({a: 0, b: undefined}, 0, g); | |
| g.next(); // Create b. | |
| CheckScopeContent({a: 0, b: 1}, 0, g); | |
| // Closure scope with a parameter. | |
| function *gen4(a, b) { | |
| var x = 2; | |
| yield a; | |
| var y = 3; | |
| yield a; | |
| return b; | |
| } | |
| g = gen4(0, 1); | |
| CheckScopeChain([debug.ScopeType.Local, | |
| debug.ScopeType.Script, | |
| debug.ScopeType.Global], g); | |
| CheckScopeContent({a: 0, b: 1, x: undefined, y: undefined}, 0, g); | |
| g.next(); // Create x. | |
| CheckScopeContent({a: 0, b: 1, x: 2, y: undefined}, 0, g); | |
| g.next(); // Create y. | |
| CheckScopeContent({a: 0, b: 1, x: 2, y: 3}, 0, g); | |
| // Closure introducing local variable using eval. | |
| function *gen5(a) { | |
| eval('var b = 2'); | |
| yield a; | |
| return b; | |
| } | |
| g = gen5(1); | |
| g.next(); | |
| CheckScopeChain([debug.ScopeType.Local, | |
| debug.ScopeType.Script, | |
| debug.ScopeType.Global], g); | |
| CheckScopeContent({a: 1, b: 2}, 0, g); | |
| // Single empty with block. | |
| function *gen6() { | |
| with({}) { | |
| yield 1; | |
| } | |
| yield 2; | |
| return 3; | |
| } | |
| g = gen6(); | |
| g.next(); | |
| CheckScopeChain([debug.ScopeType.With, | |
| debug.ScopeType.Local, | |
| debug.ScopeType.Script, | |
| debug.ScopeType.Global], g); | |
| CheckScopeContent({}, 0, g); | |
| g.next(); | |
| CheckScopeChain([debug.ScopeType.Local, | |
| debug.ScopeType.Script, | |
| debug.ScopeType.Global], g); | |
| // Nested empty with blocks. | |
| function *gen7() { | |
| with({}) { | |
| with({}) { | |
| yield 1; | |
| } | |
| yield 2; | |
| } | |
| return 3; | |
| } | |
| g = gen7(); | |
| g.next(); | |
| CheckScopeChain([debug.ScopeType.With, | |
| debug.ScopeType.With, | |
| debug.ScopeType.Local, | |
| debug.ScopeType.Script, | |
| debug.ScopeType.Global], g); | |
| CheckScopeContent({}, 0, g); | |
| // Nested with blocks using in-place object literals. | |
| function *gen8() { | |
| with({a: 1,b: 2}) { | |
| with({a: 2,b: 1}) { | |
| yield a; | |
| } | |
| yield a; | |
| } | |
| return 3; | |
| } | |
| g = gen8(); | |
| g.next(); | |
| CheckScopeChain([debug.ScopeType.With, | |
| debug.ScopeType.With, | |
| debug.ScopeType.Local, | |
| debug.ScopeType.Script, | |
| debug.ScopeType.Global], g); | |
| CheckScopeContent({a: 2, b: 1}, 0, g); | |
| g.next(); | |
| CheckScopeContent({a: 1, b: 2}, 0, g); | |
| // Catch block. | |
| function *gen9() { | |
| try { | |
| throw 42; | |
| } catch (e) { | |
| yield e; | |
| } | |
| return 3; | |
| } | |
| g = gen9(); | |
| g.next(); | |
| CheckScopeChain([debug.ScopeType.Catch, | |
| debug.ScopeType.Local, | |
| debug.ScopeType.Script, | |
| debug.ScopeType.Global], g); | |
| CheckScopeContent({e: 42}, 0, g); | |
| // For statement with block scope. | |
| function *gen10() { | |
| for (let i = 0; i < 42; i++) yield i; | |
| return 3; | |
| } | |
| g = gen10(); | |
| g.next(); | |
| CheckScopeChain([debug.ScopeType.Block, | |
| debug.ScopeType.Local, | |
| debug.ScopeType.Script, | |
| debug.ScopeType.Global], g); | |
| CheckScopeContent({i: 0}, 0, g); | |
| g.next(); | |
| CheckScopeContent({i: 1}, 0, g); | |
| // Nested generators. | |
| var gen12; | |
| function *gen11() { | |
| var b = 2; | |
| gen12 = function*() { | |
| var a = 1; | |
| yield 1; | |
| return b; | |
| }(); | |
| var a = 0; | |
| yield* gen12; | |
| } | |
| gen11().next(); | |
| g = gen12; | |
| CheckScopeChain([debug.ScopeType.Local, | |
| debug.ScopeType.Closure, | |
| debug.ScopeType.Script, | |
| debug.ScopeType.Global], g); | |
| CheckScopeContent({a: 1}, 0, g); | |
| CheckScopeContent({b: 2}, 1, g); | |
| // Set a variable in an empty scope. | |
| function *gen13() { | |
| yield 1; | |
| return 2; | |
| } | |
| var g = gen13(); | |
| assertThrows(() => Debug.generatorScope(g, 0).setVariableValue("a", 42)); | |
| CheckScopeContent({}, 0, g); | |
| // Set a variable in a simple scope. | |
| function *gen14() { | |
| var a = 0; | |
| yield 1; | |
| yield a; | |
| return 2; | |
| } | |
| var g = gen14(); | |
| assertEquals(1, g.next().value); | |
| CheckScopeContent({a: 0}, 0, g); | |
| Debug.generatorScope(g, 0).setVariableValue("a", 1); | |
| CheckScopeContent({a: 1}, 0, g); | |
| assertEquals(1, g.next().value); | |
| // Set a variable in nested with blocks using in-place object literals. | |
| function *gen15() { | |
| var c = 3; | |
| with({a: 1,b: 2}) { | |
| var d = 4; | |
| yield a; | |
| var e = 5; | |
| } | |
| yield e; | |
| return e; | |
| } | |
| var g = gen15(); | |
| assertEquals(1, g.next().value); | |
| CheckScopeChain([debug.ScopeType.With, | |
| debug.ScopeType.Local, | |
| debug.ScopeType.Script, | |
| debug.ScopeType.Global], g); | |
| CheckScopeContent({a: 1, b: 2}, 0, g); | |
| CheckScopeContent({c: 3, d: 4, e: undefined}, 1, g); | |
| // Variables don't exist in given scope. | |
| assertThrows(() => Debug.generatorScope(g, 0).setVariableValue("c", 42)); | |
| assertThrows(() => Debug.generatorScope(g, 1).setVariableValue("a", 42)); | |
| // Variables in with scope are immutable. | |
| assertThrows(() => Debug.generatorScope(g, 0).setVariableValue("a", 3)); | |
| assertThrows(() => Debug.generatorScope(g, 0).setVariableValue("b", 3)); | |
| Debug.generatorScope(g, 1).setVariableValue("c", 1); | |
| Debug.generatorScope(g, 1).setVariableValue("e", 42); | |
| CheckScopeContent({a: 1, b: 2}, 0, g); | |
| CheckScopeContent({c: 1, d: 4, e: 42}, 1, g); | |
| assertEquals(5, g.next().value); // Initialized after set. | |
| CheckScopeChain([debug.ScopeType.Local, | |
| debug.ScopeType.Script, | |
| debug.ScopeType.Global], g); | |
| Debug.generatorScope(g, 0).setVariableValue("e", 42); | |
| CheckScopeContent({c: 1, d: 4, e: 42}, 0, g); | |
| assertEquals(42, g.next().value); | |
| // Set a variable in nested with blocks using in-place object literals plus a | |
| // nested block scope. | |
| function *gen16() { | |
| var c = 3; | |
| with({a: 1,b: 2}) { | |
| let d = 4; | |
| yield a; | |
| let e = 5; | |
| yield d; | |
| } | |
| return 3; | |
| } | |
| var g = gen16(); | |
| g.next(); | |
| CheckScopeChain([debug.ScopeType.Block, | |
| debug.ScopeType.With, | |
| debug.ScopeType.Local, | |
| debug.ScopeType.Script, | |
| debug.ScopeType.Global], g); | |
| CheckScopeContent({d: 4, e: undefined}, 0, g); | |
| CheckScopeContent({a: 1, b: 2}, 1, g); | |
| CheckScopeContent({c: 3}, 2, g); | |
| Debug.generatorScope(g, 0).setVariableValue("d", 1); | |
| CheckScopeContent({d: 1, e: undefined}, 0, g); | |
| assertEquals(1, g.next().value); | |
| // Set variable in catch block. | |
| var yyzyzzyz = 4829; | |
| let xxxyyxxyx = 42284; | |
| function *gen17() { | |
| try { | |
| throw 42; | |
| } catch (e) { | |
| yield e; | |
| yield e; | |
| } | |
| return 3; | |
| } | |
| g = gen17(); | |
| g.next(); | |
| CheckScopeChain([debug.ScopeType.Catch, | |
| debug.ScopeType.Local, | |
| debug.ScopeType.Script, | |
| debug.ScopeType.Global], g); | |
| CheckScopeContent({e: 42}, 0, g); | |
| CheckScopeContent({xxxyyxxyx: 42284, | |
| printProtocolMessages : printProtocolMessages, | |
| activeWrapper : activeWrapper, | |
| DebugWrapper : DebugWrapper | |
| }, 2, g); | |
| Debug.generatorScope(g, 0).setVariableValue("e", 1); | |
| CheckScopeContent({e: 1}, 0, g); | |
| assertEquals(1, g.next().value); | |
| // Script scope. | |
| Debug.generatorScope(g, 2).setVariableValue("xxxyyxxyx", 42); | |
| assertEquals(42, xxxyyxxyx); | |
| // Global scope. | |
| assertThrows(() => Debug.generatorScope(g, 3).setVariableValue("yyzyzzyz", 42)); | |
| assertEquals(4829, yyzyzzyz); |