Improve error message when using await in DebugEvaluate

When evaluating a top-level expression while paused on a breakpoint, we
don't support an await expression as top-level statement. In these
cases, the error was not informative and could be improved.

To do so, we now propagate the information from DebugEvaluate to
ParseInfo and use the parse_info in parser-base to throw a more
informative error while parsing.

R=jarin@chromium.org

Fixed: chromium:1132245
Change-Id: I200c5af7391258256d1d86a09cbcae326327a0d9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3247037
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Simon Zünd <szuend@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Tim van der Lippe <tvanderlippe@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77587}
This commit is contained in:
Tim van der Lippe 2021-10-27 15:21:36 +01:00 committed by V8 LUCI CQ
parent c2cdcee03f
commit 52b4aae2d9
13 changed files with 133 additions and 27 deletions

View File

@ -2156,7 +2156,8 @@ MaybeHandle<JSFunction> Compiler::GetFunctionFromEval(
Handle<String> source, Handle<SharedFunctionInfo> outer_info,
Handle<Context> context, LanguageMode language_mode,
ParseRestriction restriction, int parameters_end_pos,
int eval_scope_position, int eval_position) {
int eval_scope_position, int eval_position,
ParsingWhileDebugging parsing_while_debugging) {
Isolate* isolate = context->GetIsolate();
int source_length = source->length();
isolate->counters()->total_eval_size()->Increment(source_length);
@ -2199,6 +2200,7 @@ MaybeHandle<JSFunction> Compiler::GetFunctionFromEval(
isolate, true, language_mode, REPLMode::kNo, ScriptType::kClassic,
FLAG_lazy_eval);
flags.set_is_eval(true);
flags.set_parsing_while_debugging(parsing_while_debugging);
DCHECK(!flags.is_module());
flags.set_parse_restriction(restriction);

View File

@ -131,7 +131,9 @@ class V8_EXPORT_PRIVATE Compiler : public AllStatic {
Handle<String> source, Handle<SharedFunctionInfo> outer_info,
Handle<Context> context, LanguageMode language_mode,
ParseRestriction restriction, int parameters_end_pos,
int eval_scope_position, int eval_position);
int eval_scope_position, int eval_position,
ParsingWhileDebugging parsing_while_debugging =
ParsingWhileDebugging::kNo);
// Create a function that results from wrapping |source| in a function,
// with |arguments| being a list of parameters for that function.

View File

@ -921,6 +921,12 @@ inline REPLMode construct_repl_mode(bool is_repl_mode) {
return is_repl_mode ? REPLMode::kYes : REPLMode::kNo;
}
// Indicates whether a script is parsed during debugging.
enum class ParsingWhileDebugging {
kYes,
kNo,
};
// Flag indicating whether code is built into the VM (one of the natives files).
enum NativesFlag { NOT_NATIVES_CODE, EXTENSION_CODE, INSPECTOR_CODE };

View File

@ -38,6 +38,9 @@ namespace internal {
"await is only valid in async functions and the top level bodies of " \
"modules") \
T(AwaitNotInAsyncFunction, "await is only valid in async function") \
T(AwaitNotInDebugEvaluate, \
"await can not be used when evaluating code " \
"while paused in the debugger") \
T(AtomicsWaitNotAllowed, "Atomics.wait cannot be called in this context") \
T(BadSortComparisonFunction, \
"The comparison function must be either a function or undefined") \

View File

@ -183,10 +183,10 @@ MaybeHandle<Object> DebugEvaluate::Evaluate(
Handle<JSFunction> eval_fun;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, eval_fun,
Compiler::GetFunctionFromEval(source, outer_info, context,
LanguageMode::kSloppy, NO_PARSE_RESTRICTION,
kNoSourcePosition, kNoSourcePosition,
kNoSourcePosition),
Compiler::GetFunctionFromEval(
source, outer_info, context, LanguageMode::kSloppy,
NO_PARSE_RESTRICTION, kNoSourcePosition, kNoSourcePosition,
kNoSourcePosition, ParsingWhileDebugging::kYes),
Object);
Handle<Object> result;

View File

@ -26,7 +26,8 @@ UnoptimizedCompileFlags::UnoptimizedCompileFlags(Isolate* isolate,
: flags_(0),
script_id_(script_id),
function_kind_(FunctionKind::kNormalFunction),
function_syntax_kind_(FunctionSyntaxKind::kDeclaration) {
function_syntax_kind_(FunctionSyntaxKind::kDeclaration),
parsing_while_debugging_(ParsingWhileDebugging::kNo) {
set_collect_type_profile(isolate->is_collecting_type_profile());
set_coverage_enabled(!isolate->is_best_effort_code_coverage());
set_block_coverage_enabled(isolate->is_block_code_coverage());

View File

@ -119,6 +119,15 @@ class V8_EXPORT_PRIVATE UnoptimizedCompileFlags {
return *this;
}
ParsingWhileDebugging parsing_while_debugging() const {
return parsing_while_debugging_;
}
UnoptimizedCompileFlags& set_parsing_while_debugging(
ParsingWhileDebugging value) {
parsing_while_debugging_ = value;
return *this;
}
private:
struct BitFields {
DEFINE_BIT_FIELDS(FLAG_FIELDS)
@ -141,6 +150,7 @@ class V8_EXPORT_PRIVATE UnoptimizedCompileFlags {
int script_id_;
FunctionKind function_kind_;
FunctionSyntaxKind function_syntax_kind_;
ParsingWhileDebugging parsing_while_debugging_;
};
#undef FLAG_FIELDS

View File

@ -920,10 +920,16 @@ class ParserBase {
}
if (scanner()->current_token() == Token::AWAIT && !is_async_function()) {
ReportMessageAt(scanner()->location(),
flags().allow_harmony_top_level_await()
? MessageTemplate::kAwaitNotInAsyncContext
: MessageTemplate::kAwaitNotInAsyncFunction);
if (flags().parsing_while_debugging() == ParsingWhileDebugging::kYes) {
ReportMessageAt(scanner()->location(),
MessageTemplate::kAwaitNotInDebugEvaluate);
} else if (flags().allow_harmony_top_level_await()) {
ReportMessageAt(scanner()->location(),
MessageTemplate::kAwaitNotInAsyncContext);
} else {
ReportMessageAt(scanner()->location(),
MessageTemplate::kAwaitNotInAsyncFunction);
}
return;
}

View File

@ -83,7 +83,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 48 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 53 S> */ B(Wide), B(LdaSmi), I16(284),
/* 53 S> */ B(Wide), B(LdaSmi), I16(285),
B(Star3),
B(LdaConstant), U8(0),
B(Star4),
@ -114,7 +114,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 41 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 46 S> */ B(Wide), B(LdaSmi), I16(283),
/* 46 S> */ B(Wide), B(LdaSmi), I16(284),
B(Star3),
B(LdaConstant), U8(0),
B(Star4),
@ -145,7 +145,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 48 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 53 S> */ B(Wide), B(LdaSmi), I16(284),
/* 53 S> */ B(Wide), B(LdaSmi), I16(285),
B(Star3),
B(LdaConstant), U8(0),
B(Star4),
@ -176,7 +176,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 41 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 46 S> */ B(Wide), B(LdaSmi), I16(283),
/* 46 S> */ B(Wide), B(LdaSmi), I16(284),
B(Star4),
B(LdaConstant), U8(0),
B(Star5),

View File

@ -56,7 +56,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 44 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 49 S> */ B(Wide), B(LdaSmi), I16(282),
/* 49 S> */ B(Wide), B(LdaSmi), I16(283),
B(Star3),
B(LdaConstant), U8(0),
B(Star4),
@ -88,7 +88,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 44 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 49 S> */ B(Wide), B(LdaSmi), I16(282),
/* 49 S> */ B(Wide), B(LdaSmi), I16(283),
B(Star3),
B(LdaConstant), U8(0),
B(Star4),

View File

@ -24,7 +24,7 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(1),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(280),
B(Wide), B(LdaSmi), I16(281),
B(Star2),
B(LdaConstant), U8(0),
B(Star3),
@ -55,7 +55,7 @@ frame size: 2
parameter count: 1
bytecode array length: 14
bytecodes: [
/* 56 S> */ B(Wide), B(LdaSmi), I16(282),
/* 56 S> */ B(Wide), B(LdaSmi), I16(283),
B(Star0),
B(LdaConstant), U8(0),
B(Star1),
@ -82,7 +82,7 @@ frame size: 2
parameter count: 1
bytecode array length: 14
bytecodes: [
/* 56 S> */ B(Wide), B(LdaSmi), I16(282),
/* 56 S> */ B(Wide), B(LdaSmi), I16(283),
B(Star0),
B(LdaConstant), U8(0),
B(Star1),
@ -121,7 +121,7 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(0),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(280),
B(Wide), B(LdaSmi), I16(281),
B(Star2),
B(LdaConstant), U8(0),
B(Star3),
@ -143,7 +143,7 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(1),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(281),
B(Wide), B(LdaSmi), I16(282),
B(Star3),
B(LdaConstant), U8(0),
B(Star4),
@ -158,7 +158,7 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(0),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(280),
B(Wide), B(LdaSmi), I16(281),
B(Star2),
B(LdaConstant), U8(0),
B(Star3),
@ -188,7 +188,7 @@ frame size: 2
parameter count: 1
bytecode array length: 14
bytecodes: [
/* 60 S> */ B(Wide), B(LdaSmi), I16(284),
/* 60 S> */ B(Wide), B(LdaSmi), I16(285),
B(Star0),
B(LdaConstant), U8(0),
B(Star1),
@ -214,7 +214,7 @@ frame size: 2
parameter count: 1
bytecode array length: 14
bytecodes: [
/* 53 S> */ B(Wide), B(LdaSmi), I16(283),
/* 53 S> */ B(Wide), B(LdaSmi), I16(284),
B(Star0),
B(LdaConstant), U8(0),
B(Star1),
@ -240,7 +240,7 @@ frame size: 2
parameter count: 1
bytecode array length: 14
bytecodes: [
/* 60 S> */ B(Wide), B(LdaSmi), I16(284),
/* 60 S> */ B(Wide), B(LdaSmi), I16(285),
B(Star0),
B(LdaConstant), U8(0),
B(Star1),
@ -266,7 +266,7 @@ frame size: 3
parameter count: 1
bytecode array length: 14
bytecodes: [
/* 46 S> */ B(Wide), B(LdaSmi), I16(283),
/* 46 S> */ B(Wide), B(LdaSmi), I16(284),
B(Star1),
B(LdaConstant), U8(0),
B(Star2),

View File

@ -0,0 +1,37 @@
Test evaluating await expression on a breakpoint
Running test: testScopesPaused
Evaluating await expression
{
exceptionDetails : {
columnNumber : 0
exception : {
className : SyntaxError
description : SyntaxError: await can not be used when evaluating code while paused in the debugger at run (<anonymous>:3:3) at <anonymous>:1:1
objectId : 1.1.7
subtype : error
type : object
}
exceptionId : 1
lineNumber : 0
scriptId : 5
text : Uncaught
}
result : {
className : SyntaxError
description : SyntaxError: await can not be used when evaluating code while paused in the debugger at run (<anonymous>:3:3) at <anonymous>:1:1
objectId : 1.1.6
subtype : error
type : object
}
}
Evaluating await expression in async function
{
result : {
className : Promise
description : Promise
objectId : 1.1.8
subtype : promise
type : object
}
}

View File

@ -0,0 +1,39 @@
// Copyright 2021 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('Test evaluating await expression on a breakpoint');
contextGroup.addScript(`
async function run() {
debugger;
}`);
InspectorTest.runAsyncTestSuite([async function testScopesPaused() {
Protocol.Debugger.enable();
Protocol.Runtime.evaluate({expression: 'run()'});
let {params: {callFrames}} =
await Protocol.Debugger.oncePaused(); // inside a.fn()
({result} = await Protocol.Debugger.evaluateOnCallFrame({
expression: 'await Promise.resolve(0)',
callFrameId: callFrames[0].callFrameId
}));
InspectorTest.log('Evaluating await expression');
InspectorTest.logObject(result);
({result} = await Protocol.Debugger.evaluateOnCallFrame({
expression:
'async function nested() { await fetch(\'http://google.com\'); } nested()',
callFrameId: callFrames[0].callFrameId
}));
InspectorTest.log('Evaluating await expression in async function');
InspectorTest.logObject(result);
Protocol.Debugger.resume();
Protocol.Debugger.disable();
}]);