[vm/debugger] Adds pragma support for func. decl.

This fixes issue #45710 by adding support for pragma
annotations on local functions which are lazily created
via the flow graph builder (as opposed to the kernel loader).

TEST=Added regression test to notify_debugger_on_exception_test.dart

Bug: https://github.com/dart-lang/sdk/issues/45710, https://github.com/dart-lang/sdk/issues/45987
Change-Id: I13f2f8d2b7d05ea1cb423c142537789e99a919d3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/199420
Commit-Queue: Clement Skau <cskau@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Clement Skau 2021-05-18 06:12:03 +00:00 committed by commit-bot@chromium.org
parent 1989b7f376
commit 6523c58119
5 changed files with 100 additions and 17 deletions

View file

@ -9,7 +9,12 @@
import 'test_helper.dart';
import 'service_test_common.dart';
const int LINE_A = 24;
const int LINE_A = 16;
const int LINE_B = 32;
syncThrow() {
throw 'Hello from syncThrow!'; // Line A.
}
@pragma('vm:notify-debugger-on-exception')
void catchNotifyDebugger(Function() code) {
@ -20,17 +25,30 @@ void catchNotifyDebugger(Function() code) {
}
}
syncThrow() {
throw 'Hello from syncThrow!'; // Line A.
void catchNotifyDebuggerNested() {
@pragma('vm:notify-debugger-on-exception')
void nested() {
try {
throw 'Hello from nested!'; // Line B.
} catch (e) {
// Ignore. Internals will notify debugger.
}
}
nested();
}
testMain() {
catchNotifyDebugger(syncThrow);
catchNotifyDebuggerNested();
}
final tests = <IsolateTest>[
hasStoppedWithUnhandledException,
stoppedAtLine(LINE_A),
resumeIsolate,
hasStoppedWithUnhandledException,
stoppedAtLine(LINE_B),
];
main([args = const <String>[]]) => runIsolateTests(args, tests,

View file

@ -9,7 +9,12 @@
import 'test_helper.dart';
import 'service_test_common.dart';
const int LINE_A = 24;
const int LINE_A = 16;
const int LINE_B = 32;
syncThrow() {
throw 'Hello from syncThrow!'; // Line A.
}
@pragma('vm:notify-debugger-on-exception')
void catchNotifyDebugger(Function() code) {
@ -20,17 +25,30 @@ void catchNotifyDebugger(Function() code) {
}
}
syncThrow() {
throw 'Hello from syncThrow!'; // Line A.
void catchNotifyDebuggerNested() {
@pragma('vm:notify-debugger-on-exception')
void nested() {
try {
throw 'Hello from nested!'; // Line B.
} catch (e) {
// Ignore. Internals will notify debugger.
}
}
nested();
}
testMain() {
catchNotifyDebugger(syncThrow);
catchNotifyDebuggerNested();
}
final tests = <IsolateTest>[
hasStoppedWithUnhandledException,
stoppedAtLine(LINE_A),
resumeIsolate,
hasStoppedWithUnhandledException,
stoppedAtLine(LINE_B),
];
main([args = const <String>[]]) => runIsolateTests(args, tests,

View file

@ -1257,6 +1257,7 @@ Fragment StreamingFlowGraphBuilder::BuildExpression(TokenPosition* position) {
}
Fragment StreamingFlowGraphBuilder::BuildStatement() {
intptr_t offset = ReaderOffset();
Tag tag = ReadTag(); // read tag.
switch (tag) {
case kExpressionStatement:
@ -1300,7 +1301,7 @@ Fragment StreamingFlowGraphBuilder::BuildStatement() {
case kVariableDeclaration:
return BuildVariableDeclaration();
case kFunctionDeclaration:
return BuildFunctionDeclaration();
return BuildFunctionDeclaration(offset);
default:
ReportUnexpectedTag("statement", tag);
UNREACHABLE();
@ -4241,7 +4242,9 @@ Fragment StreamingFlowGraphBuilder::BuildMapLiteral(TokenPosition* p) {
Fragment StreamingFlowGraphBuilder::BuildFunctionExpression() {
ReadPosition(); // read position.
return BuildFunctionNode(TokenPosition::kNoSource, StringIndex());
return BuildFunctionNode(TokenPosition::kNoSource, StringIndex(),
/*has_valid_annotation=*/false, /*has_pragma=*/false,
/*func_decl_offset=*/0);
}
Fragment StreamingFlowGraphBuilder::BuildLet(TokenPosition* p) {
@ -5424,16 +5427,39 @@ Fragment StreamingFlowGraphBuilder::BuildVariableDeclaration() {
return instructions;
}
Fragment StreamingFlowGraphBuilder::BuildFunctionDeclaration() {
TokenPosition position = ReadPosition(); // read position.
intptr_t variable_offset = ReaderOffset() + data_program_offset_;
Fragment StreamingFlowGraphBuilder::BuildFunctionDeclaration(intptr_t offset) {
TokenPosition position = ReadPosition();
const intptr_t variable_offset = ReaderOffset() + data_program_offset_;
// read variable declaration.
// Read variable declaration.
VariableDeclarationHelper helper(this);
bool has_pragma = false;
bool has_valid_annotation = false;
helper.ReadUntilExcluding(VariableDeclarationHelper::kAnnotations);
const intptr_t annotation_count = ReadListLength();
for (intptr_t i = 0; i < annotation_count; ++i) {
const intptr_t tag = PeekTag();
if (tag != kInvalidExpression) {
has_valid_annotation = true;
}
if (tag == kConstantExpression) {
auto& instance = Instance::Handle();
instance = constant_reader_.ReadConstantExpression();
if (instance.clazz() == IG->object_store()->pragma_class()) {
has_pragma = true;
}
continue;
}
SkipExpression();
}
helper.SetJustRead(VariableDeclarationHelper::kAnnotations);
helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd);
Fragment instructions = DebugStepCheck(position);
instructions += BuildFunctionNode(position, helper.name_index_);
instructions += BuildFunctionNode(position, helper.name_index_,
has_valid_annotation, has_pragma, offset);
instructions += StoreLocal(position, LookupVariable(variable_offset));
instructions += Drop();
return instructions;
@ -5441,7 +5467,10 @@ Fragment StreamingFlowGraphBuilder::BuildFunctionDeclaration() {
Fragment StreamingFlowGraphBuilder::BuildFunctionNode(
TokenPosition parent_position,
StringIndex name_index) {
StringIndex name_index,
bool has_valid_annotation,
bool has_pragma,
intptr_t func_decl_offset) {
intptr_t offset = ReaderOffset();
FunctionNodeHelper function_node_helper(this);
@ -5500,6 +5529,13 @@ Fragment StreamingFlowGraphBuilder::BuildFunctionNode(
*name, parsed_function()->function(), position);
}
function.set_has_pragma(has_pragma);
if ((FLAG_enable_mirrors && has_valid_annotation) || has_pragma) {
auto& lib =
Library::Handle(Z, Class::Handle(Z, function.Owner()).library());
lib.AddMetadata(function, func_decl_offset);
}
function.set_is_debuggable(function_node_helper.dart_async_marker_ ==
FunctionNodeHelper::kSync);
switch (function_node_helper.dart_async_marker_) {

View file

@ -350,9 +350,12 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
Fragment BuildTryFinally();
Fragment BuildYieldStatement();
Fragment BuildVariableDeclaration();
Fragment BuildFunctionDeclaration();
Fragment BuildFunctionDeclaration(intptr_t offset);
Fragment BuildFunctionNode(TokenPosition parent_position,
StringIndex name_index);
StringIndex name_index,
bool has_valid_annotation,
bool has_pragma,
intptr_t func_decl_offset);
// Build flow graph for '_nativeEffect'.
Fragment BuildNativeEffect();

View file

@ -452,8 +452,16 @@ class MetadataEvaluator : public KernelReaderHelper {
} else if (tag == kConstructor) {
ConstructorHelper constructor_helper(this);
constructor_helper.ReadUntilExcluding(ConstructorHelper::kAnnotations);
} else if (tag == kFunctionDeclaration) {
ReadTag();
ReadPosition(); // fileOffset
VariableDeclarationHelper variable_declaration_helper(this);
variable_declaration_helper.ReadUntilExcluding(
VariableDeclarationHelper::kAnnotations);
} else {
FATAL("No support for metadata on this type of kernel node\n");
FATAL("No support for metadata on this type of kernel node: %" Pd32
"\n",
tag);
}
}