2a90c39a66
When suspending, rather than saving all registers up to a certain index, only save the ones that are live according to the liveness analysis. Others are saved as optimized out constants, and are skipped during the GenaratorStore lowering. Symmetrically, only restore live registers when resuming. Change-Id: Icc2df905b0fe2fe5c372097bd67d5316edcd1b54 Reviewed-on: https://chromium-review.googlesource.com/905662 Commit-Queue: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Jaroslav Sevcik <jarin@chromium.org> Cr-Commit-Position: refs/heads/master@{#51153}
461 lines
11 KiB
JavaScript
461 lines
11 KiB
JavaScript
// 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.
|
|
|
|
// Flags: --no-analyze-environment-liveness
|
|
|
|
// 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);
|