blob: ec199eae568a86deabe777e2ecd7282b8782385f [file] [log] [blame]
<
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/parser.h"
#include "lib/invocation_mirror.h"
#include "platform/utils.h"
#include "vm/ast_transformer.h"
#include "vm/bootstrap.h"
#include "vm/class_finalizer.h"
#include "vm/compiler.h"
#include "vm/compiler_stats.h"
#include "vm/dart_api_impl.h"
#include "vm/dart_entry.h"
#include "vm/flags.h"
#include "vm/growable_array.h"
#include "vm/handles.h"
#include "vm/hash_table.h"
#include "vm/heap.h"
#include "vm/isolate.h"
#include "vm/longjump.h"
#include "vm/native_arguments.h"
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/os.h"
#include "vm/regexp_assembler.h"
#include "vm/report.h"
#include "vm/resolver.h"
#include "vm/scanner.h"
#include "vm/scopes.h"
#include "vm/stack_frame.h"
#include "vm/symbols.h"
#include "vm/tags.h"
#include "vm/timer.h"
#include "vm/zone.h"
namespace dart {
DEFINE_FLAG(bool, enable_debug_break, false, "Allow use of break \"message\".");
DEFINE_FLAG(bool, enable_mirrors, true,
"Disable to make importing dart:mirrors an error.");
DEFINE_FLAG(bool, load_deferred_eagerly, false,
"Load deferred libraries eagerly.");
DEFINE_FLAG(bool, trace_parser, false, "Trace parser operations.");
DEFINE_FLAG(bool, warn_mixin_typedef, true, "Warning on legacy mixin typedef.");
DEFINE_FLAG(bool, link_natives_lazily, false, "Link native calls lazily");
DEFINE_FLAG(bool, move_super, false, "Move super initializer to end of list");
DECLARE_FLAG(bool, lazy_dispatchers);
DECLARE_FLAG(bool, load_deferred_eagerly);
DECLARE_FLAG(bool, profile_vm);
DECLARE_FLAG(bool, throw_on_javascript_int_overflow);
DECLARE_FLAG(bool, warn_on_javascript_compatibility);
// Quick access to the current thread, isolate and zone.
#define T (thread())
#define I (isolate())
#define Z (zone())
#if defined(DEBUG)
class TraceParser : public ValueObject {
public:
TraceParser(intptr_t token_pos,
const Script& script,
intptr_t* trace_indent,
const char* msg) {
indent_ = trace_indent;
if (FLAG_trace_parser) {
// Skips tracing of bootstrap libraries.
if (script.HasSource()) {
intptr_t line, column;
script.GetTokenLocation(token_pos, &line, &column);
PrintIndent();
OS::Print("%s (line %" Pd ", col %" Pd ", token %" Pd ")\n",
msg, line, column, token_pos);
}
(*indent_)++;
}
}
~TraceParser() {
if (FLAG_trace_parser) {
(*indent_)--;
ASSERT(*indent_ >= 0);
}
}
private:
void PrintIndent() {
for (intptr_t i = 0; i < *indent_; i++) { OS::Print(". "); }
}
intptr_t* indent_;
};
#define TRACE_PARSER(s) \
TraceParser __p__(this->TokenPos(), this->script_, &this->trace_indent_, s)
#else // not DEBUG
#define TRACE_PARSER(s)
#endif // DEBUG
class BoolScope : public ValueObject {
public:
BoolScope(bool* addr, bool new_value) : _addr(addr), _saved_value(*addr) {
*_addr = new_value;
}
~BoolScope() {
*_addr = _saved_value;
}
private:
bool* _addr;
bool _saved_value;
};
static RawTypeArguments* NewTypeArguments(
const GrowableArray<AbstractType*>& objs) {
const TypeArguments& a =
TypeArguments::Handle(TypeArguments::New(objs.length()));
for (int i = 0; i < objs.length(); i++) {
a.SetTypeAt(i, *objs.At(i));
}
// Cannot canonicalize TypeArgument yet as its types may not have been
// finalized yet.
return a.raw();
}
LocalVariable* ParsedFunction::EnsureExpressionTemp() {
if (!has_expression_temp_var()) {
LocalVariable* temp =
new (Z) LocalVariable(function_.token_pos(),
Symbols::ExprTemp(),
Type::ZoneHandle(Type::DynamicType()));
ASSERT(temp != NULL);
set_expression_temp_var(temp);
}
ASSERT(has_expression_temp_var());
return expression_temp_var();
}
void ParsedFunction::EnsureFinallyReturnTemp(bool is_async) {
if (!has_finally_return_temp_var()) {
LocalVariable* temp = new(Z) LocalVariable(
function_.token_pos(),
String::ZoneHandle(Z, Symbols::New(":finally_ret_val")),
Type::ZoneHandle(Z, Type::DynamicType()));
ASSERT(temp != NULL);
temp->set_is_final();
if (is_async) {
temp->set_is_captured();
}
set_finally_return_temp_var(temp);
}
ASSERT(has_finally_return_temp_var());
}
void ParsedFunction::SetNodeSequence(SequenceNode* node_sequence) {
ASSERT(node_sequence_ == NULL);
ASSERT(node_sequence != NULL);
node_sequence_ = node_sequence;
}
void ParsedFunction::SetRegExpCompileData(
RegExpCompileData* regexp_compile_data) {
ASSERT(regexp_compile_data_ == NULL);
ASSERT(regexp_compile_data != NULL);
regexp_compile_data_ = regexp_compile_data;
}
void ParsedFunction::AddDeferredPrefix(const LibraryPrefix& prefix) {
// 'deferred_prefixes_' are used to invalidate code, but no invalidation is
// needed if --load_deferred_eagerly.
ASSERT(!FLAG_load_deferred_eagerly);
ASSERT(prefix.is_deferred_load());
ASSERT(!prefix.is_loaded());
for (intptr_t i = 0; i < deferred_prefixes_->length(); i++) {
if ((*deferred_prefixes_)[i]->raw() == prefix.raw()) {
return;
}
}
deferred_prefixes_->Add(&LibraryPrefix::ZoneHandle(Z, prefix.raw()));
}
void ParsedFunction::AllocateVariables() {
ASSERT(!function().IsIrregexpFunction());
LocalScope* scope = node_sequence()->scope();
const intptr_t num_fixed_params = function().num_fixed_parameters();
const intptr_t num_opt_params = function().NumOptionalParameters();
const intptr_t num_params = num_fixed_params + num_opt_params;
// Compute start indices to parameters and locals, and the number of
// parameters to copy.
if (num_opt_params == 0) {
// Parameter i will be at fp[kParamEndSlotFromFp + num_params - i] and
// local variable j will be at fp[kFirstLocalSlotFromFp - j].
first_parameter_index_ = kParamEndSlotFromFp + num_params;
first_stack_local_index_ = kFirstLocalSlotFromFp;
num_copied_params_ = 0;
} else {
// Parameter i will be at fp[kFirstLocalSlotFromFp - i] and local variable
// j will be at fp[kFirstLocalSlotFromFp - num_params - j].
first_parameter_index_ = kFirstLocalSlotFromFp;
first_stack_local_index_ = first_parameter_index_ - num_params;
num_copied_params_ = num_params;
}
// Allocate parameters and local variables, either in the local frame or
// in the context(s).
bool found_captured_variables = false;
int next_free_frame_index =
scope->AllocateVariables(first_parameter_index_,
num_params,
first_stack_local_index_,
NULL,
&found_captured_variables);
// Frame indices are relative to the frame pointer and are decreasing.
ASSERT(next_free_frame_index <= first_stack_local_index_);
num_stack_locals_ = first_stack_local_index_ - next_free_frame_index;
}
struct CatchParamDesc {
CatchParamDesc()
: token_pos(0), type(NULL), name(NULL), var(NULL) { }
intptr_t token_pos;
const AbstractType* type;
const String* name;
LocalVariable* var;
};
void ParsedFunction::AllocateIrregexpVariables(intptr_t num_stack_locals) {
ASSERT(function().IsIrregexpFunction());
ASSERT(function().NumOptionalParameters() == 0);
const intptr_t num_params = function().num_fixed_parameters();
ASSERT(num_params == RegExpMacroAssembler::kParamCount);
// Compute start indices to parameters and locals, and the number of
// parameters to copy.
// Parameter i will be at fp[kParamEndSlotFromFp + num_params - i] and
// local variable j will be at fp[kFirstLocalSlotFromFp - j].
first_parameter_index_ = kParamEndSlotFromFp + num_params;
first_stack_local_index_ = kFirstLocalSlotFromFp;
num_copied_params_ = 0;
// Frame indices are relative to the frame pointer and are decreasing.
num_stack_locals_ = num_stack_locals;
}
struct Parser::Block : public ZoneAllocated {
Block(Block* outer_block, LocalScope* local_scope, SequenceNode* seq)
: parent(outer_block), scope(local_scope), statements(seq) {
ASSERT(scope != NULL);
ASSERT(statements != NULL);
}
Block* parent; // Enclosing block, or NULL if outermost.
LocalScope* scope;
SequenceNode* statements;
};
// Class which describes an inlined finally block which is used to generate
// inlined code for the finally blocks when there is an exit from a try
// block using 'return', 'break' or 'continue'.
class Parser::TryStack : public ZoneAllocated {
public:
TryStack(Block* try_block, TryStack* outer_try, intptr_t try_index)
: try_block_(try_block),
inlined_finally_nodes_(),
outer_try_(outer_try),
try_index_(try_index),
inside_catch_(false),
inside_finally_(false) { }
TryStack* outer_try() const { return outer_try_; }
Block* try_block() const { return try_block_; }
intptr_t try_index() const { return try_index_; }
bool inside_catch() const { return inside_catch_; }
void enter_catch() { inside_catch_ = true; }
bool inside_finally() const { return inside_finally_; }
void enter_finally() { inside_finally_ = true; }
void exit_finally() { inside_finally_ = false; }
void AddNodeForFinallyInlining(AstNode* node);
AstNode* GetNodeToInlineFinally(int index) {
if (0 <= index && index < inlined_finally_nodes_.length()) {
return inlined_finally_nodes_[index];
}
return NULL;
}
private:
Block* try_block_;
GrowableArray<AstNode*> inlined_finally_nodes_;
TryStack* outer_try_;
const intptr_t try_index_;
bool inside_catch_; // True when parsing a catch clause of this try.
bool inside_finally_; // True when parsing a finally clause of an inner try
// of this try.
DISALLOW_COPY_AND_ASSIGN(TryStack);
};
void Parser::TryStack::AddNodeForFinallyInlining(AstNode* node) {
inlined_finally_nodes_.Add(node);
}
// For parsing a compilation unit.
Parser::Parser(const Script& script, const Library& library, intptr_t token_pos)
: isolate_(Thread::Current()->isolate()),
thread_(Thread::Current()),
script_(Script::Handle(zone(), script.raw())),
tokens_iterator_(TokenStream::Handle(zone(), script.tokens()),
token_pos),
token_kind_(Token::kILLEGAL),
current_block_(NULL),
is_top_level_(false),
await_is_keyword_(false),
current_member_(NULL),
allow_function_literals_(true),
parsed_function_(NULL),
innermost_function_(Function::Handle(zone())),
literal_token_(LiteralToken::Handle(zone())),
current_class_(Class::Handle(zone())),
library_(Library::Handle(zone(), library.raw())),
try_stack_(NULL),
last_used_try_index_(0),
unregister_pending_function_(false),
async_temp_scope_(NULL),
trace_indent_(0) {
ASSERT(tokens_iterator_.IsValid());
ASSERT(!library.IsNull());
}
// For parsing a function.
Parser::Parser(const Script& script,
ParsedFunction* parsed_function,
intptr_t token_position)
: isolate_(Thread::Current()->isolate()),
thread_(Thread::Current()),
script_(Script::Handle(zone(), script.raw())),
tokens_iterator_(TokenStream::Handle(zone(), script.tokens()),
token_position),
token_kind_(Token::kILLEGAL),
current_block_(NULL),
is_top_level_(false),
await_is_keyword_(false),
current_member_(NULL),
allow_function_literals_(true),
parsed_function_(parsed_function),
innermost_function_(Function::Handle(zone(),
parsed_function->function().raw())),
literal_token_(LiteralToken::Handle(zone())),
current_class_(Class::Handle(zone(),
parsed_function->function().Owner())),
library_(Library::Handle(zone(), Class::Handle(
zone(),
parsed_function->function().origin()).library())),
try_stack_(NULL),
last_used_try_index_(0),
unregister_pending_function_(false),
async_temp_scope_(NULL),
trace_indent_(0) {
ASSERT(tokens_iterator_.IsValid());
ASSERT(!current_function().IsNull());
EnsureExpressionTemp();
}
Parser::~Parser() {
if (unregister_pending_function_) {
const GrowableObjectArray& pending_functions =
GrowableObjectArray::Handle(I->object_store()->pending_functions());
ASSERT(pending_functions.Length() > 0);
ASSERT(pending_functions.At(pending_functions.Length()-1) ==
current_function().raw());
pending_functions.RemoveLast();
}
}
// Each try in this function gets its own try index.
// See definition of RawPcDescriptors::PcDescriptor.
int16_t Parser::AllocateTryIndex() {
if (!Utils::IsInt(16, last_used_try_index_ - 1)) {
ReportError("too many nested try statements");
}
return last_used_try_index_++;
}
void Parser::SetScript(const Script& script, intptr_t token_pos) {
script_ = script.raw();
tokens_iterator_.SetStream(
TokenStream::Handle(Z, script.tokens()), token_pos);
token_kind_ = Token::kILLEGAL;
}
bool Parser::SetAllowFunctionLiterals(bool value) {
bool current_value = allow_function_literals_;
allow_function_literals_ = value;
return current_value;
}
const Function& Parser::current_function() const {
ASSERT(parsed_function() != NULL);
return parsed_function()->function();
}
const Function& Parser::innermost_function() const {
return innermost_function_;
}
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) {
tokens_iterator_.SetCurrentPosition(position);
token_kind_ = Token::kILLEGAL;
}
void Parser::ParseCompilationUnit(const Library& library,
const Script& script) {
Thread* thread = Thread::Current();
ASSERT(thread->long_jump_base()->IsSafeToJump());
CSTAT_TIMER_SCOPE(thread, parser_timer);
VMTagScope tagScope(thread, VMTag::kCompileTopLevelTagId);
Parser parser(script, library, 0);
parser.ParseTopLevel();
}
void Parser::ComputeCurrentToken() {
ASSERT(token_kind_ == Token::kILLEGAL);
token_kind_ = tokens_iterator_.CurrentTokenKind();
if (token_kind_ == Token::kERROR) {
ReportError(TokenPos(), "%s", CurrentLiteral()->ToCString());
}
}
Token::Kind Parser::LookaheadToken(int num_tokens) {
return tokens_iterator_.LookaheadTokenKind(num_tokens);
}
String* Parser::CurrentLiteral() const {
String& result =
String::ZoneHandle(Z, tokens_iterator_.CurrentLiteral());
return &result;
}
RawDouble* Parser::CurrentDoubleLiteral() const {
literal_token_ ^= tokens_iterator_.CurrentToken();
ASSERT(literal_token_.kind() == Token::kDOUBLE);
return Double::RawCast(literal_token_.value());
}
RawInteger* Parser::CurrentIntegerLiteral() const {
literal_token_ ^= tokens_iterator_.CurrentToken();
ASSERT(literal_token_.kind() == Token::kINTEGER);
RawInteger* ri = Integer::RawCast(literal_token_.value());
if (FLAG_throw_on_javascript_int_overflow) {
const Integer& i = Integer::Handle(Z, ri);
if (i.CheckJavascriptIntegerOverflow()) {
ReportError(TokenPos(),
"Integer literal does not fit in a Javascript integer: %s.",
i.ToCString());
}
}
return ri;
}
struct ParamDesc {
ParamDesc()
: type(NULL),
name_pos(0),
name(NULL),
default_value(NULL),
metadata(NULL),
var(NULL),
is_final(false),
is_field_initializer(false),
has_explicit_type(false) { }
const AbstractType* type;
intptr_t name_pos;
const String* name;
const Instance* default_value; // NULL if not an optional parameter.
const Object* metadata; // NULL if no metadata or metadata not evaluated.
LocalVariable* var; // Scope variable allocated for this parameter.
bool is_final;
bool is_field_initializer;
bool has_explicit_type;
};
struct ParamList {
ParamList() {
Clear();
}
void Clear() {
num_fixed_parameters = 0;
num_optional_parameters = 0;
has_optional_positional_parameters = false;
has_optional_named_parameters = false;
has_explicit_default_values = false;
has_field_initializer = false;
implicitly_final = false;
skipped = false;
this->parameters = new ZoneGrowableArray<ParamDesc>();
}
void AddFinalParameter(intptr_t name_pos,
const String* name,
const AbstractType* type) {
this->num_fixed_parameters++;
ParamDesc param;
param.name_pos = name_pos;
param.name = name;
param.is_final = true;
param.type = type;
this->parameters->Add(param);
}
void AddReceiver(const AbstractType* receiver_type, intptr_t token_pos) {
ASSERT(this->parameters->is_empty());
AddFinalParameter(token_pos, &Symbols::This(), receiver_type);
}
void EraseParameterTypes() {
const Type& dynamic_type = Type::ZoneHandle(Type::DynamicType());
const int num_parameters = parameters->length();
for (int i = 0; i < num_parameters; i++) {
(*parameters)[i].type = &dynamic_type;
}
}
// Make the parameter variables visible/invisible.
// Field initializer parameters are always invisible.
void SetInvisible(bool invisible) {
const intptr_t num_params = parameters->length();
for (int i = 0; i < num_params; i++) {
ParamDesc& param = (*parameters)[i];
ASSERT(param.var != NULL);
if (!param.is_field_initializer) {
param.var->set_invisible(invisible);
}
}
}
void SetImplicitlyFinal() {
implicitly_final = true;
}
int num_fixed_parameters;
int num_optional_parameters;
bool has_optional_positional_parameters;
bool has_optional_named_parameters;
bool has_explicit_default_values;
bool has_field_initializer;
bool implicitly_final;
bool skipped;
ZoneGrowableArray<ParamDesc>* parameters;
};
struct MemberDesc {
MemberDesc() {
Clear();
}
void Clear() {
has_abstract = false;
has_external = false;
has_final = false;
has_const = false;
has_static = false;
has_var = false;
has_factory = false;
has_operator = false;
has_native = false;
metadata_pos = -1;
operator_token = Token::kILLEGAL;
type = NULL;
name_pos = 0;
name = NULL;
redirect_name = NULL;
dict_name = NULL;
params.Clear();
kind = RawFunction::kRegularFunction;
field_ = NULL;
}
bool IsConstructor() const {
return (kind == RawFunction::kConstructor) && !has_static;
}
bool IsFactory() const {
return (kind == RawFunction::kConstructor) && has_static;
}
bool IsFactoryOrConstructor() const {
return (kind == RawFunction::kConstructor);
}
bool IsGetter() const {
return kind == RawFunction::kGetterFunction;
}
bool IsSetter() const {
return kind == RawFunction::kSetterFunction;
}
const char* ToCString() const {
if (field_ != NULL) {
return "field";
} else if (IsConstructor()) {
return "constructor";
} else if (IsFactory()) {
return "factory";
} else if (IsGetter()) {
return "getter";
} else if (IsSetter()) {
return "setter";
}
return "method";
}
String* DictName() const {
return (dict_name != NULL) ? dict_name : name;
}
bool has_abstract;
bool has_external;
bool has_final;
bool has_const;
bool has_static;
bool has_var;
bool has_factory;
bool has_operator;
bool has_native;
intptr_t metadata_pos;
Token::Kind operator_token;
const AbstractType* type;
intptr_t name_pos;
intptr_t decl_begin_pos;
String* name;
// For constructors: NULL or name of redirected to constructor.
String* redirect_name;
// dict_name is the name used for the class namespace, if it
// differs from 'name'.
// For constructors: NULL for unnamed constructor,
// identifier after classname for named constructors.
// For getters and setters: unmangled name.
String* dict_name;
ParamList params;
RawFunction::Kind kind;
// NULL for functions, field object for static or instance fields.
Field* field_;
};
class ClassDesc : public ValueObject {
public:
ClassDesc(Zone* zone,
const Class& cls,
const String& cls_name,
bool is_interface,
intptr_t token_pos)
: zone_(zone),
clazz_(cls),
class_name_(cls_name),
token_pos_(token_pos),
functions_(zone, 4),
fields_(zone, 4) {
}
void AddFunction(const Function& function) {
functions_.Add(&Function::ZoneHandle(zone_, function.raw()));
}
const GrowableArray<const Function*>& functions() const {
return functions_;
}
void AddField(const Field& field) {
fields_.Add(&Field::ZoneHandle(zone_, field.raw()));
}
const GrowableArray<const Field*>& fields() const {
return fields_;
}
const Class& clazz() const {
return clazz_;
}
const String& class_name() const {
return class_name_;
}
bool has_constructor() const {
for (int i = 0; i < functions_.length(); i++) {
const Function* func = functions_.At(i);
if (func->kind() == RawFunction::kConstructor) {
return true;
}
}
return false;
}
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;
}
RawArray* MakeFunctionsArray() {
const intptr_t len = functions_.length();
const Array& res = Array::Handle(zone_, Array::New(len, Heap::kOld));
for (intptr_t i = 0; i < len; i++) {
res.SetAt(i, *functions_[i]);
}
return res.raw();
}
private:
Zone* zone_;
const Class& clazz_;
const String& class_name_;
intptr_t token_pos_; // Token index of "class" keyword.
GrowableArray<const Function*> functions_;
GrowableArray<const Field*> fields_;
GrowableArray<MemberDesc> members_;
};
class TopLevel : public ValueObject {
public:
explicit TopLevel(Zone* zone) :
zone_(zone),
fields_(zone, 4),
functions_(zone, 4) { }
void AddField(const Field& field) {
fields_.Add(&Field::ZoneHandle(zone_, field.raw()));
}
void AddFunction(const Function& function) {
functions_.Add(&Function::ZoneHandle(zone_, function.raw()));
}
const GrowableArray<const Field*>& fields() const {
return fields_;
}
const GrowableArray<const Function*>& functions() const {
return functions_;
}
private:
Zone* zone_;
GrowableArray<const Field*> fields_;
GrowableArray<const Function*> functions_;
};
void Parser::ParseClass(const Class& cls) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
const int64_t num_tokes_before = STAT_VALUE(thread, num_tokens_consumed);
if (!cls.is_synthesized_class()) {
ASSERT(thread->long_jump_base()->IsSafeToJump());
CSTAT_TIMER_SCOPE(thread, parser_timer);
const Script& script = Script::Handle(zone, cls.script());
const Library& lib = Library::Handle(zone, cls.library());
Parser parser(script, lib, cls.token_pos());
parser.ParseClassDefinition(cls);
} else if (cls.is_enum_class()) {
ASSERT(thread->long_jump_base()->IsSafeToJump());
CSTAT_TIMER_SCOPE(thread, parser_timer);
const Script& script = Script::Handle(zone, cls.script());
const Library& lib = Library::Handle(zone, cls.library());
Parser parser(script, lib, cls.token_pos());
parser.ParseEnumDefinition(cls);
}
const int64_t num_tokes_after = STAT_VALUE(thread, num_tokens_consumed);
INC_STAT(thread, num_class_tokens, num_tokes_after - num_tokes_before);
}
RawObject* Parser::ParseFunctionParameters(const Function& func) {
ASSERT(!func.IsNull());
Thread* thread = Thread::Current();
Isolate* isolate = thread->isolate();
StackZone stack_zone(thread);
Zone* zone = stack_zone.GetZone();
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
const Script& script = Script::Handle(zone, func.script());
const Class& owner = Class::Handle(zone, func.Owner());
ASSERT(!owner.IsNull());
ParsedFunction* parsed_function = new ParsedFunction(
thread, Function::ZoneHandle(zone, func.raw()));
Parser parser(script, parsed_function, func.token_pos());
parser.SkipFunctionPreamble();
ParamList params;
parser.ParseFormalParameterList(true, true, &params);
ParamDesc* param = params.parameters->data();
const int param_cnt = params.num_fixed_parameters +
params.num_optional_parameters;
const Array& param_descriptor =
Array::Handle(Array::New(param_cnt * kParameterEntrySize));
for (int i = 0, j = 0; i < param_cnt; i++, j += kParameterEntrySize) {
param_descriptor.SetAt(j + kParameterIsFinalOffset,
param[i].is_final ? Bool::True() : Bool::False());
param_descriptor.SetAt(j + kParameterDefaultValueOffset,
(param[i].default_value == NULL) ? Object::null_instance() :
*(param[i].default_value));
const Object* metadata = param[i].metadata;
if ((metadata != NULL) && (*metadata).IsError()) {
return metadata->raw(); // Error evaluating the metadata.
}
param_descriptor.SetAt(j + kParameterMetadataOffset,
(param[i].metadata == NULL) ? Object::null_instance() :
*(param[i].metadata));
}
return param_descriptor.raw();
} else {
Error& error = Error::Handle();
error = isolate->object_store()->sticky_error();
isolate->object_store()->clear_sticky_error();
return error.raw();
}
UNREACHABLE();
return Object::null();
}
bool Parser::ParseFormalParameters(const Function& func, ParamList* params) {
ASSERT(!func.IsNull());
// This is currently only used for constructors. To handle all kinds
// of functions, special cases for getters and possibly other kinds
// need to be added.
ASSERT(func.kind() == RawFunction::kConstructor);
ASSERT(!func.IsRedirectingFactory());
// Implicit constructors have no source, no user-defined formal parameters.
if (func.IsImplicitConstructor()) {
return true;
}
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
const Script& script = Script::Handle(func.script());
const Class& owner = Class::Handle(func.Owner());
ASSERT(!owner.IsNull());
ParsedFunction* parsed_function =
new ParsedFunction(Thread::Current(), Function::ZoneHandle(func.raw()));
Parser parser(script, parsed_function, func.token_pos());
parser.SkipFunctionPreamble();
parser.ParseFormalParameterList(true, true, params);
return true;
} else {
Thread::Current()->isolate()->object_store()->clear_sticky_error();
params->Clear();
return false;
}
UNREACHABLE();
return false;
}
void Parser::ParseFunction(ParsedFunction* parsed_function) {
Thread* thread = parsed_function->thread();
ASSERT(thread == Thread::Current());
Zone* zone = thread->zone();
CSTAT_TIMER_SCOPE(thread, parser_timer);
INC_STAT(thread, num_functions_parsed, 1);
VMTagScope tagScope(thread, VMTag::kCompileParseFunctionTagId,
FLAG_profile_vm);
ASSERT(thread->long_jump_base()->IsSafeToJump());
ASSERT(parsed_function != NULL);
const Function& func = parsed_function->function();
const Script& script = Script::Handle(zone, func.script());
Parser parser(script, parsed_function, func.token_pos());
SequenceNode* node_sequence = NULL;
switch (func.kind()) {
case RawFunction::kClosureFunction:
if (func.IsImplicitClosureFunction()) {
node_sequence =
parser.ParseImplicitClosure(func);
break;
}
if (func.IsConstructorClosureFunction()) {
node_sequence =
parser.ParseConstructorClosure(func);
break;
}
// Fall-through: Handle non-implicit closures.
case RawFunction::kRegularFunction:
case RawFunction::kGetterFunction:
case RawFunction::kSetterFunction:
case RawFunction::kConstructor:
// The call to a redirecting factory is redirected.
ASSERT(!func.IsRedirectingFactory());
if (!func.IsImplicitConstructor()) {
parser.SkipFunctionPreamble();
}
node_sequence = parser.ParseFunc(func);
break;
case RawFunction::kImplicitGetter:
ASSERT(!func.is_static());
node_sequence = parser.ParseInstanceGetter(func);
break;
case RawFunction::kImplicitSetter:
ASSERT(!func.is_static());
node_sequence = parser.ParseInstanceSetter(func);
break;
case RawFunction::kImplicitStaticFinalGetter:
node_sequence = parser.ParseStaticFinalGetter(func);
INC_STAT(thread, num_implicit_final_getters, 1);
break;
case RawFunction::kMethodExtractor:
node_sequence = parser.ParseMethodExtractor(func);
INC_STAT(thread, num_method_extractors, 1);
break;
case RawFunction::kNoSuchMethodDispatcher:
node_sequence =
parser.ParseNoSuchMethodDispatcher(func);
break;
case RawFunction::kInvokeFieldDispatcher:
node_sequence =
parser.ParseInvokeFieldDispatcher(func);
break;
case RawFunction::kIrregexpFunction:
UNREACHABLE(); // Irregexp functions have their own parser.
default:
UNREACHABLE();
}
if (parsed_function->has_expression_temp_var()) {
node_sequence->scope()->AddVariable(parsed_function->expression_temp_var());
}
node_sequence->scope()->AddVariable(
parsed_function->current_context_var());
if (parsed_function->has_finally_return_temp_var()) {
node_sequence->scope()->AddVariable(
parsed_function->finally_return_temp_var());
}
parsed_function->SetNodeSequence(node_sequence);
// The instantiator may be required at run time for generic type checks or
// allocation of generic types.
if (parser.IsInstantiatorRequired()) {
// In the case of a local function, only set the instantiator if the
// receiver (or type arguments parameter of a factory) was captured.
LocalVariable* instantiator = NULL;
const bool kTestOnly = true;
if (parser.current_function().IsInFactoryScope()) {
instantiator = parser.LookupTypeArgumentsParameter(node_sequence->scope(),
kTestOnly);
} else {
instantiator = parser.LookupReceiver(node_sequence->scope(), kTestOnly);
}
if (!parser.current_function().IsLocalFunction() ||
((instantiator != NULL) && instantiator->is_captured())) {
parsed_function->set_instantiator(instantiator);
}
}
}
RawObject* Parser::ParseMetadata(const Class& cls, intptr_t token_pos) {
Thread* thread = Thread::Current();
Isolate* isolate = thread->isolate();
StackZone stack_zone(thread);
Zone* zone = stack_zone.GetZone();
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
const Script& script = Script::Handle(zone, cls.script());
// Parsing metadata can involve following paths in the parser that are
// normally used for expressions and assume current_function is non-null,
// so we create a fake function to use as the current_function rather than
// scattering special cases throughout the parser.
const Function& fake_function = Function::ZoneHandle(zone, Function::New(
Symbols::At(),
RawFunction::kRegularFunction,
true, // is_static
false, // is_const
false, // is_abstract
false, // is_external
false, // is_native
cls,
token_pos));
fake_function.set_is_debuggable(false);
ParsedFunction* parsed_function =
new ParsedFunction(thread, fake_function);
Parser parser(script, parsed_function, token_pos);
parser.set_current_class(cls);
RawObject* metadata = parser.EvaluateMetadata();
return metadata;
} else {
Error& error = Error::Handle(zone);
error = isolate->object_store()->sticky_error();
isolate->object_store()->clear_sticky_error();
return error.raw();
}
UNREACHABLE();
return Object::null();
}
RawArray* Parser::EvaluateMetadata() {
CheckToken(Token::kAT, "Metadata character '@' expected");
GrowableObjectArray& meta_values =
GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld));
while (CurrentToken() == Token::kAT) {
ConsumeToken();
intptr_t expr_pos = TokenPos();
if (!IsIdentifier()) {
ExpectIdentifier("identifier expected");
}
// Reject expressions with deferred library prefix eagerly.
Object& obj =
Object::Handle(Z, library_.LookupLocalObject(*CurrentLiteral()));
if (!obj.IsNull() && obj.IsLibraryPrefix()) {
if (LibraryPrefix::Cast(obj).is_deferred_load()) {
ReportError("Metadata must be compile-time constant");
}
}
AstNode* expr = NULL;
if ((LookaheadToken(1) == Token::kLPAREN) ||
((LookaheadToken(1) == Token::kPERIOD) &&
(LookaheadToken(3) == Token::kLPAREN)) ||
((LookaheadToken(1) == Token::kPERIOD) &&
(LookaheadToken(3) == Token::kPERIOD) &&
(LookaheadToken(5) == Token::kLPAREN))) {
expr = ParseNewOperator(Token::kCONST);
} else {
// Can be x, C.x, or L.C.x.
expr = ParsePrimary(); // Consumes x, C or L.C.
Class& cls = Class::Handle(Z);
if (expr->IsPrimaryNode()) {
PrimaryNode* primary_node = expr->AsPrimaryNode();
if (primary_node->primary().IsClass()) {
// If the primary node referred to a class we are loading a
// qualified static field.
cls ^= primary_node->primary().raw();
} else {
ReportError(expr_pos,
"Metadata expressions must refer to a const field "
"or constructor");
}
}
if (CurrentToken() == Token::kPERIOD) {
// C.x or L.C.X.
if (cls.IsNull()) {
ReportError(expr_pos,
"Metadata expressions must refer to a const field "
"or constructor");
}
ConsumeToken();
const intptr_t ident_pos = TokenPos();
String* ident = ExpectIdentifier("identifier expected");
const Field& field = Field::Handle(Z, cls.LookupStaticField(*ident));
if (field.IsNull()) {
ReportError(ident_pos,
"Class '%s' has no field '%s'",
cls.ToCString(),
ident->ToCString());
}
if (!field.is_const()) {
ReportError(ident_pos,
"Field '%s' of class '%s' is not const",
ident->ToCString(),
cls.ToCString());
}
expr = GenerateStaticFieldLookup(field, ident_pos);
}
}
if (expr->EvalConstExpr() == NULL) {
ReportError(expr_pos, "expression must be a compile-time constant");
}
const Instance& val = EvaluateConstExpr(expr_pos, expr);
meta_values.Add(val, Heap::kOld);
}
return Array::MakeArray(meta_values);
}
SequenceNode* Parser::ParseStaticInitializer() {
ExpectIdentifier("field name expected");
CheckToken(Token::kASSIGN, "field initialier expected");
ConsumeToken();
OpenFunctionBlock(parsed_function()->function());
intptr_t expr_pos = TokenPos();
AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades);
ReturnNode* ret = new(Z) ReturnNode(expr_pos, expr);
current_block_->statements->Add(ret);
return CloseBlock();
}
ParsedFunction* Parser::ParseStaticFieldInitializer(const Field& field) {
ASSERT(field.is_static());
Thread* thread = Thread::Current();
// TODO(koda): Should there be a StackZone here?
Zone* zone = thread->zone();
const Class& script_cls = Class::Handle(zone, field.origin());
const Script& script = Script::Handle(zone, script_cls.script());
const String& field_name = String::Handle(zone, field.name());
String& init_name = String::Handle(zone,
Symbols::FromConcat(Symbols::InitPrefix(), field_name));
Object& initializer_owner = Object::Handle(field.owner());
if (field.owner() != field.origin()) {
initializer_owner =
PatchClass::New(Class::Handle(field.owner()), script_cls);
}
const Function& initializer = Function::ZoneHandle(zone,
Function::New(init_name,
RawFunction::kImplicitStaticFinalGetter,
true, // static
false, // !const
false, // !abstract
false, // !external
false, // !native
initializer_owner,
field.token_pos()));
initializer.set_result_type(AbstractType::Handle(zone, field.type()));
// Static initializer functions are hidden from the user.
// Since they are only executed once, we avoid optimizing
// and inlining them. After the field is initialized, the
// compiler can eliminate the call to the static initializer.
initializer.set_is_reflectable(false);
initializer.set_is_debuggable(false);
initializer.SetIsOptimizable(false);
initializer.set_is_inlinable(false);
ParsedFunction* parsed_function = new ParsedFunction(thread, initializer);
Parser parser(script, parsed_function, field.token_pos());
SequenceNode* body = parser.ParseStaticInitializer();
parsed_function->SetNodeSequence(body);
if (parsed_function->has_expression_temp_var()) {
body->scope()->AddVariable(parsed_function->expression_temp_var());
}
body->scope()->AddVariable(parsed_function->current_context_var());
if (parsed_function->has_finally_return_temp_var()) {
body->scope()->AddVariable(parsed_function->finally_return_temp_var());
}
// The instantiator is not required in a static expression.
ASSERT(!parser.IsInstantiatorRequired());
return parsed_function;
}
SequenceNode* Parser::ParseStaticFinalGetter(const Function& func) {
TRACE_PARSER("ParseStaticFinalGetter");
ParamList params;
ASSERT(func.num_fixed_parameters() == 0); // static.
ASSERT(!func.HasOptionalParameters());
ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
intptr_t ident_pos = TokenPos();
const String& field_name = *ExpectIdentifier("field name expected");
const Class& field_class = Class::Handle(Z, func.Owner());
const Field& field =
Field::ZoneHandle(Z, field_class.LookupStaticField(field_name));
ASSERT(!field.IsNull());
// Static final fields must have an initializer.
ExpectToken(Token::kASSIGN);
const intptr_t expr_pos = TokenPos();
if (field.is_const()) {
// We don't want to use ParseConstExpr() here because we don't want
// the constant folding code to create, compile and execute a code
// fragment to evaluate the expression. Instead, we just make sure
// the static const field initializer is a constant expression and
// leave the evaluation to the getter function.
AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades);
// This getter will only be called once at compile time.
if (expr->EvalConstExpr() == NULL) {
ReportError(expr_pos, "initializer is not a valid compile-time constant");
}
ReturnNode* return_node = new ReturnNode(ident_pos, expr);
current_block_->statements->Add(return_node);
} else {
// This getter may be called each time the static field is accessed.
// Call runtime support to parse and evaluate the initializer expression.
// The runtime function will detect circular dependencies in expressions
// and handle errors while evaluating the expression.
current_block_->statements->Add(
new (Z) InitStaticFieldNode(ident_pos, field));
ReturnNode* return_node =
new ReturnNode(ident_pos,
new LoadStaticFieldNode(ident_pos, field));
current_block_->statements->Add(return_node);
}
return CloseBlock();
}
// Create AstNodes for an implicit instance getter method:
// LoadLocalNode 0 ('this');
// LoadInstanceFieldNode (field_name);
// ReturnNode (field's value);
SequenceNode* Parser::ParseInstanceGetter(const Function& func) {
TRACE_PARSER("ParseInstanceGetter");
ParamList params;
// func.token_pos() points to the name of the field.
const intptr_t ident_pos = func.token_pos();
ASSERT(current_class().raw() == func.Owner());
params.AddReceiver(ReceiverType(current_class()), ident_pos);
ASSERT(func.num_fixed_parameters() == 1); // receiver.
ASSERT(!func.HasOptionalParameters());
ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
// Receiver is local 0.
LocalVariable* receiver = current_block_->scope->VariableAt(0);
LoadLocalNode* load_receiver = new LoadLocalNode(ident_pos, receiver);
String& field_name = String::Handle(Z, func.name());
field_name = Field::NameFromGetter(field_name);
const Class& field_class = Class::Handle(Z, func.Owner());
const Field& field =
Field::ZoneHandle(Z, field_class.LookupInstanceField(field_name));
ASSERT(!field.IsNull());
LoadInstanceFieldNode* load_field =
new LoadInstanceFieldNode(ident_pos, load_receiver, field);
ReturnNode* return_node = new ReturnNode(Scanner::kNoSourcePos, load_field);
current_block_->statements->Add(return_node);
return CloseBlock();
}
// Create AstNodes for an implicit instance setter method:
// LoadLocalNode 0 ('this')
// LoadLocalNode 1 ('value')
// SetInstanceField (field_name);
// ReturnNode (void);
SequenceNode* Parser::ParseInstanceSetter(const Function& func) {
TRACE_PARSER("ParseInstanceSetter");
// func.token_pos() points to the name of the field.
const intptr_t ident_pos = func.token_pos();
const String& field_name = *CurrentLiteral();
const Class& field_class = Class::ZoneHandle(Z, func.Owner());
const Field& field =
Field::ZoneHandle(Z, field_class.LookupInstanceField(field_name));
const AbstractType& field_type = AbstractType::ZoneHandle(Z, field.type());
ParamList params;
ASSERT(current_class().raw() == func.Owner());
params.AddReceiver(ReceiverType(current_class()), ident_pos);
params.AddFinalParameter(ident_pos,
&Symbols::Value(),
&field_type);
ASSERT(func.num_fixed_parameters() == 2); // receiver, value.
ASSERT(!func.HasOptionalParameters());
ASSERT(AbstractType::Handle(Z, func.result_type()).IsVoidType());
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
LoadLocalNode* receiver =
new LoadLocalNode(ident_pos, current_block_->scope->VariableAt(0));
LoadLocalNode* value =
new LoadLocalNode(ident_pos, current_block_->scope->VariableAt(1));
EnsureExpressionTemp();
StoreInstanceFieldNode* store_field =
new StoreInstanceFieldNode(ident_pos, receiver, field, value);
current_block_->statements->Add(store_field);
current_block_->statements->Add(new ReturnNode(Scanner::kNoSourcePos));
return CloseBlock();
}
SequenceNode* Parser::ParseConstructorClosure(const Function& func) {
TRACE_PARSER("ParseConstructorClosure");
const intptr_t token_pos = func.token_pos();
Function& constructor = Function::ZoneHandle(Z);
TypeArguments& type_args = TypeArguments::ZoneHandle(Z);
ParseConstructorClosurization(&constructor, &type_args);
ASSERT(!constructor.IsNull());
ParamList params;
// The first parameter of the closure function is the implicit closure
// argument.
params.AddFinalParameter(token_pos,
&Symbols::ClosureParameter(),
&Type::ZoneHandle(Z, Type::DynamicType()));
bool params_ok = ParseFormalParameters(constructor, &params);
USE(params_ok);
ASSERT(params_ok);
// Per language spec, the type of the closure parameters is dynamic.
// Replace the types parsed from the constructor.
params.EraseParameterTypes();
SetupDefaultsForOptionalParams(params);
ASSERT(func.num_fixed_parameters() == params.num_fixed_parameters);
ASSERT(func.NumOptionalParameters() == params.num_optional_parameters);
OpenFunctionBlock(func);
LocalScope* scope = current_block_->scope;
AddFormalParamsToScope(&params, scope);
ArgumentListNode* ctor_args = new ArgumentListNode(token_pos);
// Skip implicit closure parameter at 0.
for (intptr_t i = 1; i < func.NumParameters(); i++) {
ctor_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
}
if (func.HasOptionalNamedParameters()) {
const Array& arg_names =
Array::ZoneHandle(Array::New(func.NumOptionalParameters()));
for (intptr_t i = 0; i < arg_names.Length(); i++) {
intptr_t index = func.num_fixed_parameters() + i;
arg_names.SetAt(i, String::Handle(func.ParameterNameAt(index)));
}
ctor_args->set_names(arg_names);
}
AstNode* new_object =
CreateConstructorCallNode(token_pos, type_args, constructor, ctor_args);
ReturnNode* return_node = new ReturnNode(token_pos, new_object);
current_block_->statements->Add(return_node);
return CloseBlock();
}
SequenceNode* Parser::ParseImplicitClosure(const Function& func) {
TRACE_PARSER("ParseImplicitClosure");
intptr_t token_pos = func.token_pos();
OpenFunctionBlock(func);
ParamList params;
params.AddFinalParameter(
token_pos,
&Symbols::ClosureParameter(),
&Type::ZoneHandle(Type::DynamicType()));
const Function& parent = Function::ZoneHandle(func.parent_function());
if (parent.IsImplicitSetterFunction()) {
const intptr_t ident_pos = func.token_pos();
ASSERT(IsIdentifier());
const String& field_name = *CurrentLiteral();
const Class& field_class = Class::ZoneHandle(Z, parent.Owner());
const Field& field =
Field::ZoneHandle(Z, field_class.LookupInstanceField(field_name));
const AbstractType& field_type = AbstractType::ZoneHandle(Z, field.type());
params.AddFinalParameter(ident_pos,
&Symbols::Value(),
&field_type);
ASSERT(func.num_fixed_parameters() == 2); // closure, value.
} else if (!parent.IsGetterFunction() && !parent.IsImplicitGetterFunction()) {
const bool allow_explicit_default_values = true;
SkipFunctionPreamble();
ParseFormalParameterList(allow_explicit_default_values, false, &params);
SetupDefaultsForOptionalParams(params);
}
// Populate function scope with the formal parameters.
LocalScope* scope = current_block_->scope;
AddFormalParamsToScope(&params, scope);
ArgumentListNode* func_args = new ArgumentListNode(token_pos);
if (!func.is_static()) {
func_args->Add(LoadReceiver(token_pos));
}
// Skip implicit parameter at 0.
for (intptr_t i = 1; i < func.NumParameters(); ++i) {
func_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
}
if (func.HasOptionalNamedParameters()) {
const Array& arg_names =
Array::ZoneHandle(Array::New(func.NumOptionalParameters()));
for (intptr_t i = 0; i < arg_names.Length(); ++i) {
intptr_t index = func.num_fixed_parameters() + i;
arg_names.SetAt(i, String::Handle(func.ParameterNameAt(index)));
}
func_args->set_names(arg_names);
}
StaticCallNode* call = new StaticCallNode(token_pos, parent, func_args);
ReturnNode* return_node = new ReturnNode(token_pos, call);
current_block_->statements->Add(return_node);
return CloseBlock();
}
SequenceNode* Parser::ParseMethodExtractor(const Function& func) {
TRACE_PARSER("ParseMethodExtractor");
ASSERT(FLAG_lazy_dispatchers);
ParamList params;
const intptr_t ident_pos = func.token_pos();
ASSERT(func.token_pos() == 0);
ASSERT(current_class().raw() == func.Owner());
params.AddReceiver(ReceiverType(current_class()), ident_pos);
ASSERT(func.num_fixed_parameters() == 1); // Receiver.
ASSERT(!func.HasOptionalParameters());
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
// Receiver is local 0.
LocalVariable* receiver = current_block_->scope->VariableAt(0);
LoadLocalNode* load_receiver = new LoadLocalNode(ident_pos, receiver);
ClosureNode* closure = new ClosureNode(
ident_pos,
Function::ZoneHandle(Z, func.extracted_method_closure()),
load_receiver,
NULL);
ReturnNode* return_node = new ReturnNode(Scanner::kNoSourcePos, closure);
current_block_->statements->Add(return_node);
return CloseBlock();
}
void Parser::BuildDispatcherScope(const Function& func,
const ArgumentsDescriptor& desc) {
ParamList params;
// Receiver first.
intptr_t token_pos = func.token_pos();
params.AddReceiver(ReceiverType(current_class()), token_pos);
// Remaining positional parameters.
intptr_t i = 1;
for (; i < desc.PositionalCount(); ++i) {
ParamDesc p;
char name[64];
OS::SNPrint(name, 64, ":p%" Pd, i);
p.name = &String::ZoneHandle(Z, Symbols::New(name));
p.type = &Type::ZoneHandle(Z, Type::DynamicType());
params.parameters->Add(p);
params.num_fixed_parameters++;
}
ASSERT(desc.PositionalCount() == params.num_fixed_parameters);
// Named parameters.
for (; i < desc.Count(); ++i) {
ParamDesc p;
intptr_t index = i - desc.PositionalCount();
p.name = &String::ZoneHandle(Z, desc.NameAt(index));
p.type = &Type::ZoneHandle(Z, Type::DynamicType());
p.default_value = &Object::null_instance();
params.parameters->Add(p);
params.num_optional_parameters++;
params.has_optional_named_parameters = true;
}
ASSERT(desc.NamedCount() == params.num_optional_parameters);
SetupDefaultsForOptionalParams(params);
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
}
SequenceNode* Parser::ParseNoSuchMethodDispatcher(const Function& func) {
TRACE_PARSER("ParseNoSuchMethodDispatcher");
ASSERT(FLAG_lazy_dispatchers);
ASSERT(func.IsNoSuchMethodDispatcher());
intptr_t token_pos = func.token_pos();
ASSERT(func.token_pos() == 0);
ASSERT(current_class().raw() == func.Owner());
ArgumentsDescriptor desc(Array::Handle(Z, func.saved_args_desc()));
ASSERT(desc.Count() > 0);
// Set up scope for this function.
BuildDispatcherScope(func, desc);
// Receiver is local 0.
LocalScope* scope = current_block_->scope;
ArgumentListNode* func_args = new ArgumentListNode(token_pos);
for (intptr_t i = 0; i < desc.Count(); ++i) {
func_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
}
if (desc.NamedCount() > 0) {
const Array& arg_names =
Array::ZoneHandle(Z, Array::New(desc.NamedCount(), Heap::kOld));
for (intptr_t i = 0; i < arg_names.Length(); ++i) {
arg_names.SetAt(i, String::Handle(Z, desc.NameAt(i)));
}
func_args->set_names(arg_names);
}
const String& func_name = String::ZoneHandle(Z, func.name());
ArgumentListNode* arguments = BuildNoSuchMethodArguments(
token_pos, func_name, *func_args, NULL, false);
const intptr_t kNumArguments = 2; // Receiver, InvocationMirror.
ArgumentsDescriptor args_desc(
Array::Handle(Z, ArgumentsDescriptor::New(kNumArguments)));
Function& no_such_method = Function::ZoneHandle(Z,
Resolver::ResolveDynamicForReceiverClass(Class::Handle(Z, func.Owner()),
Symbols::NoSuchMethod(),
args_desc));
if (no_such_method.IsNull()) {
// If noSuchMethod(i) is not found, call Object:noSuchMethod.
no_such_method ^= Resolver::ResolveDynamicForReceiverClass(
Class::Handle(Z, I->object_store()->object_class()),
Symbols::NoSuchMethod(),
args_desc);
}
StaticCallNode* call =
new StaticCallNode(token_pos, no_such_method, arguments);
ReturnNode* return_node = new ReturnNode(token_pos, call);
current_block_->statements->Add(return_node);
return CloseBlock();
}
SequenceNode* Parser::ParseInvokeFieldDispatcher(const Function& func) {
TRACE_PARSER("ParseInvokeFieldDispatcher");
ASSERT(func.IsInvokeFieldDispatcher());
intptr_t token_pos = func.token_pos();
ASSERT(func.token_pos() == 0);
ASSERT(current_class().raw() == func.Owner());
const Array& args_desc = Array::Handle(Z, func.saved_args_desc());
ArgumentsDescriptor desc(args_desc);
ASSERT(desc.Count() > 0);
// Set up scope for this function.
BuildDispatcherScope(func, desc);
// Receiver is local 0.
LocalScope* scope = current_block_->scope;
ArgumentListNode* no_args = new ArgumentListNode(token_pos);
LoadLocalNode* receiver = new LoadLocalNode(token_pos, scope->VariableAt(0));
const Class& function_impl = Class::Handle(Type::Handle(
Isolate::Current()->object_store()->function_impl_type()).type_class());
const Class& owner = Class::Handle(Z, func.Owner());
ASSERT(!owner.IsNull());
const String& name = String::Handle(Z, func.name());
AstNode* function_object = NULL;
if (owner.raw() == function_impl.raw() && name.Equals(Symbols::Call())) {
function_object = receiver;
} else {
const String& getter_name = String::ZoneHandle(Z,
Symbols::New(String::Handle(Z, Field::GetterSymbol(name))));
function_object = new(Z) InstanceCallNode(
token_pos, receiver, getter_name, no_args);
}
// Pass arguments 1..n to the closure call.
ArgumentListNode* args = new(Z) ArgumentListNode(token_pos);
const Array& names = Array::Handle(
Z, Array::New(desc.NamedCount(), Heap::kOld));
// Positional parameters.
intptr_t i = 1;
for (; i < desc.PositionalCount(); ++i) {
args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
}
// Named parameters.
for (; i < desc.Count(); i++) {
args->Add(new(Z) LoadLocalNode(token_pos, scope->VariableAt(i)));
intptr_t index = i - desc.PositionalCount();
names.SetAt(index, String::Handle(Z, desc.NameAt(index)));
}
args->set_names(names);
AstNode* result = NULL;
if (owner.raw() == function_impl.raw() && name.Equals(Symbols::Call())) {
result = new ClosureCallNode(token_pos, function_object, args);
} else {
result = BuildClosureCall(token_pos, function_object, args);
}
ReturnNode* return_node = new ReturnNode(token_pos, result);
current_block_->statements->Add(return_node);
return CloseBlock();
}
AstNode* Parser::BuildClosureCall(intptr_t token_pos,
AstNode* closure,
ArgumentListNode* arguments) {
return new InstanceCallNode(token_pos,
closure,
Symbols::Call(),
arguments);
}
void Parser::SkipToMatching() {
Token::Kind opening_token = CurrentToken();
ASSERT((opening_token == Token::kLBRACE) ||
(opening_token == Token::kLPAREN));
GrowableArray<Token::Kind> token_stack(8);
GrowableArray<intptr_t> token_pos_stack(8);
// Adding the first opening brace here, because it will be consumed
// in the loop right away.
token_stack.Add(opening_token);
const intptr_t start_pos = TokenPos();
intptr_t opening_pos = start_pos;
token_pos_stack.Add(start_pos);
bool is_match = true;
bool unexpected_token_found = false;
Token::Kind token = opening_token;
intptr_t token_pos;
do {
ConsumeToken();
token = CurrentToken();
token_pos = TokenPos();
switch (token) {
case Token::kLBRACE:
case Token::kLPAREN:
case Token::kLBRACK:
token_stack.Add(token);
token_pos_stack.Add(token_pos);
break;
case Token::kRBRACE:
opening_token = token_stack.RemoveLast();
opening_pos = token_pos_stack.RemoveLast();
is_match = opening_token == Token::kLBRACE;
break;
case Token::kRPAREN:
opening_token = token_stack.RemoveLast();
opening_pos = token_pos_stack.RemoveLast();
is_match = opening_token == Token::kLPAREN;
break;
case Token::kRBRACK:
opening_token = token_stack.RemoveLast();
opening_pos = token_pos_stack.RemoveLast();
is_match = opening_token == Token::kLBRACK;
break;
case Token::kEOS:
unexpected_token_found = true;
break;
default:
// nothing.
break;
}
} while (!token_stack.is_empty() && is_match && !unexpected_token_found);
if (!is_match) {
const Error& error = Error::Handle(
LanguageError::NewFormatted(Error::Handle(),
script_, opening_pos, Report::kWarning, Heap::kNew,
"unbalanced '%s' opens here", Token::Str(opening_token)));
ReportErrors(error, script_, token_pos,
"unbalanced '%s'", Token::Str(token));
} else if (unexpected_token_found) {
ReportError(start_pos, "unterminated '%s'", Token::Str(opening_token));
}
}
void Parser::SkipBlock() {
ASSERT(CurrentToken() == Token::kLBRACE);
SkipToMatching();
}
// Skips tokens up to and including matching closing parenthesis.
void Parser::SkipToMatchingParenthesis() {
ASSERT(CurrentToken() == Token::kLPAREN);
SkipToMatching();
ASSERT(CurrentToken() == Token::kRPAREN);
ConsumeToken();
}
void Parser::ParseFormalParameter(bool allow_explicit_default_value,
bool evaluate_metadata,
ParamList* params) {
TRACE_PARSER("ParseFormalParameter");
ParamDesc parameter;
bool var_seen = false;
bool this_seen = false;
if (evaluate_metadata && (CurrentToken() == Token::kAT)) {
parameter.metadata = &Array::ZoneHandle(Z, EvaluateMetadata());
} else {
SkipMetadata();
}
if (CurrentToken() == Token::kFINAL) {
ConsumeToken();
parameter.is_final = true;
} else if (CurrentToken() == Token::kVAR) {
ConsumeToken();
var_seen = true;
// The parameter type is the 'dynamic' type.
// If this is an initializing formal, its type will be set to the type of
// the respective field when the constructor is fully parsed.
parameter.type = &Type::ZoneHandle(Z, Type::DynamicType());
}
if (CurrentToken() == Token::kTHIS) {
ConsumeToken();
ExpectToken(Token::kPERIOD);
this_seen = true;
parameter.is_field_initializer = true;
}
if ((parameter.type == NULL) && (CurrentToken() == Token::kVOID)) {
ConsumeToken();
// This must later be changed to a closure type if we recognize
// a closure/function type parameter. We check this at the end
// of ParseFormalParameter.
parameter.type = &Type::ZoneHandle(Z, Type::VoidType());
}
if (parameter.type == NULL) {
// At this point, we must see an identifier for the type or the
// function parameter.
if (!IsIdentifier()) {
ReportError("parameter name or type expected");
}
// We have not seen a parameter type yet, so we check if the next
// identifier could represent a type before parsing it.
Token::Kind follower = LookaheadToken(1);
// We have an identifier followed by a 'follower' token.
// We either parse a type or assume that no type is specified.
if ((follower == Token::kLT) || // Parameterized type.
(follower == Token::kPERIOD) || // Qualified class name of type.
Token::IsIdentifier(follower) || // Parameter name following a type.
(follower == Token::kTHIS)) { // Field parameter following a type.
// The types of formal parameters are never ignored, even in unchecked
// mode, because they are part of the function type of closurized
// functions appearing in type tests with typedefs.
parameter.has_explicit_type = true;
parameter.type = &AbstractType::ZoneHandle(Z,
ParseType(is_top_level_ ? ClassFinalizer::kResolveTypeParameters :
ClassFinalizer::kCanonicalize));
} else {
// If this is an initializing formal, its type will be set to the type of
// the respective field when the constructor is fully parsed.
parameter.type = &Type::ZoneHandle(Z, Type::DynamicType());
}
}
if (!this_seen && (CurrentToken() == Token::kTHIS)) {
ConsumeToken();
ExpectToken(Token::kPERIOD);
this_seen = true;
parameter.is_field_initializer = true;
}
// At this point, we must see an identifier for the parameter name.
parameter.name_pos = TokenPos();
parameter.name = ExpectIdentifier("parameter name expected");
if (parameter.is_field_initializer) {
params->has_field_initializer = true;
}
if (params->has_optional_named_parameters &&
(parameter.name->CharAt(0) == Library::kPrivateIdentifierStart)) {
ReportError(parameter.name_pos, "named parameter must not be private");
}
// Check for duplicate formal parameters.
const intptr_t num_existing_parameters =
params->num_fixed_parameters + params->num_optional_parameters;
for (intptr_t i = 0; i < num_existing_parameters; i++) {
ParamDesc& existing_parameter = (*params->parameters)[i];
if (existing_parameter.name->Equals(*parameter.name)) {
ReportError(parameter.name_pos, "duplicate formal parameter '%s'",
parameter.name->ToCString());
}
}
if (CurrentToken() == Token::kLPAREN) {
// This parameter is probably a closure. If we saw the keyword 'var'
// or 'final', a closure is not legal here and we ignore the
// opening parens.
if (!var_seen && !parameter.is_final) {
// The parsed parameter type is actually the function result type.
const AbstractType& result_type =
AbstractType::Handle(Z, parameter.type->raw());
// Finish parsing the function type parameter.
ParamList func_params;
// Add implicit closure object parameter.
func_params.AddFinalParameter(
TokenPos(),
&Symbols::ClosureParameter(),
&Type::ZoneHandle(Z, Type::DynamicType()));
const bool no_explicit_default_values = false;
ParseFormalParameterList(no_explicit_default_values, false, &func_params);
// The field 'is_static' has no meaning for signature functions.
const Function& signature_function = Function::Handle(Z,
Function::New(*parameter.name,
RawFunction::kSignatureFunction,
/* is_static = */ false,
/* is_const = */ false,
/* is_abstract = */ false,
/* is_external = */ false,
/* is_native = */ false,
current_class(),
parameter.name_pos));
signature_function.set_result_type(result_type);
signature_function.set_is_debuggable(false);
AddFormalParamsToFunction(&func_params, signature_function);
const String& signature = String::Handle(Z,
signature_function.Signature());
// Lookup the signature class, i.e. the class whose name is the signature.
// We only lookup in the current library, but not in its imports, and only
// create a new canonical signature class if it does not exist yet.
Class& signature_class = Class::ZoneHandle(Z,
library_.LookupLocalClass(signature));
if (signature_class.IsNull()) {
signature_class = Class::NewSignatureClass(signature,
signature_function,
script_,
parameter.name_pos);
// Record the function signature class in the current library, unless
// we are currently skipping a formal parameter list, in which case
// the signature class could remain unfinalized.
if (!params->skipped) {
library_.AddClass(signature_class);
}
} else {
signature_function.set_signature_class(signature_class);
}
ASSERT(signature_function.signature_class() == signature_class.raw());
if (!is_top_level_) {
// Finalize types in signature class here, so that the
// signature type is not computed twice.
ClassFinalizer::FinalizeTypesInClass(signature_class);
}
const Type& signature_type =
Type::ZoneHandle(Z, signature_class.SignatureType());
ASSERT(is_top_level_ || signature_type.IsFinalized());
// A signature type itself cannot be malformed or malbounded, only its
// signature function's result type or parameter types may be.
ASSERT(!signature_type.IsMalformed());
ASSERT(!signature_type.IsMalbounded());
// The type of the parameter is now the signature type.
parameter.type = &signature_type;
}
}
if ((CurrentToken() == Token::kASSIGN) || (CurrentToken() == Token::kCOLON)) {
if ((!params->has_optional_positional_parameters &&
!params->has_optional_named_parameters) ||
!allow_explicit_default_value) {
ReportError("parameter must not specify a default value");
}
if (params->has_optional_positional_parameters) {
ExpectToken(Token::kASSIGN);
} else {
ExpectToken(Token::kCOLON);
}
params->num_optional_parameters++;
params->has_explicit_default_values = true; // Also if explicitly NULL.
if (is_top_level_) {
// Skip default value parsing.
SkipExpr();
} else {
const Instance& const_value = ParseConstExpr()->literal();
parameter.default_value = &const_value;
}
} else {
if (params->has_optional_positional_parameters ||
params->has_optional_named_parameters) {
// Implicit default value is null.
params->num_optional_parameters++;
parameter.default_value = &Object::null_instance();
} else {
params->num_fixed_parameters++;
ASSERT(params->num_optional_parameters == 0);
}
}
if (parameter.type->IsVoidType()) {
ReportError("parameter '%s' may not be 'void'",
parameter.name->ToCString());
}
if (params->implicitly_final) {
parameter.is_final = true;
}
params->parameters->Add(parameter);
}
// Parses a sequence of normal or optional formal parameters.
void Parser::ParseFormalParameters(bool allow_explicit_default_values,
bool evaluate_metadata,
ParamList* params) {
TRACE_PARSER("ParseFormalParameters");
do {
ConsumeToken();
if (!params->has_optional_positional_parameters &&
!params->has_optional_named_parameters &&
(CurrentToken() == Token::kLBRACK)) {
// End of normal parameters, start of optional positional parameters.
params->has_optional_positional_parameters = true;
return;
}
if (!params->has_optional_positional_parameters &&
!params->has_optional_named_parameters &&
(CurrentToken() == Token::kLBRACE)) {
// End of normal parameters, start of optional named parameters.
params->has_optional_named_parameters = true;
return;
}
ParseFormalParameter(allow_explicit_default_values,
evaluate_metadata,
params);
} while (CurrentToken() == Token::kCOMMA);
}
void Parser::ParseFormalParameterList(bool allow_explicit_default_values,
bool evaluate_metadata,
ParamList* params) {
TRACE_PARSER("ParseFormalParameterList");
ASSERT(CurrentToken() == Token::kLPAREN);
if (LookaheadToken(1) != Token::kRPAREN) {
// Parse fixed parameters.
ParseFormalParameters(allow_explicit_default_values,
evaluate_metadata,
params);
if (params->has_optional_positional_parameters ||
params->has_optional_named_parameters) {
// Parse optional parameters.
ParseFormalParameters(allow_explicit_default_values,
evaluate_metadata,
params);
if (params->has_optional_positional_parameters) {
CheckToken(Token::kRBRACK, "',' or ']' expected");
} else {
CheckToken(Token::kRBRACE, "',' or '}' expected");
}
ConsumeToken(); // ']' or '}'.
}
if ((CurrentToken() != Token::kRPAREN) &&
!params->has_optional_positional_parameters &&
!params->has_optional_named_parameters) {
ReportError("',' or ')' expected");
}
} else {
ConsumeToken();
}
ExpectToken(Token::kRPAREN);
}
String& Parser::ParseNativeDeclaration() {
TRACE_PARSER("ParseNativeDeclaration");
ASSERT(IsSymbol(Symbols::Native()));
ConsumeToken();
CheckToken(Token::kSTRING, "string literal expected");
String& native_name = *CurrentLiteral();
ConsumeToken();
return native_name;
}
// Resolve and return the dynamic function of the given name in the superclass.
// If it is not found, and resolve_getter is true, try to resolve a getter of
// the same name. If it is still not found, return noSuchMethod and
// set is_no_such_method to true..
RawFunction* Parser::GetSuperFunction(intptr_t token_pos,
const String& name,
ArgumentListNode* arguments,
bool resolve_getter,
bool* is_no_such_method) {
const Class& super_class = Class::Handle(Z, current_class().SuperClass());
if (super_class.IsNull()) {
ReportError(token_pos, "class '%s' does not have a superclass",
String::Handle(Z, current_class().Name()).ToCString());
}
Function& super_func = Function::Handle(Z,
Resolver::ResolveDynamicAnyArgs(super_class, name));
if (!super_func.IsNull() &&
!super_func.AreValidArguments(arguments->length(),
arguments->names(),
NULL)) {
super_func = Function::null();
} else if (super_func.IsNull() && resolve_getter) {
const String& getter_name = String::ZoneHandle(Z, Field::GetterName(name));
super_func = Resolver::ResolveDynamicAnyArgs(super_class, getter_name);
ASSERT(super_func.IsNull() ||
(super_func.kind() != RawFunction::kImplicitStaticFinalGetter));
}
if (super_func.IsNull()) {
super_func =
Resolver::ResolveDynamicAnyArgs(super_class, Symbols::NoSuchMethod());
ASSERT(!super_func.IsNull());
*is_no_such_method = true;
} else {
*is_no_such_method = false;
}
return super_func.raw();
}
StaticCallNode* Parser::BuildInvocationMirrorAllocation(
intptr_t call_pos,
const String& function_name,
const ArgumentListNode& function_args,
const LocalVariable* temp_for_last_arg,
bool is_super_invocation) {
const intptr_t args_pos = function_args.token_pos();
// Build arguments to the call to the static
// InvocationMirror._allocateInvocationMirror method.
ArgumentListNode* arguments = new ArgumentListNode(args_pos);
// The first argument is the original function name.
arguments->Add(new LiteralNode(args_pos, function_name));
// The second argument is the arguments descriptor of the original function.
const Array& args_descriptor =
Array::ZoneHandle(ArgumentsDescriptor::New(function_args.length(),
function_args.names()));
arguments->Add(new LiteralNode(args_pos, args_descriptor));
// The third argument is an array containing the original function arguments,
// including the receiver.
ArrayNode* args_array =
new ArrayNode(args_pos, Type::ZoneHandle(Type::ArrayType()));
for (intptr_t i = 0; i < function_args.length(); i++) {
AstNode* arg = function_args.NodeAt(i);
if ((temp_for_last_arg != NULL) && (i == function_args.length() - 1)) {
LetNode* store_arg = new LetNode(arg->token_pos());
store_arg->AddNode(new StoreLocalNode(arg->token_pos(),
temp_for_last_arg,
arg));
store_arg->AddNode(new LoadLocalNode(arg->token_pos(),
temp_for_last_arg));
args_array->AddElement(store_arg);
} else {
args_array->AddElement(arg);
}
}
arguments->Add(args_array);
arguments->Add(new LiteralNode(args_pos, Bool::Get(is_super_invocation)));
// Lookup the static InvocationMirror._allocateInvocationMirror method.
const Class& mirror_class =
Class::Handle(Library::LookupCoreClass(Symbols::InvocationMirror()));
ASSERT(!mirror_class.IsNull());
const Function& allocation_function = Function::ZoneHandle(
mirror_class.LookupStaticFunction(
Library::PrivateCoreLibName(Symbols::AllocateInvocationMirror())));
ASSERT(!allocation_function.IsNull());
return new StaticCallNode(call_pos, allocation_function, arguments);
}
ArgumentListNode* Parser::BuildNoSuchMethodArguments(
intptr_t call_pos,
const String& function_name,
const ArgumentListNode& function_args,
const LocalVariable* temp_for_last_arg,
bool is_super_invocation) {
ASSERT(function_args.length() >= 1); // The receiver is the first argument.
const intptr_t args_pos = function_args.token_pos();
ArgumentListNode* arguments = new ArgumentListNode(args_pos);
arguments->Add(function_args.NodeAt(0));
// The second argument is the invocation mirror.
arguments->Add(BuildInvocationMirrorAllocation(call_pos,
function_name,
function_args,
temp_for_last_arg,
is_super_invocation));
return arguments;
}
AstNode* Parser::ParseSuperCall(const String& function_name) {
TRACE_PARSER("ParseSuperCall");
ASSERT(CurrentToken() == Token::kLPAREN);
const intptr_t supercall_pos = TokenPos();
// 'this' parameter is the first argument to super call.
ArgumentListNode* arguments = new ArgumentListNode(supercall_pos);
AstNode* receiver = LoadReceiver(supercall_pos);
arguments->Add(receiver);
ParseActualParameters(arguments, kAllowConst);
const bool kResolveGetter = true;
bool is_no_such_method = false;
const Function& super_function = Function::ZoneHandle(Z,
GetSuperFunction(supercall_pos,
function_name,
arguments,
kResolveGetter,
&is_no_such_method));
if (super_function.IsGetterFunction() ||
super_function.IsImplicitGetterFunction()) {
const Class& super_class =
Class::ZoneHandle(Z, current_class().SuperClass());
AstNode* closure = new StaticGetterNode(supercall_pos,
LoadReceiver(supercall_pos),
super_class,
function_name);
// 'this' is not passed as parameter to the closure.
ArgumentListNode* closure_arguments = new ArgumentListNode(supercall_pos);
for (int i = 1; i < arguments->length(); i++) {
closure_arguments->Add(arguments->NodeAt(i));
}
return BuildClosureCall(supercall_pos, closure, closure_arguments);
}
if (is_no_such_method) {
arguments = BuildNoSuchMethodArguments(
supercall_pos, function_name, *arguments, NULL, true);
}
return new StaticCallNode(supercall_pos, super_function, arguments);
}
// Simple test if a node is side effect free.
static bool IsSimpleLocalOrLiteralNode(AstNode* node) {
return node->IsLiteralNode() || node->IsLoadLocalNode();
}
AstNode* Parser::BuildUnarySuperOperator(Token::Kind op, PrimaryNode* super) {
ASSERT(super->IsSuper());
AstNode* super_op = NULL;
const intptr_t super_pos = super->token_pos();
if ((op == Token::kNEGATE) ||
(op == Token::kBIT_NOT)) {
// Resolve the operator function in the superclass.
const String& operator_function_name =
String::ZoneHandle(Z, Symbols::New(Token::Str(op)));
ArgumentListNode* op_arguments = new ArgumentListNode(super_pos);
AstNode* receiver = LoadReceiver(super_pos);
op_arguments->Add(receiver);
const bool kResolveGetter = false;
bool is_no_such_method = false;
const Function& super_operator = Function::ZoneHandle(Z,
GetSuperFunction(super_pos,
operator_function_name,
op_arguments,
kResolveGetter,
&is_no_such_method));
if (is_no_such_method) {
op_arguments = BuildNoSuchMethodArguments(
super_pos, operator_function_name, *op_arguments, NULL, true);
}
super_op = new StaticCallNode(super_pos, super_operator, op_arguments);
} else {
ReportError(super_pos, "illegal super operator call");
}
return super_op;
}
AstNode* Parser::ParseSuperOperator() {
TRACE_PARSER("ParseSuperOperator");
AstNode* super_op = NULL;
const intptr_t operator_pos = TokenPos();
if (CurrentToken() == Token::kLBRACK) {
ConsumeToken();
AstNode* index_expr = ParseExpr(kAllowConst, kConsumeCascades);
ExpectToken(Token::kRBRACK);
AstNode* receiver = LoadReceiver(operator_pos);
const Class& super_class =
Class::ZoneHandle(Z, current_class().SuperClass());
ASSERT(!super_class.IsNull());
super_op =
new LoadIndexedNode(operator_pos, receiver, index_expr, super_class);
} else {
ASSERT(Token::CanBeOverloaded(CurrentToken()) ||
(CurrentToken() == Token::kNE));
Token::Kind op = CurrentToken();
ConsumeToken();
bool negate_result = false;
if (op == Token::kNE) {
op = Token::kEQ;
negate_result = true;
}
ASSERT(Token::Precedence(op) >= Token::Precedence(Token::kEQ));
AstNode* other_operand = ParseBinaryExpr(Token::Precedence(op) + 1);
ArgumentListNode* op_arguments = new ArgumentListNode(operator_pos);
AstNode* receiver = LoadReceiver(operator_pos);
op_arguments->Add(receiver);
op_arguments->Add(other_operand);
// Resolve the operator function in the superclass.
const String& operator_function_name =
String::ZoneHandle(Z, Symbols::New(Token::Str(op)));
const bool kResolveGetter = false;
bool is_no_such_method = false;
const Function& super_operator = Function::ZoneHandle(Z,
GetSuperFunction(operator_pos,
operator_function_name,
op_arguments,
kResolveGetter,
&is_no_such_method));
if (is_no_such_method) {
op_arguments = BuildNoSuchMethodArguments(
operator_pos, operator_function_name, *op_arguments, NULL, true);
}
super_op = new StaticCallNode(operator_pos, super_operator, op_arguments);
if (negate_result) {
super_op = new UnaryOpNode(operator_pos, Token::kNOT, super_op);
}
}
return super_op;
}
ClosureNode* Parser::CreateImplicitClosureNode(const Function& func,
intptr_t token_pos,
AstNode* receiver) {
Function& implicit_closure_function =
Function::ZoneHandle(Z, func.ImplicitClosureFunction());
if (receiver != NULL) {
// If we create an implicit instance closure from inside a closure of a
// parameterized class, make sure that the receiver is captured as
// instantiator.
if (current_block_->scope->function_level() > 0) {
const Class& signature_class = Class::Handle(Z,
implicit_closure_function.signature_class());
if (signature_class.NumTypeParameters() > 0) {
CaptureInstantiator();
}
}
}
return new ClosureNode(token_pos, implicit_closure_function, receiver, NULL);
}
AstNode* Parser::ParseSuperFieldAccess(const String& field_name,
intptr_t field_pos) {
TRACE_PARSER("ParseSuperFieldAccess");
const Class& super_class = Class::ZoneHandle(Z, current_class().SuperClass());
if (super_class.IsNull()) {
ReportError("class '%s' does not have a superclass",
String::Handle(Z, current_class().Name()).ToCString());
}
AstNode* implicit_argument = LoadReceiver(field_pos);
const String& getter_name =
String::ZoneHandle(Z, Field::LookupGetterSymbol(field_name));
Function& super_getter = Function::ZoneHandle(Z);
if (!getter_name.IsNull()) {
super_getter = Resolver::ResolveDynamicAnyArgs(super_class, getter_name);
}
if (super_getter.IsNull()) {
const String& setter_name =
String::ZoneHandle(Z, Field::LookupSetterSymbol(field_name));
Function& super_setter = Function::ZoneHandle(Z);
if (!setter_name.IsNull()) {
super_setter = Resolver::ResolveDynamicAnyArgs(super_class, setter_name);
}
if (super_setter.IsNull()) {
// Check if this is an access to an implicit closure using 'super'.
// If a function exists of the specified field_name then try
// accessing it as a getter, at runtime we will handle this by
// creating an implicit closure of the function and returning it.
const Function& super_function = Function::ZoneHandle(Z,
Resolver::ResolveDynamicAnyArgs(super_class, field_name));
if (!super_function.IsNull()) {
// In case CreateAssignmentNode is called later on this
// CreateImplicitClosureNode, it will be replaced by a StaticSetterNode.
return CreateImplicitClosureNode(super_function,
field_pos,
implicit_argument);
}
// No function or field exists of the specified field_name.
// Emit a StaticGetterNode anyway, so that noSuchMethod gets called.
}
}
return new(Z) StaticGetterNode(
field_pos, implicit_argument, super_class, field_name);
}
StaticCallNode* Parser::GenerateSuperConstructorCall(
const Class& cls,
intptr_t supercall_pos,
LocalVariable* receiver,
AstNode* phase_parameter,
ArgumentListNode* forwarding_args) {
const Class& super_class = Class::Handle(Z, cls.SuperClass());
// Omit the implicit super() if there is no super class (i.e.
// we're not compiling class Object), or if the super class is an
// artificially generated "wrapper class" that has no constructor.
if (super_class.IsNull() ||
(super_class.num_native_fields() > 0 &&
Class::Handle(Z, super_class.SuperClass()).IsObjectClass())) {
return NULL;
}
String& super_ctor_name = String::Handle(Z, super_class.Name());
super_ctor_name = Symbols::FromConcat(super_ctor_name, Symbols::Dot());
ArgumentListNode* arguments = new ArgumentListNode(supercall_pos);
// Implicit 'this' parameter is the first argument.
AstNode* implicit_argument = new LoadLocalNode(supercall_pos, receiver);
arguments->Add(implicit_argument);
// Implicit construction phase parameter is second argument.
arguments->Add(phase_parameter);
// If this is a super call in a forwarding constructor, add the user-
// defined arguments to the super call and adjust the the super
// constructor name to the respective named constructor if necessary.
if (forwarding_args != NULL) {
for (int i = 0; i < forwarding_args->length(); i++) {
arguments->Add(forwarding_args->NodeAt(i));
}
String& ctor_name = String::Handle(Z, current_function().name());
String& class_name = String::Handle(Z, cls.Name());
if (ctor_name.Length() > class_name.Length() + 1) {
// Generating a forwarding call to a named constructor 'C.n'.
// Add the constructor name 'n' to the super constructor.
const intptr_t kLen = class_name.Length() + 1;
ctor_name = Symbols::New(ctor_name, kLen, ctor_name.Length() - kLen);
super_ctor_name = Symbols::FromConcat(super_ctor_name, ctor_name);
}
}
// Resolve super constructor function and check arguments.
const Function& super_ctor = Function::ZoneHandle(Z,
super_class.LookupConstructor(super_ctor_name));
if (super_ctor.IsNull()) {
ReportError(supercall_pos,
"unresolved implicit call to super constructor '%s()'",
String::Handle(Z, super_class.Name()).ToCString());
}
if (current_function().is_const() && !super_ctor.is_const()) {
ReportError(supercall_pos, "implicit call to non-const super constructor");
}
String& error_message = String::Handle(Z);
if (!super_ctor.AreValidArguments(arguments->length(),
arguments->names(),
&error_message)) {
ReportError(supercall_pos,
"invalid arguments passed to super constructor '%s()': %s",
String::Handle(Z, super_class.Name()).ToCString(),
error_message.ToCString());
}
return new StaticCallNode(supercall_pos, super_ctor, arguments);
}
StaticCallNode* Parser::ParseSuperInitializer(const Class& cls,
LocalVariable* receiver) {
TRACE_PARSER("ParseSuperInitializer");
ASSERT(CurrentToken() == Token::kSUPER);
const intptr_t supercall_pos = TokenPos();
ConsumeToken();
const Class& super_class = Class::Handle(Z, cls.SuperClass());
ASSERT(!super_class.IsNull());
String& ctor_name = String::Handle(Z, super_class.Name());
ctor_name = Symbols::FromConcat(ctor_name, Symbols::Dot());
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ctor_name = Symbols::FromConcat(
ctor_name, *ExpectIdentifier("constructor name expected"));
}
CheckToken(Token::kLPAREN, "parameter list expected");
ArgumentListNode* arguments = new ArgumentListNode(supercall_pos);
// 'this' parameter is the first argument to super class constructor.
AstNode* implicit_argument = new LoadLocalNode(supercall_pos, receiver);
arguments->Add(implicit_argument);
// Second implicit parameter is the construction phase. We optimistically
// assume that we can execute both the super initializer and the super
// constructor body. We may later change this to only execute the
// super initializer.
AstNode* phase_parameter =
new LiteralNode(supercall_pos,
Smi::ZoneHandle(Z, Smi::New(Function::kCtorPhaseAll)));
arguments->Add(phase_parameter);
// 'this' parameter must not be accessible to the other super call arguments.
receiver->set_invisible(true);
ParseActualParameters(arguments, kAllowConst);
receiver->set_invisible(false);
// Resolve the constructor.
const Function& super_ctor = Function::ZoneHandle(Z,
super_class.LookupConstructor(ctor_name));
if (super_ctor.IsNull()) {
ReportError(supercall_pos,
"super class constructor '%s' not found",
ctor_name.ToCString());
}
if (current_function().is_const() && !super_ctor.is_const()) {
ReportError(supercall_pos, "super constructor must be const");
}
String& error_message = String::Handle(Z);
if (!super_ctor.AreValidArguments(arguments->length(),
arguments->names(),
&error_message)) {
ReportError(supercall_pos,
"invalid arguments passed to super class constructor '%s': %s",
ctor_name.ToCString(),
error_message.ToCString());
}
return new StaticCallNode(supercall_pos, super_ctor, arguments);
}
AstNode* Parser::ParseInitializer(const Class& cls,
LocalVariable* receiver,
GrowableArray<Field*>* initialized_fields) {
TRACE_PARSER("ParseInitializer");
const intptr_t field_pos = TokenPos();
if (CurrentToken() == Token::kTHIS) {
ConsumeToken();
ExpectToken(Token::kPERIOD);
}
const String& field_name = *ExpectIdentifier("field name expected");
ExpectToken(Token::kASSIGN);
const bool saved_mode = SetAllowFunctionLiterals(false);
// "this" must not be accessible in initializer expressions.
receiver->set_invisible(true);
AstNode* init_expr = ParseConditionalExpr();
if (CurrentToken() == Token::kCASCADE) {
init_expr = ParseCascades(init_expr);
}
receiver->set_invisible(false);
SetAllowFunctionLiterals(saved_mode);
if (current_function().is_const() && !init_expr->IsPotentiallyConst()) {
ReportError(field_pos,
"initializer expression must be compile time constant.");
}
Field& field = Field::ZoneHandle(Z, cls.LookupInstanceField(field_name));
if (field.IsNull()) {
ReportError(field_pos, "unresolved reference to instance field '%s'",
field_name.ToCString());
}
EnsureExpressionTemp();
AstNode* instance = new(Z) LoadLocalNode(field_pos, receiver);
AstNode* initializer = CheckDuplicateFieldInit(field_pos,
initialized_fields, instance, &field, init_expr);
if (initializer == NULL) {
initializer =
new(Z) StoreInstanceFieldNode(field_pos, instance, field, init_expr);
}
return initializer;
}
void Parser::CheckFieldsInitialized(const Class& cls) {
const Array& fields = Array::Handle(Z, cls.fields());
Field& field = Field::Handle(Z);
SequenceNode* initializers = current_block_->statements;
for (int field_num = 0; field_num < fields.Length(); field_num++) {
field ^= fields.At(field_num);
if (field.is_static()) {
continue;
}
bool found = false;
for (int i = 0; i < initializers->length(); i++) {
found = false;
if (initializers->NodeAt(i)->IsStoreInstanceFieldNode()) {
StoreInstanceFieldNode* initializer =
initializers->NodeAt(i)->AsStoreInstanceFieldNode();
if (initializer->field().raw() == field.raw()) {
found = true;
break;
}
}
}
if (found) continue;
field.RecordStore(Object::Handle(Z));
}
}
AstNode* Parser::ParseExternalInitializedField(const Field& field) {
// Only use this function if the initialized field originates
// from a different class. We need to save and restore current
// class, library, and token stream (script).
ASSERT(current_class().raw() != field.origin());
const Class& saved_class = Class::Handle(Z, current_class().raw());
const Library& saved_library = Library::Handle(Z, library().raw());
const Script& saved_script = Script::Handle(Z, script().raw());
const intptr_t saved_token_pos = TokenPos();
set_current_class(Class::Handle(Z, field.origin()));
set_library(Library::Handle(Z, current_class().library()));
SetScript(Script::Handle(Z, current_class().script()),
field.token_pos());
ASSERT(IsIdentifier());
ConsumeToken();
ExpectToken(Token::kASSIGN);
AstNode* init_expr = NULL;
intptr_t expr_pos = TokenPos();
if (field.is_const()) {
init_expr = ParseConstExpr();
} else {
init_expr = ParseExpr(kAllowConst, kConsumeCascades);
if (init_expr->EvalConstExpr() != NULL) {
Instance& expr_value = Instance::ZoneHandle(Z);
if (!GetCachedConstant(expr_pos, &expr_value)) {
expr_value = EvaluateConstExpr(expr_pos, init_expr).raw();
CacheConstantValue(expr_pos, expr_value);
}
init_expr = new(Z) LiteralNode(field.token_pos(), expr_value);
}
}
set_current_class(saved_class);
set_library(saved_library);
SetScript(saved_script, saved_token_pos);
return init_expr;
}
void Parser::ParseInitializedInstanceFields(const Class& cls,
LocalVariable* receiver,
GrowableArray<Field*>* initialized_fields) {
TRACE_PARSER("ParseInitializedInstanceFields");
const Array& fields = Array::Handle(Z, cls.fields());
Field& f = Field::Handle(Z);
const intptr_t saved_pos = TokenPos();
for (int i = 0; i < fields.Length(); i++) {
f ^= fields.At(i);
if (!f.is_static() && f.has_initializer()) {
Field& field = Field::ZoneHandle(Z);
field ^= fields.At(i);
if (field.is_final()) {
// Final fields with initializer expression may not be initialized
// again by constructors. Remember that this field is already
// initialized.
initialized_fields->Add(&field);
}
AstNode* init_expr = NULL;
if (current_class().raw() != field.origin()) {
init_expr = ParseExternalInitializedField(field);
} else {
SetPosition(field.token_pos());
ASSERT(IsIdentifier());
ConsumeToken();
ExpectToken(Token::kASSIGN);
if (current_class().is_const()) {
// If the class has a const contructor, the initializer
// expression must be a compile-time constant.
init_expr = ParseConstExpr();
} else {
intptr_t expr_pos = TokenPos();
init_expr = ParseExpr(kAllowConst, kConsumeCascades);
if (init_expr->EvalConstExpr() != NULL) {
Instance& expr_value = Instance::ZoneHandle(Z);
if (!GetCachedConstant(expr_pos, &expr_value)) {
expr_value = EvaluateConstExpr(expr_pos, init_expr).raw();
CacheConstantValue(expr_pos, expr_value);
}
init_expr = new(Z) LiteralNode(field.token_pos(), expr_value);
}
}
}
ASSERT(init_expr != NULL);
AstNode* instance = new LoadLocalNode(field.token_pos(), receiver);
EnsureExpressionTemp();
AstNode* field_init =
new StoreInstanceFieldNode(field.token_pos(),
instance,
field,
init_expr);
current_block_->statements->Add(field_init);
}
}
initialized_fields->Add(NULL); // End of inline initializers.
SetPosition(saved_pos);
}
AstNode* Parser::CheckDuplicateFieldInit(
intptr_t init_pos,
GrowableArray<Field*>* initialized_fields,
AstNode* instance,
Field* field,
AstNode* init_value) {
ASSERT(!field->is_static());
AstNode* result = NULL;
// The initializer_list is divided into two sections. The sections
// are separated by a NULL entry: [f0, ... fn, NULL, fn+1, ...]
// The first fields f0 .. fn are final fields of the class that
// have an initializer expression inlined in the class declaration.
// The remaining fields are those initialized by the constructor's
// initializing formals and initializer list
int initializer_idx = 0;
while (initializer_idx < initialized_fields->length()) {
Field* initialized_field = (*initialized_fields)[initializer_idx];
initializer_idx++;
if (initialized_field == NULL) {
break;
}
if (initialized_field->raw() == field->raw()) {
// This final field has been initialized by an inlined
// initializer expression. This is a runtime error.
// Throw a NoSuchMethodError for the missing setter.
ASSERT(field->is_final());
// Build a call to NoSuchMethodError::_throwNew(
// Object receiver,
// String memberName,
// int invocation_type,
// List arguments,
// List argumentNames,
// List existingArgumentNames);
ArgumentListNode* nsm_args = new(Z) ArgumentListNode(init_pos);
// Object receiver.
nsm_args->Add(instance);
// String memberName.
String& setter_name = String::ZoneHandle(field->name());
setter_name = Field::SetterSymbol(setter_name);
nsm_args->Add(new(Z) LiteralNode(init_pos, setter_name));
// Smi invocation_type.
const int invocation_type =
InvocationMirror::EncodeType(InvocationMirror::kDynamic,
InvocationMirror::kSetter);
nsm_args->Add(new(Z) LiteralNode(
init_pos, Smi::ZoneHandle(Z, Smi::New(invocation_type))));
// List arguments.
GrowableArray<AstNode*> setter_args;
setter_args.Add(init_value);
ArrayNode* setter_args_array = new(Z) ArrayNode(
init_pos,
Type::ZoneHandle(Z, Type::ArrayType()),
setter_args);
nsm_args->Add(setter_args_array);
// List argumentNames.
// The missing implicit setter of the field has no argument names.
nsm_args->Add(new(Z) LiteralNode(init_pos, Array::ZoneHandle(Z)));
// List existingArgumentNames.
// There is no setter for the final field, thus there are
// no existing names.
nsm_args->Add(new(Z) LiteralNode(init_pos, Array::ZoneHandle(Z)));
AstNode* nsm_call =
MakeStaticCall(Symbols::NoSuchMethodError(),
Library::PrivateCoreLibName(Symbols::ThrowNew()),
nsm_args);
LetNode* let = new(Z) LetNode(init_pos);
let->AddNode(init_value);
let->AddNode(nsm_call);
result = let;
}
}
// The remaining elements in initialized_fields are fields that
// are initialized through initializing formal parameters, or
// in the constructor's initializer list. If there is a duplicate,
// it is a compile time error.
while (initializer_idx < initialized_fields->length()) {
Field* initialized_field = (*initialized_fields)[initializer_idx];
initializer_idx++;
if (initialized_field->raw() == field->raw()) {
ReportError(init_pos,
"duplicate initializer for field %s",
String::Handle(Z, field->name()).ToCString());
}
}
initialized_fields->Add(field);
return result;
}
void Parser::ParseInitializers(const Class& cls,
LocalVariable* receiver,
GrowableArray<Field*>* initialized_fields) {
TRACE_PARSER("ParseInitializers");
bool super_init_is_last = false;
intptr_t super_init_index = -1;
StaticCallNode* super_init_call = NULL;
if (CurrentToken() == Token::kCOLON) {
do {
ConsumeToken(); // Colon or comma.
if (CurrentToken() == Token::kSUPER) {
if (super_init_call != NULL) {
ReportError("duplicate call to super constructor");
}
super_init_call = ParseSuperInitializer(cls, receiver);
super_init_index = current_block_->statements->length();
current_block_->statements->Add(super_init_call);
super_init_is_last = true;
} else {
AstNode* init_statement =