Commit f0d3cf5b authored by jgruber's avatar jgruber Committed by Commit bot

[inspector] Expose scopes for suspended generator objects

This exposes scopes for suspended generator objects by adding a
[[Scopes]] internal property to generator objects, similar to how
scopes for functions currently not on the stack are handled.

BUG=chromium:667286

Review-Url: https://codereview.chromium.org/2516973003
Cr-Commit-Position: refs/heads/master@{#41244}
parent 5e3e29d9
......@@ -72,6 +72,34 @@ DebuggerScript.getFunctionScopes = function(fun)
return result;
}
/**
* @param {Object} gen
* @return {?Array<!Scope>}
*/
DebuggerScript.getGeneratorScopes = function(gen)
{
var mirror = MakeMirror(gen);
if (!mirror.isGenerator())
return null;
var generatorMirror = /** @type {!GeneratorMirror} */(mirror);
var count = generatorMirror.scopeCount();
if (count == 0)
return null;
var result = [];
for (var i = 0; i < count; i++) {
var scopeDetails = generatorMirror.scope(i).details();
var scopeObject = DebuggerScript._buildScopeObject(scopeDetails.type(), scopeDetails.object());
if (!scopeObject)
continue;
result.push({
type: /** @type {string} */(DebuggerScript._scopeTypeNames.get(scopeDetails.type())),
object: scopeObject,
name: scopeDetails.name() || ""
});
}
return result;
}
/**
* @param {Object} object
* @return {?RawLocation}
......
......@@ -392,6 +392,15 @@ GeneratorMirror.prototype.sourceLocation = function() {}
/** @return {!FunctionMirror} */
GeneratorMirror.prototype.func = function() {}
/** @return {number} */
GeneratorMirror.prototype.scopeCount = function() {}
/**
* @param {number} index
* @return {!ScopeMirror|undefined}
*/
GeneratorMirror.prototype.scope = function(index) {}
/**
* @interface
......
......@@ -689,15 +689,27 @@ v8::Local<v8::Context> V8Debugger::debuggerContext() const {
return m_debuggerContext.Get(m_isolate);
}
v8::MaybeLocal<v8::Value> V8Debugger::functionScopes(
v8::Local<v8::Context> context, v8::Local<v8::Function> function) {
v8::MaybeLocal<v8::Value> V8Debugger::getTargetScopes(
v8::Local<v8::Context> context, v8::Local<v8::Value> value,
ScopeTargetKind kind) {
if (!enabled()) {
UNREACHABLE();
return v8::Local<v8::Value>::New(m_isolate, v8::Undefined(m_isolate));
}
v8::Local<v8::Value> argv[] = {function};
v8::Local<v8::Value> argv[] = {value};
v8::Local<v8::Value> scopesValue;
if (!callDebuggerMethod("getFunctionScopes", 1, argv).ToLocal(&scopesValue))
const char* debuggerMethod = nullptr;
switch (kind) {
case FUNCTION:
debuggerMethod = "getFunctionScopes";
break;
case GENERATOR:
debuggerMethod = "getGeneratorScopes";
break;
}
if (!callDebuggerMethod(debuggerMethod, 1, argv).ToLocal(&scopesValue))
return v8::MaybeLocal<v8::Value>();
v8::Local<v8::Value> copied;
if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context,
......@@ -714,6 +726,16 @@ v8::MaybeLocal<v8::Value> V8Debugger::functionScopes(
return copied;
}
v8::MaybeLocal<v8::Value> V8Debugger::functionScopes(
v8::Local<v8::Context> context, v8::Local<v8::Function> function) {
return getTargetScopes(context, function, FUNCTION);
}
v8::MaybeLocal<v8::Value> V8Debugger::generatorScopes(
v8::Local<v8::Context> context, v8::Local<v8::Value> generator) {
return getTargetScopes(context, generator, GENERATOR);
}
v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
v8::Local<v8::Array> properties;
......@@ -756,6 +778,12 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
toV8StringInternalized(m_isolate, "[[GeneratorLocation]]"));
createDataProperty(context, properties, properties->Length(), location);
}
v8::Local<v8::Value> scopes;
if (generatorScopes(context, value).ToLocal(&scopes)) {
createDataProperty(context, properties, properties->Length(),
toV8StringInternalized(m_isolate, "[[Scopes]]"));
createDataProperty(context, properties, properties->Length(), scopes);
}
}
if (value->IsFunction()) {
v8::Local<v8::Function> function = value.As<v8::Function>();
......
......@@ -126,8 +126,19 @@ class V8Debugger {
v8::Local<v8::Object>);
v8::Local<v8::Value> functionLocation(v8::Local<v8::Context>,
v8::Local<v8::Function>);
enum ScopeTargetKind {
FUNCTION,
GENERATOR,
};
v8::MaybeLocal<v8::Value> getTargetScopes(v8::Local<v8::Context>,
v8::Local<v8::Value>,
ScopeTargetKind);
v8::MaybeLocal<v8::Value> functionScopes(v8::Local<v8::Context>,
v8::Local<v8::Function>);
v8::MaybeLocal<v8::Value> generatorScopes(v8::Local<v8::Context>,
v8::Local<v8::Value>);
v8::Isolate* m_isolate;
V8InspectorImpl* m_inspector;
......
Running test: testScopesPaused
{
id : <messageId>
result : {
result : [
[0] : {
configurable : true
enumerable : true
isOwn : true
name : a
value : {
description : 420
type : number
value : 420
}
writable : true
}
[1] : {
configurable : true
enumerable : true
isOwn : true
name : b
value : {
description : 42
type : number
value : 42
}
writable : true
}
]
}
}
Running test: testScopesNonPaused
{
id : <messageId>
result : {
result : [
[0] : {
configurable : true
enumerable : true
isOwn : true
name : a
value : {
description : 430
type : number
value : 430
}
writable : true
}
[1] : {
configurable : true
enumerable : true
isOwn : true
name : b
value : {
type : undefined
}
writable : true
}
]
}
}
// 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.
InspectorTest.addScript(
`function *gen(a) {
var b = 42;
yield a;
return b;
}
function testSuspendedGenerator()
{
var g = gen(420);
g.next();
debugger;
}`);
Protocol.Debugger.enable().then(testSuite);
function dumpInnermostScope(msg) {
var scopes = msg.result.result;
var inner_scope = scopes[0].value;
return Protocol.Runtime.getProperties({ objectId : inner_scope.objectId })
.then(InspectorTest.logMessage);
}
function dumpGeneratorScopes(msg)
{
var props = msg.result.internalProperties;
var promises = props
.filter(prop => prop.name == "[[Scopes]]")
.map(prop => prop.value.objectId)
.map(scopesId => Protocol.Runtime.getProperties({ objectId : scopesId })
.then(dumpInnermostScope));
return Promise.all(promises);
}
function fetchGeneratorProperties(objectId) {
return Protocol.Runtime.getProperties({ objectId : objectId });
}
function extractGeneratorObjectFromScope(scopeId) {
return Protocol.Runtime.getProperties({ objectId : scopeId })
.then(msg => {
var generatorObjectId = msg.result.result[0].value.objectId;
return fetchGeneratorProperties(generatorObjectId);
});
}
function dumpGeneratorScopesOnPause(msg) {
var scopeChain = msg.params.callFrames[0].scopeChain;
var promises = scopeChain
.filter(scope => scope.type === "local")
.map(scope => scope.object.objectId)
.map(scopeId => extractGeneratorObjectFromScope(scopeId)
.then(dumpGeneratorScopes));
return Promise.all(promises).then(Protocol.Debugger.resume);
}
function testSuite() {
InspectorTest.runTestSuite([
function testScopesPaused(next) {
Protocol.Debugger.oncePaused()
.then(dumpGeneratorScopesOnPause)
.then(next);
Protocol.Runtime.evaluate({ expression : "testSuspendedGenerator()" });
},
function testScopesNonPaused(next) {
Protocol.Runtime.evaluate({ expression : "gen(430)"})
.then(msg => fetchGeneratorProperties(msg.result.result.objectId))
.then(dumpGeneratorScopes)
.then(next);
},
]);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment