[debug] Add 'new.target' to the materialized stack locals for evaluate
This CL adds "new.target" to the ScopeObject with the materialized stack local variables. It's only available if the parser actually allocates a variable for it, otherwise we currently throw a ReferenceError. The added test also ensures that "new.target" is only included for debug-evaluate, but NOT for the scope view. Having ".new.target" show up there would be more confusing than helpful. Drive-by: Remove bogus DCHECK. The context we try to lookup "new.target" can be anything, not just a `with` context. R=bmeurer@chromium.org, leszeks@chromium.org Bug: chromium:1246863 Change-Id: Id4f99b3336044904e3dc76912f65b6f63f092258 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4003039 Reviewed-by: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Commit-Queue: Simon Zünd <szuend@chromium.org> Cr-Commit-Position: refs/heads/main@{#84069}
This commit is contained in:
parent
42d4209e9c
commit
8ab1c88c01
@ -851,7 +851,13 @@ bool ScopeIterator::VisitLocals(const Visitor& visitor, Mode mode,
|
||||
}
|
||||
|
||||
for (Variable* var : *current_scope_->locals()) {
|
||||
if (ScopeInfo::VariableIsSynthetic(*var->name())) continue;
|
||||
if (ScopeInfo::VariableIsSynthetic(*var->name())) {
|
||||
// We want to materialize "new.target" for debug-evaluate.
|
||||
if (mode != Mode::STACK ||
|
||||
!var->name()->Equals(*isolate_->factory()->dot_new_target_string())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int index = var->index();
|
||||
Handle<Object> value;
|
||||
@ -872,6 +878,8 @@ bool ScopeIterator::VisitLocals(const Visitor& visitor, Mode mode,
|
||||
generator_->parameters_and_registers();
|
||||
DCHECK_LT(index, parameters_and_registers.length());
|
||||
value = handle(parameters_and_registers.get(index), isolate_);
|
||||
} else if (var->IsReceiver()) {
|
||||
value = frame_inspector_->GetReceiver();
|
||||
} else {
|
||||
value = frame_inspector_->GetParameter(index);
|
||||
}
|
||||
|
@ -226,6 +226,7 @@
|
||||
V(_, dot_for_string, ".for") \
|
||||
V(_, dot_generator_object_string, ".generator_object") \
|
||||
V(_, dot_home_object_string, ".home_object") \
|
||||
V(_, dot_new_target_string, ".new.target") \
|
||||
V(_, dot_result_string, ".result") \
|
||||
V(_, dot_repl_result_string, ".repl_result") \
|
||||
V(_, dot_static_home_object_string, ".static_home_object") \
|
||||
|
@ -276,7 +276,6 @@ Handle<Object> Context::Lookup(Handle<Context> context, Handle<String> name,
|
||||
// TODO(v8:5405): Replace this check with a DCHECK when resolution of
|
||||
// of synthetic variables does not go through this code path.
|
||||
if (ScopeInfo::VariableIsSynthetic(*name)) {
|
||||
DCHECK(context->IsWithContext());
|
||||
maybe = Just(ABSENT);
|
||||
} else {
|
||||
LookupIterator it(isolate, object, name, object);
|
||||
|
@ -0,0 +1,42 @@
|
||||
Test that new.target can be inspected in Debugger.evaluateOnCallFrame
|
||||
|
||||
Running test: withExplicitUsage
|
||||
{
|
||||
className : Function
|
||||
description : function C() { const fn = new.target; debugger; }
|
||||
objectId : <objectId>
|
||||
type : function
|
||||
}
|
||||
|
||||
Running test: withDirectEval
|
||||
{
|
||||
className : Function
|
||||
description : function D() { const fn = eval('new.target'); debugger; }
|
||||
objectId : <objectId>
|
||||
type : function
|
||||
}
|
||||
|
||||
Running test: withoutExplicitUsage
|
||||
{
|
||||
className : ReferenceError
|
||||
description : ReferenceError: .new.target is not defined at eval (eval at E (:1:1), <anonymous>:1:1) at new E (<anonymous>:13:3) at <anonymous>:1:1
|
||||
objectId : <objectId>
|
||||
subtype : error
|
||||
type : object
|
||||
}
|
||||
|
||||
Running test: withInheritence
|
||||
{
|
||||
className : Function
|
||||
description : class B extends A {}
|
||||
objectId : <objectId>
|
||||
type : function
|
||||
}
|
||||
|
||||
Running test: withContextAllocatedNewTarget
|
||||
{
|
||||
className : Function
|
||||
description : function F() { () => new.target; // context-allocate. debugger; }
|
||||
objectId : <objectId>
|
||||
type : function
|
||||
}
|
83
test/inspector/debugger/evaluate-on-call-frame-new-target.js
Normal file
83
test/inspector/debugger/evaluate-on-call-frame-new-target.js
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
const {session, contextGroup, Protocol} =
|
||||
InspectorTest.start(`Test that new.target can be inspected in Debugger.evaluateOnCallFrame`);
|
||||
|
||||
contextGroup.addScript(`
|
||||
function C() {
|
||||
const fn = new.target;
|
||||
debugger;
|
||||
}
|
||||
|
||||
function D() {
|
||||
const fn = eval('new.target');
|
||||
debugger;
|
||||
}
|
||||
|
||||
function E() {
|
||||
debugger;
|
||||
}
|
||||
|
||||
class A {
|
||||
constructor() {
|
||||
const fn = new.target;
|
||||
debugger;
|
||||
}
|
||||
}
|
||||
|
||||
class B extends A {}
|
||||
|
||||
function F() {
|
||||
() => new.target; // context-allocate.
|
||||
debugger;
|
||||
}
|
||||
`);
|
||||
|
||||
async function ensureNewTargetIsNotReportedInTheScopeChain(scopeChain) {
|
||||
for (const scope of scopeChain) {
|
||||
if (scope.type !== 'local') continue;
|
||||
const {result: {result: variables}} =
|
||||
await Protocol.Runtime.getProperties({ objectId: scope.object.objectId });
|
||||
const variable = variables.find(variable => variable.name === '.new.target');
|
||||
if (variable) {
|
||||
InspectorTest.logMessage(`FAIL: 'new.target' was also reported in the scopeChain on Debugger.paused`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function evaluateNewTargetOnPause(expression) {
|
||||
await Protocol.Debugger.enable();
|
||||
Protocol.Runtime.evaluate({ expression });
|
||||
|
||||
const { params: { callFrames: [{ callFrameId, scopeChain }] } } = await Protocol.Debugger.oncePaused();
|
||||
await ensureNewTargetIsNotReportedInTheScopeChain(scopeChain);
|
||||
|
||||
const { result: { result } } = await Protocol.Debugger.evaluateOnCallFrame({
|
||||
callFrameId,
|
||||
expression: 'new.target',
|
||||
});
|
||||
InspectorTest.logMessage(result);
|
||||
|
||||
await Protocol.Debugger.resume();
|
||||
await Protocol.Debugger.disable();
|
||||
}
|
||||
|
||||
InspectorTest.runAsyncTestSuite([
|
||||
async function withExplicitUsage() {
|
||||
await evaluateNewTargetOnPause('new C()');
|
||||
},
|
||||
async function withDirectEval() {
|
||||
await evaluateNewTargetOnPause('new D()');
|
||||
},
|
||||
async function withoutExplicitUsage() {
|
||||
await evaluateNewTargetOnPause('new E()');
|
||||
},
|
||||
async function withInheritence() {
|
||||
await evaluateNewTargetOnPause('new B()');
|
||||
},
|
||||
async function withContextAllocatedNewTarget() {
|
||||
await evaluateNewTargetOnPause('new F()');
|
||||
},
|
||||
]);
|
Loading…
Reference in New Issue
Block a user