| Index: runtime/vm/parser.cc
|
| diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
|
| index 4ebafa283ba85f6787b024ad754c81440bd2552f..16d8ec20652c339b00f12a130a63fbb42c47817b 100644
|
| --- a/runtime/vm/parser.cc
|
| +++ b/runtime/vm/parser.cc
|
| @@ -70,6 +70,7 @@ DEFINE_FLAG(bool,
|
| assert_initializer,
|
| false,
|
| "Allow asserts in initializer lists.");
|
| +DEFINE_FLAG(bool, assert_message, false, "Allow message in assert statements");
|
|
|
| DECLARE_FLAG(bool, profile_vm);
|
| DECLARE_FLAG(bool, trace_service);
|
| @@ -9164,32 +9165,91 @@ AstNode* Parser::ParseAssertStatement(bool is_const) {
|
| const TokenPosition condition_pos = TokenPos();
|
| if (!I->asserts()) {
|
| SkipExpr();
|
| + if (FLAG_assert_message && (CurrentToken() == Token::kCOMMA)) {
|
| + ConsumeToken();
|
| + SkipExpr();
|
| + }
|
| ExpectToken(Token::kRPAREN);
|
| return NULL;
|
| }
|
| - AstNode* condition = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
|
| +
|
| + BoolScope saved_seen_await(&parsed_function()->have_seen_await_expr_, false);
|
| + AstNode* condition = ParseExpr(kAllowConst, kConsumeCascades);
|
| if (is_const && !condition->IsPotentiallyConst()) {
|
| ReportError(condition_pos,
|
| "initializer assert expression must be compile time constant.");
|
| }
|
| const TokenPosition condition_end = TokenPos();
|
| + AstNode* message = NULL;
|
| + TokenPosition message_pos = TokenPosition::kNoSource;
|
| + if (FLAG_assert_message && CurrentToken() == Token::kCOMMA) {
|
| + ConsumeToken();
|
| + message_pos = TokenPos();
|
| + message = ParseExpr(kAllowConst, kConsumeCascades);
|
| + if (is_const && !message->IsPotentiallyConst()) {
|
| + ReportError(
|
| + message_pos,
|
| + "initializer assert expression must be compile time constant.");
|
| + }
|
| + }
|
| ExpectToken(Token::kRPAREN);
|
|
|
| + if (!is_const) {
|
| + // Check for assertion condition being a function if not const.
|
| + ArgumentListNode* arguments = new (Z) ArgumentListNode(condition_pos);
|
| + arguments->Add(condition);
|
| + condition = MakeStaticCall(
|
| + Symbols::AssertionError(),
|
| + Library::PrivateCoreLibName(Symbols::EvaluateAssertion()), arguments);
|
| + }
|
| + AstNode* not_condition =
|
| + new (Z) UnaryOpNode(condition_pos, Token::kNOT, condition);
|
| +
|
| + // Build call to _AsertionError._throwNew(start, end, message)
|
| ArgumentListNode* arguments = new (Z) ArgumentListNode(condition_pos);
|
| - arguments->Add(condition);
|
| arguments->Add(new (Z) LiteralNode(
|
| condition_pos,
|
| - Integer::ZoneHandle(Z, Integer::New(condition_pos.value(), Heap::kOld))));
|
| + Integer::ZoneHandle(Z, Integer::New(condition_pos.Pos()))));
|
| arguments->Add(new (Z) LiteralNode(
|
| condition_end,
|
| - Integer::ZoneHandle(Z, Integer::New(condition_end.value(), Heap::kOld))));
|
| + Integer::ZoneHandle(Z, Integer::New(condition_end.Pos()))));
|
| + if (message == NULL) {
|
| + message = new (Z) LiteralNode(condition_end, Instance::ZoneHandle(Z));
|
| + }
|
| + arguments->Add(message);
|
| AstNode* assert_throw = MakeStaticCall(
|
| Symbols::AssertionError(),
|
| - Library::PrivateCoreLibName(is_const ? Symbols::CheckConstAssertion()
|
| - : Symbols::CheckAssertion()),
|
| - arguments);
|
| + Library::PrivateCoreLibName(Symbols::ThrowNew()), arguments);
|
|
|
| - return assert_throw;
|
| + AstNode* assertion_check = NULL;
|
| + if (parsed_function()->have_seen_await()) {
|
| + // The await transformation must be done manually because assertions
|
| + // are parsed as statements, not expressions. Thus, we need to check
|
| + // explicitely whether the arguments contain await operators. (Note that
|
| + // we must not parse the arguments with ParseAwaitableExpr(). In the
|
| + // corner case of assert(await a, await b), this would create two
|
| + // sibling scopes containing the temporary values for a and b. Both
|
| + // values would be allocated in the same internal context variable.)
|
| + //
|
| + // Build !condition ? _AsertionError._throwNew(...) : null;
|
| + // We need to use a conditional expression because the await transformer
|
| + // cannot transform if statements.
|
| + assertion_check = new (Z) ConditionalExprNode(
|
| + condition_pos, not_condition, assert_throw,
|
| + new (Z) LiteralNode(condition_pos, Object::null_instance()));
|
| + OpenBlock();
|
| + AwaitTransformer at(current_block_->statements, async_temp_scope_);
|
| + AstNode* transformed_assertion = at.Transform(assertion_check);
|
| + SequenceNode* preamble = CloseBlock();
|
| + preamble->Add(transformed_assertion);
|
| + assertion_check = preamble;
|
| + } else {
|
| + // Build if (!condition) _AsertionError._throwNew(...)
|
| + assertion_check = new (Z)
|
| + IfNode(condition_pos, not_condition,
|
| + NodeAsSequenceNode(condition_pos, assert_throw, NULL), NULL);
|
| + }
|
| + return assertion_check;
|
| }
|
|
|
|
|
|
|