Files
sdk/runtime/vm/parser.cc
T
dgrove@google.com 4c0f559d23 Initial checkin.
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@15 260f80e4-7a28-3924-810f-c04153c831b5
2011-10-05 05:20:07 +00:00

6905 lines
243 KiB
C++

// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/parser.h"
#include "vm/bigint_operations.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/longjump.h"
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/resolver.h"
#include "vm/scopes.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, true, "Silence warnings.");
DEFINE_FLAG(bool, warn_params, false, "Report legacy parameter syntax");
// All references to Dart names are listed here.
static const char* kAssertErrorName = "AssertError";
static const char* kFallThroughErrorName = "FallThroughError";
static const char* kThrowNewName = "throwNew";
static const char* kGrowableObjectArrayFromArrayName =
"GrowableObjectArray._usingArray";
static const char* kGrowableObjectArrayName = "GrowableObjectArray";
static const char* kMutableMapName = "MutableMap";
static const char* kMutableMapFromLiteralName = "fromLiteral";
static const char* kImmutableMapName = "ImmutableMap";
static const char* kImmutableMapConstructorName = "ImmutableMap.";
static const char* kStringClassName = "StringBase";
static const char* kInterpolateName = "_interpolate";
static const char* kThisName = "this";
static const char* kGetIteratorName = "iterator";
#if defined(DEBUG)
class TraceParser : public ValueObject {
public:
TraceParser(intptr_t token_index, const Script& script, const char* msg) {
if (FLAG_trace_parser) {
intptr_t line, column;
script.GetTokenLocation(token_index, &line, &column);
PrintIndent();
OS::Print("%s (line %d, col %d, token %d)\n",
msg, line, column, token_index);
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->token_index_, this->script_, s)
#else // not DEBUG
#define TRACE_PARSER(s)
#endif // DEBUG
static const char* kManglePrefix = "this:";
static RawString* MangledInitParamName(const String& field_name) {
return String::Concat(String::Handle(String::New(kManglePrefix)),
field_name);
}
template<typename T>
static RawArray* NewArray(const GrowableArray<T*>& objs) {
Array& a = Array::Handle(Array::New(objs.length(), Heap::kOld));
for (int i = 0; i < objs.length(); i++) {
a.SetAt(i, *objs[i]);
}
return a.raw();
}
static RawTypeArray* NewTypeArray(const GrowableArray<Type*>& objs) {
TypeArray& a = TypeArray::Handle(TypeArray::New(objs.length()));
for (int i = 0; i < objs.length(); i++) {
a.SetTypeAt(i, *objs[i]);
}
return a.raw();
}
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)
: try_block_(try_block),
inlined_finally_nodes_(),
outer_try_block_(outer_try_block) { }
TryBlocks* outer_try_block() const { return outer_try_block_; }
Block* try_block() const { return try_block_; }
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_;
DISALLOW_COPY_AND_ASSIGN(TryBlocks);
};
void Parser::TryBlocks::AddNodeForFinallyInlining(AstNode* node) {
inlined_finally_nodes_.Add(node);
}
Parser::Parser(const Script& script, const Library& library)
: script_(script),
tokens_(TokenStream::Handle(script.tokens())),
token_index_(0),
current_block_(NULL),
is_top_level_(false),
current_member_(NULL),
allow_function_literals_(true),
current_function_(Function::Handle()),
current_class_(Class::Handle()),
library_(library),
try_blocks_list_(NULL) {
SetPosition(0);
}
Parser::Parser(const Script& script,
const Function& function,
intptr_t token_index)
: script_(script),
tokens_(TokenStream::Handle(script.tokens())),
token_index_(0),
current_block_(NULL),
is_top_level_(false),
current_member_(NULL),
allow_function_literals_(true),
current_function_(function),
current_class_(Class::Handle(current_function_.owner())),
library_(Library::Handle(current_class_.library())),
try_blocks_list_(NULL) {
SetPosition(token_index);
}
bool Parser::SetAllowFunctionLiterals(bool value) {
bool current_value = allow_function_literals_;
allow_function_literals_ = value;
return current_value;
}
const Function& Parser::current_function() const {
return current_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 < token_index_ && position != 0) {
CompilerStats::num_tokens_rewind += (token_index_ - position);
}
token_index_ = position;
token_kind_ = Token::kILLEGAL;
}
void Parser::ParseCompilationUnit(const Library& library,
const Script& script) {
Parser parser(script, library);
if (FLAG_compiler_stats) {
CompilerStats::parser_timer.Start();
}
parser.ParseTopLevel();
if (FLAG_compiler_stats) {
CompilerStats::parser_timer.Stop();
CompilerStats::num_tokens_total += parser.tokens_.Length();
}
}
Token::Kind Parser::CurrentToken() {
if (token_kind_ == Token::kILLEGAL) {
token_kind_ = tokens_.KindAt(token_index_);
if (token_kind_ == Token::kERROR) {
ErrorMsg(token_index_, CurrentLiteral()->ToCString());
}
if (Token::IsPseudoKeyword(token_kind_) && !is_top_level_) {
token_kind_ = Token::kIDENT;
}
}
CompilerStats::num_token_checks++;
return token_kind_;
}
Token::Kind Parser::LookaheadToken(int num_tokens) {
CompilerStats::num_tokens_lookahead++;
CompilerStats::num_token_checks++;
return tokens_.KindAt(token_index_ + num_tokens);
}
String* Parser::CurrentLiteral() const {
String& result = String::ZoneHandle();
result ^= tokens_.LiteralAt(token_index_);
return &result;
}
// A QualIdent is an optionally qualified identifier.
struct QualIdent {
QualIdent() {
Clear();
}
void Clear() {
local_scope_ident = false;
lib_prefix = NULL;
qualifier = NULL;
ident_pos = 0;
ident = NULL;
}
bool local_scope_ident;
LibraryPrefix* lib_prefix;
String* qualifier;
intptr_t ident_pos;
String* ident;
};
struct ParamDesc {
ParamDesc()
: type(NULL),
name_pos(0),
name(NULL),
default_value(NULL),
is_final(false),
is_field_initializer(false) { }
const Type* type;
intptr_t name_pos;
const String* name;
const Object* default_value; // NULL if not an optional parameter.
bool is_final;
bool is_field_initializer;
};
struct ParamList {
ParamList() {
Clear();
}
void Clear() {
num_fixed_parameters = 0;
num_optional_parameters = 0;
has_named_optional_parameters = false;
has_field_initializer = false;
implicitly_final = false;
this->parameters = new ZoneGrowableArray<ParamDesc>();
}
void AddFinalParameter(intptr_t name_pos,
const char* name,
const Type* type) {
this->num_fixed_parameters++;
ParamDesc param;
param.name_pos = name_pos;
param.name = &String::ZoneHandle(String::NewSymbol(name));
param.is_final = true;
param.type = type;
this->parameters->Add(param);
}
void AddReceiver(intptr_t name_pos) {
ASSERT(this->parameters->length() == 0);
// The receiver does not need to be type checked.
AddFinalParameter(name_pos, kThisName, &Type::ZoneHandle(Type::VarType()));
}
void SetImplicitlyFinal() {
implicitly_final = true;
}
int num_fixed_parameters;
int num_optional_parameters;
bool has_named_optional_parameters; // Indicates use of the new syntax.
bool has_field_initializer;
bool implicitly_final;
ZoneGrowableArray<ParamDesc>* parameters;
};
struct MemberDesc {
MemberDesc() {
Clear();
}
void Clear() {
has_abstract = false;
has_final = false;
has_const = false;
has_static = false;
has_var = false;
has_factory = false;
type = NULL;
name_pos = 0;
name = NULL;
redirect_name = NULL;
params.Clear();
kind = RawFunction::kFunction;
}
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;
}
bool has_abstract;
bool has_final;
bool has_const;
bool has_static;
bool has_var;
bool has_factory;
const Type* type;
intptr_t name_pos;
String* name;
String* redirect_name; // For constructors: NULL or redirected constructor.
ParamList params;
RawFunction::Kind kind;
};
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),
is_interface_(is_interface),
token_pos_(token_pos),
functions_(4),
fields_(4) {
}
bool FunctionNameExists(const String& name, RawFunction::Kind kind) const {
// First check if a function or field of same name exists.
if (NameExists<Function>(functions_, name) ||
NameExists<Field>(fields_, name)) {
return true;
}
String& accessor_name = String::Handle();
if (kind != RawFunction::kSetterFunction) {
// Check if a getter function of same name exists.
accessor_name = Field::GetterName(name);
if (NameExists<Function>(functions_, accessor_name)) {
return true;
}
}
if (kind != RawFunction::kGetterFunction) {
// Check if a setter function of same name exists.
accessor_name = Field::SetterName(name);
if (NameExists<Function>(functions_, accessor_name)) {
return true;
}
}
return false;
}
bool FieldNameExists(const String& name) const {
// First check if a function or field of same name exists.
if (NameExists<Function>(functions_, name) ||
NameExists<Field>(fields_, name)) {
return true;
}
// Now check if a getter/setter function of same name exists.
String& getter_name = String::Handle(Field::GetterName(name));
String& setter_name = String::Handle(Field::SetterName(name));
if (NameExists<Function>(functions_, getter_name) ||
NameExists<Function>(functions_, setter_name)) {
return true;
}
return false;
}
void AddFunction(Function* function) {
ASSERT(!NameExists<Function>(functions_, String::Handle(function->name())));
functions_.Add(function);
}
const GrowableArray<Function*>& functions() const {
return functions_;
}
void AddField(Field* field) {
ASSERT(!NameExists<Field>(fields_, String::Handle(field->name())));
fields_.Add(field);
}
const GrowableArray<Field*>& fields() const {
return fields_;
}
RawClass* clazz() const {
return clazz_.raw();
}
const String& class_name() const {
return class_name_;
}
bool is_interface() const {
return is_interface_;
}
bool has_constructor() const {
for (int i = 0; i < functions_.length(); i++) {
if (functions_[i]->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:
template<typename T>
bool NameExists(const GrowableArray<T*>& list, const String& name) const {
String& test_name = String::Handle();
for (int i = 0; i < list.length(); i++) {
test_name = list[i]->name();
if (name.Equals(test_name)) {
return true;
}
}
return false;
}
const Class& clazz_;
const String& class_name_;
const bool is_interface_;
intptr_t token_pos_; // Token index of "class" keyword.
GrowableArray<Function*> functions_;
GrowableArray<Field*> fields_;
GrowableArray<MemberDesc> members_;
};
struct TopLevel {
TopLevel() : fields(4), functions(4) { }
GrowableArray<Field*> fields;
GrowableArray<Function*> functions;
};
void Parser::ParseFunction(ParsedFunction* parsed_function) {
Isolate* isolate = Isolate::Current();
// Compilation can be nested, preserve the ast node id.
const int prev_ast_node_id = isolate->ast_node_id();
isolate->set_ast_node_id(0);
ASSERT(parsed_function != NULL);
const Function& func = parsed_function->function();
const Class& cls = Class::Handle(func.owner());
const Script& script = Script::Handle(cls.script());
Parser parser(script, func, func.token_index());
if (FLAG_compiler_stats) {
CompilerStats::parser_timer.Start();
}
SequenceNode* node_sequence = NULL;
Array& default_parameter_values = Array::Handle();
switch (func.kind()) {
case RawFunction::kFunction:
case RawFunction::kClosureFunction:
case RawFunction::kGetterFunction:
case RawFunction::kSetterFunction:
case RawFunction::kConstructor:
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::kConstImplicitGetter:
node_sequence = parser.ParseStaticConstGetter(func);
break;
default:
UNREACHABLE();
}
if ((node_sequence->length() == 0) ||
!node_sequence->NodeAt(node_sequence->length() - 1)->IsReturnNode()) {
// Add implicit return node.
node_sequence->Add(new ReturnNode(parser.token_index_));
}
parsed_function->set_node_sequence(node_sequence);
// The instantiator may be required at run time for generic type checks or
// allocation of generic types.
if (parser.current_class().IsParameterized() &&
(!parser.current_function().IsInStaticScope() ||
parser.current_function().IsInFactoryScope())) {
// In the case of a local function, only set the instantiator if the
// receiver was captured.
const bool kTestOnly = true;
LocalVariable* receiver =
parser.LookupReceiver(node_sequence->scope(),
kTestOnly);
if (!parser.current_function().IsLocalFunction() ||
((receiver != NULL) && receiver->is_captured())) {
parsed_function->set_instantiator(
new LoadLocalNode(node_sequence->token_index(), *receiver));
}
}
parsed_function->set_default_parameter_values(default_parameter_values);
if (FLAG_compiler_stats) {
CompilerStats::parser_timer.Stop();
}
isolate->set_ast_node_id(prev_ast_node_id);
}
SequenceNode* Parser::ParseStaticConstGetter(const Function& func) {
ParamList params;
ASSERT(func.num_fixed_parameters() == 0); // static.
ASSERT(func.num_optional_parameters() == 0);
ASSERT(Type::Handle(func.result_type()).IsResolved());
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
// Static const fields must have an initializer.
ExpectToken(Token::kIDENT);
ExpectToken(Token::kASSIGN);
// 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.
intptr_t expr_pos = token_index_;
AstNode* expr = ParseExpr(kAllowConst);
if (expr->EvalConstExpr() == NULL) {
ErrorMsg(expr_pos, "initializer must be a compile time constant");
}
ReturnNode* return_node = new ReturnNode(token_index_, expr);
current_block_->statements->Add(return_node);
return CloseBlock();
}
// Create AstNodes for an implicit instance getter method:
// LoadLocalNode 0 ('this');
// LoadInstanceFieldNode (field_name);
// ReturnNode (field's value);
SequenceNode* Parser::ParseInstanceGetter(const Function& func) {
TRACE_PARSER("ParseInstanceGetter");
ParamList params;
params.AddReceiver(token_index_);
ASSERT(func.num_fixed_parameters() == 1); // receiver.
ASSERT(func.num_optional_parameters() == 0);
ASSERT(Type::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(token_index_, *receiver);
// token_index_ is the function's token position which points to the name of
// the field;
ASSERT(CurrentToken() == Token::kIDENT);
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(token_index_, load_receiver, field);
ReturnNode* return_node = new ReturnNode(token_index_, 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");
// token_index_ is the function's token position which points to the name of
// the field; we can use it to form the field_name.
const String& field_name = *CurrentLiteral();
const Class& field_class = Class::ZoneHandle(func.owner());
const Field& field =
Field::ZoneHandle(field_class.LookupInstanceField(field_name));
const Type& field_type = Type::ZoneHandle(field.type());
ParamList params;
params.AddReceiver(token_index_);
params.AddFinalParameter(token_index_, "value", &field_type);
ASSERT(func.num_fixed_parameters() == 2); // receiver, value.
ASSERT(func.num_optional_parameters() == 0);
ASSERT(Type::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(token_index_, *current_block_->scope->VariableAt(0));
LoadLocalNode* value =
new LoadLocalNode(token_index_, *current_block_->scope->VariableAt(1));
StoreInstanceFieldNode* store_field =
new StoreInstanceFieldNode(token_index_, receiver, field, value);
current_block_->statements->Add(store_field);
current_block_->statements->Add(new ReturnNode(token_index_));
return CloseBlock();
}
void Parser::SkipBlock() {
ASSERT(CurrentToken() == Token::kLBRACE);
GrowableArray<Token::Kind> token_stack(8);
intptr_t block_start_pos = token_index_;
bool is_match = true;
bool unexpected_token_found = false;
Token::Kind token;
intptr_t token_index;
do {
token = CurrentToken();
token_index = token_index_;
switch (token) {
case Token::kLBRACE:
case Token::kLPAREN:
case Token::kLBRACK:
token_stack.Add(token);
break;
case Token::kRBRACE:
is_match = token_stack.Last() == Token::kLBRACE;
token_stack.RemoveLast();
break;
case Token::kRPAREN:
is_match = token_stack.Last() == Token::kLPAREN;
token_stack.RemoveLast();
break;
case Token::kRBRACK:
is_match = token_stack.Last() == Token::kLBRACK;
token_stack.RemoveLast();
break;
case Token::kEOS:
unexpected_token_found = true;
break;
default:
// nothing.
break;
}
ConsumeToken();
} while (!token_stack.is_empty() && is_match && !unexpected_token_found);
if (!is_match) {
ErrorMsg(token_index, "unbalanced '%s'", Token::Str(token));
} else if (unexpected_token_found) {
ErrorMsg(block_start_pos, "unterminated block");
}
}
void Parser::ParseFormalParameter(bool allow_explicit_default_value,
ParamList* params) {
TRACE_PARSER("ParseFormalParameter");
ParamDesc parameter;
bool var_seen = false;
bool this_seen = false;
if (CurrentToken() == Token::kFINAL) {
ConsumeToken();
parameter.is_final = true;
} else if (CurrentToken() == Token::kVAR) {
ConsumeToken();
var_seen = true;
// The parameter type is the 'var' type.
parameter.type = &Type::ZoneHandle(Type::VarType());
}
if (CurrentToken() == Token::kTHIS) {
ConsumeToken();
ExpectToken(Token::kPERIOD);
this_seen = true;
parameter.is_field_initializer = true;
}
if (params->implicitly_final) {
parameter.is_final = true;
}
if ((parameter.type == NULL) && (CurrentToken() == Token::kVOID)) {
ConsumeToken();
// This must later be changed to a closure type if we recognize
// a closure/function type parameter. We check this at the end
// of ParseFormalParameter.
parameter.type = &Type::ZoneHandle(Type::VoidType());
}
if (parameter.type == NULL) {
// At this point, we must see an identifier for the type or the
// function parameter.
if (CurrentToken() != Token::kIDENT) {
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.
(follower == Token::kIDENT) || // Parameter name following a type.
(follower == Token::kTHIS)) { // Field parameter following a type.
parameter.type = &Type::ZoneHandle(
ParseType(is_top_level_ ? kCanResolve : kMustResolve));
} else {
parameter.type = &Type::ZoneHandle(Type::VarType());
}
}
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.
if (CurrentToken() != Token::kIDENT) {
ErrorMsg("parameter name expected");
}
parameter.name = CurrentLiteral();
parameter.name_pos = token_index_;
ConsumeToken();
if (parameter.is_field_initializer) {
params->has_field_initializer = true;
}
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 Type& result_type = Type::Handle(parameter.type->raw());
// Finish parsing the function type parameter.
ParamList func_params;
const bool no_explicit_default_values = false;
ParseFormalParameterList(no_explicit_default_values, &func_params);
// Change the name of the parameter type to be the signature of the
// function type.
// Note that the function type signature may involve parameters of a type
// that is a type parameter of the enclosing class, so we parameterize the
// signature with the type parameters of the enclosing class, if any.
// TODO(regis): Revisit if this is not the right thing to do.
const bool is_static =
current_function().IsNull() || current_function().IsInStaticScope();
const Function& signature_function = Function::Handle(
Function::New(*parameter.name,
RawFunction::kSignatureFunction,
is_static,
/* is_const = */ false,
parameter.name_pos));
signature_function.set_owner(current_class());
signature_function.set_result_type(result_type);
AddFormalParamsToFunction(&func_params, signature_function);
const String& signature = String::Handle(signature_function.Signature());
// Lookup the class named signature and only create a new class if it does
// not exist yet.
Class& signature_class = Class::ZoneHandle(LookupClass(signature));
if (signature_class.IsNull()) {
signature_class = Class::NewSignatureClass(signature,
signature_function,
script_,
parameter.name_pos);
// Record the function signature class in the library.
library_.AddClass(signature_class);
}
ASSERT(!Function::Handle(signature_class.signature_function()).IsNull());
ASSERT(Class::Handle(signature_function.signature_class()).IsNull());
signature_function.set_signature_class(signature_class);
// The type of the parameter is now the signature class.
// TODO(regis): What are the type arguments?
parameter.type = &Type::ZoneHandle(Type::NewRawType(signature_class));
if (!is_top_level_) {
const String& errmsg = String::Handle(
ClassFinalizer::FinalizeTypeWhileParsing(*parameter.type));
if (!errmsg.IsNull()) {
ErrorMsg(errmsg.ToCString());
}
}
}
}
if (CurrentToken() == Token::kASSIGN) {
if (!allow_explicit_default_value) {
// TODO(regis): Remove support for legacy optional parameters:
// The if guard below needs to be removed, i.e. the error must
// be reported for both positional and named optional parameters.
if (params->has_named_optional_parameters) {
ErrorMsg("parameter must not specify a default value");
}
}
if (FLAG_warn_params && !params->has_named_optional_parameters) {
// Turn on warnings automagically.
bool save_silent = FLAG_silent_warnings;
FLAG_silent_warnings = false;
Warning("legacy optional parameter '%s'", parameter.name->ToCString());
FLAG_silent_warnings = save_silent;
}
ConsumeToken();
params->num_optional_parameters++;
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_named_optional_parameters) {
// Implicit default value is null.
params->num_optional_parameters++;
parameter.default_value = &Object::ZoneHandle();
} else {
// TODO(regis): Remove support of legacy syntax.
params->num_fixed_parameters++;
if (params->num_optional_parameters > 0) {
ErrorMsg("optional parameters must be last");
}
}
}
if (parameter.type->IsVoidType()) {
ErrorMsg("parameter '%s' may not be 'void'", parameter.name->ToCString());
}
params->parameters->Add(parameter);
}
void Parser::ParseFormalParameterList(bool allow_explicit_default_values,
ParamList* params) {
TRACE_PARSER("ParseFormalParameterList");
ASSERT(CurrentToken() == Token::kLPAREN);
if (LookaheadToken(1) != Token::kRPAREN) {
// Parse positional parameters.
ParseFormalParameters(allow_explicit_default_values,
params);
if (CurrentToken() == Token::kLBRACK) {
ASSERT(!params->has_named_optional_parameters);
params->has_named_optional_parameters = true;
// Parse named optional parameters.
ParseFormalParameters(allow_explicit_default_values,
params);
if (CurrentToken() != Token::kRBRACK) {
ErrorMsg("',' or ']' expected");
}
ExpectToken(Token::kRBRACK);
}
if ((CurrentToken() != Token::kRPAREN) &&
!params->has_named_optional_parameters) {
ErrorMsg("',' or ')' expected");
}
} else {
ConsumeToken();
}
ExpectToken(Token::kRPAREN);
}
// Parses a sequence of normal or named formal parameters.
void Parser::ParseFormalParameters(bool allow_explicit_default_values,
ParamList* params) {
TRACE_PARSER("ParseFormalParameters");
do {
ConsumeToken();
if (!params->has_named_optional_parameters &&
(CurrentToken() == Token::kLBRACK)) {
// End of normal parameters, start of named parameters.
return;
}
ParseFormalParameter(allow_explicit_default_values, params);
} while (CurrentToken() == Token::kCOMMA);
}
String& Parser::ParseNativeDeclaration() {
TRACE_PARSER("ParseNativeDeclaration");
ASSERT(IsLiteral("native"));
ConsumeToken();
if (CurrentToken() != Token::kSTRING) {
ErrorMsg("string literal expected");
}
String& native_name = *CurrentLiteral();
ConsumeToken();
ExpectSemicolon();
return native_name;
}
void Parser::CheckFunctionIsCallable(intptr_t token_index,
const Function& function) {
if (Class::Handle(function.owner()).is_interface()) {
ErrorMsg(token_index, "cannot call function of interface '%s'",
function.ToFullyQualifiedCString());
}
}
static RawFunction* ResolveDynamicFunction(const Class& cls,
const String& name) {
Function& func = Function::Handle(cls.LookupDynamicFunction(name));
if (func.IsNull()) {
Class& super_cls = Class::Handle(cls.SuperClass());
while (!super_cls.IsNull()) {
func = super_cls.LookupDynamicFunction(name);
if (!func.IsNull()) {
return func.raw();
}
super_cls = super_cls.SuperClass();
}
}
return func.raw();
}
AstNode* Parser::ParseSuperCall(const String& function_name) {
TRACE_PARSER("ParseSuperCall");
ASSERT(CurrentToken() == Token::kLPAREN);
const intptr_t supercall_pos = token_index_;
const Class& super_class = Class::Handle(current_class().SuperClass());
if (super_class.IsNull()) {
ErrorMsg("class '%s' does not have a superclass",
String::Handle(current_class().Name()).ToCString());
}
// 'this' parameter is the first argument to super call.
AstNode* implicit_argument = LoadReceiver();
ArgumentListNode* arguments =
ParseActualParameters(implicit_argument, kAllowConst);
// Resolve the function.
const Function& super_function = Function::ZoneHandle(
ResolveDynamicFunction(super_class, function_name));
if (super_function.IsNull()) {
ErrorMsg(supercall_pos,
"function '%s' not found in super class",
function_name.ToCString());
}
CheckFunctionIsCallable(supercall_pos, super_function);
return new StaticCallNode(supercall_pos, super_function, arguments);
}
AstNode* Parser::CreateImplicitClosureNode(const Function& func,
intptr_t token_pos,
AstNode* receiver) {
Function& implicit_closure_function =
Function::ZoneHandle(func.ImplicitClosureFunction());
ASSERT(!implicit_closure_function.IsNull());
ASSERT(implicit_closure_function.is_static()); // TODO(regis): For now.
AstNode* node;
if (receiver == NULL) {
ASSERT(func.is_static());
node = new StaticImplicitClosureNode(token_pos, implicit_closure_function);
} else {
ASSERT(!func.is_static());
node = new ImplicitClosureNode(token_pos,
implicit_closure_function,
receiver);
}
return node;
}
AstNode* Parser::ParseSuperFieldAccess(const String& field_name) {
const intptr_t field_pos = token_index_;
const Class& super_class = Class::Handle(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();
const String& getter_name =
String::ZoneHandle(Field::GetterName(field_name));
const Function& super_getter = Function::ZoneHandle(
ResolveDynamicFunction(super_class, getter_name));
if (super_getter.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(
ResolveDynamicFunction(super_class, field_name));
if (super_function.IsNull()) {
ErrorMsg(field_pos, "field or getter '%s' not found in superclass",
field_name.ToCString());
}
return CreateImplicitClosureNode(super_function,
field_pos,
implicit_argument);
}
// All dynamic getters take one argument and no named arguments.
ASSERT(super_getter.AreValidArgumentCounts(1, 0));
ArgumentListNode* getter_arguments = new ArgumentListNode(field_pos);
getter_arguments->Add(implicit_argument);
AstNode* super_field =
new StaticCallNode(field_pos, super_getter, getter_arguments);
if (Token::IsAssignmentOperator(CurrentToken())) {
const String& setter_name =
String::ZoneHandle(Field::SetterName(field_name));
const Function& super_setter = Function::ZoneHandle(
ResolveDynamicFunction(super_class, setter_name));
if (super_setter.IsNull()) {
ErrorMsg(field_pos,
"field '%s' not assignable in superclass",
field_name.ToCString());
}
// All dynamic setters take two arguments and no named arguments.
ASSERT(super_setter.AreValidArgumentCounts(2, 0));
Token::Kind assignment_op = CurrentToken();
ConsumeToken();
AstNode* value = ParseExpr(kAllowConst);
value = ExpandAssignableOp(field_pos, assignment_op, super_field, value);
ArgumentListNode* setter_arguments = new ArgumentListNode(field_pos);
setter_arguments->Add(implicit_argument);
setter_arguments->Add(value);
super_field = new StaticCallNode(field_pos, super_setter, setter_arguments);
}
return super_field;
}
AstNode* Parser::ParseSuperInitializer(const Class& cls,
LocalVariable* receiver) {
TRACE_PARSER("ParseSuperInitializer");
ASSERT(CurrentToken() == Token::kSUPER);
const intptr_t supercall_pos = token_index_;
ConsumeToken();
const Class& super_class = Class::Handle(cls.SuperClass());
ASSERT(!super_class.IsNull());
String& ctor_name = String::Handle(super_class.Name());
String& ctor_suffix = String::Handle(String::NewSymbol("."));
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ctor_suffix = String::Concat(
ctor_suffix, *ExpectIdentifier("constructor name expected"));
}
ctor_name = String::Concat(ctor_name, ctor_suffix);
if (CurrentToken() != Token::kLPAREN) {
ErrorMsg("parameter list expected");
}
// 'this' parameter is the first argument to super class constructor.
AstNode* implicit_argument = new LoadLocalNode(supercall_pos, *receiver);
ArgumentListNode* arguments =
ParseActualParameters(implicit_argument, kAllowConst);
// Resolve the constructor.
const Function& super_ctor = Function::ZoneHandle(
super_class.LookupConstructor(ctor_name));
if (super_ctor.IsNull() ||
!super_ctor.AreValidArguments(arguments->length(),
arguments->names())) {
ErrorMsg(supercall_pos,
"super class constructor '%s' not found",
ctor_name.ToCString());
}
CheckFunctionIsCallable(supercall_pos, super_ctor);
return new StaticCallNode(supercall_pos, super_ctor, arguments);
}
AstNode* Parser::ParseInitializer(const Class& cls, LocalVariable* receiver) {
TRACE_PARSER("ParseInitializer");
const intptr_t field_pos = token_index_;
if (CurrentToken() == Token::kTHIS) {
ConsumeToken();
ExpectToken(Token::kPERIOD);
}
const String& field_name = *ExpectIdentifier("field name expected");
ExpectToken(Token::kASSIGN);
// TODO(5412278): Need to check or ensure that the initializer expression
// does not have access to the instance fields of the object.
const bool saved_mode = SetAllowFunctionLiterals(false);
AstNode* init_expr = ParseConditionalExpr();
SetAllowFunctionLiterals(saved_mode);
Field& field = Field::ZoneHandle(cls.LookupInstanceField(field_name));
if (field.IsNull()) {
ErrorMsg(field_pos, "unresolved reference to instance field '%s'",
field_name.ToCString());
}
AstNode* instance = new LoadLocalNode(field_pos, *receiver);
return new StoreInstanceFieldNode(field_pos, instance, field, init_expr);
}
void Parser::CheckConstFieldsInitialized(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() || !field.is_final()) {
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) {
ErrorMsg("const field '%s' not initialized",
String::Handle(field.name()).ToCString());
}
}
}
struct FieldInitExpression {
Field* inst_field;
AstNode* expr;
};
void Parser::ParseInitializedInstanceFields(const Class& cls,
GrowableArray<FieldInitExpression>* initializers) {
const Array& fields = Array::Handle(cls.fields());
Field& f = Field::Handle();
intptr_t saved_pos = token_index_;
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);
intptr_t field_pos = field.token_index();
SetPosition(field_pos);
ASSERT(CurrentToken() == Token::kIDENT);
ConsumeToken();
ExpectToken(Token::kASSIGN);
AstNode* init_expr = ParseConstExpr();
ASSERT(init_expr != NULL);
FieldInitExpression initializer;
initializer.inst_field = &field;
initializer.expr = init_expr;
initializers->Add(initializer);
}
}
SetPosition(saved_pos);
}
void Parser::ParseInitializers(const Class& cls, LocalVariable* receiver) {
TRACE_PARSER("ParseInitializers");
AstNode* init_statement = NULL;
AstNode* super_init_statement = NULL;
// TODO(4995181): Allow super initializer to appear in any position
// of the initializer list.
if (CurrentToken() == Token::kSUPER) {
super_init_statement = ParseSuperInitializer(cls, receiver);
} else {
init_statement = ParseInitializer(cls, receiver);
current_block_->statements->Add(init_statement);
}
while (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
init_statement = ParseInitializer(cls, receiver);
current_block_->statements->Add(init_statement);
}
// The call to super constructor is to be done after all initializers.
if (super_init_statement != NULL) {
current_block_->statements->Add(super_init_statement);
}
}
void Parser::ParseConstructorRedirection(const Class& cls,
LocalVariable* receiver) {
ASSERT(CurrentToken() == Token::kTHIS);
intptr_t call_pos = token_index_;
ConsumeToken();
String& ctor_name = String::Handle(cls.Name());
String& ctor_suffix = String::Handle(String::NewSymbol("."));
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ctor_suffix = String::Concat(
ctor_suffix, *ExpectIdentifier("constructor name expected"));
}
ctor_name = String::Concat(ctor_name, ctor_suffix);
if (CurrentToken() != Token::kLPAREN) {
ErrorMsg("parameter list expected");
}
// 'this' parameter is the first argument to super class constructor.
AstNode* implicit_argument = new LoadLocalNode(call_pos, *receiver);
ArgumentListNode* arguments =
ParseActualParameters(implicit_argument, kAllowConst);
// Resolve the constructor.
const Function& redirect_ctor = Function::ZoneHandle(
cls.LookupConstructor(ctor_name));
if (redirect_ctor.IsNull() ||
!redirect_ctor.AreValidArguments(arguments->length(),
arguments->names())) {
ErrorMsg(call_pos, "constructor '%s' not found",
ctor_name.ToCString());
}
CheckFunctionIsCallable(call_pos, redirect_ctor);
current_block_->statements->Add(
new StaticCallNode(call_pos, redirect_ctor, arguments));
}
SequenceNode* Parser::MakeImplicitConstructor(const Function& func) {
ASSERT(func.IsConstructor());
intptr_t ctor_pos = token_index_;
// Implicit 'this' is the only parameter/local variable.
OpenFunctionBlock(func);
// Parse expressions of instance fields that have an explicit
// initializers.
GrowableArray<FieldInitExpression> initializers;
Class& cls = Class::Handle(func.owner());
ParseInitializedInstanceFields(cls, &initializers);
LocalVariable* receiver = new LocalVariable(
ctor_pos,
String::ZoneHandle(String::NewSymbol(kThisName)),
Type::ZoneHandle(Type::VarType()));
current_block_->scope->AddVariable(receiver);
// Now that the "this" parameter is in scope, we can generate the code
// to strore the initializer expressions in the respective instance fields.
for (int i = 0; i < initializers.length(); i++) {
const Field* field = initializers[i].inst_field;
AstNode* instance = new LoadLocalNode(field->token_index(), *receiver);
AstNode* field_init =
new StoreInstanceFieldNode(field->token_index(),
instance,
*field,
initializers[i].expr);
current_block_->statements->Add(field_init);
}
// Super call to constructor of super class.
const Class& super_class = Class::Handle(cls.SuperClass());
ASSERT(!super_class.IsNull());
String& ctor_name = String::Handle(super_class.Name());
String& ctor_suffix = String::Handle(String::NewSymbol("."));
ctor_name = String::Concat(ctor_name, ctor_suffix);
ctor_name = String::NewSymbol(ctor_name);
ArgumentListNode* arguments = new ArgumentListNode(ctor_pos);
AstNode* implicit_argument = new LoadLocalNode(ctor_pos, *receiver);
arguments->Add(implicit_argument);
const Function& super_ctor = Function::ZoneHandle(
super_class.LookupConstructor(ctor_name));
if (super_ctor.IsNull() ||
!super_ctor.AreValidArgumentCounts(arguments->length(), 0)) {
ErrorMsg(ctor_pos,
"super class constructor '%s' not found",
ctor_name.ToCString());
}
current_block_->statements->Add(
new StaticCallNode(ctor_pos, super_ctor, arguments));
CheckConstFieldsInitialized(cls);
// Empty constructor body.
SequenceNode* statements = CloseBlock();
return statements;
}
// Parser is at the opening parenthesis of the formal parameter declaration
// of function. Parse the formal parameters and code.
SequenceNode* Parser::ParseFunc(const Function& func,
Array& default_parameter_values) {
if (IsLiteral("class")) {
// Special case: implicit constructor. There is no source text to
// parse. We just build the sequence node by hand.
return MakeImplicitConstructor(func);
}
// Build local scope for function.
OpenFunctionBlock(func);
ParamList params;
// The first parameter of a factory is the TypeArguments vector of the type
// of the instance to be allocated. We name this hidden parameter 'this'.
const bool has_receiver =
!func.is_static() || func.IsConstructor() || func.IsFactory();
const bool are_implicitly_final = func.is_const() && func.IsConstructor();
const bool allow_explicit_default_values = true;
ASSERT(CurrentToken() == Token::kLPAREN);
if (has_receiver) {
params.AddReceiver(token_index_);
}
if (are_implicitly_final) {
params.SetImplicitlyFinal();
}
ParseFormalParameterList(allow_explicit_default_values, &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(Type::Handle(func.result_type()).IsResolved());
ASSERT(func.NumberOfParameters() == params.parameters->length());
// If this is a constructor, initialize instance fields that have an
// explicit initializer expression. This has to be done before code
// for field initializer parameters are is generated.
// NB: the instance field initializers have to be compiled before
// the parameters are added to the scope, so that a parameter
// name cannot shadow a name used in the field initializer expression.
GrowableArray<FieldInitExpression> initializers;
if (func.IsConstructor()) {
Class& cls = Class::Handle(func.owner());
ParseInitializedInstanceFields(cls, &initializers);
}
// Now populate function scope with the formal parameters.
AddFormalParamsToScope(&params, current_block_->scope);
// Now that the "this" parameter is in scope, we can generate the code
// to strore the initializer expressions in the respective instance fields.
// We do this before the field parameters and the initializers from the
// constuctor's initializer list get compiled.
if (initializers.length() > 0) {
LocalVariable* receiver = current_block_->scope->VariableAt(0);
for (int i = 0; i < initializers.length(); i++) {
const Field* field = initializers[i].inst_field;
AstNode* instance = new LoadLocalNode(field->token_index(), *receiver);
AstNode* field_init =
new StoreInstanceFieldNode(field->token_index(),
instance,
*field,
initializers[i].expr);
current_block_->statements->Add(field_init);
}
}
// Turn formal field parameters into field initializers or report error
// if the function is not a constructor
if (params.has_field_initializer) {
LocalVariable* receiver = current_block_->scope->VariableAt(0);
Class& cls = Class::ZoneHandle(func.owner());
for (int i = 0; i < params.parameters->length(); i++) {
ParamDesc& param = (*params.parameters)[i];
if (param.is_field_initializer) {
if (!func.IsConstructor()) {
ErrorMsg(param.name_pos,
"field initializer only allowed in constructors");
}
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());
}
const String& mangled_name =
String::ZoneHandle(MangledInitParamName(field_name));
AstNode* instance = new LoadLocalNode(param.name_pos, *receiver);
LocalVariable* p =
current_block_->scope->LocalLookupVariable(mangled_name);
ASSERT(p != NULL);
AstNode* value = new LoadLocalNode(param.name_pos, *p);
AstNode* initializer =
new StoreInstanceFieldNode(param.name_pos, instance, field, value);
current_block_->statements->Add(initializer);
}
}
}
if (func.IsConstructor()) {
Class& cls = Class::ZoneHandle(func.owner());
bool initialized_check_needed = true;
if (CurrentToken() == Token::kCOLON) {
ConsumeToken();
LocalVariable* receiver = current_block_->scope->VariableAt(0);
ASSERT(receiver != NULL);
if ((CurrentToken() == Token::kTHIS) &&
((LookaheadToken(1) == Token::kLPAREN) ||
((LookaheadToken(1) == Token::kPERIOD) &&
(LookaheadToken(3) == Token::kLPAREN)))) {
// Either we see this(...) or this.xxx(...) which is a
// redirected constructor. We don't need to check whether
// const fields are initialized. The other constructor will
// guarantee that.
initialized_check_needed = false;
ParseConstructorRedirection(cls, receiver);
} else {
ParseInitializers(cls, receiver);
}
}
if (initialized_check_needed) {
CheckConstFieldsInitialized(cls);
}
}
if (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 or
// allocation of generic types.
if (current_class().IsParameterized() &&
(!current_function().IsInStaticScope() ||
current_function().IsInFactoryScope())) {
// 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 the
// receiver to instantiate types.
if (FLAG_enable_type_checks) {
CaptureReceiver();
}
}
}
if (CurrentToken() == Token::kLBRACE) {
ConsumeToken();
ParseStatementSequence();
ExpectToken(Token::kRBRACE);
} else if (CurrentToken() == Token::kARROW) {
ConsumeToken();
intptr_t expr_pos = token_index_;
AstNode* expr = ParseExpr(kAllowConst);
ASSERT(expr != NULL);
current_block_->statements->Add(new ReturnNode(expr_pos, expr));
} else if (IsLiteral("native")) {
ParseNativeFunctionBlock(&params, func);
} else if (CurrentToken() == Token::kSEMICOLON) {
ConsumeToken();
ASSERT(func.IsConstructor());
// Some constructors have no function body.
} else {
UnexpectedToken();
}
SequenceNode* statements = CloseBlock();
return statements;
}
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() {
if (CurrentToken() == Token::kSUPER) {
ConsumeToken();
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ExpectIdentifier("identifier expected");
}
if (CurrentToken() != Token::kLPAREN) {
ErrorMsg("'(' expected");
}
SkipToMatchingParenthesis();
} else {
SkipIf(Token::kTHIS);
SkipIf(Token::kPERIOD);
ExpectIdentifier("identifier expected");
ExpectToken(Token::kASSIGN);
SetAllowFunctionLiterals(false);
SkipExpr();
SetAllowFunctionLiterals(true);
}
while (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
SkipIf(Token::kTHIS);
SkipIf(Token::kPERIOD);
ExpectIdentifier("instance field expected");
ExpectToken(Token::kASSIGN);
SetAllowFunctionLiterals(false);
SkipExpr();
SetAllowFunctionLiterals(true);
}
}
void Parser::ParseQualIdent(QualIdent* qual_ident) {
ASSERT(CurrentToken() == Token::kIDENT);
if (!is_top_level_) {
bool local_ident = ResolveIdentInLocalScope(token_index_,
*CurrentLiteral(),
NULL);
qual_ident->ident_pos = token_index_;
qual_ident->ident = CurrentLiteral();
qual_ident->lib_prefix = NULL;
qual_ident->qualifier = NULL;
qual_ident->local_scope_ident = local_ident;
ConsumeToken();
if (!local_ident && (CurrentToken() == Token::kPERIOD)) {
LibraryPrefix& lib_prefix = LibraryPrefix::ZoneHandle();
lib_prefix = current_class().LookupLibraryPrefix(*(qual_ident->ident));
if (!lib_prefix.IsNull()) {
// We have a library prefix qualified identifier.
ConsumeToken(); // Consume the kPERIOD token.
qual_ident->lib_prefix = &lib_prefix;
qual_ident->qualifier = qual_ident->ident;
qual_ident->ident_pos = token_index_;
qual_ident->ident = ExpectIdentifier("identifier expected after '.'");
}
}
} else {
qual_ident->ident_pos = token_index_;
qual_ident->ident = CurrentLiteral();
qual_ident->lib_prefix = NULL;
qual_ident->qualifier = NULL;
qual_ident->local_scope_ident = false;
ConsumeToken();
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken(); // Consume the kPERIOD token.
qual_ident->qualifier = qual_ident->ident;
qual_ident->ident_pos = token_index_;
qual_ident->ident = ExpectIdentifier("identifier expected after '.'");
}
}
}
void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) {
ASSERT(CurrentToken() == Token::kLPAREN);
intptr_t method_pos = this->token_index_;
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->IsConstructor() || method->IsFactory())) {
ErrorMsg(method->name_pos, "'const' not allowed for methods");
}
if (method->IsConstructor() && method->has_static) {
ErrorMsg(method->name_pos, "constructor cannot be 'static'");
}
if (method->IsConstructor() && method->has_const) {
Class& cls = Class::ZoneHandle(LookupClass(members->class_name()));
cls.set_is_const();
}
if (method->has_abstract && members->is_interface()) {
ErrorMsg(method->name_pos,
"'abstract' method only allowed in class definition");
}
if (members->FunctionNameExists(*method->name, method->kind)) {
ErrorMsg(method->name_pos,
"field or method '%s' already defined", method->name->ToCString());
}
// Parse the formal parameters.
// The first parameter of factory methods is an implicit parameter called
// 'this' of type TypeArguments.
const bool has_this_param =
!method->has_static || method->IsConstructor() || method->has_factory;
const bool are_implicitly_final = method->has_const;
const bool allow_explicit_default_values =
(!method->has_abstract && !members->is_interface());
const intptr_t formal_param_pos = token_index_;
method->params.Clear();
if (has_this_param) {
method->params.AddReceiver(formal_param_pos);
}
if (are_implicitly_final) {
method->params.SetImplicitlyFinal();
}
ParseFormalParameterList(allow_explicit_default_values, &method->params);
if (method->IsGetter() || method->IsSetter()) {
int expected_num_parameters = 0;
if (method->IsGetter()) {
expected_num_parameters = (method->has_static) ? 0 : 1;
method->name = &String::ZoneHandle(Field::GetterName(*method->name));
} else {
ASSERT(method->IsSetter());
expected_num_parameters = (method->has_static) ? 1 : 2;
method->name = &String::ZoneHandle(Field::SetterName(*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 initializers.
if (CurrentToken() == Token::kCOLON) {
if (!method->IsConstructor()) {
ErrorMsg("initializers only allowed on constructors");
}
ConsumeToken();
if ((CurrentToken() == Token::kTHIS) &&
((LookaheadToken(1) == Token::kLPAREN) ||
LookaheadToken(3) == Token::kLPAREN)) {
// Redirected constructor: either this(...) or this.xxx(...).
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");
}
ExpectToken(Token::kTHIS);
String& redir_name = String::ZoneHandle(
String::Concat(members->class_name(),
String::Handle(String::NewSymbol("."))));
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
redir_name = String::Concat(redir_name,
*ExpectIdentifier("constructor name expected"));
}
method->redirect_name = &redir_name;
if (CurrentToken() != Token::kLPAREN) {
ErrorMsg("'(' expected");
}
SkipToMatchingParenthesis();
} else {
SkipInitializers();
}
}
// Only constructors can redirect to another method.
ASSERT((method->redirect_name == NULL) || method->IsConstructor());
if ((CurrentToken() == Token::kLBRACE) ||
(CurrentToken() == Token::kARROW)) {
if (method->has_abstract) {
ErrorMsg(method->name_pos,
"abstract method '%s' may not have function body",
method->name->ToCString());
} else if (method->IsConstructor() && method->has_const) {
ErrorMsg(method->name_pos,
"const constructor '%s' may not have function body",
method->name->ToCString());
} else if (method->IsFactory() && method->has_const) {
ErrorMsg(method->name_pos,
"const factory '%s' may not have function body",
method->name->ToCString());
} else if (members->is_interface()) {
ErrorMsg(method->name_pos,
"function body not allowed in interface declaration");
}
if (CurrentToken() == Token::kLBRACE) {
SkipBlock();
} else {
ConsumeToken();
SkipExpr();
ExpectSemicolon();
}
} else if (IsLiteral("native")) {
if (method->has_abstract) {
ErrorMsg(method->name_pos,
"abstract method '%s' may not have function body",
method->name->ToCString());
} else if (members->is_interface()) {
ErrorMsg(method->name_pos,
"function body not allowed in interface declaration");
} else if (method->IsConstructor() && method->has_const) {
ErrorMsg(method->name_pos,
"const constructor '%s' may not have function body",
method->name->ToCString());
}
ParseNativeDeclaration();
} else if (CurrentToken() == Token::kSEMICOLON) {
if (members->is_interface() ||
method->has_abstract ||
(method->redirect_name != NULL) ||
method->IsConstructor()) {
ConsumeToken();
} else {
ErrorMsg(method->name_pos,
"function body expected for method '%s'",
method->name->ToCString());
}
} else {
if (members->is_interface() ||
method->has_abstract ||
(method->redirect_name != NULL) ||
(method->IsConstructor() && method->has_const)) {
ExpectSemicolon();
} else {
ErrorMsg(method->name_pos,
"function body expected for method '%s'",
method->name->ToCString());
}
}
RawFunction::Kind function_kind;
if (method->IsFactoryOrConstructor()) {
function_kind = RawFunction::kConstructor;
} else if (method->has_abstract) {
function_kind = RawFunction::kAbstract;
} else if (method->IsGetter()) {
function_kind = RawFunction::kGetterFunction;
} else if (method->IsSetter()) {
function_kind = RawFunction::kSetterFunction;
} else {
function_kind = RawFunction::kFunction;
}
Function& func = Function::ZoneHandle(
Function::New(*method->name,
function_kind,
method->has_static,
method->has_const,
method_pos));
func.set_result_type(*method->type);
// 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) {
// 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);
if (field->has_const) {
ErrorMsg("keyword 'const' not allowed in field declaration");
}
if (field->has_abstract) {
ErrorMsg("keyword 'abstract' not allowed in field declaration");
}
if (field->has_factory) {
ErrorMsg("keyword 'factory' not allowed in field declaration");
}
if (members->FieldNameExists(*field->name)) {
ErrorMsg(field->name_pos,
"'%s' field/method already defined\n", field->name->ToCString());
}
while (true) {
bool has_initializer = CurrentToken() == Token::kASSIGN;
if (has_initializer) {
ConsumeToken();
// For static final fields, the initialization expression
// will be parsed through the kConstImplicitGetter method
// invocation/compilation.
// For instance fields, the expression is parsed when a constructor
// is compiled.
SkipExpr();
} else {
if (field->has_static && field->has_final) {
ErrorMsg(field->name_pos,
"static final field '%s' must have an initializer expression",
field->name->ToCString());
}
}
// Create the field object.
Field& class_field = Field::ZoneHandle(
Field::New(*field->name,
field->has_static,
field->has_final,
field->name_pos));
class_field.set_type(*field->type);
class_field.set_has_initializer(has_initializer);
members->AddField(&class_field);
// For static final fields, set value to "uninitialized" and
// create a kConstImplicitGetter getter method.
if (field->has_static && has_initializer) {
class_field.set_value(Instance::Handle(Object::sentinel()));
String& getter_name = String::ZoneHandle(Field::GetterName(*field->name));
Function& getter = Function::ZoneHandle(
Function::New(getter_name, RawFunction::kConstImplicitGetter,
field->has_static, field->has_final,
field->name_pos));
getter.set_result_type(*field->type);
members->AddFunction(&getter);
}
// For instance fields, we create implicit getter and setter methods.
if (!field->has_static) {
String& getter_name =
String::ZoneHandle(Field::GetterName(*field->name));
Function& getter = Function::ZoneHandle(
Function::New(getter_name, RawFunction::kImplicitGetter,
field->has_static, field->has_final,
field->name_pos));
ParamList params;
params.AddReceiver(token_index_);
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::ZoneHandle(
Field::SetterName(*field->name));
Function& setter = Function::ZoneHandle(
Function::New(setter_name, RawFunction::kImplicitSetter,
field->has_static, field->has_final,
field->name_pos));
ParamList params;
params.AddReceiver(token_index_);
params.AddFinalParameter(token_index_, "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->token_index_;
field->name = ExpectIdentifier("field name expected");
}
ExpectSemicolon();
}
void Parser::ParseClassMemberDefinition(ClassDesc* members) {
MemberDesc member;
current_member_ = &member;
if (CurrentToken() == Token::kABSTRACT) {
ConsumeToken();
member.has_abstract = true;
}
if (CurrentToken() == Token::kSTATIC) {
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 'var' type.
member.type = &Type::ZoneHandle(Type::VarType());
} else if (CurrentToken() == Token::kFACTORY) {
ConsumeToken();
member.has_factory = true;
member.has_static = true;
// The member result type is the type of this class.
// TODO(regis): What are the type arguments?
member.type =
&Type::ZoneHandle(Type::NewRawType(Class::Handle(members->clazz())));
}
// 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) {
// 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.
(follower == Token::kIDENT) || // 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_);
member.type = &Type::ZoneHandle(ParseType(kCanResolve));
}
}
}
// Optionally parse a (possibly named) constructor name or factory.
if ((CurrentToken() == Token::kIDENT) &&
(CurrentLiteral()->Equals(members->class_name()) || member.has_factory)) {
member.name = CurrentLiteral();
member.name_pos = this->token_index_;
// Factory result type is the same as the type name of the factory.
// TODO(srdjan): Implement checks in class finalization when all types have
// been resolved.
if (member.has_factory && !member.name->Equals(members->class_name())) {
const TypeArguments& arguments = TypeArguments::Handle();
member.type = &Type::ZoneHandle(
Type::NewParameterizedType(*member.name, arguments));
}
ConsumeToken();
// We must be dealing with a constructor or named constructor.
member.kind = RawFunction::kConstructor;
String& ctor_suffix = String::ZoneHandle(String::NewSymbol("."));
if (CurrentToken() == Token::kPERIOD) {
// Named constructor.
ConsumeToken();
const String* name = ExpectIdentifier("identifier expected");
ctor_suffix = String::Concat(ctor_suffix, *name);
}
*member.name = String::Concat(*member.name, ctor_suffix);
// Ensure that names are symbols.
*member.name = String::NewSymbol(*member.name);
if (member.type == NULL) {
// TODO(regis): What are the type arguments?
member.type =
&Type::ZoneHandle(Type::NewRawType(Class::Handle(members->clazz())));
} else {
// The type can only be already set in the factory case.
if (!member.has_factory) {
ErrorMsg(member.name_pos, "constructor must not specify return type");
}
}
if (CurrentToken() != Token::kLPAREN) {
ErrorMsg("left parenthesis expected");
}
} else if (CurrentToken() == Token::kGET) {
ConsumeToken();
member.kind = RawFunction::kGetterFunction;
member.name_pos = this->token_index_;
member.name = ExpectIdentifier("identifier expected");
// If the result type was not specified, it will be set to VarType below.
} else if (CurrentToken() == Token::kSET) {
ConsumeToken();
member.kind = RawFunction::kSetterFunction;
member.name_pos = this->token_index_;
member.name = ExpectIdentifier("identifier expected");
// The grammar allows a return type, so member.type is not always NULL here.
// However, the return type of a setter is ignored.
// TODO(regis): Revisit depending on the outcome of issue 4745047.
if (member.type == NULL) {
member.type = &Type::ZoneHandle(Type::VoidType());
}
} else if (CurrentToken() == Token::kOPERATOR) {
ConsumeToken();
if (!Token::CanBeOverloaded(CurrentToken())) {
ErrorMsg("invalid operator overloading");
}
if (member.has_static) {
ErrorMsg("operator overloading functions cannot be static");
}
member.kind = RawFunction::kFunction;
member.name_pos = this->token_index_;
member.name =
&String::ZoneHandle(String::NewSymbol(Token::Str(CurrentToken())));
ConsumeToken();
} else if (CurrentToken() == Token::kIDENT) {
member.name = CurrentLiteral();
member.name_pos = token_index_;
ConsumeToken();
} else {
ErrorMsg("identifier expected");
}
ASSERT(member.name != NULL);
if (CurrentToken() == Token::kLPAREN) {
if (members->is_interface() && member.has_static) {
if (member.has_factory) {
ErrorMsg("factory constructors are not allowed in interfaces");
} else {
ErrorMsg("static methods are not allowed in interfaces");
}
}
// Constructor or method.
if (member.type == NULL) {
member.type = &Type::ZoneHandle(Type::VarType());
}
ParseMethodOrConstructor(members, &member);
} else if (CurrentToken() == Token::kSEMICOLON ||
CurrentToken() == Token::kCOMMA ||
CurrentToken() == Token::kASSIGN) {
// Field definition.
if (member.type == NULL) {
if (member.has_final) {
member.type = &Type::ZoneHandle(Type::VarType());
} else {
ErrorMsg("missing 'var', 'final' or type in field declaration");
}
}
if (members->is_interface() && member.has_static && !member.has_final) {
ErrorMsg("static non-final fields are not allowed in interfaces");
}
ParseFieldDefinition(members, &member);
} else {
UnexpectedToken();
}
current_member_ = NULL;
members->AddMember(member);
}
void Parser::ParseClassDefinition(GrowableArray<const Class*>* classes) {
TRACE_PARSER("ParseClassDefinition");
intptr_t class_pos = token_index_;
ExpectToken(Token::kCLASS);
intptr_t classname_pos = token_index_;
String& class_name = *ExpectIdentifier("class name expected");
if (FLAG_trace_parser) {
OS::Print("TopLevel parsing class '%s'\n", class_name.ToCString());
}
Class& cls = Class::ZoneHandle();
Object& obj = Object::Handle(library_.LookupObject(class_name));
if (obj.IsNull()) {
cls = Class::New(class_name, script_);
library_.AddClass(cls);
} else {
if (!obj.IsClass()) {
ErrorMsg(classname_pos, "'%s' is already defined",
class_name.ToCString());
}
cls ^= obj.raw();
if (cls.is_interface()) {
ErrorMsg(classname_pos, "'%s' is already defined as interface",
class_name.ToCString());
} else if (cls.functions() != Array::Empty()) {
ErrorMsg(classname_pos, "class '%s' is already defined",
class_name.ToCString());
}
}
ASSERT(!cls.IsNull());
ASSERT(cls.functions() == Array::Empty());
set_current_class(cls);
ParseTypeParameters(cls);
Type& super_type = Type::Handle();
if (CurrentToken() == Token::kEXTENDS) {
ConsumeToken();
super_type = ParseType(kCanResolve);
if (super_type.IsInterfaceType()) {
ErrorMsg("class '%s' may implement, but cannot extend interface '%s'",
class_name.ToCString(),
String::Handle(super_type.Name()).ToCString());
}
} else {
// No extends clause: Implicitly extend Object.
super_type = Type::ObjectType();
}
ASSERT(!super_type.IsNull());
cls.set_super_type(super_type);
if (CurrentToken() == Token::kIMPLEMENTS) {
Array& interfaces = Array::Handle();
const intptr_t interfaces_pos = token_index_;
interfaces = ParseInterfaceList();
AddInterfaces(interfaces_pos, cls, interfaces);
}
ExpectToken(Token::kLBRACE);
ClassDesc members(cls, class_name, false, class_pos);
while (CurrentToken() != Token::kRBRACE) {
ParseClassMemberDefinition(&members);
}
ExpectToken(Token::kRBRACE);
CheckConstructors(&members);
cls.SetFields(Array::Handle(NewArray<Field>(members.fields())));
// Creating a new array for functions marks the class as parsed.
cls.SetFunctions(Array::Handle(NewArray<Function>(members.functions())));
classes->Add(&cls);
}
// 1. Add an implicit constructor if no explicit constructor is present.
// 2. Check for cycles in constructor redirection.
void Parser::CheckConstructors(ClassDesc* class_desc) {
// Add an implicit constructor if no explicit constructor is present.
if (!class_desc->has_constructor()) {
// The implicit constructor is unnamed, has no parameter, and contains
// a supercall in the initializer list.
String& ctor_name = String::ZoneHandle(
String::Concat(class_desc->class_name(),
String::Handle(String::NewSymbol("."))));
ctor_name = String::NewSymbol(ctor_name);
Function& ctor = Function::ZoneHandle(
Function::New(ctor_name,
RawFunction::kConstructor,
/* is_static = */ false,
/* is_const = */ false,
class_desc->token_pos()));
ParamList params;
params.AddReceiver(token_index_);
AddFormalParamsToFunction(&params, ctor);
// TODO(regis): What are the type arguments?
Type& result_type = Type::ZoneHandle(
Type::NewRawType(Class::Handle(class_desc->clazz())));
ctor.set_result_type(result_type);
class_desc->AddFunction(&ctor);
}
// 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];
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);
}
}
}
// 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 ((CurrentToken() == Token::kIDENT) &&
(LookaheadToken(1) == Token::kLPAREN)) {
return true;
}
const intptr_t saved_pos = token_index_;
bool is_alias_name = false;
if ((CurrentToken() == Token::kIDENT) &&
(LookaheadToken(1) == Token::kLT)) {
ConsumeToken();
if (IsTypeParameter() && (CurrentToken() == Token::kLPAREN)) {
is_alias_name = true;
}
}
SetPosition(saved_pos);
return is_alias_name;
}
void Parser::ParseFunctionTypeAlias(GrowableArray<const Class*>* classes) {
TRACE_PARSER("ParseFunctionTypeAlias");
ExpectToken(Token::kTYPEDEF);
Type& result_type = Type::Handle(Type::VarType());
intptr_t result_type_pos = token_index_;
if (CurrentToken() == Token::kVOID) {
ConsumeToken();
result_type = Type::VoidType();
} else if (!IsFunctionTypeAliasName()) {
result_type = ParseType(kDoNotResolve); // No owner class yet.
}
if (CurrentToken() != Token::kIDENT) {
ErrorMsg("function alias name expected");
}
const intptr_t alias_name_pos = token_index_;
const String* alias_name = CurrentLiteral();
ConsumeToken();
// Allocate a class to hold the type parameters and their 'extends'
// constraints. Make it the owner of the function type descriptor.
const Class& alias_owner = Class::Handle(
Class::New(String::Handle(String::NewSymbol("")), Script::Handle()));
set_current_class(alias_owner);
ParseTypeParameters(alias_owner);
if (CurrentToken() != Token::kLPAREN) {
ErrorMsg("formal parameter list expected");
}
// At this point, the type parameters have been parsed, so we can resolve the
// result type.
if (!result_type.IsNull() && !result_type.IsResolved()) {
ResolveTypeFromClass(result_type_pos, alias_owner, &result_type);
}
ParamList func_params;
const bool no_explicit_default_values = false;
ParseFormalParameterList(no_explicit_default_values, &func_params);
// TODO(regis): If no type parameters were specified, the function could be
// static. Revisit.
Function& signature_function = Function::Handle(
Function::New(*alias_name,
RawFunction::kSignatureFunction,
/* is_static = */ false,
/* is_const = */ false,
alias_name_pos));
signature_function.set_owner(alias_owner);
signature_function.set_result_type(result_type);
AddFormalParamsToFunction(&func_params, 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 class by its signature and only create a new canonical signature
// class if it does not exist yet.
Class& signature_class = Class::ZoneHandle(LookupClass(signature));
if (signature_class.IsNull()) {
signature_class = Class::NewSignatureClass(signature,
signature_function,
script_,
alias_name_pos);
// Record the function signature class in the library.
library_.AddClass(signature_class);
ASSERT(Class::Handle(signature_function.signature_class()).IsNull());
signature_function.set_signature_class(signature_class);
} else {
// Forget the just created function type desc and use the existing one.
signature_function = signature_class.signature_function();
ASSERT(signature_function.signature_class() == signature_class.raw());
}
// Lookup the class by its alias name and report an error if it exists.
Class& function_type_alias = Class::ZoneHandle(LookupClass(*alias_name));
if (function_type_alias.IsNull()) {
// Create the function type alias, but share the signature function of the
// canonical signature class.
function_type_alias = Class::NewSignatureClass(*alias_name,
signature_function,
script_,
alias_name_pos);
library_.AddClass(function_type_alias);
} else {
const char* format = function_type_alias.is_interface() ?
"'%s' is already defined" : "'%s' is already defined as class";
ErrorMsg(alias_name_pos, format, alias_name->ToCString());
}
ExpectSemicolon();
classes->Add(&function_type_alias);
}
void Parser::ParseInterfaceDefinition(GrowableArray<const Class*>* classes) {
TRACE_PARSER("ParseInterfaceDefinition");
intptr_t interface_pos = token_index_;
ExpectToken(Token::kINTERFACE);
intptr_t interfacename_pos = token_index_;
String& interface_name = *ExpectIdentifier("interface name expected");
if (FLAG_trace_parser) {
OS::Print("TopLevel parsing interface '%s'\n", interface_name.ToCString());
}
Class& interface = Class::ZoneHandle();
Object& obj = Object::Handle(library_.LookupObject(interface_name));
if (obj.IsNull()) {
interface = Class::NewInterface(interface_name, script_);
library_.AddClass(interface);
} else {
if (!obj.IsClass()) {
ErrorMsg(interfacename_pos, "'%s' is already defined",
interface_name.ToCString());
}
interface ^= obj.raw();
if (!interface.is_interface()) {
ErrorMsg(interfacename_pos,
"'%s' is already defined as class",
interface_name.ToCString());
} else if (interface.functions() != Array::Empty()) {
ErrorMsg(interfacename_pos,
"interface '%s' is already defined",
interface_name.ToCString());
}
}
ASSERT(!interface.IsNull());
ASSERT(interface.functions() == Array::Empty());
set_current_class(interface);
ParseTypeParameters(interface);
if (CurrentToken() == Token::kEXTENDS) {
Array& interfaces = Array::Handle();
const intptr_t interfaces_pos = token_index_;
interfaces = ParseInterfaceList();
AddInterfaces(interfaces_pos, interface, interfaces);
}
if (CurrentToken() == Token::kFACTORY) {
ConsumeToken();
Type& factory_type = Type::Handle();
const intptr_t factory_type_pos = token_index_;
factory_type = ParseType(kCanResolve);
if (factory_type.IsInterfaceType()) {
ErrorMsg(factory_type_pos,
"interface '%s' must have a factory class "
"but '%s' is an interface",
interface_name.ToCString(),
String::Handle(factory_type.Name()).ToCString());
}
interface.set_factory_type(factory_type);
}
ExpectToken(Token::kLBRACE);
ClassDesc members(interface, interface_name, true, interface_pos);
while (CurrentToken() != Token::kRBRACE) {
ParseClassMemberDefinition(&members);
}
ExpectToken(Token::kRBRACE);
interface.SetFields(Array::Handle(NewArray<Field>(members.fields())));
// Creating a new array for functions marks the interface as parsed.
interface.SetFunctions(
Array::Handle(NewArray<Function>(members.functions())));
ASSERT(interface.is_interface());
classes->Add(&interface);
}
// 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::kSAR) {
token_kind_ = Token::kGT;
} else if (token_kind_ == Token::kSHR) {
token_kind_ = Token::kSAR;
} else {
UNREACHABLE();
}
}
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::kSAR) ||
(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) {
if (CurrentToken() == Token::kLT) {
GrowableArray<String*> type_parameters;
GrowableArray<Type*> type_parameter_extends;
do {
ConsumeToken();
if (CurrentToken() != Token::kIDENT) {
ErrorMsg("type parameter name expected");
}
String& type_parameter_name = *CurrentLiteral();
ConsumeToken();
Type& type_extends = Type::ZoneHandle(Type::VarType());
if (CurrentToken() == Token::kEXTENDS) {
ConsumeToken();
type_extends = ParseType(kCanResolve);
}
type_parameters.Add(&type_parameter_name);
type_parameter_extends.Add(&type_extends);
} while (CurrentToken() == Token::kCOMMA);
Token::Kind token = CurrentToken();
if ((token == Token::kGT) ||
(token == Token::kSAR) ||
(token == Token::kSHR)) {
ConsumeRightAngleBracket();
} else {
ErrorMsg("right angle bracket expected");
}
cls.set_type_parameters(Array::Handle(NewArray<String>(type_parameters)));
cls.set_type_parameter_extends(
TypeArray::Handle(NewTypeArray(type_parameter_extends)));
}
}
RawTypeArguments* Parser::ParseTypeArguments(TypeResolution type_resolution) {
if (CurrentToken() == Token::kLT) {
GrowableArray<Type*> types;
do {
ConsumeToken();
Type& type = Type::ZoneHandle(ParseType(type_resolution));
types.Add(&type);
} while (CurrentToken() == Token::kCOMMA);
Token::Kind token = CurrentToken();
if ((token == Token::kGT) ||
(token == Token::kSAR) ||
(token == Token::kSHR)) {
ConsumeRightAngleBracket();
} else {
ErrorMsg("right angle bracket expected");
}
return NewTypeArray(types);
}
return TypeArguments::null();
}
// Parse and return an array of interface types.
RawArray* Parser::ParseInterfaceList() {
ASSERT((CurrentToken() == Token::kIMPLEMENTS) ||
(CurrentToken() == Token::kEXTENDS));
GrowableArray<Type*> interfaces;
do {
ConsumeToken();
// TODO(regis): The way we handle unresolved classes is not going to fly.
// We are currently not able to provide a token position for errors occuring
// after parsing, as in the class finalizer. We need to introduce an
// 'unresolved class' class consisting of a string, a token position, and a
// script, maybe a library too.
Type& interface = Type::ZoneHandle(ParseType(kCanResolve));
interfaces.Add(&interface);
} while (CurrentToken() == Token::kCOMMA);
return NewArray<Type>(interfaces);
}
void Parser::AddInterfaces(intptr_t interfaces_pos,
const Class& cls,
const Array& interfaces) {
GrowableArray<Type*> all_interfaces;
// 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++) {
Type& interface = Type::ZoneHandle();
interface ^= cls_interfaces.At(i);
all_interfaces.Add(&interface);
}
// Now add the new interfaces.
Type& conflicting = Type::Handle();
for (intptr_t i = 0; i < interfaces.Length(); i++) {
Type& interface = Type::ZoneHandle();
interface ^= interfaces.At(i);
if (!ClassFinalizer::AddInterfaceIfUnique(&all_interfaces,
&interface,
&conflicting)) {
ASSERT(!conflicting.IsNull());
ErrorMsg(interfaces_pos,
"interface '%s' conflicts with interface '%s'",
String::Handle(interface.Name()).ToCString(),
String::Handle(conflicting.Name()).ToCString());
}
}
cls_interfaces = NewArray<Type>(all_interfaces);
cls.set_interfaces(cls_interfaces);
}
void Parser::ParseTopLevelVariable(TopLevel* top_level) {
const bool is_final = (CurrentToken() == Token::kFINAL);
const bool is_static = true;
const Type& type = Type::ZoneHandle(
ParseFinalVarOrType(kIsMandatory, kCanResolve));
while (true) {
const intptr_t name_pos = token_index_;
String& var_name = *ExpectIdentifier("variable name expected");
if (library_.LookupObject(var_name) != Object::null()) {
ErrorMsg(name_pos, "'%s' is already defined", var_name.ToCString());
}
Field& field = Field::ZoneHandle(
Field::New(var_name, is_static, is_final, name_pos));
field.set_type(type);
field.set_value(Instance::Handle(Instance::null()));
top_level->fields.Add(&field);
library_.AddObject(field, var_name);
if (CurrentToken() == Token::kASSIGN) {
ConsumeToken();
SkipExpr();
field.set_value(Instance::Handle(Object::sentinel()));
// Create a static const getter.
String& getter_name = String::ZoneHandle(Field::GetterName(var_name));
Function& getter = Function::ZoneHandle(
Function::New(getter_name, RawFunction::kConstImplicitGetter,
is_static, is_final, name_pos));
getter.set_result_type(type);
top_level->functions.Add(&getter);
} else if (is_final) {
ErrorMsg(name_pos, "missing initializer for final variable");
}
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
} else if (CurrentToken() == Token::kSEMICOLON) {
ConsumeToken();
break;
} else {
ExpectSemicolon(); // Reports error.
}
}
}
void Parser::ParseTopLevelFunction(TopLevel* top_level) {
Type& result_type = Type::Handle(Type::VarType());
const bool is_static = 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(kCanResolve);
}
}
const intptr_t name_pos = token_index_;
const String& func_name = *ExpectIdentifier("function name expected");
if (library_.LookupObject(func_name) != Object::null()) {
ErrorMsg(name_pos, "'%s' is already defined", func_name.ToCString());
}
if (CurrentToken() != Token::kLPAREN) {
ErrorMsg("'(' expected");
}
const intptr_t function_pos = token_index_;
ParamList params;
const bool allow_explicit_default_values = true;
ParseFormalParameterList(allow_explicit_default_values, &params);
if (CurrentToken() == Token::kLBRACE) {
SkipBlock();
} else if (CurrentToken() == Token::kARROW) {
ConsumeToken();
SkipExpr();
ExpectSemicolon();
} else if (IsLiteral("native")) {
ParseNativeDeclaration();
} else {
ErrorMsg("function block expected");
}
Function& func = Function::ZoneHandle(
Function::New(func_name, RawFunction::kFunction,
is_static, false, function_pos));
func.set_result_type(result_type);
AddFormalParamsToFunction(&params, func);
top_level->functions.Add(&func);
library_.AddObject(func, func_name);
}
void Parser::ParseTopLevelAccessor(TopLevel* top_level) {
const bool is_static = true;
Type& result_type = Type::Handle();
bool is_getter = (CurrentToken() == Token::kGET);
if (CurrentToken() == Token::kGET ||
CurrentToken() == Token::kSET) {
ConsumeToken();
// TODO(regis): Revisit, see issue 4745047.
result_type = Type::VarType();
} else {
if (CurrentToken() == Token::kVOID) {
ConsumeToken();
result_type = Type::VoidType();
} else {
result_type = ParseType(kCanResolve);
}
is_getter = (CurrentToken() == Token::kGET);
if (CurrentToken() == Token::kGET || CurrentToken() == Token::kSET) {
ConsumeToken();
} else {
UnexpectedToken();
}
}
intptr_t name_pos = token_index_;
const String* field_name = ExpectIdentifier("accessor name expected");
if (CurrentToken() != Token::kLPAREN) {
ErrorMsg("'(' expected");
}
intptr_t accessor_pos = token_index_;
ParamList params;
const bool allow_explicit_default_values = true;
ParseFormalParameterList(allow_explicit_default_values, &params);
String& accessor_name = String::ZoneHandle();
int expected_num_parameters = -1;
if (is_getter) {
expected_num_parameters = 0;
accessor_name = Field::GetterName(*field_name);
} else {
expected_num_parameters = 1;
accessor_name = Field::SetterName(*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");
}
if (CurrentToken() == Token::kLBRACE) {
SkipBlock();
} else if (CurrentToken() == Token::kARROW) {
ConsumeToken();
SkipExpr();
ExpectSemicolon();
} else if (IsLiteral("native")) {
ParseNativeDeclaration();
} else {
ErrorMsg("function block expected");
}
Function& func = Function::ZoneHandle(
Function::New(accessor_name,
is_getter? RawFunction::kGetterFunction :
RawFunction::kSetterFunction,
is_static, false, accessor_pos));
func.set_result_type(result_type);
AddFormalParamsToFunction(&params, func);
top_level->functions.Add(&func);
library_.AddObject(func, accessor_name);
}
void Parser::ParseLibraryName() {
if ((script_.kind() == RawScript::kLibrary) &&
(CurrentToken() != Token::kLIBRARY)) {
// Handle error case early to get consistent error message.
ExpectToken(Token::kLIBRARY);
}
if (CurrentToken() == Token::kLIBRARY) {
ConsumeToken();
ExpectToken(Token::kLPAREN);
if (CurrentToken() != Token::kSTRING) {
ErrorMsg("library name expected");
}
const String& name = *CurrentLiteral();
ConsumeToken();
ExpectToken(Token::kRPAREN);
ExpectToken(Token::kSEMICOLON);
library_.SetName(name);
}
}
Dart_Result Parser::CallLibraryTagHandler(Dart_LibraryTag tag,
intptr_t token_pos,
const String& url) {
Dart_LibraryTagHandler handler = Isolate::Current()->library_tag_handler();
if (handler == NULL) {
ErrorMsg(token_pos, "no library handler registered");
}
Dart_Result result = handler(tag,
Api::NewLocalHandle(library_),
Api::NewLocalHandle(url));
if (!Dart_IsValidResult(result)) {
ErrorMsg(token_pos, "library handler failed: %s",
Dart_GetErrorCString(result));
}
return result;
}
void Parser::ParseLibraryImport() {
while (CurrentToken() == Token::kIMPORT) {
intptr_t import_pos = token_index_;
ConsumeToken();
ExpectToken(Token::kLPAREN);
if (CurrentToken() != Token::kSTRING) {
ErrorMsg("library url expected");
}
const String& url = *CurrentLiteral();
ConsumeToken();
String& prefix = String::Handle();
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
const String& kPrefix = String::Handle(String::NewSymbol("prefix"));
if ((CurrentToken() != Token::kIDENT) ||
!kPrefix.Equals(*CurrentLiteral())) {
ErrorMsg("prefix: expected");
}
ConsumeToken();
ExpectToken(Token::kCOLON);
if (CurrentToken() != Token::kSTRING) {
ErrorMsg("prefix expected");
}
prefix = CurrentLiteral()->raw();
ConsumeToken();
}
ExpectToken(Token::kRPAREN);
ExpectToken(Token::kSEMICOLON);
Dart_Result result = CallLibraryTagHandler(kCanonicalizeUrl,
import_pos,
url);
Dart_Handle handle = Dart_GetResult(result);
const String& canon_url = String::CheckedHandle(Api::UnwrapHandle(handle));
// Lookup the library URL.
Library& library = Library::Handle(Library::LookupLibrary(canon_url));
if (library.IsNull()) {
// Create a new library object and call the library tag handler.
library = Library::New(canon_url);
library.Register();
// The tag handler expects the importing library as a parameter.
CallLibraryTagHandler(kImportTag, import_pos, canon_url);
}
// Add the import to the library.
if (prefix.IsNull() || (prefix.Length() == 0)) {
library_.AddImport(library);
} else {
if (library_.LookupLocalObject(prefix) != Object::null()) {
ErrorMsg(token_index_, "'%s' is already defined", prefix.ToCString());
}
prefix = String::NewSymbol(prefix);
const LibraryPrefix& library_prefix =
LibraryPrefix::Handle(LibraryPrefix::New(prefix, library));
library_.AddObject(library_prefix, prefix);
}
}
}
void Parser::ParseLibraryInclude() {
while (CurrentToken() == Token::kSOURCE) {
intptr_t source_pos = token_index_;
ConsumeToken();
ExpectToken(Token::kLPAREN);
if (CurrentToken() != Token::kSTRING) {
ErrorMsg("source url expected");
}
const String& url = *CurrentLiteral();
ConsumeToken();
ExpectToken(Token::kRPAREN);
ExpectToken(Token::kSEMICOLON);
Dart_Result result = CallLibraryTagHandler(kCanonicalizeUrl,
source_pos,
url);
Dart_Handle handle = Dart_GetResult(result);
const String& canon_url = String::CheckedHandle(Api::UnwrapHandle(handle));
CallLibraryTagHandler(kSourceTag, source_pos, canon_url);
}
}
void Parser::ParseLibraryDefinition() {
// Handle the script tag.
if (CurrentToken() == Token::kSCRIPTTAG) {
// Nothing to do for script tags except to skip them.
ConsumeToken();
}
ParseLibraryName();
ParseLibraryImport();
ParseLibraryInclude();
}
void 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.
GrowableArray<const Class*> classes;
SetPosition(0);
is_top_level_ = true;
TopLevel top_level;
Class& toplevel_class = Class::ZoneHandle(
Class::New(String::ZoneHandle(String::NewSymbol("")), script_));
toplevel_class.set_library(library_);
if (is_library_source()) {
ParseLibraryDefinition();
}
while (true) {
set_current_class(Class::Handle()); // No current class.
if (CurrentToken() == Token::kCLASS) {
ParseClassDefinition(&classes);
} else if (CurrentToken() == Token::kTYPEDEF) {
ParseFunctionTypeAlias(&classes);
} else if (CurrentToken() == Token::kINTERFACE) {
ParseInterfaceDefinition(&classes);
} else if (IsVariableDeclaration()) {
set_current_class(toplevel_class);
ParseTopLevelVariable(&top_level);
} else if (IsTopLevelFunction()) {
set_current_class(toplevel_class);
ParseTopLevelFunction(&top_level);
} else if (IsTopLevelAccessor()) {
set_current_class(toplevel_class);
ParseTopLevelAccessor(&top_level);
} else if (CurrentToken() == Token::kEOS) {
break;
} else {
UnexpectedToken();
}
}
if ((top_level.fields.length() > 0) || (top_level.functions.length() > 0)) {
toplevel_class.SetFields(
Array::Handle(NewArray<Field>(top_level.fields)));
toplevel_class.SetFunctions(
Array::Handle(NewArray<Function>(top_level.functions)));
library_.AddAnonymousClass(toplevel_class);
classes.Add(&toplevel_class);
}
ClassFinalizer::AddPendingClasses(classes);
}
void Parser::ChainNewBlock(LocalScope* outer_scope) {
Block* block = new Block(current_block_,
outer_scope,
new SequenceNode(token_index_, 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;
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));
func.set_num_fixed_parameters(params->num_fixed_parameters);
func.set_num_optional_parameters(params->num_optional_parameters);
const int num_parameters = params->parameters->length();
ASSERT(num_parameters == func.NumberOfParameters());
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];
ASSERT(is_top_level_ || param_desc.type->IsResolved());
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;
if (param_desc.is_field_initializer) {
name = &String::ZoneHandle(MangledInitParamName(*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());
}
if (param_desc.is_final) {
parameter->set_is_final();
}
}
}
// Builds ReturnNode/NativeBodyNode for a native function.
void Parser::ParseNativeFunctionBlock(const ParamList* params,
const Function& func) {
const Class& cls = Class::Handle(func.owner());
const int num_parameters = params->parameters->length();
// Parse the function name out.
intptr_t native_pos = token_index_;
const String& native_name = ParseNativeDeclaration();
// Now resolve the native function to the corresponding native entrypoint.
NativeFunction native_function = NativeEntry::ResolveNative(cls,
native_name,
num_parameters);
if (native_function == NULL) {
ErrorMsg(native_pos, "native function '%s' cannot be found",
native_name.ToCString());
}
const bool has_opt_params = (params->num_optional_parameters > 0);
// Now add the NativeBodyNode and return statement.
current_block_->statements->Add(
new ReturnNode(token_index_, new NativeBodyNode(token_index_,
native_name,
native_function,
num_parameters,
has_opt_params)));
}
LocalVariable* Parser::LookupReceiver(LocalScope* from_scope,
bool test_only) {
const String& this_name = String::Handle(String::NewSymbol(kThisName));
return from_scope->LookupVariable(this_name, test_only);
}
void Parser::CaptureReceiver() {
ASSERT(current_block_->scope->function_level() > 0);
const bool kTestOnly = false;
// Side effect of lookup captures the receiver variable.
LocalVariable* receiver = LookupReceiver(current_block_->scope, kTestOnly);
ASSERT(receiver != NULL);
}
AstNode* Parser::LoadReceiver() {
// A nested function may access 'this', referring to the receiver of the
// outermost enclosing function.
// We should not be loading the receiver from a static scope.
ASSERT(!current_function().IsInStaticScope() ||
current_function().IsInFactoryScope());
const bool kTestOnly = false;
LocalVariable* receiver = LookupReceiver(current_block_->scope, kTestOnly);
if (receiver == NULL) {
ErrorMsg("illegal access to 'this'");
}
return new LoadLocalNode(token_index_, *receiver);
}
AstNode* Parser::CallGetter(intptr_t token_index,
AstNode* object,
const String& name) {
return new InstanceGetterNode(token_index_, object, name);
}
// Returns ast nodes of the variable initialization.
AstNode* Parser::ParseVariableDeclaration(const Type& type, bool is_final) {
TRACE_PARSER("ParseVariableDeclaration");
ASSERT(CurrentToken() == Token::kIDENT);
const intptr_t ident_pos = token_index_;
LocalVariable* variable =
new LocalVariable(ident_pos, *CurrentLiteral(), type);
ASSERT(current_block_ != NULL);
ASSERT(current_block_->scope != NULL);
ConsumeToken(); // Variable identifier.
AstNode* initialization = NULL;
if (CurrentToken() == Token::kASSIGN) {
// Variable initialization.
const intptr_t assign_pos = token_index_;
ConsumeToken();
AstNode* expr = ParseExpr(kAllowConst);
initialization = new StoreLocalNode(assign_pos, *variable, expr);
} else if (is_final) {
ErrorMsg(ident_pos, "missing initialization of 'final' variable");
} else {
// Initialize variable with null.
AstNode* null_expr = new LiteralNode(ident_pos, Instance::ZoneHandle());
initialization = new StoreLocalNode(ident_pos, *variable, null_expr);
}
// Add variable to cope after parsing the initalizer expression.
// The expression must not be able to refer to the variable.
if (!current_block_->scope->AddVariable(variable)) {
ErrorMsg(ident_pos, "identifier '%s' already defined",
variable->name().ToCString());
}
if (is_final) {
variable->set_is_final();
}
return initialization;
}
// Parses ('var' | 'final' [type] | type).
// The presence of 'final' must be detected and remembered before the call.
// If type_specification is kIsOptional, and no type can be parsed, then return
// the VarType.
// If a type is parsed, it is resolved (or not) according to type_resolution.
RawType* Parser::ParseFinalVarOrType(TypeSpecification type_specification,
TypeResolution type_resolution) {
if (CurrentToken() == Token::kVAR) {
ConsumeToken();
return Type::VarType();
}
if (CurrentToken() == Token::kFINAL) {
ConsumeToken();
type_specification = kIsOptional;
}
if (CurrentToken() != Token::kIDENT) {
if (type_specification == kIsOptional) {
return Type::VarType();
} else {
ErrorMsg("identifier expected");
}
}
if (type_specification == kIsOptional) {
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.
(follower != Token::kIDENT) && // Variable name following a type.
(follower != Token::kTHIS)) { // Field parameter following a type.
return Type::VarType();
}
}
return ParseType(type_resolution);
}
// Returns ast nodes of the variable initialization, or NULL if variables
// are not initialized. If several variables are declared and initialized,
// the individual initializers are collected in a sequence node.
AstNode* Parser::ParseVariableDeclarationList() {
TRACE_PARSER("ParseVariableDeclarationList");
bool is_final = (CurrentToken() == Token::kFINAL);
const Type& type = Type::ZoneHandle(
ParseFinalVarOrType(kIsMandatory, kMustResolve));
if (CurrentToken() != Token::kIDENT) {
ErrorMsg("identifier expected");
}
AstNode* initializers = ParseVariableDeclaration(type, is_final);
while (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
if (CurrentToken() != Token::kIDENT) {
ErrorMsg("identifier expected after comma");
}
AstNode* right = ParseVariableDeclaration(type, is_final);
if (right != NULL) {
if (initializers == NULL) {
initializers = right;
} else {
// We have a second initializer. Allocate a sequence node now.
SequenceNode* sequence = NodeAsSequenceNode(initializers->token_index(),
initializers,
current_block_->scope);
sequence->Add(right);
initializers = sequence;
}
}
}
return initializers;
}
AstNode* Parser::ParseFunctionStatement(bool is_literal) {
TRACE_PARSER("ParseFunctionStatement");
Type& result_type = Type::Handle();
const String* variable_name = NULL;
const String* function_name = NULL;
result_type = Type::VarType();
if (CurrentToken() == Token::kVOID) {
ConsumeToken();
result_type = Type::VoidType();
} else if ((CurrentToken() == Token::kIDENT) &&
(LookaheadToken(1) != Token::kLPAREN)) {
result_type = ParseType(kMustResolve);
}
intptr_t ident_pos = token_index_;
if (CurrentToken() == Token::kIDENT) {
variable_name = CurrentLiteral();
function_name = variable_name;
ConsumeToken();
} else {
if (!is_literal) {
ErrorMsg("function name expected");
}
const String& anonymous_function_name =
String::ZoneHandle(String::NewSymbol("function"));
function_name = &anonymous_function_name;
}
ASSERT(ident_pos >= 0);
if (CurrentToken() != Token::kLPAREN) {
ErrorMsg("'(' expected");
}
Function& function = Function::ZoneHandle(
Function::NewClosureFunction(*function_name,
current_function(),
token_index_));
function.set_result_type(result_type);
LocalVariable* function_variable = NULL;
ParameterizedType& function_type = ParameterizedType::ZoneHandle();
if (variable_name != NULL) {
// Add the function variable to the scope before parsing the function in
// order to allow self reference from inside the function.
// The type of the implicitly declared const variable is defined by the
// local function signature class and the type arguments of the current
// receiver (if in a non-static scope). However, the signature is not yet
// known, since the formal parameter list is not parsed yet. Therefore, we
// set the 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::FunctionInterface()).type_class());
function_type = ParameterizedType::New(unknown_signature_class,
TypeArguments::Handle());
function_type.set_is_finalized(); // No real finalization needed.
function_variable = new LocalVariable(ident_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)) {
ErrorMsg(ident_pos, "identifier '%s' already defined",
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 or create a new
// signature class for it.
const String& signature = String::Handle(function.Signature());
Class& signature_class = Class::Handle(LookupClass(signature));
if (signature_class.IsNull()) {
signature_class = Class::NewSignatureClass(signature,
function,
script_,
ident_pos);
// Record the function signature class in the library.
library_.AddClass(signature_class);
}
ASSERT(!Function::Handle(signature_class.signature_function()).IsNull());
ASSERT(Class::Handle(function.signature_class()).IsNull());
function.set_signature_class(signature_class);
// Local functions are not registered in the enclosing class, which is already
// finalized.
ASSERT(current_class().is_finalized());
if (function_variable != NULL) {
ASSERT(function_variable->type().raw() == function_type.raw());
// Patch the function variable type now that the signature is known.
// We need to create a new type for proper finalization, since the existing
// type is already marked as finalized.
// TODO(regis): Set proper signature_type_arguments if in non-static scope
// and if the signature involves generic types.
const TypeArguments& signature_type_arguments = TypeArguments::Handle();
const String& errmsg = String::Handle(
ClassFinalizer::FinalizeTypeWhileParsing(ParameterizedType::Handle(
ParameterizedType::New(signature_class,
signature_type_arguments))));
if (!errmsg.IsNull()) {
ErrorMsg(errmsg.ToCString());
}
function_type.set_type_class(signature_class);
function_type.set_arguments(signature_type_arguments);
}
// 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(ident_pos, function, statements->scope());
if (function_variable == NULL) {
ASSERT(is_literal);
return closure;
} else {
AstNode* initialization =
new StoreLocalNode(ident_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::IsTypeParameter() {
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::kSAR) {
nesting_level -= 2;
} else if (CurrentToken() == Token::kSHR) {
nesting_level -= 3;
} else if (CurrentToken() != Token::kCOMMA &&
CurrentToken() != Token::kIDENT &&
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;
}
// Returns true if the next tokens can be parsed as a type with optional
// type parameters. Current token position is not restored.
bool Parser::IsOptionalType() {
if (CurrentToken() == Token::kIDENT) {
QualIdent type_name;
ParseQualIdent(&type_name);
// Check if the type_name has been defined as a variable in a local scope,
// hiding the type.
if (type_name.local_scope_ident) {
return false;
}
if (CurrentToken() == Token::kLT && !IsTypeParameter()) {
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::IsReturnType() {
if (CurrentToken() == Token::kVOID) {
ConsumeToken();
return true;
} else if (CurrentToken() == Token::kIDENT) {
return IsOptionalType();
}
return false;
}
// Look ahead to detect whether the next tokens should be parsed as
// a variable declaration. Returns true if we detect the token pattern:
// ('var' | 'final' | type ident (';' | '=' | ','))
// Token position remains unchanged.
bool Parser::IsVariableDeclaration() {
if ((CurrentToken() == Token::kVAR) ||
(CurrentToken() == Token::kFINAL)) {
return true;
}
if (CurrentToken() != Token::kIDENT) {
return false;
}
intptr_t saved_pos = token_index_;
bool is_var_decl = false;
if (IsOptionalType()) {
if (CurrentToken() == Token::kIDENT) {
ConsumeToken();
if ((CurrentToken() == Token::kSEMICOLON) ||
(CurrentToken() == Token::kCOMMA) ||
(CurrentToken() == Token::kASSIGN)) {
is_var_decl = true;
}
}
}
SetPosition(saved_pos);
return is_var_decl;
}
bool Parser::IsFunctionDeclaration() {
// A function declaration is like a function literal but it must have
// a name.
return (CurrentToken() != Token::kLPAREN) && IsFunctionLiteral();
}
bool Parser::IsTopLevelFunction() {
// Top-level function declarations can omit the return type. Check for
// that case separately.
return ((CurrentToken() == Token::kIDENT) &&
(LookaheadToken(1) == Token::kLPAREN)) ||
IsFunctionDeclaration();
}
bool Parser::IsTopLevelAccessor() {
if ((CurrentToken() == Token::kGET) || (CurrentToken() == Token::kSET)) {
return true;
}
intptr_t saved_pos = token_index_;
if (IsReturnType()) {
if ((CurrentToken() == Token::kGET) || (CurrentToken() == Token::kSET)) {
if (LookaheadToken(1) == Token::kIDENT) { // Accessor name.
SetPosition(saved_pos);
return true;
}
}
}
SetPosition(saved_pos);
return false;
}
bool Parser::IsFunctionLiteral() {
if (!allow_function_literals_) {
return false;
}
intptr_t saved_pos = token_index_;
bool is_function_literal = false;
if ((CurrentToken() == Token::kIDENT) &&
(LookaheadToken(1) == Token::kLPAREN)) {
ConsumeToken(); // Consume function identifier.
} else if (IsReturnType()) {
if (CurrentToken() != Token::kIDENT) {
SetPosition(saved_pos);
return false;
}
ConsumeToken(); // Comsume function identifier.
}
if (CurrentToken() == Token::kLPAREN) {
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() {
intptr_t saved_pos = token_index_;
bool result = false;
if (CurrentToken() == Token::kVAR || CurrentToken() == Token::kFINAL) {
ConsumeToken();
}
if (CurrentToken() == Token::kIDENT) {
if (LookaheadToken(1) == Token::kIN) {
result = true;
} else if (IsOptionalType()) {
if (CurrentToken() == Token::kIDENT) {
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) {
intptr_t statement_pos = token_index_;
AstNode* statement = ParseStatement();
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);
intptr_t if_pos = token_index_;
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);
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;
}
CaseNode* Parser::ParseCaseClause(LocalVariable* switch_expr_value,
SourceLabel* case_label) {
TRACE_PARSER("ParseCaseStatement");
bool default_seen = false;
intptr_t case_pos = token_index_;
intptr_t expr_pos = 0;
SequenceNode* case_expressions =
new SequenceNode(case_pos, current_block_->scope);
while (CurrentToken() == Token::kCASE || CurrentToken() == Token::kDEFAULT) {
if (CurrentToken() == Token::kCASE) {
if (default_seen) {
ErrorMsg("default clause must be last case");
}
ConsumeToken(); // Keyword case.
expr_pos = token_index_;
AstNode* expr = ParseExpr(kAllowConst);
AstNode* switch_expr_load = new LoadLocalNode(case_pos,
*switch_expr_value);
AstNode* case_comparison = new ComparisonNode(case_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.
expr_pos = token_index_;
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 (CurrentToken() == Token::kIDENT &&
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(token_index_);
arguments->Add(new LiteralNode(
token_index_, Integer::ZoneHandle(Integer::New(token_index_))));
current_block_->statements->Add(
MakeStaticCall(kFallThroughErrorName, kThrowNewName, 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);
intptr_t switch_pos = token_index_;
SourceLabel* label =
SourceLabel::New(switch_pos, label_name, SourceLabel::kSwitch);
ConsumeToken();
const bool parens_are_mandatory = false;
bool paren_found = false;
if (CurrentToken() == Token::kLPAREN) {
paren_found = true;
ConsumeToken();
} else if (parens_are_mandatory) {
ErrorMsg("'(' expected");
}
intptr_t expr_pos = token_index_;
AstNode* switch_expr = ParseExpr(kAllowConst);
if (paren_found) {
ExpectToken(Token::kRPAREN);
}
ExpectToken(Token::kLBRACE);
OpenBlock();
current_block_->scope->AddLabel(label);
// Store switch expression in temporary local variable.
LocalVariable* temp_variable =
new LocalVariable(expr_pos,
String::ZoneHandle(String::NewSymbol(":switch_expr")),
Type::ZoneHandle(Type::VarType()));
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;
while (true) {
// Check for statement label
SourceLabel* case_label = NULL;
if (CurrentToken() == Token::kIDENT &&
LookaheadToken(1) == Token::kCOLON) {
// Case statements start with a label.
String* label_name = CurrentLiteral();
intptr_t label_pos = token_index_;
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, "name '%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_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 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");
intptr_t while_pos = token_index_;
SourceLabel* label =
SourceLabel::New(while_pos, label_name, SourceLabel::kWhile);
ConsumeToken();
ExpectToken(Token::kLPAREN);
AstNode* cond_expr = ParseExpr(kAllowConst);
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");
intptr_t do_pos = token_index_;
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);
ExpectToken(Token::kRPAREN);
ExpectSemicolon();
return new DoWhileNode(do_pos, label, cond_expr, dowhile_body);
}
AstNode* Parser::ParseForInStatement(intptr_t forin_pos,
SourceLabel* label) {
bool is_final = (CurrentToken() == Token::kFINAL);
const String* loop_var_name = NULL;
LocalVariable* loop_var = NULL;
intptr_t loop_var_pos = 0;
if (LookaheadToken(1) == Token::kIN) {
loop_var_pos = token_index_;
loop_var_name = ExpectIdentifier("variable name expected");
} else {
// The case without a type is handled above, so require a type here.
const Type& type = Type::ZoneHandle(
ParseFinalVarOrType(kIsMandatory, kMustResolve));
loop_var_pos = token_index_;
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);
intptr_t collection_pos = token_index_;
AstNode* collection_expr = ParseExpr(kAllowConst);
ExpectToken(Token::kRPAREN);
OpenBlock(); // Implicit block around while loop.
// Generate implicit iterator variable and add to scope.
const String& iterator_name =
String::ZoneHandle(String::NewSymbol(":for-in-iter"));
// 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 Type& iterator_type = Type::ZoneHandle(Type::VarType());
LocalVariable* iterator_var =
new LocalVariable(collection_pos, iterator_name, iterator_type);
current_block_->scope->AddVariable(iterator_var);
// Generate initialization of iterator variable.
const String& iterator_method_name =
String::ZoneHandle(String::NewSymbol(kGetIteratorName));
ArgumentListNode* no_args = new ArgumentListNode(collection_pos);
AstNode* get_iterator = new InstanceCallNode(
collection_pos, collection_expr, iterator_method_name, no_args);
AstNode* iterator_init =
new StoreLocalNode(collection_pos, *iterator_var, get_iterator);
current_block_->statements->Add(iterator_init);
// Generate while loop condition.
AstNode* iterator_has_next = new InstanceCallNode(
collection_pos,
new LoadLocalNode(collection_pos, *iterator_var),
String::ZoneHandle(String::NewSymbol("hasNext")),
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_next = new InstanceCallNode(
collection_pos,
new LoadLocalNode(collection_pos, *iterator_var),
String::ZoneHandle(String::NewSymbol("next")),
no_args);
// 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_next);
} else {
AstNode* loop_var_primary = ResolveVarOrField(loop_var_pos, *loop_var_name);
ASSERT(!loop_var_primary->IsPrimaryNode());
loop_var_assignment =
loop_var_primary->MakeAssignmentNode(iterator_next);
if (loop_var_assignment == NULL) {
ErrorMsg(loop_var_pos, "variable or field '%s' is not assignable",
loop_var_name->ToCString());
}
}
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_has_next, 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");
intptr_t for_pos = token_index_;
ConsumeToken();
ExpectToken(Token::kLPAREN);
SourceLabel* label = SourceLabel::New(for_pos, label_name, SourceLabel::kFor);
if (IsForInStatement()) {
return ParseForInStatement(for_pos, label);
}
OpenBlock();
// The label is added to the implicit scope that also contains
// the loop variable declarations.
current_block_->scope->AddLabel(label);
AstNode* initializer = NULL;
intptr_t init_pos = token_index_;
LocalScope* init_scope = current_block_->scope;
if (CurrentToken() != Token::kSEMICOLON) {
if (IsVariableDeclaration()) {
initializer = ParseVariableDeclarationList();
} else {
initializer = ParseExpr(kAllowConst);
}
}
ExpectSemicolon();
AstNode* condition = NULL;
if (CurrentToken() != Token::kSEMICOLON) {
condition = ParseExpr(kAllowConst);
}
ExpectSemicolon();
AstNode* increment = NULL;
intptr_t incr_pos = token_index_;
LocalScope* incr_scope = current_block_->scope;
if (CurrentToken() != Token::kRPAREN) {
increment = ParseExprList();
}
ExpectToken(Token::kRPAREN);
const bool parsing_loop_body = true;
SequenceNode* body = ParseNestedStatement(parsing_loop_body, NULL);
CloseBlock();
return new ForNode(for_pos,
label,
NodeAsSequenceNode(init_pos, initializer, init_scope),
condition,
NodeAsSequenceNode(incr_pos, increment, incr_scope),
body);
}
// Lookup class in the corelib implementation which contains various VM
// helper methods and classes.
static RawClass* LookupImplClass(const String& class_name) {
return Library::Handle(Library::CoreImplLibrary()).LookupClass(class_name);
}
RawClass* Parser::LookupClass(const String& class_name) {
return library_.LookupClass(class_name);
}
// Calling VM-internal helpers, uses implementation core library.
AstNode* Parser::MakeStaticCall(const char* class_name,
const char* function_name,
ArgumentListNode* arguments) {
const String& cls_name =
String::Handle(String::NewSymbol(class_name));
const Class& cls = Class::Handle(LookupImplClass(cls_name));
ASSERT(!cls.IsNull());
const String& func_name =
String::ZoneHandle(String::NewSymbol(function_name));
const Function& func = Function::ZoneHandle(
Resolver::ResolveStatic(cls,
func_name,
arguments->length(),
arguments->names(),
Resolver::kIsQualified));
ASSERT(!func.IsNull());
CheckFunctionIsCallable(arguments->token_index(), func);
return new StaticCallNode(arguments->token_index(), 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(kAssertErrorName, kThrowNewName, arguments);
}
AstNode* Parser::ParseAssertStatement() {
ConsumeToken(); // Consume assert keyword.
ExpectToken(Token::kLPAREN);
const intptr_t condition_pos = token_index_;
if (!FLAG_enable_asserts) { // always enable assert for now...
SkipExpr();
ExpectToken(Token::kRPAREN);
return NULL;
}
AstNode* condition = ParseExpr(kAllowConst);
const intptr_t condition_end = token_index_;
ExpectToken(Token::kRPAREN);
if (condition->IsClosureNode()) {
// Function literal in assert implies a call.
condition =
new ClosureCallNode(condition_pos,
condition,
new ArgumentListNode(condition_pos));
}
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,
current_block_->scope),
NULL);
}
struct CatchParamDesc {
CatchParamDesc()
: token_index(0), type(NULL), var(NULL), is_final(false) { }
intptr_t token_index;
const Type* type;
const String* var;
bool is_final;
};
// Parse the parameter specified in the catch clause.
void Parser::ParseCatchParameter(CatchParamDesc* catch_param) {
TRACE_PARSER("ParseCatchParameter");
ASSERT(catch_param != NULL);
catch_param->is_final = (CurrentToken() == Token::kFINAL);
catch_param->type = &Type::ZoneHandle(
ParseFinalVarOrType(kIsMandatory, kMustResolve));
if (CurrentToken() != Token::kIDENT) {
ErrorMsg("identifier expected");
}
catch_param->token_index = token_index_;
catch_param->var = CurrentLiteral();
ConsumeToken();
}
// Populate local scope of the catch block with the catch parameters.
void Parser::AddCatchParamsToScope(const CatchParamDesc& exception_param,
const CatchParamDesc& stack_trace_param,
LocalScope* scope) {
ASSERT(exception_param.var != NULL);
LocalVariable* var = new LocalVariable(exception_param.token_index,
*(exception_param.var),
*(exception_param.type));
if (exception_param.is_final) {
var->set_is_final();
}
bool added_to_scope = scope->AddVariable(var);
ASSERT(added_to_scope);
if (stack_trace_param.var != NULL) {
var = new LocalVariable(token_index_,
*(stack_trace_param.var),
*(stack_trace_param.type));
if (stack_trace_param.is_final) {
var->set_is_final();
}
added_to_scope = scope->AddVariable(var);
if (!added_to_scope) {
ErrorMsg(stack_trace_param.token_index,
"name '%s' already exists in scope",
stack_trace_param.var->ToCString());
}
}
}
SequenceNode* Parser::ParseFinallyBlock() {
OpenBlock();
ExpectToken(Token::kLBRACE);
ParseStatementSequence();
ExpectToken(Token::kRBRACE);
SequenceNode* finally_block = CloseBlock();
return finally_block;
}
void Parser::PushTryBlock(Block* try_block) {
TryBlocks* block = new TryBlocks(try_block, try_blocks_list_);
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) {
if (node->IsReturnNode()) {
node->AsReturnNode()->AddInlinedFinallyNode(finally_node);
} else {
ASSERT(node->IsJumpNode());
node->AsJumpNode()->AddInlinedFinallyNode(finally_node);
}
}
AstNode* Parser::ParseTryStatement(String* label_name) {
TRACE_PARSER("ParseTryStatement");
// We create three stack slots for exceptions here:
// ':saved_context_var' - Used to save the context before start of the try
// block. The context register is restored from this
// slot before processing the catch block handler.
// ':exception_var' - Used to save the current exception object that was
// thrown.
// ':stacktrace_var' - Used to save the current stack trace object into which
// the stack trace was copied into when an exception was
// thrown.
// :exception_var and :stacktrace_var get set with the exception object
// and the stacktrace object when an exception is thrown.
// These three implicit variables can never be captured variables.
const String& context_var_name =
String::ZoneHandle(String::NewSymbol(":saved_context_var"));
LocalVariable* context_var =
current_block_->scope->LocalLookupVariable(context_var_name);
if (context_var == NULL) {
context_var = new LocalVariable(token_index_,
context_var_name,
Type::ZoneHandle(Type::VarType()));
current_block_->scope->AddVariable(context_var);
}
const String& catch_excp_var_name =
String::ZoneHandle(String::NewSymbol(":exception_var"));
LocalVariable* catch_excp_var =
current_block_->scope->LocalLookupVariable(catch_excp_var_name);
if (catch_excp_var == NULL) {
catch_excp_var = new LocalVariable(token_index_,
catch_excp_var_name,
Type::ZoneHandle(Type::VarType()));
current_block_->scope->AddVariable(catch_excp_var);
}
const String& catch_trace_var_name =
String::ZoneHandle(String::NewSymbol(":stacktrace_var"));
LocalVariable* catch_trace_var =
current_block_->scope->LocalLookupVariable(catch_trace_var_name);
if (catch_trace_var == NULL) {
catch_trace_var = new LocalVariable(token_index_,
catch_trace_var_name,
Type::ZoneHandle(Type::VarType()));
current_block_->scope->AddVariable(catch_trace_var);
}
intptr_t try_pos = token_index_;
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();
Block* current_try_block = current_block_;
PushTryBlock(current_try_block);
ExpectToken(Token::kLBRACE);
ParseStatementSequence();
ExpectToken(Token::kRBRACE);
SequenceNode* try_block = CloseBlock();
// Now create a label for the end of catch block processing so that we can
// jump over the catch block code after executing the try block.
SourceLabel* end_catch_label =
SourceLabel::New(token_index_, NULL, SourceLabel::kCatch);
// Now parse the 'catch' blocks if any and merge all of them into
// an if-then sequence of the different types specified using the 'is'
// operator.
bool catch_seen = false;
bool generic_catch_seen = false;
SequenceNode* catch_handler_list = NULL;
intptr_t handler_pos = token_index_;
OpenBlock(); // Start the catch block sequence.
current_block_->scope->AddLabel(end_catch_label);
while (CurrentToken() == Token::kCATCH) {
catch_seen = true;
intptr_t catch_pos = token_index_;
ConsumeToken(); // Consume the 'catch'.
ExpectToken(Token::kLPAREN);
CatchParamDesc exception_param;
CatchParamDesc stack_trace_param;
ParseCatchParameter(&exception_param);
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
ParseCatchParameter(&stack_trace_param);
}
ExpectToken(Token::kRPAREN);
// If a generic "catch all" statement has already been seen then all
// subsequent catch statements are dead. We issue an error for now,
// it might make sense to turn this into a warning.
if (generic_catch_seen) {
ErrorMsg("A generic 'catch all' statement already exists for this "
"try block. All subsequent catch statements are dead code");
}
OpenBlock();
AddCatchParamsToScope(exception_param,
stack_trace_param,
current_block_->scope);
SequenceNode* catch_clause;
// Parse the individual catch handler code and add an unconditional
// JUMP to the end of the try block.
ExpectToken(Token::kLBRACE);
OpenBlock();
// Generate code to load the exception object (:exception_var) into
// the exception variable specified in this block.
ASSERT(exception_param.var != NULL);
LocalVariable* var = LookupLocalScope(*exception_param.var);
ASSERT(var != NULL);
ASSERT(catch_excp_var != NULL);
current_block_->statements->Add(
new StoreLocalNode(catch_pos,
*var,
new LoadLocalNode(catch_pos, *catch_excp_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 (:stacktrace_var) into the stack trace
// variable specified in this block.
LocalVariable* trace = LookupLocalScope(*stack_trace_param.var);
ASSERT(catch_trace_var != NULL);
current_block_->statements->Add(
new StoreLocalNode(catch_pos,
*trace,
new LoadLocalNode(catch_pos, *catch_trace_var)));
}
ParseStatementSequence(); // Parse the catch handler code.
current_block_->statements->Add(
new JumpNode(catch_pos, Token::kCONTINUE, end_catch_label));
SequenceNode* catch_handler = CloseBlock();
ExpectToken(Token::kRBRACE);
if (!exception_param.type->IsVarType()) { // Has a type specification.
// Now form an 'if type check' as an exception type exists in
// the catch specifier.
if (!exception_param.type->IsInstantiated() &&
(current_block_->scope->function_level() > 0)) {
// Make sure that the instantiator is captured.
CaptureReceiver();
}
AstNode* exception_type = new TypeNode(catch_pos, *exception_param.type);
AstNode* exception_var = new LoadLocalNode(catch_pos, *catch_excp_var);
AstNode* cond_expr = new ComparisonNode(
catch_pos, Token::kIS, exception_var, exception_type);
current_block_->statements->Add(
new IfNode(catch_pos, cond_expr, catch_handler, NULL));
} else {
// No exception type exists in the catch specifier so execute the
// catch handler code unconditionally.
current_block_->statements->Add(catch_handler);
generic_catch_seen = true;
}
catch_clause = CloseBlock();
// Add this individual catch handler to the catch handlers list.
current_block_->statements->Add(catch_clause);
}
catch_handler_list = CloseBlock();
TryBlocks* inner_try_block = PopTryBlock();
// Finally parse the 'finally' block.
SequenceNode* finally_block = NULL;
if (CurrentToken() == Token::kFINALLY) {
current_function_.set_is_optimizable(false);
ConsumeToken(); // Consume the 'finally'.
intptr_t finally_pos = token_index_;
// 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);
AddFinallyBlockToNode(node_to_inline, node);
node_index += 1;
node_to_inline = inner_try_block->GetNodeToInlineFinally(node_index);
token_index_ = finally_pos;
}
if (!generic_catch_seen) {
// No generic catch handler exists so execute this finally block
// before rethrowing the excetion.
finally_block = ParseFinallyBlock();
catch_handler_list->Add(finally_block);
token_index_ = finally_pos;
}
finally_block = ParseFinallyBlock();
} else {
if (!catch_seen) {
ErrorMsg("'catch' or 'finally' expected");
}
}
if (!generic_catch_seen) {
// No generic catch handler exists so rethrow the exception so that
// the next catch handler can deal with it.
catch_handler_list->Add(
new ThrowNode(handler_pos,
new LoadLocalNode(handler_pos, *catch_excp_var),
new LoadLocalNode(handler_pos, *catch_trace_var)));
}
CatchClauseNode* catch_block = new CatchClauseNode(handler_pos,
catch_handler_list,
*context_var,
*catch_excp_var,
*catch_trace_var);
// 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, end_catch_label,
*context_var, catch_block, finally_block);
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) {
ASSERT(CurrentToken() == Token::kBREAK || CurrentToken() == Token::kCONTINUE);
Token::Kind jump_kind = CurrentToken();
intptr_t jump_pos = token_index_;
SourceLabel* target = NULL;
ConsumeToken();
if (CurrentToken() == Token::kIDENT) {
// 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(
token_index_, 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();
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 (CurrentToken() == Token::kIDENT) {
if (LookaheadToken(1) == Token::kCOLON) {
// Statement starts with a label.
label_name = CurrentLiteral();
label_pos = token_index_;
ASSERT(label_pos > 0);
ConsumeToken(); // Consume identifier.
ConsumeToken(); // Consume colon.
}
}
const intptr_t statement_pos = token_index_;
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) {
ConsumeToken();
if (CurrentToken() != Token::kSEMICOLON) {
AstNode* expr = ParseExpr(kAllowConst);
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::kTHROW) {
ConsumeToken();
AstNode* expr = NULL;
if (CurrentToken() != Token::kSEMICOLON) {
expr = ParseExpr(kAllowConst);
ExpectSemicolon();
statement = new ThrowNode(statement_pos, expr, NULL);
} else { // No exception object seen so must be a rethrow.
// Check if it is ok to do a rethrow.
SourceLabel* label = current_block_->scope->LookupInnermostCatchLabel();
if (label == NULL ||
label->FunctionLevel() != current_block_->scope->function_level()) {
ErrorMsg("rethrow of an exception is not valid here");
}
ASSERT(label->owner() != NULL);
LocalScope* scope = label->owner()->parent();
ASSERT(scope != NULL);
LocalVariable* excp_var = scope->LocalLookupVariable(
String::ZoneHandle(String::NewSymbol(":exception_var")));
ASSERT(excp_var != NULL);
LocalVariable* trace_var = scope->LocalLookupVariable(
String::ZoneHandle(String::NewSymbol(":stacktrace_var")));
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);
ExpectSemicolon();
}
return statement;
}
// Static.
void Parser::ReportMsg(const Script& script,
intptr_t token_index,
const char* msg_type,
char* message,
const char* format, va_list args) {
const String& script_url = String::CheckedHandle(script.url());
const int buf_size = 256;
static char text_buffer[buf_size];
intptr_t line, column;
script.GetTokenLocation(token_index, &line, &column);
OS::VSNPrint(text_buffer, buf_size, format, args);
intptr_t msg_len =
OS::SNPrint(message, Parser::kErrorBuflen,
"'%s': %s: line %d pos %d: %s\n",
script_url.ToCString(), msg_type, line, column, text_buffer);
const String& text = String::Handle(script.GetLine(line));
ASSERT(!text.IsNull());
if (text.Length() < buf_size) {
OS::SNPrint(message + msg_len, Parser::kErrorBuflen - msg_len,
"%s\n%*s\n", text.ToCString(), column, "^");
}
}
void Parser::ErrorMsg(intptr_t token_index, const char* format, ...) {
va_list args;
va_start(args, format);
ReportMsg(script_, token_index, "Error", error_msg_, format, args);
Isolate::Current()->long_jump_base()->Jump(1, error_msg_);
UNREACHABLE();
}
void Parser::ErrorMsg(const char* format, ...) {
va_list args;
va_start(args, format);
ReportMsg(script_, token_index_, "Error", error_msg_, format, args);
Isolate::Current()->long_jump_base()->Jump(1, error_msg_);
UNREACHABLE();
}
void Parser::Warning(const char* format, ...) {
if (FLAG_silent_warnings) return;
va_list args;
va_start(args, format);
ReportMsg(script_, token_index_, "Warning", error_msg_, format, args);
if (FLAG_warning_as_error) {
Isolate::Current()->long_jump_base()->Jump(1, error_msg_);
UNREACHABLE();
}
}
void Parser::Unimplemented(const char* msg) {
ErrorMsg(token_index_, msg);
}
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::ExpectIdentifier(const char* msg) {
if (CurrentToken() != Token::kIDENT) {
ErrorMsg(msg);
}
String* ident = CurrentLiteral();
ConsumeToken();
return ident;
}
bool Parser::IsLiteral(const char* literal) {
return (CurrentToken() == Token::kIDENT)
&& CurrentLiteral()->Equals(literal, strlen(literal));
}
bool Parser::IsIncrementOperator(Token::Kind token) {
return token == Token::kINCR || token == Token::kDECR;
}
bool Parser::IsPrefixOperator(Token::Kind token) {
return token == Token::kADD || 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::ParseBinaryExpr(int min_preced) {
TRACE_PARSER("ParseBinaryExpr");
ASSERT(min_preced >= 4);
AstNode* left_operand = ParseUnaryExpr();
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 = token_index_;
ConsumeToken();
AstNode* right_operand = NULL;
if (op_kind != Token::kIS) {
right_operand = ParseBinaryExpr(current_preced + 1);
} else {
// For 'is' we expect the right operand to be a type.
if (CurrentToken() == Token::kNOT) {
ConsumeToken();
op_kind = Token::kISNOT;
}
const intptr_t type_pos = token_index_;
const Type& type = Type::ZoneHandle(ParseType(kMustResolve));
if (!type.IsInstantiated() &&
(current_block_->scope->function_level() > 0)) {
// Make sure that the instantiator is captured.
CaptureReceiver();
}
right_operand = new TypeNode(type_pos, type);
}
if (Token::IsRelationalOperator(op_kind)
|| Token::IsInstanceofOperator(op_kind)
|| Token::IsEqualityOperator(op_kind)) {
left_operand = new ComparisonNode(
op_pos, op_kind, left_operand, right_operand);
break; // Equality and relational operators cannot be chained.
} else {
StringConcatNode* str_concat = NULL;
if (op_kind == Token::kADD) {
if (left_operand->IsLiteralNode()) {
LiteralNode* lit = left_operand->AsLiteralNode();
if (lit->literal().IsString()) {
str_concat = new StringConcatNode(lit->token_index());
str_concat->AddExpr(lit);
}
} else if (left_operand->IsStringConcatNode()) {
str_concat = left_operand->AsStringConcatNode();
}
}
if (str_concat != NULL) {
str_concat->AddExpr(right_operand);
left_operand = str_concat;
} else {
left_operand = new BinaryOpNode(
op_pos, op_kind, left_operand, right_operand);
}
}
}
current_preced--;
}
return left_operand;
}
bool Parser::IsAssignableExpr(AstNode* expr) {
return expr->IsPrimaryNode()
|| expr->IsLoadLocalNode()
|| expr->IsLoadStaticFieldNode()
|| expr->IsStaticGetterNode()
|| expr->IsInstanceGetterNode()
|| expr->IsLoadIndexedNode();
}
AstNode* Parser::ParseExprList() {
TRACE_PARSER("ParseExprList");
AstNode* expressions = ParseExpr(kAllowConst);
if (CurrentToken() == Token::kCOMMA) {
// Collect comma-separated expressions in a sequence node.
SequenceNode* list = new SequenceNode(token_index_, current_block_->scope);
list->Add(expressions);
while (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
AstNode* expr = ParseExpr(kAllowConst);
list->Add(expr);
}
expressions = list;
}
return expressions;
}
static bool IsLocalOrLiteralNode(AstNode* node) {
return node->IsLoadLocalNode() || node->IsLiteralNode();
}
LocalVariable* Parser::CreateTempConstVariable(intptr_t token_index,
intptr_t token_id,
const char* s) {
char name[64];
OS::SNPrint(name, 64, "%s%d", s, token_id);
LocalVariable* temp =
new LocalVariable(token_index,
String::ZoneHandle(String::NewSymbol(name)),
Type::ZoneHandle(Type::VarType()));
temp->set_is_final();
current_block_->scope->AddVariable(temp);
return temp;
}
// If 'node' can create side effects, store its result in a temporary variable
// and return a LoadLocalNode instead.
// Side effect free nodes are LoadLocalNode and LiteralNode.
AstNode* Parser::AsSideEffectFreeNode(AstNode* node) {
if (node->IsLoadIndexedNode()) {
LoadIndexedNode* load_indexed = node->AsLoadIndexedNode();
intptr_t token_id = node->id();
intptr_t token_index = node->token_index();
node = NULL; // Do not use it.
if (!IsLocalOrLiteralNode(load_indexed->index_expr())) {
LocalVariable* temp =
CreateTempConstVariable(token_index, token_id, "lix");
AstNode* save =
new StoreLocalNode(token_index, *temp, load_indexed->index_expr());
current_block_->statements->Add(save);
AstNode* load = new LoadLocalNode(token_index, *temp);
load_indexed = new LoadIndexedNode(token_index,
load_indexed->array(),
load);
}
// The array object access may not have side effects.
if (!IsLocalOrLiteralNode(load_indexed->array())) {
LocalVariable* temp =
CreateTempConstVariable(token_index, token_id, "lia");
AstNode* save =
new StoreLocalNode(token_index, *temp, load_indexed->array());
current_block_->statements->Add(save);
AstNode* load = new LoadLocalNode(token_index, *temp);
load_indexed = new LoadIndexedNode(token_index,
load,
load_indexed->index_expr());
}
return load_indexed;
}
if (node->IsInstanceGetterNode()) {
InstanceGetterNode* getter = node->AsInstanceGetterNode();
intptr_t token_index = node->token_index();
intptr_t token_id = node->id();
node = NULL; // Do not use it.
if (!IsLocalOrLiteralNode(getter->receiver())) {
LocalVariable* temp =
CreateTempConstVariable(token_index, token_id, "igr");
AstNode* save =
new StoreLocalNode(token_index, *temp, getter->receiver());
current_block_->statements->Add(save);
AstNode* load = new LoadLocalNode(token_index, *temp);
getter = new InstanceGetterNode(token_index, load, getter->field_name());
}
return getter;
}
return node;
}
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_SAR:
return new BinaryOpNode(op_pos, Token::kSAR, 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 must be a compile time constant");
}
return new LiteralNode(expr_pos, EvaluateConstExpr(expr));
}
AstNode* Parser::ParseExpr(bool require_compiletime_const) {
TRACE_PARSER("ParseExpr");
const intptr_t expr_pos = token_index_;
AstNode* expr = ParseConditionalExpr();
if (!Token::IsAssignmentOperator(CurrentToken())) {
if (require_compiletime_const) {
expr = FoldConstExpr(expr_pos, expr);
}
return expr;
}
// Assignment expressions.
Token::Kind assignment_op = CurrentToken();
const intptr_t assignment_pos = token_index_;
ConsumeToken();
const intptr_t right_expr_pos = token_index_;
if (require_compiletime_const && (assignment_op != Token::kASSIGN)) {
ErrorMsg(right_expr_pos, "expression must be a compile time constant");
}
AstNode* right_expr = ParseExpr(require_compiletime_const);
if (assignment_op != Token::kASSIGN) {
expr = AsSideEffectFreeNode(expr);
}
right_expr =
ExpandAssignableOp(assignment_pos, assignment_op, expr, right_expr);
AstNode* assign_expr = expr->MakeAssignmentNode(right_expr);
if (assign_expr == NULL) {
ErrorMsg(assignment_pos,
"Left hand side of '%s' is not assignable",
Token::Str(assignment_op));
}
return assign_expr;
}
LiteralNode* Parser::ParseConstExpr() {
TRACE_PARSER("ParseConstExpr");
AstNode* expr = ParseExpr(kRequireConst);
ASSERT(expr->IsLiteralNode());
return expr->AsLiteralNode();
}
AstNode* Parser::ParseConditionalExpr() {
TRACE_PARSER("ParseConditionalExpr");
const intptr_t expr_pos = token_index_;
AstNode* expr = ParseBinaryExpr(Token::Precedence(Token::kOR));
if (CurrentToken() == Token::kCONDITIONAL) {
ConsumeToken();
AstNode* expr1 = ParseConditionalExpr();
ExpectToken(Token::kCOLON);
AstNode* expr2 = ParseConditionalExpr();
expr = new ConditionalExprNode(expr_pos, expr, expr1, expr2);
}
return expr;
}
AstNode* Parser::ParseUnaryExpr() {
TRACE_PARSER("ParseUnaryExpr");
AstNode* expr = NULL;
const intptr_t op_pos = token_index_;
if (IsPrefixOperator(CurrentToken())) {
Token::Kind unary_op = CurrentToken();
ConsumeToken();
expr = ParseUnaryExpr();
expr = UnaryOpNode::UnaryOpOrLiteral(op_pos, unary_op, expr);
} else if (IsIncrementOperator(CurrentToken())) {
Token::Kind incr_op = CurrentToken();
ConsumeToken();
expr = ParseUnaryExpr();
if (!IsAssignableExpr(expr)) {
ErrorMsg("Expression is not assignable");
}
// is_prefix.
AstNode* incr_op_node = expr->MakeIncrOpNode(op_pos, incr_op, true);
if (incr_op_node == NULL) {
Unimplemented("incr operation not implemented");
}
expr = incr_op_node;
} else {
expr = ParsePostfixExpr();
}
return expr;
}
ArgumentListNode* Parser::ParseActualParameters(AstNode* implicit_argument,
bool require_const) {
TRACE_PARSER("ParseActualParameters");
ASSERT(CurrentToken() == Token::kLPAREN);
const bool saved_mode = SetAllowFunctionLiterals(true);
ArgumentListNode* arguments = new ArgumentListNode(token_index_);
if (implicit_argument != NULL) {
arguments->Add(implicit_argument);
}
GrowableArray<const String*> names;
bool named_argument_seen = false;
if (LookaheadToken(1) != Token::kRPAREN) {
do {
ASSERT((CurrentToken() == Token::kLPAREN) ||
(CurrentToken() == Token::kCOMMA));
ConsumeToken();
if ((CurrentToken() == Token::kIDENT) &&
(LookaheadToken(1) == Token::kCOLON)) {
named_argument_seen = true;
// The canonicalization of the argument 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++) {
if (CurrentLiteral()->Equals(*names[i])) {
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));
} while (CurrentToken() == Token::kCOMMA);
} else {
ConsumeToken();
}
ExpectToken(Token::kRPAREN);
SetAllowFunctionLiterals(saved_mode);
if (named_argument_seen) {
arguments->set_names(Array::Handle(NewArray<const String>(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 = token_index_;
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(),
Resolver::kIsQualified));
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.
const Array& kNoArgumentNames = Array::Handle();
func = Resolver::ResolveStatic(cls,
getter_name,
kNumArguments,
kNoArgumentNames,
Resolver::kIsQualified);
if (!func.IsNull()) {
ASSERT(func.kind() != RawFunction::kConstImplicitGetter);
closure = new StaticGetterNode(call_pos,
Class::ZoneHandle(cls.raw()),
func_name);
return new ClosureCallNode(call_pos, closure, arguments);
}
} else {
closure = GenerateStaticFieldLookup(field, call_pos);
return new ClosureCallNode(call_pos, closure, arguments);
}
ErrorMsg(ident_pos, "unresolved static method '%s'", func_name.ToCString());
}
CheckFunctionIsCallable(call_pos, func);
return new StaticCallNode(call_pos, func, arguments);
}
AstNode* Parser::ParseInstanceCall(AstNode* receiver, const String& func_name) {
const intptr_t call_pos = token_index_;
if (CurrentToken() != Token::kLPAREN) {
ErrorMsg(call_pos, "left parenthesis expected");
}
ArgumentListNode* arguments = ParseActualParameters(NULL, kAllowConst);
return new InstanceCallNode(call_pos, receiver, func_name, arguments);
}
AstNode* Parser::ParseClosureCall(AstNode* closure) {
const intptr_t call_pos = token_index_;
ASSERT(CurrentToken() == Token::kLPAREN);
ArgumentListNode* arguments = ParseActualParameters(NULL, kAllowConst);
return new ClosureCallNode(call_pos, closure, arguments);
}
AstNode* Parser::ParseInstanceFieldAccess(AstNode* receiver,
const String& field_name) {
TRACE_PARSER("ParseInstanceFieldAccess");
AstNode* access = NULL;
const intptr_t call_pos = token_index_;
if (Token::IsAssignmentOperator(CurrentToken())) {
Token::Kind assignment_op = CurrentToken();
ConsumeToken();
AstNode* value = ParseExpr(kAllowConst);
AstNode* load_access =
new InstanceGetterNode(call_pos, receiver, field_name);
if (assignment_op != Token::kASSIGN) {
load_access = AsSideEffectFreeNode(load_access);
}
value = ExpandAssignableOp(call_pos, assignment_op, load_access, value);
access = load_access->MakeAssignmentNode(value);
} else {
access = CallGetter(call_pos, receiver, field_name);
}
return access;
}
AstNode* Parser::GenerateStaticFieldLookup(const Field& field,
intptr_t ident_pos) {
// Run static field initializer first if necessary.
RunStaticFieldInitializer(field);
// Access the field
if (field.is_final()) {
return new LiteralNode(ident_pos, Instance::ZoneHandle(field.value()));
} else {
return new LoadStaticFieldNode(ident_pos,
Field::ZoneHandle(field.raw()));
}
}
AstNode* Parser::ParseStaticFieldAccess(const Class& cls,
const String& field_name,
intptr_t ident_pos) {
TRACE_PARSER("ParseStaticFieldAccess");
AstNode* access = NULL;
const intptr_t call_pos = token_index_;
const Field& field = Field::ZoneHandle(cls.LookupStaticField(field_name));
Function& func = Function::ZoneHandle();
if (Token::IsAssignmentOperator(CurrentToken())) {
Token::Kind assignment_op = CurrentToken();
if (field.IsNull()) {
// No field, check if we have an explicit setter function.
const String& setter_name =
String::ZoneHandle(Field::SetterName(field_name));
const int kNumArguments = 1; // value.
const Array& kNoArgumentNames = Array::Handle();
func = Resolver::ResolveStatic(cls,
setter_name,
kNumArguments,
kNoArgumentNames,
Resolver::kIsQualified);
if (func.IsNull()) {
// No field or explicit setter function, this is an error.
ErrorMsg(ident_pos, "unknown static field '%s'",
field_name.ToCString());
return access;
}
}
ConsumeToken();
AstNode* value = ParseExpr(kAllowConst);
AstNode* load_access = NULL;
if (field.IsNull()) {
// No field found, we must have at least a setter function defined.
ASSERT(!func.IsNull());
// Explicit setter function for the field found, field does not exist.
// Create a getter node first in case it is needed. If getter node
// is used as part of, e.g., "+=", and the explicit getter does not
// exist, and error will be reported by the code generator.
load_access = new StaticGetterNode(call_pos,
Class::ZoneHandle(cls.raw()),
String::ZoneHandle(field_name.raw()));
} else {
// Field exists.
if (field.is_final()) {
// Field has been marked as final, report an error as the field
// is not settable.
ErrorMsg(ident_pos,
"field '%s' is const static, cannot assign to it",
field_name.ToCString());
return access;
}
load_access = GenerateStaticFieldLookup(field, token_index_);
}
value = ExpandAssignableOp(call_pos, assignment_op, load_access, value);
access = load_access->MakeAssignmentNode(value);
} else { // Not Token::IsAssignmentOperator(CurrentToken()).
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.
const Array& kNoArgumentNames = Array::Handle();
func = Resolver::ResolveStatic(cls,
getter_name,
kNumArguments,
kNoArgumentNames,
Resolver::kIsQualified);
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()) {
// No field or explicit getter function, this is an error.
ErrorMsg(ident_pos,
"unknown static field '%s'", field_name.ToCString());
return access;
}
access = CreateImplicitClosureNode(func, call_pos, NULL);
} else {
ASSERT(func.kind() != RawFunction::kConstImplicitGetter);
access = new StaticGetterNode(call_pos,
Class::ZoneHandle(cls.raw()),
field_name);
}
} else {
return GenerateStaticFieldLookup(field, token_index_);
}
}
return access;
}
AstNode* Parser::ParsePostfixExpr() {
TRACE_PARSER("ParsePostfixExpr");
const intptr_t postfix_expr_pos = token_index_;
AstNode* postfix_expr = ParsePrimary();
while (true) {
AstNode* selector = NULL;
AstNode* left = postfix_expr;
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
const intptr_t ident_pos = token_index_;
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.
Class& cls = Class::CheckedHandle(
left->AsPrimaryNode()->primary().raw());
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 = ParseInstanceFieldAccess(left, *ident);
} else {
// Static field access.
selector = ParseStaticFieldAccess(cls, *ident, ident_pos);
}
}
} else if (CurrentToken() == Token::kLBRACK) {
const intptr_t bracket_pos = token_index_;
ConsumeToken();
const bool saved_mode = SetAllowFunctionLiterals(true);
AstNode* index = ParseExpr(kAllowConst);
SetAllowFunctionLiterals(saved_mode);
ExpectToken(Token::kRBRACK);
AstNode* array = left;
if (left->IsPrimaryNode()) {
PrimaryNode* primary = left->AsPrimaryNode();
if (primary->primary().IsFunction()) {
ErrorMsg(bracket_pos, "cannot apply index operator to function");
} else if (primary->primary().IsClass()) {
ErrorMsg(bracket_pos, "cannot apply index operator to class");
} else if (primary->primary().IsString()) {
// Primary is an unresolved name.
String& name = String::CheckedZoneHandle(primary->primary().raw());
if (current_function().IsInStaticScope()) {
ErrorMsg(primary->token_index(),
"identifier '%s' is not declared in this scope",
name.ToCString());
} else {
// Treat as call to unresolved (instance) method.
AstNode* receiver = LoadReceiver();
selector = ParseInstanceCall(receiver, name);
}
} else {
// Internal parser error.
UNREACHABLE();
}
}
selector = new LoadIndexedNode(bracket_pos, array, index);
} else if (CurrentToken() == Token::kLPAREN) {
if (left->IsPrimaryNode()) {
PrimaryNode* primary = left->AsPrimaryNode();
const intptr_t primary_pos = primary->token_index();
if (primary->primary().IsFunction()) {
Function& func = Function::CheckedHandle(primary->primary().raw());
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().IsInStaticScope()) {
ErrorMsg(primary_pos,
"Cannot access instance method '%s' "
"from static function",
func_name.ToCString());
}
selector = ParseInstanceCall(LoadReceiver(), func_name);
}
} else if (primary->primary().IsString()) {
// Primary is an unresolved name.
String& name = String::CheckedZoneHandle(primary->primary().raw());
if (current_function().IsInStaticScope()) {
ErrorMsg(primary->token_index(),
"identifier '%s' is not declared in this scope",
name.ToCString());
} else {
// Treat as call to unresolved (instance) method.
AstNode* receiver = LoadReceiver();
selector = ParseInstanceCall(receiver, name);
}
} else if (primary->primary().IsClass()) {
ErrorMsg(left->token_index(),
"must use 'new' or 'const' to construct new instance");
} else {
// Internal parser error.
UNREACHABLE();
}
} else {
// Left is not a primary node; this must be a closure call.
AstNode* closure = left;
selector = ParseClosureCall(closure);
}
} else {
// No (more) selector to parse.
if (left->IsPrimaryNode()) {
if (left->AsPrimaryNode()->primary().IsString()) {
PrimaryNode* primary = left->AsPrimaryNode();
const String& ident =
String::CheckedZoneHandle(primary->primary().raw());
// An unresolved identifier that is not followed by a selector token
// . or [ or (.
// If we are in a static method, this is an error.
// If we are compiling an instance method, convert this into
// a runtime lookup for a field (which may be defined in a
// subclass.)
if (current_function().IsInStaticScope()) {
ErrorMsg(primary->token_index(),
"identifier '%s' is not declared in this scope",
ident.ToCString());
} else {
// Treat as call to unresolved (instance) field.
AstNode* receiver = LoadReceiver();
postfix_expr = ParseInstanceFieldAccess(receiver, ident);
}
} else if (left->AsPrimaryNode()->primary().IsFunction()) {
// Treat as implicit closure.
PrimaryNode* primary = left->AsPrimaryNode();
const Function& func =
Function::CheckedZoneHandle(primary->primary().raw());
const String& funcname = String::ZoneHandle(func.name());
if (func.is_static()) {
// Static function access.
postfix_expr = CreateImplicitClosureNode(func,
primary->token_index(),
NULL);
} else {
// Instance function access.
if (current_function().IsInStaticScope() ||
current_function().IsInFactoryScope()) {
ErrorMsg(primary->token_index(),
"illegal use of method '%s'",
funcname.ToCString());
}
AstNode* receiver = LoadReceiver();
postfix_expr = ParseInstanceFieldAccess(receiver, funcname);
}
}
}
// Done parsing selectors.
break;
}
ASSERT(selector != NULL);
postfix_expr = selector;
}
if (IsIncrementOperator(CurrentToken())) {
TRACE_PARSER("IncrementOperator");
Token::Kind incr_op = CurrentToken();
if (!IsAssignableExpr(postfix_expr)) {
ErrorMsg("expression is not assignable");
}
ConsumeToken();
// Not prefix.
AstNode* incr_op_node =
postfix_expr->MakeIncrOpNode(postfix_expr_pos, incr_op, false);
if (incr_op_node == NULL) {
Unimplemented("incr op not implemented");
}
postfix_expr = incr_op_node;
}
return postfix_expr;
}
bool Parser::ResolveTypeFromClass(intptr_t type_pos,
const Class& cls,
Type* type) {
ASSERT(type != NULL);
// Resolve class.
if (!type->HasResolvedTypeClass()) {
const String& unresolved_type_class =
String::Handle(type->unresolved_type_class());
// First check if the type is a type parameter of the given class.
const TypeParameter& type_parameter = TypeParameter::Handle(
cls.LookupTypeParameter(unresolved_type_class));
if (!type_parameter.IsNull()) {
CheckTypeParameterReference(type_pos, unresolved_type_class);
// A type parameter cannot be parameterized, so report an error if type
// arguments have previously been parsed.
if (type->arguments() != TypeArguments::null()) {
ErrorMsg(type_pos, "type parameter '%s' cannot be parameterized",
type_parameter.ToCString());
return false;
}
*type = type_parameter.raw();
return true;
}
const Class& resolved_type_class =
Class::Handle(LookupClass(unresolved_type_class));
if (resolved_type_class.IsNull()) {
return false;
}
Object& type_class = Object::Handle(resolved_type_class.raw());
ASSERT(type->IsParameterizedType());
// Replace unresolved type class with resolved type class.
ParameterizedType& parameterized_type = ParameterizedType::Handle();
parameterized_type ^= type->raw();
parameterized_type.set_type_class(type_class);
}
// Resolve type arguments, if any.
const TypeArguments& arguments = TypeArguments::Handle(type->arguments());
if (!arguments.IsNull()) {
intptr_t num_arguments = arguments.Length();
for (intptr_t i = 0; i < num_arguments; i++) {
Type& type_argument = Type::Handle(arguments.TypeAt(i));
if (!ResolveTypeFromClass(type_pos, cls, &type_argument)) {
return false;
}
arguments.SetTypeAt(i, type_argument);
}
}
return true;
}
// Return class for type name. If the name cannot be resolved (yet), give an
// error (if type_resolution == kMustResolve) or return the unresolved name.
RawObject* Parser::LookupTypeClass(const QualIdent& type_name,
TypeResolution type_resolution) {
ASSERT(type_name.ident != NULL);
Class& type_class = Class::Handle();
if (type_name.lib_prefix != NULL) {
Library& lib = Library::Handle(type_name.lib_prefix->library());
type_class ^= lib.LookupLocalClass(*(type_name.ident));
} else {
type_class ^= LookupClass(*(type_name.ident));
}
if (!type_class.IsNull()) {
return type_class.raw();
}
// Type name could not be resolved (yet).
if (type_resolution == kMustResolve) {
ErrorMsg(type_name.ident_pos, "type '%s' is not loaded",
type_name.ident->ToCString());
return Object::null_class();
}
// Return the type name.
return type_name.ident->raw();
}
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 (current_function().IsInStaticScope()) {
ErrorMsg(field_pos,
"cannot access instance field '%s' from a static function",
field_name.ToCString());
}
}
void Parser::CheckTypeParameterReference(intptr_t type_parameter_pos,
const String& type_parameter_name) {
// Type parameters cannot be referred to from a static function, except from
// a constructor or from a factory.
// A constructor is considered as non-static by the compiler.
if ((is_top_level_ &&
(current_member_ != NULL) &&
current_member_->has_static &&
!current_member_->has_factory) ||
(!current_function().IsNull() &&
current_function().IsInStaticScope() &&
!current_function().IsInFactoryScope())) {
ErrorMsg(type_parameter_pos,
"cannot refer to type parameter '%s' from a static function",
type_parameter_name.ToCString());
}
}
void Parser::RunStaticFieldInitializer(const Field& field) {
ASSERT(field.is_static());
const Instance& value = Instance::Handle(field.value());
if (value.raw() == Object::transition_sentinel()) {
ErrorMsg("Circular dependency while initializing static field '%s'",
String::Handle(field.name()).ToCString());
} else if (value.raw() == Object::sentinel()) {
// This field has not been referenced yet and thus the value has
// not been evaluated. Call the static getter method to evaluate
// the expression and canonicalize the value.
field.set_value(Instance::Handle(Object::transition_sentinel()));
const String& field_name = String::Handle(field.name());
const String& getter_name =
String::Handle(Field::GetterName(field_name));
const Class& cls = Class::Handle(field.owner());
GrowableArray<const Object*> arguments; // no arguments.
const int kNumArguments = 0; // no arguments.
const Array& kNoArgumentNames = Array::Handle();
const Function& func =
Function::Handle(Resolver::ResolveStatic(cls,
getter_name,
kNumArguments,
kNoArgumentNames,
Resolver::kIsQualified));
ASSERT(!func.IsNull());
ASSERT(func.kind() == RawFunction::kConstImplicitGetter);
Instance& const_value =
Instance::Handle(DartEntry::InvokeStatic(func, arguments));
if (const_value.IsUnhandledException()) {
ErrorMsg("Exception thrown in Parser::RunStaticFieldInitializer");
}
if (!const_value.IsNull()) {
const_value ^= const_value.Canonicalize();
}
field.set_value(const_value);
}
}
RawInstance* Parser::EvaluateConstConstructorCall(
const Class& type_class,
const TypeArguments& type_arguments,
const Function& constructor,
ArgumentListNode* arguments) {
GrowableArray<const Object*> arg_values(arguments->length() + 1);
Instance& instance = Instance::Handle();
if (!constructor.IsFactory()) {
instance = Instance::New(type_class);
if (!type_arguments.IsNull()) {
// TODO(regis): Where should we check the constraints on type parameters?
if (!type_arguments.IsInstantiated()) {
ErrorMsg("type must be constant in const constructor");
}
instance.SetTypeArguments(type_arguments);
}
arg_values.Add(&instance);
} else {
// Prepend type_arguments to list of arguments to factory.
ASSERT(type_arguments.IsZoneHandle());
arg_values.Add(&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.Add(&arg->AsLiteralNode()->literal());
}
const Instance& result = Instance::Handle(
DartEntry::InvokeStatic(constructor, arg_values));
if (result.IsUnhandledException()) {
ErrorMsg("Exception thrown in EvaluateConstConstructorCall");
}
if (constructor.IsFactory()) {
// The factory method returns the allocated object.
instance = result.raw();
}
if (!instance.IsNull()) {
instance ^= instance.Canonicalize();
}
return instance.raw();
}
// 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 (local != NULL) {
if (node != NULL) {
*node = new LoadLocalNode(ident_pos, *local);
}
return true;
}
// Try to find the identifier in the class scope.
Class& cls = Class::Handle(current_class().raw());
Function& func = Function::Handle();
Field& field = Field::Handle();
while (!cls.IsNull()) {
// First check if a field exists.
field = cls.LookupInstanceField(ident);
if (!field.IsNull()) {
if (node != NULL) {
CheckInstanceFieldAccess(ident_pos, ident);
*node = CallGetter(ident_pos, LoadReceiver(), ident);
}
return true;
}
field = cls.LookupStaticField(ident);
if (!field.IsNull()) {
if (node != NULL) {
*node = GenerateStaticFieldLookup(field, ident_pos);
}
return true;
}
// Now check if a getter/setter method exists for it in which case
// it is still a field.
const String& getter_name = String::Handle(Field::GetterName(ident));
func = cls.LookupDynamicFunction(getter_name);
if (!func.IsNull()) {
if (node != NULL) {
CheckInstanceFieldAccess(ident_pos, ident);
ASSERT(Type::Handle(func.result_type()).IsResolved());
*node = CallGetter(ident_pos, LoadReceiver(), ident);
}
return true;
}
func = cls.LookupStaticFunction(getter_name);
if (!func.IsNull()) {
if (node != NULL) {
ASSERT(Type::Handle(func.result_type()).IsResolved());
*node = new StaticGetterNode(ident_pos,
Class::ZoneHandle(cls.raw()),
ident);
}
return true;
}
const String& setter_name = String::Handle(Field::SetterName(ident));
func = cls.LookupDynamicFunction(setter_name);
if (!func.IsNull()) {
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(Type::Handle(func.result_type()).IsResolved());
*node = CallGetter(ident_pos, LoadReceiver(), ident);
}
return true;
}
func = cls.LookupStaticFunction(setter_name);
if (!func.IsNull()) {
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,
Class::ZoneHandle(cls.raw()),
ident);
}
return true;
}
// Check if an instance/static function exists.
func = cls.LookupDynamicFunction(ident);
if (func.IsNull()) {
func = cls.LookupStaticFunction(ident);
}
if (!func.IsNull()) {
if (node != NULL) {
*node = new PrimaryNode(ident_pos, Function::ZoneHandle(func.raw()));
}
return true;
}
cls = cls.SuperClass();
}
if (node != NULL) {
*node = NULL;
}
return false; // Not an unqualified identifier.
}
// Do a lookup for the identifier in the library scope of the specified
// library. If resolve_locally is true the lookup does not consider
// the libraries imported by it for the lookup.
AstNode* Parser::ResolveIdentInLibraryScope(const Library& lib,
const QualIdent& qual_ident,
bool resolve_locally) {
TRACE_PARSER("ResolveIdentInLibraryScope");
Object& obj = Object::Handle();
if (resolve_locally) {
obj = lib.LookupLocalObject(*qual_ident.ident);
} else {
obj = lib.LookupObject(*qual_ident.ident);
}
if (obj.IsClass()) {
Class& cls = Class::Handle();
cls ^= obj.raw();
return new PrimaryNode(qual_ident.ident_pos, Class::ZoneHandle(cls.raw()));
}
if (obj.IsField()) {
Field& field = Field::Handle();
field ^= obj.raw();
ASSERT(field.is_static());
return GenerateStaticFieldLookup(field, qual_ident.ident_pos);
}
Function& func = Function::Handle();
if (obj.IsFunction()) {
func ^= obj.raw();
ASSERT(func.is_static());
return new PrimaryNode(qual_ident.ident_pos,
Function::ZoneHandle(func.raw()));
} else {
ASSERT(obj.IsNull() || obj.IsLibraryPrefix());
}
// Check if there is a global getter or setter for qual_ident.
// We create a getter node even if a getter doesn't exist since
// qual_ident 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.
String& accessor_name = String::Handle(Field::GetterName(*qual_ident.ident));
if (resolve_locally) {
obj = lib.LookupLocalObject(accessor_name);
} else {
obj = lib.LookupObject(accessor_name);
}
if (obj.IsNull()) {
accessor_name = Field::SetterName(*qual_ident.ident);
if (resolve_locally) {
obj = lib.LookupLocalObject(accessor_name);
} else {
obj = lib.LookupObject(accessor_name);
}
}
if (!obj.IsNull()) {
ASSERT(obj.IsFunction());
func ^= obj.raw();
ASSERT(func.is_static());
ASSERT(Type::Handle(func.result_type()).IsResolved());
return new StaticGetterNode(qual_ident.ident_pos,
Class::ZoneHandle(func.owner()),
*qual_ident.ident);
}
if (qual_ident.qualifier != NULL) {
// This is an unresolved prefixed primary identifier, need to report
// an error.
ErrorMsg(qual_ident.ident_pos, "identifier '%s.%s' cannot be resolved",
(qual_ident.qualifier)->ToCString(),
(qual_ident.ident)->ToCString());
}
// Lexically unresolved primary identifiers are referenced by their name.
return new PrimaryNode(qual_ident.ident_pos, *qual_ident.ident);
}
// Resolve identifier, issue an error message if the name refers to
// a method or a class/interface.
// If the name cannot be resolved, turn it into an instance field access
// if we're compiling an instance method, or issue an error message
// if we're compiling a static method.
AstNode* Parser::ResolveVarOrField(intptr_t ident_pos, const String& ident) {
TRACE_PARSER("ResolveVarOrField");
// First try to find the variable in the local scope (block scope or
// class scope).
AstNode* var_or_field = NULL;
ResolveIdentInLocalScope(ident_pos, ident, &var_or_field);
if (var_or_field == NULL) {
// Not found in the local scope, so try finding the variable in the
// library scope (current library and all libraries imported by it).
QualIdent qual_ident;
qual_ident.qualifier = NULL;
qual_ident.lib_prefix = NULL;
qual_ident.ident_pos = ident_pos;
qual_ident.ident = &(String::ZoneHandle(ident.raw()));
var_or_field = ResolveIdentInLibraryScope(library_,
qual_ident,
kResolveIncludingImports);
}
if (var_or_field->IsPrimaryNode()) {
PrimaryNode* primary = var_or_field->AsPrimaryNode();
if (primary->primary().IsString()) {
// We got an unresolved name. If we are compiling a static
// method, this is an error. 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().IsInStaticScope()) {
ErrorMsg(ident_pos, "identifier '%s' is not declared in this scope",
ident.ToCString());
} else {
// Treat as call to unresolved instance field.
var_or_field = CallGetter(ident_pos, LoadReceiver(), ident);
}
} else if (primary->primary().IsFunction()) {
ErrorMsg(ident_pos, "illegal reference to method '%s'",
ident.ToCString());
} else {
ASSERT(primary->primary().IsClass());
ErrorMsg(ident_pos, "illegal reference to class or interface '%s'",
ident.ToCString());
}
}
return var_or_field;
}
// Parses type = [ident "."] ident ["<" type { "," type } ">"].
// Returns the class object if the type can be resolved. Otherwise, either give
// an error if type resolution was required, or return the unresolved name as a
// string object.
RawType* Parser::ParseType(TypeResolution type_resolution) {
if (CurrentToken() != Token::kIDENT) {
ErrorMsg("type name expected");
}
QualIdent type_name;
intptr_t type_pos = token_index_;
ParseQualIdent(&type_name);
if (type_name.local_scope_ident) {
ErrorMsg(type_pos, "Using '%s' in this context is invalid",
type_name.ident->ToCString());
}
Object& type_class = Object::Handle();
if (type_resolution == kDoNotResolve) {
// TODO(5072252): Figure out what should be done in this case.
if (type_name.qualifier != NULL) {
Unimplemented("cannot handle qualified type names yet");
}
type_class = type_name.ident->raw();
} else {
TypeParameter& type_parameter = TypeParameter::Handle();
// Check if qualifier is a type parameter of the class we are parsing.
if (type_name.qualifier != NULL) {
type_parameter =
current_class().LookupTypeParameter(*type_name.qualifier);
if (!type_parameter.IsNull()) {
ErrorMsg(type_pos, "Use of '%s' in this context is invalid",
type_name.qualifier->ToCString());
}
} else {
// Check if ident is a type parameter of the class we are parsing.
type_parameter = current_class().LookupTypeParameter(*type_name.ident);
if (!type_parameter.IsNull()) {
CheckTypeParameterReference(type_name.ident_pos, *type_name.ident);
if (CurrentToken() == Token::kLT) {
// A type parameter cannot be parameterized.
ErrorMsg(type_pos, "type parameter '%s' cannot be parameterized",
String::Handle(type_parameter.Name()).ToCString());
}
return type_parameter.raw();
}
}
// Try to resolve the type class.
type_class = LookupTypeClass(type_name, type_resolution);
}
TypeArguments& type_arguments =
TypeArguments::Handle(ParseTypeArguments(type_resolution));
const Type& type = Type::Handle(
Type::NewParameterizedType(type_class, type_arguments));
if (type_resolution == kMustResolve) {
const String& errmsg = String::Handle(
ClassFinalizer::FinalizeTypeWhileParsing(type));
if (!errmsg.IsNull()) {
ErrorMsg(errmsg.ToCString());
}
}
return type.raw();
}
// Parse "[" [ expr { "," expr } ["," ] "]".
// Note: if the array 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::ParseArrayLiteral(intptr_t type_pos,
bool is_const,
const TypeArguments& type_arguments) {
ASSERT(CurrentToken() == Token::kLBRACK || CurrentToken() == Token::kINDEX);
intptr_t literal_pos = token_index_;
bool is_empty_literal = CurrentToken() == Token::kINDEX;
ConsumeToken();
// If no type arguments are provided, leave them as null, which is equivalent
// to using Array<var>. See issue 4966724.
if (!type_arguments.IsNull()) {
// For now, only check the number of type arguments. See issue 4975876.
if (type_arguments.Length() != 1) {
ASSERT(type_pos >= 0);
ErrorMsg(type_pos, "wrong number of type arguments for Array literal");
}
}
// Parse the array elements. Note: there may be an optional extra
// comma after the last element.
ArrayNode* array = new ArrayNode(token_index_, type_arguments);
if (!is_empty_literal) {
const bool saved_mode = SetAllowFunctionLiterals(true);
while (CurrentToken() != Token::kRBRACK) {
array->AddElement(ParseExpr(is_const));
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 array at compile time.
Array& lit_array =
Array::ZoneHandle(Array::New(array->length(), Heap::kOld));
if (!type_arguments.IsNull()) {
// TODO(regis): Where should we check the constraints on type parameters?
if (!type_arguments.IsInstantiated()) {
ErrorMsg("type must be constant in const constructor");
}
lit_array.SetTypeArguments(type_arguments);
}
for (int i = 0; i < array->length(); i++) {
AstNode* elem = array->ElementAt(i);
// Arguments have been evaluated to a literal value already.
ASSERT(elem->IsLiteralNode());
lit_array.SetAt(i, elem->AsLiteralNode()->literal());
}
lit_array ^= lit_array.Canonicalize();
lit_array.MakeImmutable();
return new LiteralNode(literal_pos, lit_array);
} else {
if (!type_arguments.IsNull() &&
!type_arguments.IsInstantiated() &&
(current_block_->scope->function_level() > 0)) {
// Make sure that the instantiator is captured.
CaptureReceiver();
}
// Make a new growable array from the fixed array.
String& growable_object_array_class_name = String::Handle(
String::NewSymbol(kGrowableObjectArrayName));
const Class& growable_array_class = Class::Handle(
LookupImplClass(growable_object_array_class_name));
String& ctor_name =
String::Handle(String::NewSymbol(kGrowableObjectArrayFromArrayName));
Function& array_ctor = Function::ZoneHandle(
growable_array_class.LookupConstructor(ctor_name));
ASSERT(!array_ctor.IsNull());
ArgumentListNode* ctor_args = new ArgumentListNode(literal_pos);
ctor_args->Add(array);
return new ConstructorCallNode(
literal_pos, type_arguments, array_ctor, ctor_args);
}
}
static void AddKeyValuePair(ArrayNode* pairs,
bool is_const,
AstNode* key,
AstNode* value) {
if (is_const) {
ASSERT(key->IsLiteralNode());
ASSERT(key->AsLiteralNode()->literal().IsString());
const Instance& new_key = key->AsLiteralNode()->literal();
for (int i = 0; i < pairs->length(); i += 2) {
const Instance& key_i =
pairs->ElementAt(i)->AsLiteralNode()->literal();
ASSERT(key_i.IsString());
if (new_key.Equals(key_i)) {
// Duplicate key found. The new value replaces the previously
// defined value.
pairs->SetElementAt(i + 1, value);
return;
}
}
}
pairs->AddElement(key);
pairs->AddElement(value);
}
AstNode* Parser::ParseMapLiteral(intptr_t type_pos,
bool is_const,
const TypeArguments& type_arguments) {
TRACE_PARSER("ParseMapLiteral");
ASSERT(CurrentToken() == Token::kLBRACE);
intptr_t literal_pos = token_index_;
ConsumeToken();
String& map_class_name = String::Handle(
String::NewSymbol(is_const ? kImmutableMapName : kMutableMapName));
const Class& map_class = Class::Handle(LookupImplClass(map_class_name));
ASSERT(!map_class.IsNull());
TypeArguments& map_type_arguments =
TypeArguments::ZoneHandle(type_arguments.raw());
// If no type arguments are provided, leave them as null, which is equivalent
// to using Map<var, var>. See issue 4966724.
if (!map_type_arguments.IsNull()) {
// For now, only check the number of type arguments. See issue 4975876.
if (map_type_arguments.Length() != 2) {
ASSERT(type_pos >= 0);
ErrorMsg(type_pos, "wrong number of type arguments for Map literal");
}
}
// Parse the map entries. Note: there may be an optional extra
// comma after the last entry.
ArrayNode* kv_pairs =
new ArrayNode(token_index_, TypeArguments::ZoneHandle());
while (CurrentToken() != Token::kRBRACE) {
AstNode* key = NULL;
if (CurrentToken() == Token::kSTRING) {
key = ParseStringLiteral();
}
if (key == NULL) {
ErrorMsg("map entry key must be string literal");
} else if (is_const && !key->IsLiteralNode()) {
ErrorMsg("map entry key must be compile time constant string");
}
ExpectToken(Token::kCOLON);
const bool saved_mode = SetAllowFunctionLiterals(true);
AstNode* value = ParseExpr(is_const);
SetAllowFunctionLiterals(saved_mode);
AddKeyValuePair(kv_pairs, is_const, key, value);
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
} else if (CurrentToken() != Token::kRBRACE) {
ErrorMsg("comma or '}' expected");
}
}
ASSERT(kv_pairs->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->length(), Heap::kOld));
for (int i = 0; i < kv_pairs->length(); i++) {
AstNode* arg = kv_pairs->ElementAt(i);
// Arguments have been evaluated to a literal value already.
ASSERT(arg->IsLiteralNode());
key_value_array.SetAt(i, arg->AsLiteralNode()->literal());
}
key_value_array ^= key_value_array.Canonicalize();
key_value_array.MakeImmutable();
// Construct the map object.
ArgumentListNode* constr_args = new ArgumentListNode(token_index_);
constr_args->Add(new LiteralNode(literal_pos, key_value_array));
const String& constr_name =
String::Handle(String::NewSymbol(kImmutableMapConstructorName));
const Function& map_constr = Function::ZoneHandle(
map_class.LookupConstructor(constr_name));
ASSERT(!map_constr.IsNull());
return new LiteralNode(literal_pos, Instance::ZoneHandle(
EvaluateConstConstructorCall(
map_class, map_type_arguments, map_constr, constr_args)));
} else {
// Static call at runtime.
const String& static_factory_name =
String::Handle(String::NewSymbol(kMutableMapFromLiteralName));
const Function& static_factory = Function::ZoneHandle(
map_class.LookupStaticFunction(static_factory_name));
ASSERT(!static_factory.IsNull());
if (!map_type_arguments.IsNull() &&
!map_type_arguments.IsInstantiated() &&
(current_block_->scope->function_level() > 0)) {
// Make sure that the instantiator is captured.
CaptureReceiver();
}
ArgumentListNode* factory_param = new ArgumentListNode(literal_pos);
factory_param->Add(kv_pairs);
return new StaticCallNode(
literal_pos, static_factory, factory_param);
}
}
AstNode* Parser::ParseCompoundLiteral() {
bool is_const = false;
if (CurrentToken() == Token::kCONST) {
is_const = true;
ConsumeToken();
}
intptr_t type_pos = token_index_;
TypeArguments& type_arguments =
TypeArguments::ZoneHandle(ParseTypeArguments(kMustResolve));
AstNode* primary = NULL;
if ((CurrentToken() == Token::kLBRACK) ||
(CurrentToken() == Token::kINDEX)) {
primary = ParseArrayLiteral(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;
}
static const 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.
const String& period = String::Handle(String::NewSymbol("."));
String& constructor_name =
String::Handle(String::Concat(type_class_name, period));
if (named_constructor != NULL) {
constructor_name = String::Concat(constructor_name, *named_constructor);
}
return constructor_name;
}
AstNode* Parser::ParseNewOperator() {
TRACE_PARSER("ParseNewOperator");
const intptr_t new_pos = token_index_;
ASSERT((CurrentToken() == Token::kNEW) || (CurrentToken() == Token::kCONST));
bool is_const = (CurrentToken() == Token::kCONST);
ConsumeToken();
if (CurrentToken() != Token::kIDENT) {
ErrorMsg("type name expected");
}
// The grammar allows for an optional ('.' identifier)?, which is a named
// constructor. For that reason, we cannot unconditionally call
// ParseType(kMustResolve) after we see an identifier, because the named
// constructor would be misinterpreted as a qualified type name.
// TODO(regis): Revisit once we correctly support qualified identifiers.
// For now, we inline a customized version of ParseType(kMustResolve).
Type& type = Type::Handle();
Class& type_class = Class::ZoneHandle();
String& type_class_name = String::Handle();
TypeArguments& type_arguments = TypeArguments::ZoneHandle();
String* named_constructor = NULL;
intptr_t type_pos = token_index_;
QualIdent type_name;
ParseQualIdent(&type_name);
if (type_name.local_scope_ident) {
ErrorMsg(type_pos, "Using '%s' in this context is invalid",
type_name.ident->ToCString());
}
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
named_constructor = ExpectIdentifier("identifier expected after '.'");
}
TypeParameter& type_parameter = TypeParameter::Handle();
if (type_name.lib_prefix != NULL) {
// TODO(regis): Ascertain that this check for shadowing is valid
// See bug (490270).
// Check if qualifier is a type parameter of the class we are parsing.
type_parameter ^= current_class().LookupTypeParameter(*type_name.qualifier);
if (!type_parameter.IsNull()) {
CheckTypeParameterReference(type_pos, *type_name.qualifier);
ErrorMsg(type_pos, "type parameter '%s' cannot be instantiated",
String::Handle(type_parameter.Name()).ToCString());
}
}
// Check if ident is a type parameter of the class we are parsing.
type_parameter = current_class().LookupTypeParameter(*type_name.ident);
if (!type_parameter.IsNull()) {
CheckTypeParameterReference(type_name.ident_pos, *type_name.ident);
ErrorMsg(type_pos, "type parameter '%s' cannot be instantiated",
String::Handle(type_parameter.Name()).ToCString());
}
type_class ^= LookupTypeClass(type_name, kMustResolve);
type_class_name = type_class.Name();
// Type arguments are not allowed after the optional constructor name.
if (named_constructor == NULL) {
type_arguments = ParseTypeArguments(kMustResolve);
type = Type::NewParameterizedType(type_class, type_arguments);
const String& errmsg = String::Handle(
ClassFinalizer::FinalizeTypeWhileParsing(type));
if (!errmsg.IsNull()) {
ErrorMsg(errmsg.ToCString());
}
// The type argument vector may have been expanded with the type arguments
// of the super type when finalizing the type.
type_arguments = type.arguments();
}
if ((named_constructor == NULL) && (CurrentToken() == Token::kPERIOD)) {
ConsumeToken();
named_constructor = ExpectIdentifier("name of constructor expected");
}
// Parse constructor parameters.
if (CurrentToken() != Token::kLPAREN) {
ErrorMsg("'(' expected");
}
ArgumentListNode* arguments = ParseActualParameters(NULL, is_const);
// A constructor has an implicit 'this' parameter (instance to construct)
// and a factory has an implicit 'this' parameter (type_arguments).
intptr_t arguments_length = arguments->length() + 1;
if (type_class.is_interface()) {
// We need to make sure that an appropriate constructor is
// declared in the interface.
const String& constructor_name =
BuildConstructorName(type_class_name, named_constructor);
const String& external_constructor_name =
(named_constructor ? constructor_name : type_class_name);
Function& constructor = Function::ZoneHandle(
type_class.LookupConstructor(constructor_name));
if (constructor.IsNull()) {
ErrorMsg(new_pos, "interface '%s' has no constructor named '%s'",
type_class_name.ToCString(),
external_constructor_name.ToCString());
}
if (!constructor.AreValidArguments(arguments_length, arguments->names())) {
ErrorMsg(new_pos, "invalid arguments passed to constructor '%s' "
"for interface '%s'",
external_constructor_name.ToCString(),
type_class_name.ToCString());
}
// TODO(srdjan): Evaluate if the mapping should occur during code
// generation or here in the parser.
const Type& factory_type = Type::Handle(type_class.factory_type());
if (factory_type.IsNull()) {
ErrorMsg("cannot allocate interface '%s' without factory class",
type_class_name.ToCString());
}
if (!factory_type.HasResolvedTypeClass()) {
// This error can occur only with bootstrap classes.
const String& missing_class_name =
String::Handle(factory_type.unresolved_type_class());
ErrorMsg("Unresolved factory class '%s'",
missing_class_name.ToCString());
}
// Only change the class of the constructor to the factory class if the
// factory class implements the interface 'type'.
Class& factory_type_class = Class::Handle(factory_type.type_class());
// TODO(regis): Verify in the guide/spec that a factory class must have
// identical type parameters as the interface.
// TODO(regis): Do we check that in the parser?
// Assuming that it has been checked, it is sufficient to test if the
// raw factory type implements the raw interface type.
if (factory_type_class.IsSubtypeOf(TypeArguments::Handle(),
type_class,
TypeArguments::Handle())) {
type_class_name = factory_type_class.Name();
}
// Always change the result type of the constructor to the factory type.
type_class = factory_type_class.raw();
ASSERT(!type_class.is_interface());
}
// Make sure that an appropriate constructor exists.
const String& constructor_name =
BuildConstructorName(type_class_name, named_constructor);
const String& external_constructor_name =
(named_constructor ? constructor_name : type_class_name);
Function& constructor = Function::ZoneHandle(
type_class.LookupConstructor(constructor_name));
if (constructor.IsNull()) {
constructor = type_class.LookupFactory(constructor_name);
}
if (constructor.IsNull()) {
ErrorMsg(new_pos, "class '%s' has no constructor or factory named '%s'",
String::Handle(type_class.Name()).ToCString(),
external_constructor_name.ToCString());
}
if (!constructor.AreValidArguments(arguments_length, arguments->names())) {
ErrorMsg(new_pos, "invalid arguments passed to constructor '%s' "
"for class '%s'",
external_constructor_name.ToCString(),
String::Handle(type_class.Name()).ToCString());
}
AstNode* new_object = NULL;
if (is_const) {
if (!constructor.is_const()) {
ErrorMsg("'const' requires const constructor: '%s'",
String::Handle(constructor.name()).ToCString());
}
const Instance& const_instance = Instance::ZoneHandle(
EvaluateConstConstructorCall(
type_class, type_arguments, constructor, arguments));
new_object = new LiteralNode(new_pos, const_instance);
} else {
CheckFunctionIsCallable(new_pos, constructor);
if (!type_arguments.IsNull() &&
!type_arguments.IsInstantiated() &&
(current_block_->scope->function_level() > 0)) {
// Make sure that the instantiator is captured.
CaptureReceiver();
}
new_object = new ConstructorCallNode(
new_pos, type_arguments, constructor, arguments);
}
return new_object;
}
String& Parser::Interpolate(ArrayNode* values) {
const String& class_name =
String::Handle(String::NewSymbol(kStringClassName));
const Class& cls = Class::Handle(LookupImplClass(class_name));
ASSERT(!cls.IsNull());
const String& func_name = String::Handle(String::NewSymbol(kInterpolateName));
const Function& func =
Function::Handle(cls.LookupStaticFunction(func_name));
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->ElementAt(i)->IsLiteralNode());
value_arr.SetAt(i, values->ElementAt(i)->AsLiteralNode()->literal());
}
// Build argument array to pass to the interpolation function.
GrowableArray<const Object*> interpolate_arg;
interpolate_arg.Add(&value_arr);
// Call interpolation function.
String& concatenated = String::ZoneHandle();
concatenated ^= DartEntry::InvokeStatic(func, interpolate_arg);
if (concatenated.IsUnhandledException()) {
ErrorMsg("Exception thrown in Parser::Interpolate");
}
concatenated = String::NewSymbol(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, and
// there are never two kSTRING tokens next to each other.
AstNode* Parser::ParseStringLiteral() {
AstNode* primary = NULL;
intptr_t literal_start = token_index_;
if ((CurrentToken() == Token::kSTRING) &&
(LookaheadToken(1) != Token::kINTERPOL_VAR) &&
(LookaheadToken(1) != Token::kINTERPOL_START)) {
// Common case: no interpolation.
primary = new LiteralNode(literal_start, *CurrentLiteral());
ConsumeToken();
return primary;
}
// String interpolation needed.
bool is_compiletime_const = true;
ArrayNode* values = new ArrayNode(token_index_, TypeArguments::ZoneHandle());
GrowableArray<const Object*> arg_values;
while (CurrentToken() == Token::kSTRING) {
values->AddElement(new LiteralNode(token_index_, *CurrentLiteral()));
ConsumeToken();
if ((CurrentToken() != Token::kINTERPOL_VAR) &&
(CurrentToken() != Token::kINTERPOL_START)) {
break;
}
while ((CurrentToken() == Token::kINTERPOL_VAR) ||
(CurrentToken() == Token::kINTERPOL_START)) {
AstNode* expr = NULL;
const intptr_t expr_pos = token_index_;
if (CurrentToken() == Token::kINTERPOL_VAR) {
expr = ResolveVarOrField(token_index_, *CurrentLiteral());
ASSERT(!expr->IsPrimaryNode());
ConsumeToken();
} else {
ASSERT(CurrentToken() == Token::kINTERPOL_START);
ConsumeToken();
expr = ParseExpr(kAllowConst);
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.
if (is_compiletime_const) {
const Object* const_expr = expr->EvalConstExpr();
if (const_expr != NULL) {
// Change expr into a literal.
expr = new LiteralNode(expr_pos, EvaluateConstExpr(expr));
} else {
is_compiletime_const = false;
}
}
values->AddElement(expr);
}
// A string literal always ends with a kSTRING token.
ASSERT(CurrentToken() == Token::kSTRING);
}
if (is_compiletime_const) {
primary = new LiteralNode(literal_start, Interpolate(values));
} else {
ArgumentListNode* interpolate_arg =
new ArgumentListNode(values->token_index());
interpolate_arg->Add(values);
primary = MakeStaticCall(kStringClassName,
kInterpolateName,
interpolate_arg);
}
return primary;
}
AstNode* Parser::ParsePrimary() {
TRACE_PARSER("ParsePrimary");
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 (CurrentToken() == Token::kIDENT) {
QualIdent qual_ident;
ParseQualIdent(&qual_ident);
if (qual_ident.local_scope_ident) {
ResolveIdentInLocalScope(qual_ident.ident_pos,
*qual_ident.ident,
&primary);
} else {
if (qual_ident.qualifier == NULL) {
// This is an unqualified identifier so resolve the identifier
// locally in the main app library and all libraries imported by it.
primary = ResolveIdentInLibraryScope(library_,
qual_ident,
kResolveIncludingImports);
} 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).
const Library& lib = Library::Handle(qual_ident.lib_prefix->library());
primary = ResolveIdentInLibraryScope(lib,
qual_ident,
kResolveLocally);
}
}
ASSERT(primary != NULL);
} else if (CurrentToken() == Token::kTHIS) {
const String& this_name = String::Handle(String::NewSymbol(kThisName));
LocalVariable* local = LookupLocalScope(this_name);
if (local == NULL) {
ErrorMsg("unexpected use of 'this' in primary expression");
}
primary = new LoadLocalNode(token_index_, *local);
ConsumeToken();
} else if (CurrentToken() == Token::kINTEGER) {
String* int_literal = CurrentLiteral();
ASSERT(int_literal != NULL);
ASSERT(int_literal->Length() > 0);
const Integer& literal = Integer::ZoneHandle(Integer::New(*int_literal));
primary = new LiteralNode(token_index_, literal);
ConsumeToken();
} else if (CurrentToken() == Token::kTRUE) {
primary = new LiteralNode(token_index_, Bool::ZoneHandle(Bool::True()));
ConsumeToken();
} else if (CurrentToken() == Token::kFALSE) {
primary = new LiteralNode(token_index_, Bool::ZoneHandle(Bool::False()));
ConsumeToken();
} else if (CurrentToken() == Token::kNULL) {
primary = new LiteralNode(token_index_, Instance::ZoneHandle());
ConsumeToken();
} else if (CurrentToken() == Token::kLPAREN) {
ConsumeToken();
const bool saved_mode = SetAllowFunctionLiterals(true);
primary = ParseExpr(kAllowConst);
SetAllowFunctionLiterals(saved_mode);
ExpectToken(Token::kRPAREN);
} else if (CurrentToken() == Token::kDOUBLE) {
String* double_literal = CurrentLiteral();
ASSERT(double_literal != NULL);
ASSERT(double_literal->Length() > 0);
Double& double_value =
Double::ZoneHandle(Double::New(*double_literal));
if (double_value.IsNull()) {
ErrorMsg("invalid double literal");
}
double_value ^= double_value.Canonicalize();
primary = new LiteralNode(token_index_, double_value);
ConsumeToken();
} else if (CurrentToken() == Token::kSTRING) {
primary = ParseStringLiteral();
} else if (CurrentToken() == Token::kNEW) {
primary = ParseNewOperator();
} 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 {
primary = ParseNewOperator();
}
} else if (CurrentToken() == Token::kLT ||
CurrentToken() == Token::kLBRACK ||
CurrentToken() == Token::kINDEX ||
CurrentToken() == Token::kLBRACE) {
primary = ParseCompoundLiteral();
} else if (CurrentToken() == Token::kSUPER) {
if (current_function().IsInStaticScope()) {
ErrorMsg("cannot access superclass from static method");
} else if (current_function().IsLocalFunction()) {
ErrorMsg("cannot access superclass from local function");
}
ConsumeToken();
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
const String& ident = *ExpectIdentifier("identifier expected");
if (CurrentToken() == Token::kLPAREN) {
primary = ParseSuperCall(ident);
} else {
primary = ParseSuperFieldAccess(ident);
}
} else if (CurrentToken() == Token::kLBRACK) {
Unimplemented("Don't know yet how to interpret super[expr]");
} else {
ErrorMsg("Expected '.' or '[' after 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(AstNode* expr) {
if (expr->IsLiteralNode()) {
return expr->AsLiteralNode()->literal();
} else {
ASSERT(expr->EvalConstExpr() != NULL);
ReturnNode* ret = new ReturnNode(expr->token_index(), 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_index(), empty_scope);
seq->Add(ret);
Instance& value = Instance::ZoneHandle(Compiler::ExecuteOnce(seq));
if (value.IsNull()) {
value ^= value.Canonicalize();
}
return value;
}
}
void Parser::SkipFunctionLiteral() {
if (CurrentToken() == Token::kIDENT) {
if (LookaheadToken(1) != Token::kLPAREN) {
SkipType(true);
}
ExpectIdentifier("function name expected");
}
if (CurrentToken() == Token::kLPAREN) {
const bool allow_explicit_default_values = true;
ParamList ignore_params;
ParseFormalParameterList(allow_explicit_default_values, &ignore_params);
}
if (CurrentToken() == Token::kLBRACE) {
SkipBlock();
} else if (CurrentToken() == Token::kARROW) {
ConsumeToken();
SkipExpr();
}
}
void Parser::SkipArrayLiteral() {
if (CurrentToken() == Token::kINDEX) {
// Empty array literal.
ConsumeToken();
return;
}
ExpectToken(Token::kLBRACK);
while (CurrentToken() != Token::kRBRACK) {
SkipNestedExpr();
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
}
}
ExpectToken(Token::kRBRACK);
}
void Parser::SkipMapLiteral() {
ExpectToken(Token::kLBRACE);
while (CurrentToken() == Token::kSTRING) {
SkipStringLiteral();
ExpectToken(Token::kCOLON);
SkipNestedExpr();
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
}
}
ExpectToken(Token::kRBRACE);
}
void Parser::SkipActualParameters() {
ExpectToken(Token::kLPAREN);
while (CurrentToken() != Token::kRPAREN) {
SkipNestedExpr();
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
}
}
ExpectToken(Token::kRPAREN);
}
void Parser::SkipCompoundLiteral() {
if (CurrentToken() == Token::kLT) {
SkipTypeArguments();
}
if ((CurrentToken() == Token::kLBRACK) ||
(CurrentToken() == Token::kINDEX)) {
SkipArrayLiteral();
} else if (CurrentToken() == Token::kLBRACE) {
SkipMapLiteral();
}
}
void Parser::SkipNewOperator() {
ConsumeToken(); // Skip new or const keyword.
if (CurrentToken() == Token::kIDENT) {
SkipType(false);
if (CurrentToken() == Token::kLPAREN) {
SkipActualParameters();
return;
}
}
}
void Parser::SkipStringLiteral() {
ASSERT(CurrentToken() == Token::kSTRING);
while (CurrentToken() == Token::kSTRING) {
ConsumeToken();
if ((CurrentToken() != Token::kINTERPOL_VAR) &&
(CurrentToken() != Token::kINTERPOL_START)) {
break;
}
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::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::kSUPER:
Unimplemented("skipping of super not yet implemented");
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;
default:
UnexpectedToken();
UNREACHABLE();
break;
}
}
void Parser::SkipPostfixExpr() {
SkipPrimary();
while (true) {
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;
}
}
if (IsIncrementOperator(CurrentToken())) {
ConsumeToken();
}
}
void Parser::SkipUnaryExpr() {
if (IsPrefixOperator(CurrentToken()) ||
IsIncrementOperator(CurrentToken())) {
ConsumeToken();
SkipUnaryExpr();
} else {
SkipPostfixExpr();
}
}
void Parser::SkipBinaryExpr() {
SkipUnaryExpr();
while (Token::Precedence(Token::kOR) <= Token::Precedence(CurrentToken()) &&
Token::Precedence(CurrentToken()) <= Token::Precedence(Token::kMUL)) {
ConsumeToken();
SkipUnaryExpr();
}
}
void Parser::SkipConditionalExpr() {
SkipBinaryExpr();
if (CurrentToken() == Token::kCONDITIONAL) {
ConsumeToken();
SkipConditionalExpr();
ExpectToken(Token::kCOLON);
SkipConditionalExpr();
}
}
void Parser::SkipExpr() {
SkipConditionalExpr();
if (Token::IsAssignmentOperator(CurrentToken())) {
ConsumeToken();
SkipExpr();
}
}
void Parser::SkipNestedExpr() {
const bool saved_mode = SetAllowFunctionLiterals(true);
SkipExpr();
SetAllowFunctionLiterals(saved_mode);
}
} // namespace dart