blob: f555b6d62fb2da5536b4ba7e7cde3f1842b389e1 [file] [log] [blame]
// 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.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/kernel_binary_flowgraph.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.");
DEFINE_FLAG(bool,
support_deprecated_tearoff_syntax,
false,
"Support new tear-off syntax.");
// 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,
generic_method_syntax,
true,
"Enable generic function syntax.");
DEFINE_FLAG(bool,
initializing_formal_access,
true,
"Make initializing formal parameters visible in initializer list.");
DEFINE_FLAG(bool,
warn_super,
false,
"Warning if super initializer not last in initializer list.");
DEFINE_FLAG(
bool,
await_is_keyword,
false,
"await and yield are treated as proper keywords in synchronous code.");
DEFINE_FLAG(bool,
assert_initializer,
false,
"Allow asserts in initializer lists.");
DECLARE_FLAG(bool, profile_vm);
DECLARE_FLAG(bool, trace_service);
DECLARE_FLAG(bool, ignore_patch_signature_mismatch);
// 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_++;
// No need to check the stack unless the parser is in an unusually deep
// recurive state. Thus, we omit the more expensive stack checks in
// the common case.
const int kMaxUncheckedDepth = 100; // Somewhat arbitrary.
if (parser_->recursion_counter_ > kMaxUncheckedDepth) {
parser_->CheckStack();
}
}
~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();
}
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) {
intptr_t kernel_offset = function().kernel_offset();
Script& script = Script::Handle(Z, function().script());
kernel::StreamingScopeBuilder builder(
this, kernel_offset, script.kernel_data(), script.kernel_data_size());
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;
// 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].
first_parameter_index_ = kParamEndSlotFromFp + num_params;
first_stack_local_index_ = kFirstLocalSlotFromFp;
num_copied_params_ = 0;
} else {
// Parameter i will be at fp[kFirstLocalSlotFromFp - i] and local variable
// j will be at fp[kFirstLocalSlotFromFp - num_params - j].
first_parameter_index_ = kFirstLocalSlotFromFp;
first_stack_local_index_ = first_parameter_index_ - num_params;
num_copied_params_ = num_params;
}
// 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(
first_parameter_index_, num_params, first_stack_local_index_, NULL,
&found_captured_variables);
// Frame indices are relative to the frame pointer and are decreasing.
ASSERT(next_free_frame_index <= first_stack_local_index_);
num_stack_locals_ = first_stack_local_index_ - next_free_frame_index;
}
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;
first_stack_local_index_ = kFirstLocalSlotFromFp;
num_copied_params_ = 0;
// 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.
// Field initializer parameters are always 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);
if (FLAG_initializing_formal_access || !param.is_field_initializer) {
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, cls.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:
if (func.IsConstructorClosureFunction()) {
node_sequence = parser.ParseConstructorClosure(func);
break;
}
// Fall-through: Handle non-implicit closures.
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(!FLAG_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::ParseConstructorClosure(const Function& func) {
TRACE_PARSER("ParseConstructorClosure");
const TokenPosition token_pos = func.token_pos();
Function& constructor = Function::ZoneHandle(Z);
TypeArguments& type_args = TypeArguments::ZoneHandle(Z);
ParseConstructorClosurization(&constructor, &type_args);
ASSERT(!constructor.IsNull());
ParamList params;
// The first parameter of the closure function is the
// implicit closure argument.
params.AddFinalParameter(token_pos, &Symbols::ClosureParameter(),
&Object::dynamic_type());
bool params_ok = ParseFormalParameters(constructor, &params);
USE(params_ok);
ASSERT(params_ok);
// Per language spec, the type of the closure parameters is dynamic.
// Replace the types parsed from the constructor.
params.EraseParameterTypes();
SetupDefaultsForOptionalParams(params);
ASSERT(func.num_fixed_parameters() == params.num_fixed_parameters);
ASSERT(func.NumOptionalParameters() == params.num_optional_parameters);
OpenFunctionBlock(func);
LocalScope* scope = current_block_->scope;
AddFormalParamsToScope(&params, scope);
ArgumentListNode* ctor_args = new ArgumentListNode(token_pos);
// Skip implicit closure parameter at 0.
for (intptr_t i = 1; i < func.NumParameters(); i++) {
ctor_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
}
if (func.HasOptionalNamedParameters()) {
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)));
}
ctor_args->set_names(arg_names);
}
AstNode* new_object =
CreateConstructorCallNode(token_pos, type_args, constructor, ctor_args);
ReturnNode* return_node = new ReturnNode(token_pos, new_object);
current_block_->statements->Add(return_node);
return CloseBlock();
}
SequenceNode* Parser::ParseImplicitClosure(const Function& func) {
TRACE_PARSER("ParseImplicitClosure");
TokenPosition token_pos = func.token_pos();
OpenFunctionBlock(func);
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 (FLAG_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()) {
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);
} else if (!parent.is_static()) {
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);
} else {
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::Type im_type;
if (parent.IsImplicitGetterFunction()) {
im_type = InvocationMirror::kGetter;
} else if (parent.IsImplicitSetterFunction()) {
im_type = InvocationMirror::kSetter;
} else {
im_type = InvocationMirror::kMethod;
}
call = ThrowNoSuchMethodError(TokenPos(), owner, func_name, func_args,
InvocationMirror::kStatic, im_type,
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];
OS::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);
}
}
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);
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(false, 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 (parameter.type->IsVoidType()) {
ReportError("parameter '%s' may not be 'void'",
parameter.name->ToCString());
}
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 ||
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;
if (FLAG_initializing_formal_access) {
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;
if (FLAG_initializing_formal_access) {
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) {
if (!FLAG_generic_method_syntax) {
ReportError("generic function types not supported");
}
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 (parameter.type->IsVoidType()) {
ReportError("parameter '%s' may not be 'void'",
parameter.name->ToCString());
}
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()));
// The type_args_var is only used in the generated body of an implicit closure
// where noSuchMethod should never be called.
ASSERT(function_args.type_args_var() == NULL);
if (!function_args.type_arguments().IsNull()) {
// TODO(regis): Pass the original type arguments to the invocation mirror.
}
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);
}
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);
// '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);
}
// 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);
} 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);
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);
}
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* 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());
}
return new StaticCallNode(supercall_pos, super_ctor, arguments);
}
AstNode* Parser::ParseInitializer(const Class& cls,
LocalVariable* receiver,
GrowableArray<Field*>* initialized_fields) {
TRACE_PARSER("ParseInitializer");
const TokenPosition field_pos = TokenPos();
if (FLAG_assert_initializer && CurrentToken() == Token::kASSERT) {
return ParseAssertStatement(current_function().is_const());
}
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,
// List arguments,
// List argumentNames,
// List existingArgumentNames);
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))));
// 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()));
// List existingArgumentNames.
// There is no setter for the final field, thus there are
// no existing 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) { ... }
if (FLAG_warn_super) {
ReportWarning("Super initializer not at end");
}
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));
}
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);
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.
if (FLAG_initializing_formal_access) {
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 (FLAG_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->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);
current_block_->statements->Add(new 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 (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);
}
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 (FLAG_assert_initializer && (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 or a hash mark (for closurization).
Token::Kind next_token = LookaheadToken(1);
if ((next_token != Token::kPERIOD) && (next_token != Token::kHASH)) {
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 or #.
// Consume the identifier, let the caller consume the . or #.
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) {
if (!FLAG_generic_method_syntax) {
ReportError("generic type arguments not supported.");
}
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 (FLAG_enable_mirrors && (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());
}
}
// 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);
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 (FLAG_enable_mirrors && (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");
}
} else if (member.type->IsVoidType()) {
ReportError(member.name_pos, "field may not be 'void'");
}
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 (FLAG_enable_mirrors && (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());
}
if (!FLAG_ignore_patch_signature_mismatch) {
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 (FLAG_enable_mirrors && 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());
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());
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());
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());
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 (FLAG_enable_mirrors && 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 (FLAG_enable_mirrors && 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, no need to check FLAG_generic_method_syntax.
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) ||
(!parameterizing_class && (CurrentToken() == Token::kSUPER))) {
const bool is_lower_bound = CurrentToken() == Token::kSUPER;
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);
if (is_lower_bound) {
// TODO(regis): Handle 'super' differently than 'extends' if lower
// bounds make it in the final specification and if run time support
// for lower bounds is required.
// For now, we parse but ignore lower bounds and only support upper
// bounds.
type_parameter_bound = I->object_store()->object_type();
}
} 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 (FLAG_enable_mirrors && 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());
}
const bool is_reflectable =
!(library_.is_dart_scheme() && library_.IsPrivate(var_name));
field = Field::NewTopLevel(var_name, is_final, is_const, owner, name_pos);
field.SetFieldType(type);
field.set_is_reflectable(is_reflectable);
field.SetStaticValue(Object::null_instance(), true);
top_level->AddField(field);
library_.AddObject(field, var_name);
if (FLAG_enable_mirrors && metadata_pos.IsReal()) {
library_.AddFieldMetadata(field, metadata_pos);
}
if (CurrentToken() == Token::kASSIGN) {
ConsumeToken();
Instance& field_value = Instance::Handle(Z, Object::sentinel().raw());
bool has_simple_literal = false;
if (LookaheadToken(1) == Token::kSEMICOLON) {
has_simple_literal = IsSimpleLiteral(type, &field_value);
}
SkipExpr();
field.SetStaticValue(field_value, true);
field.set_has_initializer(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);
}
} else if (is_final) {
ReportError(name_pos, "missing initializer for final or const variable");
}
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) {
if (!FLAG_generic_method_syntax) {
ReportError("generic functions not supported");
}
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 (FLAG_enable_mirrors && 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 (FLAG_enable_mirrors && 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_);
}
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 && (IsSymbol(Symbols::Deferred()))) {
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 = ExpectIdentifier("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 (FLAG_enable_mirrors && 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())) {
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 (FLAG_enable_mirrors && 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::CheckStack() {
volatile uword c_stack_pos = Thread::GetCurrentStackPointer();
volatile uword c_stack_base = OSThread::Current()->stack_base();
volatile uword c_stack_limit =
c_stack_base - OSThread::GetSpecifiedStackSize();
// Note: during early initialization the stack_base() can return 0.
if ((c_stack_base > 0) && (c_stack_pos < c_stack_limit)) {
ReportError("stack overflow while parsing");
}
}
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(
func_end_pos,
new (Z) LoadLocalNode(TokenPosition::kNoSource, async_completer),
Symbols::CompleterCompleteError(), completer_args));
ReturnNode* return_node = new (Z) ReturnNode(func_end_pos);
// 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);
// TODO(regis): Handle generic generator function.
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);
// TODO(regis): Handle generic async function.
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);
// TODO(regis): Handle generic async generator function.
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);
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);
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);
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 and return a new future that executes a closure with the current
// body.
// 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 Class& future = Class::ZoneHandle(Z, I->object_store()->future_class());
ASSERT(!future.IsNull());
const Function& constructor = Function::ZoneHandle(
Z, future.LookupFunction(Symbols::FutureMicrotask()));
ASSERT(!constructor.IsNull());
const Class& completer =
Class::ZoneHandle(Z, I->object_store()->completer_class());
ASSERT(!completer.IsNull());
const Function& completer_constructor = Function::ZoneHandle(
Z, completer.LookupFunction(Symbols::CompleterSyncConstructor()));
ASSERT(!completer_constructor.IsNull());
LocalVariable* async_completer =
current_block_->scope->LookupVariable(Symbols::AsyncCompleter(), false);
const TokenPosition token_pos = ST(closure_body->token_pos());
// 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);
const Library& async_lib = Library::Handle(Library::AsyncLibrary());
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);
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);
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);
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);
// Add to AST:
// new Future.microtask(:async_op);
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) {
ASSERT((params != NULL) && (params->parameters != NULL));
ASSERT((params->num_optional_parameters > 0) ==
(params->has_optional_positional_parameters ||
params->has_optional_named_parameters));
if (!Utils::IsInt(16, params->num_fixed_parameters) ||
!Utils::IsInt(16, 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)));
for (int i = 0; i < num_parameters; i++) {
ParamDesc& param_desc = (*params->parameters)[i];
func.SetParameterTypeAt(i, *param_desc.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");
}
if (param_desc.is_covariant) {
if (!func.IsDynamicFunction(true)) {
ReportError(param_desc.name_pos,
"only instance functions may have "
"covariant 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();
}
if (FLAG_initializing_formal_access) {
// Field initializer parameters are implicitly final.
ASSERT(!param_desc.is_field_initializer || param_desc.is_final);
} else if (param_desc.is_field_initializer) {
parameter->set_invisible(true);
}
}
}
// 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 (!FLAG_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 ParseFunctionType(AbstractType::Handle(Z), 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);
}
// 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 (FLAG_enable_mirrors && metadata_pos.IsReal()) {
library_.AddFunctionMetadata(function, metadata_pos);
}
}
ASSERT(function.parent_function() == innermost_function_.raw());
innermost_function_ = function.raw();
if (CurrentToken() == Token::kLT) {
if (!FLAG_generic_method_syntax) {
ReportError("generic functions not supported");
}
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) &&
(!FLAG_generic_method_syntax || (ct != Token::kSUPER))) {
// 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::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.
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 (result type of function type)
// 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(false)) {
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)) {
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();
}
if (IsIdentifier()) {
if (LookaheadToken(1) == Token::kIN) {
return true;
} else if (TryParseType(false)) {
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);
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);
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);
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));
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);
}
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);
// Generate :iterator.current = expr;
AstNode* iterator =
new (Z) LoadLocalNode(TokenPosition::kNoSource, iterator_param);
AstNode* store_current = new (Z) InstanceSetterNode(
TokenPosition::kNoSource, iterator,
Library::PrivateCoreLibName(Symbols::_current()), expr);
yield->AddNode(store_current);
if (is_yield_each) {
// Generate :iterator.isYieldEach = true;
AstNode* set_is_yield_each = new (Z)
InstanceSetterNode(TokenPosition::kNoSource, iterator,
String::ZoneHandle(Symbols::IsYieldEach().raw()),
new (Z) LiteralNode(TokenPos(), Bool::True()));
yield->AddNode(set_is_yield_each);
}
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);
if (I->type_checks() &&
(((function_level == 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;
}
}
}
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();
}
// Call _throwNewIfNotLoaded if prefix is not NULL, otherwise call _throwNew.
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::Call im_call,
InvocationMirror::Type im_type,
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_call == InvocationMirror::kStatic ||
im_call == InvocationMirror::kTopLevel);
im_call = InvocationMirror::kTopLevel;
}
arguments->Add(new (Z) LiteralNode(
call_pos, Smi::ZoneHandle(Z, Smi::New(InvocationMirror::EncodeType(
im_call, im_type)))));
// 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()));
}
// List existingArgumentNames.
// Check if there exists a function with the same name unless caller
// has done the lookup already. If there is a function with the same
// name but incompatible parameters, inform the NoSuchMethodError what the
// expected parameters are.
Function& function = Function::Handle(Z);
if (func != NULL) {
function = func->raw();
} else {
function = cls.LookupStaticFunction(function_name);
}
Array& array = Array::ZoneHandle(Z);
// An unpatched external function is treated as an unresolved function.
if (!function.IsNull() && !function.is_external()) {
// The constructor for NoSuchMethodError takes a list of existing
// parameter names to produce a descriptive error message explaining
// the parameter mismatch. The problem is that the array of names
// does not describe which parameters are optional positional or
// named, which can lead to confusing error messages.
// Since the NoSuchMethodError class only uses the list to produce
// a string describing the expected parameters, we construct a more
// descriptive string here and pass it as the only element of the
// "existingArgumentNames" array of the NoSuchMethodError constructor.
// TODO(13471): Separate the implementations of NoSuchMethodError
// between dart2js and VM. Update the constructor to accept a string
// describing the formal parameters of an incompatible call target.
array = Array::New(1, Heap::kOld);
array.SetAt(0, String::Handle(Z, function.UserVisibleFormalParameters()));
}
arguments->Add(new (Z) LiteralNode(call_pos, array));
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];
OS::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);
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) {
// TODO(regis): When require_const is true, do we need to check that
// func_type_args are null or instantiated?
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);
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);
}
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);
}
}
// 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);
}
} else {
ASSERT(func.kind() != RawFunction::kImplicitStaticFinalGetter);
access = new (Z) StaticGetterNode(
ident_pos, NULL, Class::ZoneHandle(Z, cls.raw()), field_name);
}
} 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);
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);
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 (!FLAG_reify_generic_functions) {
Type& type = Type::ZoneHandle(Z, Type::DynamicType());
return new (Z) TypeNode(primary_pos, type);
}
if (FunctionLevel() > 0) {
// 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.
if (!FLAG_generic_method_syntax) {
ReportError("generic type arguments not supported.");
}
func_type_args = ParseTypeArguments(ClassFinalizer::kCanonicalize);
if (FLAG_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.
if (!FLAG_generic_method_syntax) {
ReportError("generic type arguments not supported.");
}
func_type_args = ParseTypeArguments(ClassFinalizer::kCanonicalize);
if (FLAG_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()) {
// TODO(regis): What about the parsed type arguments?
selector = LoadTypeParameter(primary_node);
} else if (primary_node->primary().IsClass()) {
// TODO(regis): What about the parsed type arguments?
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());
} 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;
}
}
// Closurization e#m of getter, setter, method or operator.
AstNode* Parser::ParseClosurization(AstNode* primary) {
if (!FLAG_support_deprecated_tearoff_syntax) {
ReportError("tear-off using the x#id syntax is a deprecated feature");
return NULL;
}
ExpectToken(Token::kHASH);
TokenPosition property_pos = TokenPos();
bool is_setter_name = false;
String& extractor_name = String::ZoneHandle(Z);
if (IsIdentifier()) {
extractor_name = CurrentLiteral()->raw();
ConsumeToken();
if (CurrentToken() == Token::kASSIGN) {
ConsumeToken();
is_setter_name = true;
}
} else if (Token::CanBeOverloaded(CurrentToken())) {
extractor_name = Symbols::Token(CurrentToken()).raw();
ConsumeToken();
} else {
ReportError("identifier or operator expected");
}
if (primary->IsPrimaryNode() && primary->AsPrimaryNode()->IsSuper()) {
// TODO(hausner): implement super#m
ReportError("closurization of super method not yet supported");
}
// Handle closurization of top-level names from library prefixes, P#m
if (primary->IsLiteralNode() &&
primary->AsLiteralNode()->literal().IsLibraryPrefix()) {
const LibraryPrefix& prefix =
LibraryPrefix::Cast(primary->AsLiteralNode()->literal());
Object& obj = Object::Handle(Z);
const bool is_private_name =
(extractor_name.CharAt(0) == Library::kPrivateIdentifierStart);
if (!is_private_name) {
// 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 does not help in hiding the private
// name, so we explicitly prevent lookup of private names here.
if (is_setter_name) {
const String& setter_name =
String::Handle(Z, Field::LookupSetterSymbol(extractor_name));
if (!setter_name.IsNull()) {
obj = prefix.LookupObject(setter_name);
}
}
if (obj.IsNull()) {
obj = prefix.LookupObject(extractor_name);
}
}
if (!prefix.is_loaded() && (parsed_function() != NULL) &&
!FLAG_load_deferred_eagerly) {
// Remember that this function depends on an import prefix of an
// unloaded deferred library.
parsed_function()->AddDeferredPrefix(prefix);
}
if (obj.IsFunction()) {
const Function& func = Function::Cast(obj);
if (!func.IsSetterFunction() || is_setter_name) {
return CreateImplicitClosureNode(func, property_pos, NULL);
}
} else if (obj.IsField()) {
const Field& field = Field::Cast(obj);
if (is_setter_name && !field.is_final()) {
const Instance& setter_closure =
Instance::ZoneHandle(field.SetterClosure());
return new (Z) LiteralNode(property_pos, setter_closure);
}
if (!is_setter_name) {
const Instance& getter_closure =
Instance::ZoneHandle(field.GetterClosure());
return new (Z) LiteralNode(property_pos, getter_closure);
}
}
return ThrowNoSuchMethodError(
property_pos, current_class(), extractor_name,
NULL, // No arguments.
InvocationMirror::kTopLevel,
is_setter_name ? InvocationMirror::kSetter : InvocationMirror::kMethod,
NULL); // No existing function.
}
// Handle closurization of static properties of classes, C#n.
if (primary->IsPrimaryNode() &&
primary->AsPrimaryNode()->primary().IsClass()) {
const Class& cls = Class::Cast(primary->AsPrimaryNode()->primary());
const Field& field =
Field::Handle(Z, cls.LookupStaticField(extractor_name));
if (!field.IsNull()) {
if (is_setter_name) {
if (!field.is_final()) {
const Instance& setter_closure =
Instance::ZoneHandle(Z, field.SetterClosure());
ASSERT(setter_closure.IsClosure());
// Note: the created closure is cached after it's created
// once. If eager compilation is desired, the compiler can
// be invoked here. The same applies for getters below.
return new (Z) LiteralNode(property_pos, setter_closure);
}
extractor_name = Field::SetterSymbol(extractor_name);
} else {
const Instance& getter_closure =
Instance::ZoneHandle(Z, field.GetterClosure());
ASSERT(getter_closure.IsClosure());
return new (Z) LiteralNode(property_pos, getter_closure);
}
} else {
Function& func = Function::Handle(Z);
if (is_setter_name) {
extractor_name = Field::LookupSetterSymbol(extractor_name);
if (!extractor_name.IsNull()) {
func = cls.LookupStaticFunction(extractor_name);
} else {
extractor_name = Field::SetterSymbol(extractor_name);
}
} else {
func = cls.LookupStaticFunction(extractor_name);
if (func.IsNull()) {
const String& getter_name =
String::Handle(Z, Field::LookupGetterSymbol(extractor_name));
if (!getter_name.IsNull()) {
func = cls.LookupStaticFunction(getter_name);
}
}
}
if (!func.IsNull()) {
return CreateImplicitClosureNode(func, property_pos, NULL);
}
}
return ThrowNoSuchMethodError(
property_pos, cls, extractor_name,
NULL, // No arguments.
InvocationMirror::kStatic,
is_setter_name ? InvocationMirror::kSetter : InvocationMirror::kMethod,
NULL); // No existing function.
}
// Closurization of instance getter, setter, method or operator.
GrowableHandlePtrArray<const String> pieces(Z, 3);
pieces.Add(Symbols::HashMark());
if (is_setter_name) {
pieces.Add(Symbols::SetterPrefix());
}
pieces.Add(extractor_name);
extractor_name = Symbols::FromConcatAll(T, pieces);
return new (Z) InstanceGetterNode(property_pos, primary, extractor_name);
}
AstNode* Parser::ParsePostfixExpr() {
TRACE_PARSER("ParsePostfixExpr");
String* expr_ident =
Token::IsIdentifier(CurrentToken()) ? CurrentLiteral() : NULL;
const TokenPosition expr_pos = TokenPos();
AstNode* expr = ParsePrimary();
if (CurrentToken() == Token::kHASH) {
expr = LoadFieldIfUnresolved(expr);
expr = ParseClosurization(expr);
} else {
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 (FLAG_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);
}
} 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);
}
}
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);
}
RawObject* Parser::EvaluateConstConstructorCall(
const Class& type_class,
const TypeArguments& type_arguments,
const Function& constructor,
ArgumentListNode* arguments) {
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();
}
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);
}
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);
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);
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 (!FLAG_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());
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()) {
type_parameter ^= CanonicalizeType(type_parameter);
ASSERT(!type_parameter.IsMalformed());
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) {
if (!FLAG_generic_method_syntax) {
ReportError("generic type arguments not supported.");
}
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));
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);
}
RawFunction* Parser::BuildConstructorClosureFunction(const Function& ctr,
TokenPosition token_pos) {
ASSERT(ctr.kind() == RawFunction::kConstructor);
Function& closure = Function::Handle(Z);
closure = I->LookupClosureFunction(innermost_function(), token_pos);
if (!closure.IsNull()) {
ASSERT(closure.IsConstructorClosureFunction());
return closure.raw();
}
String& closure_name = String::Handle(Z, ctr.name());
closure_name =
Symbols::FromConcat(T, Symbols::ConstructorClosurePrefix(), closure_name);
ParamList params;
params.AddFinalParameter(token_pos, &Symbols::ClosureParameter(),
&Object::dynamic_type());
ParseFormalParameters(ctr, &params);
// Per language spec, the type of the closure parameters is dynamic.
// Replace the types parsed from the constructor.
params.EraseParameterTypes();
closure = Function::NewClosureFunction(closure_name, innermost_function(),
token_pos);
closure.set_is_generated_body(true);
closure.set_is_debuggable(false);
closure.set_is_visible(false);
// TODO(regis): Verify that the closure cannot be generic.
closure.set_result_type(Object::dynamic_type());
AddFormalParamsToFunction(&params, closure);
ResolveSignatureTypeParameters(closure);
// Finalize function type.
Type& signature_type = Type::Handle(Z, closure.SignatureType());
signature_type ^= CanonicalizeType(signature_type);
closure.SetSignatureType(signature_type);
// Finalization would be premature when top-level parsing.
ASSERT(!is_top_level_);
return closure.raw();
}
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;
}
// Parse a primary expression of the form new T# or new T#m.
// Current token position is after the keyword new. Extracts the
// anonymous or named constructor and type arguments.
// Note that type type T has already been parsed before
// (by ParseNewOperator()) and is guaranteed to be well-formed,
// and the constructor is known to exist.
void Parser::ParseConstructorClosurization(Function* constructor,
TypeArguments* type_arguments) {
*constructor = Function::null();
*type_arguments = TypeArguments::null();
const Token::Kind la3 = LookaheadToken(3);
const bool consume_unresolved_prefix =
(la3 == Token::kLT) || (la3 == Token::kPERIOD) || (la3 == Token::kHASH);
LibraryPrefix& prefix = LibraryPrefix::ZoneHandle(Z);
AbstractType& type =
AbstractType::Handle(Z, ParseType(ClassFinalizer::kCanonicalize,
true, // allow deferred type
consume_unresolved_prefix, &prefix));
// A constructor tear-off closure can only have been created for a
// type that is loaded.
ASSERT(prefix.IsNull() || prefix.is_loaded());
ASSERT(!type.IsMalformed() && !type.IsTypeParameter());
if (!FLAG_support_deprecated_tearoff_syntax) {
ReportError("tear-off using the x#id syntax is a deprecated feature");
return;
}
ExpectToken(Token::kHASH);
String* named_constructor = NULL;
if (IsIdentifier()) {
named_constructor = CurrentLiteral();
ConsumeToken();
}
// Resolve the type and optional identifier to a constructor or factory.
Class& type_class = Class::Handle(Z, type.type_class());
String& type_class_name = String::Handle(Z, type_class.Name());
*type_arguments = type.arguments();
String& constructor_name =
BuildConstructorName(T, type_class_name, named_constructor);
*constructor = type_class.LookupConstructor(constructor_name);
if (constructor->IsNull()) {
*constructor = type_class.LookupFactory(constructor_name);
ASSERT(!constructor->IsNull());
if (constructor->IsRedirectingFactory()) {
ClassFinalizer::ResolveRedirectingFactory(type_class, *constructor);
type = constructor->RedirectionType();
ASSERT(!type.IsMalformedOrMalbounded());
if (!type.IsInstantiated()) {
ASSERT(type.IsInstantiated(kFunctions)); // No generic constructors.
Error& error = Error::Handle(Z);
type ^= type.InstantiateFrom(*type_arguments,
Object::null_type_arguments(), &error,
NULL, // instantiation_trail
NULL, // bound_trail
Heap::kOld);
ASSERT(error.IsNull());
}
*type_arguments = type.arguments();
*constructor = constructor->RedirectionTarget();
}
}
}
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) || (la3 == Token::kHASH);
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;
const bool is_tearoff_expression = (CurrentToken() == Token::kHASH);
if (is_tearoff_expression) {
if (!FLAG_support_deprecated_tearoff_syntax) {
ReportError("tear-off using the x#id syntax is a deprecated feature");
}
if (is_const) {
ReportError("tear-off closure not allowed with const allocation");
}
ConsumeToken();
if (IsIdentifier()) {
named_constructor = ExpectIdentifier("name of constructor expected");
}
} else if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
named_constructor = ExpectIdentifier("name of constructor expected");
}
// Parse constructor parameters.
TokenPosition call_pos = TokenPos();
ArgumentListNode* arguments = NULL;
if (!is_tearoff_expression) {
CheckToken(Token::kLPAREN);
call_pos = TokenPos();
arguments =
ParseActualParameters(NULL, TypeArguments::ZoneHandle(Z), is_const);
} else {
// Allocate dummy node with no arguments so we don't have to deal
// with the NULL corner case below.
arguments = new (Z) ArgumentListNode(TokenPos());
}
// 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(), &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();
if (is_tearoff_expression) {
const Function& tearoff_func = Function::ZoneHandle(
Z, BuildConstructorClosureFunction(constructor, new_pos));
// Local functions normally get parsed when the enclosing function is
// compiled. Since constructor tearoff closures don't get parsed here,
// we need to duplicate some of the side effects of parsing, namely
// creating a function scope, and capturing the instantiator of the
// enclosing function if necessary.
OpenFunctionBlock(tearoff_func);
// If there are type arguments in the tearoff expression that are
// not yet instantiated, capture the instantiators.
if (!type_arguments.IsNull() && !type_arguments.IsInstantiated()) {
CaptureAllInstantiators();
}
SequenceNode* tearoff_body = CloseBlock();
ClosureNode* closure_obj =
new (Z) ClosureNode(new_pos, tearoff_func, NULL, tearoff_body->scope());
return closure_obj;
}
ASSERT(!is_tearoff_expression);
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()) {
if (CurrentToken() == Token::kHASH) {
if (!FLAG_support_deprecated_tearoff_syntax) {
ReportError("tear-off using the x#id syntax is a deprecated feature");
}
// Closurization of top-level entity in prefix scope.
return new (Z) LiteralNode(qual_ident_pos, prefix);
} else {
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::Type call_type = 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_type,
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.
if (!FLAG_generic_method_syntax) {
ReportError("generic type arguments not supported.");
}
func_type_args = ParseTypeArguments(ClassFinalizer::kCanonicalize);
if (FLAG_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");
} else if (CurrentToken() == Token::kHASH) {
ConsumeToken();
if (IsIdentifier()) {
ConsumeToken();
}
return;
}
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();
if (CurrentToken() == Token::kHASH) {
ConsumeToken();
if (IsIdentifier()) {
ConsumeToken();
SkipIf(Token::kASSIGN);
} else if (Token::CanBeOverloaded(CurrentToken())) {
ConsumeToken();
} else {
ReportError("identifier or operator expected");
}
}
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