Commit c2a7550f authored by Alexey Kozyatinskiy's avatar Alexey Kozyatinskiy Committed by Commit Bot

[inspector] expose module variables for Debugger.evaluateOnCallFrame method

Context::Lookup method should support Module variables.

Bug: chromium:717670
Change-Id: I58d3448b9048c7f9dd7ab8b720803b3503cf91ae
Reviewed-on: https://chromium-review.googlesource.com/519389
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45950}
parent 30a29fa2
......@@ -4,6 +4,7 @@
#include "src/contexts.h"
#include "src/ast/modules.h"
#include "src/bootstrapper.h"
#include "src/debug/debug.h"
#include "src/isolate-inl.h"
......@@ -199,7 +200,6 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags,
int* index, PropertyAttributes* attributes,
InitializationFlag* init_flag,
VariableMode* variable_mode) {
DCHECK(!IsModuleContext());
Isolate* isolate = GetIsolate();
Handle<Context> context(this, isolate);
......@@ -305,7 +305,8 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags,
// 2. Check the context proper if it has slots.
if (context->IsFunctionContext() || context->IsBlockContext() ||
context->IsScriptContext() || context->IsEvalContext()) {
context->IsScriptContext() || context->IsEvalContext() ||
context->IsModuleContext()) {
// Use serialized scope information of functions and blocks to search
// for the context index.
Handle<ScopeInfo> scope_info(context->scope_info());
......@@ -346,6 +347,27 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags,
}
}
// Lookup variable in module imports and exports.
if (context->IsModuleContext()) {
VariableMode mode;
InitializationFlag flag;
MaybeAssignedFlag maybe_assigned_flag;
int cell_index =
scope_info->ModuleIndex(name, &mode, &flag, &maybe_assigned_flag);
if (cell_index != 0) {
if (FLAG_trace_contexts) {
PrintF("=> found in module imports or exports\n");
}
*index = cell_index;
*variable_mode = mode;
*init_flag = flag;
*attributes = ModuleDescriptor::GetCellIndexKind(cell_index) ==
ModuleDescriptor::kExport
? GetAttributesForMode(mode)
: READ_ONLY;
return handle(context->module(), isolate);
}
}
} else if (context->IsCatchContext()) {
// Catch contexts have the variable name in the extension slot.
if (String::Equals(name, handle(context->catch_name()))) {
......@@ -398,9 +420,11 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags,
do {
context = Handle<Context>(context->previous(), isolate);
// If we come across a whitelist context, and the name is not
// whitelisted, then only consider with, script or native contexts.
// whitelisted, then only consider with, script, module or native
// contexts.
} while (failed_whitelist && !context->IsScriptContext() &&
!context->IsNativeContext() && !context->IsWithContext());
!context->IsNativeContext() && !context->IsWithContext() &&
!context->IsModuleContext());
}
} while (follow_context_chain);
......
......@@ -661,7 +661,11 @@ class Context: public FixedArray {
// of with, or as a property of the global object. *index is -1 and
// *attributes is not ABSENT.
//
// 3) result.is_null():
// 3) result->IsModule():
// The binding was found in module imports or exports.
// *attributes is never ABSENT. imports are READ_ONLY.
//
// 4) result.is_null():
// There was no binding found, *index is always -1 and *attributes is
// always ABSENT.
Handle<Object> Lookup(Handle<String> name, ContextLookupFlags flags,
......
......@@ -172,7 +172,8 @@ DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
evaluation_context_ = outer_context;
break;
} else if (scope_type == ScopeIterator::ScopeTypeCatch ||
scope_type == ScopeIterator::ScopeTypeWith) {
scope_type == ScopeIterator::ScopeTypeWith ||
scope_type == ScopeIterator::ScopeTypeModule) {
ContextChainElement context_chain_element;
Handle<Context> current_context = it.CurrentContext();
if (!current_context->IsDebugEvaluateContext()) {
......
......@@ -262,6 +262,7 @@ Object* DeclareEvalHelper(Isolate* isolate, Handle<String> name,
Handle<Object> holder = context->Lookup(name, DONT_FOLLOW_CHAINS, &index,
&attributes, &init_flag, &mode);
DCHECK(holder.is_null() || !holder->IsModule());
DCHECK(!isolate->has_pending_exception());
Handle<JSObject> object;
......@@ -823,8 +824,9 @@ RUNTIME_FUNCTION(Runtime_DeleteLookupSlot) {
return isolate->heap()->true_value();
}
// If the slot was found in a context, it should be DONT_DELETE.
if (holder->IsContext()) {
// If the slot was found in a context or in module imports and exports it
// should be DONT_DELETE.
if (holder->IsContext() || holder->IsModule()) {
return isolate->heap()->false_value();
}
......@@ -853,6 +855,9 @@ MaybeHandle<Object> LoadLookupSlot(Handle<String> name,
name, FOLLOW_CHAINS, &index, &attributes, &flag, &mode);
if (isolate->has_pending_exception()) return MaybeHandle<Object>();
if (!holder.is_null() && holder->IsModule()) {
return Module::LoadVariable(Handle<Module>::cast(holder), index);
}
if (index != Context::kNotFound) {
DCHECK(holder->IsContext());
// If the "property" we were looking for is a local variable, the
......@@ -950,8 +955,17 @@ MaybeHandle<Object> StoreLookupSlot(Handle<String> name, Handle<Object> value,
if (holder.is_null()) {
// In case of JSProxy, an exception might have been thrown.
if (isolate->has_pending_exception()) return MaybeHandle<Object>();
} else if (holder->IsModule()) {
if ((attributes & READ_ONLY) == 0) {
Module::StoreVariable(Handle<Module>::cast(holder), index, value);
} else if (is_strict(language_mode)) {
// Setting read only property in strict mode.
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kStrictCannotAssign, name),
Object);
}
return value;
}
// The property was found in a context slot.
if (index != Context::kNotFound) {
if (flag == kNeedsInitialization &&
......
Tests evaluateOnCallFrame in module.
Running test: testTotal
foo1 (module1:7:2)
foo2 (module2:6:9)
(anonymous) (module3:4:0)
local:foo1
[
[0] : c1 = 12
[1] : g1 = 2
]
module
[
[0] : a1 = 10
[1] : g1 = 1
[2] : b1 = 11
[3] : foo1 = function foo1() { let c1 = 12; let g1 = 2; debugger; return a1 + b1 + c1 + g1; }
]
global
[
...
]
Check variables in frame#0
let g1 = 2;
#debugger;
return a1 + b1 + c1 + g1;
Array =
{
className : Function
description : function Array() { [native code] }
objectId : <objectId>
type : function
}
c1 =
{
description : 12
type : number
value : 12
}
Evaluating: ++c1
updated c1 =
{
description : 13
type : number
value : 13
}
Evaluating: --c1
g1 =
{
description : 2
type : number
value : 2
}
Evaluating: ++g1
updated g1 =
{
description : 3
type : number
value : 3
}
Evaluating: --g1
a1 =
{
description : 10
type : number
value : 10
}
Evaluating: ++a1
updated a1 =
{
description : 11
type : number
value : 11
}
Evaluating: --a1
b1 =
{
description : 11
type : number
value : 11
}
Evaluating: ++b1
updated b1 =
{
description : 12
type : number
value : 12
}
Evaluating: --b1
foo1 =
{
className : Function
description : function foo1() { let c1 = 12; let g1 = 2; debugger; return a1 + b1 + c1 + g1; }
objectId : <objectId>
type : function
}
local:foo2
[
[0] : c2 = 22
]
module
[
[0] : a2 = 20
[1] : foo1 = function foo1() { let c1 = 12; let g1 = 2; debugger; return a1 + b1 + c1 + g1; }
[2] : b2 = 21
[3] : foo2 = function foo2() { let c2 = 22; return foo1() + a2 + b2 + c2; }
]
global
[
...
]
Check variables in frame#1
let c2 = 22;
return #foo1() + a2 + b2 + c2;
}
Array =
{
className : Function
description : function Array() { [native code] }
objectId : <objectId>
type : function
}
c2 =
{
description : 22
type : number
value : 22
}
Evaluating: ++c2
updated c2 =
{
description : 23
type : number
value : 23
}
Evaluating: --c2
a2 =
{
description : 20
type : number
value : 20
}
Evaluating: ++a2
updated a2 =
{
description : 21
type : number
value : 21
}
Evaluating: --a2
foo1 =
{
className : Function
description : function foo1() { let c1 = 12; let g1 = 2; debugger; return a1 + b1 + c1 + g1; }
objectId : <objectId>
type : function
}
b2 =
{
description : 21
type : number
value : 21
}
Evaluating: ++b2
updated b2 =
{
description : 22
type : number
value : 22
}
Evaluating: --b2
foo2 =
{
className : Function
description : function foo2() { let c2 = 22; return foo1() + a2 + b2 + c2; }
objectId : <objectId>
type : function
}
module
[
[0] : a3 = 30
[1] : foo2 = function foo2() { let c2 = 22; return foo1() + a2 + b2 + c2; }
[2] : b3 = 31
]
global
[
...
]
Check variables in frame#2
export let b3 = 31;
#foo2();
Array =
{
className : Function
description : function Array() { [native code] }
objectId : <objectId>
type : function
}
a3 =
{
description : 30
type : number
value : 30
}
Evaluating: ++a3
updated a3 =
{
description : 31
type : number
value : 31
}
Evaluating: --a3
foo2 =
{
className : Function
description : function foo2() { let c2 = 22; return foo1() + a2 + b2 + c2; }
objectId : <objectId>
type : function
}
b3 =
{
description : 31
type : number
value : 31
}
Evaluating: ++b3
updated b3 =
{
description : 32
type : number
value : 32
}
Evaluating: --b3
Running test: testAnother
(anonymous) (module4:5:13)
bar (module4:5:24)
(anonymous) (module4:7:0)
local
[
]
closure:bar
[
[0] : a = 0
]
module
[
[0] : a = 1
[1] : b = 2
[2] : bar = function bar() { let a = 0; (() => {a; debugger;})(); }
]
global
[
...
]
Check variables in frame#0
let a = 0;
(() => {a; #debugger;})();
};
Array =
{
className : Function
description : function Array() { [native code] }
objectId : <objectId>
type : function
}
a =
{
description : 0
type : number
value : 0
}
Evaluating: ++a
updated a =
{
description : 1
type : number
value : 1
}
Evaluating: --a
b =
{
description : 2
type : number
value : 2
}
Evaluating: ++b
updated b =
{
description : 3
type : number
value : 3
}
Evaluating: --b
bar =
{
className : Function
description : function bar() { let a = 0; (() => {a; debugger;})(); }
objectId : <objectId>
type : function
}
local:bar
[
[0] : a = 0
]
module
[
[0] : a = 1
[1] : b = 2
[2] : bar = function bar() { let a = 0; (() => {a; debugger;})(); }
]
global
[
...
]
Check variables in frame#1
let a = 0;
(() => {a; debugger;})#();
};
Array =
{
className : Function
description : function Array() { [native code] }
objectId : <objectId>
type : function
}
a =
{
description : 0
type : number
value : 0
}
Evaluating: ++a
updated a =
{
description : 1
type : number
value : 1
}
Evaluating: --a
b =
{
description : 2
type : number
value : 2
}
Evaluating: ++b
updated b =
{
description : 3
type : number
value : 3
}
Evaluating: --b
bar =
{
className : Function
description : function bar() { let a = 0; (() => {a; debugger;})(); }
objectId : <objectId>
type : function
}
module
[
[0] : a = 1
[1] : b = 2
[2] : bar = function bar() { let a = 0; (() => {a; debugger;})(); }
]
global
[
...
]
Check variables in frame#2
};
#bar();
Array =
{
className : Function
description : function Array() { [native code] }
objectId : <objectId>
type : function
}
a =
{
description : 1
type : number
value : 1
}
Evaluating: ++a
updated a =
{
description : 2
type : number
value : 2
}
Evaluating: --a
b =
{
description : 2
type : number
value : 2
}
Evaluating: ++b
updated b =
{
description : 3
type : number
value : 3
}
Evaluating: --b
bar =
{
className : Function
description : function bar() { let a = 0; (() => {a; debugger;})(); }
objectId : <objectId>
type : function
}
Running test: testDifferentModuleVariables
(anonymous) (module5:5:0)
module
[
[0] : b2 = 21
[1] : a = 0
[2] : b = 0
[3] : c = 0
]
global
[
...
]
Check variables in frame#0
export var c = 0;
#debugger;
Array =
{
className : Function
description : function Array() { [native code] }
objectId : <objectId>
type : function
}
b2 =
{
description : 21
type : number
value : 21
}
Evaluating: ++b2
updated b2 =
{
description : 21
type : number
value : 21
}
Evaluating: --b2
a =
{
description : 0
type : number
value : 0
}
Evaluating: ++a
updated a =
{
description : 0
type : number
value : 0
}
Evaluating: --a
b =
{
description : 0
type : number
value : 0
}
Evaluating: ++b
updated b =
{
description : 1
type : number
value : 1
}
Evaluating: --b
c =
{
description : 0
type : number
value : 0
}
Evaluating: ++c
updated c =
{
description : 1
type : number
value : 1
}
Evaluating: --c
// Copyright 2017 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.
let {session, contextGroup, Protocol} =
InspectorTest.start('Tests evaluateOnCallFrame in module.');
var module1 = `
let a1 = 10;
let g1 = 1;
export let b1 = 11;
export function foo1() {
let c1 = 12;
let g1 = 2;
debugger;
return a1 + b1 + c1 + g1;
}`;
var module2 = `
import { foo1 } from 'module1';
let a2 = 20;
export let b2 = 21;
export function foo2() {
let c2 = 22;
return foo1() + a2 + b2 + c2;
}`;
var module3 = `
import { foo2 } from 'module2';
let a3 = 30;
export let b3 = 31;
foo2();
`;
var module4 = `
let a = 1;
let b = 2;
function bar() {
let a = 0;
(() => {a; debugger;})();
};
bar();`;
var module5 = `
import { b2 } from 'module2';
export const a = 0;
export let b = 0;
export var c = 0;
debugger;
`;
InspectorTest.runAsyncTestSuite([
async function testTotal() {
session.setupScriptMap();
Protocol.Debugger.enable();
contextGroup.addModule(module1, 'module1');
contextGroup.addModule(module2, 'module2');
contextGroup.addModule(module3, 'module3');
let {params:{callFrames}} = (await Protocol.Debugger.oncePaused());
session.logCallFrames(callFrames);
for (let i = 0; i < callFrames.length; ++i) {
await checkFrame(callFrames[i], i);
}
await Protocol.Debugger.resume();
},
async function testAnother() {
contextGroup.addModule(module4, 'module4');
let {params:{callFrames}} = (await Protocol.Debugger.oncePaused());
session.logCallFrames(callFrames);
for (let i = 0; i < callFrames.length; ++i) {
await checkFrame(callFrames[i], i);
}
await Protocol.Debugger.resume();
},
async function testDifferentModuleVariables() {
contextGroup.addModule(module5, 'module5');
let {params:{callFrames}} = (await Protocol.Debugger.oncePaused());
session.logCallFrames(callFrames);
for (let i = 0; i < callFrames.length; ++i) {
await checkFrame(callFrames[i], i);
}
await Protocol.Debugger.resume();
}
]);
async function checkFrame(frame, i) {
let variables = new Set();
variables.add('Array');
for (let scopeChain of frame.scopeChain) {
if (scopeChain.name) {
InspectorTest.log(scopeChain.type + ':' + scopeChain.name);
} else {
InspectorTest.log(scopeChain.type);
}
if (scopeChain.type === 'global') {
InspectorTest.log('[\n ...\n]');
continue;
}
let {result: {result}} = await Protocol.Runtime.getProperties({
objectId: scopeChain.object.objectId});
result.forEach(v => variables.add(v.name));
result = result.map(v => v.value ?
(v.name + ' = ' + v.value.description) : (v.name));
InspectorTest.logMessage(result);
}
InspectorTest.log(`Check variables in frame#${i}`);
await session.logSourceLocation(frame.location);
await checkVariables(frame.callFrameId, variables);
}
async function checkVariables(callFrameId, names) {
for (let name of names) {
var {result:{result}} = await Protocol.Debugger.evaluateOnCallFrame({
callFrameId, expression: name});
if (result.type === 'object' && result.subtype && result.subtype === 'error') {
continue;
}
InspectorTest.log(name + ' = ');
InspectorTest.logMessage(result);
if (result.type === "number") {
let updateExpression = '++' + name;
InspectorTest.log('Evaluating: ' + updateExpression);
await Protocol.Debugger.evaluateOnCallFrame({
callFrameId, expression: updateExpression});
var {result:{result}} = await Protocol.Debugger.evaluateOnCallFrame({
callFrameId, expression: name});
InspectorTest.log('updated ' + name + ' = ');
InspectorTest.logMessage(result);
updateExpression = '--' + name;
InspectorTest.log('Evaluating: ' + updateExpression);
await Protocol.Debugger.evaluateOnCallFrame({
callFrameId, expression: updateExpression});
}
}
}
......@@ -13,6 +13,7 @@
'debugger/scope-skip-variables-with-empty-name': [PASS, FAIL],
'debugger/update-call-frame-scopes': [PASS, FAIL],
'debugger/side-effect-free-debug-evaluate': [PASS, FAIL],
'debugger/evaluate-on-call-frame-in-module': [PASS, FAIL],
# Issue 6170. Crash.
'cpu-profiler/console-profile': [SKIP],
'cpu-profiler/console-profile-end-parameterless-crash': [SKIP],
......
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