Files
sdk/runtime/vm/parser.cc
T
hausner@google.com ac559f643a Introduce cache of resolved names in library
A name that is not resolved in the local scope gets looked up
in the global scope of the library to which the code being compiled
belongs. The local name dictionary gets searched first, then the
dictionary of each imported library (and and their re-exported
libraries.) Each dictionary lookup includes the lookup of the
mangled getter and setter names, which means that we concatenate
the same name many times.

This change introduces a cache that stores the result of a previous
name lookup, including negative lookup results. The latter is important
to speed up the resolution of names that are not in the global name
space, e.g. class members like .length in lists.

Experiments running dart2js (with VM option --compile_all) show
that this reduces the number of name mangling calls by a factor of 10
and speeds up compiling dart2js by 15%.

R=asiva@google.com

Review URL: https://codereview.chromium.org//135123011

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@32385 260f80e4-7a28-3924-810f-c04153c831b5
2014-02-06 21:32:31 +00:00

10784 lines
400 KiB
C++

// 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 "lib/invocation_mirror.h"
#include "platform/utils.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/flags.h"
#include "vm/growable_array.h"
#include "vm/handles.h"
#include "vm/heap.h"
#include "vm/isolate.h"
#include "vm/longjump.h"
#include "vm/native_arguments.h"
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/os.h"
#include "vm/resolver.h"
#include "vm/scanner.h"
#include "vm/scopes.h"
#include "vm/stack_frame.h"
#include "vm/symbols.h"
#include "vm/timer.h"
#include "vm/zone.h"
namespace dart {
DEFINE_FLAG(bool, enable_asserts, false, "Enable assert statements.");
DEFINE_FLAG(bool, enable_type_checks, false, "Enable type checks.");
DEFINE_FLAG(bool, trace_parser, false, "Trace parser operations.");
DEFINE_FLAG(bool, warning_as_error, false, "Treat warnings as errors.");
DEFINE_FLAG(bool, silent_warnings, false, "Silence warnings.");
DEFINE_FLAG(bool, warn_mixin_typedef, true, "Warning on legacy mixin typedef");
DECLARE_FLAG(bool, error_on_bad_type);
DECLARE_FLAG(bool, throw_on_javascript_int_overflow);
static void CheckedModeHandler(bool value) {
FLAG_enable_asserts = value;
FLAG_enable_type_checks = value;
}
// --enable-checked-mode and --checked both enable checked mode which is
// equivalent to setting --enable-asserts and --enable-type-checks.
DEFINE_FLAG_HANDLER(CheckedModeHandler,
enable_checked_mode,
"Enable checked mode.");
DEFINE_FLAG_HANDLER(CheckedModeHandler,
checked,
"Enable checked mode.");
#if defined(DEBUG)
class TraceParser : public ValueObject {
public:
TraceParser(intptr_t token_pos, const Script& script, const char* msg) {
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);
}
indent_++;
}
}
~TraceParser() { indent_--; }
private:
void PrintIndent() {
for (int i = 0; i < indent_; i++) { OS::Print(". "); }
}
static int indent_;
};
int TraceParser::indent_ = 0;
#define TRACE_PARSER(s) \
TraceParser __p__(this->TokenPos(), this->script_, s)
#else // not DEBUG
#define TRACE_PARSER(s)
#endif // DEBUG
static RawTypeArguments* NewTypeArguments(const GrowableObjectArray& objs) {
const TypeArguments& a =
TypeArguments::Handle(TypeArguments::New(objs.Length()));
AbstractType& type = AbstractType::Handle();
for (int i = 0; i < objs.Length(); i++) {
type ^= objs.At(i);
a.SetTypeAt(i, type);
}
// Cannot canonicalize TypeArgument yet as its types may not have been
// finalized yet.
return a.raw();
}
LocalVariable* ParsedFunction::EnsureExpressionTemp() {
if (!has_expression_temp_var()) {
LocalVariable* temp =
new LocalVariable(function_.token_pos(),
Symbols::ExprTemp(),
Type::ZoneHandle(Type::DynamicType()));
ASSERT(temp != NULL);
set_expression_temp_var(temp);
}
ASSERT(has_expression_temp_var());
return expression_temp_var();
}
void ParsedFunction::SetNodeSequence(SequenceNode* node_sequence) {
ASSERT(node_sequence_ == NULL);
ASSERT(node_sequence != NULL);
node_sequence_ = node_sequence;
}
void ParsedFunction::AllocateVariables() {
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).
LocalScope* context_owner = NULL; // No context needed yet.
int next_free_frame_index =
scope->AllocateVariables(first_parameter_index_,
num_params,
first_stack_local_index_,
scope,
&context_owner);
// If this function allocates context variables, but none of its enclosing
// functions do, the context on entry is not linked as parent of the allocated
// context but saved on entry and restored on exit as to prevent memory leaks.
// Add and allocate a local variable to this purpose.
if (context_owner != NULL) {
const ContextScope& context_scope =
ContextScope::Handle(function().context_scope());
if (context_scope.IsNull() || (context_scope.num_variables() == 0)) {
LocalVariable* context_var =
new LocalVariable(function().token_pos(),
Symbols::SavedEntryContextVar(),
Type::ZoneHandle(Type::DynamicType()));
context_var->set_index(next_free_frame_index--);
scope->AddVariable(context_var);
set_saved_entry_context_var(context_var);
}
}
// 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 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::TryBlocks : public ZoneAllocated {
public:
TryBlocks(Block* try_block, TryBlocks* outer_try_block, intptr_t try_index)
: try_block_(try_block),
inlined_finally_nodes_(),
outer_try_block_(outer_try_block),
try_index_(try_index),
inside_catch_(false) { }
TryBlocks* outer_try_block() const { return outer_try_block_; }
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; }
void AddNodeForFinallyInlining(AstNode* node);
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_;
TryBlocks* outer_try_block_;
const intptr_t try_index_;
bool inside_catch_;
DISALLOW_COPY_AND_ASSIGN(TryBlocks);
};
void Parser::TryBlocks::AddNodeForFinallyInlining(AstNode* node) {
inlined_finally_nodes_.Add(node);
}
// For parsing a compilation unit.
Parser::Parser(const Script& script, const Library& library, intptr_t token_pos)
: isolate_(Isolate::Current()),
script_(Script::Handle(isolate_, script.raw())),
tokens_iterator_(TokenStream::Handle(isolate_, script.tokens()),
token_pos),
token_kind_(Token::kILLEGAL),
current_block_(NULL),
is_top_level_(false),
parsing_metadata_(false),
current_member_(NULL),
allow_function_literals_(true),
parsed_function_(NULL),
innermost_function_(Function::Handle(isolate_)),
literal_token_(LiteralToken::Handle(isolate_)),
current_class_(Class::Handle(isolate_)),
library_(Library::Handle(isolate_, library.raw())),
try_blocks_list_(NULL),
last_used_try_index_(0),
unregister_pending_function_(false) {
ASSERT(tokens_iterator_.IsValid());
ASSERT(!library.IsNull());
}
// For parsing a function.
Parser::Parser(const Script& script,
ParsedFunction* parsed_function,
intptr_t token_position)
: isolate_(Isolate::Current()),
script_(Script::Handle(isolate_, script.raw())),
tokens_iterator_(TokenStream::Handle(isolate_, script.tokens()),
token_position),
token_kind_(Token::kILLEGAL),
current_block_(NULL),
is_top_level_(false),
parsing_metadata_(false),
current_member_(NULL),
allow_function_literals_(true),
parsed_function_(parsed_function),
innermost_function_(Function::Handle(isolate_,
parsed_function->function().raw())),
literal_token_(LiteralToken::Handle(isolate_)),
current_class_(Class::Handle(isolate_,
parsed_function->function().Owner())),
library_(Library::Handle(Class::Handle(
isolate_,
parsed_function->function().origin()).library())),
try_blocks_list_(NULL),
last_used_try_index_(0),
unregister_pending_function_(false) {
ASSERT(tokens_iterator_.IsValid());
ASSERT(!current_function().IsNull());
if (FLAG_enable_type_checks) {
EnsureExpressionTemp();
}
}
Parser::~Parser() {
if (unregister_pending_function_) {
const GrowableObjectArray& pending_functions =
GrowableObjectArray::Handle(
isolate()->object_store()->pending_functions());
ASSERT(pending_functions.Length() > 0);
ASSERT(pending_functions.At(pending_functions.Length()-1) ==
current_function().raw());
pending_functions.RemoveLast();
}
}
void Parser::SetScript(const Script& script, intptr_t token_pos) {
script_ = script.raw();
tokens_iterator_.SetStream(TokenStream::Handle(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_;
}
const Class& Parser::current_class() const {
return current_class_;
}
void Parser::set_current_class(const Class& value) {
current_class_ = value.raw();
}
void Parser::SetPosition(intptr_t position) {
if (position < TokenPos() && position != 0) {
CompilerStats::num_tokens_rewind += (TokenPos() - position);
}
tokens_iterator_.SetCurrentPosition(position);
token_kind_ = Token::kILLEGAL;
}
void Parser::ParseCompilationUnit(const Library& library,
const Script& script) {
ASSERT(Isolate::Current()->long_jump_base()->IsSafeToJump());
TimerScope timer(FLAG_compiler_stats, &CompilerStats::parser_timer);
Parser parser(script, library, 0);
parser.ParseTopLevel();
}
Token::Kind Parser::CurrentToken() {
if (token_kind_ == Token::kILLEGAL) {
token_kind_ = tokens_iterator_.CurrentTokenKind();
if (token_kind_ == Token::kERROR) {
ErrorMsg(TokenPos(), "%s", CurrentLiteral()->ToCString());
}
}
CompilerStats::num_token_checks++;
return token_kind_;
}
Token::Kind Parser::LookaheadToken(int num_tokens) {
CompilerStats::num_tokens_lookahead++;
CompilerStats::num_token_checks++;
return tokens_iterator_.LookaheadTokenKind(num_tokens);
}
String* Parser::CurrentLiteral() const {
String& result = String::ZoneHandle();
result = 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());
if (FLAG_throw_on_javascript_int_overflow) {
const Integer& i = Integer::Handle(ri);
if (i.CheckJavascriptIntegerOverflow()) {
ErrorMsg(TokenPos(),
"Integer literal does not fit in a Javascript integer: %s.",
i.ToCString());
}
}
return ri;
}
// A QualIdent is an optionally qualified identifier.
struct QualIdent {
QualIdent() {
Clear();
}
void Clear() {
lib_prefix = NULL;
ident_pos = 0;
ident = NULL;
}
LibraryPrefix* lib_prefix;
intptr_t ident_pos;
String* ident;
};
struct ParamDesc {
ParamDesc()
: type(NULL),
name_pos(0),
name(NULL),
default_value(NULL),
metadata(NULL),
var(NULL),
is_final(false),
is_field_initializer(false),
has_explicit_type(false) { }
const AbstractType* type;
intptr_t name_pos;
const String* name;
const Object* 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;
};
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;
implicitly_final = false;
skipped = false;
this->parameters = new ZoneGrowableArray<ParamDesc>();
}
void AddFinalParameter(intptr_t 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, intptr_t token_pos) {
ASSERT(this->parameters->is_empty());
AddFinalParameter(token_pos, &Symbols::This(), receiver_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 (!param.is_field_initializer) {
param.var->set_invisible(invisible);
}
}
}
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 implicitly_final;
bool skipped;
ZoneGrowableArray<ParamDesc>* parameters;
};
struct MemberDesc {
MemberDesc() {
Clear();
}
void Clear() {
has_abstract = false;
has_external = false;
has_final = false;
has_const = false;
has_static = false;
has_var = false;
has_factory = false;
has_operator = false;
has_native = false;
metadata_pos = -1;
operator_token = Token::kILLEGAL;
type = NULL;
name_pos = 0;
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_final;
bool has_const;
bool has_static;
bool has_var;
bool has_factory;
bool has_operator;
bool has_native;
intptr_t metadata_pos;
Token::Kind operator_token;
const AbstractType* type;
intptr_t name_pos;
intptr_t 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(const Class& cls,
const String& cls_name,
bool is_interface,
intptr_t token_pos)
: clazz_(cls),
class_name_(cls_name),
token_pos_(token_pos),
functions_(GrowableObjectArray::Handle(GrowableObjectArray::New())),
fields_(GrowableObjectArray::Handle(GrowableObjectArray::New())) {
}
void AddFunction(const Function& function) {
functions_.Add(function);
}
const GrowableObjectArray& functions() const {
return functions_;
}
void AddField(const Field& field) {
fields_.Add(field);
}
const GrowableObjectArray& fields() const {
return fields_;
}
const Class& clazz() const {
return clazz_;
}
const String& class_name() const {
return class_name_;
}
bool has_constructor() const {
Function& func = Function::Handle();
for (int i = 0; i < functions_.Length(); i++) {
func ^= functions_.At(i);
if (func.kind() == RawFunction::kConstructor) {
return true;
}
}
return false;
}
intptr_t 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;
}
private:
const Class& clazz_;
const String& class_name_;
intptr_t token_pos_; // Token index of "class" keyword.
GrowableObjectArray& functions_;
GrowableObjectArray& fields_;
GrowableArray<MemberDesc> members_;
};
struct TopLevel {
TopLevel() :
fields(GrowableObjectArray::Handle(GrowableObjectArray::New())),
functions(GrowableObjectArray::Handle(GrowableObjectArray::New())) { }
GrowableObjectArray& fields;
GrowableObjectArray& functions;
};
static bool HasReturnNode(SequenceNode* seq) {
if (seq->length() == 0) {
return false;
} else if ((seq->length()) == 1 &&
(seq->NodeAt(seq->length() - 1)->IsSequenceNode())) {
return HasReturnNode(seq->NodeAt(seq->length() - 1)->AsSequenceNode());
} else {
return seq->NodeAt(seq->length() - 1)->IsReturnNode();
}
}
void Parser::ParseClass(const Class& cls) {
if (!cls.is_synthesized_class()) {
TimerScope timer(FLAG_compiler_stats, &CompilerStats::parser_timer);
Isolate* isolate = Isolate::Current();
ASSERT(isolate->long_jump_base()->IsSafeToJump());
const Script& script = Script::Handle(isolate, cls.script());
const Library& lib = Library::Handle(isolate, cls.library());
Parser parser(script, lib, cls.token_pos());
parser.ParseClassDefinition(cls);
}
}
RawObject* Parser::ParseFunctionParameters(const Function& func) {
ASSERT(!func.IsNull());
Isolate* isolate = Isolate::Current();
StackZone zone(isolate);
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
const Script& script = Script::Handle(isolate, func.script());
const Class& owner = Class::Handle(isolate, func.Owner());
ASSERT(!owner.IsNull());
ParsedFunction* parsed_function = new ParsedFunction(
Function::ZoneHandle(func.raw()));
Parser parser(script, parsed_function, func.token_pos());
parser.SkipFunctionPreamble();
ParamList params;
parser.ParseFormalParameterList(true, true, &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));
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 {
Error& error = Error::Handle();
error = isolate->object_store()->sticky_error();
isolate->object_store()->clear_sticky_error();
return error.raw();
}
UNREACHABLE();
return Object::null();
}
void Parser::ParseFunction(ParsedFunction* parsed_function) {
TimerScope timer(FLAG_compiler_stats, &CompilerStats::parser_timer);
Isolate* isolate = Isolate::Current();
ASSERT(isolate->long_jump_base()->IsSafeToJump());
ASSERT(parsed_function != NULL);
const Function& func = parsed_function->function();
const Script& script = Script::Handle(isolate, func.script());
Parser parser(script, parsed_function, func.token_pos());
SequenceNode* node_sequence = NULL;
Array& default_parameter_values = Array::ZoneHandle(isolate, Array::null());
switch (func.kind()) {
case RawFunction::kRegularFunction:
case RawFunction::kClosureFunction:
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, default_parameter_values);
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);
break;
case RawFunction::kStaticInitializer:
node_sequence = parser.ParseStaticInitializer(func);
break;
case RawFunction::kMethodExtractor:
node_sequence = parser.ParseMethodExtractor(func);
break;
case RawFunction::kNoSuchMethodDispatcher:
node_sequence =
parser.ParseNoSuchMethodDispatcher(func, default_parameter_values);
break;
case RawFunction::kInvokeFieldDispatcher:
node_sequence =
parser.ParseInvokeFieldDispatcher(func, default_parameter_values);
break;
default:
UNREACHABLE();
}
if (!HasReturnNode(node_sequence)) {
// Add implicit return node.
node_sequence->Add(new ReturnNode(func.end_token_pos()));
}
if (parsed_function->has_expression_temp_var()) {
node_sequence->scope()->AddVariable(parsed_function->expression_temp_var());
}
if (parsed_function->has_saved_current_context_var()) {
node_sequence->scope()->AddVariable(
parsed_function->saved_current_context_var());
}
parsed_function->SetNodeSequence(node_sequence);
// The instantiator 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(
new LoadLocalNode(node_sequence->token_pos(), instantiator));
}
}
parsed_function->set_default_parameter_values(default_parameter_values);
}
RawObject* Parser::ParseMetadata(const Class& cls, intptr_t token_pos) {
Isolate* isolate = Isolate::Current();
StackZone zone(isolate);
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
const Script& script = Script::Handle(cls.script());
const Library& lib = Library::Handle(cls.library());
Parser parser(script, lib, token_pos);
parser.set_current_class(cls);
parser.set_parsing_metadata(true);
RawObject* metadata = parser.EvaluateMetadata();
return metadata;
} else {
Error& error = Error::Handle();
error = isolate->object_store()->sticky_error();
isolate->object_store()->clear_sticky_error();
return error.raw();
}
UNREACHABLE();
return Object::null();
}
RawArray* Parser::EvaluateMetadata() {
CheckToken(Token::kAT, "Metadata character '@' expected");
GrowableObjectArray& meta_values =
GrowableObjectArray::Handle(GrowableObjectArray::New());
while (CurrentToken() == Token::kAT) {
ConsumeToken();
intptr_t expr_pos = TokenPos();
if (!IsIdentifier()) {
ExpectIdentifier("identifier expected");
}
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();
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 {
ErrorMsg(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()) {
ErrorMsg(expr_pos, "Metadata expressions must refer to a const field "
"or constructor");
}
ConsumeToken();
const intptr_t ident_pos = TokenPos();
String* ident = ExpectIdentifier("identifier expected");
const Field& field = Field::Handle(cls.LookupStaticField(*ident));
if (field.IsNull()) {
ErrorMsg(ident_pos,
"Class '%s' has no field '%s'",
cls.ToCString(),
ident->ToCString());
}
if (!field.is_const()) {
ErrorMsg(ident_pos,
"Field '%s' of class '%s' is not const",
ident->ToCString(),
cls.ToCString());
}
expr = GenerateStaticFieldLookup(field, ident_pos);
}
}
if (expr->EvalConstExpr() == NULL) {
ErrorMsg(expr_pos, "expression must be a compile-time constant");
}
const Instance& val = EvaluateConstExpr(expr_pos, expr);
meta_values.Add(val);
}
return Array::MakeArray(meta_values);
}
SequenceNode* Parser::ParseStaticFinalGetter(const Function& func) {
TRACE_PARSER("ParseStaticFinalGetter");
ParamList params;
ASSERT(func.num_fixed_parameters() == 0); // static.
ASSERT(!func.HasOptionalParameters());
ASSERT(AbstractType::Handle(func.result_type()).IsResolved());
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
intptr_t ident_pos = TokenPos();
const String& field_name = *ExpectIdentifier("field name expected");
const Class& field_class = Class::Handle(func.Owner());
const Field& field =
Field::ZoneHandle(field_class.LookupStaticField(field_name));
// Static final fields must have an initializer.
ExpectToken(Token::kASSIGN);
const intptr_t 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) {
ErrorMsg(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.
// The following generated code lazily initializes the field:
// if (field.value === transition_sentinel) {
// field.value = null;
// throw("circular dependency in field initialization");
// }
// if (field.value === sentinel) {
// field.value = transition_sentinel;
// field.value = expr;
// }
// return field.value; // Type check is executed here in checked mode.
// Generate code checking for circular dependency in field initialization.
AstNode* compare_circular = new ComparisonNode(
ident_pos,
Token::kEQ_STRICT,
new LoadStaticFieldNode(ident_pos, field),
new LiteralNode(ident_pos, Object::transition_sentinel()));
// Set field to null prior to throwing exception, so that subsequent
// accesses to the field do not throw again, since initializers should only
// be executed once.
SequenceNode* report_circular = new SequenceNode(ident_pos, NULL);
report_circular->Add(
new StoreStaticFieldNode(
ident_pos,
field,
new LiteralNode(ident_pos, Instance::ZoneHandle())));
// Call CyclicInitializationError._throwNew(field_name).
ArgumentListNode* error_arguments = new ArgumentListNode(ident_pos);
error_arguments->Add(new LiteralNode(ident_pos, field_name));
report_circular->Add(
MakeStaticCall(Symbols::CyclicInitializationError(),
Library::PrivateCoreLibName(Symbols::ThrowNew()),
error_arguments));
AstNode* circular_check =
new IfNode(ident_pos, compare_circular, report_circular, NULL);
current_block_->statements->Add(circular_check);
// Generate code checking for uninitialized field.
AstNode* compare_uninitialized = new ComparisonNode(
ident_pos,
Token::kEQ_STRICT,
new LoadStaticFieldNode(ident_pos, field),
new LiteralNode(ident_pos, Object::sentinel()));
SequenceNode* initialize_field = new SequenceNode(ident_pos, NULL);
initialize_field->Add(
new StoreStaticFieldNode(
ident_pos,
field,
new LiteralNode(ident_pos, Object::transition_sentinel())));
const String& init_name = String::Handle(
Symbols::New(String::Handle(String::Concat(
Symbols::InitPrefix(), String::Handle(field.name())))));
const Function& init_function = Function::ZoneHandle(
field_class.LookupStaticFunction(init_name));
ASSERT(!init_function.IsNull());
ArgumentListNode* arguments = new ArgumentListNode(expr_pos);
StaticCallNode* init_call =
new StaticCallNode(expr_pos, init_function, arguments);
initialize_field->Add(init_call);
AstNode* uninitialized_check =
new IfNode(ident_pos, compare_uninitialized, initialize_field, NULL);
current_block_->statements->Add(uninitialized_check);
// Generate code returning the field value.
ReturnNode* return_node =
new ReturnNode(ident_pos,
new LoadStaticFieldNode(ident_pos, field));
current_block_->statements->Add(return_node);
}
return CloseBlock();
}
SequenceNode* Parser::ParseStaticInitializer(const Function& func) {
TRACE_PARSER("ParseStaticInitializer");
ParamList params;
ASSERT(func.num_fixed_parameters() == 0); // static.
ASSERT(!func.HasOptionalParameters());
ASSERT(AbstractType::Handle(func.result_type()).IsResolved());
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
// Move forward to the start of the initializer expression.
intptr_t ident_pos = TokenPos();
ExpectIdentifier("identifier expected");
ExpectToken(Token::kASSIGN);
intptr_t token_pos = TokenPos();
// Synthesize a try-catch block to wrap the initializer expression.
LocalVariable* context_var =
current_block_->scope->LocalLookupVariable(Symbols::SavedTryContextVar());
if (context_var == NULL) {
context_var = new LocalVariable(token_pos,
Symbols::SavedTryContextVar(),
Type::ZoneHandle(Type::DynamicType()));
current_block_->scope->AddVariable(context_var);
}
LocalVariable* catch_excp_var =
current_block_->scope->LocalLookupVariable(Symbols::ExceptionVar());
if (catch_excp_var == NULL) {
catch_excp_var = new LocalVariable(token_pos,
Symbols::ExceptionVar(),
Type::ZoneHandle(Type::DynamicType()));
current_block_->scope->AddVariable(catch_excp_var);
}
LocalVariable* catch_trace_var =
current_block_->scope->LocalLookupVariable(Symbols::StackTraceVar());
if (catch_trace_var == NULL) {
catch_trace_var = new LocalVariable(token_pos,
Symbols::StackTraceVar(),
Type::ZoneHandle(Type::DynamicType()));
current_block_->scope->AddVariable(catch_trace_var);
}
OpenBlock(); // Start try block.
AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades);
const Field& field = Field::ZoneHandle(func.saved_static_field());
ASSERT(!field.is_const());
StoreStaticFieldNode* store = new StoreStaticFieldNode(field.token_pos(),
field,
expr);
current_block_->statements->Add(store);
SequenceNode* try_block = CloseBlock(); // End try block.
OpenBlock(); // Start catch handler list.
OpenBlock(); // Start catch clause.
AstNode* compare_transition_sentinel = new ComparisonNode(
token_pos,
Token::kEQ_STRICT,
new LoadStaticFieldNode(ident_pos, field),
new LiteralNode(field.token_pos(), Object::transition_sentinel()));
SequenceNode* store_null = new SequenceNode(token_pos, NULL);
store_null->Add(new StoreStaticFieldNode(
field.token_pos(),
field,
new LiteralNode(token_pos, Instance::ZoneHandle())));
AstNode* transition_sentinel_check =
new IfNode(token_pos, compare_transition_sentinel, store_null, NULL);
current_block_->statements->Add(transition_sentinel_check);
current_block_->statements->Add(
new ThrowNode(token_pos,
new LoadLocalNode(token_pos, catch_excp_var),
new LoadLocalNode(token_pos, catch_trace_var)));
SequenceNode* catch_clause = CloseBlock(); // End catch clause.
current_block_->statements->Add(catch_clause);
SequenceNode* catch_handler_list = CloseBlock(); // End catch handler list.
CatchClauseNode* catch_block =
new CatchClauseNode(token_pos,
catch_handler_list,
Array::ZoneHandle(Object::empty_array().raw()),
context_var,
catch_excp_var,
catch_trace_var,
CatchClauseNode::kInvalidTryIndex,
false); // No stack trace needed.
AstNode* try_catch_node = new TryCatchNode(token_pos,
try_block,
context_var,
catch_block,
NULL, // No finally block.
AllocateTryIndex());
current_block_->statements->Add(try_catch_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 intptr_t 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(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);
ASSERT(IsIdentifier());
const String& field_name = *CurrentLiteral();
const Class& field_class = Class::Handle(func.Owner());
const Field& field =
Field::ZoneHandle(field_class.LookupInstanceField(field_name));
LoadInstanceFieldNode* load_field =
new LoadInstanceFieldNode(ident_pos, load_receiver, field);
ReturnNode* return_node =
new ReturnNode(Scanner::kNoSourcePos, 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 intptr_t ident_pos = func.token_pos();
const String& field_name = *CurrentLiteral();
const Class& field_class = Class::ZoneHandle(func.Owner());
const Field& field =
Field::ZoneHandle(field_class.LookupInstanceField(field_name));
const AbstractType& field_type = AbstractType::ZoneHandle(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(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);
current_block_->statements->Add(store_field);
current_block_->statements->Add(new ReturnNode(Scanner::kNoSourcePos));
return CloseBlock();
}
SequenceNode* Parser::ParseMethodExtractor(const Function& func) {
TRACE_PARSER("ParseMethodExtractor");
ParamList params;
const intptr_t ident_pos = func.token_pos();
ASSERT(func.token_pos() == 0);
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(func.extracted_method_closure()),
load_receiver,
NULL);
ReturnNode* return_node = new ReturnNode(Scanner::kNoSourcePos, closure);
current_block_->statements->Add(return_node);
return CloseBlock();
}
void Parser::BuildDispatcherScope(const Function& func,
const ArgumentsDescriptor& desc,
Array& default_values) {
ParamList params;
// Receiver first.
intptr_t 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(Symbols::New(name));
p.type = &Type::ZoneHandle(Type::DynamicType());
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(desc.NameAt(index));
p.type = &Type::ZoneHandle(Type::DynamicType());
p.default_value = &Object::ZoneHandle();
params.parameters->Add(p);
params.num_optional_parameters++;
params.has_optional_named_parameters = true;
}
ASSERT(desc.NamedCount() == params.num_optional_parameters);
SetupDefaultsForOptionalParams(&params, default_values);
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
}
SequenceNode* Parser::ParseNoSuchMethodDispatcher(const Function& func,
Array& default_values) {
TRACE_PARSER("ParseNoSuchMethodDispatcher");
ASSERT(func.IsNoSuchMethodDispatcher());
intptr_t token_pos = func.token_pos();
ASSERT(func.token_pos() == 0);
ASSERT(current_class().raw() == func.Owner());
ArgumentsDescriptor desc(Array::Handle(func.saved_args_desc()));
ASSERT(desc.Count() > 0);
// Set up scope for this function.
BuildDispatcherScope(func, desc, default_values);
// Receiver is local 0.
LocalScope* scope = current_block_->scope;
ArgumentListNode* func_args = new ArgumentListNode(token_pos);
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(Array::New(desc.NamedCount()));
for (intptr_t i = 0; i < arg_names.Length(); ++i) {
arg_names.SetAt(i, String::Handle(desc.NameAt(i)));
}
func_args->set_names(arg_names);
}
const String& func_name = String::ZoneHandle(func.name());
ArgumentListNode* arguments = BuildNoSuchMethodArguments(
token_pos, func_name, *func_args, NULL, false);
const Function& no_such_method = Function::ZoneHandle(
Resolver::ResolveDynamicAnyArgs(Class::Handle(func.Owner()),
Symbols::NoSuchMethod()));
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,
Array& default_values) {
TRACE_PARSER("ParseInvokeFieldDispatcher");
ASSERT(func.IsInvokeFieldDispatcher());
intptr_t token_pos = func.token_pos();
ASSERT(func.token_pos() == 0);
ASSERT(current_class().raw() == func.Owner());
const Array& args_desc = Array::Handle(func.saved_args_desc());
ArgumentsDescriptor desc(args_desc);
ASSERT(desc.Count() > 0);
// Set up scope for this function.
BuildDispatcherScope(func, desc, default_values);
// 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 String& name = String::Handle(func.name());
const String& getter_name =
String::ZoneHandle(Symbols::New(String::Handle(Field::GetterName(name))));
InstanceCallNode* getter_call = new InstanceCallNode(token_pos,
receiver,
getter_name,
no_args);
// Pass arguments 1..n to the closure call.
ArgumentListNode* closure_args = new ArgumentListNode(token_pos);
const Array& names = Array::Handle(Array::New(desc.NamedCount(), Heap::kOld));
// Positional parameters.
intptr_t i = 1;
for (; i < desc.PositionalCount(); ++i) {
closure_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
}
// Named parameters.
for (; i < desc.Count(); i++) {
closure_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
intptr_t index = i - desc.PositionalCount();
names.SetAt(index, String::Handle(desc.NameAt(index)));
}
closure_args->set_names(names);
EnsureSavedCurrentContext();
ClosureCallNode* closure_call = new ClosureCallNode(token_pos,
getter_call,
closure_args);
ReturnNode* return_node = new ReturnNode(token_pos, closure_call);
current_block_->statements->Add(return_node);
return CloseBlock();
}
void Parser::SkipBlock() {
ASSERT(CurrentToken() == Token::kLBRACE);
GrowableArray<Token::Kind> token_stack(8);
// Adding the first kLBRACE here, because it will be consumed in the loop
// right away.
token_stack.Add(CurrentToken());
const intptr_t block_start_pos = TokenPos();
bool is_match = true;
bool unexpected_token_found = false;
Token::Kind token;
intptr_t token_pos;
do {
ConsumeToken();
token = CurrentToken();
token_pos = TokenPos();
switch (token) {
case Token::kLBRACE:
case Token::kLPAREN:
case Token::kLBRACK:
token_stack.Add(token);
break;
case Token::kRBRACE:
is_match = token_stack.RemoveLast() == Token::kLBRACE;
break;
case Token::kRPAREN:
is_match = token_stack.RemoveLast() == Token::kLPAREN;
break;
case Token::kRBRACK:
is_match = token_stack.RemoveLast() == Token::kLBRACK;
break;
case Token::kEOS:
unexpected_token_found = true;
break;
default:
// nothing.
break;
}
} while (!token_stack.is_empty() && is_match && !unexpected_token_found);
if (!is_match) {
ErrorMsg(token_pos, "unbalanced '%s'", Token::Str(token));
} else if (unexpected_token_found) {
ErrorMsg(block_start_pos, "unterminated block");
}
}
void Parser::ParseFormalParameter(bool allow_explicit_default_value,
bool evaluate_metadata,
ParamList* params) {
TRACE_PARSER("ParseFormalParameter");
ParamDesc parameter;
bool var_seen = false;
bool this_seen = false;
if (evaluate_metadata && (CurrentToken() == Token::kAT)) {
parameter.metadata = &Array::ZoneHandle(EvaluateMetadata());
} else {
SkipMetadata();
}
if (CurrentToken() == Token::kFINAL) {
ConsumeToken();
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 = &Type::ZoneHandle(Type::DynamicType());
}
if (CurrentToken() == Token::kTHIS) {
ConsumeToken();
ExpectToken(Token::kPERIOD);
this_seen = true;
parameter.is_field_initializer = 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 = &Type::ZoneHandle(Type::VoidType());
}
if (parameter.type == NULL) {
// At this point, we must see an identifier for the type or the
// function parameter.
if (!IsIdentifier()) {
ErrorMsg("parameter name or type expected");
}
// We have not seen a parameter type yet, so we check if the next
// identifier could represent a type before parsing it.
Token::Kind follower = LookaheadToken(1);
// We have an identifier followed by a 'follower' token.
// We either parse a type or assume that no type is specified.
if ((follower == Token::kLT) || // Parameterized type.
(follower == Token::kPERIOD) || // Qualified class name of type.
Token::IsIdentifier(follower) || // Parameter name following a type.
(follower == Token::kTHIS)) { // Field parameter following a 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;
parameter.type = &AbstractType::ZoneHandle(
ParseType(is_top_level_ ? ClassFinalizer::kResolveTypeParameters :
ClassFinalizer::kCanonicalize));
} 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 = &Type::ZoneHandle(Type::DynamicType());
}
}
if (!this_seen && (CurrentToken() == Token::kTHIS)) {
ConsumeToken();
ExpectToken(Token::kPERIOD);
this_seen = true;
parameter.is_field_initializer = 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) == '_')) {
ErrorMsg(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)) {
ErrorMsg(parameter.name_pos, "duplicate formal parameter '%s'",
parameter.name->ToCString());
}
}
if (CurrentToken() == Token::kLPAREN) {
// 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.
if (!var_seen && !parameter.is_final) {
// The parsed parameter type is actually the function result type.
const AbstractType& result_type =
AbstractType::Handle(parameter.type->raw());
// Finish parsing the function type parameter.
ParamList func_params;
// Add implicit closure object parameter.
func_params.AddFinalParameter(
TokenPos(),
&Symbols::ClosureParameter(),
&Type::ZoneHandle(Type::DynamicType()));
const bool no_explicit_default_values = false;
ParseFormalParameterList(no_explicit_default_values, false, &func_params);
// The field 'is_static' has no meaning for signature functions.
const Function& signature_function = Function::Handle(
Function::New(*parameter.name,
RawFunction::kSignatureFunction,
/* is_static = */ false,
/* is_const = */ false,
/* is_abstract = */ false,
/* is_external = */ false,
/* is_native = */ false,
current_class(),
parameter.name_pos));
signature_function.set_result_type(result_type);
AddFormalParamsToFunction(&func_params, signature_function);
const String& signature = String::Handle(signature_function.Signature());
// Lookup the signature class, i.e. the class whose name is the signature.
// We only lookup in the current library, but not in its imports, and only
// create a new canonical signature class if it does not exist yet.
Class& signature_class = Class::ZoneHandle(
library_.LookupLocalClass(signature));
if (signature_class.IsNull()) {
signature_class = Class::NewSignatureClass(signature,
signature_function,
script_,
parameter.name_pos);
// Record the function signature class in the current library, unless
// we are currently skipping a formal parameter list, in which case
// the signature class could remain unfinalized.
if (!params->skipped) {
library_.AddClass(signature_class);
}
} else {
signature_function.set_signature_class(signature_class);
}
ASSERT(signature_function.signature_class() == signature_class.raw());
Type& signature_type = Type::ZoneHandle(signature_class.SignatureType());
if (!is_top_level_ && !signature_type.IsFinalized()) {
signature_type ^= ClassFinalizer::FinalizeType(
signature_class, signature_type, ClassFinalizer::kCanonicalize);
}
// 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) {
ErrorMsg("parameter must not specify a default value");
}
if (params->has_optional_positional_parameters) {
ExpectToken(Token::kASSIGN);
} else {
ExpectToken(Token::kCOLON);
}
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 Object& 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::ZoneHandle();
} else {
params->num_fixed_parameters++;
ASSERT(params->num_optional_parameters == 0);
}
}
if (parameter.type->IsVoidType()) {
ErrorMsg("parameter '%s' may not be 'void'", parameter.name->ToCString());
}
if (params->implicitly_final) {
parameter.is_final = true;
}
params->parameters->Add(parameter);
}
// Parses a sequence of normal or optional formal parameters.
void Parser::ParseFormalParameters(bool allow_explicit_default_values,
bool evaluate_metadata,
ParamList* params) {
TRACE_PARSER("ParseFormalParameters");
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;
}
ParseFormalParameter(allow_explicit_default_values,
evaluate_metadata,
params);
} while (CurrentToken() == Token::kCOMMA);
}
void Parser::ParseFormalParameterList(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(allow_explicit_default_values,
evaluate_metadata,
params);
if (params->has_optional_positional_parameters ||
params->has_optional_named_parameters) {
// Parse optional parameters.
ParseFormalParameters(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) {
ErrorMsg("',' or ')' expected");
}
} else {
ConsumeToken();
}
ExpectToken(Token::kRPAREN);
}
String& Parser::ParseNativeDeclaration() {
TRACE_PARSER("ParseNativeDeclaration");
ASSERT(IsLiteral("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(intptr_t token_pos,
const String& name,
ArgumentListNode* arguments,
bool resolve_getter,
bool* is_no_such_method) {
const Class& super_class = Class::Handle(current_class().SuperClass());
if (super_class.IsNull()) {
ErrorMsg(token_pos, "class '%s' does not have a superclass",
String::Handle(current_class().Name()).ToCString());
}
Function& super_func = Function::Handle(
Resolver::ResolveDynamicAnyArgs(super_class, name));
if (!super_func.IsNull() &&
!super_func.AreValidArguments(arguments->length(),
arguments->names(),
NULL)) {
super_func = Function::null();
} else if (super_func.IsNull() && resolve_getter) {
const String& getter_name = String::ZoneHandle(Field::GetterName(name));
super_func = Resolver::ResolveDynamicAnyArgs(super_class, getter_name);
ASSERT(super_func.IsNull() ||
(super_func.kind() != RawFunction::kImplicitStaticFinalGetter));
}
if (super_func.IsNull()) {
super_func =
Resolver::ResolveDynamicAnyArgs(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(
intptr_t call_pos,
const String& function_name,
const ArgumentListNode& function_args,
const LocalVariable* temp_for_last_arg,
bool is_super_invocation) {
const intptr_t 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.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 receiver.
ArrayNode* args_array =
new ArrayNode(args_pos, Type::ZoneHandle(Type::ArrayType()));
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(
intptr_t 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 intptr_t 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) {
TRACE_PARSER("ParseSuperCall");
ASSERT(CurrentToken() == Token::kLPAREN);
const intptr_t supercall_pos = TokenPos();
// 'this' parameter is the first argument to super call.
ArgumentListNode* arguments = new ArgumentListNode(supercall_pos);
AstNode* receiver = LoadReceiver(supercall_pos);
arguments->Add(receiver);
ParseActualParameters(arguments, kAllowConst);
const bool kResolveGetter = true;
bool is_no_such_method = false;
const Function& super_function = Function::ZoneHandle(
GetSuperFunction(supercall_pos,
function_name,
arguments,
kResolveGetter,
&is_no_such_method));
if (super_function.IsGetterFunction() ||
super_function.IsImplicitGetterFunction()) {
const Class& super_class = Class::ZoneHandle(current_class().SuperClass());
AstNode* closure = new StaticGetterNode(supercall_pos,
LoadReceiver(supercall_pos),
/* is_super_getter */ true,
super_class,
function_name);
EnsureSavedCurrentContext();
// 'this' is not passed as parameter to the closure.
ArgumentListNode* closure_arguments = new ArgumentListNode(supercall_pos);
for (int i = 1; i < arguments->length(); i++) {
closure_arguments->Add(arguments->NodeAt(i));
}
return new ClosureCallNode(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 intptr_t 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 =
String::ZoneHandle(Symbols::New(Token::Str(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(
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 {
ErrorMsg(super_pos, "illegal super operator call");
}
return super_op;
}
AstNode* Parser::ParseSuperOperator() {
TRACE_PARSER("ParseSuperOperator");
AstNode* super_op = NULL;
const intptr_t 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(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 =
String::ZoneHandle(Symbols::New(Token::Str(op)));
const bool kResolveGetter = false;
bool is_no_such_method = false;
const Function& super_operator = Function::ZoneHandle(
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;
}
AstNode* Parser::CreateImplicitClosureNode(const Function& func,
intptr_t token_pos,
AstNode* receiver) {
Function& implicit_closure_function =
Function::ZoneHandle(func.ImplicitClosureFunction());
if (receiver != NULL) {
// If we create an implicit instance closure from inside a closure of a
// parameterized class, make sure that the receiver is captured as
// instantiator.
if (current_block_->scope->function_level() > 0) {
const Class& signature_class = Class::Handle(
implicit_closure_function.signature_class());
if (signature_class.NumTypeParameters() > 0) {
CaptureInstantiator();
}
}
}
return new ClosureNode(token_pos, implicit_closure_function, receiver, NULL);
}
AstNode* Parser::ParseSuperFieldAccess(const String& field_name,
intptr_t field_pos) {
TRACE_PARSER("ParseSuperFieldAccess");
const Class& super_class = Class::ZoneHandle(current_class().SuperClass());
if (super_class.IsNull()) {
ErrorMsg("class '%s' does not have a superclass",
String::Handle(current_class().Name()).ToCString());
}
AstNode* implicit_argument = LoadReceiver(field_pos);
const String& getter_name =
String::ZoneHandle(Field::GetterName(field_name));
const Function& super_getter = Function::ZoneHandle(
Resolver::ResolveDynamicAnyArgs(super_class, getter_name));
if (super_getter.IsNull()) {
const String& setter_name =
String::ZoneHandle(Field::SetterName(field_name));
const Function& super_setter = Function::ZoneHandle(
Resolver::ResolveDynamicAnyArgs(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(
Resolver::ResolveDynamicAnyArgs(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 StaticGetterNode(
field_pos, implicit_argument, true, super_class, field_name);
}
void Parser::GenerateSuperConstructorCall(const Class& cls,
intptr_t supercall_pos,
LocalVariable* receiver,
ArgumentListNode* forwarding_args) {
const Class& super_class = Class::Handle(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(super_class.SuperClass()).IsObjectClass())) {
return;
}
String& super_ctor_name = String::Handle(super_class.Name());
super_ctor_name = String::Concat(super_ctor_name, Symbols::Dot());
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);
// Implicit construction phase parameter is second argument.
AstNode* phase_parameter =
new LiteralNode(supercall_pos,
Smi::ZoneHandle(Smi::New(Function::kCtorPhaseAll)));
arguments->Add(phase_parameter);
// If this is a super call in a forwarding constructor, add the user-
// defined arguments to the super call and adjust the 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(current_function().name());
String& class_name = String::Handle(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.
ctor_name = String::SubString(ctor_name, class_name.Length() + 1);
super_ctor_name = String::Concat(super_ctor_name, ctor_name);
}
}
// Resolve super constructor function and check arguments.
const Function& super_ctor = Function::ZoneHandle(
super_class.LookupConstructor(super_ctor_name));
if (super_ctor.IsNull()) {
ErrorMsg(supercall_pos,
"unresolved implicit call to super constructor '%s()'",
String::Handle(super_class.Name()).ToCString());
}
if (current_function().is_const() && !super_ctor.is_const()) {
ErrorMsg(supercall_pos, "implicit call to non-const super constructor");
}
String& error_message = String::Handle();
if (!super_ctor.AreValidArguments(arguments->length(),
arguments->names(),
&error_message)) {
ErrorMsg(supercall_pos,
"invalid arguments passed to super constructor '%s()': %s",
String::Handle(super_class.Name()).ToCString(),
error_message.ToCString());
}
current_block_->statements->Add(
new StaticCallNode(supercall_pos, super_ctor, arguments));
}
AstNode* Parser::ParseSuperInitializer(const Class& cls,
LocalVariable* receiver) {
TRACE_PARSER("ParseSuperInitializer");
ASSERT(CurrentToken() == Token::kSUPER);
const intptr_t supercall_pos = TokenPos();
ConsumeToken();
const Class& super_class = Class::Handle(cls.SuperClass());
ASSERT(!super_class.IsNull());
String& ctor_name = String::Handle(super_class.Name());
ctor_name = String::Concat(ctor_name, Symbols::Dot());
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ctor_name = String::Concat(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);
// Second implicit parameter is the construction phase. We optimistically
// assume that we can execute both the super initializer and the super
// constructor body. We may later change this to only execute the
// super initializer.
AstNode* phase_parameter =
new LiteralNode(supercall_pos,
Smi::ZoneHandle(Smi::New(Function::kCtorPhaseAll)));
arguments->Add(phase_parameter);
// 'this' parameter must not be accessible to the other super call arguments.
receiver->set_invisible(true);
ParseActualParameters(arguments, kAllowConst);
receiver->set_invisible(false);
// Resolve the constructor.
const Function& super_ctor = Function::ZoneHandle(
super_class.LookupConstructor(ctor_name));
if (super_ctor.IsNull()) {
ErrorMsg(supercall_pos,
"super class constructor '%s' not found",
ctor_name.ToCString());
}
if (current_function().is_const() && !super_ctor.is_const()) {
ErrorMsg(supercall_pos, "super constructor must be const");
}
String& error_message = String::Handle();
if (!super_ctor.AreValidArguments(arguments->length(),
arguments->names(),
&error_message)) {
ErrorMsg(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 intptr_t field_pos = TokenPos();
if (CurrentToken() == Token::kTHIS) {
ConsumeToken();
ExpectToken(Token::kPERIOD);
}
const String& field_name = *ExpectIdentifier("field name expected");
ExpectToken(Token::kASSIGN);
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() && !init_expr->IsPotentiallyConst()) {
ErrorMsg(field_pos,
"initializer expression must be compile time constant.");
}
Field& field = Field::ZoneHandle(cls.LookupInstanceField(field_name));
if (field.IsNull()) {
ErrorMsg(field_pos, "unresolved reference to instance field '%s'",
field_name.ToCString());
}
CheckDuplicateFieldInit(field_pos, initialized_fields, &field);
AstNode* instance = new LoadLocalNode(field_pos, receiver);
EnsureExpressionTemp();
return new StoreInstanceFieldNode(field_pos, instance, field, init_expr);
}
void Parser::CheckFieldsInitialized(const Class& cls) {
const Array& fields = Array::Handle(cls.fields());
Field& field = Field::Handle();
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();
if (initializer->field().raw() == field.raw()) {
found = true;
break;
}
}
}
if (found) continue;
field.UpdateGuardedCidAndLength(Object::Handle());
}
}
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 current
// class, library, and token stream (script).
ASSERT(current_class().raw() != field.origin());
const Class& saved_class = Class::Handle(current_class().raw());
const Library& saved_library = Library::Handle(library().raw());
const Script& saved_script = Script::Handle(script().raw());
const intptr_t saved_token_pos = TokenPos();
set_current_class(Class::Handle(field.origin()));
set_library(Library::Handle(current_class().library()));
SetScript(Script::Handle(current_class().script()), field.token_pos());
ASSERT(IsIdentifier());
ConsumeToken();
ExpectToken(Token::kASSIGN);
AstNode* init_expr = NULL;
intptr_t expr_pos = TokenPos();
if (field.is_const()) {
init_expr = ParseConstExpr();
} else {
init_expr = ParseExpr(kAllowConst, kConsumeCascades);
if (init_expr->EvalConstExpr() != NULL) {
init_expr =
new LiteralNode(field.token_pos(),
EvaluateConstExpr(expr_pos, init_expr));
}
}
set_current_class(saved_class);
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(cls.fields());
Field& f = Field::Handle();
const intptr_t 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();
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 (field.is_const()) {
init_expr = ParseConstExpr();
} else {
intptr_t expr_pos = TokenPos();
init_expr = ParseExpr(kAllowConst, kConsumeCascades);
if (init_expr->EvalConstExpr() != NULL) {
init_expr = new LiteralNode(field.token_pos(),
EvaluateConstExpr(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);
current_block_->statements->Add(field_init);
}
}
SetPosition(saved_pos);
}
void Parser::CheckDuplicateFieldInit(intptr_t init_pos,
GrowableArray<Field*>* initialized_fields,
Field* field) {
ASSERT(!field->is_static());
for (int i = 0; i < initialized_fields->length(); i++) {
Field* initialized_field = (*initialized_fields)[i];
if (initialized_field->raw() == field->raw()) {
ErrorMsg(init_pos,
"duplicate initialization for field %s",
String::Handle(field->name()).ToCString());
}
}
initialized_fields->Add(field);
}
void Parser::ParseInitializers(const Class& cls,
LocalVariable* receiver,
GrowableArray<Field*>* initialized_fields) {
TRACE_PARSER("ParseInitializers");
bool super_init_seen = false;
if (CurrentToken() == Token::kCOLON) {
do {
ConsumeToken(); // Colon or comma.
AstNode* init_statement;
if (CurrentToken() == Token::kSUPER) {
if (super_init_seen) {
ErrorMsg("duplicate call to super constructor");
}
init_statement = ParseSuperInitializer(cls, receiver);
super_init_seen = true;
} else {
init_statement = ParseInitializer(cls, receiver, initialized_fields);
}
current_block_->statements->Add(init_statement);
} while (CurrentToken() == Token::kCOMMA);
}
if (!super_init_seen) {
// Generate implicit super() if we haven't seen an explicit super call
// or constructor redirection.
GenerateSuperConstructorCall(cls, TokenPos(), receiver, NULL);
}
CheckFieldsInitialized(cls);
}
void Parser::ParseConstructorRedirection(const Class& cls,
LocalVariable* receiver) {
TRACE_PARSER("ParseConstructorRedirection");
ExpectToken(Token::kCOLON);
ASSERT(CurrentToken() == Token::kTHIS);
const intptr_t call_pos = TokenPos();
ConsumeToken();
String& ctor_name = String::Handle(cls.Name());
ctor_name = String::Concat(ctor_name, Symbols::Dot());
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ctor_name = String::Concat(ctor_name,
*ExpectIdentifier("constructor name expected"));
}
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);
// Construction phase parameter is second argument.
LocalVariable* phase_param = LookupPhaseParameter();
ASSERT(phase_param != NULL);
AstNode* phase_argument = new LoadLocalNode(call_pos, phase_param);
arguments->Add(phase_argument);
receiver->set_invisible(true);
ParseActualParameters(arguments, kAllowConst);
receiver->set_invisible(false);
// Resolve the constructor.
const Function& redirect_ctor = Function::ZoneHandle(
cls.LookupConstructor(ctor_name));
if (redirect_ctor.IsNull()) {
ErrorMsg(call_pos, "constructor '%s' not found", ctor_name.ToCString());
}
String& error_message = String::Handle();
if (!redirect_ctor.AreValidArguments(arguments->length(),
arguments->names(),
&error_message)) {
ErrorMsg(call_pos,
"invalid arguments passed to constructor '%s': %s",
ctor_name.ToCString(),
error_message.ToCString());
}
current_block_->statements->Add(
new StaticCallNode(call_pos, redirect_ctor, arguments));
}
SequenceNode* Parser::MakeImplicitConstructor(const Function& func) {
ASSERT(func.IsConstructor());
ASSERT(func.Owner() == current_class().raw());
const intptr_t ctor_pos = TokenPos();
OpenFunctionBlock(func);
LocalVariable* receiver = new LocalVariable(
Scanner::kNoSourcePos, Symbols::This(), *ReceiverType(current_class()));
current_block_->scope->AddVariable(receiver);
LocalVariable* phase_parameter =
new LocalVariable(Scanner::kNoSourcePos,
Symbols::PhaseParameter(),
Type::ZoneHandle(Type::SmiType()));
current_block_->scope->AddVariable(phase_parameter);
// 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()) {
ErrorMsg(ctor_pos,
"forwarding constructors must not have optional parameters");
}
// Prepare user-defined arguments to be forwarded to super call.
// The first user-defined argument is at position 2.
forwarding_args = new ArgumentListNode(Scanner::kNoSourcePos);
for (int i = 2; i < func.NumParameters(); i++) {
LocalVariable* param = new LocalVariable(
Scanner::kNoSourcePos,
String::ZoneHandle(func.ParameterNameAt(i)),
Type::ZoneHandle(Type::DynamicType()));
current_block_->scope->AddVariable(param);
forwarding_args->Add(new LoadLocalNode(Scanner::kNoSourcePos, param));
}
}
GenerateSuperConstructorCall(current_class(),
Scanner::kNoSourcePos,
receiver,
forwarding_args);
CheckFieldsInitialized(current_class());
// Empty constructor body.
current_block_->statements->Add(new ReturnNode(Scanner::kNoSourcePos));
SequenceNode* statements = CloseBlock();
return statements;
}
void Parser::CheckRecursiveInvocation() {
const GrowableObjectArray& pending_functions =
GrowableObjectArray::Handle(
isolate()->object_store()->pending_functions());
for (int i = 0; i < pending_functions.Length(); i++) {
if (pending_functions.At(i) == current_function().raw()) {
const String& fname =
String::Handle(current_function().UserVisibleName());
ErrorMsg("circular dependency for function %s", fname.ToCString());
}
}
ASSERT(!unregister_pending_function_);
pending_functions.Add(current_function());
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,
Array& default_parameter_values) {
TRACE_PARSER("ParseConstructor");
ASSERT(func.IsConstructor());
ASSERT(!func.IsFactory());
ASSERT(!func.is_static());
ASSERT(!func.IsLocalFunction());
const Class& cls = Class::Handle(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;
const bool allow_explicit_default_values = true;
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());
// Add implicit parameter for construction phase.
params.AddFinalParameter(
TokenPos(),
&Symbols::PhaseParameter(),
&Type::ZoneHandle(Type::SmiType()));
if (func.is_const()) {
params.SetImplicitlyFinal();
}
ParseFormalParameterList(allow_explicit_default_values, false, &params);
SetupDefaultsForOptionalParams(&params, default_parameter_values);
ASSERT(AbstractType::Handle(func.result_type()).IsResolved());
ASSERT(func.NumParameters() == params.parameters->length());
// 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) {
// First two parameters are implicit receiver and phase.
ASSERT(params.parameters->length() >= 2);
for (int i = 2; 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(cls.LookupInstanceField(field_name));
if (field.IsNull()) {
ErrorMsg(param.name_pos,
"unresolved reference to instance field '%s'",
field_name.ToCString());
}
if (is_redirecting_constructor) {
ErrorMsg(param.name_pos,
"redirecting constructors may not have "
"initializing formal parameters");
}
CheckDuplicateFieldInit(param.name_pos, &initialized_fields, &field);
if (!param.has_explicit_type) {
const AbstractType& field_type =
AbstractType::ZoneHandle(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);
ASSERT(p->is_invisible());
AstNode* value = new LoadLocalNode(param.name_pos, p);
EnsureExpressionTemp();
AstNode* initializer = new StoreInstanceFieldNode(
param.name_pos, instance, field, value);
current_block_->statements->Add(initializer);
}
}
}
if (is_redirecting_constructor) {
ParseConstructorRedirection(cls, receiver);
} else {
ParseInitializers(cls, receiver, &initialized_fields);
}
SequenceNode* init_statements = CloseBlock();
if (is_redirecting_constructor) {
// A redirecting super constructor simply passes the phase parameter on to
// the target which executes the corresponding phase.
current_block_->statements->Add(init_statements);
} else if (init_statements->length() > 0) {
// Generate guard around the initializer code.
LocalVariable* phase_param = LookupPhaseParameter();
AstNode* phase_value = new
LoadLocalNode(Scanner::kNoSourcePos, phase_param);
AstNode* phase_check = new BinaryOpNode(
Scanner::kNoSourcePos, Token::kBIT_AND, phase_value,
new LiteralNode(Scanner::kNoSourcePos,
Smi::ZoneHandle(Smi::New(Function::kCtorPhaseInit))));
AstNode* comparison =
new ComparisonNode(Scanner::kNoSourcePos,
Token::kNE_STRICT,
phase_check,
new LiteralNode(TokenPos(),
Smi::ZoneHandle(Smi::New(0))));
AstNode* guarded_init_statements =
new IfNode(Scanner::kNoSourcePos,
comparison,
init_statements,
NULL);
current_block_->statements->Add(guarded_init_statements);
}
// Parsing of initializers done. Now we parse the constructor body
// and add the implicit super call to the super constructor's body
// if necessary.
StaticCallNode* super_call = NULL;
// Look for the super initializer call in the sequence of initializer
// statements. If it exists and is not the last initializer statement,
// we need to create an implicit super call to the super constructor's
// body.
// Thus, iterate over all but the last initializer to see whether
// it's a super constructor call.
for (int i = 0; i < init_statements->length() - 1; i++) {
if (init_statements->NodeAt(i)->IsStaticCallNode()) {
StaticCallNode* static_call =
init_statements->NodeAt(i)->AsStaticCallNode();
if (static_call->function().IsConstructor()) {
super_call = static_call;
break;
}
}
}
if (super_call != NULL) {
// Generate an implicit call to the super constructor's body.
// We need to patch the super _initializer_ call so that it
// saves the evaluated actual arguments in temporary variables.
// The temporary variables are necessary so that the argument
// expressions are not evaluated twice.
// Note: we should never get here in the case of a redirecting
// constructor. In that case, the call to the target constructor
// is the "super call" and is implicitly at the end of the
// initializer list.
ASSERT(!is_redirecting_constructor);
ArgumentListNode* ctor_args = super_call->arguments();
// The super initializer call has at least 2 arguments: the
// implicit receiver, and the hidden construction phase.
ASSERT(ctor_args->length() >= 2);
for (int i = 2; i < ctor_args->length(); i++) {
AstNode* arg = ctor_args->NodeAt(i);
if (!IsSimpleLocalOrLiteralNode(arg)) {
LocalVariable* temp =
CreateTempConstVariable(arg->token_pos(), "sca");
AstNode* save_temp = new StoreLocalNode(arg->token_pos(), temp, arg);
ctor_args->SetNodeAt(i, save_temp);
}
}
}
OpenBlock(); // Block to collect constructor body nodes.
intptr_t body_pos = TokenPos();
// Insert the implicit super call to the super constructor body.
if (super_call != NULL) {
ArgumentListNode* initializer_args = super_call->arguments();
const Function& super_ctor = super_call->function();
// Patch the initializer call so it only executes the super initializer.
initializer_args->SetNodeAt(1,
new LiteralNode(body_pos,
Smi::ZoneHandle(Smi::New(Function::kCtorPhaseInit))));
ArgumentListNode* super_call_args = new ArgumentListNode(body_pos);
// First argument is the receiver.
super_call_args->Add(new LoadLocalNode(body_pos, receiver));
// Second argument is the construction phase argument.
AstNode* phase_parameter =
new LiteralNode(body_pos,
Smi::ZoneHandle(Smi::New(Function::kCtorPhaseBody)));
super_call_args->Add(phase_parameter);
super_call_args->set_names(initializer_args->names());
for (int i = 2; i < initializer_args->length(); i++) {
AstNode* arg = initializer_args->NodeAt(i);
if (arg->IsLiteralNode()) {
LiteralNode* lit = arg->AsLiteralNode();
super_call_args->Add(new LiteralNode(body_pos, lit->literal()));
} else {
ASSERT(arg->IsLoadLocalNode() || arg->IsStoreLocalNode());
if (arg->IsLoadLocalNode()) {
const LocalVariable& temp = arg->AsLoadLocalNode()->local();
super_call_args->Add(new LoadLocalNode(body_pos, &temp));
} else if (arg->IsStoreLocalNode()) {
const LocalVariable& temp = arg->AsStoreLocalNode()->local();
super_call_args->Add(new LoadLocalNode(body_pos, &temp));
}
}
}
ASSERT(super_ctor.AreValidArguments(super_call_args->length(),
super_call_args->names(),
NULL));
current_block_->statements->Add(
new StaticCallNode(body_pos, super_ctor, super_call_args));
}
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) {
ErrorMsg("constructors may not return a value");
} else if (IsLiteral("native")) {
ErrorMsg("native constructors not supported");
} else if (CurrentToken() == Token::kSEMICOLON) {
// Some constructors have no function body.
ConsumeToken();
} else {
UnexpectedToken();
}
SequenceNode* ctor_block = CloseBlock();
if (ctor_block->length() > 0) {
// Generate guard around the constructor body code.
LocalVariable* phase_param = LookupPhaseParameter();
AstNode* phase_value =
new LoadLocalNode(Scanner::kNoSourcePos, phase_param);
AstNode* phase_check =
new BinaryOpNode(Scanner::kNoSourcePos, Token::kBIT_AND,
phase_value,
new LiteralNode(Scanner::kNoSourcePos,
Smi::ZoneHandle(Smi::New(Function::kCtorPhaseBody))));
AstNode* comparison =
new ComparisonNode(Scanner::kNoSourcePos,
Token::kNE_STRICT,
phase_check,
new LiteralNode(body_pos,
Smi::ZoneHandle(Smi::New(0))));
AstNode* guarded_block_statements =
new IfNode(Scanner::kNoSourcePos, comparison, ctor_block, NULL);
current_block_->statements->Add(guarded_block_statements);
}
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,
Array& default_parameter_values) {
TRACE_PARSER("ParseFunc");
Function& saved_innermost_function =
Function::Handle(innermost_function().raw());
innermost_function_ = 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;
// TODO(12455) : Need better validation mechanism.
if (func.IsConstructor()) {
SequenceNode* statements = ParseConstructor(func, default_parameter_values);
innermost_function_ = saved_innermost_function.raw();
last_used_try_index_ = saved_try_index;
return statements;
}
ASSERT(!func.IsConstructor());
OpenFunctionBlock(func); // Build local scope for function.
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(),
&Type::ZoneHandle(Type::DynamicType()));
} 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 AbstractTypeArguments vector of
// the type of the instance to be allocated.
params.AddFinalParameter(
TokenPos(),
&Symbols::TypeArgumentsParameter(),
&Type::ZoneHandle(Type::DynamicType()));
}
ASSERT((CurrentToken() == Token::kLPAREN) || func.IsGetterFunction());
const bool allow_explicit_default_values = true;
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 {
ParseFormalParameterList(allow_explicit_default_values, false, &params);
// The number of parameters and their type are not yet set in local
// functions, since they are not 'top-level' parsed.
if (func.IsLocalFunction()) {
AddFormalParamsToFunction(&params, func);
}
SetupDefaultsForOptionalParams(&params, default_parameter_values);
ASSERT(AbstractType::Handle(func.result_type()).IsResolved());
ASSERT(func.NumParameters() == params.parameters->length());
// Check whether the function has any field initializer formal parameters,
// which are not allowed in non-constructor functions.
if (params.has_field_initializer) {
for (int i = 0; i < params.parameters->length(); i++) {
ParamDesc& param = (*params.parameters)[i];
if (param.is_field_initializer) {
ErrorMsg(param.name_pos,
"field initializer only allowed in constructors");
}
}
}
// Populate function scope with the formal parameters.
AddFormalParamsToScope(&params, current_block_->scope);
if (FLAG_enable_type_checks &&
(current_block_->scope->function_level() > 0)) {
// We are parsing, but not compiling, a local function.
// The instantiator may be required at run time for generic type checks.
if (IsInstantiatorRequired()) {
// 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.
CaptureInstantiator();
}
}
}
OpenBlock(); // Open a nested scope for the outermost function block.
intptr_t end_token_pos = 0;
if (CurrentToken() == Token::kLBRACE) {
ConsumeToken();
if (String::Handle(func.name()).Equals(Symbols::EqualOperator())) {
const Class& owner = Class::Handle(func.Owner());
if (!owner.IsObjectClass()) {
AddEqualityNullCheck();
}
}
ParseStatementSequence();
end_token_pos = TokenPos();
ExpectToken(Token::kRBRACE);
} else if (CurrentToken() == Token::kARROW) {
ConsumeToken();
if (String::Handle(func.name()).Equals(Symbols::EqualOperator())) {
const Class& owner = Class::Handle(func.Owner());
if (!owner.IsObjectClass()) {
AddEqualityNullCheck();
}
}
const intptr_t expr_pos = TokenPos();
AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades);
ASSERT(expr != NULL);
current_block_->statements->Add(new ReturnNode(expr_pos, expr));
end_token_pos = TokenPos();
} else if (IsLiteral("native")) {
if (String::Handle(func.name()).Equals(Symbols::EqualOperator())) {
const Class& owner = Class::Handle(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(func.name());
// TODO(regis): For an instance function, pass the receiver to
// NoSuchMethodError.
current_block_->statements->Add(
ThrowNoSuchMethodError(TokenPos(),
current_class(),
function_name,
NULL, // No arguments.
func.is_static() ?
InvocationMirror::kStatic :
InvocationMirror::kDynamic,
InvocationMirror::kMethod,
NULL)); // No existing 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();
current_block_->statements->Add(body);
innermost_function_ = saved_innermost_function.raw();
last_used_try_index_ = saved_try_index;
return CloseBlock();
}
void Parser::AddEqualityNullCheck() {
AstNode* argument =
new LoadLocalNode(Scanner::kNoSourcePos,
current_block_->scope->parent()->VariableAt(1));
LiteralNode* null_operand =
new LiteralNode(Scanner::kNoSourcePos, Instance::ZoneHandle());
ComparisonNode* check_arg =
new ComparisonNode(Scanner::kNoSourcePos,
Token::kEQ_STRICT,
argument,
null_operand);
ComparisonNode* result =
new ComparisonNode(Scanner::kNoSourcePos,
Token::kEQ_STRICT,
LoadReceiver(Scanner::kNoSourcePos),
null_operand);
SequenceNode* arg_is_null = new SequenceNode(Scanner::kNoSourcePos, NULL);
arg_is_null->Add(new ReturnNode(Scanner::kNoSourcePos, result));
IfNode* if_arg_null = new IfNode(Scanner::kNoSourcePos,
check_arg,
arg_is_null,
NULL);
current_block_->statements->Add(if_arg_null);
}
void Parser::SkipIf(Token::Kind token) {
if (CurrentToken() == token) {
ConsumeToken();
}
}
// Skips tokens up to matching closing parenthesis.
void Parser::SkipToMatchingParenthesis() {
ASSERT(CurrentToken() == Token::kLPAREN);
int level = 0;
do {
if (CurrentToken() == Token::kLPAREN) {
level++;
} else if (CurrentToken() == Token::kRPAREN) {
level--;
}
ConsumeToken();
} while ((level > 0) && (CurrentToken() != Token::kEOS));
}
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 {
SkipIf(Token::kTHIS);
SkipIf(Token::kPERIOD);
ExpectIdentifier("identifier expected");
ExpectToken(Token::kASSIGN);
SetAllowFunctionLiterals(false);
SkipExpr();
SetAllowFunctionLiterals(true);
}
} while (CurrentToken() == Token::kCOMMA);
}
void Parser::ParseQualIdent(QualIdent* qual_ident) {
TRACE_PARSER("ParseQualIdent");
ASSERT(IsIdentifier());
ASSERT(!current_class().IsNull());
qual_ident->ident_pos = TokenPos();
qual_ident->ident = CurrentLiteral();
qual_ident->lib_prefix = NULL;
ConsumeToken();
if (CurrentToken() == Token::kPERIOD) {
// An identifier cannot be resolved in a local scope when top level parsing.
if (is_top_level_ ||
!ResolveIdentInLocalScope(qual_ident->ident_pos,
*(qual_ident->ident),
NULL)) {
LibraryPrefix& lib_prefix = LibraryPrefix::ZoneHandle();
if (!current_class().IsMixinApplication()) {
lib_prefix = current_class().LookupLibraryPrefix(*(qual_ident->ident));
} else {
// TODO(hausner): Should we resolve the prefix via the library scope
// rather than via the class?
Class& cls = Class::Handle(parsed_function()->function().origin());
lib_prefix = cls.LookupLibraryPrefix(*(qual_ident->ident));
}
if (!lib_prefix.IsNull()) {
// We have a library prefix qualified identifier, unless the prefix is
// shadowed by a type parameter in scope.
if (current_class().IsNull() ||
(current_class().LookupTypeParameter(*(qual_ident->ident)) ==
TypeParameter::null())) {
ConsumeToken(); // Consume the kPERIOD token.
qual_ident->lib_prefix = &lib_prefix;
qual_ident->ident_pos = TokenPos();
qual_ident->ident =
ExpectIdentifier("identifier expected after '.'");
}
}
}
}
}
void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) {
TRACE_PARSER("ParseMethodOrConstructor");
ASSERT(CurrentToken() == Token::kLPAREN || method->IsGetter());
ASSERT(method->type != NULL);
ASSERT(method->name_pos > 0);
ASSERT(current_member_ == method);
if (method->has_var) {
ErrorMsg(method->name_pos, "keyword var not allowed for methods");
}
if (method->has_final) {
ErrorMsg(method->name_pos, "'final' not allowed for methods");
}
if (method->has_abstract && method->has_static) {
ErrorMsg(method->name_pos,
"static method '%s' cannot be abstract",
method->name->ToCString());
}
if (method->has_const && !method->IsFactoryOrConstructor()) {
ErrorMsg(method->name_pos, "'const' not allowed for methods");
}
if (method->has_abstract && method->IsFactoryOrConstructor()) {
ErrorMsg(method->name_pos, "constructor cannot be abstract");
}
if (method->has_const && method->IsConstructor()) {
current_class().set_is_const();
}
// Parse the formal parameters.
const bool are_implicitly_final = method->has_const;
const bool allow_explicit_default_values = true;
const intptr_t formal_param_pos = TokenPos();
method->params.Clear();
// Static functions do not have a receiver.
// The first parameter of a factory is the AbstractTypeArguments 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(),
&Type::ZoneHandle(Type::DynamicType()));
}
// Constructors have an implicit parameter for the construction phase.
if (method->IsConstructor()) {
method->params.AddFinalParameter(
TokenPos(),
&Symbols::PhaseParameter(),
&Type::ZoneHandle(Type::SmiType()));
}
if (are_implicitly_final) {
method->params.SetImplicitlyFinal();
}
if (!method->IsGetter()) {
ParseFormalParameterList(allow_explicit_default_values,
false,
&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::New(Token::Str(Token::kNEGATE));
}
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(Field::GetterSymbol(*method->name));
} else {
ASSERT(method->IsSetter());
expected_num_parameters = (method->has_static) ? 1 : 2;
method->dict_name =
&String::ZoneHandle(String::Concat(*method->name, Symbols::Equals()));
method->name = &String::ZoneHandle(Field::SetterSymbol(*method->name));
}
if ((method->params.num_fixed_parameters != expected_num_parameters) ||
(method->params.num_optional_parameters != 0)) {
ErrorMsg(method->name_pos, "illegal %s parameters",
method->IsGetter() ? "getter" : "setter");
}
}
// Parse redirecting factory constructor.
Type& redirection_type = Type::Handle();
String& redirection_identifier = String::Handle();
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) {
ErrorMsg("redirecting factory '%s' may not specify default values "
"for optional parameters",
method->name->ToCString());
}
ConsumeToken();
const intptr_t type_pos = TokenPos();
is_redirecting = true;
const AbstractType& type = AbstractType::Handle(
ParseType(ClassFinalizer::kResolveTypeParameters));
if (!type.IsMalformed() && type.IsTypeParameter()) {
// Replace the type with a malformed type and compile a throw when called.
redirection_type = ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(), // No previous error.
script_,
type_pos,
"factory '%s' may not redirect to type parameter '%s'",
method->name->ToCString(),
String::Handle(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()) {
ErrorMsg("initializers only allowed on constructors");
}
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.
ErrorMsg(formal_param_pos, "Redirecting constructor "
"may not use field initializer parameters");
}
ConsumeToken(); // Colon.
ExpectToken(Token::kTHIS);
String& redir_name = String::ZoneHandle(
String::Concat(members->class_name(), Symbols::Dot()));
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
redir_name = String::Concat(redir_name,
*ExpectIdentifier("constructor name expected"));
}
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());
intptr_t method_end_pos = TokenPos();
if ((CurrentToken() == Token::kLBRACE) ||
(CurrentToken() == Token::kARROW)) {
if (method->has_abstract) {
ErrorMsg(method->name_pos,
"abstract method '%s' may not have a function body",
method->name->ToCString());
} else if (method->has_external) {
ErrorMsg(method->name_pos,
"external method '%s' may not have a function body",
method->name->ToCString());
} else if (method->IsConstructor() && method->has_const) {
ErrorMsg(method->name_pos,
"const constructor '%s' may not have a function body",
method->name->ToCString());
} else if (method->IsFactory() && method->has_const) {
ErrorMsg(method->name_pos,
"const factory '%s' may not have a function body",
method->name->ToCString());
}
if (method->redirect_name != NULL) {
ErrorMsg(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 {
ConsumeToken();
SkipExpr();
method_end_pos = TokenPos();
ExpectSemicolon();
}
} else if (IsLiteral("native")) {
if (method->has_abstract) {
ErrorMsg(method->name_pos,
"abstract method '%s' may not have a function body",
method->name->ToCString());
} else if (method->IsConstructor() && method->has_const) {
ErrorMsg(method->name_pos,
"const constructor '%s' may not be native",
method->name->ToCString());
}
if (method->redirect_name != NULL) {
ErrorMsg(method->name_pos,
"Constructor with redirection may not have a function body");
}
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) {
ErrorMsg(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 {
ErrorMsg(method->name_pos,
"function body or semicolon expected for method '%s'",
method->name->ToCString());
}
}
}
RawFunction::Kind function_kind;
if (method->IsFactoryOrConstructor()) {
function_kind = RawFunction::kConstructor;
} else if (method->IsGetter()) {
function_kind = RawFunction::kGetterFunction;
} else if (method->IsSetter()) {
function_kind = RawFunction::kSetterFunction;
} else {
function_kind = RawFunction::kRegularFunction;
}
Function& func = Function::Handle(
Function::New(*method->name,
function_kind,
method->has_static,
method->has_const,
method->has_abstract,
method->has_external,
method->has_native,
current_class(),
method->decl_begin_pos));
func.set_result_type(*method->type);
func.set_end_token_pos(method_end_pos);
func.set_is_redirecting(is_redirecting);
if (method->metadata_pos > 0) {
library_.AddFunctionMetadata(func, method->metadata_pos);
}
// 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);
}
}
// No need to resolve parameter types yet, or add parameters to local scope.
ASSERT(is_top_level_);
AddFormalParamsToFunction(&method->params, 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 > 0);
ASSERT(current_member_ == field);
// All const fields are also final.
ASSERT(!field->has_const || field->has_final);
if (field->has_abstract) {
ErrorMsg("keyword 'abstract' not allowed in field declaration");
}
if (field->has_external) {
ErrorMsg("keyword 'external' not allowed in field declaration");
}
if (field->has_factory) {
ErrorMsg("keyword 'factory' not allowed in field declaration");
}
if (!field->has_static && field->has_const) {
ErrorMsg(field->name_pos, "instance field may not be 'const'");
}
Function& getter = Function::Handle();
Function& setter = Function::Handle();
Field& class_field = Field::ZoneHandle();
Instance& init_value = Instance::Handle();
while (true) {
bool has_initializer = CurrentToken() == Token::kASSIGN;
bool has_simple_literal = false;
if (has_initializer) {
ConsumeToken();
init_value = Object::sentinel().raw();
// For static const fields and static final non-const 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 const fields and static final non-const 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 (field->has_static && (field->has_const || field->has_final) &&
(LookaheadToken(1) == Token::kSEMICOLON)) {
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) {
ErrorMsg(field->name_pos,
"static %s field '%s' must have an initializer expression",
field->has_const ? "const" : "final",
field->name->ToCString());
}
}
// Create the field object.
class_field = Field::New(*field->name,
field->has_static,
field->has_final,
field->has_const,
current_class(),
field->name_pos);
class_field.set_type(*field->type);
class_field.set_has_initializer(has_initializer);
members->AddField(class_field);
field->field_ = &class_field;
if (field->metadata_pos >= 0) {
library_.AddFieldMetadata(class_field, field->metadata_pos);
}
// 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.set_value(init_value);
if (!has_simple_literal) {
String& getter_name = String::Handle(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);
members->AddFunction(getter);
// Create initializer function for non-const fields.
if (!class_field.is_const()) {
const Function& init_function = Function::ZoneHandle(
Function::NewStaticInitializer(class_field));
members->AddFunction(init_function);
}
}
}
// For instance fields, we create implicit getter and setter methods.
if (!field->has_static) {
String& getter_name = String::Handle(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);
AddFormalParamsToFunction(&params, getter);
members->AddFunction(getter);
if (!field->has_final) {
// Build a setter accessor for non-const fields.
String& setter_name = String::Handle(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(Type::Handle(Type::VoidType()));
AddFormalParamsToFunction(&params, 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.
ErrorMsg(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())) {
ErrorMsg(member->name_pos,
"%s '%s' conflicts with class name",
member->ToCString(),
name.ToCString());
}
if (members->clazz().LookupTypeParameter(name) != TypeParameter::null()) {
ErrorMsg(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())) {
ErrorMsg(member->name_pos,
"%s '%s' conflicts with previously declared %s",
member->ToCString(),
name.ToCString(),
existing_member->ToCString());
}
}
}
void Parser::ParseClassMemberDefinition(ClassDesc* members,
intptr_t 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::kCONST) {
ConsumeToken();
member.has_const = true;
} else if (CurrentToken() == Token::kFINAL) {
ConsumeToken();
member.has_final = true;
}
if (CurrentToken() == Token::kVAR) {
if (member.has_const) {
ErrorMsg("identifier expected after 'const'");
}
if (member.has_final) {
ErrorMsg("identifier expected after 'final'");
}
ConsumeToken();
member.has_var = true;
// The member type is the 'dynamic' type.
member.type = &Type::ZoneHandle(Type::DynamicType());
} else if (CurrentToken() == Token::kFACTORY) {
ConsumeToken();
if (member.has_static) {
ErrorMsg("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.
if (CurrentToken() == Token::kVOID) {
if (member.has_var || member.has_factory) {
ErrorMsg("void not expected");
}
ConsumeToken();
ASSERT(member.type == NULL);
member.type = &Type::ZoneHandle(Type::VoidType());
} else if (CurrentToken() == Token::kIDENT) {
// This is either a type name or the name of a method/constructor/field.
if ((member.type == NULL) && !member.has_factory) {
// We have not seen a member type yet, so we check if the next
// identifier could represent a type before parsing it.
Token::Kind follower = LookaheadToken(1);
// We have an identifier followed by a 'follower' token.
// We either parse a type or assume that no type is specified.
if ((follower == Token::kLT) || // Parameterized type.
(follower == Token::kGET) || // Getter following a type.
(follower == Token::kSET) || // Setter following a type.
(follower == Token::kOPERATOR) || // Operator following a type.
(Token::IsIdentifier(follower)) || // Member name following a type.
((follower == Token::kPERIOD) && // Qualified class name of type,
(LookaheadToken(3) != Token::kLPAREN))) { // but not a named constr.
ASSERT(is_top_level_);
// The declared type of fields is never ignored, even in unchecked mode,
// because getters and setters could be closurized at some time (not
// supported yet).
member.type = &AbstractType::ZoneHandle(
ParseType(ClassFinalizer::kResolveTypeParameters));
}
}
}
// 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())) {
ErrorMsg(member.name_pos, "factory name must be '%s'",
members->class_name().ToCString());
}
} else if (member.has_static) {
ErrorMsg(member.name_pos, "constructor cannot be static");
}
if (member.type != NULL) {
ErrorMsg(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(
UnresolvedClass::New(LibraryPrefix::Handle(),
*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(Type::New(
result_type_class,
TypeArguments::Handle(current_class().type_parameters()),
member.name_pos));
// We must be dealing with a constructor or named constructor.
member.kind = RawFunction::kConstructor;
*member.name = String::Concat(*member.name, Symbols::Dot());
if (CurrentToken() == Token::kPERIOD) {
// Named constructor.
ConsumeToken();
member.dict_name = ExpectIdentifier("identifier expected");
*member.name = String::Concat(*member.name, *member.dict_name);
}
// Ensure that names are symbols.
*member.name = Symbols::New(*member.name);
CheckToken(Token::kLPAREN);
} else if ((CurrentToken() == Token::kGET) && !member.has_var &&
(LookaheadToken(1) != Token::kLPAREN) &&
(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::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 = &Type::ZoneHandle(Type::DynamicType());
}
} else if ((CurrentToken() == Token::kOPERATOR) && !member.has_var &&
(LookaheadToken(1) != Token::kLPAREN) &&
(LookaheadToken(1) != Token::kASSIGN) &&
(LookaheadToken(1) != Token::kCOMMA) &&
(LookaheadToken(1) != Token::kSEMICOLON)) {
ConsumeToken();
if (!Token::CanBeOverloaded(CurrentToken())) {
ErrorMsg("invalid operator overloading");
}
if (member.has_static) {
ErrorMsg("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(Symbols::New(Token::Str(member.operator_token)));
ConsumeToken();
} else if (IsIdentifier()) {
member.name = CurrentLiteral();
member.name_pos = TokenPos();
ConsumeToken();
} else {
ErrorMsg("identifier expected");
}
ASSERT(member.name != NULL);
if (CurrentToken() == Token::kLPAREN || member.IsGetter()) {
// Constructor or method.
if (member.type == NULL) {
member.type = &Type::ZoneHandle(Type::DynamicType());
}
ASSERT(member.IsFactory() == member.has_factory);
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 = &Type::ZoneHandle(Type::DynamicType());
} else {
ErrorMsg("missing 'var', 'final', 'const' or type"
" in field declaration");
}
}
ParseFieldDefinition(members, &member);
} else {
UnexpectedToken();
}
current_member_ = NULL;
CheckMemberNameConflict(members, &member);
members->AddMember(member);
}
void Parser::ParseClassDeclaration(const GrowableObjectArray& pending_classes,
intptr_t metadata_pos) {
TRACE_PARSER("ParseClassDeclaration");
bool is_patch = false;
bool is_abstract = false;
if (is_patch_source() &&
(CurrentToken() == Token::kIDENT) &&
CurrentLiteral()->Equals("patch")) {
ConsumeToken();
is_patch = true;
} else if (CurrentToken() == Token::kABSTRACT) {
is_abstract = true;
ConsumeToken();
}
ExpectToken(Token::kCLASS);
const intptr_t 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();
TypeArguments& orig_type_parameters = TypeArguments::Handle();
Object& obj = Object::Handle(library_.LookupLocalObject(class_name));
if (obj.IsNull()) {
if (is_patch) {
ErrorMsg(classname_pos, "missing class '%s' cannot be patched",
class_name.ToCString());
}
cls = Class::New(class_name, script_, classname_pos);
library_.AddClass(cls);
} else {
if (!obj.IsClass()) {
ErrorMsg(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();
// A patch class must be given the same name as the class it is patching,
// otherwise the generic signature classes it defines will not match the
// patched generic signature classes. Therefore, new signature classes
// will be introduced and the original ones will not get finalized.
cls = Class::New(class_name, script_, classname_pos);
cls.set_library(library_);
} 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() ||
RawObject::IsTypedDataViewClassId(cls.id()))) {
ErrorMsg(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(classname_pos);
}
}
ASSERT(!cls.IsNull());
ASSERT(cls.functions() == Object::empty_array().raw());
set_current_class(cls);
ParseTypeParameters(cls);
if (is_patch) {
// Check that the new type parameters are identical to the original ones.
const TypeArguments& new_type_parameters =
TypeArguments::Handle(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) {
ErrorMsg(classname_pos,
"class '%s' must be patched with identical type parameters",
class_name.ToCString());
}
TypeParameter& new_type_param = TypeParameter::Handle();
TypeParameter& orig_type_param = TypeParameter::Handle();
String& new_name = String::Handle();
String& orig_name = String::Handle();
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)) {
ErrorMsg(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());
}
// We do not check that the bounds are repeated. We use the original ones.
// TODO(regis): Should we check?
}
cls.set_type_parameters(orig_type_parameters);
}
if (is_abstract) {
cls.set_is_abstract();
}
if (metadata_pos >= 0) {
library_.AddClassMetadata(cls, metadata_pos);
}
const bool is_mixin_declaration = (CurrentToken() == Token::kASSIGN);
if (is_mixin_declaration && is_patch) {
ErrorMsg(classname_pos,
"mixin application '%s' may not be a patch class",
class_name.ToCString());
}
AbstractType& super_type = Type::Handle();
if ((CurrentToken() == Token::kEXTENDS) || is_mixin_declaration) {
ConsumeToken(); // extends or =
const intptr_t type_pos = TokenPos();
super_type = ParseType(ClassFinalizer::kResolveTypeParameters);
if (super_type.IsMalformedOrMalbounded()) {
ErrorMsg(Error::Handle(super_type.error()));
}
if (super_type.IsDynamicType()) {
// Unlikely here, since super type is not resolved yet.
ErrorMsg(type_pos,
"class '%s' may not extend 'dynamic'",
class_name.ToCString());
}
if (super_type.IsTypeParameter()) {
ErrorMsg(type_pos,
"class '%s' may not extend type parameter '%s'",
class_name.ToCString(),
String::Handle(super_type.UserVisibleName()).ToCString());
}
// The class finalizer will check whether the super type is malbounded.
if (is_mixin_declaration) {
if (CurrentToken() != Token::kWITH) {
ErrorMsg("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) {
// Apply the changes to the patched class looked up above.
ASSERT(obj.raw() == library_.LookupLocalObject(class_name));
// The patched class must not be finalized yet.
const Class& orig_class = Class::Cast(obj);
ASSERT(!orig_class.is_finalized());
orig_class.set_patch_class(cls);
cls.set_is_patch();
}
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");
set_current_class(cls);
is_top_level_ = true;
String& class_name = String::Handle(cls.Name());
const intptr_t class_pos = TokenPos();
ClassDesc members(cls, class_name, false, class_pos);
while (CurrentToken() != Token::kLBRACE) {
ConsumeToken();
}
ExpectToken(Token::kLBRACE);
while (CurrentToken() != Token::kRBRACE) {
intptr_t metadata_pos = SkipMetadata();
ParseClassMemberDefinition(&members, metadata_pos);
}
ExpectToken(Token::kRBRACE);
CheckConstructors(&members);
// Need to compute this here since MakeArray() will clear the
// functions array in members.
const bool need_implicit_constructor =
!members.has_constructor() && !cls.is_patch();
Array& array = Array::Handle();
array = Array::MakeArray(members.fields());
cls.SetFields(array);
// Creating a new array for functions marks the class as parsed.
array = Array::MakeArray(members.functions());
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(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();
if (!orig_class.ApplyPatch(cls, &error)) {
AppendErrorMsg(error, class_pos, "applying patch failed");
}
}
}
// 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(cls.Name());
ctor_name = String::Concat(ctor_name, Symbols::Dot());
ctor_name = Symbols::New(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(
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());
ParamList params;
// Add implicit 'this' parameter.
const AbstractType* receiver_type = ReceiverType(cls);
params.AddReceiver(receiver_type, cls.token_pos());
// Add implicit parameter for construction phase.
params.AddFinalParameter(cls.token_pos(),
&Symbols::PhaseParameter(),
&Type::ZoneHandle(Type::SmiType()));
AddFormalParamsToFunction(&params, 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);
}
// 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->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) {
ErrorMsg(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,
intptr_t metadata_pos) {
TRACE_PARSER("ParseMixinAppAlias");
const intptr_t 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(library_.LookupLocalObject(class_name));
if (!obj.IsNull()) {
ErrorMsg(classname_pos, "'%s' is already defined",
class_name.ToCString());
}
const Class& mixin_application =
Class::Handle(Class::New(class_name, script_, classname_pos));
mixin_application.set_is_mixin_app_alias();
library_.AddClass(mixin_application);
set_current_class(mixin_application);
ParseTypeParameters(mixin_application);
ExpectToken(Token::kASSIGN);
if (CurrentToken() == Token::kABSTRACT) {
mixin_application.set_is_abstract();
ConsumeToken();
}
const intptr_t type_pos = TokenPos();
AbstractType& type =
AbstractType::Handle(ParseType(ClassFinalizer::kResolveTypeParameters));
if (type.IsTypeParameter()) {
ErrorMsg(type_pos,
"class '%s' may not extend type parameter '%s'",
class_name.ToCString(),
String::Handle(type.UserVisibleName()).ToCString());
}
CheckToken(Token::kWITH, "mixin application 'with Type' expected");
type = ParseMixins(type);
mixin_application.set_super_type(type);
mixin_application.set_is_synthesized_class();
// This mixin application alias needs an implicit constructor, but it is
// too early to call 'AddImplicitConstructor(mixin_application)' here,
// because this class should be lazily compiled.
if (CurrentToken() == Token::kIMPLEMENTS) {
ParseInterfaceList(mixin_application);
}
ExpectSemicolon();
pending_classes.Add(mixin_application, Heap::kOld);
if (metadata_pos >= 0) {
library_.AddClassMetadata(mixin_application, 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() {
if (IsIdentifier() && (LookaheadToken(1) == Token::kLPAREN)) {
return true;
}
const intptr_t saved_pos = TokenPos();
bool is_alias_name = false;
if (IsIdentifier() && (LookaheadToken(1) == Token::kLT)) {
ConsumeToken();
if (TryParseTypeParameters() && (CurrentToken() == Token::kLPAREN)) {
is_alias_name = true;
}
}
SetPosition(saved_pos);
return is_alias_name;
}
// Look ahead to detect if we are seeing ident [ TypeParameters ] "=".
// Token position remains unchanged.
bool Parser::IsMixinAppAlias() {
if (IsIdentifier() && (LookaheadToken(1) == Token::kASSIGN)) {
return true;
}
const intptr_t saved_pos = TokenPos();
bool is_mixin_def = false;
if (IsIdentifier() && (LookaheadToken(1) == Token::kLT)) {
ConsumeToken();
if (TryParseTypeParameters() && (CurrentToken() == Token::kASSIGN)) {
is_mixin_def = true;
}
}
SetPosition(saved_pos);
return is_mixin_def;
}
void Parser::ParseTypedef(const GrowableObjectArray& pending_classes,
intptr_t metadata_pos) {
TRACE_PARSER("ParseTypedef");
ExpectToken(Token::kTYPEDEF);
if (IsMixinAppAlias()) {
if (FLAG_warn_mixin_typedef) {
Warning("deprecated mixin application typedef");
}
ParseMixinAppAlias(pending_classes, metadata_pos);
return;
}
// Parse the result type of the function type.
AbstractType& result_type = Type::Handle(Type::DynamicType());
if (CurrentToken() == Token::kVOID) {
ConsumeToken();
result_type = Type::VoidType();
} else if (!IsFunctionTypeAliasName()) {
// 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);
}
const intptr_t 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(library_.LookupLocalObject(*alias_name));
if (!obj.IsNull()) {
ErrorMsg(alias_name_pos,
"'%s' is already defined", alias_name->ToCString());
}
// Create the function type alias signature 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 signature class as
// they are parsed.
const Class& function_type_alias = Class::Handle(
Class::NewSignatureClass(*alias_name,
Function::Handle(),
script_,
alias_name_pos));
library_.AddClass(function_type_alias);
set_current_class(function_type_alias);
// Parse the type parameters of the function type.
ParseTypeParameters(function_type_alias);
// At this point, the type parameters have been parsed, so we can resolve the
// result type.
if (!result_type.IsNull()) {
ResolveTypeFromClass(function_type_alias,
ClassFinalizer::kResolveTypeParameters,
&result_type);
}
// Parse the formal parameters of the function type.
CheckToken(Token::kLPAREN, "formal parameter list expected");
ParamList func_params;
// Add implicit closure object parameter.
func_params.AddFinalParameter(
TokenPos(),
&Symbols::ClosureParameter(),
&Type::ZoneHandle(Type::DynamicType()));
const bool no_explicit_default_values = false;
ParseFormalParameterList(no_explicit_default_values, false, &func_params);
ExpectSemicolon();
// The field 'is_static' has no meaning for signature functions.
Function& signature_function = Function::Handle(
Function::New(*alias_name,
RawFunction::kSignatureFunction,
/* is_static = */ false,
/* is_const = */ false,
/* is_abstract = */ false,
/* is_external = */ false,
/* is_native = */ false,
function_type_alias,
alias_name_pos));
signature_function.set_result_type(result_type);
AddFormalParamsToFunction(&func_params, signature_function);
// Patch the signature function in the signature class.
function_type_alias.PatchSignatureFunction(signature_function);
const String& signature = String::Handle(signature_function.Signature());
if (FLAG_trace_parser) {
OS::Print("TopLevel parsing function type alias '%s'\n",
signature.ToCString());
}
// Lookup the signature class, i.e. the class whose name is the signature.
// We only lookup in the current library, but not in its imports, and only
// create a new canonical signature class if it does not exist yet.
Class& signature_class = Class::ZoneHandle(
library_.LookupLocalClass(signature));
if (signature_class.IsNull()) {
signature_class = Class::NewSignatureClass(signature,
signature_function,
script_,
alias_name_pos);
// Record the function signature class in the current library.
library_.AddClass(signature_class);
} else {
// Forget the just created signature function and use the existing one.
signature_function = signature_class.signature_function();
function_type_alias.PatchSignatureFunction(signature_function);
}
ASSERT(signature_function.signature_class() == signature_class.raw());
// 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.IsCanonicalSignatureClass());
ASSERT(!function_type_alias.is_finalized());
pending_classes.Add(function_type_alias, Heap::kOld);
if (metadata_pos >= 0) {
library_.AddClassMetadata(function_type_alias, 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 or triple
// bracket, it is replaced by a single or double 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();
}
}
intptr_t Parser::SkipMetadata() {
if (CurrentToken() != Token::kAT) {
return -1;
}
intptr_t 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();
SkipType(false);
} while (CurrentToken() == Token::kCOMMA);
Token::Kind token = CurrentToken();
if ((token == Token::kGT) || (token == Token::kSHR)) {
ConsumeRightAngleBracket();
} else {
ErrorMsg("right angle bracket expected");
}
}
}
void Parser::SkipType(bool allow_void) {
if (CurrentToken() == Token::kVOID) {
if (!allow_void) {
ErrorMsg("'void' not allowed here");
}
ConsumeToken();
} else {
ExpectIdentifier("type name expected");
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ExpectIdentifier("name expected");
}
SkipTypeArguments();
}
}
void Parser::ParseTypeParameters(const Class& cls) {
TRACE_PARSER("ParseTypeParameters");
if (CurrentToken() == Token::kLT) {
const GrowableObjectArray& type_parameters_array =
GrowableObjectArray::Handle(GrowableObjectArray::New());
intptr_t index = 0;
TypeParameter& type_parameter = TypeParameter::Handle();
TypeParameter& existing_type_parameter = TypeParameter::Handle();
String& existing_type_parameter_name = String::Handle();
AbstractType& type_parameter_bound = Type::Handle();
do {
ConsumeToken();
const intptr_t metadata_pos = SkipMetadata();
const intptr_t type_parameter_pos = TokenPos();
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);
existing_type_parameter_name = existing_type_parameter.name();
if (existing_type_parameter_name.Equals(type_parameter_name)) {
ErrorMsg(type_parameter_pos, "duplicate type parameter '%s'",
type_parameter_name.ToCString());
}
}
if (CurrentToken() == Token::kEXTENDS) {
ConsumeToken();
// A bound may refer to the owner of the type parameter it applies to,
// i.e. to the class or interface currently being parsed.
// Postpone resolution in order to avoid resolving the class and its
// type parameters, as they are not fully parsed yet.
type_parameter_bound = ParseType(ClassFinalizer::kDoNotResolve);
} else {
type_parameter_bound = isolate()->object_store()->object_type();
}
type_parameter = TypeParameter::New(cls,
index,
type_parameter_name,
type_parameter_bound,
type_parameter_pos);
type_parameters_array.Add(type_parameter);
if (metadata_pos >= 0) {
library_.AddTypeParameterMetadata(type_parameter, metadata_pos);
}
index++;
} while (CurrentToken() == Token::kCOMMA);
Token::Kind token = CurrentToken();
if ((token == Token::kGT) || (token == Token::kSHR)) {
ConsumeRightAngleBracket();
} else {
ErrorMsg("right angle bracket expected");
}
const TypeArguments& type_parameters =
TypeArguments::Handle(NewTypeArguments(type_parameters_array));
cls.set_type_parameters(type_parameters);
// Try to resolve the upper bounds, which will at least resolve the
// referenced type parameters.
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();
ResolveTypeFromClass(cls,
ClassFinalizer::kResolveTypeParameters,
&type_parameter_bound);
type_parameter.set_bound(type_parameter_bound);
}
}
}
RawAbstractTypeArguments* Parser::ParseTypeArguments(
ClassFinalizer::FinalizationKind finalization) {
TRACE_PARSER("ParseTypeArguments");
if (CurrentToken() == Token::kLT) {
const GrowableObjectArray& types =
GrowableObjectArray::Handle(GrowableObjectArray::New());
AbstractType& type = AbstractType::Handle();
do {
ConsumeToken();
type = ParseType(finalization);
// Map a malformed type argument to dynamic.
if (type.IsMalformed()) {
type = Type::DynamicType();
}
types.Add(type);
} while (CurrentToken() == Token::kCOMMA);
Token::Kind token = CurrentToken();
if ((token == Token::kGT) || (token == Token::kSHR)) {
ConsumeRightAngleBracket();
} else {
ErrorMsg("right angle bracket expected");
}
if (finalization != ClassFinalizer::kIgnore) {
return NewTypeArguments(types);
}
}
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(GrowableObjectArray::New());
AbstractType& interface = AbstractType::Handle();
// First get all the interfaces already implemented by class.
Array& cls_interfaces = Array::Handle(cls.interfaces());
for (intptr_t i = 0; i < cls_interfaces.Length(); i++) {
interface ^= cls_interfaces.At(i);
all_interfaces.Add(interface);
}
// Now parse and add the new interfaces.
do {
ConsumeToken();
intptr_t interface_pos = TokenPos();
interface = ParseType(ClassFinalizer::kResolveTypeParameters);
if (interface.IsTypeParameter()) {
ErrorMsg(interface_pos,
"type parameter '%s' may not be used in interface list",
String::Handle(interface.UserVisibleName()).ToCString());
}
all_interfaces.Add(interface);
} while (CurrentToken() == Token::kCOMMA);
cls_interfaces = Array::MakeArray(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(GrowableObjectArray::New());
AbstractType& mixin_type = AbstractType::Handle();
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.
ErrorMsg(mixin_type.token_pos(), "illegal mixin of a malformed type");
}
if (mixin_type.IsTypeParameter()) {
ErrorMsg(mixin_type.token_pos(),
"mixin type '%s' may not be a type parameter",
String::Handle(mixin_type.UserVisibleName()).ToCString());
}
mixin_types.Add(mixin_type);
} while (CurrentToken() == Token::kCOMMA);
return MixinAppType::New(super_type,
Array::Handle(Array::MakeArray(mixin_types)));
}
void Parser::ParseTopLevelVariable(TopLevel* top_level,
intptr_t 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(ParseConstFinalVarOrType(
ClassFinalizer::kResolveTypeParameters));
Field& field = Field::Handle();
Function& getter = Function::Handle();
while (true) {
const intptr_t name_pos = TokenPos();
String& var_name = *ExpectIdentifier("variable name expected");
if (library_.LookupLocalObject(var_name) != Object::null()) {
ErrorMsg(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(Field::GetterName(var_name));
if (library_.LookupLocalObject(accessor_name) != Object::null()) {
ErrorMsg(name_pos, "getter for '%s' is already defined",
var_name.ToCString());
}
accessor_name = Field::SetterName(var_name);
if (library_.LookupLocalObject(accessor_name) != Object::null()) {
ErrorMsg(name_pos, "setter for '%s' is already defined",
var_name.ToCString());
}
field = Field::New(var_name, is_static, is_final, is_const,
current_class(), name_pos);
field.set_type(type);
field.set_value(Instance::Handle(Instance::null()));
top_level->fields.Add(field);
library_.AddObject(field, var_name);
if (metadata_pos >= 0) {
library_.AddFieldMetadata(field, metadata_pos);
}
if (CurrentToken() == Token::kASSIGN) {
ConsumeToken();
Instance& field_value = Instance::Handle(Object::sentinel().raw());
bool has_simple_literal = false;
if ((is_const || is_final) && (LookaheadToken(1) == Token::kSEMICOLON)) {
has_simple_literal = IsSimpleLiteral(type, &field_value);
}
SkipExpr();
field.set_value(field_value);
if (!has_simple_literal) {
// Create a static final getter.
String& getter_name = String::Handle(Field::GetterSymbol(var_name));
getter = Function::New(getter_name,
RawFunction::kImplicitStaticFinalGetter,
is_static,
is_const,
/* is_abstract = */ false,
/* is_external = */ false,
/* is_native = */ false,
current_class(),
name_pos);
getter.set_result_type(type);
top_level->functions.Add(getter);
// Create initializer function.
if (!field.is_const()) {
const Function& init_function = Function::ZoneHandle(
Function::NewStaticInitializer(field));
top_level->functions.Add(init_function);
}
}
} else if (is_final) {
ErrorMsg(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.
}
}
}
void Parser::ParseTopLevelFunction(TopLevel* top_level,
intptr_t metadata_pos) {
TRACE_PARSER("ParseTopLevelFunction");
const intptr_t decl_begin_pos = TokenPos();
AbstractType& result_type = Type::Handle(Type::DynamicType());
const bool is_static = true;
bool is_external = false;
bool is_patch = false;
if (is_patch_source() &&
(CurrentToken() == Token::kIDENT) &&
CurrentLiteral()->Equals("patch") &&
(LookaheadToken(1) != Token::kLPAREN)) {
ConsumeToken();
is_patch = true;
} else if (CurrentToken() == Token::kEXTERNAL) {
ConsumeToken();
is_external = true;
}
if (CurrentToken() == Token::kVOID) {
ConsumeToken();
result_type = Type::VoidType();
} else {
// Parse optional type.
if ((CurrentToken() == Token::kIDENT) &&
(LookaheadToken(1) != Token::kLPAREN)) {
result_type = ParseType(ClassFinalizer::kResolveTypeParameters);
}
}
const intptr_t name_pos = TokenPos();
const String& func_name = *ExpectIdentifier("function name expected");
bool found = library_.LookupLocalObject(func_name) != Object::null();
if (found && !is_patch) {
ErrorMsg(name_pos, "'%s' is already defined", func_name.ToCString());
} else if (!found && is_patch) {
ErrorMsg(name_pos, "missing '%s' cannot be patched", func_name.ToCString());
}
String& accessor_name = String::Handle(Field::GetterName(func_name));
if (library_.LookupLocalObject(accessor_name) != Object::null()) {
ErrorMsg(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.
CheckToken(Token::kLPAREN);
const intptr_t function_pos = TokenPos();
ParamList params;
const bool allow_explicit_default_values = true;
ParseFormalParameterList(allow_explicit_default_values, false, &params);
intptr_t function_end_pos = function_pos;
bool is_native = false;
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) {
ConsumeToken();
SkipExpr();
function_end_pos = TokenPos();
ExpectSemicolon();
} else if (IsLiteral("native")) {
ParseNativeDeclaration();
function_end_pos = TokenPos();
ExpectSemicolon();
is_native = true;
} else {
ErrorMsg("function block expected");
}
Function& func = Function::Handle(
Function::New(func_name,
RawFunction::kRegularFunction,
is_static,
/* is_const = */ false,
/* is_abstract = */ false,
is_external,
is_native,
current_class(),
decl_begin_pos));
func.set_result_type(result_type);
func.set_end_token_pos(function_end_pos);
AddFormalParamsToFunction(&params, func);
top_level->functions.Add(func);
if (!is_patch) {
library_.AddObject(func, func_name);
} else {
library_.ReplaceObject(func, func_name);
}
if (metadata_pos >= 0) {
library_.AddFunctionMetadata(func, metadata_pos);
}
}
void Parser::ParseTopLevelAccessor(TopLevel* top_level,
intptr_t metadata_pos) {
TRACE_PARSER("ParseTopLevelAccessor");
const intptr_t decl_begin_pos = TokenPos();
const bool is_static = true;
bool is_external = false;
bool is_patch = false;
AbstractType& result_type = AbstractType::Handle();
if (is_patch_source() &&
(CurrentToken() == Token::kIDENT) &&
CurrentLiteral()->Equals("patch")) {
ConsumeToken();
is_patch = true;
} 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 {
if (CurrentToken() == Token::kVOID) {
ConsumeToken();
result_type = Type::VoidType();
} else {
result_type = ParseType(ClassFinalizer::kResolveTypeParameters);
}
is_getter = (CurrentToken() == Token::kGET);
if (CurrentToken() == Token::kGET || CurrentToken() == Token::kSET) {
ConsumeToken();
} else {
UnexpectedToken();
}
}
const intptr_t name_pos = TokenPos();
const String* field_name = ExpectIdentifier("accessor name expected");
const intptr_t accessor_pos = TokenPos();
ParamList params;
if (!is_getter) {
const bool allow_explicit_default_values = true;
ParseFormalParameterList(allow_explicit_default_values, false, &params);
}
String& accessor_name = String::ZoneHandle();
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)) {
ErrorMsg(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())) {
ErrorMsg(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())) {
ErrorMsg(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) {
ErrorMsg(name_pos, "%s for '%s' is already defined",
is_getter ? "getter" : "setter",
field_name->ToCString());
} else if (!found && is_patch) {
ErrorMsg(name_pos, "missing %s for '%s' cannot be patched",
is_getter ? "getter" : "setter",
field_name->ToCString());
}
intptr_t accessor_end_pos = accessor_pos;
bool is_native = false;
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) {
ConsumeToken();
SkipExpr();
accessor_end_pos = TokenPos();
ExpectSemicolon();
} else if (IsLiteral("native")) {
ParseNativeDeclaration();
accessor_end_pos = TokenPos();
ExpectSemicolon();
is_native = true;
} else {
ErrorMsg("function block expected");
}
Function& func = Function::Handle(
Function::New(accessor_name,
is_getter ? RawFunction::kGetterFunction :
RawFunction::kSetterFunction,
is_static,
/* is_const = */ false,
/* is_abstract = */ false,
is_external,
is_native,
current_class(),
decl_begin_pos));
func.set_result_type(result_type);
func.set_end_token_pos(accessor_end_pos);
AddFormalParamsToFunction(&params, func);
top_level->functions.Add(func);
if (!is_patch) {
library_.AddObject(func, accessor_name);
} else {
library_.ReplaceObject(func, accessor_name);
}
if (metadata_pos >= 0) {
library_.AddFunctionMetadata(func, metadata_pos);
}
}
class DartApiScope : public StackResource {
public:
explicit DartApiScope(Isolate* isolate) : StackResource(isolate) {
Dart_EnterScope();
}
~DartApiScope() {
Dart_ExitScope();
}
private:
DISALLOW_COPY_AND_ASSIGN(DartApiScope);
};
RawObject* Parser::CallLibraryTagHandler(Dart_LibraryTag tag,
intptr_t token_pos,
const String& url) {
Dart_LibraryTagHandler handler = isolate()->library_tag_handler();
if (handler == NULL) {
if (url.StartsWith(Symbols::DartScheme())) {
if (tag == Dart_kCanonicalizeUrl) {
return url.raw();
}
return Object::null();
}
ErrorMsg(token_pos, "no library handler registered");
}
// Block class finalization attempts when calling into the library
// tag handler.
isolate()->BlockClassFinalization();
DartApiScope api_scope(isolate());
Dart_Handle result = handler(tag,
Api::NewHandle(isolate(), library_.raw()),
Api::NewHandle(isolate(), url.raw()));
isolate()->UnblockClassFinalization();
if (Dart_IsError(result)) {
// In case of an error we append an explanatory error message to the
// error obtained from the library tag handler.
Error& prev_error = Error::Handle();
prev_error ^= Api::UnwrapHandle(result);
AppendErrorMsg(prev_error, token_pos, "library handler failed");
}
if (tag == Dart_kCanonicalizeUrl) {
if (!Dart_IsString(result)) {
ErrorMsg(token_pos, "library handler failed URI canonicalization");
}
}
return Api::UnwrapHandle(result);
}
void Parser::ParseLibraryName() {
ASSERT(CurrentToken() == Token::kLIBRARY);
ConsumeToken();
String& lib_name = *ExpectIdentifier("library name expected");
if (CurrentToken() == Token::kPERIOD) {
while (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
lib_name = String::Concat(lib_name, Symbols::Dot());
lib_name = String::Concat(lib_name,
*ExpectIdentifier("malformed library name"));
}
lib_name = Symbols::New(lib_name);
}
library_.SetName(lib_name);
ExpectSemicolon();
}
void Parser::ParseIdentList(GrowableObjectArray* names) {
if (!IsIdentifier()) {
ErrorMsg("identifier expected");
}
while (IsIdentifier()) {
names->Add(*CurrentLiteral());
ConsumeToken(); // Identifier.
if (CurrentToken() != Token::kCOMMA) {
return;
}
ConsumeToken(); // Comma.
}
}
void Parser::ParseLibraryImportExport() {
bool is_import = (CurrentToken() == Token::kIMPORT);
bool is_export = (CurrentToken() == Token::kEXPORT);
ASSERT(is_import || is_export);
const intptr_t import_pos = TokenPos();
ConsumeToken();
CheckToken(Token::kSTRING, "library 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());
if (url.Length() == 0) {
ErrorMsg("library url expected");
}
String& prefix = String::Handle();
if (is_import && (CurrentToken() == Token::kAS)) {
ConsumeToken();
prefix = ExpectIdentifier("prefix identifier expected")->raw();
}
Array& show_names = Array::Handle();
Array& hide_names = Array::Handle();
if (IsLiteral("show") || IsLiteral("hide")) {
GrowableObjectArray& show_list =
GrowableObjectArray::Handle(GrowableObjectArray::New());
GrowableObjectArray& hide_list =
GrowableObjectArray::Handle(GrowableObjectArray::New());
for (;;) {
if (IsLiteral("show")) {
ConsumeToken();
ParseIdentList(&show_list);
} else if (IsLiteral("hide")) {
ConsumeToken();
ParseIdentList(&hide_list);
} else {
break;
}
}
if (show_list.Length() > 0) {
show_names = Array::MakeArray(show_list);
}
if (hide_list.Length() > 0) {
hide_names = Array::MakeArray(hide_list);
}
}
ExpectSemicolon();
// Canonicalize library URL.
const String& canon_url = String::CheckedHandle(
CallLibraryTagHandler(Dart_kCanonicalizeUrl, import_pos, url));
// Lookup the library URL.
Library& library = Library::Handle(Library::LookupLibrary(canon_url));
if (library.IsNull()) {
// Call the library tag handler to load the library.
CallLibraryTagHandler(Dart_kImportTag, import_pos, canon_url);
// If the library tag handler succeded without registering the
// library we create an empty library to import.
library = Library::LookupLibrary(canon_url);
if (library.IsNull()) {
library = Library::New(canon_url);
library.Register();
}
}
const Namespace& ns =
Namespace::Handle(Namespace::New(library, show_names, hide_names));
if (is_import) {
// Ensure that private dart:_ libraries are only imported into dart:
// libraries.
const String& lib_url = String::Handle(library_.url());
if (canon_url.StartsWith(Symbols::DartSchemePrivate()) &&
!lib_url.StartsWith(Symbols::DartScheme())) {
ErrorMsg(import_pos, "private library is not accessible");
}
if (prefix.IsNull() || (prefix.Length() == 0)) {
library_.AddImport(ns);
} else {
LibraryPrefix& library_prefix = LibraryPrefix::Handle();
library_prefix = library_.LookupLocalLibraryPrefix(prefix);
if (!library_prefix.IsNull()) {
library_prefix.AddImport(ns);
} else {
library_prefix = LibraryPrefix::New(prefix, ns);
library_.AddObject(library_prefix, prefix);
}
}
} else {
ASSERT(is_export);
library_.AddExport(ns);
}
}
void Parser::ParseLibraryPart() {
const intptr_t source_pos = TokenPos();
ConsumeToken(); // Consume "part".
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() {
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.
intptr_t rewind_pos = TokenPos();
intptr_t metadata_pos = SkipMetadata();
if (CurrentToken() == Token::kLIBRARY) {
if (is_patch_source()) {
ErrorMsg("patch cannot override library name");
}
ParseLibraryName();
if (metadata_pos >= 0) {
library_.AddLibraryMetadata(current_class(), metadata_pos);
}
rewind_pos = TokenPos();
metadata_pos = SkipMetadata();
}
while ((CurrentToken() == Token::kIMPORT) ||
(CurrentToken() == Token::kEXPORT)) {
ParseLibraryImportExport();
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(Library::CoreLibrary());
ASSERT(!core_lib.IsNull());
const Namespace& core_ns = Namespace::Handle(
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 (!IsLiteral("of")) {
ErrorMsg("'part of' expected");
}
ConsumeToken();
// The VM is not required to check that the library name matches the
// name of the current library, so we ignore it.
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 = isolate()->object_store();
const GrowableObjectArray& pending_classes =
GrowableObjectArray::Handle(isolate(), object_store->pending_classes());
SetPosition(0);
is_top_level_ = true;
TopLevel top_level;
Class& toplevel_class = Class::Handle(
Class::New(Symbols::TopLevel(), script_, TokenPos()));
toplevel_class.set_library(library_);
if (is_library_source() || is_patch_source()) {
set_current_class(toplevel_class);
ParseLibraryDefinition();
} else if (is_part_source()) {
ParsePartHeader();
}
const Class& cls = Class::Handle(isolate());
while (true) {
set_current_class(cls); // No current class.
intptr_t metadata_pos = SkipMetadata();
if (CurrentToken() == Token::kCLASS) {
ParseClassDeclaration(pending_classes, metadata_pos);
} else if ((CurrentToken() == Token::kTYPEDEF) &&
(LookaheadToken(1) != Token::kLPAREN)) {
set_current_class(toplevel_class);
ParseTypedef(pending_classes, metadata_pos);
} else if ((CurrentToken() == Token::kABSTRACT) &&
(LookaheadToken(1) == Token::kCLASS)) {
ParseClassDeclaration(pending_classes, metadata_pos);
} else if (is_patch_source() && IsLiteral("patch") &&
(LookaheadToken(1) == Token::kCLASS)) {
ParseClassDeclaration(pending_classes, metadata_pos);
} else {
set_current_class(toplevel_class);
if (IsVariableDeclaration()) {
ParseTopLevelVariable(&top_level, metadata_pos);
} else if (IsFunctionDeclaration()) {
ParseTopLevelFunction(&top_level, metadata_pos);
} else if (IsTopLevelAccessor()) {
ParseTopLevelAccessor(&top_level, metadata_pos);
} else if (CurrentToken() == Token::kEOS) {
break;
} else {
UnexpectedToken();
}
}
}
if ((top_level.fields.Length() > 0) || (top_level.functions.Length() > 0)) {
Array& array = Array::Handle();
array = Array::MakeArray(top_level.fields);
toplevel_class.SetFields(array);
array = Array::MakeArray(top_level.functions);
toplevel_class.SetFunctions(array);
library_.AddAnonymousClass(toplevel_class);
pending_classes.Add(toplevel_class, Heap::kOld);
}
}
void Parser::ChainNewBlock(LocalScope* outer_scope) {
Block* block = new Block(current_block_,
outer_scope,
new SequenceNode(TokenPos(), outer_scope));
current_block_ = block;
}
void Parser::OpenBlock() {
ASSERT(current_block_ != NULL);
LocalScope* outer_scope = current_block_->scope;
ChainNewBlock(new 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 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 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(func.context_scope());
ASSERT(!context_scope.IsNull());
outer_scope =
new LocalScope(LocalScope::RestoreOuterScope(context_scope), 0, 0);
}
} else {
// We are parsing a nested function while compiling the enclosing function.
outer_scope = new LocalScope(current_block_->scope,
current_block_->scope->function_level() + 1,
0);
}
ChainNewBlock(outer_scope);
}
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;
}
// Set up default values for all optional parameters to the function.
void Parser::SetupDefaultsForOptionalParams(const ParamList* params,
Array& default_values) {
if (params->num_optional_parameters > 0) {
// Build array of default parameter values.
ParamDesc* param =
params->parameters->data() + params->num_fixed_parameters;
default_values = Array::New(params->num_optional_parameters);
for (int i = 0; i < params->num_optional_parameters; i++) {
ASSERT(param->default_value != NULL);
default_values.SetAt(i, *param->default_value);
param++;
}
}
}
// 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());
const Error& error = Error::Handle(LanguageError::NewFormatted(
Error::Handle(), script, func.token_pos(),
LanguageError::kError, Heap::kNew,
"too many formal parameters"));
ErrorMsg(error);
}
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());
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);
}
}
// 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];
ASSERT(!is_top_level_ || param_desc.type->IsResolved());
const String* name = param_desc.name;
LocalVariable* parameter = new LocalVariable(
param_desc.name_pos, *name, *param_desc.type);
if (!scope->AddVariable(parameter)) {
ErrorMsg(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 (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());
TRACE_PARSER("ParseNativeFunctionBlock");
const Class& cls = Class::Handle(func.Owner());
const Library& library = Library::Handle(cls.library());
ASSERT(func.NumParameters() == params->parameters->length());
// Parse the function name out.
const intptr_t native_pos = TokenPos();
const String& native_name = ParseNativeDeclaration();
// Now resolve the native function to the corresponding native entrypoint.
const int num_params = NativeArguments::ParameterCountForResolution(func);
bool auto_setup_scope = true;
NativeFunction native_function = NativeEntry::ResolveNative(
library, native_name, num_params, &auto_setup_scope);
if (native_function == NULL) {
ErrorMsg(native_pos, "native function '%s' cannot be found",
native_name.ToCString());
}
func.SetIsNativeAutoSetupScope(auto_setup_scope);
// Now add the NativeBodyNode and return statement.
Dart_NativeEntryResolver resolver = library.native_entry_resolver();
bool is_bootstrap_native = Bootstrap::IsBootstapResolver(resolver);
current_block_->statements->Add(
new ReturnNode(TokenPos(),
new NativeBodyNode(TokenPos(),
Function::ZoneHandle(func.raw()),
native_name,
native_function,
current_block_->scope,
is_bootstrap_native)));
}
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);
}
LocalVariable* Parser::LookupPhaseParameter() {
const bool kTestOnly = false;
return current_block_->scope->LookupVariable(Symbols::PhaseParameter(),
kTestOnly);
}
void Parser::CaptureInstantiator() {
ASSERT(current_block_->scope->function_level() > 0);
bool found = false;
if (current_function().IsInFactoryScope()) {
found = current_block_->scope->CaptureVariable(
Symbols::TypeArgumentsParameter());
} else {
found = current_block_->scope->CaptureVariable(Symbols::This());
}
ASSERT(found);
}
AstNode* Parser::LoadReceiver(intptr_t 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) {
ErrorMsg(token_pos, "illegal implicit access to receiver 'this'");
}
return new LoadLocalNode(TokenPos(), receiver);
}
AstNode* Parser::LoadTypeArgumentsParameter(intptr_t token_pos) {
// A nested function may access ':type_arguments' to use as instantiator,
// referring to the implicit first parameter of the outermost enclosing
// factory function.
const bool kTestOnly = false;
LocalVariable* param = LookupTypeArgumentsParameter(current_block_->scope,
kTestOnly);
ASSERT(param != NULL);
return new LoadLocalNode(TokenPos(), param);
}
AstNode* Parser::CallGetter(intptr_t token_pos,
AstNode* object,
const String& name) {
return new InstanceGetterNode(token_pos, object, name);
}
// Returns ast nodes of the variable initialization.
AstNode* Parser::ParseVariableDeclaration(const AbstractType& type,
bool is_final,
bool is_const) {
TRACE_PARSER("ParseVariableDeclaration");
ASSERT(IsIdentifier());
const intptr_t ident_pos = TokenPos();
const String& ident = *CurrentLiteral();
LocalVariable* variable = new LocalVariable(ident_pos, ident, type);
ConsumeToken(); // Variable identifier.
AstNode* initialization = NULL;
if (CurrentToken() == Token::kASSIGN) {
// Variable initialization.
const intptr_t assign_pos = TokenPos();
ConsumeToken();
AstNode* expr = ParseExpr(is_const, kConsumeCascades);
initialization = new StoreLocalNode(assign_pos, variable, expr);
if (is_const) {
ASSERT(expr->IsLiteralNode());
variable->SetConstValue(expr->AsLiteralNode()->literal());
}
} else if (is_final || is_const) {
ErrorMsg(ident_pos,
"missing initialization of 'final' or 'const' variable");
} else {
// Initialize variable with null.
AstNode* null_expr = new LiteralNode(ident_pos, Instance::ZoneHandle());
initialization = new StoreLocalNode(ident_pos, variable, null_expr);
}
ASSERT(current_block_ != NULL);
const intptr_t previous_pos =
current_block_->scope->PreviousReferencePos(ident);
if (previous_pos >= 0) {
ASSERT(!script_.IsNull());
if (previous_pos > ident_pos) {
ErrorMsg(ident_pos,
"initializer of '%s' may not refer to itself",
ident.ToCString());
} else {
intptr_t line_number;
script_.GetTokenLocation(previous_pos, &line_number, NULL);
ErrorMsg(ident_pos,
"identifier '%s' previously used in line %" Pd "",
ident.ToCString(),
line_number);
}
}
// Add variable to scope after parsing the initalizer 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);
if (existing_var->owner() == current_block_->scope) {
ErrorMsg(ident_pos, "identifier '%s' already defined",
variable->name().ToCString());
} else {
ErrorMsg(ident_pos,
"'%s' from outer scope has already been used, cannot redefine",
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::kIDENT) {
if (type_is_optional) {
return Type::DynamicType();
} else {
ErrorMsg("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 ParseType(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(ParseConstFinalVarOrType(
FLAG_enable_type_checks ? ClassFinalizer::kCanonicalize :
ClassFinalizer::kIgnore));
if (!IsIdentifier()) {
ErrorMsg("identifier expected");
}
AstNode* initializers = ParseVariableDeclaration(type, is_final, is_const);
ASSERT(initializers != NULL);
while (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
if (!IsIdentifier()) {
ErrorMsg("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);
sequence->Add(ParseVariableDeclaration(type, is_final, is_const));
initializers = sequence;
}
return initializers;
}
AstNode* Parser::ParseFunctionStatement(bool is_literal) {
TRACE_PARSER("ParseFunctionStatement");
AbstractType& result_type = AbstractType::Handle();
const String* variable_name = NULL;
const String* function_name = NULL;
result_type = Type::DynamicType();
const intptr_t function_pos = TokenPos();
if (is_literal) {
ASSERT(CurrentToken() == Token::kLPAREN);
function_name = &Symbols::AnonymousClosure();
} else {
if (CurrentToken() == Token::kVOID) {
ConsumeToken();
result_type = Type::VoidType();
} else if ((CurrentToken() == Token::kIDENT) &&
(LookaheadToken(1) != Token::kLPAREN)) {
result_type = ParseType(ClassFinalizer::kCanonicalize);
}
const intptr_t name_pos = TokenPos();
variable_name = ExpectIdentifier("function name expected");
function_name = variable_name;
// Check that the function name has not been referenced
// before this declaration.
ASSERT(current_block_ != NULL);
const intptr_t previous_pos =
current_block_->scope->PreviousReferencePos(*function_name);
if (previous_pos >= 0) {
ASSERT(!script_.IsNull());
intptr_t line_number;
script_.GetTokenLocation(previous_pos, &line_number, NULL);
ErrorMsg(name_pos,
"identifier '%s' previously used in line %" Pd "",
function_name->ToCString(),
line_number);
}
}
CheckToken(Token::kLPAREN);
// 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();
bool is_new_closure = false;
// 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 = current_class().LookupClosureFunction(function_pos);
if (function.IsNull() || (function.token_pos() != function_pos) ||
(function.parent_function() != innermost_function().raw())) {
// The function will be registered in the lookup table by the
// EffectGraphVisitor::VisitClosureNode when the newly allocated closure
// function has been properly setup.
is_new_closure = true;
function = Function::NewClosureFunction(*function_name,
innermost_function(),
function_pos);
function.set_result_type(result_type);
}
// 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();
if (variable_name != NULL) {
// 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
// parameterized type to be patched after the actual type is known.
// We temporarily use the class of the Function interface.
const Class& unknown_signature_class = Class::Handle(
Type::Handle(Type::Function()).type_class());
function_type = Type::New(
unknown_signature_class, TypeArguments::Handle(), function_pos);
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 LocalVariable(function_pos,
*variable_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);
if (existing_var->owner() == current_block_->scope) {
ErrorMsg(function_pos, "identifier '%s' already defined",
function_variable->name().ToCString());
} else {
ErrorMsg(function_pos,
"'%s' from outer scope has already been used, cannot redefine",
function_variable->name().ToCString());
}
}
}
// Parse the local function.
Array& default_parameter_values = Array::Handle();
SequenceNode* statements = Parser::ParseFunc(function,
default_parameter_values);
// Now that the local function has formal parameters, lookup the signature
// class in the current library (but not in its imports) and only create a new
// canonical signature class if it does not exist yet.
const String& signature = String::Handle(function.Signature());
Class& signature_class = Class::ZoneHandle();
if (!is_new_closure) {
signature_class = function.signature_class();
}
if (signature_class.IsNull()) {
signature_class = library_.LookupLocalClass(signature);
}
if (signature_class.IsNull()) {
// If we don't have a signature class yet, this must be a closure we
// have not parsed before.
ASSERT(is_new_closure);
signature_class = Class::NewSignatureClass(signature,
function,
script_,
function.token_pos());
// Record the function signature class in the current library.
library_.AddClass(signature_class);
} else if (is_new_closure) {
function.set_signature_class(signature_class);
}
ASSERT(function.signature_class() == signature_class.raw());
// 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());
// Make sure that the instantiator is captured.
if ((signature_class.NumTypeParameters() > 0) &&
(current_block_->scope->function_level() > 0)) {
CaptureInstantiator();
}
// Since the signature type is cached by the signature class, it may have
// been finalized already.
Type& signature_type = Type::Handle(signature_class.SignatureType());
AbstractTypeArguments& signature_type_arguments =
AbstractTypeArguments::Handle(signature_type.arguments());
if (!signature_type.IsFinalized()) {
signature_type ^= ClassFinalizer::FinalizeType(
signature_class, signature_type, ClassFinalizer::kCanonicalize);
// The call to ClassFinalizer::FinalizeType may have
// extended the vector of type arguments.
signature_type_arguments = signature_type.arguments();
ASSERT(signature_type_arguments.IsNull() ||
(signature_type_arguments.Length() ==
signature_class.NumTypeArguments()));
// The signature_class should not have changed.
ASSERT(signature_type.type_class() == signature_class.raw());
}
// 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());
if (variable_name != NULL) {
// Patch the function type of the variable now that the signature is known.
function_type.set_type_class(signature_class);
function_type.set_arguments(signature_type_arguments);
// 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 == NULL) ||
(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 ClosureNode(function_pos, function, NULL, statements->scope());
if (function_variable == NULL) {
ASSERT(is_literal);
return closure;
} else {
AstNode* initialization =
new StoreLocalNode(function_pos, function_variable, closure);
return initialization;
}
}
// 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() {
if (CurrentToken() == Token::kLT) {
// We are possibly looking at type parameters. Find closing ">".
int nesting_level = 0;
do {
if (CurrentToken() == Token::kLT) {
nesting_level++;
} else if (CurrentToken() == Token::kGT) {
nesting_level--;
} else if (CurrentToken() == Token::kSHR) {
nesting_level -= 2;
} else if (CurrentToken() == 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 (CurrentToken() != Token::kCOMMA &&
CurrentToken() != Token::kEXTENDS) {
// We are looking at something other than type parameters.
return false;
}
ConsumeToken();
} while (nesting_level > 0);
if (nesting_level < 0) {
return false;
}
}
return true;
}
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() != AbstractTypeArguments::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());
}
// Returns true if the next tokens can be parsed as a type with optional
// type parameters. Current token position is not restored.
bool Parser::TryParseOptionalType() {
if (CurrentToken() == Token::kIDENT) {
QualIdent type_name;
ParseQualIdent(&type_name);
if ((CurrentToken() == Token::kLT) && !TryParseTypeParameters()) {
return false;
}
}
return true;
}
// Returns true if the next tokens can be parsed as a type with optional
// type parameters, or keyword "void".
// Current token position is not restored.
bool Parser::TryParseReturnType() {
if (CurrentToken() == Token::kVOID) {
ConsumeToken();
return true;
} else if (CurrentToken() == Token::kIDENT) {
return TryParseOptionalType();
}
return false;
}
// 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 intptr_t saved_pos = TokenPos();
SkipMetadata();
const bool is_var_decl = IsVariableDeclaration();
SetPosition(saved_pos);
return is_var_decl;
}
if ((CurrentToken() != Token::kIDENT) && (CurrentToken() != Token::kCONST)) {
// Not a legal type identifier or const keyword or metadata.
return false;
}
const intptr_t saved_pos = TokenPos();
bool is_var_decl = false;
bool have_type = false;
if (CurrentToken() == Token::kCONST) {
ConsumeToken();
have_type = true; // Type is dynamic.
}
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 intptr_t type_pos = TokenPos();
if (TryParseOptionalType()) {
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 detect whether the next tokens should be parsed as
// a function declaration. Token position remains unchanged.
bool Parser::IsFunctionDeclaration() {
const intptr_t saved_pos = TokenPos();
bool is_external = false;
if (is_top_level_) {
if (is_patch_source() &&
(CurrentToken() == Token::kIDENT) &&
CurrentLiteral()->Equals("patch") &&
(LookaheadToken(1) != Token::kLPAREN)) {
// Skip over 'patch' for top-level function declarations in patch sources.
ConsumeToken();
} else if (CurrentToken() == Token::kEXTERNAL) {
// Skip over 'external' for top-level function declarations.
is_external = true;
ConsumeToken();
}
}
if (IsIdentifier() && (LookaheadToken(1) == Token::kLPAREN)) {
// Possibly a function without explicit return type.
ConsumeToken(); // Consume function identifier.
} else if (TryParseReturnType()) {
if (!IsIdentifier()) {
SetPosition(saved_pos);
return false;
}
ConsumeToken(); // Consume function identifier.
} else {
SetPosition(saved_pos);
return false;
}
// Check parameter list and the following token.
if (CurrentToken() == Token::kLPAREN) {
SkipToMatchingParenthesis();
if ((CurrentToken() == Token::kLBRACE) ||
(CurrentToken() == Token::kARROW) ||
(is_top_level_ && IsLiteral("native")) ||
is_external) {
SetPosition(saved_pos);
return true;
}
}
SetPosition(saved_pos);
return false;
}
bool Parser::IsTopLevelAccessor() {
const intptr_t saved_pos = TokenPos();
if (is_patch_source() &&
(CurrentToken() == Token::kIDENT) &&
(CurrentLiteral()->Equals("patch"))) {
ConsumeToken();
} else if (CurrentToken() == Token::kEXTERNAL) {
ConsumeToken();
}
if ((CurrentToken() == Token::kGET) || (CurrentToken() == Token::kSET)) {
SetPosition(saved_pos);
return true;
}
if (TryParseReturnType()) {
if ((CurrentToken() == Token::kGET) || (CurrentToken() == Token::kSET)) {
if (Token::IsIdentifier(LookaheadToken(1))) { // Accessor name.
SetPosition(saved_pos);
return true;
}
}
}
SetPosition(saved_pos);
return false;
}
bool Parser::IsFunctionLiteral() {
if (CurrentToken() != Token::kLPAREN || !allow_function_literals_) {
return false;
}
const intptr_t saved_pos = TokenPos();
bool is_function_literal = false;
SkipToMatchingParenthesis();
if ((CurrentToken() == Token::kLBRACE) ||
(CurrentToken() == Token::kARROW)) {
is_function_literal = true;
}
SetPosition(saved_pos);
return is_function_literal;
}
// 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 intptr_t saved_pos = TokenPos();
bool result = false;
// 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) {
result = true;
} else if (TryParseOptionalType()) {
if (IsIdentifier()) {
ConsumeToken();
}
result = (CurrentToken() == Token::kIN);
}
}
SetPosition(saved_pos);
return result;
}
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;
while (CurrentToken() != Token::kRBRACE) {
const intptr_t 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) {
ErrorMsg(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 {
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 intptr_t 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 = ParseExpr(kAllowConst, kConsumeCascades);
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 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(const Instance& value) {
Class& cls = Class::Handle(value.clazz());
const Function& equal_op = Function::Handle(
Resolver::ResolveDynamicAnyArgs(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 intptr_t val_pos = values[i]->token_pos();
if (first_value.IsInteger()) {
if (!val.IsInteger()) {
ErrorMsg(val_pos, "expected case expression of type int");
}
continue;
}
if (first_value.IsString()) {
if (!val.IsString()) {
ErrorMsg(val_pos, "expected case expression of type String");
}
continue;
}
if (val.IsDouble()) {
ErrorMsg(val_pos, "case expression may not be of type double");
}
if (val.clazz() != first_value.clazz()) {
ErrorMsg(val_pos, "all case expressions must be of same type");
}
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(val)) {
ErrorMsg(val_pos,
"type class of case expression must not implement operator ==");
}
}
}
if (first_value.IsInteger()) {
return Type::Handle(Type::IntType()).type_class();
} else if (first_value.IsString()) {
return Type::Handle(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 intptr_t case_pos = TokenPos();
// The case expressions node sequence does not own the enclosing scope.
SequenceNode* case_expressions = new SequenceNode(case_pos, NULL);
while (CurrentToken() == Token::kCASE || CurrentToken() == Token::kDEFAULT) {
if (CurrentToken() == Token::kCASE) {
if (default_seen) {
ErrorMsg("default clause must be last case");
}
ConsumeToken(); // Keyword case.
const intptr_t expr_pos = TokenPos();
AstNode* expr = ParseExpr(kRequireConst, kConsumeCascades);
ASSERT(expr->IsLiteralNode());
case_expr_values->Add(expr->AsLiteralNode());
AstNode* switch_expr_load = new LoadLocalNode(case_pos,
switch_expr_value);
AstNode* case_comparison = new ComparisonNode(expr_pos,
Token::kEQ,
expr,
switch_expr_load);
case_expressions->Add(case_comparison);
} else {
if (default_seen) {
ErrorMsg("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 ArgumentListNode(TokenPos());
arguments->Add(new LiteralNode(
TokenPos(), Integer::ZoneHandle(Integer::New(TokenPos()))));
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 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 intptr_t switch_pos = TokenPos();
SourceLabel* label =
SourceLabel::New(switch_pos, label_name, SourceLabel::kSwitch);
ConsumeToken();
ExpectToken(Token::kLPAREN);
const intptr_t expr_pos = TokenPos();
AstNode* switch_expr = ParseExpr(kAllowConst, kConsumeCascades);
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(Type::New(Class::Handle(Object::dynamic_class()),
TypeArguments::Handle(),
expr_pos));
temp_var_type.SetIsFinalized();
LocalVariable* temp_variable =
new LocalVariable(expr_pos, Symbols::SwitchExpr(), temp_var_type);
current_block_->scope->AddVariable(temp_variable);
AstNode* save_switch_expr =
new 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 intptr_t 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 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();
} else {
ErrorMsg(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) {
ErrorMsg("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) {
ErrorMsg("'case' or '}' expected");
} else if (case_label != NULL) {
ErrorMsg("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(CheckCaseExpressions(case_expr_values)));
// Check for unresolved label references.
SourceLabel* unresolved_label =
current_block_->scope->CheckUnresolvedLabels();
if (unresolved_label != NULL) {
ErrorMsg("unresolved reference to label '%s'",
unresolved_label->name().ToCString());
}
SequenceNode* switch_body = CloseBlock();
ExpectToken(Token::kRBRACE);
return new SwitchNode(switch_pos, label, switch_body);
}
AstNode* Parser::ParseWhileStatement(String* label_name) {
TRACE_PARSER("ParseWhileStatement");
const intptr_t while_pos = TokenPos();
SourceLabel* label =
SourceLabel::New(while_pos, label_name, SourceLabel::kWhile);
ConsumeToken();
ExpectToken(Token::kLPAREN);
AstNode* cond_expr = ParseExpr(kAllowConst, kConsumeCascades);
ExpectToken(Token::kRPAREN);
const bool parsing_loop_body = true;
SequenceNode* while_body = ParseNestedStatement(parsing_loop_body, label);
return new WhileNode(while_pos, label, cond_expr, while_body);
}
AstNode* Parser::ParseDoWhileStatement(String* label_name) {
TRACE_PARSER("ParseDoWhileStatement");
const intptr_t 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);
AstNode* cond_expr = ParseExpr(kAllowConst, kConsumeCascades);
ExpectToken(Token::kRPAREN);
ExpectSemicolon();
return new DoWhileNode(do_pos, label, cond_expr, dowhile_body);
}
AstNode* Parser::ParseForInStatement(intptr_t forin_pos,
SourceLabel* label) {
TRACE_PARSER("ParseForInStatement");
bool is_final = (CurrentToken() == Token::kFINAL);
if (CurrentToken() == Token::kCONST) {
ErrorMsg("Loop variable cannot be 'const'");
}
const String* loop_var_name = NULL;
LocalVariable* loop_var = NULL;
intptr_t loop_var_pos = 0;
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.
const AbstractType& type =
AbstractType::ZoneHandle(ParseConstFinalVarOrType(
FLAG_enable_type_checks ? ClassFinalizer::kCanonicalize :
ClassFinalizer::kIgnore));
loop_var_pos = TokenPos();
loop_var_name = ExpectIdentifier("variable name expected");
loop_var = new LocalVariable(loop_var_pos, *loop_var_name, type);
if (is_final) {
loop_var->set_is_final();
}
}
ExpectToken(Token::kIN);
const intptr_t collection_pos = TokenPos();
AstNode* collection_expr = ParseExpr(kAllowConst, kConsumeCascades);
ExpectToken(Token::kRPAREN);
OpenBlock(); // Implicit block around while loop.
// 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 = Type::ZoneHandle(Type::DynamicType());
LocalVariable* iterator_var =
new LocalVariable(collection_pos, Symbols::ForInIter(), iterator_type);
current_block_->scope->AddVariable(iterator_var);
// Generate initialization of iterator variable.
ArgumentListNode* no_args = new ArgumentListNode(collection_pos);
AstNode* get_iterator = new InstanceGetterNode(
collection_pos, collection_expr, Symbols::GetIterator());
AstNode* iterator_init =
new StoreLocalNode(collection_pos, iterator_var, get_iterator);
current_block_->statements->Add(iterator_init);
// Generate while loop condition.
AstNode* iterator_moveNext = new InstanceCallNode(
collection_pos,
new 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);
AstNode* iterator_current = new InstanceGetterNode(
collection_pos,
new LoadLocalNode(collection_pos, iterator_var),
Symbols::Current());
// Generate assignment of next iterator value to loop variable.
AstNode* loop_var_assignment = NULL;
if (loop_var != NULL) {
// The for loop declares a new variable. Add it to the loop body scope.
current_block_->scope->AddVariable(loop_var);
loop_var_assignment =
new StoreLocalNode(loop_var_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_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 WhileNode(forin_pos, label, iterator_moveNext, 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 intptr_t 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 intptr_t init_pos = TokenPos();
LocalScope* init_scope = current_block_->scope;
if (CurrentToken() != Token::kSEMICOLON) {
if (IsVariableDeclaration()) {
initializer = ParseVariableDeclarationList();
} else {
initializer = ParseExpr(kAllowConst, kConsumeCascades);
}
}
ExpectSemicolon();
AstNode* condition = NULL;
if (CurrentToken() != Token::kSEMICOLON) {
condition = ParseExpr(kAllowConst, kConsumeCascades);
}
ExpectSemicolon();
AstNode* increment = NULL;
const intptr_t incr_pos = TokenPos();
if (CurrentToken() != Token::kRPAREN) {
increment = ParseExprList();
}
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 SequenceNode(incr_pos, NULL);
incr_sequence->Add(new CloneContextNode(for_pos));
if (increment != NULL) {
incr_sequence->Add(increment);
}
increment = incr_sequence;
break;
}
}
AstNode* for_node =
new ForNode(for_pos,
label,
NodeAsSequenceNode(init_pos, initializer, NULL),
condition,
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(Library::LookupCoreClass(cls_name));
ASSERT(!cls.IsNull());
const Function& func = Function::ZoneHandle(
Resolver::ResolveStatic(cls,
func_name,
arguments->length(),
arguments->names()));
ASSERT(!func.IsNull());
return new StaticCallNode(arguments->token_pos(), func, arguments);
}
AstNode* Parser::MakeAssertCall(intptr_t begin, intptr_t end) {
ArgumentListNode* arguments = new ArgumentListNode(begin);
arguments->Add(new LiteralNode(begin,
Integer::ZoneHandle(Integer::New(begin))));
arguments->Add(new LiteralNode(end,
Integer::ZoneHandle(Integer::New(end))));
return MakeStaticCall(Symbols::AssertionError(),
Library::PrivateCoreLibName(Symbols::ThrowNew()),
arguments);
}
AstNode* Parser::InsertClosureCallNodes(AstNode* condition) {
if (condition->IsClosureNode() ||
(condition->IsStoreLocalNode() &&
condition->AsStoreLocalNode()->value()->IsClosureNode())) {
EnsureSavedCurrentContext();
// Function literal in assert implies a call.
const intptr_t pos = condition->token_pos();
condition = new ClosureCallNode(pos, condition, new ArgumentListNode(pos));
} else if (condition->IsConditionalExprNode()) {
ConditionalExprNode* cond_expr = condition->AsConditionalExprNode();
cond_expr->set_true_expr(InsertClosureCallNodes(cond_expr->true_expr()));
cond_expr->set_false_expr(InsertClosureCallNodes(cond_expr->false_expr()));
}
return condition;
}
AstNode* Parser::ParseAssertStatement() {
TRACE_PARSER("ParseAssertStatement");
ConsumeToken(); // Consume assert keyword.
ExpectToken(Token::kLPAREN);
const intptr_t condition_pos = TokenPos();
if (!FLAG_enable_asserts && !FLAG_enable_type_checks) {
SkipExpr();
ExpectToken(Token::kRPAREN);
return NULL;
}
AstNode* condition = ParseExpr(kAllowConst, kConsumeCascades);
const intptr_t condition_end = TokenPos();
ExpectToken(Token::kRPAREN);
condition = InsertClosureCallNodes(condition);
condition = new UnaryOpNode(condition_pos, Token::kNOT, condition);
AstNode* assert_throw = MakeAssertCall(condition_pos, condition_end);
return new IfNode(condition_pos,
condition,
NodeAsSequenceNode(condition_pos, assert_throw, NULL),
NULL);
}
struct CatchParamDesc {
CatchParamDesc()
: token_pos(0), type(NULL), name(NULL), var(NULL) { }
intptr_t token_pos;
const AbstractType* type;
const String* name;
LocalVariable* var;
};
// 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 LocalVariable(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 LocalVariable(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) {
ErrorMsg(stack_trace_param->token_pos,
"name '%s' already exists in scope",
stack_trace_param->name->ToCString());
}
stack_trace_param->var = var;
}
}
SequenceNode* Parser::ParseFinallyBlock() {
TRACE_PARSER("ParseFinallyBlock");
OpenBlock();
ExpectToken(Token::kLBRACE);
ParseStatementSequence();
ExpectToken(Token::kRBRACE);
SequenceNode* finally_block = CloseBlock();
return finally_block;
}
void Parser::PushTryBlock(Block* try_block) {
intptr_t try_index = AllocateTryIndex();
TryBlocks* block = new TryBlocks(try_block, try_blocks_list_, try_index);
try_blocks_list_ = block;
}
Parser::TryBlocks* Parser::PopTryBlock() {
TryBlocks* innermost_try_block = try_blocks_list_;
try_blocks_list_ = try_blocks_list_->outer_try_block();
return innermost_try_block;
}
void Parser::AddNodeForFinallyInlining(AstNode* node) {
if (node == NULL) {
return;
}
ASSERT(node->IsReturnNode() || node->IsJumpNode());
TryBlocks* iterator = try_blocks_list_;
while (iterator != NULL) {
// 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.
if (label->owner()->IsNestedWithin(try_scope)) {
break;
}
}
iterator->AddNodeForFinallyInlining(node);
iterator = iterator->outer_try_block();
}
}
// Add the inlined finally block to the specified node.
void Parser::AddFinallyBlockToNode(AstNode* node,
InlinedFinallyNode* finally_node) {
ReturnNode* return_node = node->AsReturnNode();
if (return_node != NULL) {
return_node->AddInlinedFinallyNode(finally_node);
if (return_node->saved_return_value_var() == NULL) {
LocalVariable* temp =
CreateTempConstVariable(node->token_pos(), "finally_ret_val");
return_node->set_saved_return_value_var(temp);
}
return;
}
JumpNode* jump_node = node->AsJumpNode();
ASSERT(jump_node != NULL);
jump_node->AddInlinedFinallyNode(finally_node);
}
SequenceNode* Parser::ParseCatchClauses(
intptr_t handler_pos,
LocalVariable* exception_var,
LocalVariable* 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) || IsLiteral("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 intptr_t catch_pos = TokenPos();
CatchParamDesc exception_param;
CatchParamDesc stack_trace_param;
if (IsLiteral("on")) {
ConsumeToken();
exception_param.type = &AbstractType::ZoneHandle(
ParseType(ClassFinalizer::kCanonicalize));
} else {
exception_param.type = &AbstractType::ZoneHandle(Type::DynamicType());
}
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();
// TODO(hausner): Make implicit type be StackTrace, not dynamic.
stack_trace_param.type =
&AbstractType::ZoneHandle(Type::DynamicType());
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) 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 StoreLocalNode(catch_pos, exception_param.var,
new 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;
ArgumentListNode* no_args = new ArgumentListNode(catch_pos);
ASSERT(stack_trace_var != NULL);
current_block_->statements->Add(
new StoreLocalNode(catch_pos, stack_trace_param.var,
new LoadLocalNode(catch_pos, stack_trace_var)));
current_block_->statements->Add(
new InstanceCallNode(
catch_pos,
new LoadLocalNode(catch_pos, stack_trace_param.var),
Library::PrivateCoreLibName(Symbols::_setupFullStackTrace()),
no_args));
}
// Add nested block with user-defined code. This blocks 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 LiteralNode(catch_pos, Bool::True()));
if (is_bad_type) {
// Replace the body with one that throws.
SequenceNode* block = new 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);
} 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() &&
(current_block_->scope->function_level() > 0)) {
// Make sure that the instantiator is captured.
CaptureInstantiator();
}
TypeNode* exception_type = new TypeNode(catch_pos, *exception_param.type);
AstNode* exception_value = new LoadLocalNode(catch_pos, exception_var);
if (!exception_type->type().IsInstantiated()) {
EnsureExpressionTemp();
}
type_tests.Add(new 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);
}
}
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.
current = new SequenceNode(handler_pos, NULL);
current->Add(
new ThrowNode(handler_pos,
new LoadLocalNode(handler_pos, exception_var),
new 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 IfNode(type_test->token_pos(), type_test, catch_block, current));
current = CloseBlock();
}
return current;
}
AstNode* Parser::ParseTryStatement(String* label_name) {
TRACE_PARSER("ParseTryStatement");
// We create three variables for exceptions here:
// ':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.
LocalVariable* context_var =
current_block_->scope->LocalLookupVariable(Symbols::SavedTryContextVar());
if (context_var == NULL) {
context_var = new LocalVariable(TokenPos(),
Symbols::SavedTryContextVar(),
Type::ZoneHandle(Type::DynamicType()));
current_block_->scope->AddVariable(context_var);
}
LocalVariable* exception_var =
current_block_->scope->LocalLookupVariable(Symbols::ExceptionVar());
if (exception_var == NULL) {
exception_var = new LocalVariable(TokenPos(),
Symbols::ExceptionVar(),
Type::ZoneHandle(Type::DynamicType()));
current_block_->scope->AddVariable(exception_var);
}
LocalVariable* stack_trace_var =
current_block_->scope->LocalLookupVariable(Symbols::StackTraceVar());
if (stack_trace_var == NULL) {
stack_trace_var = new LocalVariable(TokenPos(),
Symbols::StackTraceVar(),
Type::ZoneHandle(Type::DynamicType()));
current_block_->scope->AddVariable(stack_trace_var);
}
const intptr_t try_pos = TokenPos();
ConsumeToken(); // Consume the 'try'.
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);
}
// Now parse the 'try' block.
OpenBlock();
PushTryBlock(current_block_);
ExpectToken(Token::kLBRACE);
ParseStatementSequence();
ExpectToken(Token::kRBRACE);
SequenceNode* try_block = CloseBlock();
if ((CurrentToken() != Token::kCATCH) && !IsLiteral("on") &&
(CurrentToken() != Token::kFINALLY)) {
ErrorMsg("catch or finally clause expected");
}
// Now parse the 'catch' blocks if any.
try_blocks_list_->enter_catch();
const intptr_t handler_pos = TokenPos();
const GrowableObjectArray& handler_types =
GrowableObjectArray::Handle(GrowableObjectArray::New());
bool needs_stack_trace = false;
SequenceNode* catch_handler_list =
ParseCatchClauses(handler_pos, exception_var, stack_trace_var,
handler_types, &needs_stack_trace);
TryBlocks* inner_try_block = PopTryBlock();
const intptr_t try_index = inner_try_block->try_index();
TryBlocks* outer_try_block = try_blocks_list_;
const intptr_t outer_try_index = (outer_try_block != NULL)
? outer_try_block->try_index()
: CatchClauseNode::kInvalidTryIndex;
// Finally parse the 'finally' block.
SequenceNode* finally_block = NULL;
if (CurrentToken() == Token::kFINALLY) {
ConsumeToken(); // Consume the 'finally'.
const intptr_t finally_pos = TokenPos();
// Add the finally block to the exit points recorded so far.
intptr_t node_index = 0;
AstNode* node_to_inline =
inner_try_block->GetNodeToInlineFinally(node_index);
while (node_to_inline != NULL) {
finally_block = ParseFinallyBlock();
InlinedFinallyNode* node = new InlinedFinallyNode(finally_pos,
finally_block,
context_var,
outer_try_index);
AddFinallyBlockToNode(node_to_inline, node);
node_index += 1;
node_to_inline = inner_try_block->GetNodeToInlineFinally(node_index);
tokens_iterator_.SetCurrentPosition(finally_pos);
}
finally_block = ParseFinallyBlock();
}
CatchClauseNode* catch_clause =
new CatchClauseNode(handler_pos,
catch_handler_list,
Array::ZoneHandle(Array::MakeArray(handler_types)),
context_var,
exception_var,
stack_trace_var,
(finally_block != 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 TryCatchNode(try_pos, try_block, context_var, catch_clause,
finally_block, try_index);
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 intptr_t 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) {
ErrorMsg(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 SourceLabel(
TokenPos(), target_name, SourceLabel::kForward);
switch_scope->AddLabel(target);
}
}
if (target == NULL) {
ErrorMsg(jump_pos, "label '%s' not found", target_name.ToCString());
}
} else {
target = current_block_->scope->LookupInnermostLabel(jump_kind);
if (target == NULL) {
ErrorMsg(jump_pos, "'%s' is illegal here", Token::Str(jump_kind));
}
}
ASSERT(target != NULL);
if (jump_kind == Token::kCONTINUE) {
if (target->kind() == SourceLabel::kSwitch) {
ErrorMsg(jump_pos, "'continue' jump to switch statement is illegal");
} else if (target->kind() == SourceLabel::kStatement) {
ErrorMsg(jump_pos, "'continue' jump to label '%s' is illegal",
target->name().ToCString());
}
}
if (jump_kind == Token::kBREAK && target->kind() == SourceLabel::kCase) {
ErrorMsg(jump_pos, "'break' to case clause label is illegal");
}
if (target->FunctionLevel() != current_block_->scope->function_level()) {
ErrorMsg(jump_pos, "'%s' target must be in same function context",
Token::Str(jump_kind));
}
return new JumpNode(jump_pos, jump_kind, target);
}
AstNode* Parser::ParseStatement() {
TRACE_PARSER("ParseStatement");
AstNode* statement = NULL;
intptr_t label_pos = 0;
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 > 0);
ConsumeToken(); // Consume identifier.
ConsumeToken(); // Consume colon.
}
}
const intptr_t statement_pos = TokenPos();
if (CurrentToken() == Token::kWHILE) {
statement = ParseWhileStatement(label_name);
} else if (CurrentToken() == Token::kFOR) {
statement = ParseForStatement(label_name);
} else if (CurrentToken() == Token::kDO) {
statement = ParseDoWhileStatement(label_name);
} else if (CurrentToken() == Token::kSWITCH) {
statement = ParseSwitchStatement(label_name);
} else if (CurrentToken() == Token::kTRY) {
statement = ParseTryStatement(label_name);
} else if (CurrentToken() == Token::kRETURN) {
const intptr_t return_pos = TokenPos();
ConsumeToken();
if (CurrentToken() != Token::kSEMICOLON) {
if (current_function().IsConstructor() &&
(current_block_->scope->function_level() == 0)) {
ErrorMsg(return_pos, "return of a value not allowed in constructors");
}
AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades);
statement = new ReturnNode(statement_pos, expr);
} else {
statement = new ReturnNode(statement_pos);
}
AddNodeForFinallyInlining(statement);
ExpectSemicolon();
} else if (CurrentToken() == Token::kIF) {
statement = ParseIfStatement(label_name);
} else if (CurrentToken() == Token::kASSERT) {
statement = ParseAssertStatement();
ExpectSemicolon();
} else if (IsVariableDeclaration()) {
statement = ParseVariableDeclarationList();
ExpectSemicolon();
} else if (IsFunctionDeclaration()) {
statement = ParseFunctionStatement(false);
} else if (CurrentToken() == 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 (CurrentToken() == Token::kBREAK) {
statement = ParseJump(label_name);
AddNodeForFinallyInlining(statement);
ExpectSemicolon();
} else if (CurrentToken() == Token::kCONTINUE) {
statement = ParseJump(label_name);
AddNodeForFinallyInlining(statement);
ExpectSemicolon();
} else if (CurrentToken() == Token::kSEMICOLON) {
// Empty statement, nothing to do.
ConsumeToken();
} else if (CurrentToken() == Token::kRETHROW) {
// Rethrow of current exception.
ConsumeToken();
ExpectSemicolon();
// Check if it is ok to do a rethrow.
if ((try_blocks_list_ == NULL) || !try_blocks_list_->inside_catch()) {
ErrorMsg(statement_pos, "rethrow of an exception is not valid here");
}
// The exception and stack trace variables are bound in the block
// containing the try.
LocalScope* scope = try_blocks_list_->try_block()->scope->parent();
ASSERT(scope != NULL);
LocalVariable* excp_var =
scope->LocalLookupVariable(Symbols::ExceptionVar());
ASSERT(excp_var != NULL);
LocalVariable* trace_var =
scope->LocalLookupVariable(Symbols::StackTraceVar());
ASSERT(trace_var != NULL);
statement = new ThrowNode(statement_pos,
new LoadLocalNode(statement_pos, excp_var),
new LoadLocalNode(statement_pos, trace_var));
} else {
statement = ParseExpr(kAllowConst, kConsumeCascades);
ExpectSemicolon();
}
return statement;
}
void Parser::ErrorMsg(intptr_t token_pos, const char* format, ...) const {
va_list args;
va_start(args, format);
const Error& error = Error::Handle(LanguageError::NewFormattedV(
Error::Handle(), script_, token_pos,
LanguageError::kError, Heap::kNew, format, args));
va_end(args);
isolate()->long_jump_base()->Jump(1, error);
UNREACHABLE();
}
void Parser::ErrorMsg(const char* format, ...) {
va_list args;
va_start(args, format);
const Error& error = Error::Handle(LanguageError::NewFormattedV(
Error::Handle(), script_, TokenPos(),
LanguageError::kError, Heap::kNew, format, args));
va_end(args);
isolate()->long_jump_base()->Jump(1, error);
UNREACHABLE();
}
void Parser::ErrorMsg(const Error& error) {
Isolate::Current()->long_jump_base()->Jump(1, error);
UNREACHABLE();
}
void Parser::AppendErrorMsg(
const Error& prev_error, intptr_t token_pos, const char* format, ...) {
va_list args;
va_start(args, format);
const Error& error = Error::Handle(
LanguageError::NewFormattedV(
prev_error, script_, token_pos,
LanguageError::kError, Heap::kNew,
format, args));
va_end(args);
isolate()->long_jump_base()->Jump(1, error);
UNREACHABLE();
}
void Parser::Warning(intptr_t token_pos, const char* format, ...) {
if (FLAG_silent_warnings) return;
va_list args;
va_start(args, format);
const Error& error = Error::Handle(
LanguageError::NewFormattedV(
Error::Handle(), script_, token_pos,
LanguageError::kWarning, Heap::kNew,
format, args));
va_end(args);
if (FLAG_warning_as_error) {
isolate()->long_jump_base()->Jump(1, error);
UNREACHABLE();
} else {
OS::Print("%s", error.ToErrorCString());
}
}
void Parser::Warning(const char* format, ...) {
if (FLAG_silent_warnings) return;
va_list args;
va_start(args, format);
const Error& error = Error::Handle(
LanguageError::NewFormattedV(
Error::Handle(), script_, TokenPos(),
LanguageError::kWarning, Heap::kNew,
format, args));
va_end(args);
if (FLAG_warning_as_error) {
isolate()->long_jump_base()->Jump(1, error);
UNREACHABLE();
} else {
OS::Print("%s", error.ToErrorCString());
}
}
void Parser::Unimplemented(const char* msg) {
ErrorMsg(TokenPos(), "%s", msg);
}
void Parser::CheckToken(Token::Kind token_expected, const char* msg) {
if (CurrentToken() != token_expected) {
if (msg != NULL) {
ErrorMsg("%s", msg);
} else {
ErrorMsg("'%s' expected", Token::Str(token_expected));
}
}
}
void Parser::ExpectToken(Token::Kind token_expected) {
if (CurrentToken() != token_expected) {
ErrorMsg("'%s' expected", Token::Str(token_expected));
}
ConsumeToken();
}
void Parser::ExpectSemicolon() {
if (CurrentToken() != Token::kSEMICOLON) {
ErrorMsg("semicolon expected");
}
ConsumeToken();
}
void Parser::UnexpectedToken() {
ErrorMsg("unexpected token '%s'",
CurrentToken() == Token::kIDENT ?
CurrentLiteral()->ToCString() : Token::Str(CurrentToken()));
}
String* Parser::ExpectUserDefinedTypeIdentifier(const char* msg) {
if (CurrentToken() != Token::kIDENT) {
ErrorMsg("%s", msg);
}
String* ident = CurrentLiteral();
if (ident->Equals("dynamic")) {
ErrorMsg("%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()) {
ErrorMsg("%s", msg);
}
String* ident = CurrentLiteral();
ConsumeToken();
return ident;
}
bool Parser::IsLiteral(const char* literal) {
return IsIdentifier() && CurrentLiteral()->Equals(literal);
}
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(intptr_t sequence_pos,
AstNode* node,
LocalScope* scope) {
if ((node == NULL) || !node->IsSequenceNode()) {
SequenceNode* sequence = new SequenceNode(sequence_pos, scope);
if (node != NULL) {
sequence->Add(node);
}
return sequence;
}
return node->AsSequenceNode();
}
AstNode* Parser::ThrowTypeError(intptr_t type_pos, const AbstractType& type) {
ArgumentListNode* arguments = new ArgumentListNode(type_pos);
// Location argument.
arguments->Add(new LiteralNode(
type_pos, Integer::ZoneHandle(Integer::New(type_pos))));
// Src value argument.
arguments->Add(new LiteralNode(type_pos, Instance::ZoneHandle()));
// Dst type name argument.
arguments->Add(new LiteralNode(type_pos, Symbols::Malformed()));
// Dst name argument.
arguments->Add(new LiteralNode(type_pos, Symbols::Empty()));
// Malformed type error or malbounded type error.
const Error& error = Error::Handle(type.error());
ASSERT(!error.IsNull());
arguments->Add(new LiteralNode(type_pos, String::ZoneHandle(
Symbols::New(error.ToErrorCString()))));
return MakeStaticCall(Symbols::TypeError(),
Library::PrivateCoreLibName(Symbols::ThrowNew()),
arguments);
}
AstNode* Parser::ThrowNoSuchMethodError(intptr_t call_pos,
const Class& cls,
const String& function_name,
ArgumentListNode* function_arguments,
InvocationMirror::Call im_call,
InvocationMirror::Type im_type,
Function* func) {
ArgumentListNode* arguments = new ArgumentListNode(call_pos);
// Object receiver.
// TODO(regis): For now, we pass a class literal of the unresolved
// method's owner, but this is not specified and will probably change.
Type& type = Type::ZoneHandle(
Type::New(cls, TypeArguments::Handle(), call_pos, Heap::kOld));
type ^= ClassFinalizer::FinalizeType(
current_class(), type, ClassFinalizer::kCanonicalize);
arguments->Add(new LiteralNode(call_pos, type));
// String memberName.
arguments->Add(new LiteralNode(
call_pos, String::ZoneHandle(Symbols::New(function_name))));
// Smi invocation_type.
if (cls.IsTopLevel()) {
ASSERT(im_call == InvocationMirror::kStatic ||
im_call == InvocationMirror::kTopLevel);
im_call = InvocationMirror::kTopLevel;
}
arguments->Add(new LiteralNode(call_pos, Smi::ZoneHandle(
Smi::New(InvocationMirror::EncodeType(im_call, im_type)))));
// List arguments.
if (function_arguments == NULL) {
arguments->Add(new LiteralNode(call_pos, Array::ZoneHandle()));
} else {
ArrayNode* array = new ArrayNode(call_pos,
Type::ZoneHandle(Type::ArrayType()),
function_arguments->nodes());
arguments->Add(array);
}
// List argumentNames.
if (function_arguments == NULL) {
arguments->Add(new LiteralNode(call_pos, Array::ZoneHandle()));
} else {
arguments->Add(new 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();
if (func != NULL) {
function = func->raw();
} else {
function = cls.LookupStaticFunction(function_name);
}
Array& array = Array::ZoneHandle();
if (!function.IsNull()) {
// 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(function.UserVisibleFormalParameters()));
}
arguments->Add(new LiteralNode(call_pos, array));
return MakeStaticCall(Symbols::NoSuchMethodError(),
Library::PrivateCoreLibName(Symbols::ThrowNew()),
arguments);
}
AstNode* Parser::ParseBinaryExpr(int min_preced) {
TRACE_PARSER("ParseBinaryExpr");
ASSERT(min_preced >= Token::Precedence(Token::kOR));
AstNode* left_operand = ParseUnaryExpr();
if (left_operand->IsPrimaryNode() &&
(left_operand->AsPrimaryNode()->IsSuper())) {
ErrorMsg(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 intptr_t 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 intptr_t type_pos = TokenPos();
const AbstractType& type = AbstractType::ZoneHandle(
ParseType(ClassFinalizer::kCanonicalize));
if (!type.IsInstantiated() &&
(current_block_->scope->function_level() > 0)) {
// Make sure that the instantiator is captured.
CaptureInstantiator();
}
right_operand = new TypeNode(type_pos, type);
// In production mode, the type may be malformed.
// In checked mode, the type may be malformed or malbounded.
if (((op_kind == Token::kIS) || (op_kind == Token::kISNOT) ||
(op_kind == Token::kAS)) &&
type.IsMalformedOrMalbounded()) {
// Note that a type error is thrown even if the tested value is null
// in a type test or in a type cast.
return ThrowTypeError(type_pos, type);
}
}
if (Token::IsRelationalOperator(op_kind)
|| Token::IsTypeTestOperator(op_kind)
|| Token::IsTypeCastOperator(op_kind)
|| Token::IsEqualityOperator(op_kind)) {
if (Token::IsTypeTestOperator(op_kind) ||
Token::IsTypeCastOperator(op_kind)) {
if (!right_operand->AsTypeNode()->type().IsInstantiated()) {
EnsureExpressionTemp();
}
}
left_operand = new 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::ParseExprList() {
TRACE_PARSER("ParseExprList");
AstNode* expressions = ParseExpr(kAllowConst, kConsumeCascades);
if (CurrentToken() == Token::kCOMMA) {
// Collect comma-separated expressions in a non scope owning sequence node.
SequenceNode* list = new SequenceNode(TokenPos(), NULL);
list->Add(expressions);
while (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades);
list->Add(expr);
}
expressions = list;
}
return expressions;
}
void Parser::EnsureExpressionTemp() {
// Temporary used later by the flow_graph_builder.
parsed_function()->EnsureExpressionTemp();
}
void Parser::EnsureSavedCurrentContext() {
// Used later by the flow_graph_builder to save current context.
if (!parsed_function()->has_saved_current_context_var()) {
LocalVariable* temp =
new LocalVariable(current_function().token_pos(),
Symbols::SavedCurrentContextVar(),
Type::ZoneHandle(Type::DynamicType()));
ASSERT(temp != NULL);
parsed_function()->set_saved_current_context_var(temp);
}
}
LocalVariable* Parser::CreateTempConstVariable(intptr_t token_pos,
const char* s) {
char name[64];
OS::SNPrint(name, 64, ":%s%" Pd, s, token_pos);
LocalVariable* temp =
new LocalVariable(token_pos,
String::ZoneHandle(Symbols::New(name)),
Type::ZoneHandle(Type::DynamicType()));
temp->set_is_final();
current_block_->scope->AddVariable(temp);
return temp;
}
// TODO(srdjan): Implement other optimizations.
AstNode* Parser::OptimizeBinaryOpNode(intptr_t 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(
Double::NewCanonical((left_double / right_double)));
return new LiteralNode(op_pos, dbl_obj);
}
}
}
if ((binary_op == Token::kAND) || (binary_op == Token::kOR)) {
EnsureExpressionTemp();
}
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 ((rhs_literal != NULL) &&
(rhs_literal->literal().IsSmi() || rhs_literal->literal().IsMint())) {
const int64_t val = Integer::Cast(rhs_literal->literal()).AsInt64Value();
if ((0 <= val) && (Utils::IsUint(32, val))) {
if (lhs->IsBinaryOpNode() &&
(lhs->AsBinaryOpNode()->kind() == Token::kSHL)) {
// Merge SHL and BIT_AND into one "SHL with mask" node.
BinaryOpNode* old = lhs->AsBinaryOpNode();
BinaryOpWithMask32Node* binop = new BinaryOpWithMask32Node(
old->token_pos(), old->kind(), old->left(), old->right(), val);
return binop;
}
}
}
}
return new BinaryOpNode(op_pos, binary_op, lhs, rhs);
}
AstNode* Parser::ExpandAssignableOp(intptr_t 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 BinaryOpNode(op_pos, Token::kADD, lhs, rhs);
case Token::kASSIGN_SUB:
return new BinaryOpNode(op_pos, Token::kSUB, lhs, rhs);
case Token::kASSIGN_MUL:
return new BinaryOpNode(op_pos, Token::kMUL, lhs, rhs);
case Token::kASSIGN_TRUNCDIV:
return new BinaryOpNode(op_pos, Token::kTRUNCDIV, lhs, rhs);
case Token::kASSIGN_DIV:
return new BinaryOpNode(op_pos, Token::kDIV, lhs, rhs);
case Token::kASSIGN_MOD:
return new BinaryOpNode(op_pos, Token::kMOD, lhs, rhs);
case Token::kASSIGN_SHR:
return new BinaryOpNode(op_pos, Token::kSHR, lhs, rhs);
case Token::kASSIGN_SHL:
return new BinaryOpNode(op_pos, Token::kSHL, lhs, rhs);
case Token::kASSIGN_OR:
return new BinaryOpNode(op_pos, Token::kBIT_OR, lhs, rhs);
case Token::kASSIGN_AND:
return new BinaryOpNode(op_pos, Token::kBIT_AND, lhs, rhs);
case Token::kASSIGN_XOR:
return new BinaryOpNode(op_pos, Token::kBIT_XOR, lhs, rhs);
default:
ErrorMsg(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.
AstNode* Parser::FoldConstExpr(intptr_t expr_pos, AstNode* expr) {
if (expr->IsLiteralNode()) {
return expr;
}
if (expr->EvalConstExpr() == NULL) {
ErrorMsg(expr_pos, "expression is not a valid compile-time constant");
}
return new LiteralNode(expr_pos, EvaluateConstExpr(expr_pos, expr));
}
LetNode* Parser::PrepareCompoundAssignmentNodes(AstNode** expr) {
AstNode* node = *expr;
intptr_t token_pos = node->token_pos();
LetNode* result = new 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 LoadLocalNode(token_pos, t0);
}
if (!IsSimpleLocalOrLiteralNode(load_indexed->index_expr())) {
LocalVariable* t1 = result->AddInitializer(
load_indexed->index_expr());
index = new LoadLocalNode(token_pos, t1);
}
*expr = new 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 LoadLocalNode(token_pos, t0);
}
*expr = new InstanceGetterNode(token_pos,
receiver,
getter->field_name());
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, intptr_t end_pos) {
ASSERT(expr->token_pos() >= 0);
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,
intptr_t left_pos) {
AstNode* result = original->MakeAssignmentNode(rhs);
if (result == NULL) {
String& name = String::ZoneHandle();
const Class* target_cls = &current_class();
if (original->IsTypeNode()) {
name = Symbols::New(original->AsTypeNode()->TypeName());
} else if (original->IsLoadStaticFieldNode()) {
name = original->AsLoadStaticFieldNode()->field().name();
target_cls =
&Class::Handle(original->AsLoadStaticFieldNode()->field().owner());
} else if ((left_ident != NULL) &&
(original->IsLiteralNode() ||
original->IsLoadLocalNode())) {
name = left_ident->raw();
}
if (name.IsNull()) {
ErrorMsg(left_pos, "expression is not assignable");
}
result = ThrowNoSuchMethodError(original->token_pos(),
*target_cls,
name,
NULL, // No arguments.
InvocationMirror::kStatic,
InvocationMirror::kSetter,
NULL); // No existing function.
} else if (result->IsStoreIndexedNode() ||
result->IsInstanceSetterNode() ||
result->IsStaticSetterNode() ||
result->IsStoreStaticFieldNode() ||
result->IsStoreLocalNode()) {
// Ensure that the expression temp is allocated for nodes that may need it.
EnsureExpressionTemp();
}
return result;
}
AstNode* Parser::ParseCascades(AstNode* expr) {
intptr_t cascade_pos = TokenPos();
LetNode* cascade = new LetNode(cascade_pos);
LocalVariable* cascade_receiver_var = cascade->AddInitializer(expr);
while (CurrentToken() == Token::kCASCADE) {
cascade_pos = TokenPos();
LoadLocalNode* load_cascade_receiver =
new 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 {
ErrorMsg("identifier or [ expected after ..");
}
String* expr_ident =
Token::IsIdentifier(CurrentToken()) ? CurrentLiteral() : NULL;
const intptr_t 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 intptr_t 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);
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 LoadLocalNode(cascade_pos, cascade_receiver_var));
return cascade;
}
// Convert loading of a static const field into a literal node.
static AstNode* LiteralIfStaticConst(AstNode* expr) {
if (expr->IsLoadStaticFieldNode()) {
const Field& field = expr->AsLoadStaticFieldNode()->field();
if (field.is_const()) {
ASSERT(field.value() != Object::sentinel().raw());
ASSERT(field.value() != Object::transition_sentinel().raw());
return new LiteralNode(expr->token_pos(),
Instance::ZoneHandle(field.value()));
}
}
return expr;
}
AstNode* Parser::ParseExpr(bool require_compiletime_const,
bool consume_cascades) {
TRACE_PARSER("ParseExpr");
String* expr_ident =
Token::IsIdentifier(CurrentToken()) ? CurrentLiteral() : NULL;
const intptr_t expr_pos = TokenPos();
if (CurrentToken() == Token::kTHROW) {
ConsumeToken();
if (CurrentToken() == Token::kSEMICOLON) {
ErrorMsg("expression expected after throw");
}
AstNode* expr = ParseExpr(require_compiletime_const, consume_cascades);
return new ThrowNode(expr_pos, expr, NULL);
}
AstNode* expr = ParseConditionalExpr();
if (!Token::IsAssignmentOperator(CurrentToken())) {
if ((CurrentToken() == Token::kCASCADE) && consume_cascades) {
return ParseCascades(expr);
}
expr = LiteralIfStaticConst(expr);
if (require_compiletime_const) {
expr = FoldConstExpr(expr_pos, expr);
}
return expr;
}
// Assignment expressions.
if (!IsLegalAssignableSyntax(expr, TokenPos())) {
ErrorMsg(expr_pos, "expression is not assignable");
}
const Token::Kind assignment_op = CurrentToken();
const intptr_t assignment_pos = TokenPos();
ConsumeToken();
const intptr_t right_expr_pos = TokenPos();
if (require_compiletime_const && (assignment_op != Token::kASSIGN)) {
ErrorMsg(right_expr_pos, "expression is not a valid compile-time constant");
}
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);
ASSERT(assign_expr != NULL);
let_expr->AddNode(assign_expr);
return let_expr;
} else {
AstNode* assigned_value = LiteralIfStaticConst(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");
intptr_t expr_pos = TokenPos();
AstNode* expr = ParseExpr(kRequireConst, kNoCascades);
if (!expr->IsLiteralNode()) {
ErrorMsg(expr_pos, "expression must be a compile-time constant");
}
return expr->AsLiteralNode();
}
AstNode* Parser::ParseConditionalExpr() {
TRACE_PARSER("ParseConditionalExpr");
const intptr_t expr_pos = TokenPos();
AstNode* expr = ParseBinaryExpr(Token::Precedence(Token::kOR));
if (CurrentToken() == Token::kCONDITIONAL) {
EnsureExpressionTemp();
ConsumeToken();
AstNode* expr1 = ParseExpr(kAllowConst, kNoCascades);
ExpectToken(Token::kCOLON);
AstNode* expr2 = ParseExpr(kAllowConst, kNoCascades);
expr = new ConditionalExprNode(expr_pos, expr, expr1, expr2);
}
return expr;
}
AstNode* Parser::ParseUnaryExpr() {
TRACE_PARSER("ParseUnaryExpr");
AstNode* expr = NULL;
const intptr_t op_pos = TokenPos();
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 intptr_t expr_pos = TokenPos();
expr = ParseUnaryExpr();
if (!IsLegalAssignableSyntax(expr, TokenPos())) {
ErrorMsg(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 BinaryOpNode(
op_pos,
binary_op,
expr,
new LiteralNode(op_pos, Smi::ZoneHandle(Smi::New(1))));
AstNode* store = CreateAssignmentNode(expr, add, expr_ident, expr_pos);
ASSERT(store != NULL);
let_expr->AddNode(store);
expr = let_expr;
} else {
expr = ParsePostfixExpr();
}
return expr;
}
ArgumentListNode* Parser::ParseActualParameters(
ArgumentListNode* implicit_arguments,
bool require_const) {
TRACE_PARSER("ParseActualParameters");
ASSERT(CurrentToken() == Token::kLPAREN);
const bool saved_mode = SetAllowFunctionLiterals(true);
ArgumentListNode* arguments;
if (implicit_arguments == NULL) {
arguments = new ArgumentListNode(TokenPos());
} else {
arguments = implicit_arguments;
}
const GrowableObjectArray& names =
GrowableObjectArray::Handle(GrowableObjectArray::New(Heap::kOld));
bool named_argument_seen = false;
if (LookaheadToken(1) != Token::kRPAREN) {
String& arg_name = String::Handle();
do {
ASSERT((CurrentToken() == Token::kLPAREN) ||
(CurrentToken() == Token::kCOMMA));
ConsumeToken();
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)) {
ErrorMsg("duplicate named argument");
}
}
names.Add(*CurrentLiteral());
ConsumeToken(); // ident.
ConsumeToken(); // colon.
} else if (named_argument_seen) {
ErrorMsg("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(Array::MakeArray(names)));
}
return arguments;
}
AstNode* Parser::ParseStaticCall(const Class& cls,
const String& func_name,
intptr_t ident_pos) {
TRACE_PARSER("ParseStaticCall");
const intptr_t call_pos = TokenPos();
ASSERT(CurrentToken() == Token::kLPAREN);
ArgumentListNode* arguments = ParseActualParameters(NULL, kAllowConst);
const int num_arguments = arguments->length();
const Function& func = Function::ZoneHandle(
Resolver::ResolveStatic(cls,
func_name,
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(cls.LookupStaticField(func_name));
Function& func = Function::ZoneHandle();
if (field.IsNull()) {
// No field, check if we have an explicit getter function.
const String& getter_name =
String::ZoneHandle(Field::GetterName(func_name));
const int kNumArguments = 0; // no arguments.
func = Resolver::ResolveStatic(cls,
getter_name,
kNumArguments,
Object::empty_array());
if (!func.IsNull()) {
ASSERT(func.kind() != RawFunction::kImplicitStaticFinalGetter);
EnsureSavedCurrentContext();
closure = new StaticGetterNode(call_pos,
NULL,
false,
Class::ZoneHandle(cls.raw()),
func_name);
return new ClosureCallNode(call_pos, closure, arguments);
}
} else {
EnsureSavedCurrentContext();
closure = GenerateStaticFieldLookup(field, call_pos);
return new ClosureCallNode(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.
} else if (cls.IsTopLevel() &&
(cls.library() == Library::CoreLibrary()) &&
(func.name() == Symbols::Identical().raw())) {
// This is the predefined toplevel function identical(a,b). Create
// a comparison node instead.
ASSERT(num_arguments == 2);
return new ComparisonNode(ident_pos,
Token::kEQ_STRICT,
arguments->NodeAt(0),
arguments->NodeAt(1));
}
return new StaticCallNode(call_pos, func, arguments);
}
AstNode* Parser::ParseInstanceCall(AstNode* receiver, const String& func_name) {
TRACE_PARSER("ParseInstanceCall");
const intptr_t call_pos = TokenPos();
CheckToken(Token::kLPAREN);
ArgumentListNode* arguments = ParseActualParameters(NULL, kAllowConst);
return new InstanceCallNode(call_pos, receiver, func_name, arguments);
}
AstNode* Parser::ParseClosureCall(AstNode* closure) {
TRACE_PARSER("ParseClosureCall");
const intptr_t call_pos = TokenPos();
ASSERT(CurrentToken() == Token::kLPAREN);
EnsureSavedCurrentContext();
ArgumentListNode* arguments = ParseActualParameters(NULL, kAllowConst);
return new ClosureCallNode(call_pos, closure, arguments);
}
AstNode* Parser::GenerateStaticFieldLookup(const Field& field,
intptr_t 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(field.owner());
const String& field_name = String::ZoneHandle(field.name());
const String& getter_name = String::Handle(Field::GetterName(field_name));
const Function& getter =
Function::Handle(field_owner.LookupStaticFunction(getter_name));
// Never load field directly if there is a getter (deterministic AST).
if (getter.IsNull() || field.is_const()) {
return new LoadStaticFieldNode(ident_pos, Field::ZoneHandle(field.raw()));
} else {
ASSERT(getter.kind() == RawFunction::kImplicitStaticFinalGetter);
return new StaticGetterNode(ident_pos,
NULL, // Receiver.
false, // is_super_getter.
field_owner,
field_name);
}
}
AstNode* Parser::ParseStaticFieldAccess(const Class& cls,
const String& field_name,
intptr_t ident_pos,
bool consume_cascades) {
TRACE_PARSER("ParseStaticFieldAccess");
AstNode* access = NULL;
const Field& field = Field::ZoneHandle(cls.LookupStaticField(field_name));
Function& func = Function::ZoneHandle();
if (field.IsNull()) {
// No field, check if we have an explicit getter function.
const String& getter_name =
String::ZoneHandle(Field::GetterName(field_name));
const int kNumArguments = 0; // no arguments.
func = Resolver::ResolveStatic(cls,
getter_name,
kNumArguments,
Object::empty_array());
if (func.IsNull()) {
// 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 StaticGetterNode(ident_pos,
NULL,
false,
Class::ZoneHandle(cls.raw()),
field_name);
}
} else {
ASSERT(func.kind() != RawFunction::kImplicitStaticFinalGetter);
access = new StaticGetterNode(ident_pos,
NULL,
false,
Class::ZoneHandle(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.)
// In metadata, an unresolved identifier cannot be a compile-time constant.
String& name = String::CheckedZoneHandle(primary->primary().raw());
if (parsing_metadata_) {
ErrorMsg(primary->token_pos(),
"unresolved identifier '%s' is not a compile-time constant",
name.ToCString());
}
if (current_function().is_static() ||
current_function().IsInFactoryScope()) {
return new StaticGetterNode(primary->token_pos(),
NULL, // No receiver.
false, // Not a super getter.
Class::ZoneHandle(current_class().raw()),
name);
} 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());
AstNode* closure = NULL;
const Function& func =
Function::CheckedZoneHandle(primary->primary().raw());
const String& funcname = String::ZoneHandle(func.name());
if (func.is_static()) {
// Static function access.
closure = CreateImplicitClosureNode(func, primary->token_pos(), NULL);
} else {
// Instance function access.
if (parsing_metadata_) {
ErrorMsg(primary->token_pos(),
"cannot access instance method '%s' from metadata",
funcname.ToCString());
}
if (current_function().is_static() ||
current_function().IsInFactoryScope()) {
ErrorMsg(primary->token_pos(),
"cannot access instance method '%s' from static method",
funcname.ToCString());
}
AstNode* receiver = LoadReceiver(primary->token_pos());
closure = CallGetter(primary->token_pos(), receiver, funcname);
}
return closure;
}
AstNode* Parser::ParseSelectors(AstNode* primary, bool is_cascade) {
AstNode* left = primary;
while (true) {
AstNode* selector = NULL;
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
if (left->IsPrimaryNode()) {
PrimaryNode* primary_node = left->AsPrimaryNode();
const intptr_t primary_pos = primary_node->token_pos();
if (primary_node->primary().IsFunction()) {
left = LoadClosure(primary_node);
} else if (primary_node->primary().IsTypeParameter()) {
if (current_function().is_static()) {
const String& name = String::ZoneHandle(
TypeParameter::Cast(primary_node->primary()).name());
ErrorMsg(primary_pos,
"cannot access type parameter '%s' from static function",
name.ToCString());
}
if (current_block_->scope->function_level() > 0) {
// Make sure that the instantiator is captured.
CaptureInstantiator();
}
TypeParameter& type_parameter = TypeParameter::ZoneHandle();
type_parameter ^= ClassFinalizer::FinalizeType(
current_class(),
TypeParameter::Cast(primary_node->primary()),
ClassFinalizer::kCanonicalize);
ASSERT(!type_parameter.IsMalformed());
left = new TypeNode(primary->token_pos(), type_parameter);
} else {
// Super field access handled in ParseSuperFieldAccess(),
// super calls handled in ParseSuperCall().
ASSERT(!primary_node->IsSuper());
left = LoadFieldIfUnresolved(left);
}
}
const intptr_t ident_pos = TokenPos();
String* ident = ExpectIdentifier("identifier expected");
if (CurrentToken() == Token::kLPAREN) {
// Identifier followed by a opening paren: method call.
if (left->IsPrimaryNode() &&
left->AsPrimaryNode()->primary().IsClass()) {
// Static method call prefixed with class name.
const Class& cls = Class::Cast(left->AsPrimaryNode()->primary());
selector = ParseStaticCall(cls, *ident, ident_pos);
} else {
selector = ParseInstanceCall(left, *ident);
}
} else {
// Field access.
Class& cls = Class::Handle();
if (left->IsPrimaryNode()) {
PrimaryNode* primary_node = left->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();
}
}
if (cls.IsNull()) {
// Instance field access.
selector = CallGetter(ident_pos, left, *ident);
} else {
// Static field access.
selector =
ParseStaticFieldAccess(cls, *ident, ident_pos, !is_cascade);
}
}
} else if (CurrentToken() == Token::kLBRACK) {
// Super index operator handled in ParseSuperOperator().
ASSERT(!left->IsPrimaryNode() || !left->AsPrimaryNode()->IsSuper());
const intptr_t 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 intptr_t 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(
Type::New(type_class, TypeArguments::Handle(),
primary_pos, Heap::kOld));
type ^= ClassFinalizer::FinalizeType(
current_class(), type, ClassFinalizer::kCanonicalize);
// Type may be malbounded, but not malformed.
ASSERT(!type.IsMalformed());
array = new TypeNode(primary_pos, type);
} else if (primary_node->primary().IsTypeParameter()) {
if (current_function().is_static()) {
const String& name = String::ZoneHandle(
TypeParameter::Cast(primary_node->primary()).name());
ErrorMsg(primary_pos,
"cannot access type parameter '%s' from static function",
name.ToCString());
}
if (current_block_->scope->function_level() > 0) {
// Make sure that the instantiator is captured.
CaptureInstantiator();
}
TypeParameter& type_parameter = TypeParameter::ZoneHandle();
type_parameter ^= ClassFinalizer::FinalizeType(
current_class(),
TypeParameter::Cast(primary_node->primary()),
ClassFinalizer::kCanonicalize);
ASSERT(!type_parameter.IsMalformed());
array = new TypeNode(primary_pos, type_parameter);
} else {
UNREACHABLE(); // Internal parser error.
}
}
selector = new LoadIndexedNode(bracket_pos,
array,
index,
Class::ZoneHandle());
} else if (CurrentToken() == Token::kLPAREN) {
if (left->IsPrimaryNode()) {
PrimaryNode* primary_node = left->AsPrimaryNode();
const intptr_t 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(func.name());
if (func.is_static()) {
// Parse static function call.
Class& cls = Class::Handle(func.Owner());
selector = ParseStaticCall(cls, func_name, primary_pos);
} else {
// Dynamic function call on implicit "this" parameter.
if (current_function().is_static()) {
ErrorMsg(primary_pos,
"cannot access instance method '%s' "
"from static function",
func_name.ToCString());
}
selector = ParseInstanceCall(LoadReceiver(primary_pos), func_name);
}
} else if (primary_node->primary().IsString()) {
// Primary is an unresolved name.
if (primary_node->IsSuper()) {
ErrorMsg(primary_pos, "illegal use of super");
}
String& name = String::CheckedZoneHandle(
primary_node->primary().raw());
if (current_function().is_static()) {
selector = ThrowNoSuchMethodError(primary_pos,
current_class(),
name,
NULL, // No arguments.
InvocationMirror::kStatic,
InvocationMirror::kMethod,
NULL); // No existing function.
} else {
// Treat as call to unresolved (instance) method.
selector = ParseInstanceCall(LoadReceiver(primary_pos), name);
}
} else if (primary_node->primary().IsTypeParameter()) {
const String& name = String::ZoneHandle(
TypeParameter::Cast(primary_node->primary()).name());
if (current_function().is_static()) {
// Treat as this.T(), because T is in scope.
ErrorMsg(primary_pos,
"cannot access type parameter '%s' from static function",
name.ToCString());
} else {
// Treat as call to unresolved (instance) method.
selector = ParseInstanceCall(LoadReceiver(primary_pos), name);
}
} else if (primary_node->primary().IsClass()) {
const Class& type_class = Class::Cast(primary_node->primary());
AbstractType& type = Type::ZoneHandle(Type::New(
type_class, TypeArguments::Handle(), primary_pos));
type ^= ClassFinalizer::FinalizeType(
current_class(), type, ClassFinalizer::kCanonicalize);
// Type may be malbounded, but not malformed.
ASSERT(!type.IsMalformed());
selector = new TypeNode(primary_pos, type);
} else {
UNREACHABLE(); // Internal parser error.
}
} else {
// Left is not a primary node; this must be a closure call.
AstNode* closure = left;
selector = ParseClosureCall(closure);
}
} else {
// No (more) selectors to parse.
left = LoadFieldIfUnresolved(left);
if (left->IsPrimaryNode()) {
PrimaryNode* primary_node = left->AsPrimaryNode();
const intptr_t 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(Type::New(
type_class, TypeArguments::Handle(), primary_pos));
type = ClassFinalizer::FinalizeType(
current_class(), type, ClassFinalizer::kCanonicalize);
// Type may be malbounded, but not malformed.
ASSERT(!type.IsMalformed());
left = new TypeNode(primary_pos, type);
} else if (primary_node->primary().IsTypeParameter()) {
if (current_function().is_static()) {
const String& name = String::ZoneHandle(
TypeParameter::Cast(primary_node->primary()).name());
ErrorMsg(primary_pos,
"cannot access type parameter '%s' from static function",
name.ToCString());
}
if (current_block_->scope->function_level() > 0) {
// Make sure that the instantiator is captured.
CaptureInstantiator();
}
TypeParameter& type_parameter = TypeParameter::ZoneHandle();
type_parameter ^= ClassFinalizer::FinalizeType(
current_class(),
TypeParameter::Cast(primary_node->primary()),
ClassFinalizer::kCanonicalize);
ASSERT(!type_parameter.IsMalformed());
left = new TypeNode(primary_pos, type_parameter);
} else if (primary_node->IsSuper()) {
// Return "super" to handle unary super operator calls,
// or to report illegal use of "super" otherwise.
left = primary_node;
} else {
UNREACHABLE(); // Internal parser error.
}
}
// Done parsing selectors.
return left;
}
ASSERT(selector != NULL);
left = selector;
}
}
AstNode* Parser::ParsePostfixExpr() {
TRACE_PARSER("ParsePostfixExpr");
String* expr_ident =
Token::IsIdentifier(CurrentToken()) ? CurrentLiteral() : NULL;
const intptr_t expr_pos = TokenPos();
AstNode* expr = ParsePrimary();
expr = ParseSelectors(expr, false);
if (IsIncrementOperator(CurrentToken())) {
TRACE_PARSER("IncrementOperator");
if (!IsLegalAssignableSyntax(expr, TokenPos())) {
ErrorMsg(expr_pos, "expression is not assignable");
}
Token::Kind incr_op = CurrentToken();
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 BinaryOpNode(
expr_pos,
binary_op,
new LoadLocalNode(expr_pos, temp),
new LiteralNode(expr_pos, Smi::ZoneHandle(Smi::New(1))));
AstNode* store = CreateAssignmentNode(expr, add, expr_ident, expr_pos);
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 LoadLocalNode(expr_pos, temp));
return let_expr;
}
return expr;
}
// Resolve the given type and its type arguments from the given scope class
// according to the given type finalization mode.
// If the given scope class is null, use the current library, but do not try to
// resolve type parameters.
// Not all involved type classes may get resolved yet, but at least the type
// parameters of the given class will get resolved, thereby relieving the class
// finalizer from resolving type parameters out of context.
void Parser::ResolveTypeFromClass(const Class& scope_class,
ClassFinalizer::FinalizationKind finalization,
AbstractType* type) {
ASSERT(finalization >= ClassFinalizer::kResolveTypeParameters);
ASSERT(type != NULL);
if (type->IsResolved()) {
return;
}
// Resolve class.
if (!type->HasResolvedTypeClass()) {
const UnresolvedClass& unresolved_class =
UnresolvedClass::Handle(type->unresolved_class());
const String& unresolved_class_name =
String::Handle(unresolved_class.ident());
Class& resolved_type_class = Class::Handle();
if (unresolved_class.library_prefix() == LibraryPrefix::null()) {
if (!scope_class.IsNull()) {
// First check if the type is a type parameter of the given scope class.
const TypeParameter& type_parameter = TypeParameter::Handle(
scope_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()) {
ASSERT(scope_class.raw() == current_class().raw());
*type = ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(), // No previous error.
script_,
type->token_pos(),
"type parameter '%s' cannot be referenced "
"from static member",
String::Handle(type_parameter.name()).ToCString());
return;
}
// A type parameter cannot be parameterized, so make the type
// malformed if type arguments have previously been parsed.
if (!AbstractTypeArguments::Handle(type->arguments()).IsNull()) {
*type = ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(), // No previous error.
script_,
type_parameter.token_pos(),
"type parameter '%s' cannot be parameterized",
String::Handle(type_parameter.name()).ToCString());
return;
}
*type = type_parameter.raw();
return;
}
}
// The referenced class may not have been parsed yet. It would be wrong
// to resolve it too early to an imported class of the same name.
if (finalization > ClassFinalizer::kResolveTypeParameters) {
// Resolve classname in the scope of the current library.
resolved_type_class = ResolveClassInCurrentLibraryScope(
unresolved_class_name);
}
} else {
LibraryPrefix& lib_prefix =
LibraryPrefix::Handle(unresolved_class.library_prefix());
// Resolve class name in the scope of the library prefix.
resolved_type_class =
ResolveClassInPrefixScope(lib_prefix, unresolved_class_name);
}
// At this point, we can only have a parameterized_type.
const Type& parameterized_type = Type::Cast(*type);
if (!resolved_type_class.IsNull()) {
// Replace unresolved class with resolved type class.
parameterized_type.set_type_class(resolved_type_class);
} else if (finalization >= ClassFinalizer::kCanonicalize) {
ClassFinalizer::FinalizeMalformedType(
Error::Handle(), // No previous error.
script_,
parameterized_type,
"type '%s' is not loaded",
String::Handle(parameterized_type.UserVisibleName()).ToCString());
return;
}
}
// Resolve type arguments, if any.
const AbstractTypeArguments& arguments =
AbstractTypeArguments::Handle(type->arguments());
if (!arguments.IsNull()) {
const intptr_t num_arguments = arguments.Length();
for (intptr_t i = 0; i < num_arguments; i++) {
AbstractType& type_argument = AbstractType::Handle(arguments.TypeAt(i));
ResolveTypeFromClass(scope_class, finalization, &type_argument);
arguments.SetTypeAt(i, type_argument);
}
}
}
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(intptr_t 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 (parsing_metadata_) {
ErrorMsg(field_pos,
"cannot access instance field '%s' from metadata",
field_name.ToCString());
}
if (current_function().is_static()) {
ErrorMsg(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());
TypeArguments& type_arguments = TypeArguments::Handle();
if (cls.NumTypeParameters() > 0) {
type_arguments = cls.type_parameters();
}
AbstractType& type = AbstractType::ZoneHandle(
Type::New(cls, type_arguments, cls.token_pos()));
if (cls.is_type_finalized()) {
type ^= ClassFinalizer::FinalizeType(
cls, type, ClassFinalizer::kCanonicalizeWellFormed);
// Note that the receiver type may now be a malbounded type.
}
return &type;
}
bool Parser::IsInstantiatorRequired() const {
ASSERT(!current_function().IsNull());
if (current_function().is_static() &&
!current_function().IsInFactoryScope()) {
return false;
}
return current_class().NumTypeParameters() > 0;
}
RawInstance* Parser::TryCanonicalize(const Instance& instance,
intptr_t token_pos) {
if (instance.IsNull()) {
return instance.raw();
}
const char* error_str = NULL;
Instance& result =
Instance::Handle(instance.CheckAndCanonicalize(&error_str));
if (result.IsNull()) {
ErrorMsg(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.
AstNode* Parser::RunStaticFieldInitializer(const Field& field,
intptr_t field_ref_pos) {
ASSERT(field.is_static());
const Class& field_owner = Class::ZoneHandle(field.owner());
const String& field_name = String::ZoneHandle(field.name());
const String& getter_name = String::Handle(Field::GetterName(field_name));
const Function& getter =
Function::Handle(field_owner.LookupStaticFunction(getter_name));
const Instance& value = Instance::Handle(field.value());
if (value.raw() == Object::transition_sentinel().raw()) {
if (field.is_const()) {
ErrorMsg("circular dependency while initializing static field '%s'",
field_name.ToCString());
} else {
// The implicit static getter will throw the exception if necessary.
return new StaticGetterNode(field_ref_pos,
NULL,
false,
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()) {
field.set_value(Object::transition_sentinel());
const int kNumArguments = 0; // no arguments.
const Function& func =
Function::Handle(Resolver::ResolveStatic(field_owner,
getter_name,
kNumArguments,
Object::empty_array()));
ASSERT(!func.IsNull());
ASSERT(func.kind() == RawFunction::kImplicitStaticFinalGetter);
Object& const_value = Object::Handle(
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.set_value(Object::null_instance());
// It is a compile-time error if evaluation of a compile-time constant
// would raise an exception.
AppendErrorMsg(error, field_ref_pos,
"error initializing const field '%s'",
String::Handle(field.name()).ToCString());
} else {
isolate()->long_jump_base()->Jump(1, error);
}
}
ASSERT(const_value.IsNull() || const_value.IsInstance());
Instance& instance = Instance::Handle();
instance ^= const_value.raw();
instance = TryCanonicalize(instance, field_ref_pos);
field.set_value(instance);
return NULL; // Constant
} else {
return new StaticGetterNode(field_ref_pos,
NULL,
false,
field_owner,
field_name);
}
}
if (getter.IsNull() ||
(getter.kind() == RawFunction::kImplicitStaticFinalGetter)) {
return NULL;
}
ASSERT(getter.kind() == RawFunction::kImplicitGetter);
return new StaticGetterNode(field_ref_pos,
NULL,
false,
field_owner,
field_name);
}
RawObject* Parser::EvaluateConstConstructorCall(
const Class& type_class,
const AbstractTypeArguments& type_arguments,
const Function& constructor,
ArgumentListNode* arguments) {
// Factories have one extra argument: the type arguments.
// Constructors have 2 extra arguments: rcvr and construction phase.
const int kNumExtraArgs = constructor.IsFactory() ? 1 : 2;
const int num_arguments = arguments->length() + kNumExtraArgs;
const Array& arg_values = Array::Handle(Array::New(num_arguments));
Instance& instance = Instance::Handle();
if (!constructor.IsFactory()) {
instance = Instance::New(type_class, Heap::kOld);
if (!type_arguments.IsNull()) {
if (!type_arguments.IsInstantiated()) {
ErrorMsg("type must be constant in const constructor");
}
instance.SetTypeArguments(
AbstractTypeArguments::Handle(type_arguments.Canonicalize()));
}
arg_values.SetAt(0, instance);
arg_values.SetAt(1, Smi::Handle(Smi::New(Function::kCtorPhaseAll)));
} 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(ArgumentsDescriptor::New(num_arguments,
arguments->names()));
const Object& result =
Object::Handle(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. Unless we are evaluating metadata, in which case there
// is no current function.
if (!parsing_metadata_) {
current_function().SetIsOptimizable(false);
}
if (result.IsUnhandledException()) {
return result.raw();
} else {
isolate()->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(intptr_t ident_pos,
const String &ident,
AstNode** node) {
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) {
if (local->IsConst()) {
*node = new LiteralNode(ident_pos, *local->ConstValue());
} else {
*node = new LoadLocalNode(ident_pos, local);
}
}
return true;
}
// 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(isolate());
if (!current_class().IsMixinApplication()) {
cls = current_class().raw();
} else {
cls = parsed_function()->function().origin();
}
Function& func = Function::Handle(isolate(), Function::null());
Field& field = Field::Handle(isolate(), 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);
}
}
return true;
}
// Check if an instance/static function exists.
func = cls.LookupFunction(ident);
if (!func.IsNull() &&
(func.IsDynamicFunction() || func.IsStaticFunction())) {
if (node != NULL) {
*node = new PrimaryNode(ident_pos,
Function::ZoneHandle(isolate(), func.raw()));
}
return true;
}
// Now check if a getter/setter method exists for it in which case
// it is still a field.
func = cls.LookupGetterFunction(ident);
if (!func.IsNull()) {
if (func.IsDynamicFunction()) {
if (node != NULL) {
CheckInstanceFieldAccess(ident_pos, ident);
ASSERT(AbstractType::Handle(func.result_type()).IsResolved());
*node = CallGetter(ident_pos, LoadReceiver(ident_pos), ident);
}
return true;
} else if (func.IsStaticFunction()) {
if (node != NULL) {
ASSERT(AbstractType::Handle(func.result_type()).IsResolved());
// The static getter may later be changed into a dynamically
// resolved instance setter if no static setter can
// be found.
AstNode* receiver = NULL;
const bool kTestOnly = true;
if (!current_function().is_static() &&
(LookupReceiver(current_block_->scope, kTestOnly) != NULL)) {
receiver = LoadReceiver(ident_pos);
}
*node = new StaticGetterNode(ident_pos,
receiver,
false,
Class::ZoneHandle(isolate(), cls.raw()),
ident);
}
return true;
}
}
func = cls.LookupSetterFunction(ident);
if (!func.IsNull()) {
if (func.IsDynamicFunction()) {
if (node != NULL) {
// We create a getter node even though a getter doesn't exist as
// it could be followed by an assignment which will convert it to
// a setter node. If there is no assignment we will get an error
// when we try to invoke the getter.
CheckInstanceFieldAccess(ident_pos, ident);
ASSERT(AbstractType::Handle(func.result_type()).IsResolved());
*node = CallGetter(ident_pos, LoadReceiver(ident_pos), ident);
}
return true;
} else if (func.IsStaticFunction()) {
if (node != NULL) {
// We create a getter node even though a getter doesn't exist as
// it could be followed by an assignment which will convert it to
// a setter node. If there is no assignment we will get an error
// when we try to invoke the getter.
*node = new StaticGetterNode(ident_pos,
NULL,
false,
Class::ZoneHandle(isolate(), cls.raw()),
ident);
}
return true;
}
}
// Nothing found in scope of current class.
if (node != NULL) {
*node = NULL;
}
return false; // Not an unqualified identifier.
}
RawClass* Parser::ResolveClassInCurrentLibraryScope(const String& name) {
HANDLESCOPE(isolate());
const Object& obj = Object::Handle(library_.ResolveName(name));
if (obj.IsClass()) {
return Class::Cast(obj).raw();
}
return Class::null();
}
// 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(intptr_t ident_pos,
const String& ident) {
TRACE_PARSER("ResolveIdentInCurrentLibraryScope");
HANDLESCOPE(isolate());
const Object& obj = Object::Handle(library_.ResolveName(ident));
if (obj.IsClass()) {
const Class& cls = Class::Cast(obj);
return new PrimaryNode(ident_pos, Class::ZoneHandle(cls.raw()));
} else if (obj.IsField()) {
const Field& field = Field::Cast(obj);
ASSERT(field.is_static());
return GenerateStaticFieldLookup(field, ident_pos);
} else if (obj.IsFunction()) {
const Function& func = Function::Cast(obj);
ASSERT(func.is_static());
if (func.IsGetterFunction() || func.IsSetterFunction()) {
return new StaticGetterNode(ident_pos,
/* receiver */ NULL,
/* is_super_getter */ false,
Class::ZoneHandle(func.Owner()),
ident);
} else {
return new PrimaryNode(ident_pos, Function::ZoneHandle(func.raw()));
}
} else {
ASSERT(obj.IsNull() || obj.IsLibraryPrefix());
}
// Lexically unresolved primary identifiers are referenced by their name.
return new PrimaryNode(ident_pos, ident);
}
RawClass* Parser::ResolveClassInPrefixScope(const LibraryPrefix& prefix,
const String& name) {
HANDLESCOPE(isolate());
const Object& obj = Object::Handle(prefix.LookupObject(name));
if (obj.IsClass()) {
return Class::Cast(obj).raw();
}
return Class::null();
}
// 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(intptr_t ident_pos,
const LibraryPrefix& prefix,
const String& ident) {
TRACE_PARSER("ResolveIdentInPrefixScope");
HANDLESCOPE(isolate());
Object& obj = Object::Handle(prefix.LookupObject(ident));
if (obj.IsNull()) {
// Unresolved prefixed primary identifier.
String& qualified_name = String::ZoneHandle(prefix.name());
qualified_name = String::Concat(qualified_name, Symbols::Dot());
qualified_name = String::Concat(qualified_name, ident);
qualified_name = Symbols::New(qualified_name);
return new PrimaryNode(ident_pos, qualified_name);
} else if (obj.IsClass()) {
const Class& cls = Class::Cast(obj);
return new PrimaryNode(ident_pos, Class::ZoneHandle(cls.raw()));
} else if (obj.IsField()) {
const Field& field = Field::Cast(obj);
ASSERT(field.is_static());
return GenerateStaticFieldLookup(field, ident_pos);
} else if (obj.IsFunction()) {
const Function& func = Function::Cast(obj);
ASSERT(func.is_static());
if (func.IsGetterFunction() || func.IsSetterFunction()) {
return new StaticGetterNode(ident_pos,
/* receiver */ NULL,
/* is_super_getter */ false,
Class::ZoneHandle(func.Owner()),
ident);
} else {
return new PrimaryNode(ident_pos, Function::ZoneHandle(func.raw()));
}
}
// 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(intptr_t 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;
ResolveIdentInLocalScope(ident_pos, ident, &resolved);
if (resolved == NULL) {
// Check whether the identifier is a type parameter.
if (!current_class().IsNull()) {
TypeParameter& type_parameter = TypeParameter::ZoneHandle(
current_class().LookupTypeParameter(ident));
if (!type_parameter.IsNull()) {
if (current_block_->scope->function_level() > 0) {
// Make sure that the instantiator is captured.
CaptureInstantiator();
}
type_parameter ^= ClassFinalizer::FinalizeType(
current_class(), type_parameter, ClassFinalizer::kCanonicalize);
ASSERT(!type_parameter.IsMalformed());
return new 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 intptr_t 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 {
ErrorMsg(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(
Type::New(type_class, TypeArguments::Handle(), primary_pos));
type ^= ClassFinalizer::FinalizeType(
current_class(), type, ClassFinalizer::kCanonicalize);
// Type may be malbounded, but not malformed.
ASSERT(!type.IsMalformed());
resolved = new TypeNode(primary_pos, type);
}
}
return resolved;
}
// Parses type = [ident "."] ident ["<" type { "," type } ">"], then resolve and
// finalize it according to the given type finalization mode.
RawAbstractType* Parser::ParseType(
ClassFinalizer::FinalizationKind finalization) {
TRACE_PARSER("ParseType");
CheckToken(Token::kIDENT, "type name expected");
QualIdent type_name;
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 {
ParseQualIdent(&type_name);
// An identifier cannot be resolved in a local scope when top level parsing.
if (!is_top_level_ &&
(type_name.lib_prefix == NULL) &&
ResolveIdentInLocalScope(type_name.ident_pos, *type_name.ident, NULL)) {
// The type is malformed. Skip over its type arguments.
ParseTypeArguments(ClassFinalizer::kIgnore);
return ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(), // No previous error.
script_,
type_name.ident_pos,
"using '%s' in this context is invalid",
type_name.ident->ToCString());
}
}
Object& type_class = Object::Handle(isolate());
// Leave type_class as null if type finalization mode is kIgnore.
if (finalization != ClassFinalizer::kIgnore) {
LibraryPrefix& lib_prefix = LibraryPrefix::Handle(isolate());
if (type_name.lib_prefix != NULL) {
lib_prefix = type_name.lib_prefix->raw();
}
type_class = UnresolvedClass::New(lib_prefix,
*type_name.ident,
type_name.ident_pos);
}
AbstractTypeArguments& type_arguments = AbstractTypeArguments::Handle(
isolate(), ParseTypeArguments(finalization));
if (finalization == ClassFinalizer::kIgnore) {
return Type::DynamicType();
}
AbstractType& type = AbstractType::Handle(
isolate(), Type::New(type_class, type_arguments, type_name.ident_pos));
if (finalization >= ClassFinalizer::kResolveTypeParameters) {
ResolveTypeFromClass(current_class(), finalization, &type);
if (finalization >= ClassFinalizer::kCanonicalize) {
type ^= ClassFinalizer::FinalizeType(current_class(), type, finalization);
}
}
return type.raw();
}
void Parser::CheckConstructorCallTypeArguments(
intptr_t pos, Function& constructor,
const AbstractTypeArguments& type_arguments) {
if (!type_arguments.IsNull()) {
const Class& constructor_class = Class::Handle(constructor.Owner());
ASSERT(!constructor_class.IsNull());
ASSERT(constructor_class.is_finalized());
// 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()) {
ErrorMsg(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(intptr_t type_pos,
bool is_const,
const AbstractTypeArguments& type_arguments) {
TRACE_PARSER("ParseListLiteral");
ASSERT(type_pos >= 0);
ASSERT(CurrentToken() == Token::kLBRACK || CurrentToken() == Token::kINDEX);
const intptr_t literal_pos = TokenPos();
bool is_empty_literal = CurrentToken() == Token::kINDEX;
ConsumeToken();
AbstractType& element_type = Type::ZoneHandle(Type::DynamicType());
AbstractTypeArguments& list_type_arguments =
AbstractTypeArguments::ZoneHandle(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 = AbstractTypeArguments::null();
} else if (is_const && !element_type.IsInstantiated()) {
ErrorMsg(type_pos,
"the type argument of a constant list literal cannot include "
"a type variable");
}
} else {
if (FLAG_error_on_bad_type) {
ErrorMsg(type_pos,
"a list literal takes one type argument specifying "
"the element type");
}
// Ignore type arguments.
list_type_arguments = AbstractTypeArguments::null();
}
}
ASSERT(list_type_arguments.IsNull() || (list_type_arguments.Length() == 1));
const Class& array_class = Class::Handle(
isolate()->object_store()->array_class());
Type& type = Type::ZoneHandle(
Type::New(array_class, list_type_arguments, type_pos));
type ^= ClassFinalizer::FinalizeType(
current_class(), type, ClassFinalizer::kCanonicalize);
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 intptr_t element_pos = TokenPos();
AstNode* element = ParseExpr(is_const, kConsumeCascades);
if (FLAG_enable_type_checks &&
!is_const &&
!element_type.IsDynamicType()) {
element = new AssignableNode(element_pos,
element,
element_type,
Symbols::ListLiteralElement());
}
element_list.Add(element);
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
} else if (CurrentToken() != Token::kRBRACK) {
ErrorMsg("comma or ']' expected");
}
}
ExpectToken(Token::kRBRACK);
SetAllowFunctionLiterals(saved_mode);
}
if (is_const) {
// Allocate and initialize the const list at compile time.
Array& const_list =
Array::ZoneHandle(Array::New(element_list.length(), Heap::kOld));
const_list.SetTypeArguments(
AbstractTypeArguments::Handle(list_type_arguments.Canonicalize()));
Error& malformed_error = Error::Handle();
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 (FLAG_enable_type_checks &&
!element_type.IsDynamicType() &&
(!elem->AsLiteralNode()->literal().IsNull() &&
!elem->AsLiteralNode()->literal().IsInstanceOf(
element_type, TypeArguments::Handle(), &malformed_error))) {
// If the failure is due to a malformed type error, display it instead.
if (!malformed_error.IsNull()) {
ErrorMsg(malformed_error);
} else {
ErrorMsg(elem->AsLiteralNode()->token_pos(),
"list literal element at index %d must be "
"a constant of type '%s'",
i,
String::Handle(element_type.UserVisibleName()).ToCString());
}
}
const_list.SetAt(i, elem->AsLiteralNode()->literal());
}
const_list ^= TryCanonicalize(const_list, literal_pos);
const_list.MakeImmutable();
return new LiteralNode(literal_pos, const_list);
} else {
// Factory call at runtime.
const Class& factory_class =
Class::Handle(Library::LookupCoreClass(Symbols::List()));
ASSERT(!factory_class.IsNull());
const Function& factory_method = Function::ZoneHandle(
factory_class.LookupFactory(
Library::PrivateCoreLibName(Symbols::ListLiteralFactory())));
ASSERT(!factory_method.IsNull());
if (!list_type_arguments.IsNull() &&
!list_type_arguments.IsInstantiated() &&
(current_block_->scope->function_level() > 0)) {
// Make sure that the instantiator is captured.
CaptureInstantiator();
}
AbstractTypeArguments& factory_type_args =
AbstractTypeArguments::ZoneHandle(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(Type::New(
factory_class, factory_type_args, type_pos, Heap::kNew));
factory_type ^= ClassFinalizer::FinalizeType(
current_class(), factory_type, ClassFinalizer::kFinalize);
factory_type_args = factory_type.arguments();
ASSERT(factory_type_args.Length() == factory_class.NumTypeArguments());
}
factory_type_args = factory_type_args.Canonicalize();
ArgumentListNode* factory_param = new ArgumentListNode(literal_pos);
if (element_list.length() == 0) {
// TODO(srdjan): Use Object::empty_array once issue 9871 has been fixed.
Array& empty_array = Array::ZoneHandle(Object::empty_array().raw());
LiteralNode* empty_array_literal =
new LiteralNode(TokenPos(), empty_array);
factory_param->Add(empty_array_literal);
} else {
ArrayNode* list = new ArrayNode(TokenPos(), type, element_list);
factory_param->Add(list);
}
return CreateConstructorCallNode(literal_pos,
factory_type_args,
factory_method,
factory_param);
}
}
ConstructorCallNode* Parser::CreateConstructorCallNode(
intptr_t token_pos,
const AbstractTypeArguments& type_arguments,
const Function& constructor,
ArgumentListNode* arguments) {
if (!type_arguments.IsNull() && !type_arguments.IsInstantiated()) {
EnsureExpressionTemp();
}
return new 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(intptr_t type_pos,
bool is_const,
const AbstractTypeArguments& type_arguments) {
TRACE_PARSER("ParseMapLiteral");
ASSERT(type_pos >= 0);
ASSERT(CurrentToken() == Token::kLBRACE);
const intptr_t literal_pos = TokenPos();
ConsumeToken();
AbstractType& key_type = Type::ZoneHandle(Type::DynamicType());
AbstractType& value_type = Type::ZoneHandle(Type::DynamicType());
AbstractTypeArguments& map_type_arguments =
AbstractTypeArguments::ZoneHandle(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 = AbstractTypeArguments::null();
} else if (is_const && !type_arguments.IsInstantiated()) {
ErrorMsg(type_pos,
"the type arguments of a constant map literal cannot include "
"a type variable");
}
} else {
if (FLAG_error_on_bad_type) {
ErrorMsg(type_pos,
"a map literal takes two type arguments specifying "
"the key type and the value type");
}
// Ignore type arguments.
map_type_arguments = AbstractTypeArguments::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 intptr_t key_pos = TokenPos();
AstNode* key = ParseExpr(is_const, kConsumeCascades);
if (FLAG_enable_type_checks &&
!is_const &&
!key_type.IsDynamicType()) {
key = new 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()) {
ErrorMsg(key_pos, "key value must not be of type double");
}
if (!key_value.IsInteger() &&
!key_value.IsString() &&
ImplementsEqualOperator(key_value)) {
ErrorMsg(key_pos, "key value must not implement operator ==");
}
}
ExpectToken(Token::kCOLON);
const intptr_t value_pos = TokenPos();
AstNode* value = ParseExpr(is_const, kConsumeCascades);
SetAllowFunctionLiterals(saved_mode);
if (FLAG_enable_type_checks &&
!is_const &&
!value_type.IsDynamicType()) {
value = new 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) {
ErrorMsg("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(Array::New(kv_pairs_list.length(), Heap::kOld));
AbstractType& arg_type = Type::Handle();
Error& malformed_error = Error::Handle();
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 (FLAG_enable_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_abstract_type_arguments(),
&malformed_error))) {
// If the failure is due to a malformed type error, display it.
if (!malformed_error.IsNull()) {
ErrorMsg(malformed_error);
} else {
ErrorMsg(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(arg_type.UserVisibleName()).ToCString());
}
}
}
key_value_array.SetAt(i, arg->AsLiteralNode()->literal());
}
key_value_array ^= TryCanonicalize(key_value_array, TokenPos());
key_value_array.MakeImmutable();
// Construct the map object.
const Class& immutable_map_class =
Class::Handle(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 ArgumentListNode(TokenPos());
constr_args->Add(new LiteralNode(literal_pos, key_value_array));
const Function& map_constr =
Function::ZoneHandle(immutable_map_class.LookupConstructor(
Library::PrivateCoreLibName(Symbols::ImmutableMapConstructor())));
ASSERT(!map_constr.IsNull());
const Object& constructor_result = Object::Handle(
EvaluateConstConstructorCall(immutable_map_class,
map_type_arguments,
map_constr,
constr_args));
if (constructor_result.IsUnhandledException()) {
AppendErrorMsg(Error::Cast(constructor_result),
literal_pos,
"error executing const Map constructor");
} else {
const Instance& const_instance = Instance::Cast(constructor_result);
return new LiteralNode(literal_pos,
Instance::ZoneHandle(const_instance.raw()));
}
} else {
// Factory call at runtime.
const Class& factory_class =
Class::Handle(Library::LookupCoreClass(Symbols::Map()));
ASSERT(!factory_class.IsNull());
const Function& factory_method = Function::ZoneHandle(
factory_class.LookupFactory(
Library::PrivateCoreLibName(Symbols::MapLiteralFactory())));
ASSERT(!factory_method.IsNull());
if (!map_type_arguments.IsNull() &&
!map_type_arguments.IsInstantiated() &&
(current_block_->scope->function_level() > 0)) {
// Make sure that the instantiator is captured.
CaptureInstantiator();
}
AbstractTypeArguments& factory_type_args =
AbstractTypeArguments::ZoneHandle(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(Type::New(
factory_class, factory_type_args, type_pos, Heap::kNew));
factory_type ^= ClassFinalizer::FinalizeType(
current_class(), factory_type, ClassFinalizer::kFinalize);
factory_type_args = factory_type.arguments();
ASSERT(factory_type_args.Length() == factory_class.NumTypeArguments());
}
factory_type_args = factory_type_args.Canonicalize();
ArgumentListNode* factory_param = new 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.
ArrayNode* kv_pairs = new ArrayNode(
TokenPos(),
Type::ZoneHandle(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 intptr_t type_pos = TokenPos();
AbstractTypeArguments& type_arguments = AbstractTypeArguments::Handle(
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 {
ErrorMsg("unexpected token %s", Token::Str(CurrentToken()));
}
return primary;
}
AstNode* Parser::ParseSymbolLiteral() {
ASSERT(CurrentToken() == Token::kHASH);
ConsumeToken();
intptr_t symbol_pos = TokenPos();
String& symbol = String::Handle();
if (IsIdentifier()) {
symbol = CurrentLiteral()->raw();
ConsumeToken();
while (CurrentToken() == Token::kPERIOD) {
symbol = String::Concat(symbol, Symbols::Dot());
ConsumeToken();
symbol = String::Concat(symbol,
*ExpectIdentifier("identifier expected"));
}
} else if (Token::CanBeOverloaded(CurrentToken())) {
symbol = String::New(Token::Str(CurrentToken()));
ConsumeToken();
} else {
ErrorMsg("illegal symbol literal");
}
// Lookup class Symbol from internal library and call the
// constructor to create a symbol instance.
const Library& lib = Library::Handle(Library::InternalLibrary());
const Class& symbol_class = Class::Handle(lib.LookupClass(Symbols::Symbol()));
ASSERT(!symbol_class.IsNull());
ArgumentListNode* constr_args = new ArgumentListNode(symbol_pos);
constr_args->Add(new LiteralNode(
symbol_pos, String::ZoneHandle(Symbols::New(symbol))));
const Function& constr = Function::ZoneHandle(
symbol_class.LookupConstructor(Symbols::SymbolCtor()));
ASSERT(!constr.IsNull());
const Object& result = Object::Handle(
EvaluateConstConstructorCall(symbol_class,
TypeArguments::Handle(),
constr,
constr_args));
if (result.IsUnhandledException()) {
AppendErrorMsg(Error::Cast(result),
symbol_pos,
"error executing const Symbol constructor");
}
const Instance& instance = Instance::Cast(result);
return new LiteralNode(symbol_pos, Instance::ZoneHandle(instance.raw()));
}
static String& BuildConstructorName(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.
String& constructor_name =
String::Handle(String::Concat(type_class_name, Symbols::Dot()));
if (named_constructor != NULL) {
constructor_name = String::Concat(constructor_name, *named_constructor);
}
return constructor_name;
}
AstNode* Parser::ParseNewOperator(Token::Kind op_kind) {
TRACE_PARSER("ParseNewOperator");
const intptr_t new_pos = TokenPos();
ASSERT((op_kind == Token::kNEW) || (op_kind == Token::kCONST));
bool is_const = (op_kind == Token::kCONST);
if (!IsIdentifier()) {
ErrorMsg("type name expected");
}
intptr_t type_pos = TokenPos();
AbstractType& type = AbstractType::Handle(
ParseType(ClassFinalizer::kCanonicalizeWellFormed));
// 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(), // No previous error.
script_,
type_pos,
"%s'%s' cannot be instantiated",
type.IsTypeParameter() ? "type parameter " : "",
type.IsTypeParameter() ?
String::Handle(type.UserVisibleName()).ToCString() : "dynamic");
}
// The grammar allows for an optional ('.' identifier)? after the type, which
// is a named constructor. Note that ParseType() above will not consume it as
// part of a misinterpreted qualified identifier, because only a valid library
// prefix is accepted as qualifier.
String* named_constructor = NULL;
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
named_constructor = ExpectIdentifier("name of constructor expected");
}
// Parse constructor parameters.
CheckToken(Token::kLPAREN);
intptr_t call_pos = TokenPos();
ArgumentListNode* arguments = ParseActualParameters(NULL, is_const);
// Parsing is complete, so we can return a throw in case of a malformed or
// malbounded type or report a compile-time error if the constructor is const.
if (type.IsMalformedOrMalbounded()) {
if (is_const) {
const Error& error = Error::Handle(type.error());
ErrorMsg(error);
}
return ThrowTypeError(type_pos, type);
}
// Resolve the type and optional identifier to a constructor or factory.
Class& type_class = Class::Handle(type.type_class());
String& type_class_name = String::Handle(type_class.Name());
AbstractTypeArguments& type_arguments =
AbstractTypeArguments::ZoneHandle(type.arguments());
// A constructor has an implicit 'this' parameter (instance to construct)
// and a factory has an implicit 'this' parameter (type_arguments).
// A constructor has a second implicit 'phase' parameter.
intptr_t arguments_length = arguments->length() + 2;
// An additional type check of the result of a redirecting factory may be
// required.
AbstractType& type_bound = AbstractType::ZoneHandle();
// Make sure that an appropriate constructor exists.
String& constructor_name =
BuildConstructorName(type_class_name, named_constructor);
Function& constructor = Function::ZoneHandle(
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(), // No previous error.
script_,
call_pos,
"class '%s' has no constructor or factory named '%s'",
String::Handle(type_class.Name()).ToCString(),
external_constructor_name.ToCString());
ErrorMsg(Error::Handle(type.error()));
}
return ThrowNoSuchMethodError(call_pos,
type_class,
external_constructor_name,
arguments,
InvocationMirror::kConstructor,
InvocationMirror::kMethod,
&constructor);
} else if (constructor.IsRedirectingFactory()) {
ClassFinalizer::ResolveRedirectingFactory(type_class, constructor);
Type& redirect_type = Type::Handle(constructor.RedirectionType());
if (!redirect_type.IsMalformedOrMalbounded() &&
!redirect_type.IsInstantiated()) {
// 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();
redirect_type ^= redirect_type.InstantiateFrom(type_arguments, &error);
if (!error.IsNull()) {
redirect_type = ClassFinalizer::NewFinalizedMalformedType(
error,
script_,
call_pos,
"redirecting factory type '%s' cannot be instantiated",
String::Handle(redirect_type.UserVisibleName()).ToCString());
}
}
if (redirect_type.IsMalformedOrMalbounded()) {
if (is_const) {
ErrorMsg(Error::Handle(redirect_type.error()));
}
return ThrowTypeError(redirect_type.token_pos(), redirect_type);
}
if (FLAG_enable_type_checks && !redirect_type.IsSubtypeOf(type, NULL)) {
// 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());
}
if (constructor.IsFactory()) {
// A factory does not have the implicit 'phase' parameter.
arguments_length -= 1;
}
}
// It is ok to call a factory method of an abstract class, but it is
// a dynamic error to instantiate an abstract class.
ASSERT(!constructor.IsNull());
if (type_class.is_abstract() && !constructor.IsFactory()) {
// Evaluate arguments before throwing.
LetNode* result = new LetNode(call_pos);
for (intptr_t i = 0; i < arguments->length(); ++i) {
result->AddNode(arguments->NodeAt(i));
}
ArgumentListNode* error_arguments = new ArgumentListNode(type_pos);
error_arguments->Add(new LiteralNode(
TokenPos(), Integer::ZoneHandle(Integer::New(type_pos))));
error_arguments->Add(new LiteralNode(
TokenPos(), String::ZoneHandle(type_class_name.raw())));
result->AddNode(
MakeStaticCall(Symbols::AbstractClassInstantiationError(),
Library::PrivateCoreLibName(Symbols::ThrowNew()),
error_arguments));
return result;
}
String& error_message = String::Handle();
if (!constructor.AreValidArguments(arguments_length,
arguments->names(),
&error_message)) {
const String& external_constructor_name =
(named_constructor ? constructor_name : type_class_name);
if (is_const) {
ErrorMsg(call_pos,
"invalid arguments passed to constructor '%s' "
"for class '%s': %s",
external_constructor_name.ToCString(),
String::Handle(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) {
ErrorMsg(Error::Handle(type.error()));
}
return ThrowTypeError(type_pos, type);
}
type_arguments ^= type_arguments.Canonicalize();
// 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);
ErrorMsg("non-const constructor '%s' cannot be used in "
"const object creation",
external_constructor_name.ToCString());
}
const Object& constructor_result = Object::Handle(
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.
AppendErrorMsg(Error::Cast(constructor_result),
new_pos,
"error while evaluating const constructor");
} else {
// Const constructors can return null in the case where a const native
// factory returns a null value. Thus we cannot use a Instance::Cast here.
Instance& const_instance = Instance::Handle();
const_instance ^= constructor_result.raw();
new_object = new LiteralNode(new_pos,
Instance::ZoneHandle(const_instance.raw()));
if (!type_bound.IsNull()) {
ASSERT(!type_bound.IsMalformed());
Error& malformed_error = Error::Handle();
ASSERT(!is_top_level_); // We cannot check unresolved types.
if (!const_instance.IsInstanceOf(type_bound,
TypeArguments::Handle(),
&malformed_error)) {
type_bound = ClassFinalizer::NewFinalizedMalformedType(
malformed_error,
script_,
new_pos,
"const factory result is not an instance of '%s'",
String::Handle(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() &&
(current_block_->scope->function_level() > 0)) {
// Make sure that the instantiator is captured.
CaptureInstantiator();
}
// 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 AssignableNode(new_pos,
new_object,
type_bound,
Symbols::FactoryResult());
}
return new_object;
}
String& Parser::Interpolate(const GrowableArray<AstNode*>& values) {
const Class& cls =
Class::Handle(Library::LookupCoreClass(Symbols::StringBase()));
ASSERT(!cls.IsNull());
const Function& func =
Function::Handle(cls.LookupStaticFunction(
Library::PrivateCoreLibName(Symbols::Interpolate())));
ASSERT(!func.IsNull());
// Build the array of literal values to interpolate.
const Array& value_arr = Array::Handle(Array::New(values.length()));
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(Array::New(1));
interpolate_arg.SetAt(0, value_arr);
// Call interpolation function.
String& concatenated = String::ZoneHandle();
concatenated ^= DartEntry::InvokeFunction(func, interpolate_arg);
if (concatenated.IsUnhandledException()) {
ErrorMsg("Exception thrown in Parser::Interpolate");
}
concatenated = Symbols::New(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 intptr_t 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 LiteralNode(literal_start, *CurrentLiteral());
ConsumeToken();
return primary;
}
// String interpolation needed.
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 LiteralNode(TokenPos(), *CurrentLiteral()));
}
ConsumeToken();
while ((CurrentToken() == Token::kINTERPOL_VAR) ||
(CurrentToken() == Token::kINTERPOL_START)) {
if (!allow_interpolation) {
ErrorMsg("string interpolation not allowed in this context");
}
has_interpolation = true;
AstNode* expr = NULL;
const intptr_t expr_pos = TokenPos();
if (CurrentToken() == Token::kINTERPOL_VAR) {
expr = ResolveIdent(TokenPos(), *CurrentLiteral(), true);
ConsumeToken();
} else {
ASSERT(CurrentToken() == Token::kINTERPOL_START);
ConsumeToken();
expr = ParseExpr(kAllowConst, kConsumeCascades);
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 LiteralNode(expr_pos, EvaluateConstExpr(expr_pos, expr));
} else {
is_compiletime_const = false;
}
}
values_list.Add(expr);
}
}
if (is_compiletime_const) {
if (has_interpolation) {
primary = new LiteralNode(literal_start, Interpolate(values_list));
} else {
const Array& strings = Array::Handle(Array::New(values_list.length()));
for (int i = 0; i < values_list.length(); i++) {
const Instance& part = values_list[i]->AsLiteralNode()->literal();
ASSERT(part.IsString());
strings.SetAt(i, String::Cast(part));
}
String& lit = String::ZoneHandle(String::ConcatAll(strings, Heap::kOld));
lit = Symbols::New(lit);
primary = new LiteralNode(literal_start, lit);
}
} else {
ArrayNode* values = new ArrayNode(
TokenPos(),
Type::ZoneHandle(Type::ArrayType()),
values_list);
primary = new StringInterpolateNode(TokenPos(), values);
}
return primary;
}
AstNode* Parser::ParsePrimary() {
TRACE_PARSER("ParsePrimary");
ASSERT(!is_top_level_);
AstNode* primary = NULL;
if (IsFunctionLiteral()) {
// The name of a literal function is visible from inside the function, but
// must not collide with names in the scope declaring the literal.
OpenBlock();
primary = ParseFunctionStatement(true);
CloseBlock();
} else if (IsIdentifier()) {
QualIdent qual_ident;
ParseQualIdent(&qual_ident);
if (qual_ident.lib_prefix == NULL) {
if (!ResolveIdentInLocalScope(qual_ident.ident_pos,
*qual_ident.ident,
&primary)) {
// Check whether the identifier is a type parameter.
if (!current_class().IsNull()) {
TypeParameter& type_param = TypeParameter::ZoneHandle(
current_class().LookupTypeParameter(*(qual_ident.ident)));
if (!type_param.IsNull()) {
return new PrimaryNode(qual_ident.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.ident_pos,
*qual_ident.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.ident_pos,
*qual_ident.lib_prefix,
*qual_ident.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.
// TODO(hausner): Ideally we should generate the NoSuchMethodError
// later, when we know more about how the unresolved name is used.
// For example, we don't know yet whether the unresolved name
// refers to a getter or a setter. However, it is more awkward
// to distinuish four NoSuchMethodError cases all over the place
// in the parser. The four cases are: prefixed vs non-prefixed
// name, static vs dynamic context in which the unresolved name
// is used. We cheat a little here by looking at the next token
// to determine whether we have an unresolved method call or
// field access.
if (primary->IsPrimaryNode() &&
primary->AsPrimaryNode()->primary().IsString()) {
InvocationMirror::Type call_type =
CurrentToken() == Token::kLPAREN ?
InvocationMirror::kMethod : InvocationMirror::kGetter;
const String& unresolved_name =
String::Cast(primary->AsPrimaryNode()->primary());
primary = ThrowNoSuchMethodError(primary->token_pos(),
current_class(),
unresolved_name,
NULL, // No arguments.
InvocationMirror::kTopLevel,
call_type,
NULL); // No existing function.
}
}
ASSERT(primary != NULL);
} else if (CurrentToken() == Token::kTHIS) {
LocalVariable* local = LookupLocalScope(Symbols::This());
if (local == NULL) {
ErrorMsg("receiver 'this' is not in scope");
}
primary = new LoadLocalNode(TokenPos(), local);
ConsumeToken();
} else if (CurrentToken() == Token::kINTEGER) {
const Integer& literal = Integer::ZoneHandle(CurrentIntegerLiteral());
primary = new LiteralNode(TokenPos(), literal);
ConsumeToken();
} else if (CurrentToken() == Token::kTRUE) {
primary = new LiteralNode(TokenPos(), Bool::True());
ConsumeToken();
} else if (CurrentToken() == Token::kFALSE) {
primary = new LiteralNode(TokenPos(), Bool::False());
ConsumeToken();
} else if (CurrentToken() == Token::kNULL) {
primary = new LiteralNode(TokenPos(), Instance::ZoneHandle());
ConsumeToken();
} else if (CurrentToken() == Token::kLPAREN) {
ConsumeToken();
const bool saved_mode = SetAllowFunctionLiterals(true);
primary = ParseExpr(kAllowConst, kConsumeCascades);
SetAllowFunctionLiterals(saved_mode);
ExpectToken(Token::kRPAREN);
} else if (CurrentToken() == Token::kDOUBLE) {
Double& double_value = Double::ZoneHandle(CurrentDoubleLiteral());
if (double_value.IsNull()) {
ErrorMsg("invalid double literal");
}
primary = new LiteralNode(TokenPos(), double_value);
ConsumeToken();
} else if (CurrentToken() == Token::kSTRING) {
primary = ParseStringLiteral(true);
} else if (CurrentToken() == Token::kNEW) {
ConsumeToken();
primary = ParseNewOperator(Token::kNEW);
} else if (CurrentToken() == 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 (CurrentToken() == Token::kLT ||
CurrentToken() == Token::kLBRACK ||
CurrentToken() == Token::kINDEX ||
CurrentToken() == Token::kLBRACE) {
primary = ParseCompoundLiteral();
} else if (CurrentToken() == Token::kHASH) {
primary = ParseSymbolLiteral();
} else if (CurrentToken() == Token::kSUPER) {
if (parsing_metadata_) {
ErrorMsg("cannot access superclass from metadata");
}
if (current_function().is_static()) {
ErrorMsg("cannot access superclass from static method");
}
if (current_class().SuperClass() == Class::null()) {
ErrorMsg("class '%s' does not have a superclass",
String::Handle(current_class().Name()).ToCString());
}
if (current_class().IsMixinApplication()) {
const Type& mixin_type = Type::Handle(current_class().mixin());
if (mixin_type.type_class() == current_function().origin()) {
ErrorMsg("method of mixin class '%s' may not refer to 'super'",
String::Handle(Class::Handle(
current_function().origin()).Name()).ToCString());
}
}
const intptr_t super_pos = TokenPos();
ConsumeToken();
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
const intptr_t ident_pos = TokenPos();
const String& ident = *ExpectIdentifier("identifier expected");
if (CurrentToken() == Token::kLPAREN) {
primary = ParseSuperCall(ident);
} else {
primary = ParseSuperFieldAccess(ident, ident_pos);
}
} else if ((CurrentToken() == Token::kLBRACK) ||
Token::CanBeOverloaded(CurrentToken()) ||
(CurrentToken() == Token::kNE)) {
primary = ParseSuperOperator();
} else {
primary = new 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(intptr_t expr_pos, AstNode* expr) {
if (expr->IsLiteralNode()) {
return expr->AsLiteralNode()->literal();
} else if (expr->IsLoadLocalNode() &&
expr->AsLoadLocalNode()->local().IsConst()) {
return *expr->AsLoadLocalNode()->local().ConstValue();
} else {
ASSERT(expr->EvalConstExpr() != NULL);
ReturnNode* ret = new ReturnNode(expr->token_pos(), expr);
// Compile time constant expressions cannot reference anything from a
// local scope.
LocalScope* empty_scope = new LocalScope(NULL, 0, 0);
SequenceNode* seq = new SequenceNode(expr->token_pos(), empty_scope);
seq->Add(ret);
Object& result = Object::Handle(Compiler::ExecuteOnce(seq));
if (result.IsError()) {
AppendErrorMsg(Error::Cast(result),
expr_pos,
"error evaluating constant expression");
}
ASSERT(result.IsInstance());
Instance& value = Instance::ZoneHandle();
value ^= result.raw();
value = TryCanonicalize(value, TokenPos());
return value;
}
}
void Parser::SkipFunctionLiteral() {
if (IsIdentifier()) {
if (LookaheadToken(1) != Token::kLPAREN) {
SkipType(true);
}
ExpectIdentifier("function name expected");
}
if (CurrentToken() == Token::kLPAREN) {
const bool allow_explicit_default_values = true;
ParamList params;
params.skipped = true;
ParseFormalParameterList(allow_explicit_default_values, false, &params);
}
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 (CurrentToken() == Token::kLPAREN ||
CurrentToken() == Token::kARROW ||
CurrentToken() == Token::kSEMICOLON ||
CurrentToken() == Token::kLBRACE) {
return;
}
// Case handles "native" keyword, but also return types of form
// native.SomeType where native is the name of a library.
if (CurrentToken() == Token::kIDENT &&
LookaheadToken(1) != Token::kPERIOD) {
if (CurrentLiteral()->raw() == Symbols::Native().raw()) {
return;
}
}
ConsumeToken();
}
}
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() {
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::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();
SkipExpr();
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) {
if (CurrentToken() == Token::kCASCADE) {
ConsumeToken();
if (CurrentToken() == Token::kLBRACK) {
continue; // Consume [ in next loop iteration.
} else {
ExpectIdentifier("identifier or [ expected after ..");
}
} else if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ExpectIdentifier("identifier expected");
} else if (CurrentToken() == Token::kLBRACK) {
ConsumeToken();
SkipNestedExpr();
ExpectToken(Token::kRBRACK);
} else if (CurrentToken() == Token::kLPAREN) {
SkipActualParameters();
} else {
break;
}
}
}
void Parser::SkipPostfixExpr() {
SkipPrimary();
SkipSelectors();
if (IsIncrementOperator(CurrentToken())) {
ConsumeToken();
}
}
void Parser::SkipUnaryExpr() {
if (IsPrefixOperator(CurrentToken()) ||
IsIncrementOperator(CurrentToken())) {
ConsumeToken();
SkipUnaryExpr();
} else {
SkipPostfixExpr();
}
}
void Parser::SkipBinaryExpr() {
SkipUnaryExpr();
const int min_prec = Token::Precedence(Token::kOR);
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();
}
SkipType(false);
} else if (CurrentToken() == Token::kAS) {
ConsumeToken();
SkipType(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