dart-sdk/runtime/vm/parser.cc
Samir Jindel 83c5778345 [kernel/vm] Cleanup NSM forwarding.
Change-Id: Ia2077d2752fbeb45da7d1051cdab0c16d67377d0
Reviewed-on: https://dart-review.googlesource.com/48420
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
2018-03-26 22:34:02 +00:00

14889 lines
583 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/parser.h"
#include "vm/flags.h"
#ifndef DART_PRECOMPILED_RUNTIME
#include "lib/invocation_mirror.h"
#include "platform/utils.h"
#include "vm/ast_transformer.h"
#include "vm/bootstrap.h"
#include "vm/class_finalizer.h"
#include "vm/compiler/aot/precompiler.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/compiler/frontend/kernel_binary_flowgraph.h"
#include "vm/compiler/jit/compiler.h"
#include "vm/compiler_stats.h"
#include "vm/dart_api_impl.h"
#include "vm/dart_entry.h"
#include "vm/growable_array.h"
#include "vm/handles.h"
#include "vm/hash_table.h"
#include "vm/heap.h"
#include "vm/isolate.h"
#include "vm/longjump.h"
#include "vm/native_arguments.h"
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/os.h"
#include "vm/regexp_assembler.h"
#include "vm/resolver.h"
#include "vm/safepoint.h"
#include "vm/scanner.h"
#include "vm/scopes.h"
#include "vm/stack_frame.h"
#include "vm/symbols.h"
#include "vm/tags.h"
#include "vm/timeline.h"
#include "vm/timer.h"
#include "vm/zone.h"
namespace dart {
DEFINE_FLAG(bool, enable_debug_break, false, "Allow use of break \"message\".");
DEFINE_FLAG(bool, trace_parser, false, "Trace parser operations.");
// TODO(floitsch): remove the conditional-directive flag, once we publicly
// committed to the current version.
DEFINE_FLAG(bool,
conditional_directives,
true,
"Enable conditional directives");
DEFINE_FLAG(
bool,
await_is_keyword,
false,
"await and yield are treated as proper keywords in synchronous code.");
DECLARE_FLAG(bool, profile_vm);
DECLARE_FLAG(bool, trace_service);
// Quick access to the current thread, isolate and zone.
#define T (thread())
#define I (isolate())
#define Z (zone())
// Quick synthetic token position.
#define ST(token_pos) ((token_pos).ToSynthetic())
#if defined(DEBUG)
class TraceParser : public ValueObject {
public:
TraceParser(TokenPosition token_pos,
const Script& script,
intptr_t* trace_indent,
const char* msg) {
indent_ = trace_indent;
if (FLAG_trace_parser) {
// Skips tracing of bootstrap libraries.
if (script.HasSource()) {
intptr_t line, column;
script.GetTokenLocation(token_pos, &line, &column);
PrintIndent();
OS::Print("%s (line %" Pd ", col %" Pd ", token %" Pd ")\n", msg, line,
column, token_pos.value());
}
(*indent_)++;
}
}
~TraceParser() {
if (FLAG_trace_parser) {
(*indent_)--;
ASSERT(*indent_ >= 0);
}
}
private:
void PrintIndent() {
for (intptr_t i = 0; i < *indent_; i++) {
OS::Print(". ");
}
}
intptr_t* indent_;
};
#define TRACE_PARSER(s) \
TraceParser __p__(this->TokenPos(), this->script_, &this->trace_indent_, s)
#else // not DEBUG
#define TRACE_PARSER(s)
#endif // DEBUG
class BoolScope : public ValueObject {
public:
BoolScope(bool* addr, bool new_value) : _addr(addr), _saved_value(*addr) {
*_addr = new_value;
}
~BoolScope() { *_addr = _saved_value; }
private:
bool* _addr;
bool _saved_value;
};
// Helper class to save and restore token position.
class Parser::TokenPosScope : public ValueObject {
public:
explicit TokenPosScope(Parser* p) : p_(p) { saved_pos_ = p_->TokenPos(); }
TokenPosScope(Parser* p, TokenPosition pos) : p_(p), saved_pos_(pos) {}
~TokenPosScope() { p_->SetPosition(saved_pos_); }
private:
Parser* p_;
TokenPosition saved_pos_;
DISALLOW_COPY_AND_ASSIGN(TokenPosScope);
};
class RecursionChecker : public ValueObject {
public:
explicit RecursionChecker(Parser* p) : parser_(p) {
parser_->recursion_counter_++;
// This limit also protects against stack overflow in the flow graph builder
// and some optimization passes, which may use more stack than the parser
// for the same function.
// The limit is somewhat arbitrary.
if (parser_->recursion_counter_ > 256) {
parser_->ReportError("stack overflow while parsing");
}
}
~RecursionChecker() { parser_->recursion_counter_--; }
private:
Parser* parser_;
};
static RawTypeArguments* NewTypeArguments(
const GrowableArray<AbstractType*>& objs) {
const TypeArguments& a =
TypeArguments::Handle(TypeArguments::New(objs.length()));
for (int i = 0; i < objs.length(); i++) {
a.SetTypeAt(i, *objs.At(i));
}
// Cannot canonicalize TypeArgument yet as its types may not have been
// finalized yet.
return a.raw();
}
ParsedFunction::ParsedFunction(Thread* thread, const Function& function)
: thread_(thread),
function_(function),
code_(Code::Handle(zone(), function.unoptimized_code())),
node_sequence_(NULL),
regexp_compile_data_(NULL),
instantiator_(NULL),
function_type_arguments_(NULL),
parent_type_arguments_(NULL),
current_context_var_(NULL),
arg_desc_var_(NULL),
expression_temp_var_(NULL),
finally_return_temp_var_(NULL),
deferred_prefixes_(new ZoneGrowableArray<const LibraryPrefix*>()),
guarded_fields_(new ZoneGrowableArray<const Field*>()),
default_parameter_values_(NULL),
raw_type_arguments_var_(NULL),
first_parameter_index_(0),
num_stack_locals_(0),
have_seen_await_expr_(false),
kernel_scopes_(NULL) {
ASSERT(function.IsZoneHandle());
// Every function has a local variable for the current context.
LocalVariable* temp = new (zone())
LocalVariable(function.token_pos(), function.token_pos(),
Symbols::CurrentContextVar(), Object::dynamic_type());
current_context_var_ = temp;
const bool reify_generic_argument =
function.IsGeneric() && Isolate::Current()->reify_generic_functions();
const bool load_optional_arguments = function.HasOptionalParameters();
const bool check_arguments =
(function_.IsClosureFunction() || function_.IsConvertedClosureFunction());
const bool need_argument_descriptor =
load_optional_arguments || check_arguments || reify_generic_argument;
if (need_argument_descriptor) {
arg_desc_var_ = new (zone())
LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::ArgDescVar(), Object::dynamic_type());
}
}
void ParsedFunction::AddToGuardedFields(const Field* field) const {
if ((field->guarded_cid() == kDynamicCid) ||
(field->guarded_cid() == kIllegalCid)) {
return;
}
for (intptr_t j = 0; j < guarded_fields_->length(); j++) {
const Field* other = (*guarded_fields_)[j];
if (field->Original() == other->Original()) {
// Abort background compilation early if the guarded state of this field
// has changed during compilation. We will not be able to commit
// the resulting code anyway.
if (Compiler::IsBackgroundCompilation()) {
if (!other->IsConsistentWith(*field)) {
Compiler::AbortBackgroundCompilation(
Thread::kNoDeoptId,
"Field's guarded state changed during compilation");
}
}
return;
}
}
// Note: the list of guarded fields must contain copies during background
// compilation because we will look at their guarded_cid when copying
// the array of guarded fields from callee into the caller during
// inlining.
ASSERT(!field->IsOriginal() || Thread::Current()->IsMutatorThread());
guarded_fields_->Add(&Field::ZoneHandle(Z, field->raw()));
}
void ParsedFunction::Bailout(const char* origin, const char* reason) const {
Report::MessageF(Report::kBailout, Script::Handle(function_.script()),
function_.token_pos(), Report::AtLocation,
"%s Bailout in %s: %s", origin,
String::Handle(function_.name()).ToCString(), reason);
UNREACHABLE();
}
kernel::ScopeBuildingResult* ParsedFunction::EnsureKernelScopes() {
if (kernel_scopes_ == NULL) {
kernel::StreamingScopeBuilder builder(this);
kernel_scopes_ = builder.BuildScopes();
}
return kernel_scopes_;
}
LocalVariable* ParsedFunction::EnsureExpressionTemp() {
if (!has_expression_temp_var()) {
LocalVariable* temp =
new (Z) LocalVariable(function_.token_pos(), function_.token_pos(),
Symbols::ExprTemp(), Object::dynamic_type());
ASSERT(temp != NULL);
set_expression_temp_var(temp);
}
ASSERT(has_expression_temp_var());
return expression_temp_var();
}
void ParsedFunction::EnsureFinallyReturnTemp(bool is_async) {
if (!has_finally_return_temp_var()) {
LocalVariable* temp =
new (Z) LocalVariable(function_.token_pos(), function_.token_pos(),
Symbols::FinallyRetVal(), Object::dynamic_type());
ASSERT(temp != NULL);
temp->set_is_final();
if (is_async) {
temp->set_is_captured();
}
set_finally_return_temp_var(temp);
}
ASSERT(has_finally_return_temp_var());
}
void ParsedFunction::SetNodeSequence(SequenceNode* node_sequence) {
ASSERT(node_sequence_ == NULL);
ASSERT(node_sequence != NULL);
node_sequence_ = node_sequence;
}
void ParsedFunction::SetRegExpCompileData(
RegExpCompileData* regexp_compile_data) {
ASSERT(regexp_compile_data_ == NULL);
ASSERT(regexp_compile_data != NULL);
regexp_compile_data_ = regexp_compile_data;
}
void ParsedFunction::AddDeferredPrefix(const LibraryPrefix& prefix) {
// 'deferred_prefixes_' are used to invalidate code, but no invalidation is
// needed if --load_deferred_eagerly.
ASSERT(!FLAG_load_deferred_eagerly);
ASSERT(prefix.is_deferred_load());
ASSERT(!prefix.is_loaded());
for (intptr_t i = 0; i < deferred_prefixes_->length(); i++) {
if ((*deferred_prefixes_)[i]->raw() == prefix.raw()) {
return;
}
}
deferred_prefixes_->Add(&LibraryPrefix::ZoneHandle(Z, prefix.raw()));
}
void ParsedFunction::AllocateVariables() {
ASSERT(!function().IsIrregexpFunction());
LocalScope* scope = node_sequence()->scope();
const intptr_t num_fixed_params = function().num_fixed_parameters();
const intptr_t num_opt_params = function().NumOptionalParameters();
const intptr_t num_params = num_fixed_params + num_opt_params;
// Before we start allocating frame indices to variables, we'll setup the
// parameters array, which can be used to access the raw parameters (i.e. not
// the potentially variables which are in the context)
for (intptr_t param = 0; param < function().NumParameters(); ++param) {
LocalVariable* raw_parameter = scope->VariableAt(param);
if (raw_parameter->is_captured()) {
String& tmp = String::ZoneHandle(Z);
tmp = Symbols::FromConcat(T, Symbols::OriginalParam(),
raw_parameter->name());
RELEASE_ASSERT(scope->LocalLookupVariable(tmp) == NULL);
raw_parameter = new LocalVariable(raw_parameter->declaration_token_pos(),
raw_parameter->token_pos(), tmp,
raw_parameter->type());
if (function().HasOptionalParameters()) {
bool ok = scope->AddVariable(raw_parameter);
ASSERT(ok);
// Currently our optimizer cannot prove liveness of variables properly
// when a function has try/catch. It therefore makes the conservative
// estimate that all [LocalVariable]s in the frame are live and spills
// them before call sites (in some shape or form).
//
// Since we are guaranteed to not need that, we tell the try/catch
// spilling mechanism not to care about this variable.
raw_parameter->set_is_captured_parameter(true);
} else {
raw_parameter->set_index(kParamEndSlotFromFp +
function().NumParameters() - param);
}
}
raw_parameters_.Add(raw_parameter);
}
if (function_type_arguments_ != NULL) {
LocalVariable* raw_type_args_parameter = function_type_arguments_;
if (function_type_arguments_->is_captured()) {
String& tmp = String::ZoneHandle(Z);
tmp = Symbols::FromConcat(T, Symbols::OriginalParam(),
function_type_arguments_->name());
ASSERT(scope->LocalLookupVariable(tmp) == NULL);
raw_type_args_parameter =
new LocalVariable(raw_type_args_parameter->declaration_token_pos(),
raw_type_args_parameter->token_pos(), tmp,
raw_type_args_parameter->type());
bool ok = scope->AddVariable(raw_type_args_parameter);
ASSERT(ok);
}
raw_type_arguments_var_ = raw_type_args_parameter;
}
// The copy parameters implementation will still write to local variables
// which we assign indices as with the old CopyParams implementation.
intptr_t parameter_frame_index_start;
intptr_t reamining_local_variables_start;
{
// Compute start indices to parameters and locals, and the number of
// parameters to copy.
if (num_opt_params == 0) {
// Parameter i will be at fp[kParamEndSlotFromFp + num_params - i] and
// local variable j will be at fp[kFirstLocalSlotFromFp - j].
parameter_frame_index_start = first_parameter_index_ =
kParamEndSlotFromFp + num_params;
reamining_local_variables_start = kFirstLocalSlotFromFp;
} else {
// Parameter i will be at fp[kFirstLocalSlotFromFp - i] and local variable
// j will be at fp[kFirstLocalSlotFromFp - num_params - j].
parameter_frame_index_start = first_parameter_index_ =
kFirstLocalSlotFromFp;
reamining_local_variables_start = first_parameter_index_ - num_params;
}
}
if (function_type_arguments_ != NULL && num_opt_params > 0) {
reamining_local_variables_start--;
}
// Allocate parameters and local variables, either in the local frame or
// in the context(s).
bool found_captured_variables = false;
int next_free_frame_index = scope->AllocateVariables(
parameter_frame_index_start, num_params,
parameter_frame_index_start > 0 ? kFirstLocalSlotFromFp
: kFirstLocalSlotFromFp - num_params,
NULL, &found_captured_variables);
// Frame indices are relative to the frame pointer and are decreasing.
num_stack_locals_ = -(next_free_frame_index - kFirstLocalSlotFromFp);
}
struct CatchParamDesc {
CatchParamDesc()
: token_pos(TokenPosition::kNoSource),
type(NULL),
name(NULL),
var(NULL) {}
TokenPosition token_pos;
const AbstractType* type;
const String* name;
LocalVariable* var;
};
void ParsedFunction::AllocateIrregexpVariables(intptr_t num_stack_locals) {
ASSERT(function().IsIrregexpFunction());
ASSERT(function().NumOptionalParameters() == 0);
const intptr_t num_params = function().num_fixed_parameters();
ASSERT(num_params == RegExpMacroAssembler::kParamCount);
// Compute start indices to parameters and locals, and the number of
// parameters to copy.
// Parameter i will be at fp[kParamEndSlotFromFp + num_params - i] and
// local variable j will be at fp[kFirstLocalSlotFromFp - j].
first_parameter_index_ = kParamEndSlotFromFp + num_params;
// Frame indices are relative to the frame pointer and are decreasing.
num_stack_locals_ = num_stack_locals;
}
struct Parser::Block : public ZoneAllocated {
Block(Block* outer_block, LocalScope* local_scope, SequenceNode* seq)
: parent(outer_block), scope(local_scope), statements(seq) {
ASSERT(scope != NULL);
ASSERT(statements != NULL);
}
Block* parent; // Enclosing block, or NULL if outermost.
LocalScope* scope;
SequenceNode* statements;
};
// Class which describes an inlined finally block which is used to generate
// inlined code for the finally blocks when there is an exit from a try
// block using 'return', 'break' or 'continue'.
class Parser::TryStack : public ZoneAllocated {
public:
TryStack(Block* try_block, TryStack* outer_try, intptr_t try_index)
: try_block_(try_block),
inlined_finally_nodes_(),
outer_try_(outer_try),
try_index_(try_index),
inside_catch_(false),
inside_finally_(false) {}
TryStack* outer_try() const { return outer_try_; }
Block* try_block() const { return try_block_; }
intptr_t try_index() const { return try_index_; }
bool inside_catch() const { return inside_catch_; }
void enter_catch() { inside_catch_ = true; }
bool inside_finally() const { return inside_finally_; }
void enter_finally() { inside_finally_ = true; }
void exit_finally() { inside_finally_ = false; }
void AddNodeForFinallyInlining(AstNode* node);
void RemoveJumpToLabel(SourceLabel* label);
AstNode* GetNodeToInlineFinally(int index) {
if (0 <= index && index < inlined_finally_nodes_.length()) {
return inlined_finally_nodes_[index];
}
return NULL;
}
private:
Block* try_block_;
GrowableArray<AstNode*> inlined_finally_nodes_;
TryStack* outer_try_;
const intptr_t try_index_;
bool inside_catch_; // True when parsing a catch clause of this try.
bool inside_finally_; // True when parsing a finally clause of an inner try
// of this try.
DISALLOW_COPY_AND_ASSIGN(TryStack);
};
void Parser::TryStack::AddNodeForFinallyInlining(AstNode* node) {
inlined_finally_nodes_.Add(node);
}
void Parser::TryStack::RemoveJumpToLabel(SourceLabel* label) {
int i = 0;
while (i < inlined_finally_nodes_.length()) {
if (inlined_finally_nodes_[i]->IsJumpNode()) {
JumpNode* jump = inlined_finally_nodes_[i]->AsJumpNode();
if (jump->label() == label) {
// Shift remaining entries left and delete last entry.
for (int j = i + 1; j < inlined_finally_nodes_.length(); j++) {
inlined_finally_nodes_[j - 1] = inlined_finally_nodes_[j];
}
inlined_finally_nodes_.RemoveLast();
continue;
}
}
i++;
}
}
// For parsing a compilation unit.
Parser::Parser(const Script& script,
const Library& library,
TokenPosition token_pos)
: thread_(Thread::Current()),
isolate_(thread()->isolate()),
allocation_space_(thread_->IsMutatorThread() ? Heap::kNew : Heap::kOld),
script_(Script::Handle(zone(), script.raw())),
tokens_iterator_(zone(),
TokenStream::Handle(zone(), script.tokens()),
token_pos),
token_kind_(Token::kILLEGAL),
current_block_(NULL),
is_top_level_(false),
await_is_keyword_(false),
current_member_(NULL),
allow_function_literals_(true),
parsed_function_(NULL),
innermost_function_(Function::Handle(zone())),
literal_token_(LiteralToken::Handle(zone())),
current_class_(Class::Handle(zone())),
library_(Library::Handle(zone(), library.raw())),
try_stack_(NULL),
last_used_try_index_(0),
unregister_pending_function_(false),
async_temp_scope_(NULL),
trace_indent_(0),
recursion_counter_(0) {
ASSERT(tokens_iterator_.IsValid());
ASSERT(!library.IsNull());
}
// For parsing a function.
Parser::Parser(const Script& script,
ParsedFunction* parsed_function,
TokenPosition token_pos)
: thread_(Thread::Current()),
isolate_(thread()->isolate()),
allocation_space_(thread_->IsMutatorThread() ? Heap::kNew : Heap::kOld),
script_(Script::Handle(zone(), script.raw())),
tokens_iterator_(zone(),
TokenStream::Handle(zone(), script.tokens()),
token_pos),
token_kind_(Token::kILLEGAL),
current_block_(NULL),
is_top_level_(false),
await_is_keyword_(false),
current_member_(NULL),
allow_function_literals_(true),
parsed_function_(parsed_function),
innermost_function_(
Function::Handle(zone(), parsed_function->function().raw())),
literal_token_(LiteralToken::Handle(zone())),
current_class_(
Class::Handle(zone(), parsed_function->function().Owner())),
library_(Library::Handle(
zone(),
Class::Handle(zone(), parsed_function->function().origin())
.library())),
try_stack_(NULL),
last_used_try_index_(0),
unregister_pending_function_(false),
async_temp_scope_(NULL),
trace_indent_(0),
recursion_counter_(0) {
ASSERT(tokens_iterator_.IsValid());
ASSERT(!current_function().IsNull());
EnsureExpressionTemp();
}
Parser::~Parser() {
if (unregister_pending_function_) {
const GrowableObjectArray& pending_functions =
GrowableObjectArray::Handle(T->pending_functions());
ASSERT(!pending_functions.IsNull());
ASSERT(pending_functions.Length() > 0);
ASSERT(pending_functions.At(pending_functions.Length() - 1) ==
current_function().raw());
pending_functions.RemoveLast();
}
}
// Each try in this function gets its own try index.
// See definition of RawPcDescriptors::PcDescriptor.
int16_t Parser::AllocateTryIndex() {
if (!Utils::IsInt(16, last_used_try_index_ - 1)) {
ReportError("too many nested try statements");
}
return last_used_try_index_++;
}
void Parser::SetScript(const Script& script, TokenPosition token_pos) {
script_ = script.raw();
tokens_iterator_.SetStream(TokenStream::Handle(Z, script.tokens()),
token_pos);
token_kind_ = Token::kILLEGAL;
}
bool Parser::SetAllowFunctionLiterals(bool value) {
bool current_value = allow_function_literals_;
allow_function_literals_ = value;
return current_value;
}
const Function& Parser::current_function() const {
ASSERT(parsed_function() != NULL);
return parsed_function()->function();
}
const Function& Parser::innermost_function() const {
return innermost_function_;
}
int Parser::FunctionLevel() const {
if (current_block_ != NULL) {
return current_block_->scope->function_level();
}
return 0;
}
const Class& Parser::current_class() const {
return current_class_;
}
void Parser::set_current_class(const Class& value) {
current_class_ = value.raw();
}
void Parser::SetPosition(TokenPosition position) {
tokens_iterator_.SetCurrentPosition(position);
token_kind_ = Token::kILLEGAL;
prev_token_pos_ = position;
}
// Set state and increments generational count so that thge background compiler
// can detect if loading/top-level-parsing occured during compilation.
class TopLevelParsingScope : public StackResource {
public:
explicit TopLevelParsingScope(Thread* thread) : StackResource(thread) {
isolate()->IncrTopLevelParsingCount();
}
~TopLevelParsingScope() {
isolate()->DecrTopLevelParsingCount();
isolate()->IncrLoadingInvalidationGen();
}
};
void Parser::ParseCompilationUnit(const Library& library,
const Script& script) {
Thread* thread = Thread::Current();
ASSERT(thread->long_jump_base()->IsSafeToJump());
CSTAT_TIMER_SCOPE(thread, parser_timer);
#ifndef PRODUCT
VMTagScope tagScope(thread, VMTag::kCompileTopLevelTagId);
TimelineDurationScope tds(thread, Timeline::GetCompilerStream(),
"CompileTopLevel");
if (tds.enabled()) {
tds.SetNumArguments(1);
tds.CopyArgument(0, "script", String::Handle(script.url()).ToCString());
}
#endif
TopLevelParsingScope scope(thread);
Parser parser(script, library, TokenPosition::kMinSource);
parser.ParseTopLevel();
}
void Parser::ComputeCurrentToken() {
ASSERT(token_kind_ == Token::kILLEGAL);
token_kind_ = tokens_iterator_.CurrentTokenKind();
if (token_kind_ == Token::kERROR) {
ReportError(TokenPos(), "%s", CurrentLiteral()->ToCString());
}
}
Token::Kind Parser::LookaheadToken(int num_tokens) {
return tokens_iterator_.LookaheadTokenKind(num_tokens);
}
String* Parser::CurrentLiteral() const {
String& result = String::ZoneHandle(Z, tokens_iterator_.CurrentLiteral());
return &result;
}
RawDouble* Parser::CurrentDoubleLiteral() const {
literal_token_ ^= tokens_iterator_.CurrentToken();
ASSERT(literal_token_.kind() == Token::kDOUBLE);
return Double::RawCast(literal_token_.value());
}
RawInteger* Parser::CurrentIntegerLiteral() const {
literal_token_ ^= tokens_iterator_.CurrentToken();
ASSERT(literal_token_.kind() == Token::kINTEGER);
RawInteger* ri = Integer::RawCast(literal_token_.value());
return ri;
}
struct ParamDesc {
ParamDesc()
: type(NULL),
name_pos(TokenPosition::kNoSource),
name(NULL),
default_value(NULL),
metadata(NULL),
var(NULL),
is_final(false),
is_field_initializer(false),
has_explicit_type(false),
is_covariant(false) {}
const AbstractType* type;
TokenPosition name_pos;
const String* name;
const Instance* default_value; // NULL if not an optional parameter.
const Object* metadata; // NULL if no metadata or metadata not evaluated.
LocalVariable* var; // Scope variable allocated for this parameter.
bool is_final;
bool is_field_initializer;
bool has_explicit_type;
bool is_covariant;
};
struct ParamList {
ParamList() { Clear(); }
void Clear() {
num_fixed_parameters = 0;
num_optional_parameters = 0;
has_optional_positional_parameters = false;
has_optional_named_parameters = false;
has_explicit_default_values = false;
has_field_initializer = false;
has_covariant = false;
implicitly_final = false;
skipped = false;
this->parameters = new ZoneGrowableArray<ParamDesc>();
}
void AddFinalParameter(TokenPosition name_pos,
const String* name,
const AbstractType* type) {
this->num_fixed_parameters++;
ParamDesc param;
param.name_pos = name_pos;
param.name = name;
param.is_final = true;
param.type = type;
this->parameters->Add(param);
}
void AddReceiver(const AbstractType* receiver_type, TokenPosition token_pos) {
ASSERT(this->parameters->is_empty());
AddFinalParameter(token_pos, &Symbols::This(), receiver_type);
}
void EraseParameterTypes() {
const int num_parameters = parameters->length();
for (int i = 0; i < num_parameters; i++) {
(*parameters)[i].type = &Object::dynamic_type();
}
}
// Make the parameter variables visible/invisible.
void SetInvisible(bool invisible) {
const intptr_t num_params = parameters->length();
for (int i = 0; i < num_params; i++) {
ParamDesc& param = (*parameters)[i];
ASSERT(param.var != NULL);
param.var->set_invisible(invisible);
}
}
void HideInitFormals() {
const intptr_t num_params = parameters->length();
for (int i = 0; i < num_params; i++) {
ParamDesc& param = (*parameters)[i];
if (param.is_field_initializer) {
ASSERT(param.var != NULL);
param.var->set_invisible(true);
}
}
}
void SetImplicitlyFinal() { implicitly_final = true; }
int num_fixed_parameters;
int num_optional_parameters;
bool has_optional_positional_parameters;
bool has_optional_named_parameters;
bool has_explicit_default_values;
bool has_field_initializer;
bool has_covariant;
bool implicitly_final;
bool skipped;
ZoneGrowableArray<ParamDesc>* parameters;
};
struct MemberDesc {
MemberDesc() { Clear(); }
void Clear() {
has_abstract = false;
has_external = false;
has_covariant = false;
has_final = false;
has_const = false;
has_static = false;
has_var = false;
has_factory = false;
has_operator = false;
has_native = false;
metadata_pos = TokenPosition::kNoSource;
operator_token = Token::kILLEGAL;
type = NULL;
name_pos = TokenPosition::kNoSource;
name = NULL;
redirect_name = NULL;
dict_name = NULL;
params.Clear();
kind = RawFunction::kRegularFunction;
field_ = NULL;
}
bool IsConstructor() const {
return (kind == RawFunction::kConstructor) && !has_static;
}
bool IsFactory() const {
return (kind == RawFunction::kConstructor) && has_static;
}
bool IsFactoryOrConstructor() const {
return (kind == RawFunction::kConstructor);
}
bool IsGetter() const { return kind == RawFunction::kGetterFunction; }
bool IsSetter() const { return kind == RawFunction::kSetterFunction; }
const char* ToCString() const {
if (field_ != NULL) {
return "field";
} else if (IsConstructor()) {
return "constructor";
} else if (IsFactory()) {
return "factory";
} else if (IsGetter()) {
return "getter";
} else if (IsSetter()) {
return "setter";
}
return "method";
}
String* DictName() const { return (dict_name != NULL) ? dict_name : name; }
bool has_abstract;
bool has_external;
bool has_covariant;
bool has_final;
bool has_const;
bool has_static;
bool has_var;
bool has_factory;
bool has_operator;
bool has_native;
TokenPosition metadata_pos;
Token::Kind operator_token;
const AbstractType* type;
TokenPosition name_pos;
TokenPosition decl_begin_pos;
String* name;
// For constructors: NULL or name of redirected to constructor.
String* redirect_name;
// dict_name is the name used for the class namespace, if it
// differs from 'name'.
// For constructors: NULL for unnamed constructor,
// identifier after classname for named constructors.
// For getters and setters: unmangled name.
String* dict_name;
ParamList params;
RawFunction::Kind kind;
// NULL for functions, field object for static or instance fields.
Field* field_;
};
class ClassDesc : public ValueObject {
public:
ClassDesc(Zone* zone,
const Class& cls,
const String& cls_name,
bool is_interface,
TokenPosition token_pos)
: zone_(zone),
clazz_(cls),
class_name_(cls_name),
token_pos_(token_pos),
functions_(zone, 4),
fields_(zone, 4) {}
void AddFunction(const Function& function) {
functions_.Add(&Function::ZoneHandle(zone_, function.raw()));
}
const GrowableArray<const Function*>& functions() const { return functions_; }
void AddField(const Field& field) {
fields_.Add(&Field::ZoneHandle(zone_, field.raw()));
}
const GrowableArray<const Field*>& fields() const { return fields_; }
const Class& clazz() const { return clazz_; }
const String& class_name() const { return class_name_; }
bool has_constructor() const {
for (int i = 0; i < functions_.length(); i++) {
const Function* func = functions_.At(i);
if (func->kind() == RawFunction::kConstructor) {
return true;
}
}
return false;
}
TokenPosition token_pos() const { return token_pos_; }
void AddMember(const MemberDesc& member) { members_.Add(member); }
const GrowableArray<MemberDesc>& members() const { return members_; }
MemberDesc* LookupMember(const String& name) const {
for (int i = 0; i < members_.length(); i++) {
if (name.Equals(*members_[i].name)) {
return &members_[i];
}
}
return NULL;
}
RawArray* MakeFunctionsArray() {
const intptr_t len = functions_.length();
const Array& res = Array::Handle(zone_, Array::New(len, Heap::kOld));
for (intptr_t i = 0; i < len; i++) {
res.SetAt(i, *functions_[i]);
}
return res.raw();
}
private:
Zone* zone_;
const Class& clazz_;
const String& class_name_;
TokenPosition token_pos_; // Token index of "class" keyword.
GrowableArray<const Function*> functions_;
GrowableArray<const Field*> fields_;
GrowableArray<MemberDesc> members_;
};
class TopLevel : public ValueObject {
public:
explicit TopLevel(Zone* zone)
: zone_(zone), fields_(zone, 4), functions_(zone, 4) {}
void AddField(const Field& field) {
fields_.Add(&Field::ZoneHandle(zone_, field.raw()));
}
void AddFunction(const Function& function) {
functions_.Add(&Function::ZoneHandle(zone_, function.raw()));
}
const GrowableArray<const Field*>& fields() const { return fields_; }
const GrowableArray<const Function*>& functions() const { return functions_; }
private:
Zone* zone_;
GrowableArray<const Field*> fields_;
GrowableArray<const Function*> functions_;
};
void Parser::ParseClass(const Class& cls) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
const int64_t num_tokes_before = STAT_VALUE(thread, num_tokens_consumed);
#ifndef PRODUCT
TimelineDurationScope tds(thread, Timeline::GetCompilerStream(),
"ParseClass");
if (tds.enabled()) {
tds.SetNumArguments(1);
tds.CopyArgument(0, "class", String::Handle(cls.Name()).ToCString());
}
#endif
if (!cls.is_synthesized_class()) {
ASSERT(thread->long_jump_base()->IsSafeToJump());
CSTAT_TIMER_SCOPE(thread, parser_timer);
const Script& script = Script::Handle(zone, cls.script());
const Library& lib = Library::Handle(zone, cls.library());
Parser parser(script, lib, cls.token_pos());
parser.ParseClassDefinition(cls);
} else if (cls.is_enum_class()) {
ASSERT(thread->long_jump_base()->IsSafeToJump());
CSTAT_TIMER_SCOPE(thread, parser_timer);
const Script& script = Script::Handle(zone, cls.script());
const Library& lib = Library::Handle(zone, cls.library());
Parser parser(script, lib, cls.token_pos());
parser.ParseEnumDefinition(cls);
}
const int64_t num_tokes_after = STAT_VALUE(thread, num_tokens_consumed);
INC_STAT(thread, num_class_tokens, num_tokes_after - num_tokes_before);
}
bool Parser::FieldHasFunctionLiteralInitializer(const Field& field,
TokenPosition* start,
TokenPosition* end) {
if (!field.has_initializer()) {
return false;
}
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
const Class& cls = Class::Handle(zone, field.Owner());
const Script& script = Script::Handle(zone, field.Script());
const Library& lib = Library::Handle(zone, cls.library());
Parser parser(script, lib, field.token_pos());
return parser.GetFunctionLiteralInitializerRange(field, start, end);
}
bool Parser::GetFunctionLiteralInitializerRange(const Field& field,
TokenPosition* start,
TokenPosition* end) {
ASSERT(field.has_initializer());
// Since |field| has an initializer, skip until '='.
while (CurrentToken() != Token::kASSIGN) {
ConsumeToken();
}
// Skip past the '=' as well.
ConsumeToken();
*start = TokenPos();
if (IsFunctionLiteral()) {
SkipExpr();
*end = PrevTokenPos();
return true;
}
return false;
}
RawObject* Parser::ParseFunctionParameters(const Function& func) {
ASSERT(!func.IsNull());
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
Thread* thread = Thread::Current();
StackZone stack_zone(thread);
Zone* zone = stack_zone.GetZone();
const Script& script = Script::Handle(zone, func.script());
const Class& owner = Class::Handle(zone, func.Owner());
ASSERT(!owner.IsNull());
ParsedFunction* parsed_function =
new ParsedFunction(thread, Function::ZoneHandle(zone, func.raw()));
Parser parser(script, parsed_function, func.token_pos());
parser.SkipFunctionPreamble();
const bool use_function_type_syntax = false;
const bool allow_explicit_default_values = true;
const bool evaluate_metadata = true;
ParamList params;
parser.ParseFormalParameterList(use_function_type_syntax,
allow_explicit_default_values,
evaluate_metadata, &params);
ParamDesc* param = params.parameters->data();
const int param_cnt =
params.num_fixed_parameters + params.num_optional_parameters;
const Array& param_descriptor =
Array::Handle(Array::New(param_cnt * kParameterEntrySize, Heap::kOld));
for (int i = 0, j = 0; i < param_cnt; i++, j += kParameterEntrySize) {
param_descriptor.SetAt(j + kParameterIsFinalOffset,
param[i].is_final ? Bool::True() : Bool::False());
param_descriptor.SetAt(j + kParameterDefaultValueOffset,
(param[i].default_value == NULL)
? Object::null_instance()
: *(param[i].default_value));
const Object* metadata = param[i].metadata;
if ((metadata != NULL) && (*metadata).IsError()) {
return metadata->raw(); // Error evaluating the metadata.
}
param_descriptor.SetAt(j + kParameterMetadataOffset,
(param[i].metadata == NULL)
? Object::null_instance()
: *(param[i].metadata));
}
return param_descriptor.raw();
} else {
Thread* thread = Thread::Current();
Error& error = Error::Handle();
error = thread->sticky_error();
thread->clear_sticky_error();
return error.raw();
}
UNREACHABLE();
return Object::null();
}
bool Parser::ParseFormalParameters(const Function& func, ParamList* params) {
ASSERT(!func.IsNull());
// This is currently only used for constructors. To handle all kinds
// of functions, special cases for getters and possibly other kinds
// need to be added.
ASSERT(func.kind() == RawFunction::kConstructor);
ASSERT(!func.IsRedirectingFactory());
// Implicit constructors have no source, no user-defined formal parameters.
if (func.IsImplicitConstructor()) {
return true;
}
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
const Script& script = Script::Handle(func.script());
const Class& owner = Class::Handle(func.Owner());
ASSERT(!owner.IsNull());
ParsedFunction* parsed_function =
new ParsedFunction(Thread::Current(), Function::ZoneHandle(func.raw()));
Parser parser(script, parsed_function, func.token_pos());
parser.SkipFunctionPreamble();
const bool use_function_type_syntax = false;
const bool allow_explicit_default_values = true;
const bool evaluate_metadata = true;
parser.ParseFormalParameterList(use_function_type_syntax,
allow_explicit_default_values,
evaluate_metadata, params);
return true;
} else {
Thread::Current()->clear_sticky_error();
params->Clear();
return false;
}
UNREACHABLE();
return false;
}
void Parser::ParseFunction(ParsedFunction* parsed_function) {
Thread* thread = parsed_function->thread();
ASSERT(thread == Thread::Current());
Zone* zone = thread->zone();
CSTAT_TIMER_SCOPE(thread, parser_timer);
INC_STAT(thread, num_functions_parsed, 1);
#ifndef PRODUCT
VMTagScope tagScope(thread, VMTag::kCompileParseFunctionTagId,
FLAG_profile_vm);
TimelineDurationScope tds(thread, Timeline::GetCompilerStream(),
"ParseFunction");
#endif // !PRODUCT
ASSERT(thread->long_jump_base()->IsSafeToJump());
ASSERT(parsed_function != NULL);
const Function& func = parsed_function->function();
const Script& script = Script::Handle(zone, func.script());
Parser parser(script, parsed_function, func.token_pos());
#ifndef PRODUCT
if (tds.enabled()) {
tds.SetNumArguments(1);
tds.CopyArgument(0, "function", String::Handle(func.name()).ToCString());
}
#endif // !PRODUCT
SequenceNode* node_sequence = NULL;
switch (func.kind()) {
case RawFunction::kImplicitClosureFunction:
node_sequence = parser.ParseImplicitClosure(func);
break;
case RawFunction::kClosureFunction:
case RawFunction::kRegularFunction:
case RawFunction::kGetterFunction:
case RawFunction::kSetterFunction:
case RawFunction::kConstructor:
// The call to a redirecting factory is redirected.
ASSERT(!func.IsRedirectingFactory());
if (!func.IsImplicitConstructor()) {
parser.SkipFunctionPreamble();
}
node_sequence = parser.ParseFunc(func, false);
break;
case RawFunction::kImplicitGetter:
ASSERT(!func.is_static());
node_sequence = parser.ParseInstanceGetter(func);
break;
case RawFunction::kImplicitSetter:
ASSERT(!func.is_static());
node_sequence = parser.ParseInstanceSetter(func);
break;
case RawFunction::kImplicitStaticFinalGetter:
node_sequence = parser.ParseStaticFinalGetter(func);
INC_STAT(thread, num_implicit_final_getters, 1);
break;
case RawFunction::kMethodExtractor:
node_sequence = parser.ParseMethodExtractor(func);
INC_STAT(thread, num_method_extractors, 1);
break;
case RawFunction::kNoSuchMethodDispatcher:
node_sequence = parser.ParseNoSuchMethodDispatcher(func);
break;
case RawFunction::kInvokeFieldDispatcher:
node_sequence = parser.ParseInvokeFieldDispatcher(func);
break;
case RawFunction::kIrregexpFunction:
UNREACHABLE(); // Irregexp functions have their own parser.
default:
UNREACHABLE();
}
if (parsed_function->has_expression_temp_var()) {
node_sequence->scope()->AddVariable(parsed_function->expression_temp_var());
}
node_sequence->scope()->AddVariable(parsed_function->current_context_var());
if (parsed_function->has_finally_return_temp_var()) {
node_sequence->scope()->AddVariable(
parsed_function->finally_return_temp_var());
}
parsed_function->SetNodeSequence(node_sequence);
// The instantiators may be required at run time for generic type checks or
// allocation of generic types.
if (parser.IsInstantiatorRequired()) {
// In the case of a local function, only set the instantiator if the
// receiver (or type arguments parameter of a factory) was captured.
LocalVariable* instantiator = NULL;
const bool kTestOnly = true;
if (parser.current_function().IsInFactoryScope()) {
instantiator = parser.LookupTypeArgumentsParameter(node_sequence->scope(),
kTestOnly);
} else {
instantiator = parser.LookupReceiver(node_sequence->scope(), kTestOnly);
}
if (!parser.current_function().IsLocalFunction() ||
((instantiator != NULL) && instantiator->is_captured())) {
parsed_function->set_instantiator(instantiator);
}
}
// ParseFunc has recorded the generic function type arguments variable.
ASSERT(!Isolate::Current()->reify_generic_functions() ||
!parser.current_function().IsGeneric() ||
(parsed_function->function_type_arguments() != NULL));
}
RawObject* Parser::ParseMetadata(const Field& meta_data) {
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
Thread* thread = Thread::Current();
StackZone stack_zone(thread);
Zone* zone = stack_zone.GetZone();
const Class& owner_class = Class::Handle(zone, meta_data.Owner());
const Script& script = Script::Handle(zone, meta_data.Script());
const TokenPosition token_pos = meta_data.token_pos();
// Parsing metadata can involve following paths in the parser that are
// normally used for expressions and assume current_function is non-null,
// so we create a fake function to use as the current_function rather than
// scattering special cases throughout the parser.
const Function& fake_function = Function::ZoneHandle(
zone,
Function::New(Symbols::At(), RawFunction::kRegularFunction,
true, // is_static
false, // is_const
false, // is_abstract
false, // is_external
false, // is_native
Object::Handle(zone, meta_data.RawOwner()), token_pos));
fake_function.set_is_debuggable(false);
ParsedFunction* parsed_function = new ParsedFunction(thread, fake_function);
Parser parser(script, parsed_function, token_pos);
parser.set_current_class(owner_class);
parser.OpenFunctionBlock(fake_function);
RawObject* metadata = parser.EvaluateMetadata();
return metadata;
} else {
Thread* thread = Thread::Current();
StackZone stack_zone(thread);
Zone* zone = stack_zone.GetZone();
Error& error = Error::Handle(zone);
error = thread->sticky_error();
thread->clear_sticky_error();
return error.raw();
}
UNREACHABLE();
return Object::null();
}
RawArray* Parser::EvaluateMetadata() {
CheckToken(Token::kAT, "Metadata character '@' expected");
GrowableObjectArray& meta_values =
GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld));
while (CurrentToken() == Token::kAT) {
ConsumeToken();
TokenPosition expr_pos = TokenPos();
if (!IsIdentifier()) {
ExpectIdentifier("identifier expected");
}
// Reject expressions with deferred library prefix eagerly.
Object& obj =
Object::Handle(Z, library_.LookupLocalObject(*CurrentLiteral()));
if (!obj.IsNull() && obj.IsLibraryPrefix()) {
if (LibraryPrefix::Cast(obj).is_deferred_load()) {
ReportError("Metadata must be compile-time constant");
}
}
AstNode* expr = NULL;
if ((LookaheadToken(1) == Token::kLPAREN) ||
((LookaheadToken(1) == Token::kPERIOD) &&
(LookaheadToken(3) == Token::kLPAREN)) ||
((LookaheadToken(1) == Token::kPERIOD) &&
(LookaheadToken(3) == Token::kPERIOD) &&
(LookaheadToken(5) == Token::kLPAREN))) {
expr = ParseNewOperator(Token::kCONST);
} else {
// Can be x, C.x, or L.C.x.
expr = ParsePrimary(); // Consumes x, C or L.C.
Class& cls = Class::Handle(Z);
if (expr->IsPrimaryNode()) {
PrimaryNode* primary_node = expr->AsPrimaryNode();
if (primary_node->primary().IsClass()) {
// If the primary node referred to a class we are loading a
// qualified static field.
cls ^= primary_node->primary().raw();
} else {
ReportError(expr_pos,
"Metadata expressions must refer to a const field "
"or constructor");
}
}
if (CurrentToken() == Token::kPERIOD) {
// C.x or L.C.X.
if (cls.IsNull()) {
ReportError(expr_pos,
"Metadata expressions must refer to a const field "
"or constructor");
}
ConsumeToken();
const TokenPosition ident_pos = TokenPos();
String* ident = ExpectIdentifier("identifier expected");
const Field& field = Field::Handle(Z, cls.LookupStaticField(*ident));
if (field.IsNull()) {
ReportError(ident_pos, "Class '%s' has no field '%s'",
cls.ToCString(), ident->ToCString());
}
if (!field.is_const()) {
ReportError(ident_pos, "Field '%s' of class '%s' is not const",
ident->ToCString(), cls.ToCString());
}
expr = GenerateStaticFieldLookup(field, ident_pos);
}
}
if (expr->EvalConstExpr() == NULL) {
ReportError(expr_pos, "expression must be a compile-time constant");
}
const Instance& val = EvaluateConstExpr(expr_pos, expr);
meta_values.Add(val, Heap::kOld);
}
return Array::MakeFixedLength(meta_values);
}
SequenceNode* Parser::ParseStaticInitializer() {
ExpectIdentifier("field name expected");
CheckToken(Token::kASSIGN, "field initialier expected");
ConsumeToken();
OpenFunctionBlock(parsed_function()->function());
TokenPosition expr_pos = TokenPos();
AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades);
ReturnNode* ret = new (Z) ReturnNode(expr_pos, expr);
current_block_->statements->Add(ret);
return CloseBlock();
}
ParsedFunction* Parser::ParseStaticFieldInitializer(const Field& field) {
ASSERT(field.is_static());
Thread* thread = Thread::Current();
// TODO(koda): Should there be a StackZone here?
Zone* zone = thread->zone();
#ifndef PRODUCT
VMTagScope tagScope(thread, VMTag::kCompileParseFunctionTagId,
FLAG_profile_vm);
TimelineDurationScope tds(thread, Timeline::GetCompilerStream(),
"ParseStaticFieldInitializer");
#endif // !PRODUCT
const String& field_name = String::Handle(zone, field.name());
String& init_name = String::Handle(
zone, Symbols::FromConcat(thread, Symbols::InitPrefix(), field_name));
const Script& script = Script::Handle(zone, field.Script());
Object& initializer_owner = Object::Handle(field.Owner());
initializer_owner = PatchClass::New(Class::Handle(field.Owner()), script);
const Function& initializer = Function::ZoneHandle(
zone, Function::New(init_name, RawFunction::kImplicitStaticFinalGetter,
true, // static
false, // !const
false, // !abstract
false, // !external
false, // !native
initializer_owner, field.token_pos()));
initializer.set_result_type(AbstractType::Handle(zone, field.type()));
// Static initializer functions are hidden from the user.
// Since they are only executed once, we avoid inlining them.
// After the field is initialized, the compiler can eliminate
// the call to the static initializer.
initializer.set_is_reflectable(false);
initializer.set_is_debuggable(false);
initializer.set_is_inlinable(false);
ParsedFunction* parsed_function = new ParsedFunction(thread, initializer);
Parser parser(script, parsed_function, field.token_pos());
SequenceNode* body = parser.ParseStaticInitializer();
parsed_function->SetNodeSequence(body);
if (parsed_function->has_expression_temp_var()) {
body->scope()->AddVariable(parsed_function->expression_temp_var());
}
body->scope()->AddVariable(parsed_function->current_context_var());
if (parsed_function->has_finally_return_temp_var()) {
body->scope()->AddVariable(parsed_function->finally_return_temp_var());
}
// The instantiator is not required in a static expression.
ASSERT(!parser.IsInstantiatorRequired());
return parsed_function;
}
SequenceNode* Parser::ParseStaticFinalGetter(const Function& func) {
TRACE_PARSER("ParseStaticFinalGetter");
ASSERT(func.num_fixed_parameters() == 0); // static.
ASSERT(!func.HasOptionalParameters());
ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
OpenFunctionBlock(func);
TokenPosition ident_pos = TokenPos();
const String& field_name = *ExpectIdentifier("field name expected");
const Class& field_class = Class::Handle(Z, func.Owner());
const Field& field =
Field::ZoneHandle(Z, field_class.LookupStaticField(field_name));
ASSERT(!field.IsNull());
// Static final fields must have an initializer.
ExpectToken(Token::kASSIGN);
const TokenPosition expr_pos = TokenPos();
if (field.is_const()) {
// We don't want to use ParseConstExpr() here because we don't want
// the constant folding code to create, compile and execute a code
// fragment to evaluate the expression. Instead, we just make sure
// the static const field initializer is a constant expression and
// leave the evaluation to the getter function.
AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades);
// This getter will only be called once at compile time.
if (expr->EvalConstExpr() == NULL) {
ReportError(expr_pos, "initializer is not a valid compile-time constant");
}
ReturnNode* return_node = new ReturnNode(ident_pos, expr);
current_block_->statements->Add(return_node);
} else {
// This getter may be called each time the static field is accessed.
// Call runtime support to parse and evaluate the initializer expression.
// The runtime function will detect circular dependencies in expressions
// and handle errors while evaluating the expression.
current_block_->statements->Add(new (Z)
InitStaticFieldNode(ident_pos, field));
ReturnNode* return_node =
new ReturnNode(ident_pos, new LoadStaticFieldNode(ident_pos, field));
current_block_->statements->Add(return_node);
}
return CloseBlock();
}
// Create AstNodes for an implicit instance getter method:
// LoadLocalNode 0 ('this');
// LoadInstanceFieldNode (field_name);
// ReturnNode (field's value);
SequenceNode* Parser::ParseInstanceGetter(const Function& func) {
TRACE_PARSER("ParseInstanceGetter");
ParamList params;
// func.token_pos() points to the name of the field.
const TokenPosition ident_pos = func.token_pos();
ASSERT(current_class().raw() == func.Owner());
params.AddReceiver(ReceiverType(current_class()), ident_pos);
ASSERT(func.num_fixed_parameters() == 1); // receiver.
ASSERT(!func.HasOptionalParameters());
ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
// Receiver is local 0.
LocalVariable* receiver = current_block_->scope->VariableAt(0);
LoadLocalNode* load_receiver = new LoadLocalNode(ident_pos, receiver);
String& field_name = String::Handle(Z, func.name());
field_name = Field::NameFromGetter(field_name);
const Class& field_class = Class::Handle(Z, func.Owner());
const Field& field =
Field::ZoneHandle(Z, field_class.LookupInstanceField(field_name));
ASSERT(!field.IsNull());
LoadInstanceFieldNode* load_field =
new LoadInstanceFieldNode(ident_pos, load_receiver, field);
ReturnNode* return_node = new ReturnNode(ST(ident_pos), load_field);
current_block_->statements->Add(return_node);
return CloseBlock();
}
// Create AstNodes for an implicit instance setter method:
// LoadLocalNode 0 ('this')
// LoadLocalNode 1 ('value')
// SetInstanceField (field_name);
// ReturnNode (void);
SequenceNode* Parser::ParseInstanceSetter(const Function& func) {
TRACE_PARSER("ParseInstanceSetter");
// func.token_pos() points to the name of the field.
const TokenPosition ident_pos = func.token_pos();
const String& field_name = *CurrentLiteral();
const Class& field_class = Class::ZoneHandle(Z, func.Owner());
const Field& field =
Field::ZoneHandle(Z, field_class.LookupInstanceField(field_name));
const AbstractType& field_type = AbstractType::ZoneHandle(Z, field.type());
ParamList params;
ASSERT(current_class().raw() == func.Owner());
params.AddReceiver(ReceiverType(current_class()), ident_pos);
params.AddFinalParameter(ident_pos, &Symbols::Value(), &field_type);
ASSERT(func.num_fixed_parameters() == 2); // receiver, value.
ASSERT(!func.HasOptionalParameters());
ASSERT(AbstractType::Handle(Z, func.result_type()).IsVoidType());
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
LoadLocalNode* receiver =
new LoadLocalNode(ident_pos, current_block_->scope->VariableAt(0));
LoadLocalNode* value =
new LoadLocalNode(ident_pos, current_block_->scope->VariableAt(1));
EnsureExpressionTemp();
StoreInstanceFieldNode* store_field =
new StoreInstanceFieldNode(ident_pos, receiver, field, value,
/* is_initializer = */ false);
current_block_->statements->Add(store_field);
current_block_->statements->Add(new ReturnNode(ST(ident_pos)));
return CloseBlock();
}
SequenceNode* Parser::ParseImplicitClosure(const Function& func) {
TRACE_PARSER("ParseImplicitClosure");
TokenPosition token_pos = func.token_pos();
OpenFunctionBlock(func);
if (parsed_function_->has_arg_desc_var() && FunctionLevel() == 0) {
EnsureExpressionTemp();
current_block_->scope->AddVariable(parsed_function_->arg_desc_var());
}
const Function& parent = Function::Handle(func.parent_function());
intptr_t type_args_len = 0; // Length of type args vector passed to parent.
LocalVariable* type_args_var = NULL;
if (Isolate::Current()->reify_generic_functions()) {
// The parent function of an implicit closure is the original function, i.e.
// non-closurized. It is not an enclosing function in the usual sense of a
// parent function. Do not set parent_type_arguments() in parsed_function_.
ASSERT(func.IsGeneric() == parent.IsGeneric());
if (func.IsGeneric()) {
type_args_len = func.NumTypeParameters();
// Insert function type arguments variable to scope.
type_args_var = new (Z) LocalVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::FunctionTypeArgumentsVar(), Object::dynamic_type());
current_block_->scope->AddVariable(type_args_var);
ASSERT(FunctionLevel() == 0);
parsed_function_->set_function_type_arguments(type_args_var);
}
}
ParamList params;
params.AddFinalParameter(token_pos, &Symbols::ClosureParameter(),
&Object::dynamic_type());
if (parent.IsImplicitSetterFunction()) {
const TokenPosition ident_pos = func.token_pos();
ASSERT(IsIdentifier());
params.AddFinalParameter(ident_pos, &Symbols::Value(),
&Object::dynamic_type());
ASSERT(func.num_fixed_parameters() == 2); // closure, value.
} else if (!parent.IsGetterFunction() && !parent.IsImplicitGetterFunction()) {
// NOTE: For the `kernel -> flowgraph` we don't use the parser.
if (parent.kernel_offset() <= 0) {
SkipFunctionPreamble();
const bool use_function_type_syntax = false;
const bool allow_explicit_default_values = true;
const bool evaluate_metadata = false;
ParseFormalParameterList(use_function_type_syntax,
allow_explicit_default_values, evaluate_metadata,
&params);
FinalizeFormalParameterTypes(&params);
SetupDefaultsForOptionalParams(params);
}
}
// Populate function scope with the formal parameters.
LocalScope* scope = current_block_->scope;
AddFormalParamsToScope(&params, scope);
ArgumentListNode* func_args =
new ArgumentListNode(token_pos, type_args_var, type_args_len);
if (!func.is_static()) {
func_args->Add(LoadReceiver(token_pos));
}
// Skip implicit parameter at 0.
for (intptr_t i = 1; i < func.NumParameters(); ++i) {
func_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
}
if (func.HasOptionalNamedParameters()) {
// TODO(srdjan): Must allocate array in old space, since it
// runs in background compiler. Find a better way.
const Array& arg_names =
Array::ZoneHandle(Array::New(func.NumOptionalParameters(), Heap::kOld));
for (intptr_t i = 0; i < arg_names.Length(); ++i) {
intptr_t index = func.num_fixed_parameters() + i;
arg_names.SetAt(i, String::Handle(func.ParameterNameAt(index)));
}
func_args->set_names(arg_names);
}
const String& func_name = String::ZoneHandle(parent.name());
const Class& owner = Class::Handle(parent.Owner());
Function& target = Function::ZoneHandle(owner.LookupFunction(func_name));
if (target.raw() != parent.raw()) {
NOT_IN_PRODUCT(ASSERT(Isolate::Current()->HasAttemptedReload()));
if (target.IsNull() || (target.is_static() != parent.is_static()) ||
(target.kind() != parent.kind())) {
target = Function::null();
}
}
AstNode* call = NULL;
// Check the target still exists and has compatible parameters. If not,
// throw NSME/call nSM instead of forwarding the call. Note we compare the
// parent not func because func has an extra parameter for the closure
// receiver.
if (!target.IsNull() &&
(parent.num_fixed_parameters() == target.num_fixed_parameters())) {
call = new StaticCallNode(token_pos, target, func_args,
StaticCallNode::kNoRebind);
} else if (!parent.is_static()) {
NOT_IN_PRODUCT(ASSERT(Isolate::Current()->HasAttemptedReload()));
// If a subsequent reload reintroduces the target in the middle of the
// Invocation object being constructed, we won't be able to successfully
// deopt because the generated AST will change.
func.SetIsOptimizable(false);
ArgumentListNode* arguments = BuildNoSuchMethodArguments(
token_pos, func_name, *func_args, NULL, false);
const intptr_t kTypeArgsLen = 0;
const intptr_t kNumArguments = 2; // Receiver, InvocationMirror.
ArgumentsDescriptor args_desc(Array::Handle(
Z, ArgumentsDescriptor::New(kTypeArgsLen, kNumArguments)));
Function& no_such_method =
Function::ZoneHandle(Z, Resolver::ResolveDynamicForReceiverClass(
owner, Symbols::NoSuchMethod(), args_desc));
if (no_such_method.IsNull()) {
// If noSuchMethod(i) is not found, call Object:noSuchMethod.
no_such_method ^= Resolver::ResolveDynamicForReceiverClass(
Class::Handle(Z, I->object_store()->object_class()),
Symbols::NoSuchMethod(), args_desc);
}
call = new StaticCallNode(token_pos, no_such_method, arguments,
StaticCallNode::kStatic);
} else {
NOT_IN_PRODUCT(ASSERT(Isolate::Current()->HasAttemptedReload()));
// If a subsequent reload reintroduces the target in the middle of the
// arguments array being constructed, we won't be able to successfully
// deopt because the generated AST will change.
func.SetIsOptimizable(false);
InvocationMirror::Kind im_kind;
if (parent.IsImplicitGetterFunction()) {
im_kind = InvocationMirror::kGetter;
} else if (parent.IsImplicitSetterFunction()) {
im_kind = InvocationMirror::kSetter;
} else {
im_kind = InvocationMirror::kMethod;
}
call = ThrowNoSuchMethodError(TokenPos(), owner, func_name, func_args,
InvocationMirror::kStatic, im_kind,
NULL); // No existing function.
}
ASSERT(call != NULL);
ReturnNode* return_node = new ReturnNode(token_pos, call);
current_block_->statements->Add(return_node);
return CloseBlock();
}
SequenceNode* Parser::ParseMethodExtractor(const Function& func) {
TRACE_PARSER("ParseMethodExtractor");
ParamList params;
const TokenPosition ident_pos = func.token_pos();
ASSERT(func.token_pos() == TokenPosition::kMethodExtractor);
ASSERT(current_class().raw() == func.Owner());
params.AddReceiver(ReceiverType(current_class()), ident_pos);
ASSERT(func.num_fixed_parameters() == 1); // Receiver.
ASSERT(!func.HasOptionalParameters());
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
// Receiver is local 0.
LocalVariable* receiver = current_block_->scope->VariableAt(0);
LoadLocalNode* load_receiver = new LoadLocalNode(ident_pos, receiver);
ClosureNode* closure = new ClosureNode(
ident_pos, Function::ZoneHandle(Z, func.extracted_method_closure()),
load_receiver, NULL);
ReturnNode* return_node = new ReturnNode(ident_pos, closure);
current_block_->statements->Add(return_node);
return CloseBlock();
}
void Parser::BuildDispatcherScope(const Function& func,
const ArgumentsDescriptor& desc) {
ParamList params;
// Receiver first.
TokenPosition token_pos = func.token_pos();
params.AddReceiver(ReceiverType(current_class()), token_pos);
// Remaining positional parameters.
intptr_t i = 1;
for (; i < desc.PositionalCount(); ++i) {
ParamDesc p;
char name[64];
Utils::SNPrint(name, 64, ":p%" Pd, i);
p.name = &String::ZoneHandle(Z, Symbols::New(T, name));
p.type = &Object::dynamic_type();
params.parameters->Add(p);
params.num_fixed_parameters++;
}
ASSERT(desc.PositionalCount() == params.num_fixed_parameters);
// Named parameters.
for (; i < desc.Count(); ++i) {
ParamDesc p;
intptr_t index = i - desc.PositionalCount();
p.name = &String::ZoneHandle(Z, desc.NameAt(index));
p.type = &Object::dynamic_type();
p.default_value = &Object::null_instance();
params.parameters->Add(p);
params.num_optional_parameters++;
params.has_optional_named_parameters = true;
}
ASSERT(desc.NamedCount() == params.num_optional_parameters);
SetupDefaultsForOptionalParams(params);
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
if (desc.TypeArgsLen() > 0) {
ASSERT(func.IsGeneric() && !func.HasGenericParent());
// Insert function type arguments variable to scope.
LocalVariable* type_args_var = new (Z) LocalVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::FunctionTypeArgumentsVar(), Object::dynamic_type());
current_block_->scope->AddVariable(type_args_var);
ASSERT(FunctionLevel() == 0);
parsed_function_->set_function_type_arguments(type_args_var);
}
if (parsed_function_->has_arg_desc_var() && FunctionLevel() == 0) {
EnsureExpressionTemp();
current_block_->scope->AddVariable(parsed_function_->arg_desc_var());
}
}
SequenceNode* Parser::ParseNoSuchMethodDispatcher(const Function& func) {
TRACE_PARSER("ParseNoSuchMethodDispatcher");
ASSERT(FLAG_lazy_dispatchers);
ASSERT(func.IsNoSuchMethodDispatcher());
TokenPosition token_pos = func.token_pos();
ASSERT(func.token_pos() == TokenPosition::kMinSource);
ASSERT(current_class().raw() == func.Owner());
ArgumentsDescriptor desc(Array::Handle(Z, func.saved_args_desc()));
ASSERT(desc.Count() > 0);
// Set up scope for this function.
BuildDispatcherScope(func, desc);
// Receiver is local 0.
LocalScope* scope = current_block_->scope;
ArgumentListNode* func_args = new ArgumentListNode(
token_pos, parsed_function_->function_type_arguments(),
desc.TypeArgsLen());
for (intptr_t i = 0; i < desc.Count(); ++i) {
func_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
}
if (desc.NamedCount() > 0) {
const Array& arg_names =
Array::ZoneHandle(Z, Array::New(desc.NamedCount(), Heap::kOld));
for (intptr_t i = 0; i < arg_names.Length(); ++i) {
arg_names.SetAt(i, String::Handle(Z, desc.NameAt(i)));
}
func_args->set_names(arg_names);
}
const String& func_name = String::ZoneHandle(Z, func.name());
ArgumentListNode* arguments =
BuildNoSuchMethodArguments(token_pos, func_name, *func_args, NULL, false);
const intptr_t kTypeArgsLen = 0;
const intptr_t kNumArguments = 2; // Receiver, InvocationMirror.
ArgumentsDescriptor args_desc(
Array::Handle(Z, ArgumentsDescriptor::New(kTypeArgsLen, kNumArguments)));
Function& no_such_method = Function::ZoneHandle(
Z,
Resolver::ResolveDynamicForReceiverClass(
Class::Handle(Z, func.Owner()), Symbols::NoSuchMethod(), args_desc));
if (no_such_method.IsNull()) {
// If noSuchMethod(i) is not found, call Object:noSuchMethod.
no_such_method ^= Resolver::ResolveDynamicForReceiverClass(
Class::Handle(Z, I->object_store()->object_class()),
Symbols::NoSuchMethod(), args_desc);
}
StaticCallNode* call = new StaticCallNode(
token_pos, no_such_method, arguments, StaticCallNode::kNSMDispatch);
ReturnNode* return_node = new ReturnNode(token_pos, call);
current_block_->statements->Add(return_node);
return CloseBlock();
}
SequenceNode* Parser::ParseInvokeFieldDispatcher(const Function& func) {
TRACE_PARSER("ParseInvokeFieldDispatcher");
ASSERT(func.IsInvokeFieldDispatcher());
TokenPosition token_pos = func.token_pos();
ASSERT(func.token_pos() == TokenPosition::kMinSource);
ASSERT(current_class().raw() == func.Owner());
const Array& args_desc = Array::Handle(Z, func.saved_args_desc());
ArgumentsDescriptor desc(args_desc);
ASSERT(desc.Count() > 0);
// Set up scope for this function.
BuildDispatcherScope(func, desc);
// Receiver is local 0.
LocalScope* scope = current_block_->scope;
ArgumentListNode* no_args = new ArgumentListNode(token_pos);
LoadLocalNode* receiver = new LoadLocalNode(token_pos, scope->VariableAt(0));
const Class& closure_cls =
Class::Handle(Isolate::Current()->object_store()->closure_class());
const Class& owner = Class::Handle(Z, func.Owner());
ASSERT(!owner.IsNull());
const String& name = String::Handle(Z, func.name());
AstNode* function_object = NULL;
if (owner.raw() == closure_cls.raw() && name.Equals(Symbols::Call())) {
function_object = receiver;
} else {
const String& getter_name =
String::ZoneHandle(Z, Field::GetterSymbol(name));
function_object =
new (Z) InstanceCallNode(token_pos, receiver, getter_name, no_args);
}
// Pass arguments 1..n to the closure call.
ArgumentListNode* args = new (Z)
ArgumentListNode(token_pos, parsed_function_->function_type_arguments(),
desc.TypeArgsLen());
const Array& names =
Array::Handle(Z, Array::New(desc.NamedCount(), Heap::kOld));
// Positional parameters.
intptr_t i = 1;
for (; i < desc.PositionalCount(); ++i) {
args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
}
// Named parameters.
for (; i < desc.Count(); i++) {
args->Add(new (Z) LoadLocalNode(token_pos, scope->VariableAt(i)));
intptr_t index = i - desc.PositionalCount();
names.SetAt(index, String::Handle(Z, desc.NameAt(index)));
}
args->set_names(names);
AstNode* result = NULL;
if (owner.raw() == closure_cls.raw() && name.Equals(Symbols::Call())) {
result = new ClosureCallNode(token_pos, function_object, args);
} else {
result = BuildClosureCall(token_pos, function_object, args);
}
ReturnNode* return_node = new ReturnNode(token_pos, result);
current_block_->statements->Add(return_node);
return CloseBlock();
}
AstNode* Parser::BuildClosureCall(TokenPosition token_pos,
AstNode* closure,
ArgumentListNode* arguments) {
return new InstanceCallNode(token_pos, closure, Symbols::Call(), arguments);
}
void Parser::SkipToMatching() {
Token::Kind opening_token = CurrentToken();
ASSERT((opening_token == Token::kLBRACE) ||
(opening_token == Token::kLPAREN));
GrowableArray<Token::Kind> token_stack(8);
GrowableArray<TokenPosition> token_pos_stack(8);
// Adding the first opening brace here, because it will be consumed
// in the loop right away.
token_stack.Add(opening_token);
const TokenPosition start_pos = TokenPos();
TokenPosition opening_pos = start_pos;
token_pos_stack.Add(start_pos);
bool is_match = true;
bool unexpected_token_found = false;
Token::Kind token = opening_token;
TokenPosition token_pos;
do {
ConsumeToken();
token = CurrentToken();
token_pos = TokenPos();
switch (token) {
case Token::kLBRACE:
case Token::kLPAREN:
case Token::kLBRACK:
token_stack.Add(token);
token_pos_stack.Add(token_pos);
break;
case Token::kRBRACE:
opening_token = token_stack.RemoveLast();
opening_pos = token_pos_stack.RemoveLast();
is_match = opening_token == Token::kLBRACE;
break;
case Token::kRPAREN:
opening_token = token_stack.RemoveLast();
opening_pos = token_pos_stack.RemoveLast();
is_match = opening_token == Token::kLPAREN;
break;
case Token::kRBRACK:
opening_token = token_stack.RemoveLast();
opening_pos = token_pos_stack.RemoveLast();
is_match = opening_token == Token::kLBRACK;
break;
case Token::kEOS:
opening_token = token_stack.RemoveLast();
opening_pos = token_pos_stack.RemoveLast();
unexpected_token_found = true;
break;
default:
// nothing.
break;
}
} while (!token_stack.is_empty() && is_match && !unexpected_token_found);
if (!is_match) {
const Error& error = Error::Handle(LanguageError::NewFormatted(
Error::Handle(), script_, opening_pos, Report::AtLocation,
Report::kWarning, allocation_space_, "unbalanced '%s' opens here",
Token::Str(opening_token)));
ReportErrors(error, script_, token_pos, "unbalanced '%s'",
Token::Str(token));
} else if (unexpected_token_found) {
ReportError(start_pos, "unterminated '%s'", Token::Str(opening_token));
}
}
void Parser::SkipBlock() {
ASSERT(CurrentToken() == Token::kLBRACE);
SkipToMatching();
}
// Skips tokens up to and including matching closing parenthesis.
void Parser::SkipToMatchingParenthesis() {
ASSERT(CurrentToken() == Token::kLPAREN);
SkipToMatching();
ASSERT(CurrentToken() == Token::kRPAREN);
ConsumeToken();
}
// Parses a parameter type as defined by the 'parameterTypeList' production.
void Parser::ParseParameterType(ParamList* params) {
TRACE_PARSER("ParseParameterType");
ParamDesc parameter;
parameter.has_explicit_type = true; // The type is required by the syntax.
// It is too early to resolve the type here, since it can be a result type
// referring to a not yet declared function type parameter.
parameter.type = &AbstractType::ZoneHandle(
Z, ParseTypeOrFunctionType(true, ClassFinalizer::kDoNotResolve));
// At this point, we must see an identifier for the parameter name, unless
// we are using the function type syntax (in which case the name is optional,
// unless we expect optional named parameters).
if (IsIdentifier()) {
parameter.name_pos = TokenPos();
parameter.name = CurrentLiteral();
ConsumeToken();
if (params->has_optional_named_parameters &&
(parameter.name->CharAt(0) == Library::kPrivateIdentifierStart)) {
ReportError(parameter.name_pos, "named parameter must not be private");
}
// Check for duplicate formal parameters.
const intptr_t num_existing_parameters =
params->num_fixed_parameters + params->num_optional_parameters;
for (intptr_t i = 0; i < num_existing_parameters; i++) {
ParamDesc& existing_parameter = (*params->parameters)[i];
if (existing_parameter.name->Equals(*parameter.name)) {
ReportError(parameter.name_pos, "duplicate formal parameter '%s'",
parameter.name->ToCString());
}
}
} else if (params->has_optional_named_parameters) {
ExpectIdentifier("parameter name expected");
} else {
parameter.name_pos = TokenPos();
parameter.name = &Symbols::NotNamed();
}
// The function type syntax does not allow the signature type syntax.
// No need to check for IsParameterPart().
if ((CurrentToken() == Token::kASSIGN) || (CurrentToken() == Token::kCOLON)) {
ReportError("parameter must not specify a default value");
} else {
if (params->has_optional_positional_parameters ||
params->has_optional_named_parameters) {
// Implicit default value is null.
params->num_optional_parameters++;
parameter.default_value = &Object::null_instance();
} else {
params->num_fixed_parameters++;
ASSERT(params->num_optional_parameters == 0);
}
}
if (params->implicitly_final) {
parameter.is_final = true;
}
params->parameters->Add(parameter);
if (parameter.is_covariant) {
params->has_covariant = true;
}
}
// Parses a formal parameter as defined by the 'formalParameterList' production.
void Parser::ParseFormalParameter(bool allow_explicit_default_value,
bool evaluate_metadata,
ParamList* params) {
TRACE_PARSER("ParseFormalParameter");
ParamDesc parameter;
bool var_seen = false;
bool final_seen = false;
bool this_seen = false;
if (evaluate_metadata && (CurrentToken() == Token::kAT)) {
parameter.metadata = &Array::ZoneHandle(Z, EvaluateMetadata());
} else {
SkipMetadata();
}
if (CurrentToken() == Token::kCOVARIANT &&
(LookaheadToken(1) == Token::kFINAL || LookaheadToken(1) == Token::kVAR ||
LookaheadToken(1) == Token::kVOID ||
Token::IsIdentifier(LookaheadToken(1)))) {
parameter.is_covariant = true;
ConsumeToken();
}
if (CurrentToken() == Token::kFINAL) {
ConsumeToken();
final_seen = true;
parameter.is_final = true;
} else if (CurrentToken() == Token::kVAR) {
ConsumeToken();
var_seen = true;
// The parameter type is the 'dynamic' type.
// If this is an initializing formal, its type will be set to the type of
// the respective field when the constructor is fully parsed.
parameter.type = &Object::dynamic_type();
}
if (CurrentToken() == Token::kTHIS) {
ConsumeToken();
ExpectToken(Token::kPERIOD);
this_seen = true;
parameter.is_field_initializer = true;
parameter.is_final = true;
}
if ((parameter.type == NULL) && (CurrentToken() == Token::kVOID)) {
ConsumeToken();
// This must later be changed to a closure type if we recognize
// a closure/function type parameter. We check this at the end
// of ParseFormalParameter.
parameter.type = &Object::void_type();
}
if ((parameter.type == NULL) || IsFunctionTypeSymbol()) {
// At this point, we must see an identifier for the type or the
// function parameter. The identifier may be 'Function'.
if (!IsIdentifier()) {
ReportError("parameter name or type expected");
}
// Lookahead to determine whether the next tokens are a return type
// followed by a parameter name.
bool found_type = false;
{
TokenPosScope saved_pos(this);
if (TryParseType(true)) {
if (IsIdentifier() || (CurrentToken() == Token::kTHIS)) {
found_type = true;
}
}
}
if (found_type) {
// The types of formal parameters are never ignored, even in unchecked
// mode, because they are part of the function type of closurized
// functions appearing in type tests with typedefs.
parameter.has_explicit_type = true;
// It is too early to resolve the type here, since it can be a result
// type referring to a not yet declared function type parameter.
if (parameter.type == NULL) {
parameter.type = &AbstractType::ZoneHandle(
Z, ParseTypeOrFunctionType(true, ClassFinalizer::kDoNotResolve));
} else {
parameter.type = &AbstractType::ZoneHandle(
Z,
ParseFunctionType(*parameter.type, ClassFinalizer::kDoNotResolve));
}
} else {
// If this is an initializing formal, its type will be set to the type
// of the respective field when the constructor is fully parsed.
parameter.type = &Object::dynamic_type();
}
}
if (!this_seen && (CurrentToken() == Token::kTHIS)) {
ConsumeToken();
ExpectToken(Token::kPERIOD);
this_seen = true;
parameter.is_field_initializer = true;
parameter.is_final = true;
}
// At this point, we must see an identifier for the parameter name.
parameter.name_pos = TokenPos();
parameter.name = ExpectIdentifier("parameter name expected");
if (parameter.is_field_initializer) {
params->has_field_initializer = true;
}
if (params->has_optional_named_parameters &&
(parameter.name->CharAt(0) == Library::kPrivateIdentifierStart)) {
ReportError(parameter.name_pos, "named parameter must not be private");
}
// Check for duplicate formal parameters.
const intptr_t num_existing_parameters =
params->num_fixed_parameters + params->num_optional_parameters;
for (intptr_t i = 0; i < num_existing_parameters; i++) {
ParamDesc& existing_parameter = (*params->parameters)[i];
if (existing_parameter.name->Equals(*parameter.name)) {
ReportError(parameter.name_pos, "duplicate formal parameter '%s'",
parameter.name->ToCString());
}
}
if (IsParameterPart()) {
// This parameter is probably a closure. If we saw the keyword 'var'
// or 'final', a closure is not legal here and we ignore the
// opening parens.
// TODO(hausner): The language spec appears to allow var and final
// in signature types when used with initializing formals:
// fieldFormalParameter:
// metadata finalConstVarOrType? this . identifier formalParameterList? ;
if (!var_seen && !final_seen) {
// The parsed parameter type is actually the function result type.
AbstractType& result_type =
AbstractType::Handle(Z, parameter.type->raw());
// In top-level and mixin functions, the source may be in a different
// script than the script of the current class. However, we never reparse
// signature functions (except typedef signature functions), therefore
// we do not need to keep the correct script via a patch class. Use the
// actual current class as owner of the signature function.
Function& signature_function = Function::Handle(
Z,
Function::NewSignatureFunction(current_class(), innermost_function(),
TokenPosition::kNoSource));
innermost_function_ = signature_function.raw();
// Finish parsing the function type parameter.
if (CurrentToken() == Token::kLT) {
ParseTypeParameters(false); // Not parameterizing class, but function.
}
ASSERT(CurrentToken() == Token::kLPAREN);
ParamList func_params;
// Add implicit closure object parameter.
func_params.AddFinalParameter(TokenPos(), &Symbols::ClosureParameter(),
&Object::dynamic_type());
const bool use_function_type_syntax = false;
const bool allow_explicit_default_values = false;
const bool evaluate_metadata = false;
ParseFormalParameterList(use_function_type_syntax,
allow_explicit_default_values, evaluate_metadata,
&func_params);
signature_function.set_result_type(result_type);
// The result type may refer to the signature function's type parameters,
// but was not parsed in the scope of the signature function. Adjust.
result_type.SetScopeFunction(signature_function);
AddFormalParamsToFunction(&func_params, signature_function);
ASSERT(innermost_function().raw() == signature_function.raw());
innermost_function_ = signature_function.parent_function();
Type& signature_type =
Type::ZoneHandle(Z, signature_function.SignatureType());
// A signature type itself cannot be malformed or malbounded, only its
// signature function's result type or parameter types may be.
ASSERT(!signature_type.IsMalformed());
ASSERT(!signature_type.IsMalbounded());
// The type of the parameter is now the signature type.
parameter.type = &signature_type;
}
}
if ((CurrentToken() == Token::kASSIGN) || (CurrentToken() == Token::kCOLON)) {
if ((!params->has_optional_positional_parameters &&
!params->has_optional_named_parameters) ||
!allow_explicit_default_value) {
ReportError("parameter must not specify a default value");
}
if (params->has_optional_positional_parameters) {
ExpectToken(Token::kASSIGN);
} else {
ConsumeToken();
}
params->num_optional_parameters++;
params->has_explicit_default_values = true; // Also if explicitly NULL.
if (is_top_level_) {
// Skip default value parsing.
SkipExpr();
} else {
const Instance& const_value = ParseConstExpr()->literal();
parameter.default_value = &const_value;
}
} else {
if (params->has_optional_positional_parameters ||
params->has_optional_named_parameters) {
// Implicit default value is null.
params->num_optional_parameters++;
parameter.default_value = &Object::null_instance();
} else {
params->num_fixed_parameters++;
ASSERT(params->num_optional_parameters == 0);
}
}
if (params->implicitly_final) {
parameter.is_final = true;
}
params->parameters->Add(parameter);
if (parameter.is_covariant) {
params->has_covariant = true;
}
}
// Parses a sequence of normal or optional formal parameters.
void Parser::ParseFormalParameters(bool use_function_type_syntax,
bool allow_explicit_default_values,
bool evaluate_metadata,
ParamList* params) {
TRACE_PARSER("ParseFormalParameters");
// Optional parameter lists cannot be empty.
// The completely empty parameter list is handled before getting here.
bool has_seen_parameter = false;
do {
ConsumeToken();
if (!params->has_optional_positional_parameters &&
!params->has_optional_named_parameters &&
(CurrentToken() == Token::kLBRACK)) {
// End of normal parameters, start of optional positional parameters.
params->has_optional_positional_parameters = true;
return;
}
if (!params->has_optional_positional_parameters &&
!params->has_optional_named_parameters &&
(CurrentToken() == Token::kLBRACE)) {
// End of normal parameters, start of optional named parameters.
params->has_optional_named_parameters = true;
return;
}
Token::Kind terminator = params->has_optional_positional_parameters
? Token::kRBRACK
: params->has_optional_named_parameters
? Token::kRBRACE
: Token::kRPAREN;
if (has_seen_parameter && CurrentToken() == terminator) {
// Allow a trailing comma.
break;
}
if (use_function_type_syntax) {
ASSERT(!allow_explicit_default_values && !evaluate_metadata);
ParseParameterType(params);
} else {
ParseFormalParameter(allow_explicit_default_values, evaluate_metadata,
params);
}
has_seen_parameter = true;
} while (CurrentToken() == Token::kCOMMA);
}
void Parser::ParseFormalParameterList(bool use_function_type_syntax,
bool allow_explicit_default_values,
bool evaluate_metadata,
ParamList* params) {
TRACE_PARSER("ParseFormalParameterList");
ASSERT(CurrentToken() == Token::kLPAREN);
if (LookaheadToken(1) != Token::kRPAREN) {
// Parse fixed parameters.
ParseFormalParameters(use_function_type_syntax,
allow_explicit_default_values, evaluate_metadata,
params);
if (params->has_optional_positional_parameters ||
params->has_optional_named_parameters) {
// Parse optional parameters.
ParseFormalParameters(use_function_type_syntax,
allow_explicit_default_values, evaluate_metadata,
params);
if (params->has_optional_positional_parameters) {
CheckToken(Token::kRBRACK, "',' or ']' expected");
} else {
CheckToken(Token::kRBRACE, "',' or '}' expected");
}
ConsumeToken(); // ']' or '}'.
}
if ((CurrentToken() != Token::kRPAREN) &&
!params->has_optional_positional_parameters &&
!params->has_optional_named_parameters) {
ReportError("',' or ')' expected");
}
} else {
ConsumeToken();
}
ExpectToken(Token::kRPAREN);
}
String& Parser::ParseNativeDeclaration() {
TRACE_PARSER("ParseNativeDeclaration");
ASSERT(IsSymbol(Symbols::Native()));
ConsumeToken();
CheckToken(Token::kSTRING, "string literal expected");
String& native_name = *CurrentLiteral();
ConsumeToken();
return native_name;
}
// Resolve and return the dynamic function of the given name in the superclass.
// If it is not found, and resolve_getter is true, try to resolve a getter of
// the same name. If it is still not found, return noSuchMethod and
// set is_no_such_method to true..
RawFunction* Parser::GetSuperFunction(TokenPosition token_pos,
const String& name,
ArgumentListNode* arguments,
bool resolve_getter,
bool* is_no_such_method) {
const Class& super_class = Class::Handle(Z, current_class().SuperClass());
if (super_class.IsNull()) {
ReportError(token_pos, "class '%s' does not have a superclass",
String::Handle(Z, current_class().Name()).ToCString());
}
Function& super_func = Function::Handle(
Z, Resolver::ResolveDynamicAnyArgs(Z, super_class, name));
if (!super_func.IsNull() &&
!super_func.AreValidArguments(arguments->type_args_len(),
arguments->length(), arguments->names(),
NULL)) {
super_func = Function::null();
} else if (super_func.IsNull() && resolve_getter) {
const String& getter_name =
String::ZoneHandle(Z, Field::LookupGetterSymbol(name));
if (!getter_name.IsNull()) {
super_func = Resolver::ResolveDynamicAnyArgs(Z, super_class, getter_name);
ASSERT(super_func.IsNull() ||
(super_func.kind() != RawFunction::kImplicitStaticFinalGetter));
}
}
if (super_func.IsNull()) {
super_func = Resolver::ResolveDynamicAnyArgs(Z, super_class,
Symbols::NoSuchMethod());
ASSERT(!super_func.IsNull());
*is_no_such_method = true;
} else {
*is_no_such_method = false;
}
return super_func.raw();
}
StaticCallNode* Parser::BuildInvocationMirrorAllocation(
TokenPosition call_pos,
const String& function_name,
const ArgumentListNode& function_args,
const LocalVariable* temp_for_last_arg,
bool is_super_invocation) {
const TokenPosition args_pos = function_args.token_pos();
// Build arguments to the call to the static
// InvocationMirror._allocateInvocationMirror method.
ArgumentListNode* arguments = new ArgumentListNode(args_pos);
// The first argument is the original function name.
arguments->Add(new LiteralNode(args_pos, function_name));
// The second argument is the arguments descriptor of the original function.
const Array& args_descriptor = Array::ZoneHandle(
ArgumentsDescriptor::New(function_args.type_args_len(),
function_args.length(), function_args.names()));
arguments->Add(new LiteralNode(args_pos, args_descriptor));
// The third argument is an array containing the original function arguments,
// including the function type arguments and the receiver.
ArrayNode* args_array =
new ArrayNode(args_pos, Type::ZoneHandle(Type::ArrayType()));
// A type_args_var is allocated in the generated body of an implicit
// closure and in the generated body of a noSuchMethodDispatcher.
// Pass the type arguments to the invocation mirror as the first argument.
if (function_args.type_args_var() != NULL) {
ASSERT(function_args.type_arguments().IsNull());
args_array->AddElement(
new LoadLocalNode(args_pos, function_args.type_args_var()));
} else if (!function_args.type_arguments().IsNull()) {
args_array->AddElement(
new LiteralNode(args_pos, function_args.type_arguments()));
}
for (intptr_t i = 0; i < function_args.length(); i++) {
AstNode* arg = function_args.NodeAt(i);
if ((temp_for_last_arg != NULL) && (i == function_args.length() - 1)) {
LetNode* store_arg = new LetNode(arg->token_pos());
store_arg->AddNode(
new StoreLocalNode(arg->token_pos(), temp_for_last_arg, arg));
store_arg->AddNode(
new LoadLocalNode(arg->token_pos(), temp_for_last_arg));
args_array->AddElement(store_arg);
} else {
args_array->AddElement(arg);
}
}
arguments->Add(args_array);
arguments->Add(new LiteralNode(args_pos, Bool::Get(is_super_invocation)));
// Lookup the static InvocationMirror._allocateInvocationMirror method.
const Class& mirror_class =
Class::Handle(Library::LookupCoreClass(Symbols::InvocationMirror()));
ASSERT(!mirror_class.IsNull());
const Function& allocation_function =
Function::ZoneHandle(mirror_class.LookupStaticFunction(
Library::PrivateCoreLibName(Symbols::AllocateInvocationMirror())));
ASSERT(!allocation_function.IsNull());
return new StaticCallNode(call_pos, allocation_function, arguments,
StaticCallNode::kStatic);
}
ArgumentListNode* Parser::BuildNoSuchMethodArguments(
TokenPosition call_pos,
const String& function_name,
const ArgumentListNode& function_args,
const LocalVariable* temp_for_last_arg,
bool is_super_invocation) {
ASSERT(function_args.length() >= 1); // The receiver is the first argument.
const TokenPosition args_pos = function_args.token_pos();
ArgumentListNode* arguments = new ArgumentListNode(args_pos);
arguments->Add(function_args.NodeAt(0));
// The second argument is the invocation mirror.
arguments->Add(
BuildInvocationMirrorAllocation(call_pos, function_name, function_args,
temp_for_last_arg, is_super_invocation));
return arguments;
}
AstNode* Parser::ParseSuperCall(const String& function_name,
const TypeArguments& func_type_args) {
TRACE_PARSER("ParseSuperCall");
ASSERT(CurrentToken() == Token::kLPAREN);
const TokenPosition supercall_pos = TokenPos();
// 'this' parameter is the first argument to super call (after the type args).
ArgumentListNode* arguments =
new ArgumentListNode(supercall_pos, func_type_args);
AstNode* receiver = LoadReceiver(supercall_pos);
arguments->Add(receiver);
ParseActualParameters(arguments, Object::null_type_arguments(), kAllowConst);
const bool kResolveGetter = true;
bool is_no_such_method = false;
const Function& super_function = Function::ZoneHandle(
Z, GetSuperFunction(supercall_pos, function_name, arguments,
kResolveGetter, &is_no_such_method));
if (super_function.IsGetterFunction() ||
super_function.IsImplicitGetterFunction()) {
const Class& super_class =
Class::ZoneHandle(Z, current_class().SuperClass());
AstNode* closure = new StaticGetterNode(
supercall_pos, LoadReceiver(supercall_pos), super_class, function_name,
StaticGetterSetter::kSuper);
// 'this' is not passed as parameter to the closure.
ArgumentListNode* closure_arguments =
new ArgumentListNode(supercall_pos, func_type_args);
for (int i = 1; i < arguments->length(); i++) {
closure_arguments->Add(arguments->NodeAt(i));
}
return BuildClosureCall(supercall_pos, closure, closure_arguments);
}
if (is_no_such_method) {
arguments = BuildNoSuchMethodArguments(supercall_pos, function_name,
*arguments, NULL, true);
}
return new StaticCallNode(supercall_pos, super_function, arguments,
StaticCallNode::kSuper);
}
// Simple test if a node is side effect free.
static bool IsSimpleLocalOrLiteralNode(AstNode* node) {
return node->IsLiteralNode() || node->IsLoadLocalNode();
}
AstNode* Parser::BuildUnarySuperOperator(Token::Kind op, PrimaryNode* super) {
ASSERT(super->IsSuper());
AstNode* super_op = NULL;
const TokenPosition super_pos = super->token_pos();
if ((op == Token::kNEGATE) || (op == Token::kBIT_NOT)) {
// Resolve the operator function in the superclass.
const String& operator_function_name = Symbols::Token(op);
ArgumentListNode* op_arguments = new ArgumentListNode(super_pos);
AstNode* receiver = LoadReceiver(super_pos);
op_arguments->Add(receiver);
const bool kResolveGetter = false;
bool is_no_such_method = false;
const Function& super_operator = Function::ZoneHandle(
Z, GetSuperFunction(super_pos, operator_function_name, op_arguments,
kResolveGetter, &is_no_such_method));
if (is_no_such_method) {
op_arguments = BuildNoSuchMethodArguments(
super_pos, operator_function_name, *op_arguments, NULL, true);
}
super_op = new StaticCallNode(super_pos, super_operator, op_arguments,
StaticCallNode::kSuper);
} else {
ReportError(super_pos, "illegal super operator call");
}
return super_op;
}
AstNode* Parser::ParseSuperOperator() {
TRACE_PARSER("ParseSuperOperator");
AstNode* super_op = NULL;
const TokenPosition operator_pos = TokenPos();
if (CurrentToken() == Token::kLBRACK) {
ConsumeToken();
AstNode* index_expr = ParseExpr(kAllowConst, kConsumeCascades);
ExpectToken(Token::kRBRACK);
AstNode* receiver = LoadReceiver(operator_pos);
const Class& super_class =
Class::ZoneHandle(Z, current_class().SuperClass());
ASSERT(!super_class.IsNull());
super_op =
new LoadIndexedNode(operator_pos, receiver, index_expr, super_class);
} else {
ASSERT(Token::CanBeOverloaded(CurrentToken()) ||
(CurrentToken() == Token::kNE));
Token::Kind op = CurrentToken();
ConsumeToken();
bool negate_result = false;
if (op == Token::kNE) {
op = Token::kEQ;
negate_result = true;
}
ASSERT(Token::Precedence(op) >= Token::Precedence(Token::kEQ));
AstNode* other_operand = ParseBinaryExpr(Token::Precedence(op) + 1);
ArgumentListNode* op_arguments = new ArgumentListNode(operator_pos);
AstNode* receiver = LoadReceiver(operator_pos);
op_arguments->Add(receiver);
op_arguments->Add(other_operand);
// Resolve the operator function in the superclass.
const String& operator_function_name = Symbols::Token(op);
const bool kResolveGetter = false;
bool is_no_such_method = false;
const Function& super_operator = Function::ZoneHandle(
Z, GetSuperFunction(operator_pos, operator_function_name, op_arguments,
kResolveGetter, &is_no_such_method));
if (is_no_such_method) {
op_arguments = BuildNoSuchMethodArguments(
operator_pos, operator_function_name, *op_arguments, NULL, true);
}
super_op = new StaticCallNode(operator_pos, super_operator, op_arguments,
StaticCallNode::kSuper);
if (negate_result) {
super_op = new UnaryOpNode(operator_pos, Token::kNOT, super_op);
}
}
return super_op;
}
ClosureNode* Parser::CreateImplicitClosureNode(const Function& func,
TokenPosition token_pos,
AstNode* receiver) {
Function& implicit_closure_function =
Function::ZoneHandle(Z, func.ImplicitClosureFunction());
return new ClosureNode(token_pos, implicit_closure_function, receiver, NULL);
}
AstNode* Parser::ParseSuperFieldAccess(const String& field_name,
TokenPosition field_pos) {
TRACE_PARSER("ParseSuperFieldAccess");
const Class& super_class = Class::ZoneHandle(Z, current_class().SuperClass());
if (super_class.IsNull()) {
ReportError("class '%s' does not have a superclass",
String::Handle(Z, current_class().Name()).ToCString());
}
AstNode* implicit_argument = LoadReceiver(field_pos);
const String& getter_name =
String::ZoneHandle(Z, Field::LookupGetterSymbol(field_name));
Function& super_getter = Function::ZoneHandle(Z);
if (!getter_name.IsNull()) {
super_getter = Resolver::ResolveDynamicAnyArgs(Z, super_class, getter_name);
}
if (super_getter.IsNull()) {
const String& setter_name =
String::ZoneHandle(Z, Field::LookupSetterSymbol(field_name));
Function& super_setter = Function::ZoneHandle(Z);
if (!setter_name.IsNull()) {
super_setter =
Resolver::ResolveDynamicAnyArgs(Z, super_class, setter_name);
}
if (super_setter.IsNull()) {
// Check if this is an access to an implicit closure using 'super'.
// If a function exists of the specified field_name then try
// accessing it as a getter, at runtime we will handle this by
// creating an implicit closure of the function and returning it.
const Function& super_function = Function::ZoneHandle(
Z, Resolver::ResolveDynamicAnyArgs(Z, super_class, field_name));
if (!super_function.IsNull()) {
// In case CreateAssignmentNode is called later on this
// CreateImplicitClosureNode, it will be replaced by a StaticSetterNode.
return CreateImplicitClosureNode(super_function, field_pos,
implicit_argument);
}
// No function or field exists of the specified field_name.
// Emit a StaticGetterNode anyway, so that noSuchMethod gets called.
}
}
return new (Z) StaticGetterNode(field_pos, implicit_argument, super_class,
field_name, StaticGetterSetter::kSuper);
}
StaticCallNode* Parser::GenerateSuperConstructorCall(
const Class& cls,
TokenPosition supercall_pos,
LocalVariable* receiver,
ArgumentListNode* forwarding_args) {
const Class& super_class = Class::Handle(Z, cls.SuperClass());
// Omit the implicit super() if there is no super class (i.e.
// we're not compiling class Object), or if the super class is an
// artificially generated "wrapper class" that has no constructor.
if (super_class.IsNull() ||
(super_class.num_native_fields() > 0 &&
Class::Handle(Z, super_class.SuperClass()).IsObjectClass())) {
return NULL;
}
String& super_ctor_name = String::Handle(Z, super_class.Name());
super_ctor_name = Symbols::FromDot(T, super_ctor_name);
ArgumentListNode* arguments = new ArgumentListNode(supercall_pos);
// Implicit 'this' parameter is the first argument.
AstNode* implicit_argument = new LoadLocalNode(supercall_pos, receiver);
arguments->Add(implicit_argument);
// If this is a super call in a forwarding constructor, add the user-
// defined arguments to the super call and adjust the super
// constructor name to the respective named constructor if necessary.
if (forwarding_args != NULL) {
for (int i = 0; i < forwarding_args->length(); i++) {
arguments->Add(forwarding_args->NodeAt(i));
}
String& ctor_name = String::Handle(Z, current_function().name());
String& class_name = String::Handle(Z, cls.Name());
if (ctor_name.Length() > class_name.Length() + 1) {
// Generating a forwarding call to a named constructor 'C.n'.
// Add the constructor name 'n' to the super constructor.
const intptr_t kLen = class_name.Length() + 1;
ctor_name = Symbols::New(T, ctor_name, kLen, ctor_name.Length() - kLen);
super_ctor_name = Symbols::FromConcat(T, super_ctor_name, ctor_name);
}
}
// Resolve super constructor function and check arguments.
const Function& super_ctor =
Function::ZoneHandle(Z, super_class.LookupConstructor(super_ctor_name));
if (super_ctor.IsNull()) {
if (super_class.LookupFactory(super_ctor_name) != Function::null()) {
ReportError(supercall_pos,
"illegal implicit call to factory '%s()' in super class",
String::Handle(Z, super_class.Name()).ToCString());
}
ReportError(supercall_pos,
"unresolved implicit call to super constructor '%s()'",
String::Handle(Z, super_class.Name()).ToCString());
}
if (current_function().is_const() && !super_ctor.is_const()) {
ReportError(supercall_pos, "implicit call to non-const super constructor");
}
String& error_message = String::Handle(Z);
if (!super_ctor.AreValidArguments(arguments->type_args_len(),
arguments->length(), arguments->names(),
&error_message)) {
ReportError(supercall_pos,
"invalid arguments passed to super constructor '%s()': %s",
String::Handle(Z, super_class.Name()).ToCString(),
error_message.ToCString());
}
return new StaticCallNode(supercall_pos, super_ctor, arguments,
StaticCallNode::kSuper);
}
StaticCallNode* Parser::ParseSuperInitializer(const Class& cls,
LocalVariable* receiver) {
TRACE_PARSER("ParseSuperInitializer");
ASSERT(CurrentToken() == Token::kSUPER);
const TokenPosition supercall_pos = TokenPos();
ConsumeToken();
const Class& super_class = Class::Handle(Z, cls.SuperClass());
ASSERT(!super_class.IsNull());
String& ctor_name = String::Handle(Z, super_class.Name());
ctor_name = Symbols::FromConcat(T, ctor_name, Symbols::Dot());
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ctor_name = Symbols::FromConcat(
T, ctor_name, *ExpectIdentifier("constructor name expected"));
}
CheckToken(Token::kLPAREN, "parameter list expected");
ArgumentListNode* arguments = new ArgumentListNode(supercall_pos);
// 'this' parameter is the first argument to super class constructor.
AstNode* implicit_argument = new LoadLocalNode(supercall_pos, receiver);
arguments->Add(implicit_argument);
// 'this' parameter must not be accessible to the other super call arguments.
receiver->set_invisible(true);
ParseActualParameters(arguments, Object::null_type_arguments(), kAllowConst);
receiver->set_invisible(false);
// Resolve the constructor.
const Function& super_ctor =
Function::ZoneHandle(Z, super_class.LookupConstructor(ctor_name));
if (super_ctor.IsNull()) {
if (super_class.LookupFactory(ctor_name) != Function::null()) {
ReportError(supercall_pos,
"super class constructor '%s' "
"must not be a factory constructor",
ctor_name.ToCString());
}
ReportError(supercall_pos, "super class constructor '%s' not found",
ctor_name.ToCString());
}
if (current_function().is_const() && !super_ctor.is_const()) {
ReportError(supercall_pos, "super constructor must be const");
}
String& error_message = String::Handle(Z);
if (!super_ctor.AreValidArguments(arguments->type_args_len(),
arguments->length(), arguments->names(),
&error_message)) {
ReportError(supercall_pos,
"invalid arguments passed to super class constructor '%s': %s",
ctor_name.ToCString(), error_message.ToCString());
}
if (current_function().is_const()) {
// No need to check that the type arguments to the super const contructor
// are instantiated, because generic constructors are not supported.
ASSERT(arguments->type_args_len() == 0);
// All arguments to the super const constructor must be potentially const.
for (intptr_t i = 0; i < arguments->length(); i++) {
AstNode* argument = arguments->NodeAt(i);
if (!argument->IsPotentiallyConst()) {
ReportError(
argument->token_pos(),
"super initializer argument must be compile time constant.");
}
if (argument->EvalConstExpr() != NULL) {
// If the expression is a compile-time constant, ensure that it
// is evaluated and canonicalized. See issues 27164 and 31106.
argument = FoldConstExpr(argument->token_pos(), argument);
arguments->SetNodeAt(i, argument);
}
}
}
return new StaticCallNode(supercall_pos, super_ctor, arguments,
StaticCallNode::kSuper);
}
AstNode* Parser::ParseInitializer(const Class& cls,
LocalVariable* receiver,
GrowableArray<Field*>* initialized_fields) {
TRACE_PARSER("ParseInitializer");
const TokenPosition field_pos = TokenPos();
if (CurrentToken() == Token::kASSERT) {
// Function literals are allowed in assertion initializer.
// "this" must not be accessible in assertion initializer.
receiver->set_invisible(true);
AstNode* init_assert = ParseAssertStatement(current_function().is_const());
receiver->set_invisible(false);
return init_assert;
}
if (CurrentToken() == Token::kTHIS) {
ConsumeToken();
ExpectToken(Token::kPERIOD);
}
const String& field_name = *ExpectIdentifier("field name expected");
ExpectToken(Token::kASSIGN);
TokenPosition expr_pos = TokenPos();
const bool saved_mode = SetAllowFunctionLiterals(false);
// "this" must not be accessible in initializer expressions.
receiver->set_invisible(true);
AstNode* init_expr = ParseConditionalExpr();
if (CurrentToken() == Token::kCASCADE) {
init_expr = ParseCascades(init_expr);
}
receiver->set_invisible(false);
SetAllowFunctionLiterals(saved_mode);
if (current_function().is_const()) {
if (!init_expr->IsPotentiallyConst()) {
ReportError(expr_pos,
"initializer expression must be compile time constant.");
}
if (init_expr->EvalConstExpr() != NULL) {
// If the expression is a compile-time constant, ensure that it
// is evaluated and canonicalized. See issue 27164.
init_expr = FoldConstExpr(expr_pos, init_expr);
}
}
Field& field = Field::ZoneHandle(Z, cls.LookupInstanceField(field_name));
if (field.IsNull()) {
ReportError(field_pos, "unresolved reference to instance field '%s'",
field_name.ToCString());
}
EnsureExpressionTemp();
AstNode* instance = new (Z) LoadLocalNode(field_pos, receiver);
AstNode* initializer = CheckDuplicateFieldInit(field_pos, initialized_fields,
instance, &field, init_expr);
if (initializer == NULL) {
initializer =
new (Z) StoreInstanceFieldNode(field_pos, instance, field, init_expr,
/* is_initializer = */ true);
}
return initializer;
}
void Parser::CheckFieldsInitialized(const Class& cls) {
const Array& fields = Array::Handle(Z, cls.fields());
Field& field = Field::Handle(Z);
SequenceNode* initializers = current_block_->statements;
for (int field_num = 0; field_num < fields.Length(); field_num++) {
field ^= fields.At(field_num);
if (field.is_static()) {
continue;
}
bool found = false;
for (int i = 0; i < initializers->length(); i++) {
found = false;
if (initializers->NodeAt(i)->IsStoreInstanceFieldNode()) {
StoreInstanceFieldNode* initializer =
initializers->NodeAt(i)->AsStoreInstanceFieldNode();
ASSERT(field.IsOriginal());
if (initializer->field().Original() == field.raw()) {
found = true;
break;
}
}
}
if (found) continue;
field.RecordStore(Object::null_object());
}
}
AstNode* Parser::ParseExternalInitializedField(const Field& field) {
// Only use this function if the initialized field originates
// from a different class. We need to save and restore the
// library and token stream (script).
// The current_class remains unchanged, so that type arguments
// are resolved in the correct scope class.
ASSERT(current_class().raw() != field.Origin());
const Library& saved_library = Library::Handle(Z, library().raw());
const Script& saved_script = Script::Handle(Z, script().raw());
const TokenPosition saved_token_pos = TokenPos();
const Class& origin_class = Class::Handle(Z, field.Origin());
set_library(Library::Handle(Z, origin_class.library()));
SetScript(Script::Handle(Z, origin_class.script()), field.token_pos());
ASSERT(IsIdentifier());
ConsumeToken();
ExpectToken(Token::kASSIGN);
AstNode* init_expr = NULL;
TokenPosition expr_pos = TokenPos();
if (field.is_const()) {
init_expr = ParseConstExpr();
} else {
init_expr = ParseExpr(kAllowConst, kConsumeCascades);
if (init_expr->EvalConstExpr() != NULL) {
init_expr = FoldConstExpr(expr_pos, init_expr);
}
}
set_library(saved_library);
SetScript(saved_script, saved_token_pos);
return init_expr;
}
void Parser::ParseInitializedInstanceFields(
const Class& cls,
LocalVariable* receiver,
GrowableArray<Field*>* initialized_fields) {
TRACE_PARSER("ParseInitializedInstanceFields");
const Array& fields = Array::Handle(Z, cls.fields());
Field& f = Field::Handle(Z);
const TokenPosition saved_pos = TokenPos();
for (int i = 0; i < fields.Length(); i++) {
f ^= fields.At(i);
if (!f.is_static() && f.has_initializer()) {
Field& field = Field::ZoneHandle(Z);
field ^= fields.At(i);
if (field.is_final()) {
// Final fields with initializer expression may not be initialized
// again by constructors. Remember that this field is already
// initialized.
initialized_fields->Add(&field);
}
AstNode* init_expr = NULL;
if (current_class().raw() != field.Origin()) {
init_expr = ParseExternalInitializedField(field);
} else {
SetPosition(field.token_pos());
ASSERT(IsIdentifier());
ConsumeToken();
ExpectToken(Token::kASSIGN);
if (current_class().is_const()) {
// If the class has a const contructor, the initializer
// expression must be a compile-time constant.
init_expr = ParseConstExpr();
} else {
TokenPosition expr_pos = TokenPos();
init_expr = ParseExpr(kAllowConst, kConsumeCascades);
if (init_expr->EvalConstExpr() != NULL) {
init_expr = FoldConstExpr(expr_pos, init_expr);
}
}
}
ASSERT(init_expr != NULL);
AstNode* instance = new LoadLocalNode(field.token_pos(), receiver);
EnsureExpressionTemp();
AstNode* field_init = new StoreInstanceFieldNode(
field.token_pos(), instance, field, init_expr,
/* is_initializer = */ true);
current_block_->statements->Add(field_init);
}
}
initialized_fields->Add(NULL); // End of inline initializers.
SetPosition(saved_pos);
}
AstNode* Parser::CheckDuplicateFieldInit(
TokenPosition init_pos,
GrowableArray<Field*>* initialized_fields,
AstNode* instance,
Field* field,
AstNode* init_value) {
ASSERT(!field->is_static());
AstNode* result = NULL;
const String& field_name = String::Handle(field->name());
String& initialized_name = String::Handle(Z);
// The initializer_list is divided into two sections. The sections
// are separated by a NULL entry: [f0, ... fn, NULL, fn+1, ...]
// The first fields f0 .. fn are final fields of the class that
// have an initializer expression inlined in the class declaration.
// The remaining fields are those initialized by the constructor's
// initializing formals and initializer list
int initializer_idx = 0;
while (initializer_idx < initialized_fields->length()) {
Field* initialized_field = (*initialized_fields)[initializer_idx];
initializer_idx++;
if (initialized_field == NULL) {
break;
}
initialized_name ^= initialized_field->name();
if (initialized_name.Equals(field_name) && field->has_initializer()) {
ReportError(init_pos, "final field '%s' is already initialized.",
field_name.ToCString());
}
if (initialized_field->raw() == field->raw()) {
// This final field has been initialized by an inlined
// initializer expression. This is a runtime error.
// Throw a NoSuchMethodError for the missing setter.
ASSERT(field->is_final());
// Build a call to NoSuchMethodError::_throwNew(
// Object receiver,
// String memberName,
// int invocation_type,
// Object typeArguments,
// List arguments,
// List argumentNames);
ArgumentListNode* nsm_args = new (Z) ArgumentListNode(init_pos);
// Object receiver.
nsm_args->Add(instance);
// String memberName.
String& setter_name = String::ZoneHandle(field->name());
setter_name = Field::SetterSymbol(setter_name);
nsm_args->Add(new (Z) LiteralNode(init_pos, setter_name));
// Smi invocation_type.
const int invocation_type = InvocationMirror::EncodeType(
InvocationMirror::kDynamic, InvocationMirror::kSetter);
nsm_args->Add(new (Z) LiteralNode(
init_pos, Smi::ZoneHandle(Z, Smi::New(invocation_type))));
// Object typeArguments.
nsm_args->Add(new (Z)
LiteralNode(init_pos, Object::null_type_arguments()));
// List arguments.
GrowableArray<AstNode*> setter_args;
setter_args.Add(init_value);
ArrayNode* setter_args_array = new (Z) ArrayNode(
init_pos, Type::ZoneHandle(Z, Type::ArrayType()), setter_args);
nsm_args->Add(setter_args_array);
// List argumentNames.
// The missing implicit setter of the field has no argument names.
nsm_args->Add(new (Z) LiteralNode(init_pos, Object::null_array()));
AstNode* nsm_call = MakeStaticCall(
Symbols::NoSuchMethodError(),
Library::PrivateCoreLibName(Symbols::ThrowNew()), nsm_args);
LetNode* let = new (Z) LetNode(init_pos);
let->AddNode(init_value);
let->AddNode(nsm_call);
result = let;
}
}
// The remaining elements in initialized_fields are fields that
// are initialized through initializing formal parameters, or
// in the constructor's initializer list. If there is a duplicate,
// it is a compile time error.
while (initializer_idx < initialized_fields->length()) {
Field* initialized_field = (*initialized_fields)[initializer_idx];
initializer_idx++;
if (initialized_field->raw() == field->raw()) {
ReportError(init_pos, "duplicate initializer for field %s",
String::Handle(Z, field->name()).ToCString());
}
}
initialized_fields->Add(field);
return result;
}
void Parser::ParseInitializers(const Class& cls,
LocalVariable* receiver,
GrowableArray<Field*>* initialized_fields) {
TRACE_PARSER("ParseInitializers");
bool super_init_is_last = false;
intptr_t super_init_index = -1;
StaticCallNode* super_init_call = NULL;
if (CurrentToken() == Token::kCOLON) {
do {
ConsumeToken(); // Colon or comma.
if (CurrentToken() == Token::kSUPER) {
if (super_init_call != NULL) {
ReportError("duplicate call to super constructor");
}
super_init_call = ParseSuperInitializer(cls, receiver);
super_init_index = current_block_->statements->length();
current_block_->statements->Add(super_init_call);
super_init_is_last = true;
} else {
AstNode* init_statement =
ParseInitializer(cls, receiver, initialized_fields);
super_init_is_last = false;
if (init_statement != NULL) {
current_block_->statements->Add(init_statement);
}
}
} while (CurrentToken() == Token::kCOMMA);
}
if (super_init_call == NULL) {
// Generate implicit super() if we haven't seen an explicit super call
// or constructor redirection.
super_init_call =
GenerateSuperConstructorCall(cls, TokenPos(), receiver, NULL);
if (super_init_call != NULL) {
super_init_index = current_block_->statements->length();
current_block_->statements->Add(super_init_call);
super_init_is_last = true;
}
}
if ((super_init_call != NULL) && !super_init_is_last) {
// If the super initializer call is not at the end of the initializer
// list, implicitly move it to the end. The actual parameter values
// are evaluated at the original position in the list and preserved
// in temporary variables. (The following initializer expressions
// could have side effects that alter the arguments to the super
// initializer.) E.g:
// A(x) : super(x), f = x++ { ... }
// is transformed to:
// A(x) : temp = x, f = x++, super(temp) { ... }
ASSERT(super_init_index >= 0);
ArgumentListNode* ctor_args = super_init_call->arguments();
LetNode* saved_args = new (Z) LetNode(super_init_call->token_pos());
// The super initializer call has at least 1 arguments: the
// implicit receiver.
ASSERT(ctor_args->length() >= 1);
for (int i = 1; i < ctor_args->length(); i++) {
AstNode* arg = ctor_args->NodeAt(i);
LocalVariable* temp = CreateTempConstVariable(arg->token_pos(), "sca");
AstNode* save_temp = new (Z) StoreLocalNode(arg->token_pos(), temp, arg);
saved_args->AddNode(save_temp);
ctor_args->SetNodeAt(i, new (Z) LoadLocalNode(arg->token_pos(), temp));
}
current_block_->statements->ReplaceNodeAt(super_init_index, saved_args);
current_block_->statements->Add(super_init_call);
}
CheckFieldsInitialized(cls);
}
void Parser::ParseConstructorRedirection(const Class& cls,
LocalVariable* receiver) {
TRACE_PARSER("ParseConstructorRedirection");
ExpectToken(Token::kCOLON);
ASSERT(CurrentToken() == Token::kTHIS);
const TokenPosition call_pos = TokenPos();
ConsumeToken();
String& ctor_name = String::Handle(Z, cls.Name());
GrowableHandlePtrArray<const String> pieces(Z, 3);
pieces.Add(ctor_name);
pieces.Add(Symbols::Dot());
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
pieces.Add(*ExpectIdentifier("constructor name expected"));
}
ctor_name = Symbols::FromConcatAll(T, pieces);
CheckToken(Token::kLPAREN, "parameter list expected");
ArgumentListNode* arguments = new ArgumentListNode(call_pos);
// 'this' parameter is the first argument to constructor.
AstNode* implicit_argument = new LoadLocalNode(call_pos, receiver);
arguments->Add(implicit_argument);
receiver->set_invisible(true);
ParseActualParameters(arguments, Object::null_type_arguments(), kAllowConst);
receiver->set_invisible(false);
// Resolve the constructor.
const Function& redirect_ctor =
Function::ZoneHandle(Z, cls.LookupConstructor(ctor_name));
if (redirect_ctor.IsNull()) {
if (cls.LookupFactory(ctor_name) != Function::null()) {
ReportError(call_pos,
"redirection constructor '%s' must not be a factory",
String::Handle(Z, String::ScrubName(ctor_name)).ToCString());
}
ReportError(call_pos, "constructor '%s' not found",
String::Handle(Z, String::ScrubName(ctor_name)).ToCString());
}
if (current_function().is_const() && !redirect_ctor.is_const()) {
ReportError(call_pos, "redirection constructor '%s' must be const",
String::Handle(Z, redirect_ctor.UserVisibleName()).ToCString());
}
String& error_message = String::Handle(Z);
if (!redirect_ctor.AreValidArguments(arguments->type_args_len(),
arguments->length(), arguments->names(),
&error_message)) {
ReportError(call_pos, "invalid arguments passed to constructor '%s': %s",
String::Handle(Z, redirect_ctor.UserVisibleName()).ToCString(),
error_message.ToCString());
}
current_block_->statements->Add(new StaticCallNode(
call_pos, redirect_ctor, arguments, StaticCallNode::kStatic));
}
SequenceNode* Parser::MakeImplicitConstructor(const Function& func) {
ASSERT(func.IsGenerativeConstructor());
ASSERT(func.Owner() == current_class().raw());
const TokenPosition ctor_pos = TokenPos();
OpenFunctionBlock(func);
LocalVariable* receiver =
new LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::This(), *ReceiverType(current_class()));
current_block_->scope->InsertParameterAt(0, receiver);
// Parse expressions of instance fields that have an explicit
// initializer expression.
// The receiver must not be visible to field initializer expressions.
receiver->set_invisible(true);
GrowableArray<Field*> initialized_fields;
ParseInitializedInstanceFields(current_class(), receiver,
&initialized_fields);
receiver->set_invisible(false);
// If the class of this implicit constructor is a mixin application alias,
// it is a forwarding constructor of the aliased mixin application class.
// If the class of this implicit constructor is a mixin application class,
// it is a forwarding constructor of the mixin. The forwarding
// constructor initializes the instance fields that have initializer
// expressions and then calls the respective super constructor with
// the same name and number of parameters.
ArgumentListNode* forwarding_args = NULL;
if (current_class().is_mixin_app_alias() ||
current_class().IsMixinApplication()) {
// At this point we don't support forwarding constructors
// that have optional parameters because we don't know the default
// values of the optional parameters. We would have to compile the super
// constructor to get the default values. Also, the spec is not clear
// whether optional parameters are even allowed in this situation.
// TODO(hausner): Remove this limitation if the language spec indeed
// allows optional parameters.
if (func.HasOptionalParameters()) {
const Class& super_class = Class::Handle(Z, current_class().SuperClass());
ReportError(ctor_pos,
"cannot generate an implicit mixin application constructor "
"forwarding to a super class constructor with optional "
"parameters; add a constructor without optional parameters "
"to class '%s' that redirects to the constructor with "
"optional parameters and invoke it via super from a "
"constructor of the class extending the mixin application",
String::Handle(Z, super_class.Name()).ToCString());
}
// Prepare user-defined arguments to be forwarded to super call.
// The first user-defined argument is at position 1.
forwarding_args = new ArgumentListNode(ST(ctor_pos));
for (int i = 1; i < func.NumParameters(); i++) {
LocalVariable* param =
new LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
String::ZoneHandle(Z, func.ParameterNameAt(i)),
Object::dynamic_type());
current_block_->scope->InsertParameterAt(i, param);
forwarding_args->Add(new LoadLocalNode(ST(ctor_pos), param));
}
}
AstNode* super_call = GenerateSuperConstructorCall(current_class(), ctor_pos,
receiver, forwarding_args);
if (super_call != NULL) {
current_block_->statements->Add(super_call);
}
CheckFieldsInitialized(current_class());
// Empty constructor body.
current_block_->statements->Add(new ReturnNode(ST(ctor_pos)));
SequenceNode* statements = CloseBlock();
return statements;
}
// Returns a zone allocated string.
static char* DumpPendingFunctions(
Zone* zone,
const GrowableObjectArray& pending_functions) {
ASSERT(zone != NULL);
char* result = OS::SCreate(zone, "Pending Functions:\n");
for (intptr_t i = 0; i < pending_functions.Length(); i++) {
const Function& func =
Function::Handle(zone, Function::RawCast(pending_functions.At(i)));
const String& fname = String::Handle(zone, func.UserVisibleName());
result = OS::SCreate(zone, "%s%" Pd ": %s\n", result, i, fname.ToCString());
}
return result;
}
void Parser::CheckRecursiveInvocation() {
const GrowableObjectArray& pending_functions =
GrowableObjectArray::Handle(Z, T->pending_functions());
ASSERT(!pending_functions.IsNull());
for (int i = 0; i < pending_functions.Length(); i++) {
if (pending_functions.At(i) == current_function().raw()) {
const String& fname =
String::Handle(Z, current_function().UserVisibleName());
if (FLAG_trace_service) {
const char* pending_function_dump =
DumpPendingFunctions(Z, pending_functions);
ASSERT(pending_function_dump != NULL);
ReportError("circular dependency for function %s\n%s",
fname.ToCString(), pending_function_dump);
} else {
ReportError("circular dependency for function %s", fname.ToCString());
}
}
}
ASSERT(!unregister_pending_function_);
pending_functions.Add(current_function(), Heap::kOld);
unregister_pending_function_ = true;
}
// Parser is at the opening parenthesis of the formal parameter declaration
// of function. Parse the formal parameters, initializers and code.
SequenceNode* Parser::ParseConstructor(const Function& func) {
TRACE_PARSER("ParseConstructor");
ASSERT(func.IsGenerativeConstructor());
ASSERT(!func.IsFactory());
ASSERT(!func.is_static());
ASSERT(!func.IsLocalFunction());
const Class& cls = Class::Handle(Z, func.Owner());
ASSERT(!cls.IsNull());
CheckRecursiveInvocation();
if (func.IsImplicitConstructor()) {
// Special case: implicit constructor.
// The parser adds an implicit default constructor when a class
// does not have any explicit constructor or factory (see
// Parser::AddImplicitConstructor).
// There is no source text to parse. We just build the
// sequence node by hand.
return MakeImplicitConstructor(func);
}
OpenFunctionBlock(func);
if (parsed_function_->has_arg_desc_var() && FunctionLevel() == 0) {
EnsureExpressionTemp();
current_block_->scope->AddVariable(parsed_function_->arg_desc_var());
}
ParamList params;
ASSERT(CurrentToken() == Token::kLPAREN);
// Add implicit receiver parameter which is passed the allocated
// but uninitialized instance to construct.
ASSERT(current_class().raw() == func.Owner());
params.AddReceiver(ReceiverType(current_class()), func.token_pos());
if (func.is_const()) {
params.SetImplicitlyFinal();
}
const bool use_function_type_syntax = false;
const bool allow_explicit_default_values = true;
const bool evaluate_metadata = false;
ParseFormalParameterList(use_function_type_syntax,
allow_explicit_default_values, evaluate_metadata,
&params);
FinalizeFormalParameterTypes(&params);
SetupDefaultsForOptionalParams(params);
ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
// Now populate function scope with the formal parameters.
AddFormalParamsToScope(&params, current_block_->scope);
const bool is_redirecting_constructor =
(CurrentToken() == Token::kCOLON) &&
((LookaheadToken(1) == Token::kTHIS) &&
((LookaheadToken(2) == Token::kLPAREN) ||
((LookaheadToken(2) == Token::kPERIOD) &&
(LookaheadToken(4) == Token::kLPAREN))));
GrowableArray<Field*> initialized_fields;
LocalVariable* receiver = (*params.parameters)[0].var;
OpenBlock();
// If this is not a redirecting constructor, initialize
// instance fields that have an explicit initializer expression.
if (!is_redirecting_constructor) {
// The formal parameter names must not be visible to the instance
// field initializer expressions, yet the parameters must be added to
// the scope so the expressions use the correct offsets for 'this' when
// storing values. We make the formal parameters temporarily invisible
// while parsing the instance field initializer expressions.
params.SetInvisible(true);
ParseInitializedInstanceFields(cls, receiver, &initialized_fields);
// Make the parameters (which are in the outer scope) visible again.
params.SetInvisible(false);
}
// Turn formal field parameters into field initializers.
if (params.has_field_initializer) {
// The first parameter is the implicit receiver.
ASSERT(params.parameters->length() >= 1);
for (int i = 1; i < params.parameters->length(); i++) {
ParamDesc& param = (*params.parameters)[i];
if (param.is_field_initializer) {
const String& field_name = *param.name;
Field& field =
Field::ZoneHandle(Z, cls.LookupInstanceField(field_name));
if (field.IsNull()) {
ReportError(param.name_pos,
"unresolved reference to instance field '%s'",
field_name.ToCString());
}
if (is_redirecting_constructor) {
ReportError(param.name_pos,
"redirecting constructors may not have "
"initializing formal parameters");
}
if (!param.has_explicit_type) {
const AbstractType& field_type =
AbstractType::ZoneHandle(Z, field.type());
param.type = &field_type;
// Parameter type was already set to dynamic when parsing the class
// declaration: fix it.
func.SetParameterTypeAt(i, field_type);
}
AstNode* instance = new LoadLocalNode(param.name_pos, receiver);
// Initializing formals cannot be used in the explicit initializer
// list, nor can they be used in the constructor body.
// Thus, they are set to be invisible when added to the scope.
LocalVariable* p = param.var;
ASSERT(p != NULL);
AstNode* value = new LoadLocalNode(param.name_pos, p);
EnsureExpressionTemp();
AstNode* initializer = CheckDuplicateFieldInit(
param.name_pos, &initialized_fields, instance, &field, value);
if (initializer == NULL) {
initializer = new (Z)
StoreInstanceFieldNode(param.name_pos, instance, field, value,
/* is_initializer = */ true);
}
current_block_->statements->Add(initializer);
}
}
}
if (is_redirecting_constructor) {
ParseConstructorRedirection(cls, receiver);
} else {
ParseInitializers(cls, receiver, &initialized_fields);
}
SequenceNode* init_statements = CloseBlock();
current_block_->statements->Add(init_statements);
// Parsing of initializers done. Now we parse the constructor body.
OpenBlock(); // Block to collect constructor body nodes.
params.HideInitFormals();
if (CurrentToken() == Token::kLBRACE) {
// We checked in the top-level parse phase that a redirecting
// constructor does not have a body.
ASSERT(!is_redirecting_constructor);
ConsumeToken();
ParseStatementSequence();
ExpectToken(Token::kRBRACE);
} else if (CurrentToken() == Token::kARROW) {
ReportError("constructors may not return a value");
} else if (IsSymbol(Symbols::Native())) {
ReportError("native constructors not supported");
} else if (CurrentToken() == Token::kSEMICOLON) {
// Some constructors have no function body.
ConsumeToken();
if (func.is_external()) {
// Body of an external method contains a single throw.
const String& function_name = String::ZoneHandle(func.name());
current_block_->statements->Add(ThrowNoSuchMethodError(
TokenPos(), cls, function_name,
NULL, // No arguments.
InvocationMirror::kStatic, InvocationMirror::kMethod,
NULL)); // No existing function.
}
} else {
UnexpectedToken();
}
SequenceNode* ctor_block = CloseBlock();
if (ctor_block->length() > 0) {
current_block_->statements->Add(ctor_block);
}
current_block_->statements->Add(new ReturnNode(func.end_token_pos()));
SequenceNode* statements = CloseBlock();
return statements;
}
// Parser is at the opening parenthesis of the formal parameter
// declaration of the function or constructor.
// Parse the formal parameters and code.
SequenceNode* Parser::ParseFunc(const Function& func, bool check_semicolon) {
TRACE_PARSER("ParseFunc");
ASSERT(innermost_function().raw() == func.raw());
// Save current try index. Try index starts at zero for each function.
intptr_t saved_try_index = last_used_try_index_;
last_used_try_index_ = 0;
// In case of nested async functions we also need to save the scope where
// temporaries are added.
LocalScope* saved_async_temp_scope = async_temp_scope_;
if (func.IsGenerativeConstructor()) {
SequenceNode* statements = ParseConstructor(func);
last_used_try_index_ = saved_try_index;
return statements;
}
ASSERT(!func.IsGenerativeConstructor());
OpenFunctionBlock(func); // Build local scope for function.
if (parsed_function_->has_arg_desc_var() && FunctionLevel() == 0) {
EnsureExpressionTemp();
current_block_->scope->AddVariable(parsed_function_->arg_desc_var());
}
if (Isolate::Current()->reify_generic_functions()) {
// Lookup function type arguments variable in parent function scope, if any.
if (func.HasGenericParent()) {
const String* variable_name = &Symbols::FunctionTypeArgumentsVar();
LocalVariable* parent_type_arguments =
current_block_->scope->LookupVariable(*variable_name, true);
ASSERT(parent_type_arguments != NULL);
// TODO(regis): It may be too early to capture parent_type_arguments here.
// In case it is never used, we could save capturing and concatenating.
current_block_->scope->CaptureVariable(parent_type_arguments);
if (FunctionLevel() == 0) {
parsed_function_->set_parent_type_arguments(parent_type_arguments);
if (!func.IsGeneric() && parent_type_arguments->is_captured()) {
parsed_function_->set_function_type_arguments(parent_type_arguments);
}
}
}
if (func.IsGeneric()) {
// Insert function type arguments variable to scope.
LocalVariable* function_type_arguments = new (Z) LocalVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::FunctionTypeArgumentsVar(), Object::dynamic_type());
current_block_->scope->AddVariable(function_type_arguments);
if (FunctionLevel() == 0) {
parsed_function_->set_function_type_arguments(function_type_arguments);
}
}
}
ParamList params;
// An instance closure function may capture and access the receiver, but via
// the context and not via the first formal parameter.
if (func.IsClosureFunction()) {
// The first parameter of a closure function is the closure object.
ASSERT(!func.is_const()); // Closure functions cannot be const.
params.AddFinalParameter(TokenPos(), &Symbols::ClosureParameter(),
&Object::dynamic_type());
} else if (!func.is_static()) {
// Static functions do not have a receiver.
ASSERT(current_class().raw() == func.Owner());
params.AddReceiver(ReceiverType(current_class()), func.token_pos());
} else if (func.IsFactory()) {
// The first parameter of a factory is the TypeArguments vector of
// the type of the instance to be allocated.
params.AddFinalParameter(TokenPos(), &Symbols::TypeArgumentsParameter(),
&Object::dynamic_type());
}
// Expect the parameter list unless this is a getter function, or the
// body closure of an async or generator getter function.
ASSERT((CurrentToken() == Token::kLPAREN) || func.IsGetterFunction() ||
(func.is_generated_body() &&
Function::Handle(func.parent_function()).IsGetterFunction()));
if (func.IsGetterFunction()) {
// Populate function scope with the formal parameters. Since in this case
// we are compiling a getter this will at most populate the receiver.
AddFormalParamsToScope(&params, current_block_->scope);
} else if (func.IsAsyncClosure()) {
AddAsyncClosureParameters(&params);
SetupDefaultsForOptionalParams(params);
AddFormalParamsToScope(&params, current_block_->scope);
ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
ASSERT(func.NumParameters() == params.parameters->length());
if (!Function::Handle(func.parent_function()).IsGetterFunction()) {
// Skip formal parameters. They are accessed as context variables.
// Parsing them again (and discarding them) does not work in case of
// default values with same name as already parsed formal parameter.
SkipToMatchingParenthesis();
}
} else if (func.IsSyncGenClosure()) {
AddSyncGenClosureParameters(&params);
SetupDefaultsForOptionalParams(params);
AddFormalParamsToScope(&params, current_block_->scope);
ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
if (!Function::Handle(func.parent_function()).IsGetterFunction()) {
// Skip formal parameters. They are accessed as context variables.
// Parsing them again (and discarding them) does not work in case of
// default values with same name as already parsed formal parameter.
SkipToMatchingParenthesis();
}
} else if (func.IsAsyncGenClosure()) {
AddAsyncGenClosureParameters(&params);
SetupDefaultsForOptionalParams(params);
AddFormalParamsToScope(&params, current_block_->scope);
ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
ASSERT(func.NumParameters() == params.parameters->length());
if (!Function::Handle(func.parent_function()).IsGetterFunction()) {
// Skip formal parameters. They are accessed as context variables.
// Parsing them again (and discarding them) does not work in case of
// default values with same name as already parsed formal parameter.
SkipToMatchingParenthesis();
}
} else {
const bool use_function_type_syntax = false;
const bool allow_explicit_default_values = true;
const bool evaluate_metadata = false;
ParseFormalParameterList(use_function_type_syntax,
allow_explicit_default_values, evaluate_metadata,
&params);
if (!is_top_level_) {
FinalizeFormalParameterTypes(&params);
}
// The number of parameters and their type are not yet set in local
// functions, since they are not 'top-level' parsed.
// However, they are already set when the local function is compiled, since
// the local function was parsed when its parent was compiled.
if (func.parameter_types() == Object::empty_array().raw()) {
AddFormalParamsToFunction(&params, func);
}
ResolveSignatureTypeParameters(func);
if (!is_top_level_) {
ClassFinalizer::FinalizeSignature(Class::Handle(Z, func.origin()), func);
}
SetupDefaultsForOptionalParams(params);
ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
// Populate function scope with the formal parameters.
AddFormalParamsToScope(&params, current_block_->scope);
}
const TokenPosition modifier_pos = TokenPos();
RawFunction::AsyncModifier func_modifier = ParseFunctionModifier();
if (!func.is_generated_body()) {
// Don't add a modifier to the closure representing the body of
// the asynchronous function or generator.
func.set_modifier(func_modifier);
}
OpenBlock(); // Open a nested scope for the outermost function block.
Function& generated_body_closure = Function::ZoneHandle(Z);
if (func.IsAsyncFunction()) {
ASSERT(!func.is_generated_body());
// The code of an async function is synthesized. Disable debugging.
func.set_is_debuggable(false);
if (FLAG_causal_async_stacks) {
// In order to collect causal asynchronous stacks efficiently we rely on
// this function not being inlined.
func.set_is_inlinable(false);
}
generated_body_closure = OpenAsyncFunction(func.token_pos());
} else if (func.IsAsyncClosure()) {
// The closure containing the body of an async function is debuggable.
ASSERT(func.is_debuggable());
if (FLAG_causal_async_stacks) {
// In order to collect causal asynchronous stacks efficiently we rely on
// this function not being inlined.
func.set_is_inlinable(false);
}
OpenAsyncClosure();
} else if (func.IsSyncGenerator()) {
// The code of a sync generator is synthesized. Disable debugging.
func.set_is_debuggable(false);
generated_body_closure = OpenSyncGeneratorFunction(func.token_pos());
} else if (func.IsSyncGenClosure()) {
// The closure containing the body of a sync generator is debuggable.
ASSERT(func.is_debuggable());
async_temp_scope_ = current_block_->scope;
} else if (func.IsAsyncGenerator()) {
func.set_is_debuggable(false);
if (FLAG_causal_async_stacks) {
// In order to collect causal asynchronous stacks efficiently we rely on
// this function not being inlined.
func.set_is_inlinable(false);
}
generated_body_closure = OpenAsyncGeneratorFunction(func.token_pos());
} else if (func.IsAsyncGenClosure()) {
// The closure containing the body of an async* function is debuggable.
ASSERT(func.is_debuggable());
if (FLAG_causal_async_stacks) {
// In order to collect causal asynchronous stacks efficiently we rely on
// this function not being inlined.
func.set_is_inlinable(false);
}
OpenAsyncGeneratorClosure();
}
// Function level is now correctly set to parse the (possibly async) body.
if (I->argument_type_checks() && (FunctionLevel() > 0)) {
// We are parsing, but not compiling, a local function.
// The instantiator may be required at run time for generic type checks.
// Note that the source of this local function may not reference the
// generic type explicitly. However, it may assign a value to a captured
// variable declared with its generic type in the enclosing function.
// Make sure that the receiver of the enclosing instance function
// (or implicit first parameter of an enclosing factory) is marked as
// captured if type checks are enabled, because they may access it to
// instantiate types.
// If any enclosing parent of the function being parsed is generic, capture
// their function type arguments.
CaptureAllInstantiators();
}
BoolScope allow_await(&this->await_is_keyword_,
func.IsAsyncOrGenerator() || func.is_generated_body());
TokenPosition end_token_pos = TokenPosition::kNoSource;
if (CurrentToken() == Token::kLBRACE) {
ConsumeToken();
if (String::Handle(Z, func.name()).Equals(Symbols::EqualOperator())) {
const Class& owner = Class::Handle(Z, func.Owner());
if (!owner.IsObjectClass()) {
AddEqualityNullCheck();
}
}
ParseStatementSequence();
end_token_pos = TokenPos();
ExpectToken(Token::kRBRACE);
} else if (CurrentToken() == Token::kARROW) {
if (func.IsGenerator()) {
ReportError(modifier_pos,
"=> style function may not be sync* or async* generator");
}
ConsumeToken();
if (String::Handle(Z, func.name()).Equals(Symbols::EqualOperator())) {
const Class& owner = Class::Handle(Z, func.Owner());
if (!owner.IsObjectClass()) {
AddEqualityNullCheck();
}
}
const TokenPosition expr_pos = TokenPos();
AstNode* expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
ASSERT(expr != NULL);
expr = AddAsyncResultTypeCheck(expr_pos, expr);
current_block_->statements->Add(new (Z) ReturnNode(expr_pos, expr));
end_token_pos = TokenPos();
if (check_semicolon) {
ExpectSemicolon();
}
} else if (IsSymbol(Symbols::Native())) {
if (String::Handle(Z, func.name()).Equals(Symbols::EqualOperator())) {
const Class& owner = Class::Handle(Z, func.Owner());
if (!owner.IsObjectClass()) {
AddEqualityNullCheck();
}
}
ParseNativeFunctionBlock(&params, func);
end_token_pos = TokenPos();
ExpectSemicolon();
} else if (func.is_external()) {
// Body of an external method contains a single throw.
const String& function_name = String::ZoneHandle(Z, func.name());
current_block_->statements->Add(ThrowNoSuchMethodError(
TokenPos(), Class::Handle(func.Owner()), function_name,
NULL, // Ignore arguments.
func.is_static() ? InvocationMirror::kStatic
: InvocationMirror::kDynamic,
InvocationMirror::kMethod,
&func)); // Unpatched external function.
end_token_pos = TokenPos();
} else {
UnexpectedToken();
}
ASSERT(func.end_token_pos() == func.token_pos() ||
func.end_token_pos() == end_token_pos);
func.set_end_token_pos(end_token_pos);
SequenceNode* body = CloseBlock();
if (Isolate::Current()->reify_generic_functions() && func.IsGeneric() &&
!generated_body_closure.IsNull()) {
LocalVariable* existing_var = body->scope()->LookupVariable(
Symbols::FunctionTypeArgumentsVar(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
}
if (func.IsAsyncFunction()) {
body = CloseAsyncFunction(generated_body_closure, body);
generated_body_closure.set_end_token_pos(end_token_pos);
} else if (func.IsAsyncClosure()) {
body = CloseAsyncClosure(body, end_token_pos);
} else if (func.IsSyncGenerator()) {
body = CloseSyncGenFunction(generated_body_closure, body);
generated_body_closure.set_end_token_pos(end_token_pos);
} else if (func.IsSyncGenClosure()) {
// body is unchanged.
} else if (func.IsAsyncGenerator()) {
body = CloseAsyncGeneratorFunction(generated_body_closure, body);
generated_body_closure.set_end_token_pos(end_token_pos);
} else if (func.IsAsyncGenClosure()) {
body = CloseAsyncGeneratorClosure(body);
}
EnsureHasReturnStatement(body, end_token_pos);
current_block_->statements->Add(body);
last_used_try_index_ = saved_try_index;
async_temp_scope_ = saved_async_temp_scope;
return CloseBlock();
}
void Parser::AddEqualityNullCheck() {
AstNode* argument = new LoadLocalNode(
TokenPosition::kNoSource, current_block_->scope->parent()->VariableAt(1));
LiteralNode* null_operand =
new LiteralNode(TokenPosition::kNoSource, Instance::ZoneHandle(Z));
ComparisonNode* check_arg = new ComparisonNode(
TokenPosition::kNoSource, Token::kEQ_STRICT, argument, null_operand);
ComparisonNode* result =
new ComparisonNode(TokenPosition::kNoSource, Token::kEQ_STRICT,
LoadReceiver(TokenPosition::kNoSource), null_operand);
SequenceNode* arg_is_null =
new SequenceNode(TokenPosition::kNoSource, current_block_->scope);
arg_is_null->Add(new ReturnNode(TokenPosition::kNoSource, result));
IfNode* if_arg_null =
new IfNode(TokenPosition::kNoSource, check_arg, arg_is_null, NULL);
current_block_->statements->Add(if_arg_null);
}
AstNode* Parser::AddAsyncResultTypeCheck(TokenPosition expr_pos,
AstNode* expr) {
if (I->type_checks() &&
(((FunctionLevel() == 0) && current_function().IsAsyncClosure()))) {
// In checked mode, when the declared result type is Future<T>, verify
// that the returned expression is of type T or Future<T> as follows:
// return temp = expr, temp is Future ? temp as Future<T> : temp as T;
// In case of a mismatch, we need a TypeError and not a CastError, so
// we do not actually implement an "as" test, but an "assignable" test.
Function& async_func =
Function::Handle(Z, current_function().parent_function());
const AbstractType& result_type =
AbstractType::ZoneHandle(Z, async_func.result_type());
const Class& future_class =
Class::ZoneHandle(Z, I->object_store()->future_class());
ASSERT(!future_class.IsNull());
if (result_type.type_class() == future_class.raw()) {
const TypeArguments& result_type_args =
TypeArguments::ZoneHandle(Z, result_type.arguments());
if (!result_type_args.IsNull() && (result_type_args.Length() == 1)) {
const AbstractType& result_type_arg =
AbstractType::ZoneHandle(Z, result_type_args.TypeAt(0));
LetNode* checked_expr = new (Z) LetNode(expr_pos);
LocalVariable* temp = checked_expr->AddInitializer(expr);
temp->set_is_final();
const AbstractType& future_type =
AbstractType::ZoneHandle(Z, future_class.RareType());
AstNode* is_future = new (Z) LoadLocalNode(expr_pos, temp);
is_future =
new (Z) ComparisonNode(expr_pos, Token::kIS, is_future,
new (Z) TypeNode(expr_pos, future_type));
AstNode* as_future_t = new (Z) LoadLocalNode(expr_pos, temp);
as_future_t = new (Z) AssignableNode(expr_pos, as_future_t, result_type,
Symbols::FunctionResult());
AstNode* as_t = new (Z) LoadLocalNode(expr_pos, temp);
as_t = new (Z) AssignableNode(expr_pos, as_t, result_type_arg,
Symbols::FunctionResult());
checked_expr->AddNode(new (Z) ConditionalExprNode(expr_pos, is_future,
as_future_t, as_t));
expr = checked_expr;
}
}
}
return expr;
}
void Parser::SkipIf(Token::Kind token) {
if (CurrentToken() == token) {
ConsumeToken();
}
}
void Parser::SkipInitializers() {
ASSERT(CurrentToken() == Token::kCOLON);
do {
ConsumeToken(); // Colon or comma.
if (CurrentToken() == Token::kSUPER) {
ConsumeToken();
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ExpectIdentifier("identifier expected");
}
CheckToken(Token::kLPAREN);
SkipToMatchingParenthesis();
} else if (CurrentToken() == Token::kASSERT) {
ConsumeToken();
CheckToken(Token::kLPAREN);
SkipToMatchingParenthesis();
} else {
SkipIf(Token::kTHIS);
SkipIf(Token::kPERIOD);
ExpectIdentifier("identifier expected");
ExpectToken(Token::kASSIGN);
SetAllowFunctionLiterals(false);
SkipExpr();
SetAllowFunctionLiterals(true);
}
} while (CurrentToken() == Token::kCOMMA);
}
// If the current identifier is a library prefix followed by a period,
// consume the identifier and period, and return the resolved library
// prefix.
RawLibraryPrefix* Parser::ParsePrefix() {
ASSERT(IsIdentifier());
// A library prefix can never stand by itself. It must be followed by
// a period.
Token::Kind next_token = LookaheadToken(1);
if (next_token != Token::kPERIOD) {
return LibraryPrefix::null();
}
const String& ident = *CurrentLiteral();
// It is relatively fast to look up a name in the library dictionary,
// compared to searching the nested local scopes. Look up the name
// in the library scope and return in the common case where ident is
// not a library prefix.
LibraryPrefix& prefix =
LibraryPrefix::Handle(Z, library_.LookupLocalLibraryPrefix(ident));
if (prefix.IsNull()) {
return LibraryPrefix::null();
}
// A library prefix with the name exists. Now check whether it is
// shadowed by a local definition.
if (!is_top_level_ &&
ResolveIdentInLocalScope(TokenPos(), ident, NULL, NULL)) {
return LibraryPrefix::null();
}
// Check whether the identifier is shadowed by a function type parameter.
if (InGenericFunctionScope() && (innermost_function().LookupTypeParameter(
ident, NULL) != TypeParameter::null())) {
return LibraryPrefix::null();
}
// Check whether the identifier is shadowed by a class type parameter.
ASSERT(!current_class().IsNull());
if (current_class().LookupTypeParameter(ident) != TypeParameter::null()) {
return LibraryPrefix::null();
}
// We have a name that is not shadowed, followed by a period.
// Consume the identifier, let the caller consume the period.
ConsumeToken();
return prefix.raw();
}
void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) {
TRACE_PARSER("ParseMethodOrConstructor");
// We are at the beginning of the formal parameters list.
ASSERT(CurrentToken() == Token::kLPAREN || CurrentToken() == Token::kLT ||
method->IsGetter());
ASSERT(method->type != NULL); // May still be unresolved.
ASSERT(current_member_ == method);
if (method->has_covariant) {
ReportError(method->name_pos,
"methods and constructors cannot be declared covariant");
}
if (method->has_var) {
ReportError(method->name_pos, "keyword var not allowed for methods");
}
if (method->has_final) {
ReportError(method->name_pos, "'final' not allowed for methods");
}
if (method->has_abstract && method->has_static) {
ReportError(method->name_pos, "static method '%s' cannot be abstract",
method->name->ToCString());
}
if (method->has_const && !method->IsFactoryOrConstructor()) {
ReportError(method->name_pos, "'const' not allowed for methods");
}
if (method->has_abstract && method->IsFactoryOrConstructor()) {
ReportError(method->name_pos, "constructor cannot be abstract");
}
if (method->has_const && method->IsConstructor()) {
current_class().set_is_const();
}
Function& func = Function::Handle(
Z,
Function::New(*method->name, // May change.
method->kind, method->has_static, method->has_const,
method->has_abstract, // May change.
method->has_external,
method->has_native, // May change.
current_class(), method->decl_begin_pos));
ASSERT(innermost_function().IsNull());
innermost_function_ = func.raw();
if (CurrentToken() == Token::kLT) {
TokenPosition type_param_pos = TokenPos();
if (method->IsFactoryOrConstructor()) {
ReportError(method->name_pos, "constructor cannot be generic");
}
if (method->IsGetter() || method->IsSetter()) {
ReportError(type_param_pos, "%s cannot be generic",
method->IsGetter() ? "getter" : "setter");
}
ParseTypeParameters(false); // Not parameterizing class, but function.
}
// Parse the formal parameters.
const TokenPosition formal_param_pos = TokenPos();
method->params.Clear();
// Static functions do not have a receiver.
// The first parameter of a factory is the TypeArguments vector of
// the type of the instance to be allocated.
if (!method->has_static || method->IsConstructor()) {
method->params.AddReceiver(ReceiverType(current_class()), formal_param_pos);
} else if (method->IsFactory()) {
method->params.AddFinalParameter(formal_param_pos,
&Symbols::TypeArgumentsParameter(),
&Object::dynamic_type());
}
if (method->has_const) {
method->params.SetImplicitlyFinal();
}
if (!method->IsGetter()) {
const bool use_function_type_syntax = false;
const bool allow_explicit_default_values = true;
const bool evaluate_metadata = false;
ParseFormalParameterList(use_function_type_syntax,
allow_explicit_default_values, evaluate_metadata,
&method->params);
}
// Now that we know the parameter list, we can distinguish between the
// unary and binary operator -.
if (method->has_operator) {
if ((method->operator_token == Token::kSUB) &&
(method->params.num_fixed_parameters == 1)) {
// Patch up name for unary operator - so it does not clash with the
// name for binary operator -.
method->operator_token = Token::kNEGATE;
*method->name = Symbols::Token(Token::kNEGATE).raw();
}
CheckOperatorArity(*method);
}
// Mangle the name for getter and setter functions and check function
// arity.
if (method->IsGetter() || method->IsSetter()) {
int expected_num_parameters = 0;
if (method->IsGetter()) {
expected_num_parameters = (method->has_static) ? 0 : 1;
method->dict_name = method->name;
method->name = &String::ZoneHandle(Z, Field::GetterSymbol(*method->name));
} else {
ASSERT(method->IsSetter());
expected_num_parameters = (method->has_static) ? 1 : 2;
method->dict_name = &String::ZoneHandle(
Z, Symbols::FromConcat(T, *method->name, Symbols::Equals()));
method->name = &String::ZoneHandle(Z, Field::SetterSymbol(*method->name));
}
if ((method->params.num_fixed_parameters != expected_num_parameters) ||
(method->params.num_optional_parameters != 0)) {
ReportError(method->name_pos, "illegal %s parameters",
method->IsGetter() ? "getter" : "setter");
}
}
// Parse redirecting factory constructor.
Type& redirection_type = Type::Handle(Z);
String& redirection_identifier = String::Handle(Z);
bool is_redirecting = false;
if (method->IsFactory() && (CurrentToken() == Token::kASSIGN)) {
// Default parameter values are disallowed in redirecting factories.
if (method->params.has_explicit_default_values) {
ReportError(
"redirecting factory '%s' may not specify default values "
"for optional parameters",
method->name->ToCString());
}
if (method->has_external) {
ReportError(TokenPos(),
"external factory constructor '%s' may not have redirection",
method->name->ToCString());
}
ConsumeToken();
const TokenPosition type_pos = TokenPos();
is_redirecting = true;
const bool consume_unresolved_prefix =
(LookaheadToken(3) == Token::kLT) ||
(LookaheadToken(3) == Token::kPERIOD);
const AbstractType& type = AbstractType::Handle(
Z, ParseType(ClassFinalizer::kResolveTypeParameters, true,
consume_unresolved_prefix));
if (!type.IsMalformed() && type.IsTypeParameter()) {
// Replace the type with a malformed type and compile a throw when called.
redirection_type = ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(Z), // No previous error.
script_, type_pos,
"factory '%s' may not redirect to type parameter '%s'",
method->name->ToCString(),
String::Handle(Z, type.UserVisibleName()).ToCString());
} else {
// We handle malformed and malbounded redirection type at run time.
redirection_type ^= type.raw();
}
if (CurrentToken() == Token::kPERIOD) {
// Named constructor or factory.
ConsumeToken();
redirection_identifier = ExpectIdentifier("identifier expected")->raw();
}
} else if (CurrentToken() == Token::kCOLON) {
// Parse initializers.
if (!method->IsConstructor()) {
ReportError("initializers only allowed on constructors");
}
if (method->has_external) {
ReportError(TokenPos(),
"external constructor '%s' may not have initializers",
method->name->ToCString());
}
if ((LookaheadToken(1) == Token::kTHIS) &&
((LookaheadToken(2) == Token::kLPAREN) ||
LookaheadToken(4) == Token::kLPAREN)) {
// Redirected constructor: either this(...) or this.xxx(...).
is_redirecting = true;
if (method->params.has_field_initializer) {
// Constructors that redirect to another constructor must not
// initialize any fields using field initializer parameters.
ReportError(formal_param_pos,
"Redirecting constructor "
"may not use field initializer parameters");
}
ConsumeToken(); // Colon.
ExpectToken(Token::kTHIS);
GrowableHandlePtrArray<const String> pieces(Z, 3);
pieces.Add(members->class_name());
pieces.Add(Symbols::Dot());
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
pieces.Add(*ExpectIdentifier("constructor name expected"));
}
String& redir_name =
String::ZoneHandle(Z, Symbols::FromConcatAll(T, pieces));
method->redirect_name = &redir_name;
CheckToken(Token::kLPAREN);
SkipToMatchingParenthesis();
} else {
SkipInitializers();
}
}
// Only constructors can redirect to another method.
ASSERT((method->redirect_name == NULL) || method->IsConstructor());
if (method->IsConstructor() && method->has_external &&
method->params.has_field_initializer) {
ReportError(method->name_pos,
"external constructor '%s' may not have field initializers",
method->name->ToCString());
}
const TokenPosition modifier_pos = TokenPos();
RawFunction::AsyncModifier async_modifier = ParseFunctionModifier();
if ((method->IsFactoryOrConstructor() || method->IsSetter()) &&
(async_modifier != RawFunction::kNoModifier)) {
ReportError(modifier_pos, "%s '%s' may not be async, async* or sync*",
(method->IsSetter()) ? "setter" : "constructor",
method->name->ToCString());
}
TokenPosition method_end_pos = TokenPos();
String* native_name = NULL;
if ((CurrentToken() == Token::kLBRACE) || (CurrentToken() == Token::kARROW)) {
if (method->has_abstract) {
ReportError(TokenPos(),
"abstract method '%s' may not have a function body",
method->name->ToCString());
} else if (method->has_external) {
ReportError(TokenPos(), "external %s '%s' may not have a function body",
method->IsFactoryOrConstructor() ? "constructor" : "method",
method->name->ToCString());
} else if (method->IsConstructor() && method->has_const) {
ReportError(TokenPos(),
"const constructor '%s' may not have a function body",
method->name->ToCString());
} else if (method->IsFactory() && method->has_const) {
ReportError(TokenPos(), "const factory '%s' may not have a function body",
method->name->ToCString());
}
if (method->redirect_name != NULL) {
ReportError(method->name_pos,
"Constructor with redirection may not have a function body");
}
if (CurrentToken() == Token::kLBRACE) {
SkipBlock();
method_end_pos = TokenPos();
ExpectToken(Token::kRBRACE);
} else {
if ((async_modifier & RawFunction::kGeneratorBit) != 0) {
ReportError(modifier_pos,
"=> style function may not be sync* or async* generator");
}
ConsumeToken();
BoolScope allow_await(&this->await_is_keyword_,
async_modifier != RawFunction::kNoModifier);
SkipExpr();
method_end_pos = TokenPos();
ExpectSemicolon();
}
} else if (IsSymbol(Symbols::Native())) {
if (method->has_abstract) {
ReportError(method->name_pos,
"abstract method '%s' may not have a function body",
method->name->ToCString());
} else if (method->IsConstructor() && method->has_const) {
ReportError(method->name_pos, "const constructor '%s' may not be native",
method->name->ToCString());
}
if (method->redirect_name != NULL) {
ReportError(method->name_pos,
"Constructor with redirection may not have a function body");
}
native_name = &ParseNativeDeclaration();
method_end_pos = TokenPos();
ExpectSemicolon();
method->has_native = true;
} else {
// We haven't found a method body. Issue error if one is required.
const bool must_have_body = method->has_static && !method->has_external &&
redirection_type.IsNull();
if (must_have_body) {
ReportError(method->name_pos, "function body expected for method '%s'",
method->name->ToCString());
}
if (CurrentToken() == Token::kSEMICOLON) {
ConsumeToken();
if (!method->has_static && !method->has_external &&
!method->IsConstructor()) {
// Methods, getters and setters without a body are
// implicitly abstract.
method->has_abstract = true;
}
} else {
// Signature is not followed by semicolon or body. Issue an
// appropriate error.
const bool must_have_semicolon =
(method->redirect_name != NULL) ||
(method->IsConstructor() && method->has_const) ||
method->has_external;
if (must_have_semicolon) {
ExpectSemicolon();
} else {
ReportError(method->name_pos,
"function body or semicolon expected for method '%s'",
method->name->ToCString());
}
}
}
if (method->has_abstract && (async_modifier != RawFunction::kNoModifier)) {
ReportError(modifier_pos,
"abstract function '%s' may not be async, async* or sync*",
method->name->ToCString());
}
// Update function object.
func.set_name(*method->name);
func.set_is_abstract(method->has_abstract);
func.set_is_native(method->has_native);
func.set_result_type(*method->type);
// The result type may refer to func's type parameters,
// but was not parsed in the scope of func. Adjust.
method->type->SetScopeFunction(func);
func.set_end_token_pos(method_end_pos);
func.set_is_redirecting(is_redirecting);
func.set_modifier(async_modifier);
if (library_.is_dart_scheme() && library_.IsPrivate(*method->name)) {
func.set_is_reflectable(false);
}
if (is_patch_source() && IsPatchAnnotation(method->metadata_pos)) {
// Currently, we just ignore the patch annotation. If the function
// name already exists in the patched class, this function will replace
// the one in the patched class.
method->metadata_pos = TokenPosition::kNoSource;
}
if (method->metadata_pos.IsReal()) {
library_.AddFunctionMetadata(func, method->metadata_pos);
}
if (method->has_native) {
func.set_native_name(*native_name);
}
// If this method is a redirecting factory, set the redirection information.
if (!redirection_type.IsNull()) {
ASSERT(func.IsFactory());
func.SetRedirectionType(redirection_type);
if (!redirection_identifier.IsNull()) {
func.SetRedirectionIdentifier(redirection_identifier);
}
}
ASSERT(is_top_level_);
AddFormalParamsToFunction(&method->params, func);
ASSERT(innermost_function().raw() == func.raw());
innermost_function_ = Function::null();
ResolveSignatureTypeParameters(func);
members->AddFunction(func);
}
void Parser::ParseFieldDefinition(ClassDesc* members, MemberDesc* field) {
TRACE_PARSER("ParseFieldDefinition");
// The parser has read the first field name and is now at the token
// after the field name.
ASSERT(CurrentToken() == Token::kSEMICOLON ||
CurrentToken() == Token::kCOMMA || CurrentToken() == Token::kASSIGN);
ASSERT(field->type != NULL);
ASSERT(field->name_pos.IsReal());
ASSERT(current_member_ == field);
// All const fields are also final.
ASSERT(!field->has_const || field->has_final);
if (field->has_covariant) {
if (field->has_static) {
ReportError("static fields cannot be declared covariant");
} else if (field->has_final) {
ReportError("final fields cannot be declared covariant");
}
}
if (field->has_abstract) {
ReportError("keyword 'abstract' not allowed in field declaration");
}
if (field->has_external) {
ReportError("keyword 'external' not allowed in field declaration");
}
if (field->has_factory) {
ReportError("keyword 'factory' not allowed in field declaration");
}
if (!field->has_static && field->has_const) {
ReportError(field->name_pos, "instance field may not be 'const'");
}
Function& getter = Function::Handle(Z);
Function& setter = Function::Handle(Z);
Field& class_field = Field::ZoneHandle(Z);
Instance& init_value = Instance::Handle(Z);
while (true) {
bool has_initializer = CurrentToken() == Token::kASSIGN;
bool has_simple_literal = false;
if (has_initializer) {
ConsumeToken();
init_value = Object::sentinel().raw();
// For static fields, the initialization expression will be parsed
// through the kImplicitStaticFinalGetter method invocation/compilation.
// For instance fields, the expression is parsed when a constructor
// is compiled.
// For static fields with very simple initializer expressions
// (e.g. a literal number or string), we optimize away the
// kImplicitStaticFinalGetter and initialize the field here.
// However, the class finalizer will check the value type for
// assignability once the declared field type can be resolved. If the
// value is not assignable (assuming checked mode and disregarding actual
// mode), the field value is reset and a kImplicitStaticFinalGetter is
// created at finalization time.
if ((LookaheadToken(1) == Token::kSEMICOLON) ||
(LookaheadToken(1) == Token::kCOMMA)) {
has_simple_literal = IsSimpleLiteral(*field->type, &init_value);
}
SkipExpr();
} else {
// Static const and static final fields must have an initializer.
// Static const fields are implicitly final.
if (field->has_static && field->has_final) {
ReportError(field->name_pos,
"static %s field '%s' must have an initializer expression",
field->has_const ? "const" : "final",
field->name->ToCString());
}
}
TokenPosition end_token_pos = TokenPos();
// Create the field object.
const bool is_reflectable =
!(library_.is_dart_scheme() && library_.IsPrivate(*field->name));
class_field = Field::New(*field->name, field->has_static, field->has_final,
field->has_const, is_reflectable, current_class(),
*field->type, field->name_pos, end_token_pos);
class_field.set_has_initializer(has_initializer);
members->AddField(class_field);
field->field_ = &class_field;
if (is_patch_source() && IsPatchAnnotation(field->metadata_pos)) {
// Currently, we just ignore the patch annotation on fields.
// All fields in the patch class are added to the patched class.
field->metadata_pos = TokenPosition::kNoSource;
}
if ((field->metadata_pos.IsReal())) {
library_.AddFieldMetadata(class_field, field->metadata_pos);
}
// Start tracking types for fields with simple initializers in their
// definition. This avoids some of the overhead to track this at runtime
// and rules out many fields from being unnecessary unboxing candidates.
if (!field->has_static && has_initializer && has_simple_literal) {
class_field.RecordStore(init_value);
if (!init_value.IsNull() && init_value.IsDouble()) {
class_field.set_is_double_initialized(true);
}
}
// For static final fields (this includes static const fields), set value to
// "uninitialized" and create a kImplicitStaticFinalGetter getter method.
if (field->has_static && has_initializer) {
class_field.SetStaticValue(init_value, true);
if (!has_simple_literal) {
String& getter_name =
String::Handle(Z, Field::GetterSymbol(*field->name));
getter = Function::New(
getter_name, RawFunction::kImplicitStaticFinalGetter,
field->has_static, field->has_const,
/* is_abstract = */ false,
/* is_external = */ false,
/* is_native = */ false, current_class(), field->name_pos);
getter.set_result_type(*field->type);
getter.set_is_debuggable(false);
if (library_.is_dart_scheme() && library_.IsPrivate(*field->name)) {
getter.set_is_reflectable(false);
}
members->AddFunction(getter);
}
}
// For instance fields, we create implicit getter and setter methods.
if (!field->has_static) {
String& getter_name =
String::Handle(Z, Field::GetterSymbol(*field->name));
getter = Function::New(getter_name, RawFunction::kImplicitGetter,
field->has_static, field->has_final,
/* is_abstract = */ false,
/* is_external = */ false,
/* is_native = */ false, current_class(),
field->name_pos);
ParamList params;
ASSERT(current_class().raw() == getter.Owner());
params.AddReceiver(ReceiverType(current_class()), field->name_pos);
getter.set_result_type(*field->type);
getter.set_is_debuggable(false);
AddFormalParamsToFunction(&params, getter);
ResolveSignatureTypeParameters(getter);
members->AddFunction(getter);
if (!field->has_final) {
// Build a setter accessor for non-const fields.
String& setter_name =
String::Handle(Z, Field::SetterSymbol(*field->name));
setter = Function::New(setter_name, RawFunction::kImplicitSetter,
field->has_static, field->has_final,
/* is_abstract = */ false,
/* is_external = */ false,
/* is_native = */ false, current_class(),
field->name_pos);
ParamList params;
ASSERT(current_class().raw() == setter.Owner());
params.AddReceiver(ReceiverType(current_class()), field->name_pos);
params.AddFinalParameter(TokenPos(), &Symbols::Value(), field->type);
setter.set_result_type(Object::void_type());
setter.set_is_debuggable(false);
if (library_.is_dart_scheme() && library_.IsPrivate(*field->name)) {
setter.set_is_reflectable(false);
}
AddFormalParamsToFunction(&params, setter);
ResolveSignatureTypeParameters(setter);
members->AddFunction(setter);
}
}
if (CurrentToken() != Token::kCOMMA) {
break;
}
ConsumeToken();
field->name_pos = this->TokenPos();
field->name = ExpectIdentifier("field name expected");
}
ExpectSemicolon();
}
void Parser::CheckOperatorArity(const MemberDesc& member) {
intptr_t expected_num_parameters; // Includes receiver.
Token::Kind op = member.operator_token;
if (op == Token::kASSIGN_INDEX) {
expected_num_parameters = 3;
} else if ((op == Token::kBIT_NOT) || (op == Token::kNEGATE)) {
expected_num_parameters = 1;
} else {
expected_num_parameters = 2;
}
if ((member.params.num_optional_parameters > 0) ||
member.params.has_optional_positional_parameters ||
member.params.has_optional_named_parameters ||
(member.params.num_fixed_parameters != expected_num_parameters)) {
// Subtract receiver when reporting number of expected arguments.
ReportError(member.name_pos, "operator %s expects %" Pd " argument(s)",
member.name->ToCString(), (expected_num_parameters - 1));
}
}
void Parser::CheckMemberNameConflict(ClassDesc* members, MemberDesc* member) {
const String& name = *member->DictName();
if (name.Equals(members->class_name())) {
ReportError(member->name_pos, "%s '%s' conflicts with class name",
member->ToCString(), name.ToCString());
}
if (members->clazz().LookupTypeParameter(name) != TypeParameter::null()) {
ReportError(member->name_pos, "%s '%s' conflicts with type parameter",
member->ToCString(), name.ToCString());
}
for (int i = 0; i < members->members().length(); i++) {
MemberDesc* existing_member = &members->members()[i];
if (name.Equals(*existing_member->DictName())) {
ReportError(
member->name_pos, "%s '%s' conflicts with previously declared %s",
member->ToCString(), name.ToCString(), existing_member->ToCString());
}
}
}
void Parser::ParseClassMemberDefinition(ClassDesc* members,
TokenPosition metadata_pos) {
TRACE_PARSER("ParseClassMemberDefinition");
MemberDesc member;
current_member_ = &member;
member.metadata_pos = metadata_pos;
member.decl_begin_pos = TokenPos();
if ((CurrentToken() == Token::kEXTERNAL) &&
(LookaheadToken(1) != Token::kLPAREN)) {
ConsumeToken();
member.has_external = true;
}
if ((CurrentToken() == Token::kSTATIC) &&
(LookaheadToken(1) != Token::kLPAREN)) {
ConsumeToken();
member.has_static = true;
}
if (CurrentToken() == Token::kCOVARIANT) {
ConsumeToken();
member.has_covariant = true;
}
if (CurrentToken() == Token::kCONST) {
ConsumeToken();
member.has_const = true;
} else if (CurrentToken() == Token::kFINAL) {
ConsumeToken();
member.has_final = true;
}
if (CurrentToken() == Token::kVAR) {
if (member.has_const) {
ReportError("identifier expected after 'const'");
}
if (member.has_final) {
ReportError("identifier expected after 'final'");
}
ConsumeToken();
member.has_var = true;
// The member type is the 'dynamic' type.
member.type = &Object::dynamic_type();
} else if ((CurrentToken() == Token::kFACTORY) &&
(LookaheadToken(1) != Token::kLPAREN)) {
ConsumeToken();
if (member.has_static) {
ReportError("factory method cannot be explicitly marked static");
}
member.has_factory = true;
member.has_static = true;
// The result type depends on the name of the factory method.
}
// Optionally parse a type.
bool found_type = false;
{
// Lookahead to determine whether the next tokens are a return type.
TokenPosScope saved_pos(this);
if (TryParseType(true)) {
if (IsIdentifier() || (CurrentToken() == Token::kGET) ||
(CurrentToken() == Token::kSET) ||
(CurrentToken() == Token::kOPERATOR)) {
found_type = true;
}
}
}
if (found_type) {
// It is too early to resolve the type here, since it can be a result type
// referring to a not yet declared function type parameter.
member.type = &AbstractType::ZoneHandle(
Z, ParseTypeOrFunctionType(true, ClassFinalizer::kDoNotResolve));
}
// Optionally parse a (possibly named) constructor name or factory.
if (IsIdentifier() &&
(CurrentLiteral()->Equals(members->class_name()) || member.has_factory)) {
member.name_pos = TokenPos();
member.name = CurrentLiteral(); // Unqualified identifier.
ConsumeToken();
if (member.has_factory) {
// The factory name may be qualified, but the first identifier must match
// the name of the immediately enclosing class.
if (!member.name->Equals(members->class_name())) {
ReportError(member.name_pos, "factory name must be '%s'",
members->class_name().ToCString());
}
} else if (member.has_static) {
ReportError(member.name_pos, "constructor cannot be static");
}
if (member.type != NULL) {
ReportError(member.name_pos, "constructor must not specify return type");
}
// Do not bypass class resolution by using current_class() directly, since
// it may be a patch class.
const Object& result_type_class =
Object::Handle(Z, UnresolvedClass::New(LibraryPrefix::Handle(Z),
*member.name, member.name_pos));
// The type arguments of the result type are the type parameters of the
// current class. Note that in the case of a patch class, they are copied
// from the class being patched.
member.type = &Type::ZoneHandle(
Z,
Type::New(result_type_class,
TypeArguments::Handle(Z, current_class().type_parameters()),
member.name_pos));
// We must be dealing with a constructor or named constructor.
member.kind = RawFunction::kConstructor;
GrowableHandlePtrArray<const String> to_concat(Z, 3);
to_concat.Add(*member.name);
to_concat.Add(Symbols::Dot());
if (CurrentToken() == Token::kPERIOD) {
// Named constructor.
ConsumeToken();
member.dict_name = ExpectIdentifier("identifier expected");
to_concat.Add(*member.dict_name);
}
*member.name = Symbols::FromConcatAll(T, to_concat);
CheckToken(Token::kLPAREN);
} else if ((CurrentToken() == Token::kGET) && !member.has_var &&
(LookaheadToken(1) != Token::kLPAREN) &&
(LookaheadToken(1) != Token::kLT) &&
(LookaheadToken(1) != Token::kASSIGN) &&
(LookaheadToken(1) != Token::kCOMMA) &&
(LookaheadToken(1) != Token::kSEMICOLON)) {
ConsumeToken();
member.kind = RawFunction::kGetterFunction;
member.name_pos = this->TokenPos();
member.name = ExpectIdentifier("identifier expected");
// If the result type was not specified, it will be set to DynamicType.
} else if ((CurrentToken() == Token::kSET) && !member.has_var &&
(LookaheadToken(1) != Token::kLPAREN) &&
(LookaheadToken(1) != Token::kLT) &&
(LookaheadToken(1) != Token::kASSIGN) &&
(LookaheadToken(1) != Token::kCOMMA) &&
(LookaheadToken(1) != Token::kSEMICOLON)) {
ConsumeToken();
member.kind = RawFunction::kSetterFunction;
member.name_pos = this->TokenPos();
member.name = ExpectIdentifier("identifier expected");
CheckToken(Token::kLPAREN);
// The grammar allows a return type, so member.type is not always NULL here.
// If no return type is specified, the return type of the setter is dynamic.
if (member.type == NULL) {
member.type = &Object::dynamic_type();
}
} else if ((CurrentToken() == Token::kOPERATOR) && !member.has_var &&
(LookaheadToken(1) != Token::kLPAREN) &&
(LookaheadToken(1) != Token::kASSIGN) &&
(LookaheadToken(1) != Token::kCOMMA) &&
(LookaheadToken(1) != Token::kSEMICOLON)) {
// TODO(hausner): handle the case of a generic function named 'operator':
// eg: T operator<T>(a, b) => ...
ConsumeToken();
if (!Token::CanBeOverloaded(CurrentToken())) {
ReportError("invalid operator overloading");
}
if (member.has_static) {
ReportError("operator overloading functions cannot be static");
}
member.operator_token = CurrentToken();
member.has_operator = true;
member.kind = RawFunction::kRegularFunction;
member.name_pos = this->TokenPos();
member.name =
&String::ZoneHandle(Z, Symbols::Token(member.operator_token).raw());
ConsumeToken();
} else if (IsIdentifier()) {
member.name = CurrentLiteral();
member.name_pos = TokenPos();
ConsumeToken();
} else {
ReportError("identifier expected");
}
ASSERT(member.name != NULL);
if (IsParameterPart() || member.IsGetter()) {
// Constructor or method.
if (member.type == NULL) {
member.type = &Object::dynamic_type();
}
ASSERT(member.IsFactory() == member.has_factory);
// Note that member.type may still be unresolved and may refer to not yet
// parsed function type parameters.
ParseMethodOrConstructor(members, &member);
} else if (CurrentToken() == Token::kSEMICOLON ||
CurrentToken() == Token::kCOMMA ||
CurrentToken() == Token::kASSIGN) {
// Field definition.
if (member.has_const) {
// const fields are implicitly final.
member.has_final = true;
}
if (member.type == NULL) {
if (member.has_final) {
member.type = &Object::dynamic_type();
} else {
ReportError(
"missing 'var', 'final', 'const' or type"
" in field declaration");
}
}
if (!member.type->IsResolved()) {
AbstractType& type = AbstractType::ZoneHandle(Z, member.type->raw());
ResolveTypeParameters(&type);
member.type = &type;
}
ParseFieldDefinition(members, &member);
} else {
UnexpectedToken();
}
current_member_ = NULL;
CheckMemberNameConflict(members, &member);
members->AddMember(member);
}
void Parser::ParseEnumDeclaration(const GrowableObjectArray& pending_classes,
const Object& tl_owner,
TokenPosition metadata_pos) {
TRACE_PARSER("ParseEnumDeclaration");
const TokenPosition declaration_pos =
(metadata_pos.IsReal()) ? metadata_pos : TokenPos();
ConsumeToken();
const TokenPosition name_pos = TokenPos();
String* enum_name =
ExpectUserDefinedTypeIdentifier("enum type name expected");
if (FLAG_trace_parser) {
OS::Print("TopLevel parsing enum '%s'\n", enum_name->ToCString());
}
ExpectToken(Token::kLBRACE);
if (!IsIdentifier()) {
ReportError("Enumeration must have at least one name");
}
while (IsIdentifier()) {
ConsumeToken();
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
if (CurrentToken() == Token::kRBRACE) {
break;
}
} else if (CurrentToken() == Token::kRBRACE) {
break;
} else {
ReportError(", or } expected");
}
}
ExpectToken(Token::kRBRACE);
Object& obj = Object::Handle(Z, library_.LookupLocalObject(*enum_name));
if (!obj.IsNull()) {
ReportError(name_pos, "'%s' is already defined", enum_name->ToCString());
}
Class& cls = Class::Handle(Z);
cls = Class::New(library_, *enum_name, script_, declaration_pos);
library_.AddClass(cls);
cls.set_is_synthesized_class();
cls.set_is_enum_class();
if (metadata_pos.IsReal()) {
library_.AddClassMetadata(cls, tl_owner, metadata_pos);
}
cls.set_super_type(Type::Handle(Z, Type::ObjectType()));
pending_classes.Add(cls, Heap::kOld);
}
void Parser::ParseClassDeclaration(const GrowableObjectArray& pending_classes,
const Object& tl_owner,
TokenPosition metadata_pos) {
TRACE_PARSER("ParseClassDeclaration");
bool is_patch = false;
bool is_abstract = false;
TokenPosition declaration_pos =
metadata_pos.IsReal() ? metadata_pos : TokenPos();
if (is_patch_source() && IsPatchAnnotation(metadata_pos)) {
is_patch = true;
metadata_pos = TokenPosition::kNoSource;
declaration_pos = TokenPos();
} else if (CurrentToken() == Token::kABSTRACT) {
is_abstract = true;
ConsumeToken();
}
ExpectToken(Token::kCLASS);
const TokenPosition classname_pos = TokenPos();
String& class_name = *ExpectUserDefinedTypeIdentifier("class name expected");
if (FLAG_trace_parser) {
OS::Print("TopLevel parsing class '%s'\n", class_name.ToCString());
}
Class& cls = Class::Handle(Z);
TypeArguments& orig_type_parameters = TypeArguments::Handle(Z);
Object& obj = Object::Handle(Z, library_.LookupLocalObject(class_name));
if (obj.IsNull()) {
if (is_patch) {
ReportError(classname_pos, "missing class '%s' cannot be patched",
class_name.ToCString());
}
cls = Class::New(library_, class_name, script_, declaration_pos);
library_.AddClass(cls);
} else {
if (!obj.IsClass()) {
ReportError(classname_pos, "'%s' is already defined",
class_name.ToCString());
}
cls ^= obj.raw();
if (is_patch) {
// Preserve and reuse the original type parameters and bounds since the
// ones defined in the patch class will not be finalized.
orig_type_parameters = cls.type_parameters();
cls = Class::New(library_, class_name, script_, declaration_pos);
} else {
// Not patching a class, but it has been found. This must be one of the
// pre-registered classes from object.cc or a duplicate definition.
if (!(cls.is_prefinalized() || cls.IsClosureClass() ||
RawObject::IsImplicitFieldClassId(cls.id()))) {
ReportError(classname_pos, "class '%s' is already defined",
class_name.ToCString());
}
// Pre-registered classes need their scripts connected at this time.
cls.set_script(script_);
cls.set_token_pos(declaration_pos);
}
}
ASSERT(!cls.IsNull());
ASSERT(cls.functions() == Object::empty_array().raw());
set_current_class(cls);
ParseTypeParameters(true); // Parameterizing current class.
if (is_patch) {
// Check that the new type parameters are identical to the original ones.
const TypeArguments& new_type_parameters =
TypeArguments::Handle(Z, cls.type_parameters());
const int new_type_params_count =
new_type_parameters.IsNull() ? 0 : new_type_parameters.Length();
const int orig_type_params_count =
orig_type_parameters.IsNull() ? 0 : orig_type_parameters.Length();
if (new_type_params_count != orig_type_params_count) {
ReportError(classname_pos,
"class '%s' must be patched with identical type parameters",
class_name.ToCString());
}
TypeParameter& new_type_param = TypeParameter::Handle(Z);
TypeParameter& orig_type_param = TypeParameter::Handle(Z);
String& new_name = String::Handle(Z);
String& orig_name = String::Handle(Z);
AbstractType& new_bound = AbstractType::Handle(Z);
AbstractType& orig_bound = AbstractType::Handle(Z);
for (int i = 0; i < new_type_params_count; i++) {
new_type_param ^= new_type_parameters.TypeAt(i);
orig_type_param ^= orig_type_parameters.TypeAt(i);
new_name = new_type_param.name();
orig_name = orig_type_param.name();
if (!new_name.Equals(orig_name)) {
ReportError(new_type_param.token_pos(),
"type parameter '%s' of patch class '%s' does not match "
"original type parameter '%s'",
new_name.ToCString(), class_name.ToCString(),
orig_name.ToCString());
}
new_bound = new_type_param.bound();
orig_bound = orig_type_param.bound();
if (!new_bound.Equals(orig_bound)) {
ReportError(new_type_param.token_pos(),
"bound '%s' of type parameter '%s' of patch class '%s' "
"does not match original type parameter bound '%s'",
String::Handle(new_bound.UserVisibleName()).ToCString(),
new_name.ToCString(), class_name.ToCString(),
String::Handle(orig_bound.UserVisibleName()).ToCString());
}
}
cls.set_type_parameters(orig_type_parameters);
}
if (is_abstract) {
cls.set_is_abstract();
}
if (metadata_pos.IsReal()) {
library_.AddClassMetadata(cls, tl_owner, metadata_pos);
}
const bool is_mixin_declaration = (CurrentToken() == Token::kASSIGN);
if (is_mixin_declaration && is_patch) {
ReportError(classname_pos,
"mixin application '%s' may not be a patch class",
class_name.ToCString());
}
AbstractType& super_type = Type::Handle(Z);
if ((CurrentToken() == Token::kEXTENDS) || is_mixin_declaration) {
ConsumeToken(); // extends or =
const TokenPosition type_pos = TokenPos();
super_type = ParseType(ClassFinalizer::kResolveTypeParameters);
if (super_type.IsMalformedOrMalbounded()) {
ReportError(Error::Handle(Z, super_type.error()));
}
if (super_type.IsDynamicType()) {
// Unlikely here, since super type is not resolved yet.
ReportError(type_pos, "class '%s' may not extend 'dynamic'",
class_name.ToCString());
}
if (super_type.IsTypeParameter()) {
ReportError(type_pos, "class '%s' may not extend type parameter '%s'",
class_name.ToCString(),
String::Handle(Z, super_type.UserVisibleName()).ToCString());
}
// The class finalizer will check whether the super type is malbounded.
if (is_mixin_declaration) {
if (CurrentToken() != Token::kWITH) {
ReportError("mixin application clause 'with type' expected");
}
cls.set_is_mixin_app_alias();
cls.set_is_synthesized_class();
}
if (CurrentToken() == Token::kWITH) {
super_type = ParseMixins(super_type);
}
} else {
// No extends clause: implicitly extend Object, unless Object itself.
if (!cls.IsObjectClass()) {
super_type = Type::ObjectType();
}
}
ASSERT(!super_type.IsNull() || cls.IsObjectClass());
cls.set_super_type(super_type);
if (CurrentToken() == Token::kIMPLEMENTS) {
ParseInterfaceList(cls);
}
if (is_patch) {
cls.set_is_patch();
// Apply the changes to the patched class looked up above.
ASSERT(obj.raw() == library_.LookupLocalObject(class_name));
const Class& orig_class = Class::Cast(obj);
if (orig_class.is_finalized()) {
orig_class.SetRefinalizeAfterPatch();
pending_classes.Add(orig_class, Heap::kOld);
}
library_.AddPatchClass(cls);
}
pending_classes.Add(cls, Heap::kOld);
if (is_mixin_declaration) {
ExpectSemicolon();
} else {
CheckToken(Token::kLBRACE);
SkipBlock();
ExpectToken(Token::kRBRACE);
}
}
void Parser::ParseClassDefinition(const Class& cls) {
TRACE_PARSER("ParseClassDefinition");
INC_STAT(thread(), num_classes_parsed, 1);
set_current_class(cls);
is_top_level_ = true;
String& class_name = String::Handle(Z, cls.Name());
SkipMetadata();
if (CurrentToken() == Token::kABSTRACT) {
ConsumeToken();
}
ExpectToken(Token::kCLASS);
const TokenPosition class_pos = TokenPos();
ClassDesc members(Z, cls, class_name, false, class_pos);
while (CurrentToken() != Token::kLBRACE) {
ConsumeToken();
}
ExpectToken(Token::kLBRACE);
while (CurrentToken() != Token::kRBRACE) {
TokenPosition metadata_pos = SkipMetadata();
ParseClassMemberDefinition(&members, metadata_pos);
}
ExpectToken(Token::kRBRACE);
if (cls.LookupTypeParameter(class_name) != TypeParameter::null()) {
ReportError(class_pos, "class name conflicts with type parameter '%s'",
class_name.ToCString());
}
CheckConstructors(&members);
// Need to compute this here since MakeFixedLength() will clear the
// functions array in members.
const bool need_implicit_constructor =
!members.has_constructor() && !cls.is_patch();
cls.AddFields(members.fields());
// Creating a new array for functions marks the class as parsed.
Array& array = Array::Handle(Z, members.MakeFunctionsArray());
cls.SetFunctions(array);
// Add an implicit constructor if no explicit constructor is present.
// No implicit constructors are needed for patch classes.
if (need_implicit_constructor) {
AddImplicitConstructor(cls);
}
if (cls.is_patch()) {
// Apply the changes to the patched class looked up above.
Object& obj = Object::Handle(Z, library_.LookupLocalObject(class_name));
// The patched class must not be finalized yet.
const Class& orig_class = Class::Cast(obj);
ASSERT(!orig_class.is_finalized());
Error& error = Error::Handle(Z);
// Check if this is a case of patching a class after it has already
// been finalized.
if (orig_class.is_refinalize_after_patch()) {
if (!cls.ValidatePostFinalizePatch(orig_class, &error)) {
Report::LongJumpF(error, script_, class_pos,
"patch validation failed, not applying patch.\n");
}
}
if (!orig_class.ApplyPatch(cls, &error)) {
Report::LongJumpF(error, script_, class_pos, "applying patch failed");
}
}
}
void Parser::ParseEnumDefinition(const Class& cls) {
TRACE_PARSER("ParseEnumDefinition");
INC_STAT(thread(), num_classes_parsed, 1);
set_current_class(cls);
const Class& helper_class =
Class::Handle(Z, Library::LookupCoreClass(Symbols::_EnumHelper()));
ASSERT(!helper_class.IsNull());
SkipMetadata();
ExpectToken(Token::kENUM);
const String& enum_name = String::Handle(Z, cls.ScrubbedName());
ClassDesc enum_members(Z, cls, enum_name, false, cls.token_pos());
// Add instance field 'final int index'.
Field& index_field = Field::ZoneHandle(Z);
const Type& int_type = Type::Handle(Z, Type::IntType());
index_field = Field::New(Symbols::Index(),
false, // Not static.
true, // Field is final.
false, // Not const.
true, // Is reflectable.
cls, int_type, cls.token_pos(), cls.token_pos());
enum_members.AddField(index_field);
// Add implicit getter for index field.
const String& getter_name =
String::Handle(Z, Field::GetterSymbol(Symbols::Index()));
Function& getter = Function::Handle(Z);
getter = Function::New(getter_name, RawFunction::kImplicitGetter,
/* is_static = */ false,
/* is_const = */ true,
/* is_abstract = */ false,
/* is_external = */ false,
/* is_native = */ false, cls, cls.token_pos());
getter.set_result_type(int_type);
getter.set_is_debuggable(false);
ParamList params;
params.AddReceiver(&Object::dynamic_type(), cls.token_pos());
AddFormalParamsToFunction(&params, getter);
ResolveSignatureTypeParameters(getter);
enum_members.AddFunction(getter);
ASSERT(IsIdentifier());
ASSERT(CurrentLiteral()->raw() == cls.Name());
ConsumeToken(); // Enum type name.
ExpectToken(Token::kLBRACE);
Field& enum_value = Field::Handle(Z);
intptr_t i = 0;
GrowableArray<String*> declared_names(8);
while (IsIdentifier()) {
String* enum_ident = CurrentLiteral();
// Check for name conflicts.
if (enum_ident->raw() == cls.Name()) {
ReportError("enum identifier '%s' cannot be equal to enum type name",
CurrentLiteral()->ToCString());
} else if (enum_ident->raw() == Symbols::Index().raw()) {
ReportError(
"enum identifier conflicts with "
"implicit instance field 'index'");
} else if (enum_ident->raw() == Symbols::Values().raw()) {
ReportError(
"enum identifier conflicts with "
"implicit static field 'values'");
} else if (enum_ident->raw() == Symbols::toString().raw()) {
ReportError(
"enum identifier conflicts with "
"implicit instance method 'toString()'");
}
for (intptr_t n = 0; n < declared_names.length(); n++) {
if (enum_ident->Equals(*declared_names[n])) {
ReportError("Duplicate name '%s' in enum definition '%s'",
enum_ident->ToCString(), enum_name.ToCString());
}
}
declared_names.Add(enum_ident);
// Create the static const field for the enumeration value.
// Note that we do not set the field type to E, because we temporarily store
// a Smi in the field. The class finalizer would detect the bad type and
// reset the value to sentinel.
enum_value =
Field::New(*enum_ident,
/* is_static = */ true,
/* is_final = */ true,
/* is_const = */ true,
/* is_reflectable = */ true, cls, Object::dynamic_type(),
cls.token_pos(), cls.token_pos());
enum_value.set_has_initializer(false);
enum_members.AddField(enum_value);
// Initialize the field with the ordinal value. It will be patched
// later with the enum constant instance.
const Smi& ordinal_value = Smi::Handle(Z, Smi::New(i));
enum_value.SetStaticValue(ordinal_value, true);
enum_value.RecordStore(ordinal_value);
i++;
ConsumeToken(); // Enum value name.
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
}
}
ExpectToken(Token::kRBRACE);
const Class& array_class = Class::Handle(Z, I->object_store()->array_class());
TypeArguments& values_type_args =
TypeArguments::ZoneHandle(Z, TypeArguments::New(1));
const Type& enum_type = Type::Handle(Type::NewNonParameterizedType(cls));
values_type_args.SetTypeAt(0, enum_type);
Type& values_type = Type::ZoneHandle(
Z, Type::New(array_class, values_type_args, cls.token_pos(), Heap::kOld));
values_type ^= CanonicalizeType(values_type);
values_type_args = values_type.arguments(); // Get canonical type arguments.
// Add static field 'const List<E> values'.
Field& values_field = Field::ZoneHandle(Z);
values_field = Field::New(Symbols::Values(),
/* is_static = */ true,
/* is_final = */ true,
/* is_const = */ true,
/* is_reflectable = */ true, cls, values_type,
cls.token_pos(), cls.token_pos());
enum_members.AddField(values_field);
// Add static field 'const _deleted_enum_sentinel'.
// This field does not need to be of type E.
Field& deleted_enum_sentinel = Field::ZoneHandle(Z);
deleted_enum_sentinel =
Field::New(Symbols::_DeletedEnumSentinel(),
/* is_static = */ true,
/* is_final = */ true,
/* is_const = */ true,
/* is_reflectable = */ false, cls, Object::dynamic_type(),
cls.token_pos(), cls.token_pos());
enum_members.AddField(deleted_enum_sentinel);
// Allocate the immutable array containing the enumeration values.
// The actual enum instance values will be patched in later.
const Array& values_array = Array::Handle(Z, Array::New(i, Heap::kOld));
values_array.SetTypeArguments(values_type_args);
values_field.SetStaticValue(values_array, true);
values_field.RecordStore(values_array);
// Clone the _name field from the helper class.
Field& _name_field = Field::Handle(
Z, helper_class.LookupInstanceFieldAllowPrivate(Symbols::_name()));
ASSERT(!_name_field.IsNull());
_name_field = _name_field.Clone(cls);
enum_members.AddField(_name_field);
// Add an implicit getter function for the _name field. We use the field's
// name directly here so that the private key matches those of the other
// cloned helper functions and fields.
const Type& string_type = Type::Handle(Z, Type::StringType());
const String& name_getter_name = String::Handle(
Z, Field::GetterSymbol(String::Handle(_name_field.name())));
Function& name_getter = Function::Handle(Z);
name_getter = Function::New(name_getter_name, RawFunction::kImplicitGetter,
/* is_static = */ false,
/* is_const = */ true,
/* is_abstract = */ false,
/* is_external = */ false,
/* is_native = */ false, cls, cls.token_pos());
name_getter.set_result_type(string_type);
name_getter.set_is_debuggable(false);
ParamList name_params;
name_params.AddReceiver(&Object::dynamic_type(), cls.token_pos());
AddFormalParamsToFunction(&name_params, name_getter);
ResolveSignatureTypeParameters(name_getter);
enum_members.AddFunction(name_getter);
// Clone the toString() function from the helper class.
Function& to_string_func = Function::Handle(
Z, helper_class.LookupDynamicFunctionAllowPrivate(Symbols::toString()));
ASSERT(!to_string_func.IsNull());
to_string_func = to_string_func.Clone(cls);
enum_members.AddFunction(to_string_func);
// Clone the hashCode getter function from the helper class.
Function& hash_code_func = Function::Handle(
Z, helper_class.LookupDynamicFunctionAllowPrivate(Symbols::hashCode()));
ASSERT(!hash_code_func.IsNull());
hash_code_func = hash_code_func.Clone(cls);
enum_members.AddFunction(hash_code_func);
cls.AddFields(enum_members.fields());
const Array& functions = Array::Handle(Z, enum_members.MakeFunctionsArray());
cls.SetFunctions(functions);
}
// Add an implicit constructor to the given class.
void Parser::AddImplicitConstructor(const Class& cls) {
// The implicit constructor is unnamed, has no explicit parameter.
String& ctor_name = String::ZoneHandle(Z, cls.Name());
ctor_name = Symbols::FromDot(T, ctor_name);
// To indicate that this is an implicit constructor, we set the
// token position and end token position of the function
// to the token position of the class.
Function& ctor = Function::Handle(
Z, Function::New(ctor_name, RawFunction::kConstructor,
/* is_static = */ false,
/* is_const = */ false,
/* is_abstract = */ false,
/* is_external = */ false,
/* is_native = */ false, cls, cls.token_pos()));
ctor.set_end_token_pos(ctor.token_pos());
ctor.set_is_debuggable(false);
if (library_.is_dart_scheme() && library_.IsPrivate(ctor_name)) {
ctor.set_is_reflectable(false);
}
ParamList params;
// Add implicit 'this' parameter.
const AbstractType* receiver_type = ReceiverType(cls);
params.AddReceiver(receiver_type, cls.token_pos());
AddFormalParamsToFunction(&params, ctor);
ctor.set_result_type(Object::dynamic_type());
ResolveSignatureTypeParameters(ctor);
// The body of the constructor cannot modify the type of the constructed
// instance, which is passed in as the receiver.
ctor.set_result_type(*receiver_type);
cls.AddFunction(ctor);
}
void Parser::CheckFinalInitializationConflicts(const ClassDesc* class_desc,
const MemberDesc* member) {
const ParamList* params = &member->params;
if (!params->has_field_initializer) {
return;
}
const ZoneGrowableArray<ParamDesc>& parameters = *params->parameters;
const GrowableArray<const Field*>& fields = class_desc->fields();
String& field_name = String::Handle(Z);
for (intptr_t p = 0; p < parameters.length(); p++) {
const ParamDesc& current_param = parameters[p];
if (!current_param.is_field_initializer) {
continue;
}
const String& param_name = *current_param.name;
for (intptr_t i = 0; i < fields.length(); i++) {
const Field* current_field = fields.At(i);
if (!current_field->is_final() || !current_field->has_initializer()) {
continue;
}
field_name ^= current_field->name();
if (param_name.Equals(field_name)) {
ReportError(current_param.name_pos,
"final field '%s' is already initialized.",
param_name.ToCString());
}
}
}
}
// Check for cycles in constructor redirection.
void Parser::CheckConstructors(ClassDesc* class_desc) {
// Check for cycles in constructor redirection.
const GrowableArray<MemberDesc>& members = class_desc->members();
for (int i = 0; i < members.length(); i++) {
MemberDesc* member = &members[i];
if (member->IsConstructor()) {
// Check that our constructors don't try and reinitialize an initialized
// final variable.
CheckFinalInitializationConflicts(class_desc, member);
}
if (member->redirect_name == NULL) {
continue;
}
GrowableArray<MemberDesc*> ctors;
while ((member != NULL) && (member->redirect_name != NULL)) {
ASSERT(member->IsConstructor());
// Check whether we have already seen this member.
for (int i = 0; i < ctors.length(); i++) {
if (ctors[i] == member) {
ReportError(member->name_pos,
"cyclic reference in constructor redirection");
}
}
// We haven't seen this member. Add it to the list and follow
// the next redirection. If we can't find the constructor to
// which the current one redirects, we ignore the unresolved
// reference. We'll catch it later when the constructor gets
// compiled.
ctors.Add(member);
member = class_desc->LookupMember(*member->redirect_name);
}
}
}
void Parser::ParseMixinAppAlias(const GrowableObjectArray& pending_classes,
const Object& tl_owner,
TokenPosition metadata_pos) {
TRACE_PARSER("ParseMixinAppAlias");
const TokenPosition classname_pos = TokenPos();
String& class_name = *ExpectUserDefinedTypeIdentifier("class name expected");
if (FLAG_trace_parser) {
OS::Print("toplevel parsing mixin application alias class '%s'\n",
class_name.ToCString());
}
const Object& obj = Object::Handle(Z, library_.LookupLocalObject(class_name));
if (!obj.IsNull()) {
ReportError(classname_pos, "'%s' is already defined",
class_name.ToCString());
}
const Class& mixin_application = Class::Handle(
Z, Class::New(library_, class_name, script_, classname_pos));
mixin_application.set_is_mixin_app_alias();
library_.AddClass(mixin_application);
set_current_class(mixin_application);
ParseTypeParameters(true); // Parameterizing current class.
ExpectToken(Token::kASSIGN);
if (CurrentToken() == Token::kABSTRACT) {
mixin_application.set_is_abstract();
ConsumeToken();
}
const TokenPosition type_pos = TokenPos();
AbstractType& type = AbstractType::Handle(
Z, ParseType(ClassFinalizer::kResolveTypeParameters));
if (type.IsTypeParameter()) {
ReportError(type_pos, "class '%s' may not extend type parameter '%s'",
class_name.ToCString(),
String::Handle(Z, type.UserVisibleName()).ToCString());
}
CheckToken(Token::kWITH, "mixin application 'with Type' expected");
type = ParseMixins(type);
mixin_application.set_super_type(type);
mixin_application.set_is_synthesized_class();
// This mixin application alias needs an implicit constructor, but it is
// too early to call 'AddImplicitConstructor(mixin_application)' here,
// because this class should be lazily compiled.
if (CurrentToken() == Token::kIMPLEMENTS) {
ParseInterfaceList(mixin_application);
}
ExpectSemicolon();
pending_classes.Add(mixin_application, Heap::kOld);
if (metadata_pos.IsReal()) {
library_.AddClassMetadata(mixin_application, tl_owner, metadata_pos);
}
}
// Look ahead to detect if we are seeing ident [ TypeParameters ] ("(" | "=").
// We need this lookahead to distinguish between the optional return type
// and the alias name of a function type alias.
// Token position remains unchanged.
bool Parser::IsFunctionTypeAliasName(bool* use_function_type_syntax) {
if (IsIdentifier()) {
const Token::Kind ahead = LookaheadToken(1);
if ((ahead == Token::kLPAREN) || (ahead == Token::kASSIGN)) {
*use_function_type_syntax = (ahead == Token::kASSIGN);
return true;
}
}
const TokenPosScope saved_pos(this);
if (IsIdentifier() && (LookaheadToken(1) == Token::kLT)) {
ConsumeToken();
if (TryParseTypeParameters()) {
const Token::Kind current = CurrentToken();
if ((current == Token::kLPAREN) || (current == Token::kASSIGN)) {
*use_function_type_syntax = (current == Token::kASSIGN);
return true;
}
}
}
*use_function_type_syntax = false;
return false;
}
void Parser::ParseTypedef(const GrowableObjectArray& pending_classes,
const Object& tl_owner,
TokenPosition metadata_pos) {
TRACE_PARSER("ParseTypedef");
TokenPosition declaration_pos =
metadata_pos.IsReal() ? metadata_pos : TokenPos();
ExpectToken(Token::kTYPEDEF);
// Distinguish between two possible typedef forms:
// 1) returnType? identifier typeParameters? formalParameterList ;
// 2) identifier typeParameters? '=' functionType ;
bool use_function_type_syntax; // Set to false for form 1, true for form 2.
// If present, parse the result type of the function type.
AbstractType& result_type = Type::Handle(Z);
if (CurrentToken() == Token::kVOID) {
ConsumeToken();
result_type = Type::VoidType();
use_function_type_syntax = false;
} else if (!IsFunctionTypeAliasName(&use_function_type_syntax)) {
// Type annotations in typedef are never ignored, even in production mode.
// Wait until we have an owner class before resolving the result type.
result_type = ParseType(ClassFinalizer::kDoNotResolve);
ASSERT(!use_function_type_syntax);
}
const TokenPosition alias_name_pos = TokenPos();
const String* alias_name =
ExpectUserDefinedTypeIdentifier("function alias name expected");
// Lookup alias name and report an error if it is already defined in
// the library scope.
const Object& obj =
Object::Handle(Z, library_.LookupLocalObject(*alias_name));
if (!obj.IsNull()) {
ReportError(alias_name_pos, "'%s' is already defined",
alias_name->ToCString());
}
// Create the function type alias scope class. It will be linked to its
// signature function after it has been parsed. The type parameters, in order
// to be properly finalized, need to be associated to this scope class as
// they are parsed.
const Class& function_type_alias = Class::Handle(
Z, Class::New(library_, *alias_name, script_, declaration_pos));
function_type_alias.set_is_synthesized_class();
function_type_alias.set_is_abstract();
function_type_alias.set_is_prefinalized();
// Make sure the function type alias can be recognized as a typedef class by
// setting its signature function. When use_function_type_syntax is true, this
// temporary signature function is replaced while parsing the function type.
Function& signature_function = Function::Handle(
Z, Function::NewSignatureFunction(function_type_alias,
Function::Handle(Z), alias_name_pos));
function_type_alias.set_signature_function(signature_function);
library_.AddClass(function_type_alias);
ASSERT(function_type_alias.IsTypedefClass());
ASSERT(current_class().IsTopLevel());
set_current_class(function_type_alias);
// Parse the type parameters of the typedef class.
ParseTypeParameters(true); // Parameterizing current class.
ASSERT(innermost_function().IsNull());
if (use_function_type_syntax) {
ExpectToken(Token::kASSIGN);
ASSERT(result_type.IsNull()); // Not parsed yet.
const Type& function_type = Type::Handle(
Z, ParseFunctionType(result_type, ClassFinalizer::kDoNotResolve));
signature_function = function_type.signature();
} else {
innermost_function_ = signature_function.raw();
ParamList params;
// Parse the formal parameters of the function type.
CheckToken(Token::kLPAREN, "formal parameter list expected");
// Add implicit closure object parameter.
params.AddFinalParameter(TokenPos(), &Symbols::ClosureParameter(),
&Object::dynamic_type());
const bool allow_explicit_default_values = false;
const bool evaluate_metadata = false;
ParseFormalParameterList(use_function_type_syntax,
allow_explicit_default_values, evaluate_metadata,
&params);
if (result_type.IsNull()) {
result_type = Type::DynamicType();
}
signature_function.set_result_type(result_type);
// The result type may refer to the signature function's type parameters,
// but was not parsed in the scope of the signature function. Adjust.
result_type.SetScopeFunction(signature_function);
AddFormalParamsToFunction(&params, signature_function);
ASSERT(innermost_function().raw() == signature_function.raw());
innermost_function_ = Function::null();
}
ExpectSemicolon();
ASSERT(innermost_function().IsNull());
ASSERT(function_type_alias.signature_function() == signature_function.raw());
// At this point, all function type parameters have been parsed and the class
// function_type_alias is recognized as a typedef, so we can resolve all type
// parameters in the signature type defined by the typedef.
AbstractType& function_type =
Type::Handle(Z, signature_function.SignatureType());
ASSERT(current_class().raw() == function_type_alias.raw());
ResolveTypeParameters(&function_type);
// Resolving does not replace type or signature.
ASSERT(function_type_alias.signature_function() ==
Type::Cast(function_type).signature());
if (FLAG_trace_parser) {
OS::Print("TopLevel parsing function type alias '%s'\n",
String::Handle(Z, signature_function.Signature()).ToCString());
}
// The alias should not be marked as finalized yet, since it needs to be
// checked in the class finalizer for illegal self references.
ASSERT(!function_type_alias.is_finalized());
pending_classes.Add(function_type_alias, Heap::kOld);
if (metadata_pos.IsReal()) {
library_.AddClassMetadata(function_type_alias, tl_owner, metadata_pos);
}
}
// Consumes exactly one right angle bracket. If the current token is
// a single bracket token, it is consumed normally. However, if it is
// a double bracket, it is replaced by a single bracket token without
// incrementing the token index.
void Parser::ConsumeRightAngleBracket() {
if (token_kind_ == Token::kGT) {
ConsumeToken();
} else if (token_kind_ == Token::kSHR) {
token_kind_ = Token::kGT;
} else {
UNREACHABLE();
}
}
bool Parser::IsPatchAnnotation(TokenPosition pos) {
if (pos == TokenPosition::kNoSource) {
return false;
}
TokenPosScope saved_pos(this);
SetPosition(pos);
ExpectToken(Token::kAT);
return IsSymbol(Symbols::Patch());
}
TokenPosition Parser::SkipMetadata() {
if (CurrentToken() != Token::kAT) {
return TokenPosition::kNoSource;
}
TokenPosition metadata_pos = TokenPos();
while (CurrentToken() == Token::kAT) {
ConsumeToken();
ExpectIdentifier("identifier expected");
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ExpectIdentifier("identifier expected");
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ExpectIdentifier("identifier expected");
}
}
if (CurrentToken() == Token::kLPAREN) {
SkipToMatchingParenthesis();
}
}
return metadata_pos;
}
void Parser::SkipTypeArguments() {
if (CurrentToken() == Token::kLT) {
do {
ConsumeToken();
SkipTypeOrFunctionType(true);
} while (CurrentToken() == Token::kCOMMA);
Token::Kind token = CurrentToken();
if ((token == Token::kGT) || (token == Token::kSHR)) {
ConsumeRightAngleBracket();
} else {
ReportError("right angle bracket expected");
}
}
}
void Parser::SkipTypeParameters() {
// Function already parsed.
if (IsTypeParameters()) {
const bool skipped = TryParseTypeParameters();
ASSERT(skipped);
}
}
void Parser::SkipType(bool allow_void) {
if (CurrentToken() == Token::kVOID) {
if (!allow_void) {
ReportError("'void' not allowed here");
}
ConsumeToken();
} else {
ExpectIdentifier("type name expected");
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ExpectIdentifier("name expected");
}
SkipTypeArguments();
}
}
void Parser::SkipTypeOrFunctionType(bool allow_void) {
if (CurrentToken() == Token::kVOID) {
TokenPosition void_pos = TokenPos();
ConsumeToken();
// 'void' is always allowed as result type of a function type.
if (!allow_void && !IsFunctionTypeSymbol()) {
ReportError(void_pos, "'void' not allowed here");
}
} else if (!IsFunctionTypeSymbol()) {
// Including 'Function' not followed by '(' or '<'.
SkipType(false);
}
while (IsFunctionTypeSymbol()) {
ConsumeToken();
SkipTypeArguments();
if (CurrentToken() == Token::kLPAREN) {
SkipToMatchingParenthesis();
} else {
ReportError("'(' expected");
}
}
}
void Parser::ParseTypeParameters(bool parameterizing_class) {
TRACE_PARSER("ParseTypeParameters");
if (CurrentToken() == Token::kLT) {
GrowableArray<AbstractType*> type_parameters_array(Z, 2);
intptr_t index = 0;
TypeParameter& type_parameter = TypeParameter::Handle(Z);
TypeParameter& existing_type_parameter = TypeParameter::Handle(Z);
String& existing_type_parameter_name = String::Handle(Z);
AbstractType& type_parameter_bound = Type::Handle(Z);
do {
ConsumeToken();
const TokenPosition metadata_pos = SkipMetadata();
const TokenPosition type_parameter_pos = TokenPos();
const TokenPosition declaration_pos =
metadata_pos.IsReal() ? metadata_pos : type_parameter_pos;
String& type_parameter_name =
*ExpectUserDefinedTypeIdentifier("type parameter expected");
// Check for duplicate type parameters.
for (intptr_t i = 0; i < index; i++) {
existing_type_parameter ^= type_parameters_array.At(i)->raw();
existing_type_parameter_name = existing_type_parameter.name();
if (existing_type_parameter_name.Equals(type_parameter_name)) {
ReportError(type_parameter_pos, "duplicate type parameter '%s'",
type_parameter_name.ToCString());
}
}
if (CurrentToken() == Token::kEXTENDS) {
ConsumeToken();
// A bound may refer to the owner of the type parameter it applies to,
// i.e. to the class or function currently being parsed.
// Postpone resolution in order to avoid resolving the owner and its
// type parameters, as they are not fully parsed yet.
type_parameter_bound =
ParseTypeOrFunctionType(false, ClassFinalizer::kDoNotResolve);
} else {
type_parameter_bound = I->object_store()->object_type();
}
// Note that we cannot yet calculate the final index of a function type
// parameter, because we may not have parsed the parent function yet.
type_parameter = TypeParameter::New(
parameterizing_class ? current_class() : Class::Handle(Z),
parameterizing_class ? Function::Handle(Z) : innermost_function(),
index, type_parameter_name, type_parameter_bound, declaration_pos);
type_parameters_array.Add(
&AbstractType::ZoneHandle(Z, type_parameter.raw()));
if (metadata_pos.IsReal()) {
library_.AddTypeParameterMetadata(type_parameter, metadata_pos);
}
index++;
} while (CurrentToken() == Token::kCOMMA);
Token::Kind token = CurrentToken();
if ((token == Token::kGT) || (token == Token::kSHR)) {
ConsumeRightAngleBracket();
} else {
ReportError("right angle bracket expected");
}
const TypeArguments& type_parameters =
TypeArguments::Handle(Z, NewTypeArguments(type_parameters_array));
if (parameterizing_class) {
current_class().set_type_parameters(type_parameters);
} else {
innermost_function().set_type_parameters(type_parameters);
}
// Resolve type parameters referenced by upper bounds.
const intptr_t num_types = type_parameters.Length();
for (intptr_t i = 0; i < num_types; i++) {
type_parameter ^= type_parameters.TypeAt(i);
type_parameter_bound = type_parameter.bound();
ResolveTypeParameters(&type_parameter_bound);
type_parameter.set_bound(type_parameter_bound);
}
}
}
RawTypeArguments* Parser::ParseTypeArguments(
ClassFinalizer::FinalizationKind finalization) {
TRACE_PARSER("ParseTypeArguments");
if (CurrentToken() == Token::kLT) {
GrowableArray<AbstractType*> types;
AbstractType& type = AbstractType::Handle(Z);
do {
ConsumeToken();
type = ParseTypeOrFunctionType(true, finalization);
// Map a malformed type argument to dynamic.
if (type.IsMalformed()) {
type = Type::DynamicType();
}
types.Add(&AbstractType::ZoneHandle(Z, type.raw()));
} while (CurrentToken() == Token::kCOMMA);
Token::Kind token = CurrentToken();
if ((token == Token::kGT) || (token == Token::kSHR)) {
ConsumeRightAngleBracket();
} else {
ReportError("right angle bracket expected");
}
if (finalization != ClassFinalizer::kIgnore) {
TypeArguments& type_args = TypeArguments::Handle(NewTypeArguments(types));
if (finalization == ClassFinalizer::kCanonicalize) {
type_args = type_args.Canonicalize();
}
return type_args.raw();
}
}
return TypeArguments::null();
}
// Parse interface list and add to class cls.
void Parser::ParseInterfaceList(const Class& cls) {
TRACE_PARSER("ParseInterfaceList");
ASSERT(CurrentToken() == Token::kIMPLEMENTS);
const GrowableObjectArray& all_interfaces =
GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld));
AbstractType& interface = AbstractType::Handle(Z);
// First get all the interfaces already implemented by class.
Array& cls_interfaces = Array::Handle(Z, cls.interfaces());
for (intptr_t i = 0; i < cls_interfaces.Length(); i++) {
interface ^= cls_interfaces.At(i);
all_interfaces.Add(interface, Heap::kOld);
}
// Now parse and add the new interfaces.
do {
ConsumeToken();
TokenPosition interface_pos = TokenPos();
interface = ParseType(ClassFinalizer::kResolveTypeParameters);
if (interface.IsTypeParameter()) {
ReportError(interface_pos,
"type parameter '%s' may not be used in interface list",
String::Handle(Z, interface.UserVisibleName()).ToCString());
}
all_interfaces.Add(interface, Heap::kOld);
} while (CurrentToken() == Token::kCOMMA);
cls_interfaces = Array::MakeFixedLength(all_interfaces);
cls.set_interfaces(cls_interfaces);
}
RawAbstractType* Parser::ParseMixins(const AbstractType& super_type) {
TRACE_PARSER("ParseMixins");
ASSERT(CurrentToken() == Token::kWITH);
const GrowableObjectArray& mixin_types =
GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld));
AbstractType& mixin_type = AbstractType::Handle(Z);
do {
ConsumeToken();
mixin_type = ParseType(ClassFinalizer::kResolveTypeParameters);
if (mixin_type.IsDynamicType()) {
// The string 'dynamic' is not resolved yet at this point, but a malformed
// type mapped to dynamic can be encountered here.
ReportError(mixin_type.token_pos(), "illegal mixin of a malformed type");
}
if (mixin_type.IsTypeParameter()) {
ReportError(mixin_type.token_pos(),
"mixin type '%s' may not be a type parameter",
String::Handle(Z, mixin_type.UserVisibleName()).ToCString());
}
mixin_types.Add(mixin_type, Heap::kOld);
} while (CurrentToken() == Token::kCOMMA);
return MixinAppType::New(
super_type, Array::Handle(Z, Array::MakeFixedLength(mixin_types)));
}
void Parser::ParseTopLevelVariable(TopLevel* top_level,
const Object& owner,
TokenPosition metadata_pos) {
TRACE_PARSER("ParseTopLevelVariable");
const bool is_const = (CurrentToken() == Token::kCONST);
// Const fields are implicitly final.
const bool is_final = is_const || (CurrentToken() == Token::kFINAL);
const bool is_static = true;
const AbstractType& type = AbstractType::ZoneHandle(
Z, ParseConstFinalVarOrType(ClassFinalizer::kResolveTypeParameters));
Field& field = Field::Handle(Z);
Function& getter = Function::Handle(Z);
while (true) {
const TokenPosition name_pos = TokenPos();
String& var_name = *ExpectIdentifier("variable name expected");
if (library_.LookupLocalObject(var_name) != Object::null()) {
ReportError(name_pos, "'%s' is already defined", var_name.ToCString());
}
// Check whether a getter or setter for this name exists. A const
// or final field implies a setter which throws a NoSuchMethodError,
// thus we need to check for conflicts with existing setters and
// getters.
String& accessor_name =
String::Handle(Z, Field::LookupGetterSymbol(var_name));
if (!accessor_name.IsNull() &&
library_.LookupLocalObject(accessor_name) != Object::null()) {
ReportError(name_pos, "getter for '%s' is already defined",
var_name.ToCString());
}
accessor_name = Field::LookupSetterSymbol(var_name);
if (!accessor_name.IsNull() &&
library_.LookupLocalObject(accessor_name) != Object::null()) {
ReportError(name_pos, "setter for '%s' is already defined",
var_name.ToCString());
}
bool has_initializer = CurrentToken() == Token::kASSIGN;
bool has_simple_literal = false;
Instance& field_value = Instance::Handle(Z, Object::sentinel().raw());
if (has_initializer) {
ConsumeToken();
if (LookaheadToken(1) == Token::kSEMICOLON) {
has_simple_literal = IsSimpleLiteral(type, &field_value);
}
SkipExpr();
} else if (is_final) {
ReportError(name_pos, "missing initializer for final or const variable");
}
TokenPosition end_token_pos = TokenPos();
// Create the field object.
const bool is_reflectable =
!(library_.is_dart_scheme() && library_.IsPrivate(var_name));
field = Field::NewTopLevel(var_name, is_final, is_const, owner, name_pos,
end_token_pos);
field.SetFieldType(type);
field.set_has_initializer(has_initializer);
field.set_is_reflectable(is_reflectable);
top_level->AddField(field);
library_.AddObject(field, var_name);
if (metadata_pos.IsReal()) {
library_.AddFieldMetadata(field, metadata_pos);
}
if (has_initializer) {
field.SetStaticValue(field_value, true);
if (!has_simple_literal) {
// Create a static final getter.
String& getter_name = String::Handle(Z, Field::GetterSymbol(var_name));
getter =
Function::New(getter_name, RawFunction::kImplicitStaticFinalGetter,
is_static, is_const,
/* is_abstract = */ false,
/* is_external = */ false,
/* is_native = */ false, owner, name_pos);
getter.set_result_type(type);
getter.set_is_debuggable(false);
getter.set_is_reflectable(is_reflectable);
top_level->AddFunction(getter);
}
}
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
} else if (CurrentToken() == Token::kSEMICOLON) {
ConsumeToken();
break;
} else {
ExpectSemicolon(); // Reports error.
}
}
}
RawFunction::AsyncModifier Parser::ParseFunctionModifier() {
if (IsSymbol(Symbols::Async())) {
ConsumeToken();
if (CurrentToken() == Token::kMUL) {
const bool enableAsyncStar = true;
if (!enableAsyncStar) {
ReportError("async* generator functions are not yet supported");
}
ConsumeToken();
return RawFunction::kAsyncGen;
} else {
return RawFunction::kAsync;
}
} else if (IsSymbol(Symbols::Sync()) && (LookaheadToken(1) == Token::kMUL)) {
const bool enableSyncStar = true;
if (!enableSyncStar) {
ReportError("sync* generator functions are not yet supported");
}
ConsumeToken();
ConsumeToken();
return RawFunction::kSyncGen;
}
return RawFunction::kNoModifier;
}
void Parser::ParseTopLevelFunction(TopLevel* top_level,
const Object& owner,
TokenPosition metadata_pos) {
TRACE_PARSER("ParseTopLevelFunction");
const TokenPosition decl_begin_pos = TokenPos();
AbstractType& result_type = Type::Handle(Z, Type::DynamicType());
bool is_external = false;
bool is_patch = false;
if (is_patch_source() && IsPatchAnnotation(metadata_pos)) {
is_patch = true;
metadata_pos = TokenPosition::kNoSource;
} else if (CurrentToken() == Token::kEXTERNAL) {
ConsumeToken();
is_external = true;
}
// Parse optional result type.
if (IsFunctionReturnType()) {
// It is too early to resolve the type here, since it can be a result type
// referring to a not yet declared function type parameter.
result_type = ParseTypeOrFunctionType(true, ClassFinalizer::kDoNotResolve);
}
const TokenPosition name_pos = TokenPos();
const String& func_name = *ExpectIdentifier("function name expected");
bool found = library_.LookupLocalObject(func_name) != Object::null();
if (found && !is_patch) {
ReportError(name_pos, "'%s' is already defined", func_name.ToCString());
} else if (!found && is_patch) {
ReportError(name_pos, "missing '%s' cannot be patched",
func_name.ToCString());
}
const String& accessor_name =
String::Handle(Z, Field::LookupGetterSymbol(func_name));
if (!accessor_name.IsNull() &&
library_.LookupLocalObject(accessor_name) != Object::null()) {
ReportError(name_pos, "'%s' is already defined as getter",
func_name.ToCString());
}
// A setter named x= may co-exist with a function named x, thus we do
// not need to check setters.
Function& func = Function::Handle(
Z, Function::New(func_name, RawFunction::kRegularFunction,
/* is_static = */ true,
/* is_const = */ false,
/* is_abstract = */ false, is_external,
/* is_native = */ false, // May change.
owner, decl_begin_pos));
ASSERT(innermost_function().IsNull());
innermost_function_ = func.raw();
if (CurrentToken() == Token::kLT) {
ParseTypeParameters(false); // Not parameterizing class, but function.
}
CheckToken(Token::kLPAREN);
const TokenPosition function_pos = TokenPos();
ParamList params;
const bool use_function_type_syntax = false;
const bool allow_explicit_default_values = true;
const bool evaluate_metadata = false;
ParseFormalParameterList(use_function_type_syntax,
allow_explicit_default_values, evaluate_metadata,
&params);
const TokenPosition modifier_pos = TokenPos();
RawFunction::AsyncModifier func_modifier = ParseFunctionModifier();
TokenPosition function_end_pos = function_pos;
bool is_native = false;
String* native_name = NULL;
if (is_external) {
function_end_pos = TokenPos();
ExpectSemicolon();
} else if (CurrentToken() == Token::kLBRACE) {
SkipBlock();
function_end_pos = TokenPos();
ExpectToken(Token::kRBRACE);
} else if (CurrentToken() == Token::kARROW) {
if ((func_modifier & RawFunction::kGeneratorBit) != 0) {
ReportError(modifier_pos,
"=> style function may not be sync* or async* generator");
}
ConsumeToken();
BoolScope allow_await(&this->await_is_keyword_,
func_modifier != RawFunction::kNoModifier);
SkipExpr();
function_end_pos = TokenPos();
ExpectSemicolon();
} else if (IsSymbol(Symbols::Native())) {
native_name = &ParseNativeDeclaration();
function_end_pos = TokenPos();
ExpectSemicolon();
is_native = true;
func.set_is_native(true);
} else {
ReportError("function block expected");
}
func.set_result_type(result_type);
// The result type may refer to func's type parameters,
// but was not parsed in the scope of func. Adjust.
result_type.SetScopeFunction(func);
func.set_end_token_pos(function_end_pos);
func.set_modifier(func_modifier);
if (library_.is_dart_scheme() && library_.IsPrivate(func_name)) {
func.set_is_reflectable(false);
}
if (is_native) {
func.set_native_name(*native_name);
}
AddFormalParamsToFunction(&params, func);
ASSERT(innermost_function().raw() == func.raw());
innermost_function_ = Function::null();
ResolveSignatureTypeParameters(func);
top_level->AddFunction(func);
if (!is_patch) {
library_.AddObject(func, func_name);
} else {
// Need to remove the previously added function that is being patched.
const Class& toplevel_cls = Class::Handle(Z, library_.toplevel_class());
const Function& replaced_func =
Function::Handle(Z, toplevel_cls.LookupStaticFunction(func_name));
ASSERT(!replaced_func.IsNull());
toplevel_cls.RemoveFunction(replaced_func);
library_.ReplaceObject(func, func_name);
}
if (metadata_pos.IsReal()) {
library_.AddFunctionMetadata(func, metadata_pos);
}
}
void Parser::ParseTopLevelAccessor(TopLevel* top_level,
const Object& owner,
TokenPosition metadata_pos) {
TRACE_PARSER("ParseTopLevelAccessor");
const TokenPosition decl_begin_pos = TokenPos();
const bool is_static = true;
bool is_external = false;
bool is_patch = false;
AbstractType& result_type = AbstractType::Handle(Z);
if (is_patch_source() && IsPatchAnnotation(metadata_pos)) {
is_patch = true;
metadata_pos = TokenPosition::kNoSource;
} else if (CurrentToken() == Token::kEXTERNAL) {
ConsumeToken();
is_external = true;
}
bool is_getter = (CurrentToken() == Token::kGET);
if (CurrentToken() == Token::kGET || CurrentToken() == Token::kSET) {
ConsumeToken();
result_type = Type::DynamicType();
} else {
result_type =
ParseTypeOrFunctionType(true, ClassFinalizer::kResolveTypeParameters);
is_getter = (CurrentToken() == Token::kGET);
if (CurrentToken() == Token::kGET || CurrentToken() == Token::kSET) {
ConsumeToken();
} else {
UnexpectedToken();
}
}
const TokenPosition name_pos = TokenPos();
const String* field_name = ExpectIdentifier("accessor name expected");
const TokenPosition accessor_pos = TokenPos();
ParamList params;
if (!is_getter) {
const bool use_function_type_syntax = false;
const bool allow_explicit_default_values = true;
const bool evaluate_metadata = false;
ParseFormalParameterList(use_function_type_syntax,
allow_explicit_default_values, evaluate_metadata,
&params);
}
String& accessor_name = String::ZoneHandle(Z);
int expected_num_parameters = -1;
if (is_getter) {
expected_num_parameters = 0;
accessor_name = Field::GetterSymbol(*field_name);
} else {
expected_num_parameters = 1;
accessor_name = Field::SetterSymbol(*field_name);
}
if ((params.num_fixed_parameters != expected_num_parameters) ||
(params.num_optional_parameters != 0)) {
ReportError(name_pos, "illegal %s parameters",
is_getter ? "getter" : "setter");
}
// Check whether this getter conflicts with a function or top-level variable
// with the same name.
if (is_getter &&
(library_.LookupLocalObject(*field_name) != Object::null())) {
ReportError(name_pos, "'%s' is already defined in this library",
field_name->ToCString());
}
// Check whether this setter conflicts with the implicit setter
// of a top-level variable with the same name.
if (!is_getter &&
(library_.LookupLocalField(*field_name) != Object::null())) {
ReportError(name_pos, "Variable '%s' is already defined in this library",
field_name->ToCString());
}
bool found = library_.LookupLocalObject(accessor_name) != Object::null();
if (found && !is_patch) {
ReportError(name_pos, "%s for '%s' is already defined",
is_getter ? "getter" : "setter", field_name->ToCString());
} else if (!found && is_patch) {
ReportError(name_pos, "missing %s for '%s' cannot be patched",
is_getter ? "getter" : "setter", field_name->ToCString());
}
const TokenPosition modifier_pos = TokenPos();
RawFunction::AsyncModifier func_modifier = ParseFunctionModifier();
if (!is_getter && (func_modifier != RawFunction::kNoModifier)) {
ReportError(modifier_pos,
"setter function cannot be async, async* or sync*");
}
TokenPosition accessor_end_pos = accessor_pos;
bool is_native = false;
String* native_name = NULL;
if (is_external) {
accessor_end_pos = TokenPos();
ExpectSemicolon();
} else if (CurrentToken() == Token::kLBRACE) {
SkipBlock();
accessor_end_pos = TokenPos();
ExpectToken(Token::kRBRACE);
} else if (CurrentToken() == Token::kARROW) {
if (is_getter && ((func_modifier & RawFunction::kGeneratorBit) != 0)) {
ReportError(modifier_pos,
"=> style getter may not be sync* or async* generator");
}
ConsumeToken();
BoolScope allow_await(&this->await_is_keyword_,
func_modifier != RawFunction::kNoModifier);
SkipExpr();
accessor_end_pos = TokenPos();
ExpectSemicolon();
} else if (IsSymbol(Symbols::Native())) {
native_name = &ParseNativeDeclaration();
accessor_end_pos = TokenPos();
ExpectSemicolon();
is_native = true;
} else {
ReportError("function block expected");
}
Function& func = Function::Handle(
Z, Function::New(accessor_name,
is_getter ? RawFunction::kGetterFunction
: RawFunction::kSetterFunction,
is_static,
/* is_const = */ false,
/* is_abstract = */ false, is_external, is_native, owner,
decl_begin_pos));
func.set_result_type(result_type);
// The result type may refer to func's type parameters,
// but was not parsed in the scope of func. Adjust.
result_type.SetScopeFunction(func);
func.set_end_token_pos(accessor_end_pos);
func.set_modifier(func_modifier);
if (is_native) {
func.set_is_debuggable(false);
func.set_native_name(*native_name);
}
if (library_.is_dart_scheme() && library_.IsPrivate(accessor_name)) {
func.set_is_reflectable(false);
}
AddFormalParamsToFunction(&params, func);
ResolveSignatureTypeParameters(func);
top_level->AddFunction(func);
if (!is_patch) {
library_.AddObject(func, accessor_name);
} else {
// Need to remove the previously added accessor that is being patched.
const Class& toplevel_cls = Class::Handle(
Z, owner.IsClass() ? Class::Cast(owner).raw()
: PatchClass::Cast(owner).patched_class());
const Function& replaced_func =
Function::Handle(Z, toplevel_cls.LookupFunction(accessor_name));
ASSERT(!replaced_func.IsNull());
toplevel_cls.RemoveFunction(replaced_func);
library_.ReplaceObject(func, accessor_name);
}
if (metadata_pos.IsReal()) {
library_.AddFunctionMetadata(func, metadata_pos);
}
}
RawObject* Parser::CallLibraryTagHandler(Dart_LibraryTag tag,
TokenPosition token_pos,
const String& url) {
Dart_LibraryTagHandler handler = I->library_tag_handler();
if (handler == NULL) {
if (url.StartsWith(Symbols::DartScheme())) {
if (tag == Dart_kCanonicalizeUrl) {
return url.raw();
}
return Object::null();
}
ReportError(token_pos, "no library handler registered");
}
// Block class finalization attempts when calling into the library
// tag handler.
I->BlockClassFinalization();
Object& result = Object::Handle(Z);
{
TransitionVMToNative transition(T);
Api::Scope api_scope(T);
Dart_Handle retval = handler(tag, Api::NewHandle(T, library_.raw()),
Api::NewHandle(T, url.raw()));
result = Api::UnwrapHandle(retval);
}
I->UnblockClassFinalization();
if (result.IsError()) {
// In case of an error we append an explanatory error message to the
// error obtained from the library tag handler.
const Error& prev_error = Error::Cast(result);
Report::LongJumpF(prev_error, script_, token_pos, "library handler failed");
}
if (tag == Dart_kCanonicalizeUrl) {
if (!result.IsString()) {
ReportError(token_pos, "library handler failed URI canonicalization");
}
}
return result.raw();
}
void Parser::ParseLibraryName() {
ASSERT(CurrentToken() == Token::kLIBRARY);
ConsumeToken();
String& lib_name = *ExpectIdentifier("library name expected");
if (CurrentToken() == Token::kPERIOD) {
GrowableHandlePtrArray<const String> pieces(Z, 3);
pieces.Add(lib_name);
while (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
pieces.Add(Symbols::Dot());
pieces.Add(*ExpectIdentifier("malformed library name"));
}
lib_name = Symbols::FromConcatAll(T, pieces);
}
library_.SetName(lib_name);
ExpectSemicolon();
}
void Parser::ParseIdentList(GrowableObjectArray* names) {
if (!IsIdentifier()) {
ReportError("identifier expected");
}
while (IsIdentifier()) {
names->Add(*CurrentLiteral(), allocation_space_);
ConsumeToken(); // Identifier.
if (CurrentToken() != Token::kCOMMA) {
return;
}
ConsumeToken(); // Comma.
}
}
void Parser::ParseLibraryImportExport(const Object& tl_owner,
TokenPosition metadata_pos) {
ASSERT(Thread::Current()->IsMutatorThread());
bool is_import = (CurrentToken() == Token::kIMPORT);
bool is_export = (CurrentToken() == Token::kEXPORT);
ASSERT(is_import || is_export);
const TokenPosition import_pos = TokenPos();
ConsumeToken();
CheckToken(Token::kSTRING, "library url expected");
AstNode* url_literal = ParseStringLiteral(false);
if (FLAG_conditional_directives) {
bool condition_triggered = false;
while (CurrentToken() == Token::kIF) {
// Conditional import: if (env == val) uri.
ConsumeToken();
ExpectToken(Token::kLPAREN);
// Parse dotted name.
const GrowableObjectArray& pieces = GrowableObjectArray::Handle(
Z, GrowableObjectArray::New(allocation_space_));
pieces.Add(*ExpectIdentifier("identifier expected"), allocation_space_);
while (CurrentToken() == Token::kPERIOD) {
pieces.Add(Symbols::Dot(), allocation_space_);
ConsumeToken();
pieces.Add(*ExpectIdentifier("identifier expected"), allocation_space_);
}
if (I->obfuscate()) {
// If we are obfuscating then we need to deobfuscate environment name.
Obfuscator::Deobfuscate(T, pieces);
}
AstNode* valueNode = NULL;
if (CurrentToken() == Token::kEQ) {
ConsumeToken();
CheckToken(Token::kSTRING, "string literal expected");
valueNode = ParseStringLiteral(false);
ASSERT(valueNode->IsLiteralNode());
ASSERT(valueNode->AsLiteralNode()->literal().IsString());
}
ExpectToken(Token::kRPAREN);
CheckToken(Token::kSTRING, "library url expected");
AstNode* conditional_url_literal = ParseStringLiteral(false);
// If there was already a condition that triggered, don't try to match
// again.
if (condition_triggered) {
continue;
}
// Check if this conditional line overrides the default import.
const String& key = String::Handle(String::ConcatAll(
Array::Handle(Array::MakeFixedLength(pieces)), allocation_space_));
const String& value =
(valueNode == NULL)
? Symbols::True()
: String::Cast(valueNode->AsLiteralNode()->literal());
// Call the embedder to supply us with the environment.
const String& env_value =
String::Handle(Api::GetEnvironmentValue(T, key));
if (!env_value.IsNull() && env_value.Equals(value)) {
condition_triggered = true;
url_literal = conditional_url_literal;
}
}
}
ASSERT(url_literal->IsLiteralNode());
ASSERT(url_literal->AsLiteralNode()->literal().IsString());
const String& url = String::Cast(url_literal->AsLiteralNode()->literal());
if (url.Length() == 0) {
ReportError("library url expected");
}
bool is_deferred_import = false;
if (is_import && (CurrentToken() == Token::kDEFERRED)) {
is_deferred_import = true;
ConsumeToken();
CheckToken(Token::kAS, "'as' expected");
}
String& prefix = String::Handle(Z);
TokenPosition prefix_pos = TokenPosition::kNoSource;
if (is_import && (CurrentToken() == Token::kAS)) {
ConsumeToken();
prefix_pos = TokenPos();
prefix =
ExpectUserDefinedTypeIdentifier("prefix identifier expected")->raw();
}
Array& show_names = Array::Handle(Z);
Array& hide_names = Array::Handle(Z);
if (is_deferred_import || IsSymbol(Symbols::Show()) ||
IsSymbol(Symbols::Hide())) {
GrowableObjectArray& show_list = GrowableObjectArray::Handle(
Z, GrowableObjectArray::New(allocation_space_));
GrowableObjectArray& hide_list = GrowableObjectArray::Handle(
Z, GrowableObjectArray::New(allocation_space_));
// Libraries imported through deferred import automatically hide
// the name 'loadLibrary'.
if (is_deferred_import) {
hide_list.Add(Symbols::LoadLibrary());
}
for (;;) {
if (IsSymbol(Symbols::Show())) {
ConsumeToken();
ParseIdentList(&show_list);
} else if (IsSymbol(Symbols::Hide())) {
ConsumeToken();
ParseIdentList(&hide_list);
} else {
break;
}
}
if (show_list.Length() > 0) {
show_names = Array::MakeFixedLength(show_list);
}
if (hide_list.Length() > 0) {
hide_names = Array::MakeFixedLength(hide_list);
}
}
ExpectSemicolon();
// Canonicalize library URL.
const String& canon_url = String::CheckedHandle(
CallLibraryTagHandler(Dart_kCanonicalizeUrl, import_pos, url));
// Create a new library if it does not exist yet.
Library& library = Library::Handle(Z, Library::LookupLibrary(T, canon_url));
if (library.IsNull()) {
library = Library::New(canon_url);
library.Register(T);
}
// If loading hasn't been requested yet, and if this is not a deferred
// library import, call the library tag handler to request loading
// the library.
if (library.LoadNotStarted() &&
(!is_deferred_import || FLAG_load_deferred_eagerly)) {
library.SetLoadRequested();
CallLibraryTagHandler(Dart_kImportTag, import_pos, canon_url);
}
Namespace& ns =
Namespace::Handle(Z, Namespace::New(library, show_names, hide_names));
if (metadata_pos.IsReal()) {
ns.AddMetadata(tl_owner, metadata_pos);
}
// Ensure that private dart:_ libraries are only imported into dart:
// libraries, including indirectly through exports.
const String& lib_url = String::Handle(Z, library_.url());
if (canon_url.StartsWith(Symbols::DartSchemePrivate()) &&
!lib_url.StartsWith(Symbols::DartScheme()) &&
!lib_url.StartsWith(Symbols::DartInternalPackage())) {
ReportError(import_pos, "private library is not accessible");
}
if (!FLAG_enable_mirrors && Symbols::DartMirrors().Equals(canon_url)) {
ReportError(import_pos,
"import of dart:mirrors with --enable-mirrors=false");
}
if (is_import) {
if (prefix.IsNull() || (prefix.Length() == 0)) {
ASSERT(!is_deferred_import);
library_.AddImport(ns);
} else {
LibraryPrefix& library_prefix = LibraryPrefix::Handle(Z);
library_prefix = library_.LookupLocalLibraryPrefix(prefix);
if (!library_prefix.IsNull()) {
// Check that prefix names of deferred import clauses are
// unique.
if (!is_deferred_import && library_prefix.is_deferred_load()) {
ReportError(prefix_pos,
"prefix '%s' already used in a deferred import clause",
prefix.ToCString());
}
if (is_deferred_import) {
ReportError(prefix_pos, "prefix of deferred import must be unique");
}
library_prefix.AddImport(ns);
} else {
library_prefix =
LibraryPrefix::New(prefix, ns, is_deferred_import, library_);
library_.AddObject(library_prefix, prefix);
}
}
} else {
ASSERT(is_export);
library_.AddExport(ns);
}
}
void Parser::ParseLibraryPart() {
const TokenPosition source_pos = TokenPos();
ConsumeToken(); // Consume "part".
if (IsSymbol(Symbols::Of())) {
ReportError("part of declarations are not allowed in script files");
}
CheckToken(Token::kSTRING, "url expected");
AstNode* url_literal = ParseStringLiteral(false);
ASSERT(url_literal->IsLiteralNode());
ASSERT(url_literal->AsLiteralNode()->literal().IsString());
const String& url = String::Cast(url_literal->AsLiteralNode()->literal());
ExpectSemicolon();
const String& canon_url = String::CheckedHandle(
CallLibraryTagHandler(Dart_kCanonicalizeUrl, source_pos, url));
CallLibraryTagHandler(Dart_kSourceTag, source_pos, canon_url);
}
void Parser::ParseLibraryDefinition(const Object& tl_owner) {
TRACE_PARSER("ParseLibraryDefinition");
// Handle the script tag.
if (CurrentToken() == Token::kSCRIPTTAG) {
// Nothing to do for script tags except to skip them.
ConsumeToken();
}
ASSERT(script_.kind() != RawScript::kSourceTag);
// We may read metadata tokens that are part of the toplevel
// declaration that follows the library definitions. Therefore, we
// need to remember the position of the last token that was
// successfully consumed.
TokenPosition rewind_pos = TokenPos();
TokenPosition metadata_pos = SkipMetadata();
if (CurrentToken() == Token::kLIBRARY) {
if (is_patch_source()) {
ReportError("patch cannot override library name");
}
ParseLibraryName();
if (metadata_pos.IsReal()) {
library_.AddLibraryMetadata(tl_owner, metadata_pos);
}
rewind_pos = TokenPos();
metadata_pos = SkipMetadata();
}
while ((CurrentToken() == Token::kIMPORT) ||
(CurrentToken() == Token::kEXPORT)) {
ParseLibraryImportExport(tl_owner, metadata_pos);
rewind_pos = TokenPos();
metadata_pos = SkipMetadata();
}
// Core lib has not been explicitly imported, so we implicitly
// import it here.
if (!library_.ImportsCorelib()) {
Library& core_lib = Library::Handle(Z, Library::CoreLibrary());
ASSERT(!core_lib.IsNull());
const Namespace& core_ns = Namespace::Handle(
Z,
Namespace::New(core_lib, Object::null_array(), Object::null_array()));
library_.AddImport(core_ns);
}
while (CurrentToken() == Token::kPART) {
ParseLibraryPart();
rewind_pos = TokenPos();
metadata_pos = SkipMetadata();
}
SetPosition(rewind_pos);
}
void Parser::ParsePartHeader() {
SkipMetadata();
CheckToken(Token::kPART, "'part of' expected");
ConsumeToken();
if (!IsSymbol(Symbols::Of())) {
ReportError("'part of' expected");
}
ConsumeToken();
// The VM is not required to check that the library name or URI matches the
// name or URI of the current library, so we ignore them.
if (CurrentToken() == Token::kSTRING) {
ParseStringLiteral(false);
} else {
ExpectIdentifier("library name expected");
while (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ExpectIdentifier("malformed library name");
}
}
ExpectSemicolon();
}
void Parser::ParseTopLevel() {
TRACE_PARSER("ParseTopLevel");
// Collect the classes found at the top level in this growable array.
// They need to be registered with class finalization after parsing
// has been completed.
ObjectStore* object_store = I->object_store();
const GrowableObjectArray& pending_classes =
GrowableObjectArray::Handle(Z, object_store->pending_classes());
SetPosition(TokenPosition::kMinSource);
is_top_level_ = true;
TopLevel top_level(Z);
Object& tl_owner = Object::Handle(Z);
Class& toplevel_class = Class::Handle(Z, library_.toplevel_class());
if (toplevel_class.IsNull()) {
toplevel_class =
Class::New(library_, Symbols::TopLevel(), script_, TokenPos());
toplevel_class.set_library(library_);
library_.set_toplevel_class(toplevel_class);
tl_owner = toplevel_class.raw();
} else {
tl_owner = PatchClass::New(toplevel_class, script_);
}
if (is_library_source() || is_patch_source()) {
set_current_class(toplevel_class);
ParseLibraryDefinition(tl_owner);
} else if (is_part_source()) {
ParsePartHeader();
}
const Class& cls = Class::Handle(Z);
while (true) {
set_current_class(cls); // No current class.
TokenPosition metadata_pos = SkipMetadata();
if (CurrentToken() == Token::kCLASS) {
ParseClassDeclaration(pending_classes, tl_owner, metadata_pos);
} else if (CurrentToken() == Token::kENUM) {
ParseEnumDeclaration(pending_classes, tl_owner, metadata_pos);
} else if ((CurrentToken() == Token::kTYPEDEF) &&
(LookaheadToken(1) != Token::kLPAREN)) {
set_current_class(toplevel_class);
ParseTypedef(pending_classes, tl_owner, metadata_pos);
} else if ((CurrentToken() == Token::kABSTRACT) &&
(LookaheadToken(1) == Token::kCLASS)) {
ParseClassDeclaration(pending_classes, tl_owner, metadata_pos);
} else {
set_current_class(toplevel_class);
if (IsVariableDeclaration()) {
ParseTopLevelVariable(&top_level, tl_owner, metadata_pos);
} else if (IsFunctionDeclaration()) {
ParseTopLevelFunction(&top_level, tl_owner, metadata_pos);
} else if (IsTopLevelAccessor()) {
ParseTopLevelAccessor(&top_level, tl_owner, metadata_pos);
} else if (CurrentToken() == Token::kEOS) {
break;
} else {
UnexpectedToken();
}
}
}
if (top_level.fields().length() > 0) {
toplevel_class.AddFields(top_level.fields());
}
for (intptr_t i = 0; i < top_level.functions().length(); i++) {
toplevel_class.AddFunction(*top_level.functions()[i]);
}
if (toplevel_class.is_finalized()) {
toplevel_class.ResetFinalization();
}
pending_classes.Add(toplevel_class, Heap::kOld);
}
void Parser::ChainNewBlock(LocalScope* outer_scope) {
Block* block = new (Z) Block(current_block_, outer_scope,
new (Z) SequenceNode(TokenPos(), outer_scope));
current_block_ = block;
}
void Parser::OpenBlock() {
ASSERT(current_block_ != NULL);
LocalScope* outer_scope = current_block_->scope;
ChainNewBlock(new (Z) LocalScope(outer_scope, outer_scope->function_level(),
outer_scope->loop_level()));
}
void Parser::OpenLoopBlock() {
ASSERT(current_block_ != NULL);
LocalScope* outer_scope = current_block_->scope;
ChainNewBlock(new (Z) LocalScope(outer_scope, outer_scope->function_level(),
outer_scope->loop_level() + 1));
}
void Parser::OpenFunctionBlock(const Function& func) {
LocalScope* outer_scope;
if (current_block_ == NULL) {
if (!func.IsLocalFunction()) {
// We are compiling a non-nested function.
outer_scope = new (Z) LocalScope(NULL, 0, 0);
} else {
// We are compiling the function of an invoked closure.
// Restore the outer scope containing all captured variables.
const ContextScope& context_scope =
ContextScope::Handle(Z, func.context_scope());
ASSERT(!context_scope.IsNull());
outer_scope = new (Z)
LocalScope(LocalScope::RestoreOuterScope(context_scope), 0, 0);
}
} else {
// We are parsing a nested function while compiling the enclosing function.
outer_scope = new (Z) LocalScope(
current_block_->scope, current_block_->scope->function_level() + 1, 0);
}
ChainNewBlock(outer_scope);
}
void Parser::OpenAsyncClosure() {
TRACE_PARSER("OpenAsyncClosure");
async_temp_scope_ = current_block_->scope;
OpenAsyncTryBlock();
}
SequenceNode* Parser::CloseAsyncGeneratorTryBlock(SequenceNode* body) {
TRACE_PARSER("CloseAsyncGeneratorTryBlock");
// The generated try-catch-finally that wraps the async generator function
// body is the outermost try statement.
ASSERT(try_stack_ != NULL);
ASSERT(try_stack_->outer_try() == NULL);
// We only get here when parsing an async generator body.
ASSERT(innermost_function().IsAsyncGenClosure());
const TokenPosition try_end_pos = innermost_function().end_token_pos();
// The try-block (closure body code) has been parsed. We are now
// generating the code for the catch block.
LocalScope* try_scope = current_block_->scope;
try_stack_->enter_catch();
OpenBlock(); // Catch handler list.
OpenBlock(); // Catch block.
// Add the exception and stack trace parameters to the scope.
CatchParamDesc exception_param;
CatchParamDesc stack_trace_param;
exception_param.token_pos = TokenPosition::kNoSource;
exception_param.type = &Object::dynamic_type();
exception_param.name = &Symbols::ExceptionParameter();
stack_trace_param.token_pos = TokenPosition::kNoSource;
stack_trace_param.type = &Object::dynamic_type();
stack_trace_param.name = &Symbols::StackTraceParameter();
AddCatchParamsToScope(&exception_param, &stack_trace_param,
current_block_->scope);
// Generate code to save the exception object and stack trace
// in local variables.
LocalVariable* context_var =
try_scope->LocalLookupVariable(Symbols::SavedTryContextVar());
ASSERT(context_var != NULL);
LocalVariable* exception_var =
try_scope->LocalLookupVariable(Symbols::ExceptionVar());
ASSERT(exception_var != NULL);
if (exception_param.var != NULL) {
// Generate code to load the exception object (:exception_var) into
// the exception variable specified in this block.
current_block_->statements->Add(new (Z) StoreLocalNode(
TokenPosition::kNoSource, exception_param.var,
new (Z) LoadLocalNode(TokenPosition::kNoSource, exception_var)));
}
LocalVariable* stack_trace_var =
try_scope->LocalLookupVariable(Symbols::StackTraceVar());
ASSERT(stack_trace_var != NULL);
if (stack_trace_param.var != NULL) {
// A stack trace variable is specified in this block, so generate code
// to load the stack trace object (:stack_trace_var) into the stack
// trace variable specified in this block.
current_block_->statements->Add(new (Z) StoreLocalNode(
TokenPosition::kNoSource, stack_trace_param.var,
new (Z) LoadLocalNode(TokenPosition::kNoSource, stack_trace_var)));
}
LocalVariable* saved_exception_var =
try_scope->LocalLookupVariable(Symbols::SavedExceptionVar());
LocalVariable* saved_stack_trace_var =
try_scope->LocalLookupVariable(Symbols::SavedStackTraceVar());
SaveExceptionAndStackTrace(current_block_->statements, exception_var,
stack_trace_var, saved_exception_var,
saved_stack_trace_var);
// Catch block: add the error to the stream.
// :controller.AddError(:exception, :stack_trace);
// return; // The finally block will close the stream.
LocalVariable* controller =
current_block_->scope->LookupVariable(Symbols::ColonController(), false);
ASSERT(controller != NULL);
ArgumentListNode* args = new (Z) ArgumentListNode(TokenPosition::kNoSource);
args->Add(new (Z)
LoadLocalNode(TokenPosition::kNoSource, exception_param.var));
args->Add(new (Z)
LoadLocalNode(TokenPosition::kNoSource, stack_trace_param.var));
current_block_->statements->Add(new (Z) InstanceCallNode(
try_end_pos, new (Z) LoadLocalNode(TokenPosition::kNoSource, controller),
Symbols::AddError(), args));
ReturnNode* return_node = new (Z) ReturnNode(try_end_pos);
AddNodeForFinallyInlining(return_node);
current_block_->statements->Add(return_node);
AstNode* catch_block = CloseBlock();
current_block_->statements->Add(catch_block);
SequenceNode* catch_handler_list = CloseBlock();
TryStack* try_statement = PopTry();
ASSERT(try_stack_ == NULL); // We popped the outermost try block.
// Finally block: closing the stream and returning. Instead of simply
// returning, create an await state and suspend. There may be outstanding
// calls to schedule the generator body. This suspension ensures that we
// do not repeat any code of the generator body.
// :controller.close();
// suspend;
// We need to inline this code in all recorded exit points.
intptr_t node_index = 0;
SequenceNode* finally_clause = NULL;
if (try_stack_ != NULL) {
try_stack_->enter_finally();
}
do {
OpenBlock();
ArgumentListNode* no_args =
new (Z) ArgumentListNode(TokenPosition::kNoSource);
current_block_->statements->Add(new (Z) InstanceCallNode(
try_end_pos,
new (Z) LoadLocalNode(TokenPosition::kNoSource, controller),
Symbols::Close(), no_args));
// Suspend after the close.
AwaitMarkerNode* await_marker = new (Z) AwaitMarkerNode(
async_temp_scope_, current_block_->scope, TokenPosition::kNoSource);
current_block_->statements->Add(await_marker);
ReturnNode* continuation_ret = new (Z) ReturnNode(try_end_pos);
continuation_ret->set_return_type(ReturnNode::kContinuationTarget);
current_block_->statements->Add(continuation_ret);
finally_clause = CloseBlock();
AstNode* node_to_inline = try_statement->GetNodeToInlineFinally(node_index);
if (node_to_inline != NULL) {
InlinedFinallyNode* node =
new (Z) InlinedFinallyNode(try_end_pos, finally_clause, context_var,
// No outer try statement
CatchClauseNode::kInvalidTryIndex);
finally_clause = NULL;
AddFinallyClauseToNode(true, node_to_inline, node);
node_index++;
}
} while (finally_clause == NULL);
if (try_stack_ != NULL) {
try_stack_->exit_finally();
}
// Catch block handles all exceptions.
const Array& handler_types = Array::ZoneHandle(Z, Array::New(1, Heap::kOld));
handler_types.SetAt(0, Object::dynamic_type());
CatchClauseNode* catch_clause = new (Z) CatchClauseNode(
TokenPosition::kNoSource, catch_handler_list, handler_types, context_var,
exception_var, stack_trace_var, saved_exception_var,
saved_stack_trace_var, AllocateTryIndex(), true);
const intptr_t try_index = try_statement->try_index();
AstNode* try_catch_node = new (Z)
TryCatchNode(TokenPosition::kNoSource, body, context_var, catch_clause,
finally_clause, try_index, finally_clause);
current_block_->statements->Add(try_catch_node);
return CloseBlock();
}
SequenceNode* Parser::CloseAsyncTryBlock(SequenceNode* try_block,
TokenPosition func_end_pos) {
// This is the outermost try-catch of the function.
ASSERT(try_stack_ != NULL);
ASSERT(try_stack_->outer_try() == NULL);
ASSERT(innermost_function().IsAsyncClosure());
LocalScope* try_scope = current_block_->scope;
try_stack_->enter_catch();
OpenBlock(); // Catch handler list.
OpenBlock(); // Catch block.
CatchParamDesc exception_param;
CatchParamDesc stack_trace_param;
exception_param.token_pos = TokenPosition::kNoSource;
exception_param.type = &Object::dynamic_type();
exception_param.name = &Symbols::ExceptionParameter();
stack_trace_param.token_pos = TokenPosition::kNoSource;
stack_trace_param.type = &Object::dynamic_type();
stack_trace_param.name = &Symbols::StackTraceParameter();
AddCatchParamsToScope(&exception_param, &stack_trace_param,
current_block_->scope);
LocalVariable* context_var =
try_scope->LocalLookupVariable(Symbols::SavedTryContextVar());
ASSERT(context_var != NULL);
LocalVariable* exception_var =
try_scope->LocalLookupVariable(Symbols::ExceptionVar());
if (exception_param.var != NULL) {
// Generate code to load the exception object (:exception_var) into
// the exception variable specified in this block.
ASSERT(exception_var != NULL);
current_block_->statements->Add(new (Z) StoreLocalNode(
TokenPosition::kNoSource, exception_param.var,
new (Z) LoadLocalNode(TokenPosition::kNoSource, exception_var)));
}
LocalVariable* stack_trace_var =
try_scope->LocalLookupVariable(Symbols::StackTraceVar());
if (stack_trace_param.var != NULL) {
// A stack trace variable is specified in this block, so generate code
// to load the stack trace object (:stack_trace_var) into the stack
// trace variable specified in this block.
ASSERT(stack_trace_var != NULL);
current_block_->statements->Add(new (Z) StoreLocalNode(
TokenPosition::kNoSource, stack_trace_param.var,
new (Z) LoadLocalNode(TokenPosition::kNoSource, stack_trace_var)));
}
LocalVariable* saved_exception_var =
try_scope->LocalLookupVariable(Symbols::SavedExceptionVar());
LocalVariable* saved_stack_trace_var =
try_scope->LocalLookupVariable(Symbols::SavedStackTraceVar());
SaveExceptionAndStackTrace(current_block_->statements, exception_var,
stack_trace_var, saved_exception_var,
saved_stack_trace_var);
// Complete the async future with an error. This catch block executes
// unconditionally, there is no need to generate a type check for.
LocalVariable* async_completer =
current_block_->scope->LookupVariable(Symbols::AsyncCompleter(), false);
ASSERT(async_completer != NULL);
ArgumentListNode* completer_args =
new (Z) ArgumentListNode(TokenPosition::kNoSource);
completer_args->Add(
new (Z) LoadLocalNode(TokenPosition::kNoSource, exception_param.var));
completer_args->Add(
new (Z) LoadLocalNode(TokenPosition::kNoSource, stack_trace_param.var));
current_block_->statements->Add(new (Z) InstanceCallNode(
TokenPosition::kNoSource,
new (Z) LoadLocalNode(TokenPosition::kNoSource, async_completer),
Symbols::CompleterCompleteError(), completer_args));
ReturnNode* return_node = new (Z) ReturnNode(TokenPosition::kNoSource);
// Behavior like a continuation return, i.e,. don't call a completer.
return_node->set_return_type(ReturnNode::kContinuation);
current_block_->statements->Add(return_node);
AstNode* catch_block = CloseBlock();
current_block_->statements->Add(catch_block);
SequenceNode* catch_handler_list = CloseBlock();
const Array& handler_types = Array::ZoneHandle(Z, Array::New(1, Heap::kOld));
handler_types.SetAt(0, *exception_param.type);
TryStack* try_statement = PopTry();
const intptr_t try_index = try_statement->try_index();
CatchClauseNode* catch_clause = new (Z) CatchClauseNode(
TokenPosition::kNoSource, catch_handler_list, handler_types, context_var,
exception_var, stack_trace_var, saved_exception_var,
saved_stack_trace_var, CatchClauseNode::kInvalidTryIndex, true);
AstNode* try_catch_node = new (Z) TryCatchNode(
TokenPosition::kNoSource, try_block, context_var, catch_clause,
NULL, // No finally clause.
try_index,
NULL); // No rethrow-finally clause.
current_block_->statements->Add(try_catch_node);
return CloseBlock();
}
// Wrap the body of the async or async* closure in a try/catch block.
void Parser::OpenAsyncTryBlock() {
ASSERT(innermost_function().IsAsyncClosure() ||
innermost_function().IsAsyncGenClosure());
LocalVariable* context_var = NULL;
LocalVariable* exception_var = NULL;
LocalVariable* stack_trace_var = NULL;
LocalVariable* saved_exception_var = NULL;
LocalVariable* saved_stack_trace_var = NULL;
SetupExceptionVariables(current_block_->scope, true, &context_var,
&exception_var, &stack_trace_var,
&saved_exception_var, &saved_stack_trace_var);
// Open the try block.
OpenBlock();
// This is the outermost try-catch in the function.
ASSERT(try_stack_ == NULL);
PushTry(current_block_);
// Validate that we always get try index of 0.
ASSERT(try_stack_->try_index() == CatchClauseNode::kImplicitAsyncTryIndex);
SetupSavedTryContext(context_var);
}
void Parser::AddSyncGenClosureParameters(ParamList* params) {
// Create the parameter list for the body closure of a sync generator:
// 1) Implicit closure parameter;
// 2) Iterator
// Add implicit closure parameter if not already present.
if (params->parameters->length() == 0) {
params->AddFinalParameter(TokenPosition::kMinSource,
&Symbols::ClosureParameter(),
&Object::dynamic_type());
}
ParamDesc iterator_param;
iterator_param.name = &Symbols::IteratorParameter();
iterator_param.type = &Object::dynamic_type();
params->parameters->Add(iterator_param);
params->num_fixed_parameters++;
}
void Parser::AddAsyncGenClosureParameters(ParamList* params) {
// Create the parameter list for the body closure of an async generator.
// The closure has the same parameters as an asynchronous non-generator.
AddAsyncClosureParameters(params);
}
RawFunction* Parser::OpenSyncGeneratorFunction(TokenPosition func_pos) {
Function& body = Function::Handle(Z);
String& body_closure_name = String::Handle(Z);
bool is_new_closure = false;
AddContinuationVariables();
// Check whether a function for the body of this generator
// function has already been created by a previous
// compilation.
const Function& found_func = Function::Handle(
Z, I->LookupClosureFunction(innermost_function(), func_pos));
if (!found_func.IsNull()) {
ASSERT(found_func.IsSyncGenClosure());
body = found_func.raw();
body_closure_name = body.name();
} else {
// Create the closure containing the body of this generator function.
String& generator_name = String::Handle(Z, innermost_function().name());
body_closure_name =
Symbols::NewFormatted(T, "<%s_sync_body>", generator_name.ToCString());
body = Function::NewClosureFunction(body_closure_name, innermost_function(),
func_pos);
body.set_is_generated_body(true);
body.set_result_type(Object::dynamic_type());
is_new_closure = true;
}
ParamList closure_params;
AddSyncGenClosureParameters(&closure_params);
if (is_new_closure) {
// Add the parameters to the newly created closure.
AddFormalParamsToFunction(&closure_params, body);
ResolveSignatureTypeParameters(body);
// Finalize function type.
Type& signature_type = Type::Handle(Z, body.SignatureType());
signature_type ^= CanonicalizeType(signature_type);
body.SetSignatureType(signature_type);
ASSERT(AbstractType::Handle(Z, body.result_type()).IsResolved());
ASSERT(body.NumParameters() == closure_params.parameters->length());
}
OpenFunctionBlock(body);
AddFormalParamsToScope(&closure_params, current_block_->scope);
async_temp_scope_ = current_block_->scope;
return body.raw();
}
SequenceNode* Parser::CloseSyncGenFunction(const Function& closure,
SequenceNode* closure_body) {
// Explicitly reference variables of the sync generator function from the
// closure body in order to mark them as captured.
LocalVariable* existing_var =
closure_body->scope()->LookupVariable(Symbols::AwaitJumpVar(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
existing_var =
closure_body->scope()->LookupVariable(Symbols::AwaitContextVar(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
// :await_jump_var = -1;
LocalVariable* jump_var =
current_block_->scope->LookupVariable(Symbols::AwaitJumpVar(), false);
LiteralNode* init_value = new (Z)
LiteralNode(TokenPosition::kNoSource, Smi::ZoneHandle(Smi::New(-1)));
current_block_->statements->Add(
new (Z) StoreLocalNode(TokenPosition::kNoSource, jump_var, init_value));
// return new SyncIterable(body_closure);
const Class& iterable_class =
Class::Handle(Z, Library::LookupCoreClass(Symbols::_SyncIterable()));
ASSERT(!iterable_class.IsNull());
const Function& iterable_constructor =
Function::ZoneHandle(Z, iterable_class.LookupConstructorAllowPrivate(
Symbols::_SyncIterableConstructor()));
ASSERT(!iterable_constructor.IsNull());
const String& closure_name = String::Handle(Z, closure.name());
ASSERT(closure_name.IsSymbol());
ArgumentListNode* arguments =
new (Z) ArgumentListNode(TokenPosition::kNoSource);
ClosureNode* closure_obj = new (Z) ClosureNode(
TokenPosition::kNoSource, closure, NULL, closure_body->scope());
arguments->Add(closure_obj);
ConstructorCallNode* new_iterable = new (Z) ConstructorCallNode(
TokenPosition::kNoSource, TypeArguments::ZoneHandle(Z),
iterable_constructor, arguments);
ReturnNode* return_node =
new (Z) ReturnNode(TokenPosition::kNoSource, new_iterable);
current_block_->statements->Add(return_node);
return CloseBlock();
}
void Parser::AddAsyncClosureParameters(ParamList* params) {
// Async closures have three optional parameters:
// * A continuation result.
// * A continuation error.
// * A continuation stack trace.
ASSERT(params->parameters->length() <= 1);
// Add implicit closure parameter if not yet present.
if (params->parameters->length() == 0) {
params->AddFinalParameter(TokenPosition::kMinSource,
&Symbols::ClosureParameter(),
&Object::dynamic_type());
}
ParamDesc result_param;
result_param.name = &Symbols::AsyncOperationParam();
result_param.default_value = &Object::null_instance();
result_param.type = &Object::dynamic_type();
params->parameters->Add(result_param);
ParamDesc error_param;
error_param.name = &Symbols::AsyncOperationErrorParam();
error_param.default_value = &Object::null_instance();
error_param.type = &Object::dynamic_type();
params->parameters->Add(error_param);
ParamDesc stack_trace_param;
stack_trace_param.name = &Symbols::AsyncOperationStackTraceParam();
stack_trace_param.default_value = &Object::null_instance();
stack_trace_param.type = &Object::dynamic_type();
params->parameters->Add(stack_trace_param);
params->has_optional_positional_parameters = true;
params->num_optional_parameters += 3;
}
RawFunction* Parser::OpenAsyncFunction(TokenPosition async_func_pos) {
TRACE_PARSER("OpenAsyncFunction");
AddContinuationVariables();
AddAsyncClosureVariables();
Function& closure = Function::Handle(Z);
bool is_new_closure = false;
// Check whether a function for the asynchronous function body of
// this async function has already been created by a previous
// compilation of this function.
const Function& found_func = Function::Handle(
Z, I->LookupClosureFunction(innermost_function(), async_func_pos));
if (!found_func.IsNull()) {
ASSERT(found_func.IsAsyncClosure());
closure = found_func.raw();
} else {
// Create the closure containing the body of this async function.
const String& async_func_name =
String::Handle(Z, innermost_function().name());
String& closure_name =
String::Handle(Z, Symbols::NewFormatted(T, "<%s_async_body>",
async_func_name.ToCString()));
closure = Function::NewClosureFunction(closure_name, innermost_function(),
async_func_pos);
closure.set_is_generated_body(true);
closure.set_result_type(Object::dynamic_type());
is_new_closure = true;
}
// Create the parameter list for the async body closure.
ParamList closure_params;
AddAsyncClosureParameters(&closure_params);
if (is_new_closure) {
// Add the parameters to the newly created closure.
AddFormalParamsToFunction(&closure_params, closure);
ResolveSignatureTypeParameters(closure);
// Finalize function type.
Type& signature_type = Type::Handle(Z, closure.SignatureType());
signature_type ^= CanonicalizeType(signature_type);
closure.SetSignatureType(signature_type);
ASSERT(AbstractType::Handle(Z, closure.result_type()).IsResolved());
ASSERT(closure.NumParameters() == closure_params.parameters->length());
}
OpenFunctionBlock(closure);
AddFormalParamsToScope(&closure_params, current_block_->scope);
async_temp_scope_ = current_block_->scope;
return closure.raw();
}
void Parser::AddContinuationVariables() {
// Add to current block's scope:
// var :await_jump_var;
// var :await_ctx_var;
LocalVariable* await_jump_var =
new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::AwaitJumpVar(), Object::dynamic_type());
current_block_->scope->AddVariable(await_jump_var);
LocalVariable* await_ctx_var =
new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::AwaitContextVar(), Object::dynamic_type());
current_block_->scope->AddVariable(await_ctx_var);
}
void Parser::AddAsyncClosureVariables() {
// Add to current block's scope:
// var :async_op;
// var :async_then_callback;
// var :async_catch_error_callback;
// var :async_completer;
// var :async_stack_trace;
LocalVariable* async_op_var =
new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::AsyncOperation(), Object::dynamic_type());
current_block_->scope->AddVariable(async_op_var);
LocalVariable* async_then_callback_var = new (Z)
LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::AsyncThenCallback(), Object::dynamic_type());
current_block_->scope->AddVariable(async_then_callback_var);
LocalVariable* async_catch_error_callback_var = new (Z)
LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::AsyncCatchErrorCallback(), Object::dynamic_type());
current_block_->scope->AddVariable(async_catch_error_callback_var);
LocalVariable* async_completer =
new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::AsyncCompleter(), Object::dynamic_type());
current_block_->scope->AddVariable(async_completer);
LocalVariable* async_stack_trace = new (Z)
LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::AsyncStackTraceVar(), Object::dynamic_type());
current_block_->scope->AddVariable(async_stack_trace);
}
void Parser::AddAsyncGeneratorVariables() {
// Add to current block's scope:
// var :controller;
// The :controller variable is used by the async generator closure to
// store the StreamController object to which the yielded expressions
// are added.
// var :async_op;
// var :async_then_callback;
// var :async_catch_error_callback;
// var :async_stack_trace;
// var :controller_stream;
// These variables are used to store the async generator closure containing
// the body of the async* function. They are used by the await operator.
LocalVariable* controller_var =
new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::ColonController(), Object::dynamic_type());
current_block_->scope->AddVariable(controller_var);
LocalVariable* async_op_var =
new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::AsyncOperation(), Object::dynamic_type());
current_block_->scope->AddVariable(async_op_var);
LocalVariable* async_then_callback_var = new (Z)
LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::AsyncThenCallback(), Object::dynamic_type());
current_block_->scope->AddVariable(async_then_callback_var);
LocalVariable* async_catch_error_callback_var = new (Z)
LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::AsyncCatchErrorCallback(), Object::dynamic_type());
current_block_->scope->AddVariable(async_catch_error_callback_var);
LocalVariable* async_stack_trace = new (Z)
LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::AsyncStackTraceVar(), Object::dynamic_type());
current_block_->scope->AddVariable(async_stack_trace);
LocalVariable* controller_stream = new (Z)
LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::ControllerStream(), Object::dynamic_type());
current_block_->scope->AddVariable(controller_stream);
}
RawFunction* Parser::OpenAsyncGeneratorFunction(TokenPosition async_func_pos) {
TRACE_PARSER("OpenAsyncGeneratorFunction");
AddContinuationVariables();
AddAsyncGeneratorVariables();
Function& closure = Function::Handle(Z);
bool is_new_closure = false;
// Check whether a function for the asynchronous function body of
// this async generator has already been created by a previous
// compilation of this function.
const Function& found_func = Function::Handle(
Z, I->LookupClosureFunction(innermost_function(), async_func_pos));
if (!found_func.IsNull()) {
ASSERT(found_func.IsAsyncGenClosure());
closure = found_func.raw();
} else {
// Create the closure containing the body of this async generator function.
const String& async_generator_name =
String::Handle(Z, innermost_function().name());
const String& closure_name = String::Handle(
Z, Symbols::NewFormatted(T, "<%s_async_gen_body>",
async_generator_name.ToCString()));
closure = Function::NewClosureFunction(closure_name, innermost_function(),
async_func_pos);
closure.set_is_generated_body(true);
closure.set_result_type(Object::dynamic_type());
is_new_closure = true;
}
ParamList closure_params;
AddAsyncGenClosureParameters(&closure_params);
if (is_new_closure) {
// Add the parameters to the newly created closure.
AddFormalParamsToFunction(&closure_params, closure);
ResolveSignatureTypeParameters(closure);
// Finalize function type.
Type& signature_type = Type::Handle(Z, closure.SignatureType());
signature_type ^= CanonicalizeType(signature_type);
closure.SetSignatureType(signature_type);
ASSERT(AbstractType::Handle(Z, closure.result_type()).IsResolved());
ASSERT(closure.NumParameters() == closure_params.parameters->length());
}
OpenFunctionBlock(closure);
AddFormalParamsToScope(&closure_params, current_block_->scope);
async_temp_scope_ = current_block_->scope;
return closure.raw();
}
// Generate the Ast nodes for the implicit code of the async* function.
//
// f(...) async* {
// var :controller;
// var :await_jump_var = -1;
// var :await_context_var;
// f_async_body() {
// ... source code of f ...
// }
// var :async_op = f_async_body;
// var :async_then_callback = _asyncThenWrapperHelper(:async_op);
// var :async_catch_error_callback = _asyncCatchErrorWrapperHelper(:async_op);
// :controller = new _AsyncStarStreamController(:async_op);
// var :controller_stream = :controller.stream;
// return :controller_stream;
// }
SequenceNode* Parser::CloseAsyncGeneratorFunction(const Function& closure_func,
SequenceNode* closure_body) {
TRACE_PARSER("CloseAsyncGeneratorFunction");
ASSERT(!closure_func.IsNull());
ASSERT(closure_body != NULL);
// Explicitly reference variables of the async generator function from the
// closure body in order to mark them as captured.
LocalVariable* existing_var =
closure_body->scope()->LookupVariable(Symbols::AwaitJumpVar(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
existing_var =
closure_body->scope()->LookupVariable(Symbols::AwaitContextVar(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
existing_var =
closure_body->scope()->LookupVariable(Symbols::ColonController(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
existing_var =
closure_body->scope()->LookupVariable(Symbols::AsyncOperation(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
existing_var = closure_body->scope()->LookupVariable(
Symbols::AsyncThenCallback(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
existing_var = closure_body->scope()->LookupVariable(
Symbols::AsyncCatchErrorCallback(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
existing_var = closure_body->scope()->LookupVariable(
Symbols::AsyncStackTraceVar(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
existing_var =
closure_body->scope()->LookupVariable(Symbols::ControllerStream(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
const Library& async_lib = Library::Handle(Library::AsyncLibrary());
const Class& controller_class = Class::Handle(
Z,
async_lib.LookupClassAllowPrivate(Symbols::_AsyncStarStreamController()));
ASSERT(!controller_class.IsNull());
const Function& controller_constructor = Function::ZoneHandle(
Z, controller_class.LookupConstructorAllowPrivate(
Symbols::_AsyncStarStreamControllerConstructor()));
// :await_jump_var = -1;
LocalVariable* jump_var =
current_block_->scope->LookupVariable(Symbols::AwaitJumpVar(), false);
LiteralNode* init_value = new (Z)
LiteralNode(TokenPosition::kNoSource, Smi::ZoneHandle(Smi::New(-1)));
current_block_->statements->Add(
new (Z) StoreLocalNode(TokenPosition::kNoSource, jump_var, init_value));
TokenPosition token_pos = TokenPosition::kNoSource;
// Add to AST:
// :async_op = <closure>; (containing the original body)
LocalVariable* async_op_var =
current_block_->scope->LookupVariable(Symbols::AsyncOperation(), false);
ClosureNode* closure_obj = new (Z) ClosureNode(
TokenPosition::kNoSource, closure_func, NULL, closure_body->scope());
StoreLocalNode* store_async_op = new (Z)
StoreLocalNode(TokenPosition::kNoSource, async_op_var, closure_obj);
current_block_->statements->Add(store_async_op);
if (FLAG_causal_async_stacks) {
// Add to AST:
// :async_stack_trace = _asyncStackTraceHelper(:async_op);
const Function& async_stack_trace_helper = Function::ZoneHandle(
Z,
async_lib.LookupFunctionAllowPrivate(Symbols::AsyncStackTraceHelper()));
ASSERT(!async_stack_trace_helper.IsNull());
ArgumentListNode* async_stack_trace_helper_args =
new (Z) ArgumentListNode(TokenPosition::kNoSource);
async_stack_trace_helper_args->Add(
new (Z) LoadLocalNode(TokenPosition::kNoSource, async_op_var));
StaticCallNode* async_stack_trace_helper_call = new (Z)
StaticCallNode(token_pos, async_stack_trace_helper,
async_stack_trace_helper_args, StaticCallNode::kStatic);
LocalVariable* async_stack_trace_var =
current_block_->scope->LookupVariable(Symbols::AsyncStackTraceVar(),
false);
StoreLocalNode* store_async_stack_trace = new (Z) StoreLocalNode(
token_pos, async_stack_trace_var, async_stack_trace_helper_call);
current_block_->statements->Add(store_async_stack_trace);
}
// :async_then_callback = _asyncThenWrapperHelper(:async_op)
const Function& async_then_wrapper_helper = Function::ZoneHandle(
Z,
async_lib.LookupFunctionAllowPrivate(Symbols::AsyncThenWrapperHelper()));
ASSERT(!async_then_wrapper_helper.IsNull());
ArgumentListNode* async_then_wrapper_helper_args =
new (Z) ArgumentListNode(TokenPosition::kNoSource);
async_then_wrapper_helper_args->Add(
new (Z) LoadLocalNode(TokenPosition::kNoSource, async_op_var));
StaticCallNode* then_wrapper_call = new (Z)
StaticCallNode(TokenPosition::kNoSource, async_then_wrapper_helper,
async_then_wrapper_helper_args, StaticCallNode::kStatic);
LocalVariable* async_then_callback_var =
current_block_->scope->LookupVariable(Symbols::AsyncThenCallback(),
false);
StoreLocalNode* store_async_then_callback = new (Z) StoreLocalNode(
TokenPosition::kNoSource, async_then_callback_var, then_wrapper_call);
current_block_->statements->Add(store_async_then_callback);
// :async_catch_error_callback = _asyncErrorWrapperHelper(:async_op)
const Function& async_error_wrapper_helper = Function::ZoneHandle(
Z,
async_lib.LookupFunctionAllowPrivate(Symbols::AsyncErrorWrapperHelper()));
ASSERT(!async_error_wrapper_helper.IsNull());
ArgumentListNode* async_error_wrapper_helper_args =
new (Z) ArgumentListNode(TokenPosition::kNoSource);
async_error_wrapper_helper_args->Add(
new (Z) LoadLocalNode(TokenPosition::kNoSource, async_op_var));
StaticCallNode* error_wrapper_call = new (Z)
StaticCallNode(TokenPosition::kNoSource, async_error_wrapper_helper,
async_error_wrapper_helper_args, StaticCallNode::kStatic);
LocalVariable* async_catch_error_callback_var =
current_block_->scope->LookupVariable(Symbols::AsyncCatchErrorCallback(),
false);
StoreLocalNode* store_async_catch_error_callback = new (Z)
StoreLocalNode(TokenPosition::kNoSource, async_catch_error_callback_var,
error_wrapper_call);
current_block_->statements->Add(store_async_catch_error_callback);
// :controller = new _AsyncStarStreamController(body_closure);
ArgumentListNode* arguments =
new (Z) ArgumentListNode(TokenPosition::kNoSource);
arguments->Add(new (Z) LoadLocalNode(TokenPosition::kNoSource, async_op_var));
ConstructorCallNode* controller_constructor_call =
new (Z) ConstructorCallNode(TokenPosition::kNoSource,
TypeArguments::ZoneHandle(Z),
controller_constructor, arguments);
LocalVariable* controller_var =
current_block_->scope->LookupVariable(Symbols::ColonController(), false);
StoreLocalNode* store_controller = new (Z) StoreLocalNode(
TokenPosition::kNoSource, controller_var, controller_constructor_call);
current_block_->statements->Add(store_controller);
// Grab :controller.stream
InstanceGetterNode* controller_stream = new (Z) InstanceGetterNode(
TokenPosition::kNoSource,
new (Z) LoadLocalNode(TokenPosition::kNoSource, controller_var),
Symbols::Stream());
// Store :controller.stream into :controller_stream inside the closure.
// We have to remember the stream because a new instance is generated for
// each getter invocation and in order to recreate the linkage, we need the
// awaited on instance.
LocalVariable* controller_stream_var =
current_block_->scope->LookupVariable(Symbols::ControllerStream(), false);
ASSERT(controller_stream_var != NULL);
StoreLocalNode* store_controller_stream = new (Z) StoreLocalNode(
TokenPosition::kNoSource, controller_stream_var, controller_stream);
current_block_->statements->Add(store_controller_stream);
// return :controller.stream;
ReturnNode* return_node = new (Z) ReturnNode(
TokenPosition::kNoSource,
new (Z) LoadLocalNode(TokenPosition::kNoSource, controller_stream_var));
current_block_->statements->Add(return_node);
return CloseBlock();
}
void Parser::OpenAsyncGeneratorClosure() {
async_temp_scope_ = current_block_->scope;
OpenAsyncTryBlock();
}
SequenceNode* Parser::CloseAsyncGeneratorClosure(SequenceNode* body) {
// We need a temporary expression to store intermediate return values.
parsed_function()->EnsureExpressionTemp();
SequenceNode* new_body = CloseAsyncGeneratorTryBlock(body);
ASSERT(new_body != NULL);
ASSERT(new_body->scope() != NULL);
return new_body;
}
// Add a return node to the sequence if necessary.
void Parser::EnsureHasReturnStatement(SequenceNode* seq,
TokenPosition return_pos) {
if ((seq->length() == 0) || !seq->NodeAt(seq->length() - 1)->IsReturnNode()) {
const Function& func = innermost_function();
// The implicit return value of synchronous generator closures is false,
// to indicate that there are no more elements in the iterable.
// In other cases the implicit return value is null.
AstNode* return_value =
func.IsSyncGenClosure()
? new LiteralNode(return_pos, Bool::False())
: new LiteralNode(return_pos, Instance::ZoneHandle());
seq->Add(new ReturnNode(return_pos, return_value));
}
}
SequenceNode* Parser::CloseBlock() {
SequenceNode* statements = current_block_->statements;
if (current_block_->scope != NULL) {
// Record the begin and end token index of the scope.
ASSERT(statements != NULL);
current_block_->scope->set_begin_token_pos(statements->token_pos());
current_block_->scope->set_end_token_pos(TokenPos());
}
current_block_ = current_block_->parent;
return statements;
}
SequenceNode* Parser::CloseAsyncFunction(const Function& closure,
SequenceNode* closure_body) {
TRACE_PARSER("CloseAsyncFunction");
ASSERT(!closure.IsNull());
ASSERT(closure_body != NULL);
// Explicitly reference variables of the async function from the
// closure body in order to mark them as captured.
LocalVariable* existing_var =
closure_body->scope()->LookupVariable(Symbols::AwaitJumpVar(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
existing_var =
closure_body->scope()->LookupVariable(Symbols::AwaitContextVar(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
existing_var =
closure_body->scope()->LookupVariable(Symbols::AsyncCompleter(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
existing_var = closure_body->scope()->LookupVariable(
Symbols::AsyncStackTraceVar(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
// Create a completer that executes a closure with the current body and
// return the corresponding future.
// No need to capture parameters or other variables, since they have already
// been captured in the corresponding scope as the body has been parsed within
// a nested block (contained in the async function's block).
const Library& async_lib = Library::Handle(Z, Library::AsyncLibrary());
LocalVariable* async_completer =
current_block_->scope->LookupVariable(Symbols::AsyncCompleter(), false);
const TokenPosition token_pos = ST(closure_body->token_pos());
Function& completer_constructor = Function::ZoneHandle(Z);
if (FLAG_sync_async) {
const Class& completer_class = Class::Handle(
Z, async_lib.LookupClassAllowPrivate(Symbols::_AsyncAwaitCompleter()));
ASSERT(!completer_class.IsNull());
completer_constructor = completer_class.LookupConstructorAllowPrivate(
Symbols::_AsyncAwaitCompleterConstructor());
ASSERT(!completer_constructor.IsNull());
// Add to AST:
// :async_completer = new _AsyncAwaitCompleter();
} else {
const Class& completer =
Class::Handle(Z, I->object_store()->completer_class());
ASSERT(!completer.IsNull());
completer_constructor =
completer.LookupFunction(Symbols::CompleterSyncConstructor());
ASSERT(!completer_constructor.IsNull());
// Add to AST:
// :async_completer = new Completer.sync();
}
ArgumentListNode* empty_args = new (Z) ArgumentListNode(token_pos);
ConstructorCallNode* completer_constructor_node =
new (Z) ConstructorCallNode(token_pos, TypeArguments::ZoneHandle(Z),
completer_constructor, empty_args);
StoreLocalNode* store_completer = new (Z)
StoreLocalNode(token_pos, async_completer, completer_constructor_node);
current_block_->statements->Add(store_completer);
// :await_jump_var = -1;
LocalVariable* jump_var =
current_block_->scope->LookupVariable(Symbols::AwaitJumpVar(), false);
LiteralNode* init_value =
new (Z) LiteralNode(token_pos, Smi::ZoneHandle(Smi::New(-1)));
current_block_->statements->Add(
new (Z) StoreLocalNode(token_pos, jump_var, init_value));
// Add to AST:
// :async_op = <closure>; (containing the original body)
LocalVariable* async_op_var =
current_block_->scope->LookupVariable(Symbols::AsyncOperation(), false);
ClosureNode* cn =
new (Z) ClosureNode(token_pos, closure, NULL, closure_body->scope());
StoreLocalNode* store_async_op =
new (Z) StoreLocalNode(token_pos, async_op_var, cn);
current_block_->statements->Add(store_async_op);
if (FLAG_causal_async_stacks) {
// Add to AST:
// :async_stack_trace = _asyncStackTraceHelper();
const Function& async_stack_trace_helper = Function::ZoneHandle(
Z,
async_lib.LookupFunctionAllowPrivate(Symbols::AsyncStackTraceHelper()));
ASSERT(!async_stack_trace_helper.IsNull());
ArgumentListNode* async_stack_trace_helper_args =
new (Z) ArgumentListNode(token_pos);
async_stack_trace_helper_args->Add(
new (Z) LoadLocalNode(token_pos, async_op_var));
StaticCallNode* async_stack_trace_helper_call = new (Z)
StaticCallNode(token_pos, async_stack_trace_helper,
async_stack_trace_helper_args, StaticCallNode::kStatic);
LocalVariable* async_stack_trace_var =
current_block_->scope->LookupVariable(Symbols::AsyncStackTraceVar(),
false);
StoreLocalNode* store_async_stack_trace = new (Z) StoreLocalNode(
token_pos, async_stack_trace_var, async_stack_trace_helper_call);
current_block_->statements->Add(store_async_stack_trace);
}
// :async_then_callback = _asyncThenWrapperHelper(:async_op)
const Function& async_then_wrapper_helper = Function::ZoneHandle(
Z,
async_lib.LookupFunctionAllowPrivate(Symbols::AsyncThenWrapperHelper()));
ASSERT(!async_then_wrapper_helper.IsNull());
ArgumentListNode* async_then_wrapper_helper_args =
new (Z) ArgumentListNode(token_pos);
async_then_wrapper_helper_args->Add(
new (Z) LoadLocalNode(token_pos, async_op_var));
StaticCallNode* then_wrapper_call = new (Z)
StaticCallNode(token_pos, async_then_wrapper_helper,
async_then_wrapper_helper_args, StaticCallNode::kStatic);
LocalVariable* async_then_callback_var =
current_block_->scope->LookupVariable(Symbols::AsyncThenCallback(),
false);
StoreLocalNode* store_async_then_callback = new (Z)
StoreLocalNode(token_pos, async_then_callback_var, then_wrapper_call);
current_block_->statements->Add(store_async_then_callback);
// :async_catch_error_callback = _asyncErrorWrapperHelper(:async_op)
const Function& async_error_wrapper_helper = Function::ZoneHandle(
Z,
async_lib.LookupFunctionAllowPrivate(Symbols::AsyncErrorWrapperHelper()));
ASSERT(!async_error_wrapper_helper.IsNull());
ArgumentListNode* async_error_wrapper_helper_args =
new (Z) ArgumentListNode(token_pos);
async_error_wrapper_helper_args->Add(
new (Z) LoadLocalNode(token_pos, async_op_var));
StaticCallNode* error_wrapper_call = new (Z)
StaticCallNode(token_pos, async_error_wrapper_helper,
async_error_wrapper_helper_args, StaticCallNode::kStatic);
LocalVariable* async_catch_error_callback_var =
current_block_->scope->LookupVariable(Symbols::AsyncCatchErrorCallback(),
false);
StoreLocalNode* store_async_catch_error_callback = new (Z) StoreLocalNode(
token_pos, async_catch_error_callback_var, error_wrapper_call);
current_block_->statements->Add(store_async_catch_error_callback);
if (FLAG_sync_async) {
// Add to AST:
// :async_completer.start(:async_op);
ArgumentListNode* arguments = new (Z) ArgumentListNode(token_pos);
arguments->Add(new (Z) LoadLocalNode(token_pos, async_op_var));
InstanceCallNode* start_call = new (Z) InstanceCallNode(
token_pos, new (Z) LoadLocalNode(token_pos, async_completer),
Symbols::_AsyncAwaitStart(), arguments);
current_block_->statements->Add(start_call);
} else {
// Add to AST:
// new Future.microtask(:async_op);
const Class& future = Class::Handle(Z, I->object_store()->future_class());
ASSERT(!future.IsNull());
const Function& constructor = Function::ZoneHandle(
Z, future.LookupFunction(Symbols::FutureMicrotask()));
ASSERT(!constructor.IsNull());
ArgumentListNode* arguments = new (Z) ArgumentListNode(token_pos);
arguments->Add(new (Z) LoadLocalNode(token_pos, async_op_var));
ConstructorCallNode* future_node = new (Z) ConstructorCallNode(
token_pos, TypeArguments::ZoneHandle(Z), constructor, arguments);
current_block_->statements->Add(future_node);
}
// Add to AST:
// return :async_completer.future;
ReturnNode* return_node = new (Z) ReturnNode(
token_pos,
new (Z) InstanceGetterNode(
token_pos, new (Z) LoadLocalNode(token_pos, async_completer),
Symbols::CompleterFuture()));
current_block_->statements->Add(return_node);
return CloseBlock();
}
SequenceNode* Parser::CloseAsyncClosure(SequenceNode* body,
TokenPosition func_end_pos) {
// We need a temporary expression to store intermediate return values.
parsed_function()->EnsureExpressionTemp();
SequenceNode* new_body = CloseAsyncTryBlock(body, func_end_pos);
ASSERT(new_body != NULL);
ASSERT(new_body->scope() != NULL);
return new_body;
}
// Set up default values for all optional parameters to the function.
void Parser::SetupDefaultsForOptionalParams(const ParamList& params) {
if ((current_function().raw() == innermost_function().raw()) &&
(params.num_optional_parameters > 0)) {
ZoneGrowableArray<const Instance*>* default_values =
new ZoneGrowableArray<const Instance*>(zone(),
params.num_optional_parameters);
// Build array of default parameter values.
const ZoneGrowableArray<ParamDesc>& parameters = *params.parameters;
const int first_opt_param_offset = params.num_fixed_parameters;
for (int i = 0; i < params.num_optional_parameters; i++) {
const Instance* default_value =
parameters[i + first_opt_param_offset].default_value;
default_values->Add(default_value);
}
parsed_function()->set_default_parameter_values(default_values);
}
}
void Parser::FinalizeFormalParameterTypes(const ParamList* params) {
ASSERT((params != NULL) && (params->parameters != NULL));
const int num_parameters = params->parameters->length();
AbstractType& type = AbstractType::Handle(Z);
for (int i = 0; i < num_parameters; i++) {
ParamDesc& param_desc = (*params->parameters)[i];
type = param_desc.type->raw();
ResolveTypeParameters(&type);
type = CanonicalizeType(type);
if (type.raw() != param_desc.type->raw()) {
param_desc.type = &AbstractType::ZoneHandle(Z, type.raw());
}
}
}
// Populate the parameter type array and parameter name array of the function
// with the formal parameter types and names.
void Parser::AddFormalParamsToFunction(const ParamList* params,
const Function& func) {
Isolate* isolate = Isolate::Current();
ASSERT((params != NULL) && (params->parameters != NULL));
ASSERT((params->num_optional_parameters > 0) ==
(params->has_optional_positional_parameters ||
params->has_optional_named_parameters));
if (!Utils::IsUint(RawFunction::kMaxFixedParametersBits,
params->num_fixed_parameters) ||
!Utils::IsUint(RawFunction::kMaxOptionalParametersBits,
params->num_optional_parameters)) {
const Script& script = Script::Handle(Class::Handle(func.Owner()).script());
Report::MessageF(Report::kError, script, func.token_pos(),
Report::AtLocation, "too many formal parameters");
}
func.set_num_fixed_parameters(params->num_fixed_parameters);
func.SetNumOptionalParameters(params->num_optional_parameters,
params->has_optional_positional_parameters);
const int num_parameters = params->parameters->length();
ASSERT(num_parameters == func.NumParameters());
ASSERT(func.parameter_types() == Object::empty_array().raw());
ASSERT(func.parameter_names() == Object::empty_array().raw());
func.set_parameter_types(
Array::Handle(Array::New(num_parameters, Heap::kOld)));
func.set_parameter_names(
Array::Handle(Array::New(num_parameters, Heap::kOld)));
AbstractType& param_type = AbstractType::Handle();
for (int i = 0; i < num_parameters; i++) {
ParamDesc& param_desc = (*params->parameters)[i];
param_type = param_desc.type->raw();
if (param_desc.is_covariant) {
if (!func.IsDynamicFunction(true)) {
ReportError(param_desc.name_pos,
"only instance functions may have "
"covariant parameters");
}
// In non-strong mode, the covariant keyword is ignored. In strong mode,
// the parameter type is changed to Object.
if (isolate->strong()) {
param_type = Type::ObjectType();
}
}
func.SetParameterTypeAt(i, param_type);
func.SetParameterNameAt(i, *param_desc.name);
if (param_desc.is_field_initializer && !func.IsGenerativeConstructor()) {
// Redirecting constructors are detected later in ParseConstructor.
ReportError(param_desc.name_pos,
"only generative constructors may have "
"initializing formal parameters");
}
}
}
// Populate local scope with the formal parameters.
void Parser::AddFormalParamsToScope(const ParamList* params,
LocalScope* scope) {
ASSERT((params != NULL) && (params->parameters != NULL));
ASSERT(scope != NULL);
const int num_parameters = params->parameters->length();
for (int i = 0; i < num_parameters; i++) {
ParamDesc& param_desc = (*params->parameters)[i];
const String* name = param_desc.name;
LocalVariable* parameter = new (Z) LocalVariable(
param_desc.name_pos, param_desc.name_pos, *name, *param_desc.type);
if (!scope->InsertParameterAt(i, parameter)) {
ReportError(param_desc.name_pos, "name '%s' already exists in scope",
param_desc.name->ToCString());
}
param_desc.var = parameter;
if (param_desc.is_final) {
parameter->set_is_final();
}
// Field initializer parameters are implicitly final.
ASSERT(!param_desc.is_field_initializer || param_desc.is_final);
}
}
// Builds ReturnNode/NativeBodyNode for a native function.
void Parser::ParseNativeFunctionBlock(const ParamList* params,
const Function& func) {
ASSERT(func.is_native());
ASSERT(func.NumParameters() == params->parameters->length());
TRACE_PARSER("ParseNativeFunctionBlock");
// Parse the function name out.
const String& native_name = ParseNativeDeclaration();
// Now add the NativeBodyNode and return statement.
current_block_->statements->Add(new (Z) ReturnNode(
TokenPos(),
new (Z) NativeBodyNode(TokenPos(), Function::ZoneHandle(Z, func.raw()),
native_name, current_block_->scope,
FLAG_link_natives_lazily)));
}
LocalVariable* Parser::LookupReceiver(LocalScope* from_scope, bool test_only) {
ASSERT(!current_function().is_static());
return from_scope->LookupVariable(Symbols::This(), test_only);
}
LocalVariable* Parser::LookupTypeArgumentsParameter(LocalScope* from_scope,
bool test_only) {
ASSERT(current_function().IsInFactoryScope());
return from_scope->LookupVariable(Symbols::TypeArgumentsParameter(),
test_only);
}
void Parser::CaptureInstantiator() {
ASSERT(FunctionLevel() > 0);
const String* variable_name = current_function().IsInFactoryScope()
? &Symbols::TypeArgumentsParameter()
: &Symbols::This();
current_block_->scope->CaptureVariable(
current_block_->scope->LookupVariable(*variable_name, true));
}
void Parser::CaptureFunctionTypeArguments() {
ASSERT(InGenericFunctionScope());
ASSERT(FunctionLevel() > 0);
if (!Isolate::Current()->reify_generic_functions()) {
return;
}
const String* variable_name = &Symbols::FunctionTypeArgumentsVar();
current_block_->scope->CaptureVariable(
current_block_->scope->LookupVariable(*variable_name, true));
}
void Parser::CaptureAllInstantiators() {
if (IsInstantiatorRequired()) {
CaptureInstantiator();
}
if (innermost_function().HasGenericParent()) {
CaptureFunctionTypeArguments();
}
}
AstNode* Parser::LoadReceiver(TokenPosition token_pos) {
// A nested function may access 'this', referring to the receiver of the
// outermost enclosing function.
const bool kTestOnly = false;
LocalVariable* receiver = LookupReceiver(current_block_->scope, kTestOnly);
if (receiver == NULL) {
ReportError(token_pos, "illegal implicit access to receiver 'this'");
}
return new (Z) LoadLocalNode(TokenPos(), receiver);
}
InstanceGetterNode* Parser::CallGetter(TokenPosition token_pos,
AstNode* object,
const String& name) {
return new (Z) InstanceGetterNode(token_pos, object, name);
}
// Returns ast nodes of the variable initialization.
AstNode* Parser::ParseVariableDeclaration(const AbstractType& type,
bool is_final,
bool is_const,
SequenceNode** await_preamble) {
TRACE_PARSER("ParseVariableDeclaration");
ASSERT(IsIdentifier());
const TokenPosition ident_pos = TokenPos();
const String& ident = *CurrentLiteral();
ConsumeToken(); // Variable identifier.
const TokenPosition assign_pos = TokenPos();
AstNode* initialization = NULL;
LocalVariable* variable = NULL;
if (CurrentToken() == Token::kASSIGN) {
// Variable initialization.
ConsumeToken();
AstNode* expr =
ParseAwaitableExpr(is_const, kConsumeCascades, await_preamble);
const TokenPosition expr_end_pos = TokenPos();
variable = new (Z) LocalVariable(ident_pos, expr_end_pos, ident, type);
initialization = new (Z) StoreLocalNode(assign_pos, variable, expr);
if (is_const) {
ASSERT(expr->IsLiteralNode());
variable->SetConstValue(expr->AsLiteralNode()->literal());
}
} else if (is_final || is_const) {
ReportError(ident_pos,
"missing initialization of 'final' or 'const' variable");
} else {
// Initialize variable with null.
variable = new (Z) LocalVariable(ident_pos, assign_pos, ident, type);
AstNode* null_expr =
new (Z) LiteralNode(ident_pos, Object::null_instance());
initialization = new (Z) StoreLocalNode(ident_pos, variable, null_expr);
}
ASSERT(current_block_ != NULL);
const TokenPosition previous_pos =
current_block_->scope->PreviousReferencePos(ident);
if (previous_pos.IsReal()) {
ASSERT(!script_.IsNull());
if (previous_pos > ident_pos) {
ReportError(ident_pos, "initializer of '%s' may not refer to itself",
ident.ToCString());
} else {
intptr_t line_number;
script_.GetTokenLocation(previous_pos, &line_number, NULL);
ReportError(ident_pos, "identifier '%s' previously used in line %" Pd "",
ident.ToCString(), line_number);
}
}
// Add variable to scope after parsing the initializer expression.
// The expression must not be able to refer to the variable.
if (!current_block_->scope->AddVariable(variable)) {
LocalVariable* existing_var =
current_block_->scope->LookupVariable(variable->name(), true);
ASSERT(existing_var != NULL);
// Use before define cases have already been detected and reported above.
ASSERT(existing_var->owner() == current_block_->scope);
ReportError(ident_pos, "identifier '%s' already defined",
variable->name().ToCString());
}
if (is_final || is_const) {
variable->set_is_final();
}
return initialization;
}
// Parses ('var' | 'final' [type] | 'const' [type] | type).
// The presence of 'final' or 'const' must be detected and remembered
// before the call. If a type is parsed, it may be resolved and finalized
// according to the given type finalization mode.
RawAbstractType* Parser::ParseConstFinalVarOrType(
ClassFinalizer::FinalizationKind finalization) {
TRACE_PARSER("ParseConstFinalVarOrType");
if (CurrentToken() == Token::kVAR) {
ConsumeToken();
return Type::DynamicType();
}
bool type_is_optional = false;
if ((CurrentToken() == Token::kFINAL) || (CurrentToken() == Token::kCONST)) {
ConsumeToken();
type_is_optional = true;
}
if ((CurrentToken() == Token::kVOID) || IsFunctionTypeSymbol()) {
return ParseTypeOrFunctionType(true, finalization);
}
if (CurrentToken() != Token::kIDENT) {
if (type_is_optional) {
return Type::DynamicType();
} else {
ReportError("type name expected");
}
}
if (type_is_optional) {
Token::Kind follower = LookaheadToken(1);
// We have an identifier followed by a 'follower' token.
// We either parse a type or return now.
if ((follower != Token::kLT) && // Parameterized type.
(follower != Token::kPERIOD) && // Qualified class name of type.
!Token::IsIdentifier(follower) && // Variable name following a type.
(follower != Token::kTHIS)) { // Field parameter following a type.
return Type::DynamicType();
}
}
return ParseTypeOrFunctionType(false, finalization); // void handled above.
}
// Returns ast nodes of the variable initialization. Variables without an
// explicit initializer are initialized to null. If several variables are
// declared, the individual initializers are collected in a sequence node.
AstNode* Parser::ParseVariableDeclarationList() {
TRACE_PARSER("ParseVariableDeclarationList");
SkipMetadata();
bool is_final = (CurrentToken() == Token::kFINAL);
bool is_const = (CurrentToken() == Token::kCONST);
const AbstractType& type = AbstractType::ZoneHandle(
Z,
ParseConstFinalVarOrType(I->type_checks() ? ClassFinalizer::kCanonicalize
: ClassFinalizer::kIgnore));
if (!IsIdentifier()) {
ReportError("identifier expected");
}
SequenceNode* preamble = NULL;
AstNode* initializers =
ParseVariableDeclaration(type, is_final, is_const, &preamble);
ASSERT(initializers != NULL);
if (preamble != NULL) {
preamble->Add(initializers);
initializers = preamble;
}
while (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
if (!IsIdentifier()) {
ReportError("identifier expected after comma");
}
// We have a second initializer. Allocate a sequence node now.
// The sequence does not own the current scope. Set its own scope to NULL.
SequenceNode* sequence =
NodeAsSequenceNode(initializers->token_pos(), initializers, NULL);
preamble = NULL;
AstNode* declaration =
ParseVariableDeclaration(type, is_final, is_const, &preamble);
if (preamble != NULL) {
sequence->Add(preamble);
}
sequence->Add(declaration);
initializers = sequence;
}
return initializers;
}
AstNode* Parser::ParseFunctionStatement(bool is_literal) {
TRACE_PARSER("ParseFunctionStatement");
AbstractType& result_type = AbstractType::Handle(Z, Type::DynamicType());
const String* function_name = NULL;
const TokenPosition function_pos = TokenPos();
TokenPosition function_name_pos = TokenPosition::kNoSource;
TokenPosition metadata_pos = TokenPosition::kNoSource;
if (is_literal) {
ASSERT(CurrentToken() == Token::kLPAREN || CurrentToken() == Token::kLT);
function_name = &Symbols::AnonymousClosure();
} else {
metadata_pos = SkipMetadata();
// Parse optional result type.
if (IsFunctionReturnType()) {
// It is too early to resolve the type here, since it can be a result type
// referring to a not yet declared function type parameter.
result_type =
ParseTypeOrFunctionType(true, ClassFinalizer::kDoNotResolve);
}
function_name_pos = TokenPos();
function_name = ExpectIdentifier("function name expected");
// Check that the function name has not been referenced
// before this declaration.
ASSERT(current_block_ != NULL);
const TokenPosition previous_pos =
current_block_->scope->PreviousReferencePos(*function_name);
if (previous_pos.IsReal()) {
ASSERT(!script_.IsNull());
intptr_t line_number;
script_.GetTokenLocation(previous_pos, &line_number, NULL);
ReportError(function_name_pos,
"identifier '%s' previously used in line %" Pd "",
function_name->ToCString(), line_number);
}
}
// Check whether we have parsed this closure function before, in a previous
// compilation. If so, reuse the function object, else create a new one
// and register it in the current class.
// Note that we cannot share the same closure function between the closurized
// and non-closurized versions of the same parent function.
Function& function = Function::ZoneHandle(Z);
bool found_func = true;
// TODO(hausner): There could be two different closures at the given
// function_pos, one enclosed in a closurized function and one enclosed in the
// non-closurized version of this same function.
function = I->LookupClosureFunction(innermost_function(), function_pos);
if (function.IsNull()) {
// The function will be registered in the lookup table by the
// EffectGraphVisitor::VisitClosureNode when the newly allocated closure
// function has been properly setup.
found_func = false;
function = Function::NewClosureFunction(*function_name,
innermost_function(), function_pos);
function.set_result_type(result_type);
// The result type may refer to the function's type parameters,
// but was not parsed in the scope of the function. Adjust.
result_type.SetScopeFunction(function);
if (metadata_pos.IsReal()) {
library_.AddFunctionMetadata(function, metadata_pos);
}
}
ASSERT(function.parent_function() == innermost_function_.raw());
innermost_function_ = function.raw();
if (CurrentToken() == Token::kLT) {
if (!found_func) {
ParseTypeParameters(false); // Not parameterizing class, but function.
} else {
TryParseTypeParameters();
}
}
CheckToken(Token::kLPAREN);
// The function type needs to be finalized at compile time, since the closure
// may be type checked at run time when assigned to a function variable,
// passed as a function argument, or returned as a function result.
LocalVariable* function_variable = NULL;
Type& function_type = Type::ZoneHandle(Z);
if (!is_literal) {
// Since the function type depends on the signature of the closure function,
// it cannot be determined before the formal parameter list of the closure
// function is parsed. Therefore, we set the function type to a new
// function type to be patched after the actual type is known.
// We temporarily use the Closure class as scope class.
const Class& unknown_scope_class =
Class::Handle(Z, I->object_store()->closure_class());
function_type =
Type::New(unknown_scope_class, TypeArguments::Handle(Z), function_pos);
function_type.set_signature(function);
function_type.SetIsFinalized(); // No finalization needed.
// Add the function variable to the scope before parsing the function in
// order to allow self reference from inside the function.
function_variable = new (Z) LocalVariable(function_name_pos, function_pos,
*function_name, function_type);
function_variable->set_is_final();
ASSERT(current_block_ != NULL);
ASSERT(current_block_->scope != NULL);
if (!current_block_->scope->AddVariable(function_variable)) {
LocalVariable* existing_var = current_block_->scope->LookupVariable(
function_variable->name(), true);
ASSERT(existing_var != NULL);
// Use before define cases have already been detected and reported above.
ASSERT(existing_var->owner() == current_block_->scope);
ReportError(function_pos, "identifier '%s' already defined",
function_variable->name().ToCString());
}
}
Type& signature_type = Type::ZoneHandle(Z);
SequenceNode* statements = NULL;
if (!found_func) {
// Parse the local function. As a side effect of the parsing, the
// variables of this function's scope that are referenced by the local
// function (and its inner nested functions) will be marked as captured.
ResolveTypeParameters(&result_type);
function.set_result_type(result_type); // Update type without scope change.
// Type parameters appearing in parameter types are resolved in ParseFunc.
statements = Parser::ParseFunc(function, !is_literal);
INC_STAT(thread(), num_functions_parsed, 1);
// Now that the local function has formal parameters, finalize its signature
signature_type = function.SignatureType();
signature_type ^= CanonicalizeType(signature_type);
function.SetSignatureType(signature_type);
} else {
// The local function was parsed before. The captured variables are
// saved in the function's context scope. Iterate over the context scope
// and mark its variables as captured.
const ContextScope& context_scope =
ContextScope::Handle(Z, function.context_scope());
ASSERT(!context_scope.IsNull());
String& var_name = String::Handle(Z);
for (int i = 0; i < context_scope.num_variables(); i++) {
var_name = context_scope.NameAt(i);
// We need to look up the name in a way that returns even hidden
// variables, e.g. 'this' in an initializer list.
LocalVariable* v = current_block_->scope->LookupVariable(var_name, true);
ASSERT(v != NULL);
current_block_->scope->CaptureVariable(v);
}
SkipFunctionLiteral();
signature_type = function.SignatureType();
}
// Local functions are registered in the enclosing class, but
// ignored during class finalization. The enclosing class has
// already been finalized.
ASSERT(current_class().is_finalized());
ASSERT(signature_type.IsFinalized());
// Make sure that the instantiators are captured.
if ((FunctionLevel() > 0) && !signature_type.IsInstantiated()) {
CaptureAllInstantiators();
}
// A local signature type itself cannot be malformed or malbounded, only its
// signature function's result type or parameter types may be.
ASSERT(!signature_type.IsMalformed());
ASSERT(!signature_type.IsMalbounded());
if (!is_literal) {
// Patch the function type of the variable now that the signature is known.
function_type.set_type_class(Class::Handle(Z, signature_type.type_class()));
function_type.set_arguments(
TypeArguments::Handle(Z, signature_type.arguments()));
ASSERT(function_type.signature() == function.raw());
// The function type was initially marked as instantiated, but it may
// actually be uninstantiated.
function_type.ResetIsFinalized();
// The function variable type should have been patched above.
ASSERT(function_variable->type().raw() == function_type.raw());
}
// The code generator does not compile the closure function when visiting
// a ClosureNode. The generated code allocates a new Closure object containing
// the current context. The type of the Closure object refers to the closure
// function, which will be compiled on first invocation of the closure object.
// Therefore, we ignore the parsed default_parameter_values and the
// node_sequence representing the body of the closure function, which will be
// parsed again when compiled later.
// The only purpose of parsing the function now (besides reporting obvious
// errors) is to mark referenced variables of the enclosing scopes as
// captured. The captured variables will be recorded along with their
// allocation information in a Scope object stored in the function object.
// This Scope object is then provided to the compiler when compiling the local
// function. It would be too early to record the captured variables here,
// since further closure functions may capture more variables.
// This Scope object is constructed after all variables have been allocated.
// The local scope of the parsed function can be pruned, since contained
// variables are not relevant for the compilation of the enclosing function.
// This pruning is done by omitting to hook the local scope in its parent
// scope in the constructor of LocalScope.
AstNode* closure =
new (Z) ClosureNode(function_pos, function, NULL,
statements != NULL ? statements->scope() : NULL);
ASSERT(innermost_function_.raw() == function.raw());
innermost_function_ = function.parent_function();
return is_literal
? closure
: new (Z) StoreLocalNode(function_pos, function_variable, closure);
}
// Returns true if the current and next tokens can be parsed as type
// parameters. Current token position is not saved and restored.
bool Parser::TryParseTypeParameters() {
ASSERT(CurrentToken() == Token::kLT);
int nesting_level = 0;
do {
Token::Kind ct = CurrentToken();
if (ct == Token::kLT) {
nesting_level++;
} else if (ct == Token::kGT) {
nesting_level--;
} else if (ct == Token::kSHR) {
nesting_level -= 2;
} else if (ct == Token::kIDENT) {
// Check to see if it is a qualified identifier.
if (LookaheadToken(1) == Token::kPERIOD) {
// Consume the identifier, the period will be consumed below.
ConsumeToken();
}
} else if ((ct != Token::kCOMMA) && (ct != Token::kEXTENDS)) {
// We are looking at something other than type parameters.
return false;
}
ConsumeToken();
} while (nesting_level > 0);
if (nesting_level < 0) {
return false;
}
return true;
}
// Returns true if the next tokens can be parsed as type parameters.
bool Parser::IsTypeParameters() {
if (CurrentToken() == Token::kLT) {
TokenPosScope param_pos(this);
if (!TryParseTypeParameters()) {
return false;
}
return true;
}
return false;
}
// Returns true if the next tokens are [ typeParameters ] '('.
bool Parser::IsParameterPart() {
if (CurrentToken() == Token::kLPAREN) {
return true;
}
if (CurrentToken() == Token::kLT) {
TokenPosScope type_arg_pos(this);
if (!TryParseTypeParameters()) {
return false;
}
return CurrentToken() == Token::kLPAREN;
}
return false;
}
// Returns true if the current and next tokens can be parsed as type
// arguments. Current token position is not saved and restored.
bool Parser::TryParseTypeArguments() {
ASSERT(CurrentToken() == Token::kLT);
int nesting_level = 0;
do {
Token::Kind ct = CurrentToken();
if (ct == Token::kLT) {
nesting_level++;
} else if (ct == Token::kGT) {
nesting_level--;
} else if (ct == Token::kSHR) {
nesting_level -= 2;
} else if (ct == Token::kVOID) {
ConsumeToken();
continue;
} else if (ct == Token::kIDENT) {
if (IsFunctionTypeSymbol()) {
if (!TryParseType(false)) {
return false;
}
continue;
} else {
// Check to see if it is a qualified identifier.
if (LookaheadToken(1) == Token::kPERIOD) {
// Consume the identifier, the period will be consumed below.
ConsumeToken();
}
}
} else if (ct != Token::kCOMMA) {
return false;
}
ConsumeToken();
} while (nesting_level > 0);
if (nesting_level < 0) {
return false;
}
return true;
}
// Returns true if the next tokens are [ typeArguments ] '('.
bool Parser::IsArgumentPart() {
if (CurrentToken() == Token::kLPAREN) {
return true;
}
if (CurrentToken() == Token::kLT) {
TokenPosScope type_arg_pos(this);
if (!TryParseTypeArguments()) {
return false;
}
return CurrentToken() == Token::kLPAREN;
}
return false;
}
bool Parser::IsSimpleLiteral(const AbstractType& type, Instance* value) {
// Assigning null never causes a type error.
if (CurrentToken() == Token::kNULL) {
*value = Instance::null();
return true;
}
// If the type of the const field is guaranteed to be instantiated once
// resolved at class finalization time, and if the type of the literal is one
// of int, double, String, or bool, then preset the field with the value and
// perform the type check (in checked mode only) at finalization time.
if (type.IsTypeParameter() || (type.arguments() != TypeArguments::null())) {
// Type parameters are always resolved eagerly by the parser and never
// resolved later by the class finalizer. Therefore, we know here that if
// 'type' is not a type parameter (an unresolved type will not get resolved
// to a type parameter later) and if 'type' has no type arguments, then it
// will be instantiated at class finalization time. Otherwise, we return
// false, since the type test would not be possible at finalization time for
// an uninstantiated type.
return false;
}
if (CurrentToken() == Token::kINTEGER) {
*value = CurrentIntegerLiteral();
return true;
} else if (CurrentToken() == Token::kDOUBLE) {
*value = CurrentDoubleLiteral();
return true;
} else if (CurrentToken() == Token::kSTRING) {
*value = CurrentLiteral()->raw();
return true;
} else if (CurrentToken() == Token::kTRUE) {
*value = Bool::True().raw();
return true;
} else if (CurrentToken() == Token::kFALSE) {
*value = Bool::False().raw();
return true;
}
return false;
}
// Returns true if the current token is kIDENT or a pseudo-keyword.
bool Parser::IsIdentifier() {
return Token::IsIdentifier(CurrentToken()) &&
!(await_is_keyword_ &&
((CurrentLiteral()->raw() == Symbols::Await().raw()) ||
(CurrentLiteral()->raw() == Symbols::Async().raw()) ||
(CurrentLiteral()->raw() == Symbols::YieldKw().raw())));
}
bool Parser::IsSymbol(const String& symbol) {
return (CurrentLiteral()->raw() == symbol.raw()) &&
(CurrentToken() == Token::kIDENT);
}
// Returns true if the current token is 'Function' followed by '<' or '('.
// 'Function' not followed by '<' or '(' denotes the Function class.
bool Parser::IsFunctionTypeSymbol() {
return IsSymbol(Symbols::Function()) &&
((LookaheadToken(1) == Token::kLPAREN) ||
(LookaheadToken(1) == Token::kLT));
}
// Returns true if the next tokens can be parsed as a an optionally
// qualified identifier: [ident '.'] ident.
// Current token position is not restored.
bool Parser::TryParseQualIdent() {
if (CurrentToken() != Token::kIDENT) {
return false;
}
ConsumeToken();
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
if (CurrentToken() != Token::kIDENT) {
return false;
}
ConsumeToken();
}
return true;
}
// Returns true if the next tokens can be parsed as a type with optional
// type parameters. Current token position is not restored.
// Allow 'void' as type if 'allow_void' is true.
// Note that 'void Function()' is always allowed, since it is a function type
// and not the void type.
// TODO(regis): Consider removing allow_void argument, since this call is only
// used where void is allowed. Wait for Dart 2 spec to stabilize.
bool Parser::TryParseType(bool allow_void) {
bool found = false;
if (CurrentToken() == Token::kVOID) {
ConsumeToken();
if (allow_void) {
found = true;
} else if (!IsFunctionTypeSymbol()) {
return false;
}
} else if ((CurrentToken() == Token::kIDENT) && !IsFunctionTypeSymbol()) {
// 'Function' not followed by '(' or '<' means the Function class.
if (!TryParseQualIdent()) {
return false;
}
if ((CurrentToken() == Token::kLT) && !TryParseTypeArguments()) {
return false;
}
found = true;
}
while (IsFunctionTypeSymbol()) {
ConsumeToken();
if ((CurrentToken() == Token::kLT) && !TryParseTypeParameters()) {
return false;
}
if (CurrentToken() == Token::kLPAREN) {
SkipToMatchingParenthesis();
} else {
return false;
}
found = true;
}
return found;
}
// Look ahead to detect whether the next tokens should be parsed as
// a variable declaration. Ignores optional metadata.
// Returns true if we detect the token pattern:
// 'var'
// | 'final'
// | const [type] ident (';' | '=' | ',')
// | type ident (';' | '=' | ',')
// Token position remains unchanged.
bool Parser::IsVariableDeclaration() {
if ((CurrentToken() == Token::kVAR) || (CurrentToken() == Token::kFINAL)) {
return true;
}
// Skip optional metadata.
if (CurrentToken() == Token::kAT) {
const TokenPosition saved_pos = TokenPos();
SkipMetadata();
const bool is_var_decl = IsVariableDeclaration();
SetPosition(saved_pos);
return is_var_decl;
}
if ((CurrentToken() != Token::kIDENT) && (CurrentToken() != Token::kVOID) &&
(CurrentToken() != Token::kCONST)) {
// Not a legal type identifier or void or const keyword or metadata.
return false;
}
const TokenPosition saved_pos = TokenPos();
bool is_var_decl = false;
bool have_type = false;
if (CurrentToken() == Token::kCONST) {
ConsumeToken();
have_type = true; // Type is dynamic if 'const' is not followed by a type.
}
if ((CurrentToken() == Token::kVOID) || IsFunctionTypeSymbol()) {
if (TryParseType(true)) {
have_type = true;
}
} else if (IsIdentifier()) { // Type or variable name.
Token::Kind follower = LookaheadToken(1);
if ((follower == Token::kLT) || // Parameterized type.
(follower == Token::kPERIOD) || // Qualified class name of type.
Token::IsIdentifier(follower)) { // Variable name following a type.
// We see the beginning of something that could be a type.
const TokenPosition type_pos = TokenPos();
if (TryParseType(false)) { // void handled above.
have_type = true;
} else {
SetPosition(type_pos);
}
}
}
if (have_type && IsIdentifier()) {
ConsumeToken();
if ((CurrentToken() == Token::kSEMICOLON) ||
(CurrentToken() == Token::kCOMMA) ||
(CurrentToken() == Token::kASSIGN)) {
is_var_decl = true;
}
}
SetPosition(saved_pos);
return is_var_decl;
}
// Look ahead to see if the following tokens are a return type followed
// by an identifier.
bool Parser::IsFunctionReturnType() {
TokenPosScope decl_pos(this);
if (TryParseType(true)) {
if (IsIdentifier()) {
// Return type followed by function name.
return true;
}
}
return false;
}
// Look ahead to detect whether the next tokens should be parsed as
// a function declaration. Token position remains unchanged.
bool Parser::IsFunctionDeclaration() {
bool is_external = false;
TokenPosScope decl_pos(this);
SkipMetadata();
if ((is_top_level_) && (CurrentToken() == Token::kEXTERNAL)) {
// Skip over 'external' for top-level function declarations.
is_external = true;
ConsumeToken();
}
const TokenPosition type_or_name_pos = TokenPos();
if (TryParseType(true)) {
if (!IsIdentifier()) {
SetPosition(type_or_name_pos);
}
} else {
SetPosition(type_or_name_pos);
}
// Check for function name followed by optional type parameters.
if (!IsIdentifier()) {
return false;
}
ConsumeToken();
if ((CurrentToken() == Token::kLT) && !TryParseTypeParameters()) {
return false;
}
// Optional type, function name and optinal type parameters are parsed.
if (CurrentToken() != Token::kLPAREN) {
return false;
}
// Check parameter list and the following token.
SkipToMatchingParenthesis();
if ((CurrentToken() == Token::kLBRACE) || (CurrentToken() == Token::kARROW) ||
(is_top_level_ && IsSymbol(Symbols::Native())) || is_external ||
IsSymbol(Symbols::Async()) || IsSymbol(Symbols::Sync())) {
return true;
}
return false;
}
bool Parser::IsTopLevelAccessor() {
const TokenPosScope saved_pos(this);
if (CurrentToken() == Token::kEXTERNAL) {
ConsumeToken();
}
if ((CurrentToken() == Token::kGET) || (CurrentToken() == Token::kSET)) {
return true;
}
if (TryParseType(true)) {
if ((CurrentToken() == Token::kGET) || (CurrentToken() == Token::kSET)) {
if (Token::IsIdentifier(LookaheadToken(1))) { // Accessor name.
return true;
}
}
}
return false;
}
bool Parser::IsFunctionLiteral() {
if (!allow_function_literals_) {
return false;
}
if ((CurrentToken() == Token::kLPAREN) || (CurrentToken() == Token::kLT)) {
TokenPosScope saved_pos(this);
if ((CurrentToken() == Token::kLT) && !TryParseTypeParameters()) {
return false;
}
if (CurrentToken() != Token::kLPAREN) {
return false;
}
SkipToMatchingParenthesis();
ParseFunctionModifier();
if ((CurrentToken() == Token::kLBRACE) ||
(CurrentToken() == Token::kARROW)) {
return true;
}
}
return false;
}
// Current token position is the token after the opening ( of the for
// statement. Returns true if we recognize a for ( .. in expr)
// statement.
bool Parser::IsForInStatement() {
const TokenPosScope saved_pos(this);
// Allow const modifier as well when recognizing a for-in statement
// pattern. We will get an error later if the loop variable is
// declared with const.
if (CurrentToken() == Token::kVAR || CurrentToken() == Token::kFINAL ||
CurrentToken() == Token::kCONST) {
ConsumeToken();
}
// void as the loop variable type does not make much sense, but it is not
// disallowed by the spec.
if (IsIdentifier() || (CurrentToken() == Token::kVOID)) {
if ((CurrentToken() != Token::kVOID) && (LookaheadToken(1) == Token::kIN)) {
return true;
} else if (TryParseType(true)) {
if (IsIdentifier()) {
ConsumeToken();
}
return CurrentToken() == Token::kIN;
}
}
return false;
}
static bool ContainsAbruptCompletingStatement(SequenceNode* seq);
static bool IsAbruptCompleting(AstNode* statement) {
return statement->IsReturnNode() || statement->IsJumpNode() ||
statement->IsThrowNode() ||
(statement->IsSequenceNode() &&
ContainsAbruptCompletingStatement(statement->AsSequenceNode()));
}
static bool ContainsAbruptCompletingStatement(SequenceNode* seq) {
for (int i = 0; i < seq->length(); i++) {
if (IsAbruptCompleting(seq->NodeAt(i))) {
return true;
}
}
return false;
}
void Parser::ParseStatementSequence() {
TRACE_PARSER("ParseStatementSequence");
const bool dead_code_allowed = true;
bool abrupt_completing_seen = false;
RecursionChecker rc(this);
while (CurrentToken() != Token::kRBRACE) {
const TokenPosition statement_pos = TokenPos();
AstNode* statement = ParseStatement();
// Do not add statements with no effect (e.g., LoadLocalNode).
if ((statement != NULL) && statement->IsLoadLocalNode()) {
// Skip load local.
continue;
}
if (statement != NULL) {
if (!dead_code_allowed && abrupt_completing_seen) {
ReportError(statement_pos,
"dead code after abrupt completing statement");
}
current_block_->statements->Add(statement);
abrupt_completing_seen |= IsAbruptCompleting(statement);
}
}
}
// Parse nested statement of if, while, for, etc. We automatically generate
// a sequence of one statement if there are no curly braces.
// The argument 'parsing_loop_body' indicates the parsing of a loop statement.
SequenceNode* Parser::ParseNestedStatement(bool parsing_loop_body,
SourceLabel* label) {
TRACE_PARSER("ParseNestedStatement");
if (parsing_loop_body) {
OpenLoopBlock();
} else {
OpenBlock();
}
if (label != NULL) {
current_block_->scope->AddLabel(label);
}
if (CurrentToken() == Token::kLBRACE) {
ConsumeToken();
ParseStatementSequence();
ExpectToken(Token::kRBRACE);
} else {
RecursionChecker rc(this);
AstNode* statement = ParseStatement();
if (statement != NULL) {
current_block_->statements->Add(statement);
}
}
SequenceNode* sequence = CloseBlock();
return sequence;
}
AstNode* Parser::ParseIfStatement(String* label_name) {
TRACE_PARSER("ParseIfStatement");
ASSERT(CurrentToken() == Token::kIF);
const TokenPosition if_pos = TokenPos();
SourceLabel* label = NULL;
if (label_name != NULL) {
label = SourceLabel::New(if_pos, label_name, SourceLabel::kStatement);
OpenBlock();
current_block_->scope->AddLabel(label);
}
ConsumeToken();
ExpectToken(Token::kLPAREN);
AstNode* cond_expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
ExpectToken(Token::kRPAREN);
const bool parsing_loop_body = false;
SequenceNode* true_branch = ParseNestedStatement(parsing_loop_body, NULL);
SequenceNode* false_branch = NULL;
if (CurrentToken() == Token::kELSE) {
ConsumeToken();
false_branch = ParseNestedStatement(parsing_loop_body, NULL);
}
AstNode* if_node =
new (Z) IfNode(if_pos, cond_expr, true_branch, false_branch);
if (label != NULL) {
current_block_->statements->Add(if_node);
SequenceNode* sequence = CloseBlock();
sequence->set_label(label);
if_node = sequence;
}
return if_node;
}
// Return true if the type class of the given value implements the
// == operator.
static bool ImplementsEqualOperator(Zone* zone, const Instance& value) {
Class& cls = Class::Handle(value.clazz());
const Function& equal_op = Function::Handle(
zone,
Resolver::ResolveDynamicAnyArgs(zone, cls, Symbols::EqualOperator()));
ASSERT(!equal_op.IsNull());
cls = equal_op.Owner();
return !cls.IsObjectClass();
}
// Check that all case expressions are of the same type, either int, String,
// or any other class that does not override the == operator.
// The expressions are compile-time constants and are thus in the form
// of a LiteralNode.
RawClass* Parser::CheckCaseExpressions(
const GrowableArray<LiteralNode*>& values) {
const intptr_t num_expressions = values.length();
if (num_expressions == 0) {
return Object::dynamic_class();
}
const Instance& first_value = values[0]->literal();
for (intptr_t i = 0; i < num_expressions; i++) {
const Instance& val = values[i]->literal();
const TokenPosition val_pos = values[i]->token_pos();
if (first_value.IsInteger()) {
if (!val.IsInteger()) {
ReportError(val_pos, "expected case expression of type int");
}
continue;
}
if (first_value.IsString()) {
if (!val.IsString()) {
ReportError(val_pos, "expected case expression of type String");
}
continue;
}
if (val.IsDouble()) {
ReportError(val_pos, "case expression may not be of type double");
}
if (val.clazz() != first_value.clazz()) {
ReportError(val_pos, "all case expressions must be of same type");
}
if (val.clazz() == I->object_store()->symbol_class()) {
continue;
}
if (i == 0) {
// The value is of some type other than int, String or double.
// Check that the type class does not override the == operator.
// Check this only in the first loop iteration since all values
// are of the same type, which we check above.
if (ImplementsEqualOperator(Z, val)) {
ReportError(val_pos,
"type class of case expression must not "
"implement operator ==");
}
}
}
if (first_value.IsInteger()) {
return Type::Handle(Z, Type::IntType()).type_class();
} else if (first_value.IsString()) {
return Type::Handle(Z, Type::StringType()).type_class();
}
return first_value.clazz();
}
CaseNode* Parser::ParseCaseClause(LocalVariable* switch_expr_value,
GrowableArray<LiteralNode*>* case_expr_values,
SourceLabel* case_label) {
TRACE_PARSER("ParseCaseClause");
bool default_seen = false;
const TokenPosition case_pos = TokenPos();
// The case expressions node sequence does not own the enclosing scope.
SequenceNode* case_expressions = new (Z) SequenceNode(case_pos, NULL);
while (CurrentToken() == Token::kCASE || CurrentToken() == Token::kDEFAULT) {
if (CurrentToken() == Token::kCASE) {
if (default_seen) {
ReportError("default clause must be last case");
}
ConsumeToken(); // Keyword case.
const TokenPosition expr_pos = TokenPos();
AstNode* expr = ParseExpr(kRequireConst, kConsumeCascades);
ASSERT(expr->IsLiteralNode());
case_expr_values->Add(expr->AsLiteralNode());
AstNode* switch_expr_load =
new (Z) LoadLocalNode(case_pos, switch_expr_value);
AstNode* case_comparison =
new (Z) ComparisonNode(expr_pos, Token::kEQ, expr, switch_expr_load);
case_expressions->Add(case_comparison);
} else {
if (default_seen) {
ReportError("only one default clause is allowed");
}
ConsumeToken(); // Keyword default.
default_seen = true;
// The default case always succeeds.
}
ExpectToken(Token::kCOLON);
}
OpenBlock();
bool abrupt_completing_seen = false;
while (true) {
// Check whether the next statement still belongs to the current case
// clause. If we see 'case' or 'default', optionally preceeded by
// a label, or closing brace, we stop parsing statements.
Token::Kind next_token;
if (IsIdentifier() && LookaheadToken(1) == Token::kCOLON) {
next_token = LookaheadToken(2);
} else {
next_token = CurrentToken();
}
if (next_token == Token::kRBRACE) {
// End of switch statement.
break;
}
if ((next_token == Token::kCASE) || (next_token == Token::kDEFAULT)) {
// End of this case clause. If there is a possible fall-through to
// the next case clause, throw an implicit FallThroughError.
if (!abrupt_completing_seen) {
ArgumentListNode* arguments = new (Z) ArgumentListNode(TokenPos());
arguments->Add(new (Z) LiteralNode(
TokenPos(), Integer::ZoneHandle(
Z, Integer::New(TokenPos().value(), Heap::kOld))));
current_block_->statements->Add(MakeStaticCall(
Symbols::FallThroughError(),
Library::PrivateCoreLibName(Symbols::ThrowNew()), arguments));
}
break;
}
// The next statement still belongs to this case.
AstNode* statement = ParseStatement();
if (statement != NULL) {
current_block_->statements->Add(statement);
abrupt_completing_seen |= IsAbruptCompleting(statement);
}
}
SequenceNode* statements = CloseBlock();
return new (Z) CaseNode(case_pos, case_label, case_expressions, default_seen,
switch_expr_value, statements);
}
AstNode* Parser::ParseSwitchStatement(String* label_name) {
TRACE_PARSER("ParseSwitchStatement");
ASSERT(CurrentToken() == Token::kSWITCH);
const TokenPosition switch_pos = TokenPos();
SourceLabel* label =
SourceLabel::New(switch_pos, label_name, SourceLabel::kSwitch);
ConsumeToken();
ExpectToken(Token::kLPAREN);
const TokenPosition expr_pos = TokenPos();
AstNode* switch_expr =
ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
ExpectToken(Token::kRPAREN);
ExpectToken(Token::kLBRACE);
OpenBlock();
current_block_->scope->AddLabel(label);
// Store switch expression in temporary local variable. The type of the
// variable is set to dynamic. It will later be patched to match the
// type of the case clause expressions. Therefore, we have to allocate
// a new type representing dynamic and can't reuse the canonical
// type object for dynamic.
const Type& temp_var_type =
Type::ZoneHandle(Z, Type::New(Class::Handle(Z, Object::dynamic_class()),
TypeArguments::Handle(Z), expr_pos));
temp_var_type.SetIsFinalized();
LocalVariable* temp_variable = new (Z)
LocalVariable(expr_pos, expr_pos, Symbols::SwitchExpr(), temp_var_type);
current_block_->scope->AddVariable(temp_variable);
AstNode* save_switch_expr =
new (Z) StoreLocalNode(expr_pos, temp_variable, switch_expr);
current_block_->statements->Add(save_switch_expr);
// Parse case clauses
bool default_seen = false;
GrowableArray<LiteralNode*> case_expr_values;
while (true) {
// Check for statement label
SourceLabel* case_label = NULL;
if (IsIdentifier() && LookaheadToken(1) == Token::kCOLON) {
// Case statements start with a label.
String* label_name = CurrentLiteral();
const TokenPosition label_pos = TokenPos();
ConsumeToken(); // Consume label identifier.
ConsumeToken(); // Consume colon.
case_label = current_block_->scope->LocalLookupLabel(*label_name);
if (case_label == NULL) {
// Label does not exist yet. Add it to scope of switch statement.
case_label =
new (Z) SourceLabel(label_pos, *label_name, SourceLabel::kCase);
current_block_->scope->AddLabel(case_label);
} else if (case_label->kind() == SourceLabel::kForward) {
// We have seen a 'continue' with this label name. Resolve
// the forward reference.
case_label->ResolveForwardReference();
RemoveNodesForFinallyInlining(case_label);
} else {
ReportError(label_pos, "label '%s' already exists in scope",
label_name->ToCString());
}
ASSERT(case_label->kind() == SourceLabel::kCase);
}
if (CurrentToken() == Token::kCASE || CurrentToken() == Token::kDEFAULT) {
if (default_seen) {
ReportError("no case clauses allowed after default clause");
}
CaseNode* case_clause =
ParseCaseClause(temp_variable, &case_expr_values, case_label);
default_seen = case_clause->contains_default();
current_block_->statements->Add(case_clause);
} else if (CurrentToken() != Token::kRBRACE) {
ReportError("'case' or '}' expected");
} else if (case_label != NULL) {
ReportError("expecting at least one case clause after label");
} else {
break;
}
}
// Check that all expressions in case clauses are of the same class,
// or implement int, double or String. Patch the type of the temporary
// variable holding the switch expression to match the type of the
// case clause constants.
temp_var_type.set_type_class(
Class::Handle(Z, CheckCaseExpressions(case_expr_values)));
// Check for unresolved label references.
SourceLabel* unresolved_label =
current_block_->scope->CheckUnresolvedLabels();
if (unresolved_label != NULL) {
ReportError("unresolved reference to label '%s'",
unresolved_label->name().ToCString());
}
SequenceNode* switch_body = CloseBlock();
ExpectToken(Token::kRBRACE);
return new (Z) SwitchNode(switch_pos, label, switch_body);
}
AstNode* Parser::ParseWhileStatement(String* label_name) {
TRACE_PARSER("ParseWhileStatement");
const TokenPosition while_pos = TokenPos();
SourceLabel* label =
SourceLabel::New(while_pos, label_name, SourceLabel::kWhile);
ConsumeToken();
ExpectToken(Token::kLPAREN);
SequenceNode* await_preamble = NULL;
AstNode* cond_expr =
ParseAwaitableExpr(kAllowConst, kConsumeCascades, &await_preamble);
ExpectToken(Token::kRPAREN);
const bool parsing_loop_body = true;
SequenceNode* while_body = ParseNestedStatement(parsing_loop_body, label);
WhileNode* while_node = new (Z)
WhileNode(while_pos, label, cond_expr, await_preamble, while_body);
return while_node;
}
AstNode* Parser::ParseDoWhileStatement(String* label_name) {
TRACE_PARSER("ParseDoWhileStatement");
const TokenPosition do_pos = TokenPos();
SourceLabel* label =
SourceLabel::New(do_pos, label_name, SourceLabel::kDoWhile);
ConsumeToken();
const bool parsing_loop_body = true;
SequenceNode* dowhile_body = ParseNestedStatement(parsing_loop_body, label);
ExpectToken(Token::kWHILE);
ExpectToken(Token::kLPAREN);
SequenceNode* await_preamble = NULL;
TokenPosition expr_pos = TokenPos();
AstNode* cond_expr =
ParseAwaitableExpr(kAllowConst, kConsumeCascades, &await_preamble);
if (await_preamble != NULL) {
// Prepend the preamble to the condition.
LetNode* await_cond = new (Z) LetNode(expr_pos);
await_cond->AddNode(await_preamble);
await_cond->AddNode(cond_expr);
cond_expr = await_cond;
}
ExpectToken(Token::kRPAREN);
ExpectSemicolon();
return new (Z) DoWhileNode(do_pos, label, cond_expr, dowhile_body);
}
static LocalVariable* LookupSavedTryContextVar(LocalScope* scope) {
LocalVariable* var =
scope->LocalLookupVariable(Symbols::SavedTryContextVar());
ASSERT((var != NULL) && !var->is_captured());
return var;
}
static LocalVariable* LookupAsyncSavedTryContextVar(Thread* thread,
LocalScope* scope,
uint16_t try_index) {
Zone* zone = thread->zone();
const String& async_saved_try_ctx_name = String::ZoneHandle(
zone, Symbols::NewFormatted(
thread, "%s%d",
Symbols::AsyncSavedTryCtxVarPrefix().ToCString(), try_index));
LocalVariable* var = scope->LocalLookupVariable(async_saved_try_ctx_name);
ASSERT(var != NULL);
return var;
}
// If the await or yield being parsed is in a try block, the continuation code
// needs to restore the corresponding stack-based variable :saved_try_ctx_var,
// and the stack-based variable :saved_try_ctx_var of the outer try block.
// The inner :saved_try_ctx_var is used by a finally clause handling an
// exception thrown by the continuation code in a try block or catch block.
// If no finally clause exists, the catch or finally clause of the outer try
// block, if any, uses the outer :saved_try_ctx_var to handle the exception.
//
// * Try blocks and catch blocks:
// Set the context variable for this try block and for the outer try block.
// * Finally blocks:
// Set the context variable for the outer try block. Note that the try
// declaring the finally is popped before parsing the finally clause, so the
// outer try block is at the top of the try block list.
void Parser::CheckAsyncOpInTryBlock(
LocalVariable** saved_try_ctx,
LocalVariable** async_saved_try_ctx,
LocalVariable** outer_saved_try_ctx,
LocalVariable** outer_async_saved_try_ctx) const {
*saved_try_ctx = NULL;
*async_saved_try_ctx = NULL;
*outer_saved_try_ctx = NULL;
*outer_async_saved_try_ctx = NULL;
if (try_stack_ != NULL) {
LocalScope* scope = try_stack_->try_block()->scope;
uint16_t try_index = try_stack_->try_index();
const int current_function_level = FunctionLevel();
if (scope->function_level() == current_function_level) {
// The block declaring :saved_try_ctx_var variable is the parent of the
// pushed try block.
*saved_try_ctx = LookupSavedTryContextVar(scope->parent());
*async_saved_try_ctx =
LookupAsyncSavedTryContextVar(T, async_temp_scope_, try_index);
if ((try_stack_->outer_try() != NULL) && !try_stack_->inside_finally()) {
// Collecting the outer try scope is not necessary if we
// are in a finally block.
scope = try_stack_->outer_try()->try_block()->scope;
try_index = try_stack_->outer_try()->try_index();
if (scope->function_level() == current_function_level) {
*outer_saved_try_ctx = LookupSavedTryContextVar(scope->parent());
*outer_async_saved_try_ctx =
LookupAsyncSavedTryContextVar(T, async_temp_scope_, try_index);
}
}
}
}
// An async or async* has an implicitly created try-catch around the
// function body, so the await or yield inside the async closure should always
// be created with a try scope.
ASSERT((*saved_try_ctx != NULL) || innermost_function().IsAsyncFunction() ||
innermost_function().IsAsyncGenerator() ||
innermost_function().IsSyncGenClosure() ||
innermost_function().IsSyncGenerator());
}
// Build an AST node for static call to Dart function print(str).
// Used during debugging to insert print in generated dart code.
AstNode* Parser::DartPrint(const char* str) {
const Library& lib = Library::Handle(Library::CoreLibrary());
const Function& print_fn =
Function::ZoneHandle(Z, lib.LookupFunctionAllowPrivate(Symbols::print()));
ASSERT(!print_fn.IsNull());
ArgumentListNode* one_arg =
new (Z) ArgumentListNode(TokenPosition::kNoSource);
String& msg = String::ZoneHandle(Symbols::NewFormatted(T, "%s", str));
one_arg->Add(new (Z) LiteralNode(TokenPosition::kNoSource, msg));
AstNode* print_call = new (Z) StaticCallNode(
TokenPosition::kNoSource, print_fn, one_arg, StaticCallNode::kStatic);
return print_call;
}
AstNode* Parser::ParseAwaitForStatement(String* label_name) {
TRACE_PARSER("ParseAwaitForStatement");
ASSERT(IsAwaitKeyword());
const TokenPosition await_for_pos = TokenPos();
ConsumeToken(); // await.
ASSERT(CurrentToken() == Token::kFOR);
ConsumeToken(); // for.
ExpectToken(Token::kLPAREN);
if (!innermost_function().IsAsyncFunction() &&
!innermost_function().IsAsyncClosure() &&
!innermost_function().IsAsyncGenerator() &&
!innermost_function().IsAsyncGenClosure()) {
ReportError(await_for_pos,
"await for loop is only allowed in an asynchronous function");
}
// Parse loop variable.
bool loop_var_is_final = (CurrentToken() == Token::kFINAL);
if (CurrentToken() == Token::kCONST) {
ReportError("Loop variable cannot be 'const'");
}
bool new_loop_var = false;
AbstractType& loop_var_type = AbstractType::ZoneHandle(Z);
if (LookaheadToken(1) != Token::kIN) {
// Declaration of a new loop variable.
// Delay creation of the local variable until we know its actual
// position, which is inside the loop body.
new_loop_var = true;
loop_var_type = ParseConstFinalVarOrType(I->type_checks()
? ClassFinalizer::kCanonicalize
: ClassFinalizer::kIgnore);
}
TokenPosition loop_var_pos = TokenPos();
const String* loop_var_name = ExpectIdentifier("variable name expected");
// Parse stream expression.
ExpectToken(Token::kIN);
// Open a block for the iterator variable and the try-finally statement
// that contains the loop. Ensure that the block starts at a different
// token position than the following loop block. Both blocks can allocate
// contexts and if they have a matching token position range,
// it can be an issue (cf. bug 26941).
OpenBlock();
const Block* await_for_block = current_block_;
const TokenPosition stream_expr_pos = TokenPos();
AstNode* stream_expr =
ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
ExpectToken(Token::kRPAREN);
// Create :stream to store the stream into temporarily.
LocalVariable* stream_var =
new (Z) LocalVariable(stream_expr_pos, stream_expr_pos,
Symbols::ColonStream(), Object::dynamic_type());
current_block_->scope->AddVariable(stream_var);
// Store the stream expression into a variable.
StoreLocalNode* store_stream_var =
new (Z) StoreLocalNode(stream_expr_pos, stream_var, stream_expr);
current_block_->statements->Add(store_stream_var);
// Register the awaiter on the stream by invoking `_asyncStarListenHelper`.
const Library& async_lib = Library::Handle(Library::AsyncLibrary());
const Function& async_star_listen_helper = Function::ZoneHandle(
Z,
async_lib.LookupFunctionAllowPrivate(Symbols::_AsyncStarListenHelper()));
ASSERT(!async_star_listen_helper.IsNull());
LocalVariable* async_op_var =
current_block_->scope->LookupVariable(Symbols::AsyncOperation(), false);
ASSERT(async_op_var != NULL);
ArgumentListNode* async_star_listen_helper_args =
new (Z) ArgumentListNode(stream_expr_pos);
async_star_listen_helper_args->Add(
new (Z) LoadLocalNode(stream_expr_pos, stream_var));
async_star_listen_helper_args->Add(
new (Z) LoadLocalNode(stream_expr_pos, async_op_var));
StaticCallNode* async_star_listen_helper_call = new (Z)
StaticCallNode(stream_expr_pos, async_star_listen_helper,
async_star_listen_helper_args, StaticCallNode::kStatic);
current_block_->statements->Add(async_star_listen_helper_call);
// Build creation of implicit StreamIterator.
// var :for-in-iter = new StreamIterator(stream_expr).
const Class& stream_iterator_cls =
Class::ZoneHandle(Z, I->object_store()->stream_iterator_class());
ASSERT(!stream_iterator_cls.IsNull());
const Function& iterator_ctor = Function::ZoneHandle(
Z,
stream_iterator_cls.LookupFunction(Symbols::StreamIteratorConstructor()));
ASSERT(!iterator_ctor.IsNull());
ArgumentListNode* ctor_args = new (Z) ArgumentListNode(stream_expr_pos);
ctor_args->Add(new (Z) LoadLocalNode(stream_expr_pos, stream_var));
ConstructorCallNode* ctor_call = new (Z) ConstructorCallNode(
stream_expr_pos, TypeArguments::ZoneHandle(Z), iterator_ctor, ctor_args);
const AbstractType& iterator_type = Object::dynamic_type();
LocalVariable* iterator_var = new (Z) LocalVariable(
stream_expr_pos, stream_expr_pos, Symbols::ForInIter(), iterator_type);
current_block_->scope->AddVariable(iterator_var);
AstNode* iterator_init =
new (Z) StoreLocalNode(stream_expr_pos, iterator_var, ctor_call);
current_block_->statements->Add(iterator_init);
// We need to ensure that the stream is cancelled after the loop.
// Thus, wrap the loop in a try-finally that calls :for-in-iter.close()
// in the finally clause. It is harmless to call close() if the stream
// is already cancelled (when moveNext() returns false).
// Note: even though this is async code, we do not need to set up
// the closurized saved_exception_var and saved_stack_trace_var because
// there can not be a suspend/resume event before the exception is
// rethrown in the catch clause. The catch block of the implicit
// try-finally is empty.
LocalVariable* context_var = NULL;
LocalVariable* exception_var = NULL;
LocalVariable* stack_trace_var = NULL;
LocalVariable* saved_exception_var = NULL;
LocalVariable* saved_stack_trace_var = NULL;
SetupExceptionVariables(current_block_->scope,
false, // Do not create the saved_ vars.
&context_var, &exception_var, &stack_trace_var,
&saved_exception_var, &saved_stack_trace_var);
OpenBlock(); // try block.
PushTry(current_block_);
SetupSavedTryContext(context_var);
// Build while loop condition.
// while (await :for-in-iter.moveNext())
LocalVariable* saved_try_ctx;
LocalVariable* async_saved_try_ctx;
LocalVariable* outer_saved_try_ctx;
LocalVariable* outer_async_saved_try_ctx;
CheckAsyncOpInTryBlock(&saved_try_ctx, &async_saved_try_ctx,
&outer_saved_try_ctx, &outer_async_saved_try_ctx);
ArgumentListNode* no_args = new (Z) ArgumentListNode(stream_expr_pos);
AstNode* iterator_moveNext = new (Z) InstanceCallNode(
stream_expr_pos, new (Z) LoadLocalNode(stream_expr_pos, iterator_var),
Symbols::MoveNext(), no_args);
OpenBlock();
#if !defined(PRODUCT)
// Call '_asyncStarMoveNextHelper' so that the debugger can intercept and
// handle single stepping into a async* generator.
const Function& async_star_move_next_helper = Function::ZoneHandle(
Z, isolate()->object_store()->async_star_move_next_helper());
ASSERT(!async_star_move_next_helper.IsNull());
ArgumentListNode* async_star_move_next_helper_args =
new (Z) ArgumentListNode(stream_expr_pos);
async_star_move_next_helper_args->Add(
new (Z) LoadLocalNode(stream_expr_pos, stream_var));
StaticCallNode* async_star_move_next_helper_call = new (Z)
StaticCallNode(stream_expr_pos, async_star_move_next_helper,
async_star_move_next_helper_args, StaticCallNode::kStatic);
current_block_->statements->Add(async_star_move_next_helper_call);
#endif
AstNode* await_moveNext = new (Z) AwaitNode(
stream_expr_pos, iterator_moveNext, saved_try_ctx, async_saved_try_ctx,
outer_saved_try_ctx, outer_async_saved_try_ctx, current_block_->scope);
AwaitTransformer at(current_block_->statements, async_temp_scope_);
await_moveNext = at.Transform(await_moveNext);
SequenceNode* await_preamble = CloseBlock();
// Parse the for loop body. Ideally, we would use ParseNestedStatement()
// here, but that does not work well because we have to insert an implicit
// variable assignment and potentially a variable declaration in the
// loop body.
OpenLoopBlock();
SourceLabel* label =
SourceLabel::New(await_for_pos, label_name, SourceLabel::kFor);
current_block_->scope->AddLabel(label);
const TokenPosition loop_var_assignment_pos = TokenPos();
AstNode* iterator_current = new (Z) InstanceGetterNode(
loop_var_assignment_pos,
new (Z) LoadLocalNode(loop_var_assignment_pos, iterator_var),
Symbols::Current());
// Generate assignment of next iterator value to loop variable.
AstNode* loop_var_assignment = NULL;
if (new_loop_var) {
// The for loop variable is new for each iteration.
// Create a variable and add it to the loop body scope.
// Note that the variable token position needs to be inside the
// loop block, so it gets put in the loop context level.
LocalVariable* loop_var =
new (Z) LocalVariable(loop_var_assignment_pos, loop_var_assignment_pos,
*loop_var_name, loop_var_type);
if (loop_var_is_final) {
loop_var->set_is_final();
}
current_block_->scope->AddVariable(loop_var);
loop_var_assignment = new (Z)
StoreLocalNode(loop_var_assignment_pos, loop_var, iterator_current);
} else {
AstNode* loop_var_primary =
ResolveIdent(loop_var_pos, *loop_var_name, false);
ASSERT(!loop_var_primary->IsPrimaryNode());
loop_var_assignment =
CreateAssignmentNode(loop_var_primary, iterator_current, loop_var_name,
loop_var_assignment_pos);
ASSERT(loop_var_assignment != NULL);
}
current_block_->statements->Add(loop_var_assignment);
// Now parse the for-in loop statement or block.
if (CurrentToken() == Token::kLBRACE) {
ConsumeToken();
ParseStatementSequence();
ExpectToken(Token::kRBRACE);
} else {
AstNode* statement = ParseStatement();
if (statement != NULL) {
current_block_->statements->Add(statement);
}
}
SequenceNode* for_loop_block = CloseBlock();
WhileNode* while_node = new (Z) WhileNode(
await_for_pos, label, await_moveNext, await_preamble, for_loop_block);
// Add the while loop to the try block.
current_block_->statements->Add(while_node);
SequenceNode* try_block = CloseBlock();
// Create an empty "catch all" block that rethrows the current
// exception and stacktrace.
try_stack_->enter_catch();
SequenceNode* catch_block = new (Z) SequenceNode(await_for_pos, NULL);
if (outer_saved_try_ctx != NULL) {
catch_block->Add(new (Z) StoreLocalNode(
TokenPosition::kNoSource, outer_saved_try_ctx,
new (Z) LoadLocalNode(TokenPosition::kNoSource,
outer_async_saved_try_ctx)));
}
// We don't need to copy the current exception and stack trace variables
// into :saved_exception_var and :saved_stack_trace_var here because there
// is no code in the catch clause that could suspend the function.
// Rethrow the exception.
catch_block->Add(new (Z) ThrowNode(
await_for_pos, new (Z) LoadLocalNode(await_for_pos, exception_var),
new (Z) LoadLocalNode(await_for_pos, stack_trace_var)));
TryStack* try_statement = PopTry();
const intptr_t try_index = try_statement->try_index();
TryStack* outer_try = try_stack_;
const intptr_t outer_try_index = (outer_try != NULL)
? outer_try->try_index()
: CatchClauseNode::kInvalidTryIndex;
// The finally block contains a call to cancel the stream.
// :for-in-iter.cancel();
// Inline the finally block to the exit points in the try block.
intptr_t node_index = 0;
SequenceNode* finally_clause = NULL;
if (try_stack_ != NULL) {
try_stack_->enter_finally();
}
do {
OpenBlock();
// Restore the saved try context of the enclosing try block if one
// exists.
if (outer_saved_try_ctx != NULL) {
current_block_->statements->Add(new (Z) StoreLocalNode(
TokenPosition::kNoSource, outer_saved_try_ctx,
new (Z) LoadLocalNode(TokenPosition::kNoSource,
outer_async_saved_try_ctx)));
}
// :for-in-iter.cancel();
ArgumentListNode* no_args =
new (Z) ArgumentListNode(TokenPosition::kNoSource);
current_block_->statements->Add(new (Z) InstanceCallNode(
ST(await_for_pos),
new (Z) LoadLocalNode(TokenPosition::kNoSource, iterator_var),
Symbols::Cancel(), no_args));
finally_clause = CloseBlock();
AstNode* node_to_inline = try_statement->GetNodeToInlineFinally(node_index);
if (node_to_inline != NULL) {
InlinedFinallyNode* node =
new (Z) InlinedFinallyNode(TokenPosition::kNoSource, finally_clause,
context_var, outer_try_index);
finally_clause = NULL;
AddFinallyClauseToNode(true, node_to_inline, node);
node_index++;
}
} while (finally_clause == NULL);
if (try_stack_ != NULL) {
try_stack_->exit_finally();
}
// Create the try-statement and add to the current sequence, which is
// the block around the loop statement.
const Array& handler_types = Array::ZoneHandle(Z, Array::New(1, Heap::kOld));
// Catch block handles all exceptions.
handler_types.SetAt(0, Object::dynamic_type());
CatchClauseNode* catch_clause = new (Z) CatchClauseNode(
await_for_pos, catch_block, handler_types, context_var, exception_var,
stack_trace_var, exception_var, stack_trace_var, AllocateTryIndex(),
true); // Needs stack trace.
AstNode* try_catch_node =
new (Z) TryCatchNode(await_for_pos, try_block, context_var, catch_clause,
finally_clause, try_index, finally_clause);
ASSERT(current_block_ == await_for_block);
await_for_block->statements->Add(try_catch_node);
return CloseBlock(); // Implicit block around while loop.
}
AstNode* Parser::ParseForInStatement(TokenPosition forin_pos,
SourceLabel* label) {
TRACE_PARSER("ParseForInStatement");
bool loop_var_is_final = (CurrentToken() == Token::kFINAL);
if (CurrentToken() == Token::kCONST) {
ReportError("Loop variable cannot be 'const'");
}
const String* loop_var_name = NULL;
TokenPosition loop_var_pos = TokenPosition::kNoSource;
bool new_loop_var = false;
AbstractType& loop_var_type = AbstractType::ZoneHandle(Z);
if (LookaheadToken(1) == Token::kIN) {
loop_var_pos = TokenPos();
loop_var_name = ExpectIdentifier("variable name expected");
} else {
// The case without a type is handled above, so require a type here.
// Delay creation of the local variable until we know its actual
// position, which is inside the loop body.
new_loop_var = true;
loop_var_type = ParseConstFinalVarOrType(I->type_checks()
? ClassFinalizer::kCanonicalize
: ClassFinalizer::kIgnore);
loop_var_pos = TokenPos();
loop_var_name = ExpectIdentifier("variable name expected");
}
ExpectToken(Token::kIN);
// Ensure that the block token range contains the call to moveNext and it
// also starts the block at a different token position than the following
// loop block. Both blocks can allocate contexts and if they have a matching
// token position range, it can be an issue (cf. bug 26941).
OpenBlock(); // Implicit block around while loop.
const TokenPosition collection_pos = TokenPos();
AstNode* collection_expr =
ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
ExpectToken(Token::kRPAREN);
// Generate implicit iterator variable and add to scope.
// We could set the type of the implicit iterator variable to Iterator<T>
// where T is the type of the for loop variable. However, the type error
// would refer to the compiler generated iterator and could confuse the user.
// It is better to leave the iterator untyped and postpone the type error
// until the loop variable is assigned to.
const AbstractType& iterator_type = Object::dynamic_type();
LocalVariable* iterator_var = new (Z) LocalVariable(
collection_pos, collection_pos, Symbols::ForInIter(), iterator_type);
current_block_->scope->AddVariable(iterator_var);
// Generate initialization of iterator variable.
ArgumentListNode* no_args = new (Z) ArgumentListNode(collection_pos);
AstNode* get_iterator = new (Z)
InstanceGetterNode(collection_pos, collection_expr, Symbols::Iterator());
AstNode* iterator_init =
new (Z) StoreLocalNode(collection_pos, iterator_var, get_iterator);
current_block_->statements->Add(iterator_init);
// Generate while loop condition.
AstNode* iterator_moveNext = new (Z) InstanceCallNode(
collection_pos, new (Z) LoadLocalNode(collection_pos, iterator_var),
Symbols::MoveNext(), no_args);
// Parse the for loop body. Ideally, we would use ParseNestedStatement()
// here, but that does not work well because we have to insert an implicit
// variable assignment and potentially a variable declaration in the
// loop body.
OpenLoopBlock();
current_block_->scope->AddLabel(label);
const TokenPosition loop_var_assignment_pos = TokenPos();
AstNode* iterator_current = new (Z) InstanceGetterNode(
loop_var_assignment_pos,
new (Z) LoadLocalNode(loop_var_assignment_pos, iterator_var),
Symbols::Current());
// Generate assignment of next iterator value to loop variable.
AstNode* loop_var_assignment = NULL;
if (new_loop_var) {
// The for loop variable is new for each iteration.
// Create a variable and add it to the loop body scope.
LocalVariable* loop_var = new (Z) LocalVariable(
loop_var_pos, loop_var_assignment_pos, *loop_var_name, loop_var_type);
if (loop_var_is_final) {
loop_var->set_is_final();
}
current_block_->scope->AddVariable(loop_var);
loop_var_assignment = new (Z)
StoreLocalNode(loop_var_assignment_pos, loop_var, iterator_current);
} else {
AstNode* loop_var_primary =
ResolveIdent(loop_var_pos, *loop_var_name, false);
ASSERT(!loop_var_primary->IsPrimaryNode());
loop_var_assignment =
CreateAssignmentNode(loop_var_primary, iterator_current, loop_var_name,
loop_var_assignment_pos);
ASSERT(loop_var_assignment != NULL);
}
current_block_->statements->Add(loop_var_assignment);
// Now parse the for-in loop statement or block.
if (CurrentToken() == Token::kLBRACE) {
ConsumeToken();
ParseStatementSequence();
ExpectToken(Token::kRBRACE);
} else {
AstNode* statement = ParseStatement();
if (statement != NULL) {
current_block_->statements->Add(statement);
}
}
SequenceNode* for_loop_statement = CloseBlock();
AstNode* while_statement = new (Z)
WhileNode(forin_pos, label, iterator_moveNext, NULL, for_loop_statement);
current_block_->statements->Add(while_statement);
return CloseBlock(); // Implicit block around while loop.
}
AstNode* Parser::ParseForStatement(String* label_name) {
TRACE_PARSER("ParseForStatement");
const TokenPosition for_pos = TokenPos();
ConsumeToken();
ExpectToken(Token::kLPAREN);
SourceLabel* label = SourceLabel::New(for_pos, label_name, SourceLabel::kFor);
if (IsForInStatement()) {
return ParseForInStatement(for_pos, label);
}
// Open a block that contains the loop variable. Make it a loop block so
// that we allocate a new context if the loop variable is captured.
OpenLoopBlock();
AstNode* initializer = NULL;
const TokenPosition init_pos = TokenPos();
LocalScope* init_scope = current_block_->scope;
if (CurrentToken() != Token::kSEMICOLON) {
if (IsVariableDeclaration()) {
initializer = ParseVariableDeclarationList();
} else {
initializer = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
}
}
ExpectSemicolon();
AstNode* condition = NULL;
SequenceNode* condition_preamble = NULL;
if (CurrentToken() != Token::kSEMICOLON) {
condition =
ParseAwaitableExpr(kAllowConst, kConsumeCascades, &condition_preamble);
}
ExpectSemicolon();
AstNode* increment = NULL;
const TokenPosition incr_pos = TokenPos();
if (CurrentToken() != Token::kRPAREN) {
increment = ParseAwaitableExprList();
}
ExpectToken(Token::kRPAREN);
const bool parsing_loop_body = true;
SequenceNode* body = ParseNestedStatement(parsing_loop_body, label);
// Check whether any of the variables in the initializer part of
// the for statement are captured by a closure. If so, we insert a
// node that creates a new Context for the loop variable before
// the increment expression is evaluated.
for (int i = 0; i < init_scope->num_variables(); i++) {
if (init_scope->VariableAt(i)->is_captured() &&
(init_scope->VariableAt(i)->owner() == init_scope)) {
SequenceNode* incr_sequence = new (Z) SequenceNode(incr_pos, NULL);
incr_sequence->Add(new (Z) CloneContextNode(for_pos, init_scope));
if (increment != NULL) {
incr_sequence->Add(increment);
}
increment = incr_sequence;
break;
}
}
AstNode* for_node = new (Z)
ForNode(for_pos, label, NodeAsSequenceNode(init_pos, initializer, NULL),
condition, condition_preamble,
NodeAsSequenceNode(incr_pos, increment, NULL), body);
current_block_->statements->Add(for_node);
return CloseBlock();
}
// Calling VM-internal helpers, uses implementation core library.
AstNode* Parser::MakeStaticCall(const String& cls_name,
const String& func_name,
ArgumentListNode* arguments) {
const Class& cls = Class::Handle(Z, Library::LookupCoreClass(cls_name));
ASSERT(!cls.IsNull());
const intptr_t kTypeArgsLen = 0; // Not passing type args to generic func.
const Function& func = Function::ZoneHandle(
Z, Resolver::ResolveStatic(cls, func_name, kTypeArgsLen,
arguments->length(), arguments->names()));
ASSERT(!func.IsNull());
return new (Z) StaticCallNode(arguments->token_pos(), func, arguments,
StaticCallNode::kStatic);
}
AstNode* Parser::ParseAssertStatement(bool is_const) {
TRACE_PARSER("ParseAssertStatement");
ConsumeToken(); // Consume assert keyword.
ExpectToken(Token::kLPAREN);
const TokenPosition condition_pos = TokenPos();
if (!I->asserts()) {
SkipExpr();
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
if (CurrentToken() != Token::kRPAREN) {
SkipExpr();
if (CurrentToken() == Token::kCOMMA) {
// Allow trailing comma.
ConsumeToken();
}
}
}
ExpectToken(Token::kRPAREN);
return 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 (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
if (CurrentToken() != Token::kRPAREN) {
message_pos = TokenPos();
message = ParseExpr(kAllowConst, kConsumeCascades);
if (is_const && !message->IsPotentiallyConst()) {
ReportError(
message_pos,
"initializer assert expression must be compile time constant.");
}
if (CurrentToken() == Token::kCOMMA) {
// Allow trailing comma.
ConsumeToken();
}
}
}
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(new (Z) LiteralNode(
condition_pos,
Integer::ZoneHandle(Z, Integer::New(condition_pos.Pos()))));
arguments->Add(new (Z) LiteralNode(
condition_end,
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(Symbols::ThrowNew()), arguments);
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
// explicitly 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;
}
// Populate local scope of the catch block with the catch parameters.
void Parser::AddCatchParamsToScope(CatchParamDesc* exception_param,
CatchParamDesc* stack_trace_param,
LocalScope* scope) {
if (exception_param->name != NULL) {
LocalVariable* var = new (Z)
LocalVariable(exception_param->token_pos, exception_param->token_pos,
*exception_param->name, *exception_param->type);
var->set_is_final();
bool added_to_scope = scope->AddVariable(var);
ASSERT(added_to_scope);
exception_param->var = var;
}
if (stack_trace_param->name != NULL) {
LocalVariable* var = new (Z) LocalVariable(
stack_trace_param->token_pos, stack_trace_param->token_pos,
*stack_trace_param->name, *stack_trace_param->type);
var->set_is_final();
bool added_to_scope = scope->AddVariable(var);
if (!added_to_scope) {
// The name of the exception param is reused for the stack trace param.
ReportError(stack_trace_param->token_pos,
"name '%s' already exists in scope",
stack_trace_param->name->ToCString());
}
stack_trace_param->var = var;
}
}
// Generate code to load the exception object (:exception_var) into
// the saved exception variable (:saved_exception_var) used to rethrow.
// Generate code to load the stack trace object (:stack_trace_var) into
// the saved stacktrace variable (:saved_stack_trace_var) used to rethrow.
void Parser::SaveExceptionAndStackTrace(SequenceNode* statements,
LocalVariable* exception_var,
LocalVariable* stack_trace_var,
LocalVariable* saved_exception_var,
LocalVariable* saved_stack_trace_var) {
ASSERT(innermost_function().IsAsyncClosure() ||
innermost_function().IsAsyncFunction() ||
innermost_function().IsSyncGenClosure() ||
innermost_function().IsSyncGenerator() ||
innermost_function().IsAsyncGenClosure() ||
innermost_function().IsAsyncGenerator());
ASSERT(saved_exception_var != NULL);
ASSERT(exception_var != NULL);
statements->Add(new (Z) StoreLocalNode(
TokenPosition::kNoSource, saved_exception_var,
new (Z) LoadLocalNode(TokenPosition::kNoSource, exception_var)));
ASSERT(saved_stack_trace_var != NULL);
ASSERT(stack_trace_var != NULL);
statements->Add(new (Z) StoreLocalNode(
TokenPosition::kNoSource, saved_stack_trace_var,
new (Z) LoadLocalNode(TokenPosition::kNoSource, stack_trace_var)));
}
SequenceNode* Parser::EnsureFinallyClause(
bool parse,
bool is_async,
LocalVariable* exception_var,
LocalVariable* stack_trace_var,
LocalVariable* rethrow_exception_var,
LocalVariable* rethrow_stack_trace_var) {
TRACE_PARSER("EnsureFinallyClause");
ASSERT(parse || (is_async && (try_stack_ != NULL)));
// Increasing the loop level prevents the reuse of a parent context and forces
// the allocation of a local context to hold captured variables declared
// inside the finally clause. Otherwise, a captured variable gets allocated at
// different slots in the parent context each time the finally clause is
// reparsed, which is done to duplicate the ast. Since only one closure is
// kept due to canonicalization, it will access the correct slot in only one
// copy of the finally clause and the wrong slot in all others. By allocating
// a local context, all copies use the same slot in different local contexts.
// See issue #26948. This is a temporary fix until we eliminate reparsing.
OpenLoopBlock();
if (parse) {
ExpectToken(Token::kLBRACE);
}
if (try_stack_ != NULL) {
try_stack_->enter_finally();
}
// In case of async closures we need to restore the saved try context of an
// outer try block (if it exists). The current try block has already been
// removed from the stack of try blocks.
if (is_async) {
if (try_stack_ != NULL) {
LocalScope* scope = try_stack_->try_block()->scope;
if (scope->function_level() == current_block_->scope->function_level()) {
LocalVariable* saved_try_ctx =
LookupSavedTryContextVar(scope->parent());
LocalVariable* async_saved_try_ctx = LookupAsyncSavedTryContextVar(
T, async_temp_scope_, try_stack_->try_index());
current_block_->statements->Add(new (Z) StoreLocalNode(
TokenPosition::kNoSource, saved_try_ctx,
new (Z)
LoadLocalNode(TokenPosition::kNoSource, async_saved_try_ctx)));
}
}
// We need to save the exception variables as in catch clauses, whether
// there is an outer try or not. Note that this is only necessary if the
// finally clause contains an await or yield.
// TODO(hausner): Optimize.
SaveExceptionAndStackTrace(current_block_->statements, exception_var,
stack_trace_var, rethrow_exception_var,
rethrow_stack_trace_var);
}
if (parse) {
ParseStatementSequence();
ExpectToken(Token::kRBRACE);
}
SequenceNode* finally_clause = CloseBlock();
if (try_stack_ != NULL) {
try_stack_->exit_finally();
}
return finally_clause;
}
void Parser::PushTry(Block* try_block) {
intptr_t try_index = AllocateTryIndex();
try_stack_ = new (Z) TryStack(try_block, try_stack_, try_index);
}
Parser::TryStack* Parser::PopTry() {
TryStack* innermost_try = try_stack_;
try_stack_ = try_stack_->outer_try();
return innermost_try;
}
void Parser::AddNodeForFinallyInlining(AstNode* node) {
if (node == NULL) {
return;
}
ASSERT(node->IsReturnNode() || node->IsJumpNode());
const intptr_t func_level = FunctionLevel();
TryStack* iterator = try_stack_;
while ((iterator != NULL) &&
(iterator->try_block()->scope->function_level() == func_level)) {
// For continue and break node check if the target label is in scope.
if (node->IsJumpNode()) {
SourceLabel* label = node->AsJumpNode()->label();
ASSERT(label != NULL);
LocalScope* try_scope = iterator->try_block()->scope;
// If the label is defined in a scope which is a child (nested scope)
// of the try scope then we are not breaking out of this try block
// so we do not need to inline the finally code. Otherwise we need
// to inline the finally code of this try block and then move on to the
// next outer try block.
// For unresolved forward jumps to switch cases, we don't yet know
// to which scope the label will be resolved. Tentatively add the
// jump to all nested try statements and remove the outermost ones
// when we know the exact jump target. (See
// RemoveNodesForFinallyInlining below.)
if (!label->IsUnresolved() && label->owner()->IsNestedWithin(try_scope)) {
break;
}
}
iterator->AddNodeForFinallyInlining(node);
iterator = iterator->outer_try();
}
}
void Parser::RemoveNodesForFinallyInlining(SourceLabel* label) {
TryStack* iterator = try_stack_;
const intptr_t func_level = FunctionLevel();
while ((iterator != NULL) &&
(iterator->try_block()->scope->function_level() == func_level)) {
iterator->RemoveJumpToLabel(label);
iterator = iterator->outer_try();
}
}
// Add the inlined finally clause to the specified node.
void Parser::AddFinallyClauseToNode(bool is_async,
AstNode* node,
InlinedFinallyNode* finally_clause) {
ReturnNode* return_node = node->AsReturnNode();
if (return_node != NULL) {
if (FunctionLevel() == 0) {
parsed_function()->EnsureFinallyReturnTemp(is_async);
}
return_node->AddInlinedFinallyNode(finally_clause);
return;
}
JumpNode* jump_node = node->AsJumpNode();
ASSERT(jump_node != NULL);
jump_node->AddInlinedFinallyNode(finally_clause);
}
SequenceNode* Parser::ParseCatchClauses(
TokenPosition handler_pos,
bool is_async,
LocalVariable* exception_var,
LocalVariable* stack_trace_var,
LocalVariable* rethrow_exception_var,
LocalVariable* rethrow_stack_trace_var,
const GrowableObjectArray& handler_types,
bool* needs_stack_trace) {
// All catch blocks are merged into an if-then-else sequence of the
// different types specified using the 'is' operator. While parsing
// record the type tests (either a ComparisonNode or else the LiteralNode
// true for a generic catch) and the catch bodies in a pair of parallel
// lists. Afterward, construct the nested if-then-else.
bool generic_catch_seen = false;
GrowableArray<AstNode*> type_tests;
GrowableArray<SequenceNode*> catch_blocks;
while ((CurrentToken() == Token::kCATCH) || IsSymbol(Symbols::On())) {
// Open a block that contains the if or an unconditional body. It's
// closed in the loop that builds the if-then-else nest.
OpenBlock();
const TokenPosition catch_pos = TokenPos();
CatchParamDesc exception_param;
CatchParamDesc stack_trace_param;
if (IsSymbol(Symbols::On())) {
ConsumeToken();
exception_param.type = &AbstractType::ZoneHandle(
Z, ParseTypeOrFunctionType(false, ClassFinalizer::kCanonicalize));
} else {
exception_param.type = &Object::dynamic_type();
}
if (CurrentToken() == Token::kCATCH) {
ConsumeToken(); // Consume the 'catch'.
ExpectToken(Token::kLPAREN);
exception_param.token_pos = TokenPos();
exception_param.name = ExpectIdentifier("identifier expected");
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
stack_trace_param.type = &Object::dynamic_type();
stack_trace_param.token_pos = TokenPos();
stack_trace_param.name = ExpectIdentifier("identifier expected");
}
ExpectToken(Token::kRPAREN);
}
// Create a block containing the catch clause parameters and the
// following code:
// 1) Store exception object and stack trace object into user-defined
// variables (as needed).
// 2) In async code, save exception object and stack trace object into
// captured :saved_exception_var and :saved_stack_trace_var.
// 3) Nested block with source code from catch clause block.
OpenBlock();
AddCatchParamsToScope(&exception_param, &stack_trace_param,
current_block_->scope);
if (exception_param.var != NULL) {
// Generate code to load the exception object (:exception_var) into
// the exception variable specified in this block.
ASSERT(exception_var != NULL);
current_block_->statements->Add(new (Z) StoreLocalNode(
catch_pos, exception_param.var,
new (Z) LoadLocalNode(catch_pos, exception_var)));
}
if (stack_trace_param.var != NULL) {
// A stack trace variable is specified in this block, so generate code
// to load the stack trace object (:stack_trace_var) into the stack
// trace variable specified in this block.
*needs_stack_trace = true;
ASSERT(stack_trace_var != NULL);
current_block_->statements->Add(new (Z) StoreLocalNode(
catch_pos, stack_trace_param.var,
new (Z) LoadLocalNode(catch_pos, stack_trace_var)));
}
// Add nested block with user-defined code. This block allows
// declarations in the body to shadow the catch parameters.
CheckToken(Token::kLBRACE);
current_block_->statements->Add(ParseNestedStatement(false, NULL));
catch_blocks.Add(CloseBlock());
const bool is_bad_type = exception_param.type->IsMalformed() ||
exception_param.type->IsMalbounded();
if (exception_param.type->IsDynamicType() || is_bad_type) {
// There is no exception type or else it is malformed or malbounded.
// In the first case, unconditionally execute the catch body. In the
// second case, unconditionally throw.
generic_catch_seen = true;
type_tests.Add(new (Z) LiteralNode(catch_pos, Bool::True()));
if (is_bad_type) {
// Replace the body with one that throws.
SequenceNode* block = new (Z) SequenceNode(catch_pos, NULL);
block->Add(ThrowTypeError(catch_pos, *exception_param.type));
catch_blocks.Last() = block;
}
// This catch clause will handle all exceptions. We can safely forget
// all previous catch clause types.
handler_types.SetLength(0);
handler_types.Add(*exception_param.type, Heap::kOld);
} else {
// Has a type specification that is not malformed or malbounded. Now
// form an 'if type check' to guard the catch handler code.
if (!exception_param.type->IsInstantiated() && (FunctionLevel() > 0)) {
// Make sure that the instantiators are captured.
CaptureAllInstantiators();
}
TypeNode* exception_type =
new (Z) TypeNode(catch_pos, *exception_param.type);
AstNode* exception_value =
new (Z) LoadLocalNode(catch_pos, exception_var);
if (!exception_type->type().IsInstantiated()) {
EnsureExpressionTemp();
}
type_tests.Add(new (Z) ComparisonNode(catch_pos, Token::kIS,
exception_value, exception_type));
// Do not add uninstantiated types (e.g. type parameter T or generic
// type List<T>), since the debugger won't be able to instantiate it
// when walking the stack.
//
// This means that the debugger is not able to determine whether an
// exception is caught if the catch clause uses generic types. It
// will report the exception as uncaught when in fact it might be
// caught and handled when we unwind the stack.
if (!generic_catch_seen && exception_param.type->IsInstantiated()) {
handler_types.Add(*exception_param.type, Heap::kOld);
}
}
ASSERT(type_tests.length() == catch_blocks.length());
}
// Build the if/then/else nest from the inside out. Keep the AST simple
// for the case of a single generic catch clause. The initial value of
// current is the last (innermost) else block if there were any catch
// clauses.
SequenceNode* current = NULL;
if (!generic_catch_seen) {
// There isn't a generic catch clause so create a clause body that
// rethrows the exception. This includes the case that there were no
// catch clauses.
// An await cannot possibly be executed in between the catch entry and here,
// therefore, it is safe to rethrow the stack-based :exception_var instead
// of the captured copy :saved_exception_var.
current = new (Z) SequenceNode(handler_pos, NULL);
current->Add(new (Z) ThrowNode(
handler_pos, new (Z) LoadLocalNode(handler_pos, exception_var),
new (Z) LoadLocalNode(handler_pos, stack_trace_var)));
} else if (type_tests.Last()->IsLiteralNode()) {
ASSERT(type_tests.Last()->AsLiteralNode()->literal().raw() ==
Bool::True().raw());
// The last body is entered unconditionally. Start building the
// if/then/else nest with that body as the innermost else block.
// Note that it is nested inside an extra block which we opened
// before we knew the body was entered unconditionally.
type_tests.RemoveLast();
current_block_->statements->Add(catch_blocks.RemoveLast());
current = CloseBlock();
}
// If the last body was entered conditionally and there is no need to add
// a rethrow, use an empty else body (current = NULL above).
while (!type_tests.is_empty()) {
AstNode* type_test = type_tests.RemoveLast();
SequenceNode* catch_block = catch_blocks.RemoveLast();
current_block_->statements->Add(new (Z) IfNode(
type_test->token_pos(), type_test, catch_block, current));
current = CloseBlock();
}
// In case of async closures, restore :saved_try_context_var before executing
// the catch clauses.
if (is_async && (current != NULL)) {
ASSERT(try_stack_ != NULL);
SequenceNode* async_code = new (Z) SequenceNode(handler_pos, NULL);
const TryStack* try_block = try_stack_->outer_try();
if (try_block != NULL) {
LocalScope* scope = try_block->try_block()->scope;
if (scope->function_level() == current_block_->scope->function_level()) {
LocalVariable* saved_try_ctx =
LookupSavedTryContextVar(scope->parent());
LocalVariable* async_saved_try_ctx = LookupAsyncSavedTryContextVar(
T, async_temp_scope_, try_block->try_index());
async_code->Add(new (Z) StoreLocalNode(
TokenPosition::kNoSource, saved_try_ctx,
new (Z)
LoadLocalNode(TokenPosition::kNoSource, async_saved_try_ctx)));
}
}
SaveExceptionAndStackTrace(async_code, exception_var, stack_trace_var,
rethrow_exception_var, rethrow_stack_trace_var);
// The async_code node sequence contains code to restore the context (if
// an outer try block is present) and code to save the exception and
// stack trace variables.
// This async code is inserted before the current node sequence containing
// the chain of if/then/else handling all catch clauses.
async_code->Add(current);
current = async_code;
}
return current;
}
void Parser::SetupSavedTryContext(LocalVariable* saved_try_context) {
const String& async_saved_try_ctx_name = String::ZoneHandle(
Z, Symbols::NewFormatted(T, "%s%d",
Symbols::AsyncSavedTryCtxVarPrefix().ToCString(),
last_used_try_index_ - 1));
LocalVariable* async_saved_try_ctx =
new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
async_saved_try_ctx_name, Object::dynamic_type());
ASSERT(async_temp_scope_ != NULL);
async_temp_scope_->AddVariable(async_saved_try_ctx);
ASSERT(saved_try_context != NULL);
current_block_->statements->Add(new (Z) StoreLocalNode(
TokenPosition::kNoSource, async_saved_try_ctx,
new (Z) LoadLocalNode(TokenPosition::kNoSource, saved_try_context)));
}
// We create three variables for exceptions:
// ':saved_try_context_var' - Used to save the context before the start of
// the try block. The context register is
// restored from this variable before
// processing the catch block handler.
// ':exception_var' - Used to save the current exception object that was
// thrown.
// ':stack_trace_var' - Used to save the current stack trace object which
// the stack trace was copied into when an exception
// was thrown.
// :exception_var and :stack_trace_var get set with the exception object
// and the stack trace object when an exception is thrown. These three
// implicit variables can never be captured.
//
// In case of async code, we create two additional variables:
// ':saved_exception_var' - Used to capture the exception object above.
// ':saved_stack_trace_var' - Used to capture the stack trace object above.
void Parser::SetupExceptionVariables(LocalScope* try_scope,
bool is_async,
LocalVariable** context_var,
LocalVariable** exception_var,
LocalVariable** stack_trace_var,
LocalVariable** saved_exception_var,
LocalVariable** saved_stack_trace_var) {
// Consecutive try statements share the same set of variables.
*context_var = try_scope->LocalLookupVariable(Symbols::SavedTryContextVar());
if (*context_var == NULL) {
*context_var = new (Z)
LocalVariable(TokenPos(), TokenPos(), Symbols::SavedTryContextVar(),
Object::dynamic_type());
try_scope->AddVariable(*context_var);
}
*exception_var = try_scope->LocalLookupVariable(Symbols::ExceptionVar());
if (*exception_var == NULL) {
*exception_var =
new (Z) LocalVariable(TokenPos(), TokenPos(), Symbols::ExceptionVar(),
Object::dynamic_type());
try_scope->AddVariable(*exception_var);
}
*stack_trace_var = try_scope->LocalLookupVariable(Symbols::StackTraceVar());
if (*stack_trace_var == NULL) {
*stack_trace_var =
new (Z) LocalVariable(TokenPos(), TokenPos(), Symbols::StackTraceVar(),
Object::dynamic_type());
try_scope->AddVariable(*stack_trace_var);
}
if (is_async) {
*saved_exception_var =
try_scope->LocalLookupVariable(Symbols::SavedExceptionVar());
if (*saved_exception_var == NULL) {
*saved_exception_var = new (Z)
LocalVariable(TokenPos(), TokenPos(), Symbols::SavedExceptionVar(),
Object::dynamic_type());
try_scope->AddVariable(*saved_exception_var);
}
*saved_stack_trace_var =
try_scope->LocalLookupVariable(Symbols::SavedStackTraceVar());
if (*saved_stack_trace_var == NULL) {
*saved_stack_trace_var = new (Z)
LocalVariable(TokenPos(), TokenPos(), Symbols::SavedStackTraceVar(),
Object::dynamic_type());
try_scope->AddVariable(*saved_stack_trace_var);
}
}
}
AstNode* Parser::ParseTryStatement(String* label_name) {
TRACE_PARSER("ParseTryStatement");
const TokenPosition try_pos = TokenPos();
SourceLabel* try_label = NULL;
if (label_name != NULL) {
try_label = SourceLabel::New(try_pos, label_name, SourceLabel::kStatement);
OpenBlock();
current_block_->scope->AddLabel(try_label);
}
const bool is_async = innermost_function().IsAsyncClosure() ||
innermost_function().IsAsyncFunction() ||
innermost_function().IsSyncGenClosure() ||
innermost_function().IsSyncGenerator() ||
innermost_function().IsAsyncGenClosure() ||
innermost_function().IsAsyncGenerator();
LocalVariable* context_var = NULL;
LocalVariable* exception_var = NULL;
LocalVariable* stack_trace_var = NULL;
LocalVariable* saved_exception_var = NULL;
LocalVariable* saved_stack_trace_var = NULL;
SetupExceptionVariables(current_block_->scope, is_async, &context_var,
&exception_var, &stack_trace_var,
&saved_exception_var, &saved_stack_trace_var);
ConsumeToken(); // Consume the 'try'.
// Now parse the 'try' block.
OpenBlock();
PushTry(current_block_);
ExpectToken(Token::kLBRACE);
if (is_async) {
SetupSavedTryContext(context_var);
}
ParseStatementSequence();
ExpectToken(Token::kRBRACE);
SequenceNode* try_block = CloseBlock();
if ((CurrentToken() != Token::kCATCH) && !IsSymbol(Symbols::On()) &&
(CurrentToken() != Token::kFINALLY)) {
ReportError("catch or finally clause expected");
}
// Now parse the 'catch' blocks if any.
try_stack_->enter_catch();
const TokenPosition handler_pos = TokenPos();
const GrowableObjectArray& handler_types =
GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld));
bool needs_stack_trace = false;
SequenceNode* catch_handler_list =
ParseCatchClauses(handler_pos, is_async, exception_var, stack_trace_var,
is_async ? saved_exception_var : exception_var,
is_async ? saved_stack_trace_var : stack_trace_var,
handler_types, &needs_stack_trace);
TryStack* try_statement = PopTry();
const intptr_t try_index = try_statement->try_index();
TryStack* outer_try = try_stack_;
const intptr_t outer_try_index = (outer_try != NULL)
? outer_try->try_index()
: CatchClauseNode::kInvalidTryIndex;
// Finally, parse or generate the 'finally' clause.
// A finally clause is required in async code to restore the saved try context
// of an existing outer try. Generate a finally clause to this purpose if it
// is not declared.
SequenceNode* finally_clause = NULL;
SequenceNode* rethrow_clause = NULL;
const bool parse = CurrentToken() == Token::kFINALLY;
if (parse || (is_async && (try_stack_ != NULL))) {
if (parse) {
ConsumeToken(); // Consume the 'finally'.
}
const TokenPosition finally_pos = TokenPos();
// Add the finally block to the exit points recorded so far.
intptr_t node_index = 0;
AstNode* node_to_inline = try_statement->GetNodeToInlineFinally(node_index);
while (node_to_inline != NULL) {
finally_clause = EnsureFinallyClause(
parse, is_async, exception_var, stack_trace_var,
is_async ? saved_exception_var : exception_var,
is_async ? saved_stack_trace_var : stack_trace_var);
InlinedFinallyNode* node = new (Z) InlinedFinallyNode(
finally_pos, finally_clause, context_var, outer_try_index);
AddFinallyClauseToNode(is_async, node_to_inline, node);
node_index += 1;
node_to_inline = try_statement->GetNodeToInlineFinally(node_index);
tokens_iterator_.SetCurrentPosition(finally_pos);
}
finally_clause =
EnsureFinallyClause(parse, is_async, exception_var, stack_trace_var,
is_async ? saved_exception_var : exception_var,
is_async ? saved_stack_trace_var : stack_trace_var);
if (finally_clause != NULL) {
// Re-parse to create a duplicate of finally clause to avoid unintended
// sharing of try-indices if the finally-block contains a try-catch.
// The flow graph builder emits two copies of the finally-block if the
// try-block has a normal exit: one for the exception- and one for the
// non-exception case (see EffectGraphVisitor::VisitTryCatchNode)
tokens_iterator_.SetCurrentPosition(finally_pos);
rethrow_clause = EnsureFinallyClause(
parse, is_async, exception_var, stack_trace_var,
is_async ? saved_exception_var : exception_var,
is_async ? saved_stack_trace_var : stack_trace_var);
}
}
CatchClauseNode* catch_clause = new (Z) CatchClauseNode(
handler_pos, catch_handler_list,
Array::ZoneHandle(Z, Array::MakeFixedLength(handler_types)), context_var,
exception_var, stack_trace_var,
is_async ? saved_exception_var : exception_var,
is_async ? saved_stack_trace_var : stack_trace_var,
(finally_clause != NULL) ? AllocateTryIndex()
: CatchClauseNode::kInvalidTryIndex,
needs_stack_trace);
// Now create the try/catch ast node and return it. If there is a label
// on the try/catch, close the block that's embedding the try statement
// and attach the label to it.
AstNode* try_catch_node =
new (Z) TryCatchNode(try_pos, try_block, context_var, catch_clause,
finally_clause, try_index, rethrow_clause);
if (try_label != NULL) {
current_block_->statements->Add(try_catch_node);
SequenceNode* sequence = CloseBlock();
sequence->set_label(try_label);
try_catch_node = sequence;
}
return try_catch_node;
}
AstNode* Parser::ParseJump(String* label_name) {
TRACE_PARSER("ParseJump");
ASSERT(CurrentToken() == Token::kBREAK || CurrentToken() == Token::kCONTINUE);
Token::Kind jump_kind = CurrentToken();
const TokenPosition jump_pos = TokenPos();
SourceLabel* target = NULL;
ConsumeToken();
if (IsIdentifier()) {
// Explicit label after break/continue.
const String& target_name = *CurrentLiteral();
ConsumeToken();
// Handle pathological cases first.
if (label_name != NULL && target_name.Equals(*label_name)) {
if (jump_kind == Token::kCONTINUE) {
ReportError(jump_pos, "'continue' jump to label '%s' is illegal",
target_name.ToCString());
}
// L: break L; is a no-op.
return NULL;
}
target = current_block_->scope->LookupLabel(target_name);
if (target == NULL && jump_kind == Token::kCONTINUE) {
// Either a reference to a non-existent label, or a forward reference
// to a case label that we haven't seen yet. If we are inside a switch
// statement, create a "forward reference" label in the scope of
// the switch statement.
LocalScope* switch_scope = current_block_->scope->LookupSwitchScope();
if (switch_scope != NULL) {
// We found a switch scope. Enter a forward reference to the label.
target =
new (Z) SourceLabel(TokenPos(), target_name, SourceLabel::kForward);
switch_scope->AddLabel(target);
}
}
if (target == NULL) {
ReportError(jump_pos, "label '%s' not found", target_name.ToCString());
}
} else if (FLAG_enable_debug_break && (CurrentToken() == Token::kSTRING)) {
const char* message = Z->MakeCopyOfString(CurrentLiteral()->ToCString());
ConsumeToken();
return new (Z) StopNode(jump_pos, message);
} else {
target = current_block_->scope->LookupInnermostLabel(jump_kind);
if (target == NULL) {
ReportError(jump_pos, "'%s' is illegal here", Token::Str(jump_kind));
}
}
ASSERT(target != NULL);
if (jump_kind == Token::kCONTINUE) {
if (target->kind() == SourceLabel::kSwitch) {
ReportError(jump_pos, "'continue' jump to switch statement is illegal");
} else if (target->kind() == SourceLabel::kStatement) {
ReportError(jump_pos, "'continue' jump to label '%s' is illegal",
target->name().ToCString());
}
}
if (jump_kind == Token::kBREAK && target->kind() == SourceLabel::kCase) {
ReportError(jump_pos, "'break' to case clause label is illegal");
}
if (target->FunctionLevel() != FunctionLevel()) {
ReportError(jump_pos, "'%s' target must be in same function context",
Token::Str(jump_kind));
}
return new (Z) JumpNode(jump_pos, jump_kind, target);
}
AstNode* Parser::ParseYieldStatement() {
bool is_yield_each = false;
const TokenPosition yield_pos = TokenPos();
ConsumeToken(); // yield reserved word.
if (CurrentToken() == Token::kMUL) {
is_yield_each = true;
ConsumeToken();
}
if (!innermost_function().IsGenerator() &&
!innermost_function().IsGeneratorClosure()) {
ReportError(yield_pos,
"yield%s statement only allowed in generator functions",
is_yield_each ? "*" : "");
}
AstNode* expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
LetNode* yield = new (Z) LetNode(yield_pos);
if (innermost_function().IsSyncGenerator() ||
innermost_function().IsSyncGenClosure()) {
// Yield statement in sync* function.
LocalVariable* iterator_param =
LookupLocalScope(Symbols::IteratorParameter());
ASSERT(iterator_param != NULL);
AstNode* iterator =
new (Z) LoadLocalNode(TokenPosition::kNoSource, iterator_param);
if (is_yield_each) {
// Generate :iterator._yieldEachIterable = expr;
yield->AddNode(new (Z) InstanceSetterNode(
TokenPosition::kNoSource, iterator,
Library::PrivateCoreLibName(Symbols::_yieldEachIterable()), expr));
} else {
// Generate :iterator._current = expr;
yield->AddNode(new (Z) InstanceSetterNode(
TokenPosition::kNoSource, iterator,
Library::PrivateCoreLibName(Symbols::_current()), expr));
}
AwaitMarkerNode* await_marker = new (Z) AwaitMarkerNode(
async_temp_scope_, current_block_->scope, TokenPosition::kNoSource);
yield->AddNode(await_marker);
// Return true to indicate that a value has been generated.
ReturnNode* return_true = new (Z)
ReturnNode(yield_pos, new (Z) LiteralNode(TokenPos(), Bool::True()));
return_true->set_return_type(ReturnNode::kContinuationTarget);
yield->AddNode(return_true);
// If this expression is part of a try block, also append the code for
// restoring the saved try context that lives on the stack and possibly the
// saved try context of the outer try block.
LocalVariable* saved_try_ctx;
LocalVariable* async_saved_try_ctx;
LocalVariable* outer_saved_try_ctx;
LocalVariable* outer_async_saved_try_ctx;
CheckAsyncOpInTryBlock(&saved_try_ctx, &async_saved_try_ctx,
&outer_saved_try_ctx, &outer_async_saved_try_ctx);
if (saved_try_ctx != NULL) {
yield->AddNode(new (Z) StoreLocalNode(
TokenPosition::kNoSource, saved_try_ctx,
new (Z)
LoadLocalNode(TokenPosition::kNoSource, async_saved_try_ctx)));
if (outer_saved_try_ctx != NULL) {
yield->AddNode(new (Z) StoreLocalNode(
TokenPosition::kNoSource, outer_saved_try_ctx,
new (Z) LoadLocalNode(TokenPosition::kNoSource,
outer_async_saved_try_ctx)));
}
} else {
ASSERT(outer_saved_try_ctx == NULL);
}
} else {
// yield statement in async* function.
ASSERT(innermost_function().IsAsyncGenerator() ||
innermost_function().IsAsyncGenClosure());
LocalVariable* controller_var =
LookupLocalScope(Symbols::ColonController());
ASSERT(controller_var != NULL);
// :controller.add[Stream](expr);
ArgumentListNode* add_args = new (Z) ArgumentListNode(yield_pos);
add_args->Add(expr);
AstNode* add_call = new (Z) InstanceCallNode(
yield_pos,
new (Z) LoadLocalNode(TokenPosition::kNoSource, controller_var),
is_yield_each ? Symbols::AddStream() : Symbols::add(), add_args);
// if (:controller.add[Stream](expr)) {
// return;
// }
// await_marker;
// continuation_return;
// restore saved_try_context
SequenceNode* true_branch =
new (Z) SequenceNode(TokenPosition::kNoSource, NULL);
AstNode* return_from_generator = new (Z) ReturnNode(yield_pos);
true_branch->Add(return_from_generator);
AddNodeForFinallyInlining(return_from_generator);
AstNode* if_is_cancelled =
new (Z) IfNode(TokenPosition::kNoSource, add_call, true_branch, NULL);
yield->AddNode(if_is_cancelled);
AwaitMarkerNode* await_marker = new (Z) AwaitMarkerNode(
async_temp_scope_, current_block_->scope, TokenPosition::kNoSource);
yield->AddNode(await_marker);
ReturnNode* continuation_return = new (Z) ReturnNode(yield_pos);
continuation_return->set_return_type(ReturnNode::kContinuationTarget);
yield->AddNode(continuation_return);
// If this expression is part of a try block, also append the code for
// restoring the saved try context that lives on the stack and possibly the
// saved try context of the outer try block.
LocalVariable* saved_try_ctx;
LocalVariable* async_saved_try_ctx;
LocalVariable* outer_saved_try_ctx;
LocalVariable* outer_async_saved_try_ctx;
CheckAsyncOpInTryBlock(&saved_try_ctx, &async_saved_try_ctx,
&outer_saved_try_ctx, &outer_async_saved_try_ctx);
if (saved_try_ctx != NULL) {
yield->AddNode(new (Z) StoreLocalNode(
TokenPosition::kNoSource, saved_try_ctx,
new (Z)
LoadLocalNode(TokenPosition::kNoSource, async_saved_try_ctx)));
if (outer_saved_try_ctx != NULL) {
yield->AddNode(new (Z) StoreLocalNode(
TokenPosition::kNoSource, outer_saved_try_ctx,
new (Z) LoadLocalNode(TokenPosition::kNoSource,
outer_async_saved_try_ctx)));
}
} else {
ASSERT(outer_saved_try_ctx == NULL);
}
}
return yield;
}
AstNode* Parser::ParseStatement() {
TRACE_PARSER("ParseStatement");
AstNode* statement = NULL;
TokenPosition label_pos = TokenPosition::kNoSource;
String* label_name = NULL;
if (IsIdentifier()) {
if (LookaheadToken(1) == Token::kCOLON) {
// Statement starts with a label.
label_name = CurrentLiteral();
label_pos = TokenPos();
ASSERT(label_pos.IsReal());
ConsumeToken(); // Consume identifier.
ConsumeToken(); // Consume colon.
}
}
const TokenPosition statement_pos = TokenPos();
const Token::Kind token = CurrentToken();
if (token == Token::kWHILE) {
statement = ParseWhileStatement(label_name);
} else if (token == Token::kFOR) {
statement = ParseForStatement(label_name);
} else if (IsAwaitKeyword() && (LookaheadToken(1) == Token::kFOR)) {
statement = ParseAwaitForStatement(label_name);
} else if (token == Token::kDO) {
statement = ParseDoWhileStatement(label_name);
} else if (token == Token::kSWITCH) {
statement = ParseSwitchStatement(label_name);
} else if (token == Token::kTRY) {
statement = ParseTryStatement(label_name);
} else if (token == Token::kRETURN) {
const TokenPosition return_pos = TokenPos();
ConsumeToken();
if (CurrentToken() != Token::kSEMICOLON) {
const TokenPosition expr_pos = TokenPos();
const int function_level = FunctionLevel();
if (current_function().IsGenerativeConstructor() &&
(function_level == 0)) {
ReportError(expr_pos,
"return of a value is not allowed in constructors");
} else if (current_function().IsGeneratorClosure() &&
(function_level == 0)) {
ReportError(expr_pos, "generator functions may not return a value");
}
AstNode* expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
expr = AddAsyncResultTypeCheck(expr_pos, expr);
statement = new (Z) ReturnNode(statement_pos, expr);
} else {
if (current_function().IsSyncGenClosure() && (FunctionLevel() == 0)) {
// In a synchronous generator, return without an expression
// returns false, signaling that the iterator terminates and
// did not yield a value.
statement = new (Z) ReturnNode(
statement_pos, new (Z) LiteralNode(return_pos, Bool::False()));
} else {
statement = new (Z) ReturnNode(statement_pos);
}
}
AddNodeForFinallyInlining(statement);
ExpectSemicolon();
} else if (IsYieldKeyword()) {
statement = ParseYieldStatement();
ExpectSemicolon();
} else if (token == Token::kIF) {
statement = ParseIfStatement(label_name);
} else if (token == Token::kASSERT) {
statement = ParseAssertStatement();
ExpectSemicolon();
} else if (IsVariableDeclaration()) {
statement = ParseVariableDeclarationList();
ExpectSemicolon();
} else if (IsFunctionDeclaration()) {
statement = ParseFunctionStatement(false);
} else if (token == Token::kLBRACE) {
SourceLabel* label = NULL;
OpenBlock();
if (label_name != NULL) {
label = SourceLabel::New(label_pos, label_name, SourceLabel::kStatement);
current_block_->scope->AddLabel(label);
}
ConsumeToken();
ParseStatementSequence();
statement = CloseBlock();
if (label != NULL) {
statement->AsSequenceNode()->set_label(label);
}
ExpectToken(Token::kRBRACE);
} else if (token == Token::kBREAK) {
statement = ParseJump(label_name);
if ((statement != NULL) && !statement->IsStopNode()) {
AddNodeForFinallyInlining(statement);
}
ExpectSemicolon();
} else if (token == Token::kCONTINUE) {
statement = ParseJump(label_name);
AddNodeForFinallyInlining(statement);
ExpectSemicolon();
} else if (token == Token::kSEMICOLON) {
// Empty statement, nothing to do.
ConsumeToken();
} else if (token == Token::kRETHROW) {
// Rethrow of current exception.
ConsumeToken();
ExpectSemicolon();
// Check if it is ok to do a rethrow. Find the innermost enclosing
// catch block.
TryStack* try_statement = try_stack_;
while ((try_statement != NULL) && !try_statement->inside_catch()) {
try_statement = try_statement->outer_try();
}
if (try_statement == NULL) {
ReportError(statement_pos, "rethrow of an exception is not valid here");
}
// If in async code, use :saved_exception_var and :saved_stack_trace_var
// instead of :exception_var and :stack_trace_var.
// These variables are bound in the block containing the try.
// Look in the try scope directly.
LocalScope* scope = try_statement->try_block()->scope->parent();
ASSERT(scope != NULL);
LocalVariable* excp_var;
LocalVariable* trace_var;
if (innermost_function().IsAsyncClosure() ||
innermost_function().IsAsyncFunction() ||
innermost_function().IsSyncGenClosure() ||
innermost_function().IsSyncGenerator() ||
innermost_function().IsAsyncGenClosure() ||
innermost_function().IsAsyncGenerator()) {
excp_var = scope->LocalLookupVariable(Symbols::SavedExceptionVar());
trace_var = scope->LocalLookupVariable(Symbols::SavedStackTraceVar());
} else {
excp_var = scope->LocalLookupVariable(Symbols::ExceptionVar());
trace_var = scope->LocalLookupVariable(Symbols::StackTraceVar());
}
ASSERT(excp_var != NULL);
ASSERT(trace_var != NULL);
statement = new (Z)
ThrowNode(statement_pos, new (Z) LoadLocalNode(statement_pos, excp_var),
new (Z) LoadLocalNode(statement_pos, trace_var));
} else {
statement = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
ExpectSemicolon();
}
return statement;
}
void Parser::ReportError(const Error& error) {
Report::LongJump(error);
UNREACHABLE();
}
void Parser::ReportErrors(const Error& prev_error,
const Script& script,
TokenPosition token_pos,
const char* format,
...) {
va_list args;
va_start(args, format);
Report::LongJumpV(prev_error, script, token_pos, format, args);
va_end(args);
UNREACHABLE();
}
void Parser::ReportError(TokenPosition token_pos,
const char* format,
...) const {
va_list args;
va_start(args, format);
Report::MessageV(Report::kError, script_, token_pos, Report::AtLocation,
format, args);
va_end(args);
UNREACHABLE();
}
void Parser::ReportErrorBefore(const char* format, ...) {
va_list args;
va_start(args, format);
Report::MessageV(Report::kError, script_, PrevTokenPos(),
Report::AfterLocation, format, args);
va_end(args);
UNREACHABLE();
}
void Parser::ReportError(const char* format, ...) const {
va_list args;
va_start(args, format);
Report::MessageV(Report::kError, script_, TokenPos(), Report::AtLocation,
format, args);
va_end(args);
UNREACHABLE();
}
void Parser::ReportWarning(TokenPosition token_pos,
const char* format,
...) const {
va_list args;
va_start(args, format);
Report::MessageV(Report::kWarning, script_, token_pos, Report::AtLocation,
format, args);
va_end(args);
}
void Parser::ReportWarning(const char* format, ...) const {
va_list args;
va_start(args, format);
Report::MessageV(Report::kWarning, script_, TokenPos(), Report::AtLocation,
format, args);
va_end(args);
}
void Parser::CheckToken(Token::Kind token_expected, const char* msg) {
if (CurrentToken() != token_expected) {
if (msg != NULL) {
ReportError("%s", msg);
} else {
ReportError("'%s' expected", Token::Str(token_expected));
}
}
}
void Parser::ExpectToken(Token::Kind token_expected) {
if (CurrentToken() != token_expected) {
ReportError("'%s' expected", Token::Str(token_expected));
}
ConsumeToken();
}
void Parser::ExpectSemicolon() {
if (CurrentToken() != Token::kSEMICOLON) {
ReportErrorBefore("semicolon expected");
}
ConsumeToken();
}
void Parser::UnexpectedToken() {
ReportError("unexpected token '%s'", CurrentToken() == Token::kIDENT
? CurrentLiteral()->ToCString()
: Token::Str(CurrentToken()));
}
String* Parser::ExpectUserDefinedTypeIdentifier(const char* msg) {
if (CurrentToken() != Token::kIDENT) {
ReportError("%s", msg);
}
String* ident = CurrentLiteral();
if (ident->Equals("dynamic")) {
ReportError("%s", msg);
}
ConsumeToken();
return ident;
}
// Check whether current token is an identifier or a built-in identifier.
String* Parser::ExpectIdentifier(const char* msg) {
if (!IsIdentifier()) {
ReportError("%s", msg);
}
String* ident = CurrentLiteral();
ConsumeToken();
return ident;
}
bool Parser::IsAwaitKeyword() {
return (FLAG_await_is_keyword || await_is_keyword_) &&
IsSymbol(Symbols::Await());
}
bool Parser::IsYieldKeyword() {
return (FLAG_await_is_keyword || await_is_keyword_) &&
IsSymbol(Symbols::YieldKw());
}
static bool IsIncrementOperator(Token::Kind token) {
return token == Token::kINCR || token == Token::kDECR;
}
static bool IsPrefixOperator(Token::Kind token) {
return (token == Token::kSUB) || (token == Token::kNOT) ||
(token == Token::kBIT_NOT);
}
SequenceNode* Parser::NodeAsSequenceNode(TokenPosition sequence_pos,
AstNode* node,
LocalScope* scope) {
if ((node == NULL) || !node->IsSequenceNode()) {
SequenceNode* sequence = new SequenceNode(sequence_pos, scope);
if (node != NULL) {
sequence->Add(node);
}
return sequence;
}
return node->AsSequenceNode();
}
AstNode* Parser::ThrowTypeError(TokenPosition type_pos,
const AbstractType& type,
LibraryPrefix* prefix) {
ArgumentListNode* arguments = new (Z) ArgumentListNode(type_pos);
String& method_name = String::Handle(Z);
if (prefix == NULL) {
method_name = Library::PrivateCoreLibName(Symbols::ThrowNew()).raw();
} else {
arguments->Add(new (Z) LiteralNode(type_pos, *prefix));
method_name =
Library::PrivateCoreLibName(Symbols::ThrowNewIfNotLoaded()).raw();
}
// Location argument.
arguments->Add(new (Z) LiteralNode(
type_pos,
Integer::ZoneHandle(Z, Integer::New(type_pos.value(), Heap::kOld))));
// Src value argument.
arguments->Add(new (Z) LiteralNode(type_pos, Object::null_instance()));
// Dst type argument.
arguments->Add(new (Z) LiteralNode(type_pos, type));
// Dst name argument.
arguments->Add(new (Z) LiteralNode(type_pos, Symbols::Empty()));
// Bound error msg argument.
arguments->Add(new (Z) LiteralNode(type_pos, Object::null_instance()));
return MakeStaticCall(Symbols::TypeError(), method_name, arguments);
}
// Call _throwNewIfNotLoaded if prefix is not NULL, otherwise call _throwNew.
AstNode* Parser::ThrowNoSuchMethodError(TokenPosition call_pos,
const Class& cls,
const String& function_name,
ArgumentListNode* function_arguments,
InvocationMirror::Level im_level,
InvocationMirror::Kind im_kind,
const Function* func,
const LibraryPrefix* prefix) {
ArgumentListNode* arguments = new (Z) ArgumentListNode(call_pos);
String& method_name = String::Handle(Z);
if (prefix == NULL || !prefix->is_deferred_load()) {
method_name = Library::PrivateCoreLibName(Symbols::ThrowNew()).raw();
} else {
arguments->Add(new (Z) LiteralNode(call_pos, *prefix));
method_name =
Library::PrivateCoreLibName(Symbols::ThrowNewIfNotLoaded()).raw();
}
// Object receiver.
// If the function is external and dynamic, pass the actual receiver,
// otherwise, pass a class literal of the unresolved method's owner.
if ((func != NULL) && !func->IsNull() && func->is_external() &&
!func->is_static()) {
arguments->Add(LoadReceiver(func->token_pos()));
} else {
AbstractType& type = AbstractType::ZoneHandle(Z);
type ^= Type::New(cls, TypeArguments::Handle(Z), call_pos, Heap::kOld);
type ^= CanonicalizeType(type);
arguments->Add(new (Z) LiteralNode(call_pos, type));
}
// String memberName.
arguments->Add(new (Z) LiteralNode(
call_pos, String::ZoneHandle(Z, Symbols::New(T, function_name))));
// Smi invocation_type.
if (cls.IsTopLevel()) {
ASSERT(im_level == InvocationMirror::kStatic ||
im_level == InvocationMirror::kTopLevel);
im_level = InvocationMirror::kTopLevel;
}
arguments->Add(new (Z) LiteralNode(
call_pos, Smi::ZoneHandle(Z, Smi::New(InvocationMirror::EncodeType(
im_level, im_kind)))));
// Type arguments.
arguments->Add(new (Z) LiteralNode(
call_pos, function_arguments == NULL
? TypeArguments::ZoneHandle(Z, TypeArguments::null())
: function_arguments->type_arguments()));
// List arguments.
if (function_arguments == NULL) {
arguments->Add(new (Z) LiteralNode(call_pos, Object::null_array()));
} else {
ArrayNode* array =
new (Z) ArrayNode(call_pos, Type::ZoneHandle(Z, Type::ArrayType()),
function_arguments->nodes());
arguments->Add(array);
}
// List argumentNames.
if (function_arguments == NULL) {
arguments->Add(new (Z) LiteralNode(call_pos, Object::null_array()));
} else {
arguments->Add(new (Z) LiteralNode(call_pos, function_arguments->names()));
}
return MakeStaticCall(Symbols::NoSuchMethodError(), method_name, arguments);
}
AstNode* Parser::ParseBinaryExpr(int min_preced) {
TRACE_PARSER("ParseBinaryExpr");
ASSERT(min_preced >= Token::Precedence(Token::kIFNULL));
AstNode* left_operand = ParseUnaryExpr();
if (left_operand->IsPrimaryNode() &&
(left_operand->AsPrimaryNode()->IsSuper())) {
ReportError(left_operand->token_pos(), "illegal use of 'super'");
}
int current_preced = Token::Precedence(CurrentToken());
while (current_preced >= min_preced) {
while (Token::Precedence(CurrentToken()) == current_preced) {
Token::Kind op_kind = CurrentToken();
const TokenPosition op_pos = TokenPos();
ConsumeToken();
AstNode* right_operand = NULL;
if ((op_kind != Token::kIS) && (op_kind != Token::kAS)) {
right_operand = ParseBinaryExpr(current_preced + 1);
} else {
// For 'is' and 'as' we expect the right operand to be a type.
if ((op_kind == Token::kIS) && (CurrentToken() == Token::kNOT)) {
ConsumeToken();
op_kind = Token::kISNOT;
}
const TokenPosition type_pos = TokenPos();
const AbstractType& type = AbstractType::ZoneHandle(
Z, ParseTypeOrFunctionType(false, ClassFinalizer::kCanonicalize));
if (!type.IsInstantiated() && (FunctionLevel() > 0)) {
// Make sure that the instantiators are captured.
CaptureAllInstantiators();
}
right_operand = new (Z) TypeNode(type_pos, type);
// In production mode, the type may be malformed.
// In checked mode, the type may be malformed or malbounded.
if (type.IsMalformedOrMalbounded()) {
// Note that a type error is thrown in a type test or in
// a type cast even if the tested value is null.
// We need to evaluate the left operand for potential
// side effects.
LetNode* let = new (Z) LetNode(left_operand->token_pos());
let->AddNode(left_operand);
let->AddNode(ThrowTypeError(type_pos, type));
left_operand = let;
break; // Type checks and casts can't be chained.
}
}
if (Token::IsRelationalOperator(op_kind) ||
Token::IsTypeTestOperator(op_kind) ||
Token::IsTypeCastOperator(op_kind) ||
Token::IsEqualityOperator(op_kind)) {
left_operand = new (Z)
ComparisonNode(op_pos, op_kind, left_operand, right_operand);
break; // Equality and relational operators cannot be chained.
} else {
left_operand =
OptimizeBinaryOpNode(op_pos, op_kind, left_operand, right_operand);
}
}
current_preced--;
}
return left_operand;
}
AstNode* Parser::ParseAwaitableExprList() {
TRACE_PARSER("ParseAwaitableExprList");
SequenceNode* preamble = NULL;
AstNode* expressions =
ParseAwaitableExpr(kAllowConst, kConsumeCascades, &preamble);
if (preamble != NULL) {
preamble->Add(expressions);
expressions = preamble;
}
if (CurrentToken() == Token::kCOMMA) {
// Collect comma-separated expressions in a non scope owning sequence node.
SequenceNode* list = new (Z) SequenceNode(TokenPos(), NULL);
list->Add(expressions);
while (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
preamble = NULL;
AstNode* expr =
ParseAwaitableExpr(kAllowConst, kConsumeCascades, &preamble);
if (preamble != NULL) {
list->Add(preamble);
}
list->Add(expr);
}
expressions = list;
}
return expressions;
}
void Parser::EnsureExpressionTemp() {
// Temporary used later by the flow_graph_builder.
parsed_function()->EnsureExpressionTemp();
}
LocalVariable* Parser::CreateTempConstVariable(TokenPosition token_pos,
const char* s) {
char name[64];
Utils::SNPrint(name, 64, ":%s%" Pd "", s, token_pos.value());
LocalVariable* temp = new (Z) LocalVariable(
token_pos, token_pos, String::ZoneHandle(Z, Symbols::New(T, name)),
Object::dynamic_type());
temp->set_is_final();
current_block_->scope->AddVariable(temp);
return temp;
}
AstNode* Parser::OptimizeBinaryOpNode(TokenPosition op_pos,
Token::Kind binary_op,
AstNode* lhs,
AstNode* rhs) {
LiteralNode* lhs_literal = lhs->AsLiteralNode();
LiteralNode* rhs_literal = rhs->AsLiteralNode();
if ((lhs_literal != NULL) && (rhs_literal != NULL)) {
if (lhs_literal->literal().IsDouble() &&
rhs_literal->literal().IsDouble()) {
double left_double = Double::Cast(lhs_literal->literal()).value();
double right_double = Double::Cast(rhs_literal->literal()).value();
if (binary_op == Token::kDIV) {
const Double& dbl_obj = Double::ZoneHandle(
Z, Double::NewCanonical((left_double / right_double)));
return new (Z) LiteralNode(op_pos, dbl_obj);
}
}
}
if (binary_op == Token::kBIT_AND) {
// Normalize so that rhs is a literal if any is.
if ((rhs_literal == NULL) && (lhs_literal != NULL)) {
// Swap.
LiteralNode* temp = rhs_literal;
rhs_literal = lhs_literal;
lhs_literal = temp;
}
}
if (binary_op == Token::kIFNULL) {
// Handle a ?? b.
if ((lhs->EvalConstExpr() != NULL) && (rhs->EvalConstExpr() != NULL)) {
Instance& expr_value = Instance::ZoneHandle(
Z, EvaluateConstExpr(lhs->token_pos(), lhs).raw());
if (expr_value.IsNull()) {
expr_value = EvaluateConstExpr(rhs->token_pos(), rhs).raw();
}
return new (Z) LiteralNode(op_pos, expr_value);
}
LetNode* result = new (Z) LetNode(op_pos);
LocalVariable* left_temp = result->AddInitializer(lhs);
left_temp->set_is_final();
const TokenPosition no_pos = TokenPosition::kNoSource;
LiteralNode* null_operand =
new (Z) LiteralNode(no_pos, Object::null_instance());
LoadLocalNode* load_left_temp = new (Z) LoadLocalNode(no_pos, left_temp);
ComparisonNode* null_compare = new (Z)
ComparisonNode(no_pos, Token::kNE_STRICT, load_left_temp, null_operand);
// If the expression is a compile-time constant, ensure that it
// is evaluated and canonicalized. See issue 31066.
if (rhs->EvalConstExpr() != NULL) {
rhs = FoldConstExpr(rhs->token_pos(), rhs);
}
result->AddNode(
new (Z) ConditionalExprNode(op_pos, null_compare, load_left_temp, rhs));
return result;
}
return new (Z) BinaryOpNode(op_pos, binary_op, lhs, rhs);
}
AstNode* Parser::ExpandAssignableOp(TokenPosition op_pos,
Token::Kind assignment_op,
AstNode* lhs,
AstNode* rhs) {
TRACE_PARSER("ExpandAssignableOp");
switch (assignment_op) {
case Token::kASSIGN:
return rhs;
case Token::kASSIGN_ADD:
return new (Z) BinaryOpNode(op_pos, Token::kADD, lhs, rhs);
case Token::kASSIGN_SUB:
return new (Z) BinaryOpNode(op_pos, Token::kSUB, lhs, rhs);
case Token::kASSIGN_MUL:
return new (Z) BinaryOpNode(op_pos, Token::kMUL, lhs, rhs);
case Token::kASSIGN_TRUNCDIV:
return new (Z) BinaryOpNode(op_pos, Token::kTRUNCDIV, lhs, rhs);
case Token::kASSIGN_DIV:
return new (Z) BinaryOpNode(op_pos, Token::kDIV, lhs, rhs);
case Token::kASSIGN_MOD:
return new (Z) BinaryOpNode(op_pos, Token::kMOD, lhs, rhs);
case Token::kASSIGN_SHR:
return new (Z) BinaryOpNode(op_pos, Token::kSHR, lhs, rhs);
case Token::kASSIGN_SHL:
return new (Z) BinaryOpNode(op_pos, Token::kSHL, lhs, rhs);
case Token::kASSIGN_OR:
return new (Z) BinaryOpNode(op_pos, Token::kBIT_OR, lhs, rhs);
case Token::kASSIGN_AND:
return new (Z) BinaryOpNode(op_pos, Token::kBIT_AND, lhs, rhs);
case Token::kASSIGN_XOR:
return new (Z) BinaryOpNode(op_pos, Token::kBIT_XOR, lhs, rhs);
case Token::kASSIGN_COND:
return new (Z) BinaryOpNode(op_pos, Token::kIFNULL, lhs, rhs);
default:
ReportError(op_pos,
"internal error: ExpandAssignableOp '%s' unimplemented",
Token::Name(assignment_op));
UNIMPLEMENTED();
return NULL;
}
}
// Evaluates the value of the compile time constant expression
// and returns a literal node for the value.
LiteralNode* Parser::FoldConstExpr(TokenPosition expr_pos, AstNode* expr) {
if (expr->IsLiteralNode()) {
return expr->AsLiteralNode();
}
if (expr->EvalConstExpr() == NULL) {
ReportError(expr_pos, "expression is not a valid compile-time constant");
}
return new (Z) LiteralNode(expr_pos, EvaluateConstExpr(expr_pos, expr));
}
LetNode* Parser::PrepareCompoundAssignmentNodes(AstNode** expr) {
AstNode* node = *expr;
TokenPosition token_pos = node->token_pos();
LetNode* result = new (Z) LetNode(token_pos);
if (node->IsLoadIndexedNode()) {
LoadIndexedNode* load_indexed = node->AsLoadIndexedNode();
AstNode* array = load_indexed->array();
AstNode* index = load_indexed->index_expr();
if (!IsSimpleLocalOrLiteralNode(load_indexed->array())) {
LocalVariable* t0 = result->AddInitializer(load_indexed->array());
array = new (Z) LoadLocalNode(token_pos, t0);
}
if (!IsSimpleLocalOrLiteralNode(load_indexed->index_expr())) {
LocalVariable* t1 = result->AddInitializer(load_indexed->index_expr());
index = new (Z) LoadLocalNode(token_pos, t1);
}
*expr = new (Z)
LoadIndexedNode(token_pos, array, index, load_indexed->super_class());
return result;
}
if (node->IsInstanceGetterNode()) {
InstanceGetterNode* getter = node->AsInstanceGetterNode();
AstNode* receiver = getter->receiver();
if (!IsSimpleLocalOrLiteralNode(getter->receiver())) {
LocalVariable* t0 = result->AddInitializer(getter->receiver());
receiver = new (Z) LoadLocalNode(token_pos, t0);
}
*expr = new (Z) InstanceGetterNode(
token_pos, receiver, getter->field_name(), getter->is_conditional());
return result;
}
return result;
}
// Check whether the syntax of expression expr is a grammatically legal
// assignable expression. This check is used to detect situations where
// the expression itself is assignable, but the source is grammatically
// wrong. The AST representation of an expression cannot distinguish
// between x = 0 and (x) = 0. The latter is illegal.
// A syntactically legal assignable expression always ends with an
// identifier token or a ] token. We rewind the token iterator and
// check whether the token before end_pos is an identifier or ].
bool Parser::IsLegalAssignableSyntax(AstNode* expr, TokenPosition end_pos) {
ASSERT(expr->token_pos().IsReal());
ASSERT(expr->token_pos() < end_pos);
SetPosition(expr->token_pos());
Token::Kind token = Token::kILLEGAL;
while (TokenPos() < end_pos) {
token = CurrentToken();
ConsumeToken();
}
ASSERT(TokenPos() == end_pos);
return Token::IsIdentifier(token) || (token == Token::kRBRACK);
}
AstNode* Parser::CreateAssignmentNode(AstNode* original,
AstNode* rhs,
const String* left_ident,
TokenPosition left_pos,
bool is_compound /* = false */) {
AstNode* result = original->MakeAssignmentNode(rhs);
if (result == NULL) {
String& name = String::ZoneHandle(Z);
const Class* target_cls = &current_class();
if (original->IsTypeNode()) {
name = Symbols::New(T, original->AsTypeNode()->TypeName());
} else if (original->IsLoadStaticFieldNode()) {
name = original->AsLoadStaticFieldNode()->field().name();
target_cls =
&Class::Handle(Z, original->AsLoadStaticFieldNode()->field().Owner());
} else if ((left_ident != NULL) &&
(original->IsLiteralNode() || original->IsLoadLocalNode())) {
name = left_ident->raw();
}
if (name.IsNull()) {
ReportError(left_pos, "expression is not assignable");
}
ArgumentListNode* error_arguments =
new (Z) ArgumentListNode(rhs->token_pos());
error_arguments->Add(rhs);
result = ThrowNoSuchMethodError(
original->token_pos(), *target_cls,
String::Handle(Z, Field::SetterSymbol(name)), error_arguments,
InvocationMirror::kStatic,
original->IsLoadLocalNode() ? InvocationMirror::kLocalVar
: InvocationMirror::kSetter,
NULL); // No existing function.
}
// The compound assignment operator a ??= b is different from other
// a op= b assignments. If a is non-null, the assignment to a must be
// dropped:
// normally: a op= b ==> a = a op b
// however: a ??= b ==> a ?? (a = b)
// Therefore, we need to transform a = (a ?? b) into a ?? (a = b)
if (is_compound && rhs->IsBinaryOpNode() &&
(rhs->AsBinaryOpNode()->kind() == Token::kIFNULL)) {
BinaryOpNode* ifnull = rhs->AsBinaryOpNode();
AstNode* modified_assign =
CreateAssignmentNode(original, ifnull->right(), left_ident, left_pos);
result = OptimizeBinaryOpNode(ifnull->token_pos(), ifnull->kind(),
ifnull->left(), modified_assign);
}
return result;
}
AstNode* Parser::ParseCascades(AstNode* expr) {
TokenPosition cascade_pos = TokenPos();
LetNode* cascade = new (Z) LetNode(cascade_pos);
LocalVariable* cascade_receiver_var = cascade->AddInitializer(expr);
while (CurrentToken() == Token::kCASCADE) {
cascade_pos = TokenPos();
LoadLocalNode* load_cascade_receiver =
new (Z) LoadLocalNode(cascade_pos, cascade_receiver_var);
if (Token::IsIdentifier(LookaheadToken(1))) {
// Replace .. with . for ParseSelectors().
token_kind_ = Token::kPERIOD;
} else if (LookaheadToken(1) == Token::kLBRACK) {
ConsumeToken();
} else {
ReportError("identifier or [ expected after ..");
}
String* expr_ident =
Token::IsIdentifier(CurrentToken()) ? CurrentLiteral() : NULL;
const TokenPosition expr_pos = TokenPos();
expr = ParseSelectors(load_cascade_receiver, true);
// Assignments after a cascade are part of the cascade. The
// assigned expression must not contain cascades.
if (Token::IsAssignmentOperator(CurrentToken())) {
Token::Kind assignment_op = CurrentToken();
const TokenPosition assignment_pos = TokenPos();
ConsumeToken();
AstNode* right_expr = ParseExpr(kAllowConst, kNoCascades);
if (assignment_op != Token::kASSIGN) {
// Compound assignment: store inputs with side effects into
// temporary locals.
LetNode* let_expr = PrepareCompoundAssignmentNodes(&expr);
right_expr =
ExpandAssignableOp(assignment_pos, assignment_op, expr, right_expr);
AstNode* assign_expr =
CreateAssignmentNode(expr, right_expr, expr_ident, expr_pos, true);
ASSERT(assign_expr != NULL);
let_expr->AddNode(assign_expr);
expr = let_expr;
} else {
right_expr =
ExpandAssignableOp(assignment_pos, assignment_op, expr, right_expr);
AstNode* assign_expr =
CreateAssignmentNode(expr, right_expr, expr_ident, expr_pos);
ASSERT(assign_expr != NULL);
expr = assign_expr;
}
}
cascade->AddNode(expr);
}
// The result is an expression with the (side effects of the) cascade
// sequence followed by the (value of the) receiver temp variable load.
cascade->AddNode(new (Z) LoadLocalNode(cascade_pos, cascade_receiver_var));
return cascade;
}
// Convert loading of a static const field into a literal node.
static AstNode* LiteralIfStaticConst(Zone* zone, AstNode* expr) {
if (expr->IsLoadStaticFieldNode()) {
const Field& field = expr->AsLoadStaticFieldNode()->field();
if (field.is_const() &&
!expr->AsLoadStaticFieldNode()->is_deferred_reference()) {
ASSERT(field.StaticValue() != Object::sentinel().raw());
ASSERT(field.StaticValue() != Object::transition_sentinel().raw());
return new (zone) LiteralNode(
expr->token_pos(), Instance::ZoneHandle(zone, field.StaticValue()));
}
}
return expr;
}
AstNode* Parser::ParseAwaitableExpr(bool require_compiletime_const,
bool consume_cascades,
SequenceNode** await_preamble) {
TRACE_PARSER("ParseAwaitableExpr");
BoolScope saved_seen_await(&parsed_function()->have_seen_await_expr_, false);
AstNode* expr = ParseExpr(require_compiletime_const, consume_cascades);
if (parsed_function()->have_seen_await()) {
// Make sure we do not reuse the scope to avoid creating contexts that we
// are unaware of, i.e, creating contexts that have already been covered.
// See FlowGraphBuilder::VisitSequenceNode() for details on when contexts
// are created.
OpenBlock();
AwaitTransformer at(current_block_->statements, async_temp_scope_);
AstNode* result = at.Transform(expr);
SequenceNode* preamble = CloseBlock();
if (await_preamble == NULL) {
current_block_->statements->Add(preamble);
} else {
*await_preamble = preamble;
}
return result;
}
return expr;
}
AstNode* Parser::ParseExpr(bool require_compiletime_const,
bool consume_cascades) {
TRACE_PARSER("ParseExpr");
String* expr_ident =
Token::IsIdentifier(CurrentToken()) ? CurrentLiteral() : NULL;
const TokenPosition expr_pos = TokenPos();
RecursionChecker rc(this);
if (CurrentToken() == Token::kTHROW) {
if (require_compiletime_const) {
ReportError("'throw expr' is not a valid compile-time constant");
}
ConsumeToken();
if (CurrentToken() == Token::kSEMICOLON) {
ReportError("expression expected after throw");
}
AstNode* expr = ParseExpr(require_compiletime_const, consume_cascades);
return new (Z) ThrowNode(expr_pos, expr, NULL);
}
if (require_compiletime_const) {
// Check whether we already have evaluated a compile-time constant
// at this source location.
Instance& existing_const = Instance::ZoneHandle(Z);
if (GetCachedConstant(expr_pos, &existing_const)) {
SkipConditionalExpr();
return new (Z) LiteralNode(expr_pos, existing_const);
}
}
AstNode* expr = ParseConditionalExpr();
if (!Token::IsAssignmentOperator(CurrentToken())) {
if ((CurrentToken() == Token::kCASCADE) && consume_cascades) {
return ParseCascades(expr);
}
if (require_compiletime_const) {
expr = FoldConstExpr(expr_pos, expr);
} else {
expr = LiteralIfStaticConst(Z, expr);
}
return expr;
}
// Assignment expressions.
if (!IsLegalAssignableSyntax(expr, TokenPos())) {
ReportError(expr_pos, "expression is not assignable");
}
const Token::Kind assignment_op = CurrentToken();
const TokenPosition assignment_pos = TokenPos();
if (require_compiletime_const) {
ReportError(assignment_pos,
"expression is not a valid compile-time constant");
}
ConsumeToken();
AstNode* right_expr = ParseExpr(require_compiletime_const, consume_cascades);
if (assignment_op != Token::kASSIGN) {
// Compound assignment: store inputs with side effects into temp. locals.
LetNode* let_expr = PrepareCompoundAssignmentNodes(&expr);
AstNode* assigned_value =
ExpandAssignableOp(assignment_pos, assignment_op, expr, right_expr);
AstNode* assign_expr =
CreateAssignmentNode(expr, assigned_value, expr_ident, expr_pos, true);
ASSERT(assign_expr != NULL);
let_expr->AddNode(assign_expr);
return let_expr;
} else {
AstNode* assigned_value = LiteralIfStaticConst(Z, right_expr);
AstNode* assign_expr =
CreateAssignmentNode(expr, assigned_value, expr_ident, expr_pos);
ASSERT(assign_expr != NULL);
return assign_expr;
}
}
LiteralNode* Parser::ParseConstExpr() {
TRACE_PARSER("ParseConstExpr");
TokenPosition expr_pos = TokenPos();
AstNode* expr = ParseExpr(kRequireConst, kNoCascades);
if (!expr->IsLiteralNode()) {
ReportError(expr_pos, "expression must be a compile-time constant");
}
return expr->AsLiteralNode();
}
AstNode* Parser::ParseConditionalExpr() {
TRACE_PARSER("ParseConditionalExpr");
const TokenPosition expr_pos = TokenPos();
AstNode* expr = ParseBinaryExpr(Token::Precedence(Token::kIFNULL));
if (CurrentToken() == Token::kCONDITIONAL) {
EnsureExpressionTemp();
ConsumeToken();
AstNode* expr1 = ParseExpr(kAllowConst, kNoCascades);
ExpectToken(Token::kCOLON);
AstNode* expr2 = ParseExpr(kAllowConst, kNoCascades);
expr = new (Z) ConditionalExprNode(expr_pos, expr, expr1, expr2);
}
return expr;
}
AstNode* Parser::ParseUnaryExpr() {
TRACE_PARSER("ParseUnaryExpr");
AstNode* expr = NULL;
const TokenPosition op_pos = TokenPos();
if (IsAwaitKeyword()) {
TRACE_PARSER("ParseAwaitExpr");
if (!innermost_function().IsAsyncFunction() &&
!innermost_function().IsAsyncClosure() &&
!innermost_function().IsAsyncGenerator() &&
!innermost_function().IsAsyncGenClosure()) {
ReportError("await operator is only allowed in an asynchronous function");
}
ConsumeToken();
parsed_function()->record_await();
LocalVariable* saved_try_ctx;
LocalVariable* async_saved_try_ctx;
LocalVariable* outer_saved_try_ctx;
LocalVariable* outer_async_saved_try_ctx;
CheckAsyncOpInTryBlock(&saved_try_ctx, &async_saved_try_ctx,
&outer_saved_try_ctx, &outer_async_saved_try_ctx);
expr = new (Z) AwaitNode(op_pos, ParseUnaryExpr(), saved_try_ctx,
async_saved_try_ctx, outer_saved_try_ctx,
outer_async_saved_try_ctx, current_block_->scope);
} else if (IsPrefixOperator(CurrentToken())) {
Token::Kind unary_op = CurrentToken();
if (unary_op == Token::kSUB) {
unary_op = Token::kNEGATE;
}
ConsumeToken();
expr = ParseUnaryExpr();
if (expr->IsPrimaryNode() && (expr->AsPrimaryNode()->IsSuper())) {
expr = BuildUnarySuperOperator(unary_op, expr->AsPrimaryNode());
} else {
expr = UnaryOpNode::UnaryOpOrLiteral(op_pos, unary_op, expr);
}
} else if (IsIncrementOperator(CurrentToken())) {
Token::Kind incr_op = CurrentToken();
ConsumeToken();
String* expr_ident =
Token::IsIdentifier(CurrentToken()) ? CurrentLiteral() : NULL;
const TokenPosition expr_pos = TokenPos();
expr = ParseUnaryExpr();
if (!IsLegalAssignableSyntax(expr, TokenPos())) {
ReportError(expr_pos, "expression is not assignable");
}
// Is prefix.
LetNode* let_expr = PrepareCompoundAssignmentNodes(&expr);
Token::Kind binary_op =
(incr_op == Token::kINCR) ? Token::kADD : Token::kSUB;
BinaryOpNode* add = new (Z) BinaryOpNode(
op_pos, binary_op, expr,
new (Z) LiteralNode(op_pos, Smi::ZoneHandle(Z, Smi::New(1))));
AstNode* store =
CreateAssignmentNode(expr, add, expr_ident, expr_pos, true);
ASSERT(store != NULL);
let_expr->AddNode(store);
expr = let_expr;
} else {
expr = ParsePostfixExpr();
}
return expr;
}
ArgumentListNode* Parser::ParseActualParameters(
ArgumentListNode* implicit_arguments,
const TypeArguments& func_type_args,
bool require_const) {
TRACE_PARSER("ParseActualParameters");
ASSERT(CurrentToken() == Token::kLPAREN);
const bool saved_mode = SetAllowFunctionLiterals(true);
ArgumentListNode* arguments;
if (implicit_arguments == NULL) {
// When require_const is true, no function type arguments are passed, so
// there is no need to check that they are instantiated.
ASSERT(!require_const || func_type_args.IsNull());
arguments = new (Z) ArgumentListNode(TokenPos(), func_type_args);
} else {
// If implicit arguments are provided, they include type arguments (if any).
ASSERT(func_type_args.IsNull());
arguments = implicit_arguments;
}
const GrowableObjectArray& names =
GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld));
bool named_argument_seen = false;
if (LookaheadToken(1) != Token::kRPAREN) {
String& arg_name = String::Handle(Z);
do {
ASSERT((CurrentToken() == Token::kLPAREN) ||
(CurrentToken() == Token::kCOMMA));
ConsumeToken();
if (CurrentToken() == Token::kRPAREN) {
// Allow trailing comma.
break;
}
if (IsIdentifier() && (LookaheadToken(1) == Token::kCOLON)) {
named_argument_seen = true;
// The canonicalization of the arguments descriptor array built in
// the code generator requires that the names are symbols, i.e.
// canonicalized strings.
ASSERT(CurrentLiteral()->IsSymbol());
for (int i = 0; i < names.Length(); i++) {
arg_name ^= names.At(i);
if (CurrentLiteral()->Equals(arg_name)) {
ReportError("duplicate named argument");
}
}
names.Add(*CurrentLiteral(), Heap::kOld);
ConsumeToken(); // ident.
ConsumeToken(); // colon.
} else if (named_argument_seen) {
ReportError("named argument expected");
}
arguments->Add(ParseExpr(require_const, kConsumeCascades));
} while (CurrentToken() == Token::kCOMMA);
} else {
ConsumeToken();
}
ExpectToken(Token::kRPAREN);
SetAllowFunctionLiterals(saved_mode);
if (named_argument_seen) {
arguments->set_names(Array::Handle(Z, Array::MakeFixedLength(names)));
}
return arguments;
}
AstNode* Parser::ParseStaticCall(const Class& cls,
const String& func_name,
TokenPosition ident_pos,
const TypeArguments& func_type_args,
const LibraryPrefix* prefix) {
TRACE_PARSER("ParseStaticCall");
const TokenPosition call_pos = TokenPos();
ASSERT(CurrentToken() == Token::kLPAREN);
ArgumentListNode* arguments =
ParseActualParameters(NULL, func_type_args, kAllowConst);
const int num_arguments = arguments->length();
const Function& func = Function::ZoneHandle(
Z, Resolver::ResolveStatic(cls, func_name, func_type_args.Length(),
num_arguments, arguments->names()));
if (func.IsNull()) {
// Check if there is a static field of the same name, it could be a closure
// and so we try and invoke the closure.
AstNode* closure = NULL;
const Field& field = Field::ZoneHandle(Z, cls.LookupStaticField(func_name));
Function& func = Function::ZoneHandle(Z);
if (field.IsNull()) {
// No field, check if we have an explicit getter function.
const String& getter_name =
String::ZoneHandle(Z, Field::LookupGetterSymbol(func_name));
if (!getter_name.IsNull()) {
const int kTypeArgsLen = 0; // no type arguments.
const int kNumArguments = 0; // no arguments.
func = Resolver::ResolveStatic(cls, getter_name, kTypeArgsLen,
kNumArguments, Object::empty_array());
if (!func.IsNull()) {
ASSERT(func.kind() != RawFunction::kImplicitStaticFinalGetter);
closure = new (Z)
StaticGetterNode(call_pos, NULL, Class::ZoneHandle(Z, cls.raw()),
func_name, StaticGetterSetter::kStatic);
return BuildClosureCall(call_pos, closure, arguments);
}
}
} else {
closure = GenerateStaticFieldLookup(field, call_pos);
return BuildClosureCall(call_pos, closure, arguments);
}
// Could not resolve static method: throw a NoSuchMethodError.
return ThrowNoSuchMethodError(ident_pos, cls, func_name, arguments,
InvocationMirror::kStatic,
InvocationMirror::kMethod,
NULL, // No existing function.
prefix);
} else if (cls.IsTopLevel() && (cls.library() == Library::CoreLibrary()) &&
(func.name() == Symbols::Identical().raw()) &&
func_type_args.IsNull()) {
// This is the predefined toplevel function identical(a,b).
// Create a comparison node instead of a static call to the function.
ASSERT(num_arguments == 2);
// If both arguments are constant expressions of type string,
// evaluate and canonicalize them.
// This guarantees that identical("ab", "a"+"b") is true.
// An alternative way to guarantee this would be to introduce
// an AST node that canonicalizes a value.
AstNode* arg0 = arguments->NodeAt(0);
const Instance* val0 = arg0->EvalConstExpr();
if ((val0 != NULL) && (val0->IsString())) {
AstNode* arg1 = arguments->NodeAt(1);
const Instance* val1 = arg1->EvalConstExpr();
if ((val1 != NULL) && (val1->IsString())) {
arguments->SetNodeAt(
0, new (Z) LiteralNode(arg0->token_pos(),
EvaluateConstExpr(arg0->token_pos(), arg0)));
arguments->SetNodeAt(
1, new (Z) LiteralNode(arg1->token_pos(),
EvaluateConstExpr(arg1->token_pos(), arg1)));
}
}
return new (Z) ComparisonNode(ident_pos, Token::kEQ_STRICT,
arguments->NodeAt(0), arguments->NodeAt(1));
}
return new (Z)
StaticCallNode(ident_pos, func, arguments, StaticCallNode::kStatic);
}
AstNode* Parser::ParseInstanceCall(AstNode* receiver,
const String& func_name,
TokenPosition ident_pos,
const TypeArguments& func_type_args,
bool is_conditional) {
TRACE_PARSER("ParseInstanceCall");
CheckToken(Token::kLPAREN);
ArgumentListNode* arguments =
ParseActualParameters(NULL, func_type_args, kAllowConst);
return new (Z) InstanceCallNode(ident_pos, receiver, func_name, arguments,
is_conditional);
}
AstNode* Parser::ParseClosureCall(AstNode* closure,
const TypeArguments& func_type_args) {
TRACE_PARSER("ParseClosureCall");
const TokenPosition call_pos = TokenPos();
ASSERT(CurrentToken() == Token::kLPAREN);
ArgumentListNode* arguments =
ParseActualParameters(NULL, func_type_args, kAllowConst);
return BuildClosureCall(call_pos, closure, arguments);
}
AstNode* Parser::GenerateStaticFieldLookup(const Field& field,
TokenPosition ident_pos) {
// If the static field has an initializer, initialize the field at compile
// time, which is only possible if the field is const.
AstNode* initializing_getter = RunStaticFieldInitializer(field, ident_pos);
if (initializing_getter != NULL) {
// The field is not yet initialized and could not be initialized at compile
// time. The getter will initialize the field.
return initializing_getter;
}
// The field is initialized.
ASSERT(field.is_static());
const Class& field_owner = Class::ZoneHandle(Z, field.Owner());
const String& field_name = String::ZoneHandle(Z, field.name());
const String& getter_name =
String::Handle(Z, Field::GetterSymbol(field_name));
const Function& getter =
Function::Handle(Z, field_owner.LookupStaticFunction(getter_name));
// Never load field directly if there is a getter (deterministic AST).
if (getter.IsNull() || field.is_const()) {
return new (Z)
LoadStaticFieldNode(ident_pos, Field::ZoneHandle(Z, field.raw()));
} else {
ASSERT(getter.kind() == RawFunction::kImplicitStaticFinalGetter);
return new (Z)
StaticGetterNode(ident_pos,
NULL, // Receiver.
field_owner, field_name, StaticGetterSetter::kStatic);
}
}
// Reference to 'field_name' with explicit class as primary.
AstNode* Parser::GenerateStaticFieldAccess(const Class& cls,
const String& field_name,
TokenPosition ident_pos) {
AstNode* access = NULL;
const Field& field = Field::ZoneHandle(Z, cls.LookupStaticField(field_name));
Function& func = Function::ZoneHandle(Z);
if (field.IsNull()) {
// No field, check if we have an explicit getter function.
func = cls.LookupGetterFunction(field_name);
if (func.IsNull() || func.IsDynamicFunction()) {
// We might be referring to an implicit closure, check to see if
// there is a function of the same name.
func = cls.LookupStaticFunction(field_name);
if (!func.IsNull()) {
access = CreateImplicitClosureNode(func, ident_pos, NULL);
} else {
// No function to closurize found found.
// This field access may turn out to be a call to the setter.
// Create a getter call, which may later be turned into
// a setter call, or else the backend will generate
// a throw NoSuchMethodError().
access = new (Z)
StaticGetterNode(ident_pos, NULL, Class::ZoneHandle(Z, cls.raw()),
field_name, StaticGetterSetter::kStatic);
}
} else {
ASSERT(func.kind() != RawFunction::kImplicitStaticFinalGetter);
access = new (Z)
StaticGetterNode(ident_pos, NULL, Class::ZoneHandle(Z, cls.raw()),
field_name, StaticGetterSetter::kStatic);
}
} else {
access = GenerateStaticFieldLookup(field, ident_pos);
}
return access;
}
AstNode* Parser::LoadFieldIfUnresolved(AstNode* node) {
if (!node->IsPrimaryNode()) {
return node;
}
PrimaryNode* primary = node->AsPrimaryNode();
if (primary->primary().IsString()) {
if (primary->IsSuper()) {
return primary;
}
// In a static method, evaluation of an unresolved identifier causes a
// NoSuchMethodError to be thrown.
// In an instance method, we convert this into a getter call
// for a field (which may be defined in a subclass.)
const String& name =
String::Cast(Object::ZoneHandle(primary->primary().raw()));
if (primary->is_deferred_reference()) {
StaticGetterNode* getter = new (Z)
StaticGetterNode(primary->token_pos(),
NULL, // No receiver.
Class::ZoneHandle(Z, library_.toplevel_class()),
name, StaticGetterSetter::kStatic);
getter->set_is_deferred(primary->is_deferred_reference());
return getter;
} else if (current_function().is_static() ||
current_function().IsInFactoryScope()) {
StaticGetterNode* getter =
new (Z) StaticGetterNode(primary->token_pos(),
NULL, // No receiver.
Class::ZoneHandle(Z, current_class().raw()),
name, StaticGetterSetter::kStatic);
getter->set_is_deferred(primary->is_deferred_reference());
return getter;
} else {
AstNode* receiver = LoadReceiver(primary->token_pos());
return CallGetter(node->token_pos(), receiver, name);
}
}
return primary;
}
AstNode* Parser::LoadClosure(PrimaryNode* primary) {
ASSERT(primary->primary().IsFunction());
const Function& func =
Function::Cast(Object::ZoneHandle(primary->primary().raw()));
const String& funcname = String::ZoneHandle(Z, func.name());
if (func.is_static()) {
// Static function access.
ClosureNode* closure =
CreateImplicitClosureNode(func, primary->token_pos(), NULL);
closure->set_is_deferred(primary->is_deferred_reference());
return closure;
} else {
// Instance function access.
if (current_function().is_static() ||
current_function().IsInFactoryScope()) {
ReportError(primary->token_pos(),
"cannot access instance method '%s' from static method",
funcname.ToCString());
}
AstNode* receiver = LoadReceiver(primary->token_pos());
return CallGetter(primary->token_pos(), receiver, funcname);
}
UNREACHABLE();
return NULL;
}
AstNode* Parser::LoadTypeParameter(PrimaryNode* primary) {
const TokenPosition primary_pos = primary->token_pos();
TypeParameter& type_parameter = TypeParameter::ZoneHandle(Z);
type_parameter = TypeParameter::Cast(primary->primary()).raw();
if (type_parameter.IsClassTypeParameter()) {
if (ParsingStaticMember()) {
const String& name = String::Handle(Z, type_parameter.name());
ReportError(primary_pos,
"cannot access type parameter '%s' "
"from static function",
name.ToCString());
}
if (FunctionLevel() > 0) {
// Make sure that the class instantiator is captured.
CaptureInstantiator();
}
type_parameter ^= CanonicalizeType(type_parameter);
} else {
ASSERT(type_parameter.IsFunctionTypeParameter());
if (!Isolate::Current()->reify_generic_functions()) {
Type& type = Type::ZoneHandle(Z, Type::DynamicType());
return new (Z) TypeNode(primary_pos, type);
}
if ((FunctionLevel() > 0) && innermost_function().HasGenericParent()) {
// Make sure that the parent function type arguments are captured.
CaptureFunctionTypeArguments();
}
}
ASSERT(type_parameter.IsFinalized());
ASSERT(!type_parameter.IsMalformed());
return new (Z) TypeNode(primary_pos, type_parameter);
}
AstNode* Parser::ParseSelectors(AstNode* primary, bool is_cascade) {
AstNode* left = primary;
while (true) {
AstNode* selector = NULL;
if ((CurrentToken() == Token::kPERIOD) ||
(CurrentToken() == Token::kQM_PERIOD)) {
// Unconditional or conditional property extraction or method call.
bool is_conditional = CurrentToken() == Token::kQM_PERIOD;
ConsumeToken();
if (left->IsPrimaryNode()) {
PrimaryNode* primary_node = left->AsPrimaryNode();
if (primary_node->primary().IsFunction()) {
left = LoadClosure(primary_node);
} else if (primary_node->primary().IsTypeParameter()) {
left = LoadTypeParameter(primary_node);
} else {
// Super field access handled in ParseSuperFieldAccess(),
// super calls handled in ParseSuperCall().
ASSERT(!primary_node->IsSuper());
left = LoadFieldIfUnresolved(left);
}
}
const TokenPosition ident_pos = TokenPos();
String* ident = ExpectIdentifier("identifier expected");
if (IsArgumentPart()) {
// Identifier followed by optional type arguments and opening paren:
// method call.
TypeArguments& func_type_args = TypeArguments::ZoneHandle(Z);
if (CurrentToken() == Token::kLT) {
// Type arguments.
func_type_args = ParseTypeArguments(ClassFinalizer::kCanonicalize);
if (Isolate::Current()->reify_generic_functions()) {
if (!func_type_args.IsNull() && !func_type_args.IsInstantiated() &&
(FunctionLevel() > 0)) {
// Make sure that the instantiators are captured.
CaptureAllInstantiators();
}
} else {
func_type_args = TypeArguments::null();
}
}
PrimaryNode* primary_node = left->AsPrimaryNode();
if ((primary_node != NULL) && primary_node->primary().IsClass()) {
// Static method call prefixed with class name.
const Class& cls = Class::Cast(primary_node->primary());
selector = ParseStaticCall(cls, *ident, ident_pos, func_type_args,
primary_node->prefix());
} else {
if ((primary_node != NULL) && primary_node->is_deferred_reference()) {
const Class& cls = Class::Handle(library_.toplevel_class());
selector = ParseStaticCall(cls, *ident, ident_pos, func_type_args,
primary_node->prefix());
} else {
selector = ParseInstanceCall(left, *ident, ident_pos,
func_type_args, is_conditional);
}
}
} else {
// Field access.
Class& cls = Class::Handle(Z);
bool is_deferred = false;
if (left->IsPrimaryNode()) {
PrimaryNode* primary_node = left->AsPrimaryNode();
is_deferred = primary_node->is_deferred_reference();
if (primary_node->primary().IsClass()) {
// If the primary node referred to a class we are loading a
// qualified static field.
cls ^= primary_node->primary().raw();
} else if (is_deferred) {
cls = library_.toplevel_class();
}
}
if (cls.IsNull()) {
// Instance field access.
selector = new (Z)
InstanceGetterNode(ident_pos, left, *ident, is_conditional);
} else {
// Static field access.
selector = GenerateStaticFieldAccess(cls, *ident, ident_pos);
ASSERT(selector != NULL);
if (selector->IsLoadStaticFieldNode()) {
selector->AsLoadStaticFieldNode()->set_is_deferred(is_deferred);
} else if (selector->IsStaticGetterNode()) {
selector->AsStaticGetterNode()->set_is_deferred(is_deferred);
}
}
}
} else if (CurrentToken() == Token::kLBRACK) {
// Super index operator handled in ParseSuperOperator().
ASSERT(!left->IsPrimaryNode() || !left->AsPrimaryNode()->IsSuper());
const TokenPosition bracket_pos = TokenPos();
ConsumeToken();
left = LoadFieldIfUnresolved(left);
const bool saved_mode = SetAllowFunctionLiterals(true);
AstNode* index = ParseExpr(kAllowConst, kConsumeCascades);
SetAllowFunctionLiterals(saved_mode);
ExpectToken(Token::kRBRACK);
AstNode* array = left;
if (left->IsPrimaryNode()) {
PrimaryNode* primary_node = left->AsPrimaryNode();
const TokenPosition primary_pos = primary_node->token_pos();
if (primary_node->primary().IsFunction()) {
array = LoadClosure(primary_node);
} else if (primary_node->primary().IsClass()) {
const Class& type_class = Class::Cast(primary_node->primary());
AbstractType& type = Type::ZoneHandle(
Z, Type::New(type_class, TypeArguments::Handle(Z), primary_pos,
Heap::kOld));
type ^= CanonicalizeType(type);
// Type may be malbounded, but not malformed.
ASSERT(!type.IsMalformed());
array = new (Z) TypeNode(primary_pos, type,
primary_node->is_deferred_reference());
} else if (primary_node->primary().IsTypeParameter()) {
array = LoadTypeParameter(primary_node);
} else {
UNREACHABLE(); // Internal parser error.
}
}
selector = new (Z)
LoadIndexedNode(bracket_pos, array, index, Class::ZoneHandle(Z));
} else if (IsArgumentPart()) {
TypeArguments& func_type_args = TypeArguments::ZoneHandle(Z);
if (CurrentToken() == Token::kLT) {
// Type arguments.
func_type_args = ParseTypeArguments(ClassFinalizer::kCanonicalize);
if (Isolate::Current()->reify_generic_functions()) {
if (!func_type_args.IsNull() && !func_type_args.IsInstantiated() &&
(FunctionLevel() > 0)) {
// Make sure that the instantiators are captured.
CaptureAllInstantiators();
}
} else {
func_type_args = TypeArguments::null();
}
}
if (left->IsPrimaryNode()) {
PrimaryNode* primary_node = left->AsPrimaryNode();
const TokenPosition primary_pos = primary_node->token_pos();
if (primary_node->primary().IsFunction()) {
const Function& func = Function::Cast(primary_node->primary());
const String& func_name = String::ZoneHandle(Z, func.name());
if (func.is_static()) {
// Parse static function call.
Class& cls = Class::Handle(Z, func.Owner());
selector =
ParseStaticCall(cls, func_name, primary_pos, func_type_args);
} else {
// Dynamic function call on implicit "this" parameter.
if (current_function().is_static()) {
ReportError(primary_pos,
"cannot access instance method '%s' "
"from static function",
func_name.ToCString());
}
selector = ParseInstanceCall(LoadReceiver(primary_pos), func_name,
primary_pos, func_type_args,
false /* is_conditional */);
}
} else if (primary_node->primary().IsString()) {
// Primary is an unresolved name.
if (primary_node->IsSuper()) {
ReportError(primary_pos, "illegal use of super");
}
const String& name =
String::Cast(Object::ZoneHandle(primary_node->primary().raw()));
if (primary_node->is_deferred_reference()) {
// The static call will be converted to throwing a NSM error.
const Class& cls = Class::Handle(library_.toplevel_class());
selector = ParseStaticCall(cls, name, primary_pos, func_type_args,
primary_node->prefix());
} else if (current_function().is_static()) {
// The static call will be converted to throwing a NSM error.
selector = ParseStaticCall(current_class(), name, primary_pos,
func_type_args);
} else {
// Treat as call to unresolved (instance) method.
selector =
ParseInstanceCall(LoadReceiver(primary_pos), name, primary_pos,
func_type_args, false /* is_conditional */);
}
} else if (primary_node->primary().IsTypeParameter()) {
// The parsed type arguments (if any) are not parameterizing the type
// parameter (this would be a compile-time error), but are passed as
// the type arguments of a generic closure call with the type
// parameter as the closure. This will result in a NSM call, whether
// the call is generic or not.
selector = LoadTypeParameter(primary_node);
selector = ParseClosureCall(selector, func_type_args);
} else if (primary_node->primary().IsClass()) {
// The parsed type arguments (if any) are not parameterizing the
// class, but are passed as the type arguments of a generic closure
// call with the class as the closure. This will result in a NSM call,
// whether the call is generic or not.
const Class& type_class = Class::Cast(primary_node->primary());
AbstractType& type = Type::ZoneHandle(
Z, Type::New(type_class, Object::null_type_arguments(),
primary_pos, Heap::kOld));
type ^= CanonicalizeType(type);
// Type may be malbounded, but not malformed.
ASSERT(!type.IsMalformed());
selector = new (Z) TypeNode(primary_pos, type,
primary_node->is_deferred_reference());
selector = ParseClosureCall(selector, func_type_args);
} else {
UNREACHABLE(); // Internal parser error.
}
} else {
// Left is not a primary node; this must be a closure call.
AstNode* closure = left;
selector = ParseClosureCall(closure, func_type_args);
}
} else {
// No (more) selectors to parse.
left = LoadFieldIfUnresolved(left);
if (left->IsPrimaryNode()) {
PrimaryNode* primary_node = left->AsPrimaryNode();
const TokenPosition primary_pos = primary->token_pos();
if (primary_node->primary().IsFunction()) {
// Treat as implicit closure.
left = LoadClosure(primary_node);
} else if (primary_node->primary().IsClass()) {
const Class& type_class = Class::Cast(primary_node->primary());
AbstractType& type = Type::ZoneHandle(
Z, Type::New(type_class, TypeArguments::Handle(Z), primary_pos,
Heap::kOld));
type = CanonicalizeType(type);
// Type may be malbounded, but not malformed.
ASSERT(!type.IsMalformed());
left = new (Z) TypeNode(primary_pos, type,
primary_node->is_deferred_reference());
} else if (primary_node->primary().IsTypeParameter()) {
left = LoadTypeParameter(primary_node);
} else if (primary_node->IsSuper()) {
// Return "super" to handle unary super operator calls,
// or to report illegal use of "super" otherwise.
left = primary_node;
} else {
UNREACHABLE(); // Internal parser error.
}
}
// Done parsing selectors.
return left;
}
ASSERT(selector != NULL);
left = selector;
}
}
AstNode* Parser::ParsePostfixExpr() {
TRACE_PARSER("ParsePostfixExpr");
String* expr_ident =
Token::IsIdentifier(CurrentToken()) ? CurrentLiteral() : NULL;
const TokenPosition expr_pos = TokenPos();
AstNode* expr = ParsePrimary();
expr = ParseSelectors(expr, false);
if (IsIncrementOperator(CurrentToken())) {
TRACE_PARSER("IncrementOperator");
if (!IsLegalAssignableSyntax(expr, TokenPos())) {
ReportError(expr_pos, "expression is not assignable");
}
Token::Kind incr_op = CurrentToken();
const TokenPosition op_pos = TokenPos();
ConsumeToken();
// Not prefix.
LetNode* let_expr = PrepareCompoundAssignmentNodes(&expr);
LocalVariable* temp = let_expr->AddInitializer(expr);
Token::Kind binary_op =
(incr_op == Token::kINCR) ? Token::kADD : Token::kSUB;
BinaryOpNode* add = new (Z) BinaryOpNode(
op_pos, binary_op, new (Z) LoadLocalNode(op_pos, temp),
new (Z) LiteralNode(op_pos, Smi::ZoneHandle(Z, Smi::New(1))));
AstNode* store =
CreateAssignmentNode(expr, add, expr_ident, expr_pos, true);
ASSERT(store != NULL);
// The result is a pair of the (side effects of the) store followed by
// the (value of the) initial value temp variable load.
let_expr->AddNode(store);
let_expr->AddNode(new (Z) LoadLocalNode(op_pos, temp));
return let_expr;
}
return expr;
}
// Resolve the type parameters that may appear in the given signature from the
// signature function and current class.
// Unresolved type classes get resolved later by the class finalizer.
void Parser::ResolveSignatureTypeParameters(const Function& signature) {
const Function& saved_innermost_function =
Function::Handle(Z, innermost_function().raw());
innermost_function_ = signature.raw();
AbstractType& type = AbstractType::Handle();
// Resolve upper bounds of function type parameters.
const intptr_t num_type_params = signature.NumTypeParameters();
if (num_type_params > 0) {
TypeParameter& type_param = TypeParameter::Handle();
const TypeArguments& type_params =
TypeArguments::Handle(signature.type_parameters());
for (intptr_t i = 0; i < num_type_params; i++) {
type_param ^= type_params.TypeAt(i);
type = type_param.bound();
ResolveTypeParameters(&type);
type_param.set_bound(type);
}
}
// Resolve result type.
type = signature.result_type();
ResolveTypeParameters(&type);
signature.set_result_type(type); // Update type without scope change.
// Resolve formal parameter types.
const intptr_t num_parameters = signature.NumParameters();
for (intptr_t i = 0; i < num_parameters; i++) {
type = signature.ParameterTypeAt(i);
ResolveTypeParameters(&type);
signature.SetParameterTypeAt(i, type);
}
innermost_function_ = saved_innermost_function.raw();
}
// Resolve the type parameters that may appear in the given type and in its type
// arguments from the current function and current class.
// Unresolved type classes get resolved later by the class finalizer.
void Parser::ResolveTypeParameters(AbstractType* type) {
ASSERT(type != NULL);
if (type->IsResolved()) {
// Some types are resolved by definition, such as a TypeParameter.
return;
}
// Resolve type class.
if (!type->HasResolvedTypeClass()) {
const UnresolvedClass& unresolved_class =
UnresolvedClass::Handle(Z, type->unresolved_class());
const String& unresolved_class_name =
String::Handle(Z, unresolved_class.ident());
if (unresolved_class.library_or_library_prefix() == Object::null()) {
// First check if the type is a function type parameter.
if (InGenericFunctionScope()) {
intptr_t type_param_func_level = FunctionLevel();
TypeParameter& type_parameter = TypeParameter::ZoneHandle(
Z, innermost_function().LookupTypeParameter(
unresolved_class_name, &type_param_func_level));
if (!type_parameter.IsNull()) {
// A type parameter cannot be parameterized, so make the type
// malformed if type arguments have previously been parsed.
if (type->arguments() != TypeArguments::null()) {
*type = ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(Z), // No previous error.
script_, type_parameter.token_pos(),
"type parameter '%s' cannot be parameterized",
String::Handle(Z, type_parameter.name()).ToCString());
return;
}
if (Isolate::Current()->reify_generic_functions()) {
ASSERT(!type_parameter.IsMalformed());
*type = type_parameter.raw();
} else {
*type = Type::DynamicType();
}
return;
}
}
// Then check if the type is a class type parameter.
const TypeParameter& type_parameter = TypeParameter::Handle(
Z, current_class().LookupTypeParameter(unresolved_class_name));
if (!type_parameter.IsNull()) {
// A type parameter is considered to be a malformed type when
// referenced by a static member.
if (ParsingStaticMember()) {
*type = ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(Z), // No previous error.
script_, type->token_pos(),
"type parameter '%s' cannot be referenced "
"from static member",
String::Handle(Z, type_parameter.name()).ToCString());
return;
}
// A type parameter cannot be parameterized, so make the type
// malformed if type arguments have previously been parsed.
if (type->arguments() != TypeArguments::null()) {
*type = ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(Z), // No previous error.
script_, type_parameter.token_pos(),
"type parameter '%s' cannot be parameterized",
String::Handle(Z, type_parameter.name()).ToCString());
return;
}
*type = type_parameter.raw();
return;
}
}
}
// Resolve type arguments, if any.
if (type->arguments() != TypeArguments::null()) {
const TypeArguments& arguments =
TypeArguments::Handle(Z, type->arguments());
// Already resolved if canonical.
if (!arguments.IsCanonical()) {
const intptr_t num_arguments = arguments.Length();
AbstractType& type_argument = AbstractType::Handle(Z);
for (intptr_t i = 0; i < num_arguments; i++) {
type_argument = arguments.TypeAt(i);
ResolveTypeParameters(&type_argument);
arguments.SetTypeAt(i, type_argument);
}
}
}
if (type->IsFunctionType()) {
const Function& signature =
Function::Handle(Z, Type::Cast(*type).signature());
Type& signature_type = Type::Handle(Z, signature.SignatureType());
if (signature_type.raw() != type->raw()) {
ResolveTypeParameters(&signature_type);
} else {
ResolveSignatureTypeParameters(signature);
}
}
}
RawAbstractType* Parser::CanonicalizeType(const AbstractType& type) {
// If the current class is the result of a mixin application, we must
// use the class scope of the class from which the function originates.
if (current_class().IsMixinApplication()) {
return ClassFinalizer::FinalizeType(
Class::Handle(Z, parsed_function()->function().origin()), type);
}
return ClassFinalizer::FinalizeType(current_class(), type);
}
LocalVariable* Parser::LookupLocalScope(const String& ident) {
if (current_block_ == NULL) {
return NULL;
}
// A found name is treated as accessed and possibly marked as captured.
const bool kTestOnly = false;
return current_block_->scope->LookupVariable(ident, kTestOnly);
}
void Parser::CheckInstanceFieldAccess(TokenPosition field_pos,
const String& field_name) {
// Fields are not accessible from a static function, except from a
// constructor, which is considered as non-static by the compiler.
if (current_function().is_static()) {
ReportError(field_pos,
"cannot access instance field '%s' from a static function",
field_name.ToCString());
}
}
bool Parser::ParsingStaticMember() const {
if (is_top_level_) {
return (current_member_ != NULL) && current_member_->has_static &&
!current_member_->has_factory;
}
ASSERT(!current_function().IsNull());
return current_function().is_static() &&
!current_function().IsInFactoryScope();
}
const AbstractType* Parser::ReceiverType(const Class& cls) {
ASSERT(!cls.IsNull());
ASSERT(!cls.IsTypedefClass());
// Note that if cls is _Closure, the returned type will be _Closure,
// and not the signature type.
Type& type = Type::ZoneHandle(Z, cls.CanonicalType());
if (!type.IsNull()) {
return &type;
}
type = Type::New(cls, TypeArguments::Handle(Z, cls.type_parameters()),
cls.token_pos(), Heap::kOld);
if (cls.is_type_finalized()) {
type ^= ClassFinalizer::FinalizeType(cls, type);
// Note that the receiver type may now be a malbounded type.
cls.SetCanonicalType(type);
}
return &type;
}
bool Parser::IsInstantiatorRequired() const {
ASSERT(!current_function().IsNull());
if (current_function().is_static() &&
!current_function().IsInFactoryScope()) {
return false;
}
return current_class().IsGeneric();
}
bool Parser::InGenericFunctionScope() const {
if (!innermost_function().IsNull()) {
// With one more free tag bit in Function, we could cache this information.
if (innermost_function().IsGeneric() ||
innermost_function().HasGenericParent()) {
return true;
}
}
return false;
}
void Parser::InsertCachedConstantValue(const Script& script,
TokenPosition token_pos,
const Instance& value) {
ASSERT(Thread::Current()->IsMutatorThread());
const intptr_t kInitialConstMapSize = 16;
ASSERT(!script.InVMHeap());
if (script.compile_time_constants() == Array::null()) {
const Array& array = Array::Handle(
HashTables::New<ConstantsMap>(kInitialConstMapSize, Heap::kOld));
script.set_compile_time_constants(array);
}
ConstantsMap constants(script.compile_time_constants());
constants.InsertNewOrGetValue(token_pos, value);
script.set_compile_time_constants(constants.Release());
}
void Parser::CacheConstantValue(TokenPosition token_pos,
const Instance& value) {
if (current_function().kind() == RawFunction::kImplicitStaticFinalGetter) {
// Don't cache constants in initializer expressions. They get
// evaluated only once.
return;
}
InsertCachedConstantValue(script_, token_pos, value);
INC_STAT(thread_, num_cached_consts, 1);
}
bool Parser::GetCachedConstant(TokenPosition token_pos, Instance* value) {
bool is_present = false;
ASSERT(!script_.InVMHeap());
if (script_.compile_time_constants() == Array::null()) {
return false;
}
ConstantsMap constants(script_.compile_time_constants());
*value ^= constants.GetOrNull(token_pos, &is_present);
// Mutator compiler thread may add constants while background compiler
// is running, and thus change the value of 'compile_time_constants';
// do not assert that 'compile_time_constants' has not changed.
constants.Release();
if (FLAG_compiler_stats && is_present) {
thread_->compiler_stats()->num_const_cache_hits++;
}
return is_present;
}
RawInstance* Parser::TryCanonicalize(const Instance& instance,
TokenPosition token_pos) {
if (instance.IsNull()) {
return instance.raw();
}
const char* error_str = NULL;
Instance& result =
Instance::Handle(Z, instance.CheckAndCanonicalize(thread(), &error_str));
if (result.IsNull()) {
ReportError(token_pos, "Invalid const object %s", error_str);
}
return result.raw();
}
// If the field is already initialized, return no ast (NULL).
// Otherwise, if the field is constant, initialize the field and return no ast.
// If the field is not initialized and not const, return the ast for the getter.
StaticGetterNode* Parser::RunStaticFieldInitializer(
const Field& field,
TokenPosition field_ref_pos) {
ASSERT(field.is_static());
const Class& field_owner = Class::ZoneHandle(Z, field.Owner());
const String& field_name = String::ZoneHandle(Z, field.name());
const String& getter_name =
String::Handle(Z, Field::GetterSymbol(field_name));
const Function& getter =
Function::Handle(Z, field_owner.LookupStaticFunction(getter_name));
const Instance& value = Instance::Handle(Z, field.StaticValue());
if (value.raw() == Object::transition_sentinel().raw()) {
if (field.is_const()) {
ReportError("circular dependency while initializing static field '%s'",
field_name.ToCString());
} else {
// The implicit static getter will throw the exception if necessary.
return new (Z) StaticGetterNode(field_ref_pos, NULL, field_owner,
field_name, StaticGetterSetter::kStatic);
}
} else if (value.raw() == Object::sentinel().raw()) {
// This field has not been referenced yet and thus the value has
// not been evaluated. If the field is const, call the static getter method
// to evaluate the expression and canonicalize the value.
if (field.is_const()) {
NoReloadScope no_reload_scope(isolate(), thread());
NoOOBMessageScope no_msg_scope(thread());
field.SetStaticValue(Object::transition_sentinel());
const int kTypeArgsLen = 0; // No type argument vector.
const int kNumArguments = 0; // No arguments.
const Function& func = Function::Handle(
Z, Resolver::ResolveStatic(field_owner, getter_name, kTypeArgsLen,
kNumArguments, Object::empty_array()));
ASSERT(!func.IsNull());
ASSERT(func.kind() == RawFunction::kImplicitStaticFinalGetter);
Object& const_value = Object::Handle(Z);
const_value = DartEntry::InvokeFunction(func, Object::empty_array());
if (const_value.IsError()) {
const Error& error = Error::Cast(const_value);
if (error.IsUnhandledException()) {
// An exception may not occur in every parse attempt, i.e., the
// generated AST is not deterministic. Therefore mark the function as
// not optimizable.
current_function().SetIsOptimizable(false);
field.SetStaticValue(Object::null_instance());
// It is a compile-time error if evaluation of a compile-time constant
// would raise an exception.
const String& field_name = String::Handle(Z, field.name());
ReportErrors(error, script_, field_ref_pos,
"error initializing const field '%s'",
field_name.ToCString());
} else {
ReportError(error);
}
UNREACHABLE();
}
ASSERT(const_value.IsNull() || const_value.IsInstance());
Instance& instance = Instance::Handle(Z);
instance ^= const_value.raw();
instance = TryCanonicalize(instance, field_ref_pos);
field.SetStaticValue(instance);
return NULL; // Constant
} else {
return new (Z) StaticGetterNode(field_ref_pos, NULL, field_owner,
field_name, StaticGetterSetter::kStatic);
}
}
if (getter.IsNull() ||
(getter.kind() == RawFunction::kImplicitStaticFinalGetter)) {
return NULL;
}
ASSERT(getter.kind() == RawFunction::kImplicitGetter);
return new (Z) StaticGetterNode(field_ref_pos, NULL, field_owner, field_name,
StaticGetterSetter::kStatic);
}
RawObject* Parser::EvaluateConstConstructorCall(
const Class& type_class,
const TypeArguments& type_arguments,
const Function& constructor,
ArgumentListNode* arguments,
bool obfuscate_symbol_instances /* = true */) {
NoReloadScope no_reload_scope(isolate(), thread());
NoOOBMessageScope no_msg_scope(thread());
// Factories and constructors are not generic functions.
const int kTypeArgsLen = 0;
// Factories have one extra argument: the type arguments.
// Constructors have one extra arguments: receiver.
const int kNumExtraArgs = 1;
const int num_arguments = arguments->length() + kNumExtraArgs;
const Array& arg_values =
Array::Handle(Z, Array::New(num_arguments, allocation_space_));
Instance& instance = Instance::Handle(Z);
if (!constructor.IsFactory()) {
instance = Instance::New(type_class, allocation_space_);
if (!type_arguments.IsNull()) {
if (!type_arguments.IsInstantiated()) {
ReportError("type must be constant in const constructor");
}
instance.SetTypeArguments(
TypeArguments::Handle(Z, type_arguments.Canonicalize()));
}
arg_values.SetAt(0, instance);
} else {
// Prepend type_arguments to list of arguments to factory.
ASSERT(type_arguments.IsZoneHandle());
arg_values.SetAt(0, type_arguments);
}
for (int i = 0; i < arguments->length(); i++) {
AstNode* arg = arguments->NodeAt(i);
// Arguments have been evaluated to a literal value already.
ASSERT(arg->IsLiteralNode());
arg_values.SetAt((i + kNumExtraArgs), arg->AsLiteralNode()->literal());
}
const Array& args_descriptor =
Array::Handle(Z, ArgumentsDescriptor::New(kTypeArgsLen, num_arguments,
arguments->names()));
const Object& result = Object::Handle(
Z, DartEntry::InvokeFunction(constructor, arg_values, args_descriptor));
if (result.IsError()) {
// An exception may not occur in every parse attempt, i.e., the
// generated AST is not deterministic. Therefore mark the function as
// not optimizable.
current_function().SetIsOptimizable(false);
if (result.IsUnhandledException()) {
return result.raw();
} else {
thread()->long_jump_base()->Jump(1, Error::Cast(result));
UNREACHABLE();
return Object::null();
}
} else {
if (constructor.IsFactory()) {
// The factory method returns the allocated object.
instance ^= result.raw();
}
if (obfuscate_symbol_instances && I->obfuscate() &&
(instance.clazz() == I->object_store()->symbol_class())) {
Obfuscator::ObfuscateSymbolInstance(T, instance);
}
return TryCanonicalize(instance, TokenPos());
}
}
// Do a lookup for the identifier in the block scope and the class scope
// return true if the identifier is found, false otherwise.
// If node is non NULL return an AST node corresponding to the identifier.
bool Parser::ResolveIdentInLocalScope(TokenPosition ident_pos,
const String& ident,
AstNode** node,
intptr_t* function_level) {
TRACE_PARSER("ResolveIdentInLocalScope");
// First try to find the identifier in the nested local scopes.
LocalVariable* local = LookupLocalScope(ident);
if (current_block_ != NULL) {
current_block_->scope->AddReferencedName(ident_pos, ident);
}
if (local != NULL) {
if (node != NULL) {
*node = new (Z) LoadLocalNode(ident_pos, local);
}
if (function_level != NULL) {
*function_level = local->owner()->function_level();
}
return true;
}
// If we are compiling top-level code, we don't need to look for
// the identifier in the current (top-level) class. The class scope
// of the top-level class is part of the library scope.
if (current_class().IsTopLevel()) {
if (node != NULL) {
*node = NULL;
}
return false;
}
// Try to find the identifier in the class scope of the current class.
// If the current class is the result of a mixin application, we must
// use the class scope of the class from which the function originates.
Class& cls = Class::Handle(Z);
if (!current_class().IsMixinApplication()) {
cls = current_class().raw();
} else {
cls = parsed_function()->function().origin();
}
Function& func = Function::Handle(Z, Function::null());
Field& field = Field::Handle(Z, Field::null());
// First check if a field exists.
field = cls.LookupField(ident);
if (!field.IsNull()) {
if (node != NULL) {
if (!field.is_static()) {
CheckInstanceFieldAccess(ident_pos, ident);
*node = CallGetter(ident_pos, LoadReceiver(ident_pos), ident);
} else {
*node = GenerateStaticFieldLookup(field, ident_pos);
}
}
if (function_level != NULL) {
*function_level = 0;
}
return true;
}
// Check if an instance/static function exists.
func = cls.LookupFunction(ident);
if (!func.IsNull() && (func.IsDynamicFunction() || func.IsStaticFunction() ||
func.is_abstract())) {
if (node != NULL) {
*node =
new (Z) PrimaryNode(ident_pos, Function::ZoneHandle(Z, func.raw()));
}
return true;
}
// Now check if a getter/setter method exists for it in which case
// it is still a field.
// A setter without a corresponding getter binds to the non-existing
// getter. (The getter could be followed by an assignment which will
// convert it to a setter node. If there is no assignment the non-existing
// getter will throw a NoSuchMethodError.)
func = cls.LookupGetterFunction(ident);
if (func.IsNull()) {
func = cls.LookupSetterFunction(ident);
}
if (!func.IsNull()) {
if (func.IsDynamicFunction() || func.is_abstract()) {
if (node != NULL) {
CheckInstanceFieldAccess(ident_pos, ident);
ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
*node = CallGetter(ident_pos, LoadReceiver(ident_pos), ident);
}
return true;
} else if (func.IsStaticFunction()) {
if (node != NULL) {
*node = new (Z)
StaticGetterNode(ident_pos, NULL, Class::ZoneHandle(Z, cls.raw()),
ident, StaticGetterSetter::kStatic);
}
return true;
}
}
// Nothing found in scope of current class.
if (node != NULL) {
*node = NULL;
}
return false;
}
// Resolve an identifier by checking the global scope of the current
// library. If not found in the current library, then look in the scopes
// of all libraries that are imported without a library prefix.
AstNode* Parser::ResolveIdentInCurrentLibraryScope(TokenPosition ident_pos,
const String& ident) {
TRACE_PARSER("ResolveIdentInCurrentLibraryScope");
HANDLESCOPE(thread());
const Object& obj = Object::Handle(Z, library_.ResolveName(ident));
if (obj.IsClass()) {
const Class& cls = Class::Cast(obj);
return new (Z) PrimaryNode(ident_pos, Class::ZoneHandle(Z, cls.raw()));
} else if (obj.IsField()) {
const Field& field = Field::Cast(obj);
ASSERT(field.is_static());
AstNode* get_field = GenerateStaticFieldLookup(field, ident_pos);
if (get_field->IsStaticGetterNode()) {
get_field->AsStaticGetterNode()->set_owner(library_);
}
return get_field;
} else if (obj.IsFunction()) {
const Function& func = Function::Cast(obj);
ASSERT(func.is_static());
if (func.IsGetterFunction() || func.IsSetterFunction()) {
StaticGetterNode* getter = new (Z) StaticGetterNode(
ident_pos,
/* receiver */ NULL, Class::ZoneHandle(Z, func.Owner()), ident,
StaticGetterSetter::kStatic);
getter->set_owner(library_);
return getter;
} else {
return new (Z)
PrimaryNode(ident_pos, Function::ZoneHandle(Z, func.raw()));
}
} else if (obj.IsLibraryPrefix()) {
const LibraryPrefix& prefix = LibraryPrefix::Cast(obj);
ReportError(ident_pos, "illegal use of library prefix '%s'",
String::Handle(prefix.name()).ToCString());
} else {
ASSERT(obj.IsNull());
}
// Lexically unresolved primary identifiers are referenced by their name.
return new (Z) PrimaryNode(ident_pos, ident);
}
// Do a lookup for the identifier in the scope of the specified
// library prefix. This means trying to resolve it locally in all of the
// libraries present in the library prefix.
AstNode* Parser::ResolveIdentInPrefixScope(TokenPosition ident_pos,
const LibraryPrefix& prefix,
const String& ident) {
TRACE_PARSER("ResolveIdentInPrefixScope");
HANDLESCOPE(thread());
if (ident.CharAt(0) == Library::kPrivateIdentifierStart) {
// Private names are not exported by libraries. The name mangling
// of private names with a library-specific suffix usually ensures
// that _x in library A is not found when looked up from library B.
// In the pathological case where a library imports itself with
// a prefix, the name mangling would not help in hiding the private
// name, so we need to explicitly reject private names here.
return NULL;
}
Object& obj = Object::Handle(Z);
if (prefix.is_loaded() || FLAG_load_deferred_eagerly) {
obj = prefix.LookupObject(ident);
} else {
// Remember that this function depends on an import prefix of an
// unloaded deferred library. Note that parsed_function() can be
// NULL when parsing expressions outside the scope of a function.
if (parsed_function() != NULL) {
parsed_function()->AddDeferredPrefix(prefix);
}
}
const bool is_deferred = prefix.is_deferred_load();
if (obj.IsNull()) {
// Unresolved prefixed primary identifier.
return NULL;
} else if (obj.IsClass()) {
const Class& cls = Class::Cast(obj);
PrimaryNode* primary =
new (Z) PrimaryNode(ident_pos, Class::ZoneHandle(Z, cls.raw()));
if (is_deferred) {
primary->set_prefix(&prefix);
}
return primary;
} else if (obj.IsField()) {
const Field& field = Field::Cast(obj);
ASSERT(field.is_static());
AstNode* get_field = GenerateStaticFieldLookup(field, ident_pos);
ASSERT(get_field != NULL);
ASSERT(get_field->IsLoadStaticFieldNode() ||
get_field->IsStaticGetterNode());
if (get_field->IsLoadStaticFieldNode()) {
get_field->AsLoadStaticFieldNode()->set_is_deferred(is_deferred);
} else if (get_field->IsStaticGetterNode()) {
get_field->AsStaticGetterNode()->set_is_deferred(is_deferred);
get_field->AsStaticGetterNode()->set_owner(prefix);
}
return get_field;
} else if (obj.IsFunction()) {
const Function& func = Function::Cast(obj);
ASSERT(func.is_static());
if (func.IsGetterFunction() || func.IsSetterFunction()) {
StaticGetterNode* getter = new (Z) StaticGetterNode(
ident_pos,
/* receiver */ NULL, Class::ZoneHandle(Z, func.Owner()), ident,
StaticGetterSetter::kStatic);
getter->set_is_deferred(is_deferred);
getter->set_owner(prefix);
return getter;
} else {
PrimaryNode* primary =
new (Z) PrimaryNode(ident_pos, Function::ZoneHandle(Z, func.raw()));
if (is_deferred) {
primary->set_prefix(&prefix);
}
return primary;
}
}
// All possible object types are handled above.
UNREACHABLE();
return NULL;
}
// Resolve identifier. Issue an error message if
// the ident refers to a method and allow_closure_names is false.
// If the name cannot be resolved, turn it into an instance field access
// if we're compiling an instance method, or generate
// throw NoSuchMethodError if we're compiling a static method.
AstNode* Parser::ResolveIdent(TokenPosition ident_pos,
const String& ident,
bool allow_closure_names) {
TRACE_PARSER("ResolveIdent");
// First try to find the variable in the local scope (block scope or
// class scope).
AstNode* resolved = NULL;
intptr_t resolved_func_level = 0;
ResolveIdentInLocalScope(ident_pos, ident, &resolved, &resolved_func_level);
if (InGenericFunctionScope()) {
intptr_t type_param_func_level = FunctionLevel();
const TypeParameter& type_parameter =
TypeParameter::ZoneHandle(Z, innermost_function().LookupTypeParameter(
ident, &type_param_func_level));
if (!type_parameter.IsNull()) {
if ((resolved == NULL) || (resolved_func_level < type_param_func_level)) {
// The identifier is a function type parameter, possibly shadowing
// 'resolved'.
if (!Isolate::Current()->reify_generic_functions()) {
Type& type = Type::ZoneHandle(Z, Type::DynamicType());
return new (Z) TypeNode(ident_pos, type);
}
ASSERT(type_parameter.IsFinalized());
ASSERT(!type_parameter.IsMalformed());
if ((FunctionLevel() > 0) && innermost_function().HasGenericParent()) {
// Make sure that the parent function type arguments are captured.
CaptureFunctionTypeArguments();
}
return new (Z) TypeNode(ident_pos, type_parameter);
}
}
}
if (resolved == NULL) {
// Check whether the identifier is a class type parameter.
if (!current_class().IsNull()) {
TypeParameter& type_parameter = TypeParameter::ZoneHandle(
Z, current_class().LookupTypeParameter(ident));
if (!type_parameter.IsNull()) {
if (ParsingStaticMember()) {
const String& name = String::Handle(Z, type_parameter.name());
ReportError(ident_pos,
"cannot access type parameter '%s' "
"from static function",
name.ToCString());
}
type_parameter ^= CanonicalizeType(type_parameter);
ASSERT(!type_parameter.IsMalformed());
if (FunctionLevel() > 0) {
// Make sure that the class instantiator is captured.
CaptureInstantiator();
}
return new (Z) TypeNode(ident_pos, type_parameter);
}
}
// Not found in the local scope, and the name is not a type parameter.
// Try finding the variable in the library scope (current library
// and all libraries imported by it without a library prefix).
resolved = ResolveIdentInCurrentLibraryScope(ident_pos, ident);
}
if (resolved->IsPrimaryNode()) {
PrimaryNode* primary = resolved->AsPrimaryNode();
const TokenPosition primary_pos = primary->token_pos();
if (primary->primary().IsString()) {
// We got an unresolved name. If we are compiling a static
// method, evaluation of an unresolved identifier causes a
// NoSuchMethodError to be thrown. In an instance method, we convert
// the unresolved name to an instance field access, since a
// subclass might define a field with this name.
if (current_function().is_static()) {
resolved = ThrowNoSuchMethodError(ident_pos, current_class(), ident,
NULL, // No arguments.
InvocationMirror::kStatic,
InvocationMirror::kField,
NULL); // No existing function.
} else {
// Treat as call to unresolved instance field.
resolved = CallGetter(ident_pos, LoadReceiver(ident_pos), ident);
}
} else if (primary->primary().IsFunction()) {
if (allow_closure_names) {
resolved = LoadClosure(primary);
} else {
ReportError(ident_pos, "illegal reference to method '%s'",
ident.ToCString());
}
} else if (primary->primary().IsClass()) {
const Class& type_class = Class::Cast(primary->primary());
AbstractType& type =
Type::ZoneHandle(Z, Type::New(type_class, TypeArguments::Handle(Z),
primary_pos, Heap::kOld));
type ^= CanonicalizeType(type);
// Type may be malbounded, but not malformed.
ASSERT(!type.IsMalformed());
resolved =
new (Z) TypeNode(primary_pos, type, primary->is_deferred_reference());
}
}
return resolved;
}
RawAbstractType* Parser::ParseType(
ClassFinalizer::FinalizationKind finalization,
bool allow_deferred_type,
bool consume_unresolved_prefix) {
LibraryPrefix& prefix = LibraryPrefix::Handle(Z);
return ParseType(finalization, allow_deferred_type, consume_unresolved_prefix,
&prefix);
}
// Parses and returns a type or a function type.
RawAbstractType* Parser::ParseTypeOrFunctionType(
bool allow_void,
ClassFinalizer::FinalizationKind finalization) {
TRACE_PARSER("ParseTypeOrFunctionType");
AbstractType& type = AbstractType::Handle(Z);
if (CurrentToken() == Token::kVOID) {
TokenPosition void_pos = TokenPos();
type = Type::VoidType();
ConsumeToken();
// 'void' is always allowed as result type of a function type.
if (!allow_void && !IsFunctionTypeSymbol()) {
ReportError(void_pos, "'void' not allowed here");
}
} else if (!IsFunctionTypeSymbol()) {
// Including 'Function' not followed by '(' or '<'.
// It is too early to resolve the type here, since it can
// refer to a not yet declared function type parameter.
type = ParseType(ClassFinalizer::kDoNotResolve);
}
while (IsFunctionTypeSymbol()) {
if (type.IsNull()) {
type = Type::DynamicType();
}
// 'type' is the result type of the function type.
type = ParseFunctionType(type, ClassFinalizer::kDoNotResolve);
}
// At this point, all type parameters have been parsed, resolve the type.
if (finalization == ClassFinalizer::kIgnore) {
return Type::DynamicType();
}
if (finalization >= ClassFinalizer::kResolveTypeParameters) {
ResolveTypeParameters(&type);
if (finalization >= ClassFinalizer::kCanonicalize) {
type ^= CanonicalizeType(type);
}
}
return type.raw();
}
// Parses and returns a function type.
// If 'result_type' is not null, parsing of the result type is skipped.
RawType* Parser::ParseFunctionType(
const AbstractType& result_type,
ClassFinalizer::FinalizationKind finalization) {
TRACE_PARSER("ParseFunctionType");
AbstractType& type = AbstractType::Handle(Z, result_type.raw());
if (type.IsNull()) {
if (CurrentToken() == Token::kVOID) {
ConsumeToken();
type = Type::VoidType();
} else if (IsFunctionTypeSymbol()) {
type = Type::DynamicType();
} else {
// Including 'Function' not followed by '(' or '<'.
// It is too early to resolve the type here, since it can
// refer to a not yet declared function type parameter.
type = ParseType(ClassFinalizer::kDoNotResolve);
}
}
if (!IsSymbol(Symbols::Function())) {
ReportError("'Function' expected");
}
do {
ConsumeToken();
const Function& signature_function = Function::Handle(
Z, Function::NewSignatureFunction(current_class(), innermost_function(),
TokenPosition::kNoSource));
innermost_function_ = signature_function.raw();
signature_function.set_result_type(type);
// The result type may refer to the signature function's type parameters,
// but was not parsed in the scope of the signature function. Adjust.
type.SetScopeFunction(signature_function);
// Parse optional type parameters.
if (CurrentToken() == Token::kLT) {
ParseTypeParameters(false); // Not parameterizing class, but function.
}
ParamList params;
// We do not yet allow Function of any arity, so expect parameter list.
CheckToken(Token::kLPAREN, "formal parameter list expected");
// Add implicit closure object parameter. Do not specify a token position,
// since it would make no sense after function type canonicalization.
params.AddFinalParameter(TokenPosition::kNoSource,
&Symbols::ClosureParameter(),
&Object::dynamic_type());
const bool use_function_type_syntax = true;
const bool allow_explicit_default_values = false;
const bool evaluate_metadata = false;
ParseFormalParameterList(use_function_type_syntax,
allow_explicit_default_values, evaluate_metadata,
&params);
AddFormalParamsToFunction(&params, signature_function);
innermost_function_ = innermost_function_.parent_function();
if (innermost_function().IsNull() && current_class().IsTypedefClass() &&
!IsFunctionTypeSymbol()) {
// The last parsed signature function is the typedef signature function.
// Set it in the typedef class before building the signature type.
current_class().set_signature_function(signature_function);
}
type = signature_function.SignatureType();
} while (IsFunctionTypeSymbol());
// At this point, all type parameters have been parsed, resolve the type.
if (finalization == ClassFinalizer::kIgnore) {
return Type::DynamicType();
}
if (finalization >= ClassFinalizer::kResolveTypeParameters) {
ResolveTypeParameters(&type);
if (finalization >= ClassFinalizer::kCanonicalize) {
type ^= CanonicalizeType(type);
}
}
return Type::RawCast(type.raw());
}
// Parses type = [ident "."] ident ["<" type { "," type } ">"], then resolve and
// finalize it according to the given type finalization mode.
// Returns type and sets prefix.
RawAbstractType* Parser::ParseType(
ClassFinalizer::FinalizationKind finalization,
bool allow_deferred_type,
bool consume_unresolved_prefix,
LibraryPrefix* prefix) {
TRACE_PARSER("ParseType");
CheckToken(Token::kIDENT, "type name expected");
TokenPosition ident_pos = TokenPos();
String& type_name = String::Handle(Z);
if (finalization == ClassFinalizer::kIgnore) {
if (!is_top_level_ && (current_block_ != NULL)) {
// Add the library prefix or type class name to the list of referenced
// names of this scope, even if the type is ignored.
current_block_->scope->AddReferencedName(TokenPos(), *CurrentLiteral());
}
SkipQualIdent();
} else {
*prefix = ParsePrefix();
if (!prefix->IsNull()) {
ExpectToken(Token::kPERIOD);
}
type_name = CurrentLiteral()->raw();
ConsumeToken();
// Check whether we have a malformed qualified type name if the caller
// requests to consume unresolved prefix names:
// If we didn't see a valid prefix but the identifier is followed by
// a period and another identifier, consume the qualified identifier
// and create a malformed type.
if (consume_unresolved_prefix && prefix->IsNull() &&
(CurrentToken() == Token::kPERIOD) &&
(Token::IsIdentifier(LookaheadToken(1)))) {
if (!is_top_level_ && (current_block_ != NULL)) {
// Add the unresolved prefix name to the list of referenced
// names of this scope.
current_block_->scope->AddReferencedName(TokenPos(), type_name);
}
ConsumeToken(); // Period token.
ASSERT(IsIdentifier());
String& qualified_name = String::Handle(Z, type_name.raw());
qualified_name =
String::Concat(qualified_name, Symbols::Dot(), allocation_space_);
qualified_name =
String::Concat(qualified_name, *CurrentLiteral(), allocation_space_);
ConsumeToken();
// The type is malformed. Skip over its type arguments.
ParseTypeArguments(ClassFinalizer::kIgnore);
return ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(Z), // No previous error.
script_, ident_pos, "qualified name '%s' does not refer to a type",
qualified_name.ToCString());
}
// If parsing inside a local scope, check whether the type name
// is shadowed by a local declaration.
if (!is_top_level_ && (prefix->IsNull()) &&
ResolveIdentInLocalScope(ident_pos, type_name, NULL, NULL)) {
// The type is malformed. Skip over its type arguments.
ParseTypeArguments(ClassFinalizer::kIgnore);
return ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(Z), // No previous error.
script_, ident_pos, "using '%s' in this context is invalid",
type_name.ToCString());
}
if ((!FLAG_load_deferred_eagerly || !allow_deferred_type) &&
!prefix->IsNull() && prefix->is_deferred_load()) {
// If deferred prefixes are allowed but it is not yet loaded,
// remember that this function depends on the prefix.
if (allow_deferred_type && !prefix->is_loaded()) {
if (parsed_function() != NULL) {
parsed_function()->AddDeferredPrefix(*prefix);
}
}
// If the deferred prefixes are not allowed, or if the prefix is not yet
// loaded when finalization is requested, return a malformed type.
// Otherwise, handle resolution below, as needed.
if (!allow_deferred_type ||
(!prefix->is_loaded() &&
(finalization > ClassFinalizer::kResolveTypeParameters))) {
ParseTypeArguments(ClassFinalizer::kIgnore);
return ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(Z), // No previous error.
script_, ident_pos,
!prefix->is_loaded() && allow_deferred_type
? "deferred type '%s.%s' is not yet loaded"
: "using deferred type '%s.%s' is invalid",
String::Handle(Z, prefix->name()).ToCString(),
type_name.ToCString());
}
}
}
Object& type_class = Object::Handle(Z);
// Leave type_class as null if type finalization mode is kIgnore.
if (finalization != ClassFinalizer::kIgnore) {
type_class = UnresolvedClass::New(*prefix, type_name, ident_pos);
}
TypeArguments& type_arguments =
TypeArguments::Handle(Z, ParseTypeArguments(finalization));
if (finalization == ClassFinalizer::kIgnore) {
return Type::DynamicType();
}
AbstractType& type = AbstractType::Handle(
Z, Type::New(type_class, type_arguments, ident_pos, Heap::kOld));
if (finalization >= ClassFinalizer::kResolveTypeParameters) {
ResolveTypeParameters(&type);
if (finalization >= ClassFinalizer::kCanonicalize) {
type ^= CanonicalizeType(type);
}
}
return type.raw();
}
void Parser::CheckConstructorCallTypeArguments(
TokenPosition pos,
const Function& constructor,
const TypeArguments& type_arguments) {
if (!type_arguments.IsNull()) {
const Class& constructor_class = Class::Handle(Z, constructor.Owner());
ASSERT(!constructor_class.IsNull());
ASSERT(constructor_class.is_finalized());
ASSERT(type_arguments.IsCanonical());
// Do not report the expected vs. actual number of type arguments, because
// the type argument vector is flattened and raw types are allowed.
if (type_arguments.Length() != constructor_class.NumTypeArguments()) {
ReportError(pos, "wrong number of type arguments passed to constructor");
}
}
}
// Parse "[" [ expr { "," expr } ["," ] "]".
// Note: if the list literal is empty and the brackets have no whitespace
// between them, the scanner recognizes the opening and closing bracket
// as one token of type Token::kINDEX.
AstNode* Parser::ParseListLiteral(TokenPosition type_pos,
bool is_const,
const TypeArguments& type_arguments) {
TRACE_PARSER("ParseListLiteral");
ASSERT(type_pos.IsReal());
ASSERT(CurrentToken() == Token::kLBRACK || CurrentToken() == Token::kINDEX);
const TokenPosition literal_pos = TokenPos();
if (is_const) {
Instance& existing_const = Instance::ZoneHandle(Z);
if (GetCachedConstant(literal_pos, &existing_const)) {
SkipListLiteral();
return new (Z) LiteralNode(literal_pos, existing_const);
}
}
bool is_empty_literal = CurrentToken() == Token::kINDEX;
ConsumeToken();
AbstractType& element_type = Type::ZoneHandle(Z, Type::DynamicType());
TypeArguments& list_type_arguments =
TypeArguments::ZoneHandle(Z, type_arguments.raw());
// If no type argument vector is provided, leave it as null, which is
// equivalent to using dynamic as the type argument for the element type.
if (!list_type_arguments.IsNull()) {
ASSERT(list_type_arguments.Length() > 0);
// List literals take a single type argument.
if (list_type_arguments.Length() == 1) {
element_type = list_type_arguments.TypeAt(0);
ASSERT(!element_type.IsMalformed()); // Would be mapped to dynamic.
ASSERT(!element_type.IsMalbounded()); // No declared bound in List.
if (element_type.IsDynamicType()) {
list_type_arguments = TypeArguments::null();
} else if (is_const && !element_type.IsInstantiated()) {
ReportError(type_pos,
"the type argument of a constant list literal cannot "
"include a type variable");
}
} else {
if (I->error_on_bad_type()) {
ReportError(type_pos,
"a list literal takes one type argument specifying "
"the element type");
}
// Ignore type arguments.
list_type_arguments = TypeArguments::null();
}
}
ASSERT(list_type_arguments.IsNull() || (list_type_arguments.Length() == 1));
const Class& array_class = Class::Handle(Z, I->object_store()->array_class());
Type& type = Type::ZoneHandle(
Z, Type::New(array_class, list_type_arguments, type_pos, Heap::kOld));
type ^= CanonicalizeType(type);
GrowableArray<AstNode*> element_list;
// Parse the list elements. Note: there may be an optional extra
// comma after the last element.
if (!is_empty_literal) {
const bool saved_mode = SetAllowFunctionLiterals(true);
while (CurrentToken() != Token::kRBRACK) {
const TokenPosition element_pos = TokenPos();
AstNode* element = ParseExpr(is_const, kConsumeCascades);
if (I->type_checks() && !is_const && !element_type.IsDynamicType()) {
element = new (Z) AssignableNode(element_pos, element, element_type,
Symbols::ListLiteralElement());
}
element_list.Add(element);
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
} else if (CurrentToken() != Token::kRBRACK) {
ReportError("comma or ']' expected");
}
}
ExpectToken(Token::kRBRACK);
SetAllowFunctionLiterals(saved_mode);
}
if (is_const) {
// Allocate and initialize the const list at compile time.
if ((element_list.length() == 0) && list_type_arguments.IsNull()) {
return new (Z) LiteralNode(literal_pos, Object::empty_array());
}
Array& const_list =
Array::ZoneHandle(Z, Array::New(element_list.length(), Heap::kOld));
const_list.SetTypeArguments(
TypeArguments::Handle(Z, list_type_arguments.Canonicalize()));
Error& bound_error = Error::Handle(Z);
for (int i = 0; i < element_list.length(); i++) {
AstNode* elem = element_list[i];
// Arguments have been evaluated to a literal value already.
ASSERT(elem->IsLiteralNode());
ASSERT(!is_top_level_); // We cannot check unresolved types.
if (I->type_checks() && !element_type.IsDynamicType() &&
(!elem->AsLiteralNode()->literal().IsNull() &&
!elem->AsLiteralNode()->literal().IsInstanceOf(
element_type, Object::null_type_arguments(),
Object::null_type_arguments(), &bound_error))) {
// If the failure is due to a bound error, display it instead.
if (!bound_error.IsNull()) {
ReportError(bound_error);
} else {
ReportError(
elem->AsLiteralNode()->token_pos(),
"list literal element at index %d must be "
"a constant of type '%s'",
i, String::Handle(Z, element_type.UserVisibleName()).ToCString());
}
}
const_list.SetAt(i, elem->AsLiteralNode()->literal());
}
const_list.MakeImmutable();
const_list ^= TryCanonicalize(const_list, literal_pos);
CacheConstantValue(literal_pos, const_list);
return new (Z) LiteralNode(literal_pos, const_list);
} else {
// Factory call at runtime.
const Class& factory_class =
Class::Handle(Z, Library::LookupCoreClass(Symbols::List()));
ASSERT(!factory_class.IsNull());
const Function& factory_method = Function::ZoneHandle(
Z, factory_class.LookupFactory(
Library::PrivateCoreLibName(Symbols::ListLiteralFactory())));
ASSERT(!factory_method.IsNull());
if (!list_type_arguments.IsNull() &&
!list_type_arguments.IsInstantiated() && (FunctionLevel() > 0)) {
// Make sure that the instantiators are captured.
CaptureAllInstantiators();
}
TypeArguments& factory_type_args =
TypeArguments::ZoneHandle(Z, list_type_arguments.raw());
// If the factory class extends other parameterized classes, adjust the
// type argument vector.
if (!factory_type_args.IsNull() && (factory_class.NumTypeArguments() > 1)) {
ASSERT(factory_type_args.Length() == 1);
Type& factory_type = Type::Handle(
Z, Type::New(factory_class, factory_type_args, type_pos, Heap::kOld));
// It is not strictly necessary to canonicalize factory_type, but only its
// type argument vector.
factory_type ^= CanonicalizeType(factory_type);
factory_type_args = factory_type.arguments();
ASSERT(factory_type_args.Length() == factory_class.NumTypeArguments());
ASSERT(factory_type_args.IsCanonical());
} else {
factory_type_args = factory_type_args.Canonicalize();
}
ArgumentListNode* factory_param = new (Z) ArgumentListNode(literal_pos);
if (element_list.length() == 0) {
LiteralNode* empty_array_literal =
new (Z) LiteralNode(TokenPos(), Object::empty_array());
factory_param->Add(empty_array_literal);
} else {
ArrayNode* list = new (Z) ArrayNode(TokenPos(), type, element_list);
factory_param->Add(list);
}
return CreateConstructorCallNode(literal_pos, factory_type_args,
factory_method, factory_param);
}
}
ConstructorCallNode* Parser::CreateConstructorCallNode(
TokenPosition token_pos,
const TypeArguments& type_arguments,
const Function& constructor,
ArgumentListNode* arguments) {
if (!type_arguments.IsNull() && !type_arguments.IsInstantiated()) {
EnsureExpressionTemp();
}
return new (Z)
ConstructorCallNode(token_pos, type_arguments, constructor, arguments);
}
static void AddKeyValuePair(GrowableArray<AstNode*>* pairs,
bool is_const,
AstNode* key,
AstNode* value) {
if (is_const) {
ASSERT(key->IsLiteralNode());
const Instance& new_key = key->AsLiteralNode()->literal();
for (int i = 0; i < pairs->length(); i += 2) {
const Instance& key_i = (*pairs)[i]->AsLiteralNode()->literal();
// The keys of a compile time constant map are compile time
// constants, i.e. canonicalized values. Thus, we can compare
// raw pointers to check for equality.
if (new_key.raw() == key_i.raw()) {
// Duplicate key found. The new value replaces the previously
// defined value.
(*pairs)[i + 1] = value;
return;
}
}
}
pairs->Add(key);
pairs->Add(value);
}
AstNode* Parser::ParseMapLiteral(TokenPosition type_pos,
bool is_const,
const TypeArguments& type_arguments) {
TRACE_PARSER("ParseMapLiteral");
ASSERT(type_pos.IsReal());
ASSERT(CurrentToken() == Token::kLBRACE);
const TokenPosition literal_pos = TokenPos();
if (is_const) {
Instance& existing_const = Instance::ZoneHandle(Z);
if (GetCachedConstant(literal_pos, &existing_const)) {
SkipMapLiteral();
return new (Z) LiteralNode(literal_pos, existing_const);
}
}
ConsumeToken(); // Opening brace.
AbstractType& key_type = Type::ZoneHandle(Z, Type::DynamicType());
AbstractType& value_type = Type::ZoneHandle(Z, Type::DynamicType());
TypeArguments& map_type_arguments =
TypeArguments::ZoneHandle(Z, type_arguments.raw());
// If no type argument vector is provided, leave it as null, which is
// equivalent to using dynamic as the type argument for the both key and value
// types.
if (!map_type_arguments.IsNull()) {
ASSERT(map_type_arguments.Length() > 0);
// Map literals take two type arguments.
if (map_type_arguments.Length() == 2) {
key_type = map_type_arguments.TypeAt(0);
value_type = map_type_arguments.TypeAt(1);
// Malformed type arguments are mapped to dynamic.
ASSERT(!key_type.IsMalformed() && !value_type.IsMalformed());
// No declared bounds in Map.
ASSERT(!key_type.IsMalbounded() && !value_type.IsMalbounded());
if (key_type.IsDynamicType() && value_type.IsDynamicType()) {
map_type_arguments = TypeArguments::null();
} else if (is_const && !type_arguments.IsInstantiated()) {
ReportError(type_pos,
"the type arguments of a constant map literal cannot "
"include a type variable");
}
} else {
if (I->error_on_bad_type()) {
ReportError(type_pos,
"a map literal takes two type arguments specifying "
"the key type and the value type");
}
// Ignore type arguments.
map_type_arguments = TypeArguments::null();
}
}
ASSERT(map_type_arguments.IsNull() || (map_type_arguments.Length() == 2));
map_type_arguments ^= map_type_arguments.Canonicalize();
GrowableArray<AstNode*> kv_pairs_list;
// Parse the map entries. Note: there may be an optional extra
// comma after the last entry.
while (CurrentToken() != Token::kRBRACE) {
const bool saved_mode = SetAllowFunctionLiterals(true);
const TokenPosition key_pos = TokenPos();
AstNode* key = ParseExpr(is_const, kConsumeCascades);
if (I->type_checks() && !is_const && !key_type.IsDynamicType()) {
key = new (Z)
AssignableNode(key_pos, key, key_type, Symbols::ListLiteralElement());
}
if (is_const) {
ASSERT(key->IsLiteralNode());
const Instance& key_value = key->AsLiteralNode()->literal();
if (key_value.IsDouble()) {
ReportError(key_pos, "key value must not be of type double");
}
if (!key_value.IsInteger() && !key_value.IsString() &&
(key_value.clazz() != I->object_store()->symbol_class()) &&
ImplementsEqualOperator(Z, key_value)) {
ReportError(key_pos, "key value must not implement operator ==");
}
}
ExpectToken(Token::kCOLON);
const TokenPosition value_pos = TokenPos();
AstNode* value = ParseExpr(is_const, kConsumeCascades);
SetAllowFunctionLiterals(saved_mode);
if (I->type_checks() && !is_const && !value_type.IsDynamicType()) {
value = new (Z) AssignableNode(value_pos, value, value_type,
Symbols::ListLiteralElement());
}
AddKeyValuePair(&kv_pairs_list, is_const, key, value);
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
} else if (CurrentToken() != Token::kRBRACE) {
ReportError("comma or '}' expected");
}
}
ASSERT(kv_pairs_list.length() % 2 == 0);
ExpectToken(Token::kRBRACE);
if (is_const) {
// Create the key-value pair array, canonicalize it and then create
// the immutable map object with it. This all happens at compile time.
// The resulting immutable map object is returned as a literal.
// First, create the canonicalized key-value pair array.
Array& key_value_array =
Array::ZoneHandle(Z, Array::New(kv_pairs_list.length(), Heap::kOld));
AbstractType& arg_type = Type::Handle(Z);
Error& bound_error = Error::Handle(Z);
for (int i = 0; i < kv_pairs_list.length(); i++) {
AstNode* arg = kv_pairs_list[i];
// Arguments have been evaluated to a literal value already.
ASSERT(arg->IsLiteralNode());
ASSERT(!is_top_level_); // We cannot check unresolved types.
if (I->type_checks()) {
if ((i % 2) == 0) {
// Check key type.
arg_type = key_type.raw();
} else {
// Check value type.
arg_type = value_type.raw();
}
if (!arg_type.IsDynamicType() &&
(!arg->AsLiteralNode()->literal().IsNull() &&
!arg->AsLiteralNode()->literal().IsInstanceOf(
arg_type, Object::null_type_arguments(),
Object::null_type_arguments(), &bound_error))) {
// If the failure is due to a bound error, display it.
if (!bound_error.IsNull()) {
ReportError(bound_error);
} else {
ReportError(
arg->AsLiteralNode()->token_pos(),
"map literal %s at index %d must be "
"a constant of type '%s'",
((i % 2) == 0) ? "key" : "value", i >> 1,
String::Handle(Z, arg_type.UserVisibleName()).ToCString());
}
}
}
key_value_array.SetAt(i, arg->AsLiteralNode()->literal());
}
key_value_array.MakeImmutable();
key_value_array ^= TryCanonicalize(key_value_array, TokenPos());
// Construct the map object.
const Class& immutable_map_class =
Class::Handle(Z, Library::LookupCoreClass(Symbols::ImmutableMap()));
ASSERT(!immutable_map_class.IsNull());
// If the immutable map class extends other parameterized classes, we need
// to adjust the type argument vector. This is currently not the case.
ASSERT(immutable_map_class.NumTypeArguments() == 2);
ArgumentListNode* constr_args = new (Z) ArgumentListNode(TokenPos());
constr_args->Add(new (Z) LiteralNode(literal_pos, key_value_array));
const Function& map_constr = Function::ZoneHandle(
Z, immutable_map_class.LookupConstructorAllowPrivate(
Symbols::ImmutableMapConstructor()));
ASSERT(!map_constr.IsNull());
const Object& constructor_result = Object::Handle(
Z, EvaluateConstConstructorCall(immutable_map_class, map_type_arguments,
map_constr, constr_args));
if (constructor_result.IsUnhandledException()) {
ReportErrors(Error::Cast(constructor_result), script_, literal_pos,
"error executing const Map constructor");
} else {
const Instance& const_instance = Instance::Cast(constructor_result);
CacheConstantValue(literal_pos, const_instance);
return new (Z) LiteralNode(literal_pos,
Instance::ZoneHandle(Z, const_instance.raw()));
}
} else {
// Factory call at runtime.
const Class& factory_class =
Class::Handle(Z, Library::LookupCoreClass(Symbols::Map()));
ASSERT(!factory_class.IsNull());
const Function& factory_method = Function::ZoneHandle(
Z, factory_class.LookupFactory(
Library::PrivateCoreLibName(Symbols::MapLiteralFactory())));
ASSERT(!factory_method.IsNull());
if (!map_type_arguments.IsNull() && !map_type_arguments.IsInstantiated() &&
(FunctionLevel() > 0)) {
// Make sure that the instantiators are captured.
CaptureAllInstantiators();
}
TypeArguments& factory_type_args =
TypeArguments::ZoneHandle(Z, map_type_arguments.raw());
// If the factory class extends other parameterized classes, adjust the
// type argument vector.
if (!factory_type_args.IsNull() && (factory_class.NumTypeArguments() > 2)) {
ASSERT(factory_type_args.Length() == 2);
Type& factory_type = Type::Handle(
Z, Type::New(factory_class, factory_type_args, type_pos, Heap::kOld));
// It is not strictly necessary to canonicalize factory_type, but only its
// type argument vector.
factory_type ^= CanonicalizeType(factory_type);
factory_type_args = factory_type.arguments();
ASSERT(factory_type_args.Length() == factory_class.NumTypeArguments());
ASSERT(factory_type_args.IsCanonical());
} else {
factory_type_args = factory_type_args.Canonicalize();
}
ArgumentListNode* factory_param = new (Z) ArgumentListNode(literal_pos);
// The kv_pair array is temporary and of element type dynamic. It is passed
// to the factory to initialize a properly typed map. Pass a pre-allocated
// array for the common empty map literal case.
if (kv_pairs_list.length() == 0) {
LiteralNode* empty_array_literal =
new (Z) LiteralNode(TokenPos(), Object::empty_array());
factory_param->Add(empty_array_literal);
} else {
ArrayNode* kv_pairs = new (Z) ArrayNode(
TokenPos(), Type::ZoneHandle(Z, Type::ArrayType()), kv_pairs_list);
factory_param->Add(kv_pairs);
}
return CreateConstructorCallNode(literal_pos, factory_type_args,
factory_method, factory_param);
}
UNREACHABLE();
return NULL;
}
AstNode* Parser::ParseCompoundLiteral() {
TRACE_PARSER("ParseCompoundLiteral");
bool is_const = false;
if (CurrentToken() == Token::kCONST) {
is_const = true;
ConsumeToken();
}
const TokenPosition type_pos = TokenPos();
TypeArguments& type_arguments = TypeArguments::Handle(
Z, ParseTypeArguments(ClassFinalizer::kCanonicalize));
// Malformed type arguments are mapped to dynamic, so we will not encounter
// them here.
// Map and List interfaces do not declare bounds on their type parameters, so
// we will not see malbounded type arguments here.
AstNode* primary = NULL;
if ((CurrentToken() == Token::kLBRACK) || (CurrentToken() == Token::kINDEX)) {
primary = ParseListLiteral(type_pos, is_const, type_arguments);
} else if (CurrentToken() == Token::kLBRACE) {
primary = ParseMapLiteral(type_pos, is_const, type_arguments);
} else {
UnexpectedToken();
}
return primary;
}
AstNode* Parser::ParseSymbolLiteral() {
ASSERT(CurrentToken() == Token::kHASH);
ConsumeToken();
TokenPosition symbol_pos = TokenPos();
String& symbol = String::ZoneHandle(Z);
if (IsIdentifier()) {
symbol = CurrentLiteral()->raw();
ConsumeToken();
GrowableHandlePtrArray<const String> pieces(Z, 3);
pieces.Add(symbol);
while (CurrentToken() == Token::kPERIOD) {
pieces.Add(Symbols::Dot());
ConsumeToken();
pieces.Add(*ExpectIdentifier("identifier expected"));
}
symbol = Symbols::FromConcatAll(T, pieces);
} else if (Token::CanBeOverloaded(CurrentToken())) {
symbol = Symbols::Token(CurrentToken()).raw();
ConsumeToken();
} else {
ReportError("illegal symbol literal");
}
ASSERT(symbol.IsSymbol());
Instance& symbol_instance = Instance::ZoneHandle(Z);
if (GetCachedConstant(symbol_pos, &symbol_instance)) {
return new (Z) LiteralNode(symbol_pos, symbol_instance);
}
// Call Symbol class constructor to create a symbol instance.
const Class& symbol_class = Class::Handle(I->object_store()->symbol_class());
ASSERT(!symbol_class.IsNull());
ArgumentListNode* constr_args = new (Z) ArgumentListNode(symbol_pos);
constr_args->Add(new (Z) LiteralNode(symbol_pos, symbol));
const Function& constr = Function::ZoneHandle(
Z, symbol_class.LookupConstructor(Symbols::SymbolCtor()));
ASSERT(!constr.IsNull());
const Object& result =
Object::Handle(Z, EvaluateConstConstructorCall(
symbol_class, TypeArguments::Handle(Z), constr,
constr_args, /*obfuscate_symbol_instances=*/false));
if (result.IsUnhandledException()) {
ReportErrors(Error::Cast(result), script_, symbol_pos,
"error executing const Symbol constructor");
}
symbol_instance ^= result.raw();
CacheConstantValue(symbol_pos, symbol_instance);
return new (Z) LiteralNode(symbol_pos, symbol_instance);
}
static String& BuildConstructorName(Thread* thread,
const String& type_class_name,
const String* named_constructor) {
// By convention, the static function implementing a named constructor 'C'
// for class 'A' is labeled 'A.C', and the static function implementing the
// unnamed constructor for class 'A' is labeled 'A.'.
// This convention prevents users from explicitly calling constructors.
Zone* zone = thread->zone();
String& constructor_name =
String::Handle(zone, Symbols::FromDot(thread, type_class_name));
if (named_constructor != NULL) {
constructor_name =
Symbols::FromConcat(thread, constructor_name, *named_constructor);
}
return constructor_name;
}
AstNode* Parser::ParseNewOperator(Token::Kind op_kind) {
TRACE_PARSER("ParseNewOperator");
const TokenPosition new_pos = TokenPos();
ASSERT((op_kind == Token::kNEW) || (op_kind == Token::kCONST));
bool is_const = (op_kind == Token::kCONST);
if (!IsIdentifier()) {
ReportError("type name expected");
}
TokenPosition type_pos = TokenPos();
// Can't allocate const objects of a deferred type.
const bool allow_deferred_type = !is_const;
const Token::Kind la3 = LookaheadToken(3);
const bool consume_unresolved_prefix =
(la3 == Token::kLT) || (la3 == Token::kPERIOD);
LibraryPrefix& prefix = LibraryPrefix::ZoneHandle(Z);
AbstractType& type = AbstractType::ZoneHandle(
Z, ParseType(ClassFinalizer::kCanonicalize, allow_deferred_type,
consume_unresolved_prefix, &prefix));
if (FLAG_load_deferred_eagerly && !prefix.IsNull() &&
prefix.is_deferred_load() && !prefix.is_loaded()) {
// Add runtime check.
Type& malformed_type = Type::ZoneHandle(Z);
malformed_type = ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(Z), // No previous error.
script_, type_pos, "deferred type '%s.%s' is not yet loaded",
String::Handle(Z, prefix.name()).ToCString(),
String::Handle(type.Name()).ToCString());
// Note: Adding a statement to current block is a hack, parsing an
// expression should have no side-effect.
current_block_->statements->Add(
ThrowTypeError(type_pos, malformed_type, &prefix));
}
// In case the type is malformed, throw a dynamic type error after finishing
// parsing the instance creation expression.
if (!type.IsMalformed() && (type.IsTypeParameter() || type.IsDynamicType())) {
// Replace the type with a malformed type.
type = ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(Z), // No previous error.
script_, type_pos, "%s'%s' cannot be instantiated",
type.IsTypeParameter() ? "type parameter " : "",
type.IsTypeParameter()
? String::Handle(Z, type.UserVisibleName()).ToCString()
: "dynamic");
}
// Attempting to instantiate an enum type is a compile-time error.
Class& type_class = Class::Handle(Z, type.type_class());
if (type_class.is_enum_class()) {
ReportError(new_pos, "enum type '%s' can not be instantiated",
String::Handle(Z, type_class.Name()).ToCString());
}
// The type can be followed by an optional named constructor identifier.
// Note that we tell ParseType() above not to consume it as part of
// a misinterpreted qualified identifier. Only a valid library
// prefix is accepted as qualifier.
String* named_constructor = NULL;
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
named_constructor = ExpectIdentifier("name of constructor expected");
}
// Parse constructor parameters.
TokenPosition call_pos = TokenPos();
CheckToken(Token::kLPAREN);
ArgumentListNode* arguments =
ParseActualParameters(NULL, TypeArguments::ZoneHandle(Z), is_const);
// Parsing is complete, so we can return a throw in case of a malformed or
// malbounded type or report a compile-time error if the constructor is const.
if (type.IsMalformedOrMalbounded()) {
if (is_const) {
const Error& error = Error::Handle(Z, type.error());
ReportError(error);
}
if (arguments->length() > 0) {
// Evaluate arguments for side-effects and throw.
LetNode* error_result = new (Z) LetNode(type_pos);
for (intptr_t i = 0; i < arguments->length(); ++i) {
error_result->AddNode(arguments->NodeAt(i));
}
error_result->AddNode(ThrowTypeError(type_pos, type));
return error_result;
}
return ThrowTypeError(type_pos, type);
}
// Resolve the type and optional identifier to a constructor or factory.
String& type_class_name = String::Handle(Z, type_class.Name());
TypeArguments& type_arguments =
TypeArguments::ZoneHandle(Z, type.arguments());
// A constructor has an implicit 'this' parameter (instance to construct)
// and a factory has an implicit 'this' parameter (type_arguments).
intptr_t arguments_length = arguments->length() + 1;
// An additional type check of the result of a redirecting factory may be
// required.
AbstractType& type_bound = AbstractType::ZoneHandle(Z);
// Make sure that an appropriate constructor exists.
String& constructor_name =
BuildConstructorName(T, type_class_name, named_constructor);
Function& constructor =
Function::ZoneHandle(Z, type_class.LookupConstructor(constructor_name));
if (constructor.IsNull()) {
constructor = type_class.LookupFactory(constructor_name);
if (constructor.IsNull()) {
const String& external_constructor_name =
(named_constructor ? constructor_name : type_class_name);
// Replace the type with a malformed type and compile a throw or report a
// compile-time error if the constructor is const.
if (is_const) {
type = ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(Z), // No previous error.
script_, call_pos,
"class '%s' has no constructor or factory named '%s'",
String::Handle(Z, type_class.Name()).ToCString(),
external_constructor_name.ToCString());
ReportError(Error::Handle(Z, type.error()));
}
return ThrowNoSuchMethodError(
call_pos, type_class, external_constructor_name, arguments,
InvocationMirror::kConstructor, InvocationMirror::kMethod,
NULL); // No existing function.
} else if (constructor.IsRedirectingFactory()) {
ClassFinalizer::ResolveRedirectingFactory(type_class, constructor);
Type& redirect_type = Type::ZoneHandle(Z, constructor.RedirectionType());
if (!redirect_type.IsMalformedOrMalbounded() &&
!redirect_type.IsInstantiated()) {
// No generic constructors allowed.
ASSERT(redirect_type.IsInstantiated(kFunctions));
// The type arguments of the redirection type are instantiated from the
// type arguments of the parsed type of the 'new' or 'const' expression.
Error& error = Error::Handle(Z);
redirect_type ^= redirect_type.InstantiateFrom(
type_arguments, Object::null_type_arguments(), kNoneFree, &error,
NULL, // instantiation_trail
NULL, // bound_trail
Heap::kOld);
if (!error.IsNull()) {
redirect_type = ClassFinalizer::NewFinalizedMalformedType(
error, script_, call_pos,
"redirecting factory type '%s' cannot be instantiated",
String::Handle(Z, redirect_type.UserVisibleName()).ToCString());
}
}
if (!redirect_type.HasResolvedTypeClass()) {
// If the redirection type is unresolved, we convert the allocation
// into throwing a type error.
const UnresolvedClass& cls =
UnresolvedClass::Handle(Z, redirect_type.unresolved_class());
const LibraryPrefix& prefix = LibraryPrefix::Cast(
Object::Handle(Z, cls.library_or_library_prefix()));
if (!prefix.IsNull() && !prefix.is_loaded() &&
!FLAG_load_deferred_eagerly) {
// If the redirection type is unresolved because it refers to
// an unloaded deferred prefix, mark this function as depending
// on the library prefix. It will then get invalidated when the
// prefix is loaded.
parsed_function()->AddDeferredPrefix(prefix);
}
redirect_type = ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(Z), script_, call_pos,
"redirection type '%s' is not loaded",
String::Handle(Z, redirect_type.UserVisibleName()).ToCString());
}
if (redirect_type.IsMalformedOrMalbounded()) {
if (is_const) {
ReportError(Error::Handle(Z, redirect_type.error()));
}
return ThrowTypeError(redirect_type.token_pos(), redirect_type);
}
if (I->type_checks() &&
!redirect_type.IsSubtypeOf(type, NULL, NULL, Heap::kOld)) {
// Additional type checking of the result is necessary.
type_bound = type.raw();
}
type = redirect_type.raw();
type_class = type.type_class();
type_class_name = type_class.Name();
type_arguments = type.arguments();
constructor = constructor.RedirectionTarget();
constructor_name = constructor.name();
ASSERT(!constructor.IsNull());
}
}
ASSERT(!constructor.IsNull());
// It is a compile time error to instantiate a const instance of an
// abstract class. Factory methods are ok.
if (is_const && type_class.is_abstract() && !constructor.IsFactory()) {
ReportError(new_pos, "cannot instantiate abstract class");
}
// It is ok to call a factory method of an abstract class, but it is
// a dynamic error to instantiate an abstract class.
if (type_class.is_abstract() && !constructor.IsFactory()) {
// Evaluate arguments before throwing.
LetNode* result = new (Z) LetNode(call_pos);
for (intptr_t i = 0; i < arguments->length(); ++i) {
result->AddNode(arguments->NodeAt(i));
}
ArgumentListNode* error_arguments = new (Z) ArgumentListNode(type_pos);
error_arguments->Add(new (Z) LiteralNode(
TokenPos(),
Integer::ZoneHandle(Z, Integer::New(type_pos.value(), Heap::kOld))));
error_arguments->Add(new (Z) LiteralNode(
TokenPos(), String::ZoneHandle(Z, type_class_name.raw())));
result->AddNode(MakeStaticCall(
Symbols::AbstractClassInstantiationError(),
Library::PrivateCoreLibName(Symbols::ThrowNew()), error_arguments));
return result;
}
type_arguments ^= type_arguments.Canonicalize();
const int kTypeArgsLen = 0;
String& error_message = String::Handle(Z);
if (!constructor.AreValidArguments(kTypeArgsLen, arguments_length,
arguments->names(), &error_message)) {
const String& external_constructor_name =
(named_constructor ? constructor_name : type_class_name);
if (is_const) {
ReportError(call_pos,
"invalid arguments passed to constructor '%s' "
"for class '%s': %s",
external_constructor_name.ToCString(),
String::Handle(Z, type_class.Name()).ToCString(),
error_message.ToCString());
}
return ThrowNoSuchMethodError(call_pos, type_class,
external_constructor_name, arguments,
InvocationMirror::kConstructor,
InvocationMirror::kMethod, &constructor);
}
// Return a throw in case of a malformed or malbounded type or report a
// compile-time error if the constructor is const.
if (type.IsMalformedOrMalbounded()) {
if (is_const) {
ReportError(Error::Handle(Z, type.error()));
}
return ThrowTypeError(type_pos, type);
}
// Make the constructor call.
AstNode* new_object = NULL;
if (is_const) {
if (!constructor.is_const()) {
const String& external_constructor_name =
(named_constructor ? constructor_name : type_class_name);
ReportError(
"non-const constructor '%s' cannot be used in "
"const object creation",
external_constructor_name.ToCString());
}
Instance& const_instance = Instance::ZoneHandle(Z);
if (GetCachedConstant(new_pos, &const_instance)) {
// Cache hit, nothing else to do.
} else {
Object& constructor_result = Object::Handle(
Z, EvaluateConstConstructorCall(type_class, type_arguments,
constructor, arguments));
if (constructor_result.IsUnhandledException()) {
// It's a compile-time error if invocation of a const constructor
// call fails.
ReportErrors(Error::Cast(constructor_result), script_, new_pos,
"error while evaluating const constructor");
}
const_instance ^= constructor_result.raw();
CacheConstantValue(new_pos, const_instance);
}
new_object = new (Z) LiteralNode(new_pos, const_instance);
if (!type_bound.IsNull()) {
ASSERT(!type_bound.IsMalformed());
Error& bound_error = Error::Handle(Z);
ASSERT(!is_top_level_); // We cannot check unresolved types.
if (!const_instance.IsInstanceOf(
type_bound, Object::null_type_arguments(),
Object::null_type_arguments(), &bound_error)) {
type_bound = ClassFinalizer::NewFinalizedMalformedType(
bound_error, script_, new_pos,
"const factory result is not an instance of '%s'",
String::Handle(Z, type_bound.UserVisibleName()).ToCString());
new_object = ThrowTypeError(new_pos, type_bound);
}
type_bound = AbstractType::null();
}
} else {
CheckConstructorCallTypeArguments(new_pos, constructor, type_arguments);
if (!type_arguments.IsNull() && !type_arguments.IsInstantiated() &&
(FunctionLevel() > 0)) {
// Make sure that the instantiators are captured.
CaptureAllInstantiators();
}
// If the type argument vector is not instantiated, we verify in checked
// mode at runtime that it is within its declared bounds.
new_object = CreateConstructorCallNode(new_pos, type_arguments, constructor,
arguments);
}
if (!type_bound.IsNull()) {
new_object = new (Z) AssignableNode(new_pos, new_object, type_bound,
Symbols::FactoryResult());
}
return new_object;
}
String& Parser::Interpolate(const GrowableArray<AstNode*>& values) {
NoReloadScope no_reload_scope(isolate(), thread());
NoOOBMessageScope no_msg_scope(thread());
const Class& cls =
Class::Handle(Z, Library::LookupCoreClass(Symbols::StringBase()));
ASSERT(!cls.IsNull());
const Function& func = Function::Handle(
Z, cls.LookupStaticFunction(
Library::PrivateCoreLibName(Symbols::Interpolate())));
ASSERT(!func.IsNull());
// Build the array of literal values to interpolate.
const Array& value_arr =
Array::Handle(Z, Array::New(values.length(), Heap::kOld));
for (int i = 0; i < values.length(); i++) {
ASSERT(values[i]->IsLiteralNode());
value_arr.SetAt(i, values[i]->AsLiteralNode()->literal());
}
// Build argument array to pass to the interpolation function.
const Array& interpolate_arg = Array::Handle(Z, Array::New(1, Heap::kOld));
interpolate_arg.SetAt(0, value_arr);
// Call interpolation function.
Object& result = Object::Handle(Z);
result = DartEntry::InvokeFunction(func, interpolate_arg);
if (result.IsUnhandledException()) {
ReportError("%s", Error::Cast(result).ToErrorCString());
}
String& concatenated = String::ZoneHandle(Z);
concatenated ^= result.raw();
concatenated = Symbols::New(T, concatenated);
return concatenated;
}
// A string literal consists of the concatenation of the next n tokens
// that satisfy the EBNF grammar:
// literal = kSTRING {{ interpol } kSTRING }
// interpol = kINTERPOL_VAR | (kINTERPOL_START expression kINTERPOL_END)
// In other words, the scanner breaks down interpolated strings so that
// a string literal always begins and ends with a kSTRING token.
AstNode* Parser::ParseStringLiteral(bool allow_interpolation) {
TRACE_PARSER("ParseStringLiteral");
AstNode* primary = NULL;
const TokenPosition literal_start = TokenPos();
ASSERT(CurrentToken() == Token::kSTRING);
Token::Kind l1_token = LookaheadToken(1);
if ((l1_token != Token::kSTRING) && (l1_token != Token::kINTERPOL_VAR) &&
(l1_token != Token::kINTERPOL_START)) {
// Common case: no interpolation.
primary = new (Z) LiteralNode(literal_start, *CurrentLiteral());
ConsumeToken();
return primary;
}
// String interpolation needed.
// First, check whether we've cached a compile-time constant for this
// string interpolation.
Instance& cached_string = Instance::Handle(Z);
if (GetCachedConstant(literal_start, &cached_string)) {
SkipStringLiteral();
return new (Z) LiteralNode(literal_start,
Instance::ZoneHandle(Z, cached_string.raw()));
}
bool is_compiletime_const = true;
bool has_interpolation = false;
GrowableArray<AstNode*> values_list;
while (CurrentToken() == Token::kSTRING) {
if (CurrentLiteral()->Length() > 0) {
// Only add non-empty string sections to the values list
// that will be concatenated.
values_list.Add(new (Z) LiteralNode(TokenPos(), *CurrentLiteral()));
}
ConsumeToken();
while ((CurrentToken() == Token::kINTERPOL_VAR) ||
(CurrentToken() == Token::kINTERPOL_START)) {
if (!allow_interpolation) {
ReportError("string interpolation not allowed in this context");
}
has_interpolation = true;
AstNode* expr = NULL;
const TokenPosition expr_pos = TokenPos();
if (CurrentToken() == Token::kINTERPOL_VAR) {
expr = ResolveIdent(TokenPos(), *CurrentLiteral(), true);
ConsumeToken();
} else {
ASSERT(CurrentToken() == Token::kINTERPOL_START);
ConsumeToken();
const bool saved_mode = SetAllowFunctionLiterals(true);
expr = ParseExpr(kAllowConst, kConsumeCascades);
SetAllowFunctionLiterals(saved_mode);
ExpectToken(Token::kINTERPOL_END);
}
// Check if this interpolated string is still considered a compile time
// constant. If it is we need to evaluate if the current string part is
// a constant or not. Only strings, numbers, booleans and null values
// are allowed in compile time const interpolations.
if (is_compiletime_const) {
const Object* const_expr = expr->EvalConstExpr();
if ((const_expr != NULL) &&
(const_expr->IsNumber() || const_expr->IsString() ||
const_expr->IsBool() || const_expr->IsNull())) {
// Change expr into a literal.
expr =
new (Z) LiteralNode(expr_pos, EvaluateConstExpr(expr_pos, expr));
} else {
is_compiletime_const = false;
}
}
values_list.Add(expr);
}
}
if (is_compiletime_const) {
if (has_interpolation) {
const String& interpolated_string = Interpolate(values_list);
primary = new (Z) LiteralNode(literal_start, interpolated_string);
CacheConstantValue(literal_start, interpolated_string);
} else {
GrowableHandlePtrArray<const String> pieces(Z, values_list.length());
for (int i = 0; i < values_list.length(); i++) {
const Instance& part = values_list[i]->AsLiteralNode()->literal();
ASSERT(part.IsString());
pieces.Add(String::Cast(part));
}
const String& lit =
String::ZoneHandle(Z, Symbols::FromConcatAll(T, pieces));
primary = new (Z) LiteralNode(literal_start, lit);
// Caching of constant not necessary because the symbol lookup will
// find the value next time.
}
} else {
ArrayNode* values = new (Z) ArrayNode(
TokenPos(), Type::ZoneHandle(Z, Type::ArrayType()), values_list);
primary = new (Z) StringInterpolateNode(TokenPos(), values);
}
return primary;
}
AstNode* Parser::ParsePrimary() {
TRACE_PARSER("ParsePrimary");
ASSERT(!is_top_level_);
AstNode* primary = NULL;
const Token::Kind token = CurrentToken();
if (IsFunctionLiteral()) {
primary = ParseFunctionStatement(true);
} else if (IsIdentifier()) {
TokenPosition qual_ident_pos = TokenPos();
const LibraryPrefix& prefix = LibraryPrefix::ZoneHandle(Z, ParsePrefix());
if (!prefix.IsNull()) {
ExpectToken(Token::kPERIOD);
}
String& ident = *CurrentLiteral();
ConsumeToken();
if (prefix.IsNull()) {
intptr_t primary_func_level = 0;
ResolveIdentInLocalScope(qual_ident_pos, ident, &primary,
&primary_func_level);
// Check whether the identifier is shadowed by a function type parameter.
if (InGenericFunctionScope()) {
intptr_t type_param_func_level = FunctionLevel();
TypeParameter& type_param = TypeParameter::ZoneHandle(
Z, innermost_function().LookupTypeParameter(
ident, &type_param_func_level));
if (!type_param.IsNull()) {
if ((primary == NULL) ||
(primary_func_level < type_param_func_level)) {
// The identifier is a function type parameter, possibly shadowing
// already resolved 'primary'.
return new (Z) PrimaryNode(qual_ident_pos, type_param);
}
}
}
if (primary == NULL) {
// Check whether the identifier is a type parameter.
if (!current_class().IsNull()) {
TypeParameter& type_param = TypeParameter::ZoneHandle(
Z, current_class().LookupTypeParameter(ident));
if (!type_param.IsNull()) {
return new (Z) PrimaryNode(qual_ident_pos, type_param);
}
}
// This is a non-local unqualified identifier so resolve the
// identifier locally in the main app library and all libraries
// imported by it.
primary = ResolveIdentInCurrentLibraryScope(qual_ident_pos, ident);
}
} else {
// This is a qualified identifier with a library prefix so resolve
// the identifier locally in that library (we do not include the
// libraries imported by that library).
primary = ResolveIdentInPrefixScope(qual_ident_pos, prefix, ident);
// If the identifier could not be resolved, throw a NoSuchMethodError.
// Note: unlike in the case of an unqualified identifier, do not
// interpret the unresolved identifier as an instance method or
// instance getter call when compiling an instance method.
if (primary == NULL) {
if (prefix.is_deferred_load() && ident.Equals(Symbols::LoadLibrary())) {
// Hack Alert: recognize special 'loadLibrary' call on the
// prefix object. The prefix is the primary. Rewind parser and
// let ParseSelectors() handle the loadLibrary call.
SetPosition(qual_ident_pos);
ConsumeToken(); // Prefix name.
primary = new (Z) LiteralNode(qual_ident_pos, prefix);
} else {
GrowableHandlePtrArray<const String> pieces(Z, 3);
pieces.Add(String::Handle(Z, prefix.name()));
pieces.Add(Symbols::Dot());
pieces.Add(ident);
const String& qualified_name =
String::ZoneHandle(Z, Symbols::FromConcatAll(T, pieces));
primary = new (Z) PrimaryNode(qual_ident_pos, qualified_name);
if (prefix.is_deferred_load()) {
primary->AsPrimaryNode()->set_prefix(&prefix);
}
}
} else if (FLAG_load_deferred_eagerly && prefix.is_deferred_load()) {
// primary != NULL.
GrowableHandlePtrArray<const String> pieces(Z, 3);
pieces.Add(String::Handle(Z, prefix.name()));
pieces.Add(Symbols::Dot());
pieces.Add(ident);
const String& qualified_name =
String::ZoneHandle(Z, Symbols::FromConcatAll(T, pieces));
InvocationMirror::Kind call_kind = CurrentToken() == Token::kLPAREN
? InvocationMirror::kMethod
: InvocationMirror::kGetter;
// Note: Adding a statement to current block is a hack, parsing an
// expression should have no side-effect.
current_block_->statements->Add(ThrowNoSuchMethodError(
qual_ident_pos, current_class(), qualified_name,
NULL, // No arguments.
InvocationMirror::kTopLevel, call_kind,
NULL, // No existing function.
&prefix));
}
}
ASSERT(primary != NULL);
} else if (token == Token::kTHIS) {
LocalVariable* local = LookupLocalScope(Symbols::This());
if (local == NULL) {
ReportError("receiver 'this' is not in scope");
}
primary = new (Z) LoadLocalNode(TokenPos(), local);
ConsumeToken();
} else if (token == Token::kINTEGER) {
const Integer& literal = Integer::ZoneHandle(Z, CurrentIntegerLiteral());
primary = new (Z) LiteralNode(TokenPos(), literal);
ConsumeToken();
} else if (token == Token::kTRUE) {
primary = new (Z) LiteralNode(TokenPos(), Bool::True());
ConsumeToken();
} else if (token == Token::kFALSE) {
primary = new (Z) LiteralNode(TokenPos(), Bool::False());
ConsumeToken();
} else if (token == Token::kNULL) {
primary = new (Z) LiteralNode(TokenPos(), Object::null_instance());
ConsumeToken();
} else if (token == Token::kLPAREN) {
ConsumeToken();
const bool saved_mode = SetAllowFunctionLiterals(true);
primary = ParseExpr(kAllowConst, kConsumeCascades);
SetAllowFunctionLiterals(saved_mode);
ExpectToken(Token::kRPAREN);
} else if (token == Token::kDOUBLE) {
const Double& double_value = Double::ZoneHandle(Z, CurrentDoubleLiteral());
if (double_value.IsNull()) {
ReportError("invalid double literal");
}
primary = new (Z) LiteralNode(TokenPos(), double_value);
ConsumeToken();
} else if (token == Token::kSTRING) {
primary = ParseStringLiteral(true);
} else if (token == Token::kNEW) {
ConsumeToken();
primary = ParseNewOperator(Token::kNEW);
} else if (token == Token::kCONST) {
if ((LookaheadToken(1) == Token::kLT) ||
(LookaheadToken(1) == Token::kLBRACK) ||
(LookaheadToken(1) == Token::kINDEX) ||
(LookaheadToken(1) == Token::kLBRACE)) {
primary = ParseCompoundLiteral();
} else {
ConsumeToken();
primary = ParseNewOperator(Token::kCONST);
}
} else if (token == Token::kLT || token == Token::kLBRACK ||
token == Token::kINDEX || token == Token::kLBRACE) {
primary = ParseCompoundLiteral();
} else if (token == Token::kHASH) {
primary = ParseSymbolLiteral();
} else if (token == Token::kSUPER) {
if (current_function().is_static()) {
ReportError("cannot access superclass from static method");
}
if (current_class().SuperClass() == Class::null()) {
ReportError("class '%s' does not have a superclass",
String::Handle(Z, current_class().Name()).ToCString());
}
const TokenPosition super_pos = TokenPos();
ConsumeToken();
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
const TokenPosition ident_pos = TokenPos();
const String& ident = *ExpectIdentifier("identifier expected");
if (IsArgumentPart()) {
TypeArguments& func_type_args = TypeArguments::ZoneHandle(Z);
if (CurrentToken() == Token::kLT) {
// Type arguments.
func_type_args = ParseTypeArguments(ClassFinalizer::kCanonicalize);
if (Isolate::Current()->reify_generic_functions()) {
if (!func_type_args.IsNull() && !func_type_args.IsInstantiated() &&
(FunctionLevel() > 0)) {
// Make sure that the instantiators are captured.
CaptureAllInstantiators();
}
} else {
func_type_args = TypeArguments::null();
}
}
primary = ParseSuperCall(ident, func_type_args);
} else {
primary = ParseSuperFieldAccess(ident, ident_pos);
}
} else if ((CurrentToken() == Token::kLBRACK) ||
Token::CanBeOverloaded(CurrentToken()) ||
(CurrentToken() == Token::kNE)) {
primary = ParseSuperOperator();
} else if (CurrentToken() == Token::kQM_PERIOD) {
ReportError("super call or super getter may not use ?.");
} else {
primary = new (Z) PrimaryNode(super_pos, Symbols::Super());
}
} else {
UnexpectedToken();
}
return primary;
}
// Evaluate expression in expr and return the value. The expression must
// be a compile time constant.
const Instance& Parser::EvaluateConstExpr(TokenPosition expr_pos,
AstNode* expr) {
NoReloadScope no_reload_scope(isolate(), thread());
NoOOBMessageScope no_msg_scope(thread());
if (expr->IsLiteralNode()) {
return expr->AsLiteralNode()->literal();
} else if (expr->IsLoadLocalNode() &&
expr->AsLoadLocalNode()->local().IsConst()) {
return *expr->AsLoadLocalNode()->local().ConstValue();
} else if (expr->IsLoadStaticFieldNode()) {
const Field& field = expr->AsLoadStaticFieldNode()->field();
// We already checked that this field is const and has been
// initialized.
ASSERT(field.is_const());
ASSERT(field.StaticValue() != Object::sentinel().raw());
ASSERT(field.StaticValue() != Object::transition_sentinel().raw());
return Instance::ZoneHandle(Z, field.StaticValue());
} else if (expr->IsTypeNode()) {
AbstractType& type =
AbstractType::ZoneHandle(Z, expr->AsTypeNode()->type().raw());
ASSERT(type.IsInstantiated() && !type.IsMalformedOrMalbounded());
return type;
} else if (expr->IsClosureNode()) {
const Function& func = expr->AsClosureNode()->function();
ASSERT((func.IsImplicitStaticClosureFunction()));
Instance& closure = Instance::ZoneHandle(Z, func.ImplicitStaticClosure());
closure = TryCanonicalize(closure, expr_pos);
return closure;
} else {
ASSERT(expr->EvalConstExpr() != NULL);
Instance& value = Instance::ZoneHandle(Z);
if (GetCachedConstant(expr_pos, &value)) {
return value;
}
ReturnNode* ret = new (Z) ReturnNode(expr_pos, expr);
// Compile time constant expressions cannot reference anything from a
// local scope.
LocalScope* empty_scope = new (Z) LocalScope(NULL, 0, 0);
SequenceNode* seq = new (Z) SequenceNode(expr_pos, empty_scope);
seq->Add(ret);
INC_STAT(thread_, num_execute_const, 1);
Object& result = Object::Handle(Z, Compiler::ExecuteOnce(seq));
if (result.IsError()) {
ReportErrors(Error::Cast(result), script_, expr_pos,
"error evaluating constant expression");
}
ASSERT(result.IsInstance() || result.IsNull());
value ^= result.raw();
value = TryCanonicalize(value, expr_pos);
CacheConstantValue(expr_pos, value);
return value;
}
}
void Parser::SkipFunctionLiteral() {
if (IsIdentifier()) {
if (LookaheadToken(1) != Token::kLPAREN) {
SkipTypeOrFunctionType(true);
}
ExpectIdentifier("function name expected");
}
if (CurrentToken() == Token::kLPAREN) {
SkipToMatchingParenthesis();
}
RawFunction::AsyncModifier async_modifier = ParseFunctionModifier();
BoolScope allow_await(&this->await_is_keyword_,
async_modifier != RawFunction::kNoModifier);
if (CurrentToken() == Token::kLBRACE) {
SkipBlock();
ExpectToken(Token::kRBRACE);
} else if (CurrentToken() == Token::kARROW) {
ConsumeToken();
SkipExpr();
}
}
// Skips function/method/constructor/getter/setter preambles until the formal
// parameter list. It is enough to skip the tokens, since we have already
// previously parsed the function.
void Parser::SkipFunctionPreamble() {
while (true) {
if (IsFunctionTypeSymbol()) {
ConsumeToken();
SkipTypeParameters();
SkipToMatchingParenthesis();
continue;
}
const Token::Kind token = CurrentToken();
if (token == Token::kLPAREN) {
return;
}
if (token == Token::kGET) {
if (LookaheadToken(1) == Token::kLT) {
// Case: Generic Function/method named get.
ConsumeToken(); // Parse away 'get' (the function's name).
SkipTypeParameters();
continue;
}
if (LookaheadToken(1) == Token::kLPAREN) {
// Case: Function/method named get.
ConsumeToken(); // Parse away 'get' (the function's name).
return;
}
// Case: Getter.
ConsumeToken(); // Parse away 'get'.
ConsumeToken(); // Parse away the getter name.
return;
}
ConsumeToken(); // Can be static, factory, operator, void, ident, etc...
}
}
void Parser::SkipListLiteral() {
if (CurrentToken() == Token::kINDEX) {
// Empty list literal.
ConsumeToken();
return;
}
ExpectToken(Token::kLBRACK);
while (CurrentToken() != Token::kRBRACK) {
SkipNestedExpr();
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
} else {
break;
}
}
ExpectToken(Token::kRBRACK);
}
void Parser::SkipMapLiteral() {
ExpectToken(Token::kLBRACE);
while (CurrentToken() != Token::kRBRACE) {
SkipNestedExpr();
ExpectToken(Token::kCOLON);
SkipNestedExpr();
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
} else {
break;
}
}
ExpectToken(Token::kRBRACE);
}
void Parser::SkipActualParameters() {
if (CurrentToken() == Token::kLT) {
SkipTypeArguments();
}
ExpectToken(Token::kLPAREN);
while (CurrentToken() != Token::kRPAREN) {
if (IsIdentifier() && (LookaheadToken(1) == Token::kCOLON)) {
// Named actual parameter.
ConsumeToken();
ConsumeToken();
}
SkipNestedExpr();
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
}
}
ExpectToken(Token::kRPAREN);
}
void Parser::SkipCompoundLiteral() {
if (CurrentToken() == Token::kLT) {
SkipTypeArguments();
}
if ((CurrentToken() == Token::kLBRACK) || (CurrentToken() == Token::kINDEX)) {
SkipListLiteral();
} else if (CurrentToken() == Token::kLBRACE) {
SkipMapLiteral();
}
}
void Parser::SkipSymbolLiteral() {
ConsumeToken(); // Hash sign.
if (IsIdentifier()) {
ConsumeToken();
while (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ExpectIdentifier("identifier expected");
}
} else if (Token::CanBeOverloaded(CurrentToken())) {
ConsumeToken();
} else {
UnexpectedToken();
}
}
void Parser::SkipNewOperator() {
ConsumeToken(); // Skip new or const keyword.
if (IsIdentifier()) {
SkipType(false);
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ExpectIdentifier("identifier expected");
}
if (CurrentToken() == Token::kLPAREN) {
SkipActualParameters();
return;
}
}
}
void Parser::SkipStringLiteral() {
ASSERT(CurrentToken() == Token::kSTRING);
while (CurrentToken() == Token::kSTRING) {
ConsumeToken();
while (true) {
if (CurrentToken() == Token::kINTERPOL_VAR) {
ConsumeToken();
} else if (CurrentToken() == Token::kINTERPOL_START) {
ConsumeToken();
const bool saved_mode = SetAllowFunctionLiterals(true);
SkipExpr();
SetAllowFunctionLiterals(saved_mode);
ExpectToken(Token::kINTERPOL_END);
} else {
break;
}
}
}
}
void Parser::SkipPrimary() {
if (IsFunctionLiteral()) {
SkipFunctionLiteral();
return;
}
switch (CurrentToken()) {
case Token::kTHIS:
case Token::kSUPER:
case Token::kNULL:
case Token::kTRUE:
case Token::kFALSE:
case Token::kINTEGER:
case Token::kDOUBLE:
ConsumeToken();
break;
case Token::kIDENT:
ConsumeToken();
break;
case Token::kSTRING:
SkipStringLiteral();
break;
case Token::kLPAREN:
ConsumeToken();
SkipNestedExpr();
ExpectToken(Token::kRPAREN);
break;
case Token::kNEW:
SkipNewOperator();
break;
case Token::kCONST:
if ((LookaheadToken(1) == Token::kLT) ||
(LookaheadToken(1) == Token::kLBRACE) ||
(LookaheadToken(1) == Token::kLBRACK) ||
(LookaheadToken(1) == Token::kINDEX)) {
ConsumeToken();
SkipCompoundLiteral();
} else {
SkipNewOperator();
}
break;
case Token::kLT:
case Token::kLBRACE:
case Token::kLBRACK:
case Token::kINDEX:
SkipCompoundLiteral();
break;
case Token::kHASH:
SkipSymbolLiteral();
break;
default:
if (IsIdentifier()) {
ConsumeToken(); // Handle pseudo-keyword identifiers.
} else {
UnexpectedToken();
UNREACHABLE();
}
break;
}
}
void Parser::SkipSelectors() {
while (true) {
const Token::Kind current_token = CurrentToken();
if (current_token == Token::kCASCADE) {
ConsumeToken();
if (CurrentToken() == Token::kLBRACK) {
continue; // Consume [ in next loop iteration.
} else {
ExpectIdentifier("identifier or [ expected after ..");
}
} else if ((current_token == Token::kPERIOD) ||
(current_token == Token::kQM_PERIOD)) {
ConsumeToken();
ExpectIdentifier("identifier expected");
} else if (current_token == Token::kLBRACK) {
ConsumeToken();
SkipNestedExpr();
ExpectToken(Token::kRBRACK);
} else if (IsArgumentPart()) {
SkipActualParameters();
} else {
break;
}
}
}
void Parser::SkipPostfixExpr() {
SkipPrimary();
SkipSelectors();
if (IsIncrementOperator(CurrentToken())) {
ConsumeToken();
}
}
void Parser::SkipUnaryExpr() {
if (IsPrefixOperator(CurrentToken()) || IsIncrementOperator(CurrentToken()) ||
IsAwaitKeyword()) {
ConsumeToken();
SkipUnaryExpr();
} else {
SkipPostfixExpr();
}
}
void Parser::SkipBinaryExpr() {
SkipUnaryExpr();
const int min_prec = Token::Precedence(Token::kIFNULL);
const int max_prec = Token::Precedence(Token::kMUL);
while (((min_prec <= Token::Precedence(CurrentToken())) &&
(Token::Precedence(CurrentToken()) <= max_prec))) {
if (CurrentToken() == Token::kIS) {
ConsumeToken();
if (CurrentToken() == Token::kNOT) {
ConsumeToken();
}
SkipTypeOrFunctionType(false);
} else if (CurrentToken() == Token::kAS) {
ConsumeToken();
SkipTypeOrFunctionType(false);
} else {
ConsumeToken();
SkipUnaryExpr();
}
}
}
void Parser::SkipConditionalExpr() {
SkipBinaryExpr();
if (CurrentToken() == Token::kCONDITIONAL) {
ConsumeToken();
SkipExpr();
ExpectToken(Token::kCOLON);
SkipExpr();
}
}
void Parser::SkipExpr() {
while (CurrentToken() == Token::kTHROW) {
ConsumeToken();
}
SkipConditionalExpr();
if (CurrentToken() == Token::kCASCADE) {
SkipSelectors();
}
if (Token::IsAssignmentOperator(CurrentToken())) {
ConsumeToken();
SkipExpr();
}
}
void Parser::SkipNestedExpr() {
const bool saved_mode = SetAllowFunctionLiterals(true);
SkipExpr();
SetAllowFunctionLiterals(saved_mode);
}
void Parser::SkipQualIdent() {
ASSERT(IsIdentifier());
ConsumeToken();
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken(); // Consume the kPERIOD token.
ExpectIdentifier("identifier expected after '.'");
}
}
} // namespace dart
#else // DART_PRECOMPILED_RUNTIME
namespace dart {
void ParsedFunction::AddToGuardedFields(const Field* field) const {
UNREACHABLE();
}
kernel::ScopeBuildingResult* ParsedFunction::EnsureKernelScopes() {
UNREACHABLE();
return NULL;
}
LocalVariable* ParsedFunction::EnsureExpressionTemp() {
UNREACHABLE();
return NULL;
}
void ParsedFunction::SetNodeSequence(SequenceNode* node_sequence) {
UNREACHABLE();
}
void ParsedFunction::SetRegExpCompileData(
RegExpCompileData* regexp_compile_data) {
UNREACHABLE();
}
void ParsedFunction::AllocateVariables() {
UNREACHABLE();
}
void ParsedFunction::AllocateIrregexpVariables(intptr_t num_stack_locals) {
UNREACHABLE();
}
void ParsedFunction::Bailout(const char* origin, const char* reason) const {
UNREACHABLE();
}
void Parser::ParseCompilationUnit(const Library& library,
const Script& script) {
UNREACHABLE();
}
void Parser::ParseClass(const Class& cls) {
UNREACHABLE();
}
RawObject* Parser::ParseFunctionParameters(const Function& func) {
UNREACHABLE();
return Object::null();
}
void Parser::ParseFunction(ParsedFunction* parsed_function) {
UNREACHABLE();
}
RawObject* Parser::ParseMetadata(const Field& meta_data) {
UNREACHABLE();
return Object::null();
}
ParsedFunction* Parser::ParseStaticFieldInitializer(const Field& field) {
UNREACHABLE();
return NULL;
}
void Parser::InsertCachedConstantValue(const Script& script,
TokenPosition token_pos,
const Instance& value) {
UNREACHABLE();
}
ArgumentListNode* Parser::BuildNoSuchMethodArguments(
TokenPosition call_pos,
const String& function_name,
const ArgumentListNode& function_args,
const LocalVariable* temp_for_last_arg,
bool is_super_invocation) {
UNREACHABLE();
return NULL;
}
bool Parser::FieldHasFunctionLiteralInitializer(const Field& field,
TokenPosition* start,
TokenPosition* end) {
UNREACHABLE();
return false;
}
} // namespace dart
#endif // DART_PRECOMPILED_RUNTIME