blob: 96fdacaaa578ddab92a0cb529c10cedd07b10b5a [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/parser.h"
#include "vm/flags.h"
#ifndef DART_PRECOMPILED_RUNTIME
#include "lib/invocation_mirror.h"
#include "platform/utils.h"
#include "vm/ast_transformer.h"
#include "vm/bootstrap.h"
#include "vm/class_finalizer.h"
#include "vm/compiler/aot/precompiler.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/compiler/frontend/scope_builder.h"
#include "vm/compiler/jit/compiler.h"
#include "vm/compiler_stats.h"
#include "vm/dart_api_impl.h"
#include "vm/dart_entry.h"
#include "vm/growable_array.h"
#include "vm/handles.h"
#include "vm/hash_table.h"
#include "vm/heap/heap.h"
#include "vm/heap/safepoint.h"
#include "vm/isolate.h"
#include "vm/longjump.h"
#include "vm/native_arguments.h"
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/os.h"
#include "vm/regexp_assembler.h"
#include "vm/resolver.h"
#include "vm/scanner.h"
#include "vm/scopes.h"
#include "vm/stack_frame.h"
#include "vm/symbols.h"
#include "vm/tags.h"
#include "vm/timeline.h"
#include "vm/timer.h"
#include "vm/zone.h"
namespace dart {
DEFINE_FLAG(bool, enable_debug_break, false, "Allow use of break \"message\".");
DEFINE_FLAG(bool, trace_parser, false, "Trace parser operations.");
// TODO(floitsch): remove the conditional-directive flag, once we publicly
// committed to the current version.
DEFINE_FLAG(bool,
conditional_directives,
true,
"Enable conditional directives");
DEFINE_FLAG(
bool,
await_is_keyword,
false,
"await and yield are treated as proper keywords in synchronous code.");
DECLARE_FLAG(bool, profile_vm);
DECLARE_FLAG(bool, trace_service);
// Quick access to the current thread, isolate and zone.
#define T (thread())
#define I (isolate())
#define Z (zone())
// Quick synthetic token position.
#define ST(token_pos) ((token_pos).ToSynthetic())
#if defined(DEBUG)
class TraceParser : public ValueObject {
public:
TraceParser(TokenPosition token_pos,
const Script& script,
intptr_t* trace_indent,
const char* msg) {
indent_ = trace_indent;
if (FLAG_trace_parser) {
// Skips tracing of bootstrap libraries.
if (script.HasSource()) {
intptr_t line, column;
script.GetTokenLocation(token_pos, &line, &column);
PrintIndent();
OS::PrintErr("%s (line %" Pd ", col %" Pd ", token %" Pd ")\n", msg,
line, column, token_pos.value());
}
(*indent_)++;
}
}
~TraceParser() {
if (FLAG_trace_parser) {
(*indent_)--;
ASSERT(*indent_ >= 0);
}
}
private:
void PrintIndent() {
for (intptr_t i = 0; i < *indent_; i++) {
OS::PrintErr(". ");
}
}
intptr_t* indent_;
};
#define TRACE_PARSER(s) \
TraceParser __p__(this->TokenPos(), this->script_, &this->trace_indent_, s)
#else // not DEBUG
#define TRACE_PARSER(s)
#endif // DEBUG
class BoolScope : public ValueObject {
public:
BoolScope(bool* addr, bool new_value) : _addr(addr), _saved_value(*addr) {
*_addr = new_value;
}
~BoolScope() { *_addr = _saved_value; }
private:
bool* _addr;
bool _saved_value;
};
// Helper class to save and restore token position.
class Parser::TokenPosScope : public ValueObject {
public:
explicit TokenPosScope(Parser* p) : p_(p) { saved_pos_ = p_->TokenPos(); }
TokenPosScope(Parser* p, TokenPosition pos) : p_(p), saved_pos_(pos) {}
~TokenPosScope() { p_->SetPosition(saved_pos_); }
private:
Parser* p_;
TokenPosition saved_pos_;
DISALLOW_COPY_AND_ASSIGN(TokenPosScope);
};
class RecursionChecker : public ValueObject {
public:
explicit RecursionChecker(Parser* p) : parser_(p) {
parser_->recursion_counter_++;
// This limit also protects against stack overflow in the flow graph builder
// and some optimization passes, which may use more stack than the parser
// for the same function.
// The limit is somewhat arbitrary.
if (parser_->recursion_counter_ > 256) {
parser_->ReportError("stack overflow while parsing");
}
}
~RecursionChecker() { parser_->recursion_counter_--; }
private:
Parser* parser_;
};
static RawTypeArguments* NewTypeArguments(
const GrowableArray<AbstractType*>& objs) {
const TypeArguments& a =
TypeArguments::Handle(TypeArguments::New(objs.length()));
for (int i = 0; i < objs.length(); i++) {
a.SetTypeAt(i, *objs.At(i));
}
// Cannot canonicalize TypeArgument yet as its types may not have been
// finalized yet.
return a.raw();
}
ParsedFunction::ParsedFunction(Thread* thread, const Function& function)
: thread_(thread),
function_(function),
code_(Code::Handle(zone(), function.unoptimized_code())),
node_sequence_(NULL),
regexp_compile_data_(NULL),
instantiator_(NULL),
function_type_arguments_(NULL),
parent_type_arguments_(NULL),
current_context_var_(NULL),
arg_desc_var_(NULL),
expression_temp_var_(NULL),
finally_return_temp_var_(NULL),
deferred_prefixes_(new ZoneGrowableArray<const LibraryPrefix*>()),
guarded_fields_(new ZoneGrowableArray<const Field*>()),
default_parameter_values_(NULL),
raw_type_arguments_var_(NULL),
first_parameter_index_(),
num_stack_locals_(0),
have_seen_await_expr_(false),
kernel_scopes_(NULL) {
ASSERT(function.IsZoneHandle());
// Every function has a local variable for the current context.
LocalVariable* temp = new (zone())
LocalVariable(function.token_pos(), function.token_pos(),
Symbols::CurrentContextVar(), Object::dynamic_type());
current_context_var_ = temp;
const bool reify_generic_argument =
function.IsGeneric() && Isolate::Current()->reify_generic_functions();
const bool load_optional_arguments = function.HasOptionalParameters();
const bool check_arguments = function_.IsClosureFunction();
const bool need_argument_descriptor =
load_optional_arguments || check_arguments || reify_generic_argument;
if (need_argument_descriptor) {
arg_desc_var_ = new (zone())
LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::ArgDescVar(), Object::dynamic_type());
}
}
void ParsedFunction::AddToGuardedFields(const Field* field) const {
if ((field->guarded_cid() == kDynamicCid) ||
(field->guarded_cid() == kIllegalCid)) {
return;
}
for (intptr_t j = 0; j < guarded_fields_->length(); j++) {
const Field* other = (*guarded_fields_)[j];
if (field->Original() == other->Original()) {
// Abort background compilation early if the guarded state of this field
// has changed during compilation. We will not be able to commit
// the resulting code anyway.
if (Compiler::IsBackgroundCompilation()) {
if (!other->IsConsistentWith(*field)) {
Compiler::AbortBackgroundCompilation(
Thread::kNoDeoptId,
"Field's guarded state changed during compilation");
}
}
return;
}
}
// Note: the list of guarded fields must contain copies during background
// compilation because we will look at their guarded_cid when copying
// the array of guarded fields from callee into the caller during
// inlining.
ASSERT(!field->IsOriginal() || Thread::Current()->IsMutatorThread());
guarded_fields_->Add(&Field::ZoneHandle(Z, field->raw()));
}
void ParsedFunction::Bailout(const char* origin, const char* reason) const {
Report::MessageF(Report::kBailout, Script::Handle(function_.script()),
function_.token_pos(), Report::AtLocation,
"%s Bailout in %s: %s", origin,
String::Handle(function_.name()).ToCString(), reason);
UNREACHABLE();
}
kernel::ScopeBuildingResult* ParsedFunction::EnsureKernelScopes() {
if (kernel_scopes_ == NULL) {
kernel::ScopeBuilder builder(this);
kernel_scopes_ = builder.BuildScopes();
}
return kernel_scopes_;
}
LocalVariable* ParsedFunction::EnsureExpressionTemp() {
if (!has_expression_temp_var()) {
LocalVariable* temp =
new (Z) LocalVariable(function_.token_pos(), function_.token_pos(),
Symbols::ExprTemp(), Object::dynamic_type());
ASSERT(temp != NULL);
set_expression_temp_var(temp);
}
ASSERT(has_expression_temp_var());
return expression_temp_var();
}
void ParsedFunction::EnsureFinallyReturnTemp(bool is_async) {
if (!has_finally_return_temp_var()) {
LocalVariable* temp =
new (Z) LocalVariable(function_.token_pos(), function_.token_pos(),
Symbols::FinallyRetVal(), Object::dynamic_type());
ASSERT(temp != NULL);
temp->set_is_final();
if (is_async) {
temp->set_is_captured();
}
set_finally_return_temp_var(temp);
}
ASSERT(has_finally_return_temp_var());
}
void ParsedFunction::SetNodeSequence(SequenceNode* node_sequence) {
ASSERT(node_sequence_ == NULL);
ASSERT(node_sequence != NULL);
node_sequence_ = node_sequence;
}
void ParsedFunction::SetRegExpCompileData(
RegExpCompileData* regexp_compile_data) {
ASSERT(regexp_compile_data_ == NULL);
ASSERT(regexp_compile_data != NULL);
regexp_compile_data_ = regexp_compile_data;
}
void ParsedFunction::AddDeferredPrefix(const LibraryPrefix& prefix) {
// 'deferred_prefixes_' are used to invalidate code, but no invalidation is
// needed if --load_deferred_eagerly.
ASSERT(!FLAG_load_deferred_eagerly);
ASSERT(prefix.is_deferred_load());
ASSERT(!prefix.is_loaded());
for (intptr_t i = 0; i < deferred_prefixes_->length(); i++) {
if ((*deferred_prefixes_)[i]->raw() == prefix.raw()) {
return;
}
}
deferred_prefixes_->Add(&LibraryPrefix::ZoneHandle(Z, prefix.raw()));
}
void ParsedFunction::AllocateVariables() {
ASSERT(!function().IsIrregexpFunction());
LocalScope* scope = node_sequence()->scope();
const intptr_t num_fixed_params = function().num_fixed_parameters();
const intptr_t num_opt_params = function().NumOptionalParameters();
const intptr_t num_params = num_fixed_params + num_opt_params;
// Before we start allocating indices to variables, we'll setup the
// parameters array, which can be used to access the raw parameters (i.e. not
// the potentially variables which are in the context)
for (intptr_t param = 0; param < function().NumParameters(); ++param) {
LocalVariable* raw_parameter = scope->VariableAt(param);
if (raw_parameter->is_captured()) {
String& tmp = String::ZoneHandle(Z);
tmp = Symbols::FromConcat(T, Symbols::OriginalParam(),
raw_parameter->name());
RELEASE_ASSERT(scope->LocalLookupVariable(tmp) == NULL);
raw_parameter = new LocalVariable(raw_parameter->declaration_token_pos(),
raw_parameter->token_pos(), tmp,
raw_parameter->type());
if (function().HasOptionalParameters()) {
bool ok = scope->AddVariable(raw_parameter);
ASSERT(ok);
// Currently our optimizer cannot prove liveness of variables properly
// when a function has try/catch. It therefore makes the conservative
// estimate that all [LocalVariable]s in the frame are live and spills
// them before call sites (in some shape or form).
//
// Since we are guaranteed to not need that, we tell the try/catch
// spilling mechanism not to care about this variable.
raw_parameter->set_is_captured_parameter(true);
} else {
raw_parameter->set_index(
VariableIndex(function().NumParameters() - param));
}
}
raw_parameters_.Add(raw_parameter);
}
if (function_type_arguments_ != NULL) {
LocalVariable* raw_type_args_parameter = function_type_arguments_;
if (function_type_arguments_->is_captured()) {
String& tmp = String::ZoneHandle(Z);
tmp = Symbols::FromConcat(T, Symbols::OriginalParam(),
function_type_arguments_->name());
ASSERT(scope->LocalLookupVariable(tmp) == NULL);
raw_type_args_parameter =
new LocalVariable(raw_type_args_parameter->declaration_token_pos(),
raw_type_args_parameter->token_pos(), tmp,
raw_type_args_parameter->type());
bool ok = scope->AddVariable(raw_type_args_parameter);
ASSERT(ok);
}
raw_type_arguments_var_ = raw_type_args_parameter;
}
// The copy parameters implementation will still write to local variables
// which we assign indices as with the old CopyParams implementation.
VariableIndex parameter_index_start;
VariableIndex reamining_local_variables_start;
{
// Compute start indices to parameters and locals, and the number of
// parameters to copy.
if (num_opt_params == 0) {
parameter_index_start = first_parameter_index_ =
VariableIndex(num_params);
reamining_local_variables_start = VariableIndex(0);
} else {
parameter_index_start = first_parameter_index_ = VariableIndex(0);
reamining_local_variables_start = VariableIndex(-num_params);
}
}
if (function_type_arguments_ != NULL && num_opt_params > 0) {
reamining_local_variables_start =
VariableIndex(reamining_local_variables_start.value() - 1);
}
// Allocate parameters and local variables, either in the local frame or
// in the context(s).
bool found_captured_variables = false;
VariableIndex first_local_index =
VariableIndex(parameter_index_start.value() > 0 ? 0 : -num_params);
VariableIndex next_free_index = scope->AllocateVariables(
parameter_index_start, num_params, first_local_index, NULL,
&found_captured_variables);
num_stack_locals_ = -next_free_index.value();
}
struct CatchParamDesc {
CatchParamDesc()
: token_pos(TokenPosition::kNoSource),
type(NULL),
name(NULL),
var(NULL) {}
TokenPosition token_pos;
const AbstractType* type;
const String* name;
LocalVariable* var;
};
void ParsedFunction::AllocateIrregexpVariables(intptr_t num_stack_locals) {
ASSERT(function().IsIrregexpFunction());
ASSERT(function().NumOptionalParameters() == 0);
const intptr_t num_params = function().num_fixed_parameters();
ASSERT(num_params == RegExpMacroAssembler::kParamCount);
// Compute start indices to parameters and locals, and the number of
// parameters to copy.
first_parameter_index_ = VariableIndex(num_params);
// Frame indices are relative to the frame pointer and are decreasing.
num_stack_locals_ = num_stack_locals;
}
struct Parser::Block : public ZoneAllocated {
Block(Block* outer_block, LocalScope* local_scope, SequenceNode* seq)
: parent(outer_block), scope(local_scope), statements(seq) {
ASSERT(scope != NULL);
ASSERT(statements != NULL);
}
Block* parent; // Enclosing block, or NULL if outermost.
LocalScope* scope;
SequenceNode* statements;
};
// Class which describes an inlined finally block which is used to generate
// inlined code for the finally blocks when there is an exit from a try
// block using 'return', 'break' or 'continue'.
class Parser::TryStack : public ZoneAllocated {
public:
TryStack(Block* try_block, TryStack* outer_try, intptr_t try_index)
: try_block_(try_block),
inlined_finally_nodes_(),
outer_try_(outer_try),
try_index_(try_index),
inside_catch_(false),
inside_finally_(false) {}
TryStack* outer_try() const { return outer_try_; }
Block* try_block() const { return try_block_; }
intptr_t try_index() const { return try_index_; }
bool inside_catch() const { return inside_catch_; }
void enter_catch() { inside_catch_ = true; }
bool inside_finally() const { return inside_finally_; }
void enter_finally() { inside_finally_ = true; }
void exit_finally() { inside_finally_ = false; }
void AddNodeForFinallyInlining(AstNode* node);
void RemoveJumpToLabel(SourceLabel* label);
AstNode* GetNodeToInlineFinally(int index) {
if (0 <= index && index < inlined_finally_nodes_.length()) {
return inlined_finally_nodes_[index];
}
return NULL;
}
private:
Block* try_block_;
GrowableArray<AstNode*> inlined_finally_nodes_;
TryStack* outer_try_;
const intptr_t try_index_;
bool inside_catch_; // True when parsing a catch clause of this try.
bool inside_finally_; // True when parsing a finally clause of an inner try
// of this try.
DISALLOW_COPY_AND_ASSIGN(TryStack);
};
void Parser::TryStack::AddNodeForFinallyInlining(AstNode* node) {
inlined_finally_nodes_.Add(node);
}
void Parser::TryStack::RemoveJumpToLabel(SourceLabel* label) {
int i = 0;
while (i < inlined_finally_nodes_.length()) {
if (inlined_finally_nodes_[i]->IsJumpNode()) {
JumpNode* jump = inlined_finally_nodes_[i]->AsJumpNode();
if (jump->label() == label) {
// Shift remaining entries left and delete last entry.
for (int j = i + 1; j < inlined_finally_nodes_.length(); j++) {
inlined_finally_nodes_[j - 1] = inlined_finally_nodes_[j];
}
inlined_finally_nodes_.RemoveLast();
continue;
}
}
i++;
}
}
// For parsing a compilation unit.
Parser::Parser(const Script& script,
const Library& library,
TokenPosition token_pos)
: thread_(Thread::Current()),
isolate_(thread()->isolate()),
allocation_space_(thread_->IsMutatorThread() ? Heap::kNew : Heap::kOld),
script_(Script::Handle(zone(), script.raw())),
tokens_iterator_(zone(),
TokenStream::Handle(zone(), script.tokens()),
token_pos),
token_kind_(Token::kILLEGAL),
current_block_(NULL),
is_top_level_(false),
await_is_keyword_(false),
current_member_(NULL),
allow_function_literals_(true),
parsed_function_(NULL),
innermost_function_(Function::Handle(zone())),
literal_token_(LiteralToken::Handle(zone())),
current_class_(Class::Handle(zone())),
library_(Library::Handle(zone(), library.raw())),
try_stack_(NULL),
last_used_try_index_(0),
unregister_pending_function_(false),
async_temp_scope_(NULL),
trace_indent_(0),
recursion_counter_(0) {
ASSERT(tokens_iterator_.IsValid());
ASSERT(!library.IsNull());
}
// For parsing a function.
Parser::Parser(const Script& script,
ParsedFunction* parsed_function,
TokenPosition token_pos)
: thread_(Thread::Current()),
isolate_(thread()->isolate()),
allocation_space_(thread_->IsMutatorThread() ? Heap::kNew : Heap::kOld),
script_(Script::Handle(zone(), script.raw())),
tokens_iterator_(zone(),
TokenStream::Handle(zone(), script.tokens()),
token_pos),
token_kind_(Token::kILLEGAL),
current_block_(NULL),
is_top_level_(false),
await_is_keyword_(false),
current_member_(NULL),
allow_function_literals_(true),
parsed_function_(parsed_function),
innermost_function_(
Function::Handle(zone(), parsed_function->function().raw())),
literal_token_(LiteralToken::Handle(zone())),
current_class_(
Class::Handle(zone(), parsed_function->function().Owner())),
library_(Library::Handle(
zone(),
Class::Handle(zone(), parsed_function->function().origin())
.library())),
try_stack_(NULL),
last_used_try_index_(0),
unregister_pending_function_(false),
async_temp_scope_(NULL),
trace_indent_(0),
recursion_counter_(0) {
ASSERT(tokens_iterator_.IsValid());
ASSERT(!current_function().IsNull());
EnsureExpressionTemp();
}
Parser::~Parser() {
if (unregister_pending_function_) {
const GrowableObjectArray& pending_functions =
GrowableObjectArray::Handle(T->pending_functions());
ASSERT(!pending_functions.IsNull());
ASSERT(pending_functions.Length() > 0);
ASSERT(pending_functions.At(pending_functions.Length() - 1) ==
current_function().raw());
pending_functions.RemoveLast();
}
}
// Each try in this function gets its own try index.
// See definition of RawPcDescriptors::PcDescriptor.
int16_t Parser::AllocateTryIndex() {
if (!Utils::IsInt(16, last_used_try_index_ - 1)) {
ReportError("too many nested try statements");
}
return last_used_try_index_++;
}
void Parser::SetScript(const Script& script, TokenPosition token_pos) {
script_ = script.raw();
tokens_iterator_.SetStream(TokenStream::Handle(Z, script.tokens()),
token_pos);
token_kind_ = Token::kILLEGAL;
}
bool Parser::SetAllowFunctionLiterals(bool value) {
bool current_value = allow_function_literals_;
allow_function_literals_ = value;
return current_value;
}
const Function& Parser::current_function() const {
ASSERT(parsed_function() != NULL);
return parsed_function()->function();
}
const Function& Parser::innermost_function() const {
return innermost_function_;
}
int Parser::FunctionLevel() const {
if (current_block_ != NULL) {
return current_block_->scope->function_level();
}
return 0;
}
const Class& Parser::current_class() const {
return current_class_;
}
void Parser::set_current_class(const Class& value) {
current_class_ = value.raw();
}
void Parser::SetPosition(TokenPosition position) {
tokens_iterator_.SetCurrentPosition(position);
token_kind_ = Token::kILLEGAL;
prev_token_pos_ = position;
}
// Set state and increments generational count so that thge background compiler
// can detect if loading/top-level-parsing occured during compilation.
class TopLevelParsingScope : public StackResource {
public:
explicit TopLevelParsingScope(Thread* thread) : StackResource(thread) {
isolate()->IncrTopLevelParsingCount();
}
~TopLevelParsingScope() {
isolate()->DecrTopLevelParsingCount();
isolate()->IncrLoadingInvalidationGen();
}
};
void Parser::ParseCompilationUnit(const Library& library,
const Script& script) {
Thread* thread = Thread::Current();
ASSERT(thread->long_jump_base()->IsSafeToJump());
CSTAT_TIMER_SCOPE(thread, parser_timer);
#ifndef PRODUCT
VMTagScope tagScope(thread, VMTag::kCompileTopLevelTagId);
TimelineDurationScope tds(thread, Timeline::GetCompilerStream(),
"CompileTopLevel");
if (tds.enabled()) {
tds.SetNumArguments(1);
tds.CopyArgument(0, "script", String::Handle(script.url()).ToCString());
}
#endif
TopLevelParsingScope scope(thread);
Parser parser(script, library, TokenPosition::kMinSource);
parser.ParseTopLevel();
}
void Parser::ComputeCurrentToken() {
ASSERT(token_kind_ == Token::kILLEGAL);
token_kind_ = tokens_iterator_.CurrentTokenKind();
if (token_kind_ == Token::kERROR) {
ReportError(TokenPos(), "%s", CurrentLiteral()->ToCString());
}
}
Token::Kind Parser::LookaheadToken(int num_tokens) {
return tokens_iterator_.LookaheadTokenKind(num_tokens);
}
String* Parser::CurrentLiteral() const {
String& result = String::ZoneHandle(Z, tokens_iterator_.CurrentLiteral());
return &result;
}
RawDouble* Parser::CurrentDoubleLiteral() const {
literal_token_ ^= tokens_iterator_.CurrentToken();
ASSERT(literal_token_.kind() == Token::kDOUBLE);
return Double::RawCast(literal_token_.value());
}
RawInteger* Parser::CurrentIntegerLiteral() const {
literal_token_ ^= tokens_iterator_.CurrentToken();
ASSERT(literal_token_.kind() == Token::kINTEGER);
RawInteger* ri = Integer::RawCast(literal_token_.value());
return ri;
}
struct ParamDesc {
ParamDesc()
: type(NULL),
name_pos(TokenPosition::kNoSource),
name(NULL),
default_value(NULL),
metadata(NULL),
var(NULL),
is_final(false),
is_field_initializer(false),
has_explicit_type(false),
is_covariant(false) {}
const AbstractType* type;
TokenPosition name_pos;
const String* name;
const Instance* default_value; // NULL if not an optional parameter.
const Object* metadata; // NULL if no metadata or metadata not evaluated.
LocalVariable* var; // Scope variable allocated for this parameter.
bool is_final;
bool is_field_initializer;
bool has_explicit_type;
bool is_covariant;
};
struct ParamList {
ParamList() { Clear(); }
void Clear() {
num_fixed_parameters = 0;
num_optional_parameters = 0;
has_optional_positional_parameters = false;
has_optional_named_parameters = false;
has_explicit_default_values = false;
has_field_initializer = false;
has_covariant = false;
implicitly_final = false;
skipped = false;
this->parameters = new ZoneGrowableArray<ParamDesc>();
}
void AddFinalParameter(TokenPosition name_pos,
const String* name,
const AbstractType* type) {
this->num_fixed_parameters++;
ParamDesc param;
param.name_pos = name_pos;
param.name = name;
param.is_final = true;
param.type = type;
this->parameters->Add(param);
}
void AddReceiver(const AbstractType* receiver_type, TokenPosition token_pos) {
ASSERT(this->parameters->is_empty());
AddFinalParameter(token_pos, &Symbols::This(), receiver_type);
}
void EraseParameterTypes() {
const int num_parameters = parameters->length();
for (int i = 0; i < num_parameters; i++) {
(*parameters)[i].type = &Object::dynamic_type();
}
}
// Make the parameter variables visible/invisible.
void SetInvisible(bool invisible) {
const intptr_t num_params = parameters->length();
for (int i = 0; i < num_params; i++) {
ParamDesc& param = (*parameters)[i];
ASSERT(param.var != NULL);
param.var->set_invisible(invisible);
}
}
void HideInitFormals() {
const intptr_t num_params = parameters->length();
for (int i = 0; i < num_params; i++) {
ParamDesc& param = (*parameters)[i];
if (param.is_field_initializer) {
ASSERT(param.var != NULL);
param.var->set_invisible(true);
}
}
}
void SetImplicitlyFinal() { implicitly_final = true; }
int num_fixed_parameters;
int num_optional_parameters;
bool has_optional_positional_parameters;
bool has_optional_named_parameters;
bool has_explicit_default_values;
bool has_field_initializer;
bool has_covariant;
bool implicitly_final;
bool skipped;
ZoneGrowableArray<ParamDesc>* parameters;
};
struct MemberDesc {
MemberDesc() { Clear(); }
void Clear() {
has_abstract = false;
has_external = false;
has_covariant = false;
has_final = false;
has_const = false;
has_static = false;
has_var = false;
has_factory = false;
has_operator = false;
has_native = false;
metadata_pos = TokenPosition::kNoSource;
operator_token = Token::kILLEGAL;
type = NULL;
name_pos = TokenPosition::kNoSource;
name = NULL;
redirect_name = NULL;
dict_name = NULL;
params.Clear();
kind = RawFunction::kRegularFunction;
field_ = NULL;
}
bool IsConstructor() const {
return (kind == RawFunction::kConstructor) && !has_static;
}
bool IsFactory() const {
return (kind == RawFunction::kConstructor) && has_static;
}
bool IsFactoryOrConstructor() const {
return (kind == RawFunction::kConstructor);
}
bool IsGetter() const { return kind == RawFunction::kGetterFunction; }
bool IsSetter() const { return kind == RawFunction::kSetterFunction; }
const char* ToCString() const {
if (field_ != NULL) {
return "field";
} else if (IsConstructor()) {
return "constructor";
} else if (IsFactory()) {
return "factory";
} else if (IsGetter()) {
return "getter";
} else if (IsSetter()) {
return "setter";
}
return "method";
}
String* DictName() const { return (dict_name != NULL) ? dict_name : name; }
bool has_abstract;
bool has_external;
bool has_covariant;
bool has_final;
bool has_const;
bool has_static;
bool has_var;
bool has_factory;
bool has_operator;
bool has_native;
TokenPosition metadata_pos;
Token::Kind operator_token;
const AbstractType* type;
TokenPosition name_pos;
TokenPosition decl_begin_pos;
String* name;
// For constructors: NULL or name of redirected to constructor.
String* redirect_name;
// dict_name is the name used for the class namespace, if it
// differs from 'name'.
// For constructors: NULL for unnamed constructor,
// identifier after classname for named constructors.
// For getters and setters: unmangled name.
String* dict_name;
ParamList params;
RawFunction::Kind kind;
// NULL for functions, field object for static or instance fields.
Field* field_;
};
class ClassDesc : public ValueObject {
public:
ClassDesc(Zone* zone,
const Class& cls,
const String& cls_name,
bool is_interface,
TokenPosition token_pos)
: zone_(zone),
clazz_(cls),
class_name_(cls_name),
token_pos_(token_pos),
functions_(zone, 4),
fields_(zone, 4) {}
void AddFunction(const Function& function) {
functions_.Add(&Function::ZoneHandle(zone_, function.raw()));
}
const GrowableArray<const Function*>& functions() const { return functions_; }
void AddField(const Field& field) {
fields_.Add(&Field::ZoneHandle(zone_, field.raw()));
}
const GrowableArray<const Field*>& fields() const { return fields_; }
const Class& clazz() const { return clazz_; }
const String& class_name() const { return class_name_; }
bool has_constructor() const {
for (int i = 0; i < functions_.length(); i++) {
const Function* func = functions_.At(i);
if (func->kind() == RawFunction::kConstructor) {
return true;
}
}
return false;
}
TokenPosition token_pos() const { return token_pos_; }
void AddMember(const MemberDesc& member) { members_.Add(member); }
const GrowableArray<MemberDesc>& members() const { return members_; }
MemberDesc* LookupMember(const String& name) const {
for (int i = 0; i < members_.length(); i++) {
if (name.Equals(*members_[i].name)) {
return &members_[i];
}
}
return NULL;
}
RawArray* MakeFunctionsArray() {
const intptr_t len = functions_.length();
const Array& res = Array::Handle(zone_, Array::New(len, Heap::kOld));
for (intptr_t i = 0; i < len; i++) {
res.SetAt(i, *functions_[i]);
}
return res.raw();
}
private:
Zone* zone_;
const Class& clazz_;
const String& class_name_;
TokenPosition token_pos_; // Token index of "class" keyword.
GrowableArray<const Function*> functions_;
GrowableArray<const Field*> fields_;
GrowableArray<MemberDesc> members_;
};
class TopLevel : public ValueObject {
public:
explicit TopLevel(Zone* zone)
: zone_(zone), fields_(zone, 4), functions_(zone, 4) {}
void AddField(const Field& field) {
fields_.Add(&Field::ZoneHandle(zone_, field.raw()));
}
void AddFunction(const Function& function) {
functions_.Add(&Function::ZoneHandle(zone_, function.raw()));
}
const GrowableArray<const Field*>& fields() const { return fields_; }
const GrowableArray<const Function*>& functions() const { return functions_; }
private:
Zone* zone_;
GrowableArray<const Field*> fields_;
GrowableArray<const Function*> functions_;
};
void Parser::ParseClass(const Class& cls) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
const int64_t num_tokes_before = STAT_VALUE(thread, num_tokens_consumed);
#ifndef PRODUCT
TimelineDurationScope tds(thread, Timeline::GetCompilerStream(),
"ParseClass");
if (tds.enabled()) {
tds.SetNumArguments(1);
tds.CopyArgument(0, "class", String::Handle(cls.Name()).ToCString());
}
#endif
if (!cls.is_synthesized_class()) {
ASSERT(thread->long_jump_base()->IsSafeToJump());
CSTAT_TIMER_SCOPE(thread, parser_timer);
const Script& script = Script::Handle(zone, cls.script());
const Library& lib = Library::Handle(zone, cls.library());
Parser parser(script, lib, cls.token_pos());
parser.ParseClassDefinition(cls);
} else if (cls.is_enum_class()) {
ASSERT(thread->long_jump_base()->IsSafeToJump());
CSTAT_TIMER_SCOPE(thread, parser_timer);
const Script& script = Script::Handle(zone, cls.script());
const Library& lib = Library::Handle(zone, cls.library());
Parser parser(script, lib, cls.token_pos());
parser.ParseEnumDefinition(cls);
}
const int64_t num_tokes_after = STAT_VALUE(thread, num_tokens_consumed);
INC_STAT(thread, num_class_tokens, num_tokes_after - num_tokes_before);
}
bool Parser::FieldHasFunctionLiteralInitializer(const Field& field,
TokenPosition* start,
TokenPosition* end) {
if (!field.has_initializer()) {
return false;
}
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
const Class& cls = Class::Handle(zone, field.Owner());
const Script& script = Script::Handle(zone, field.Script());
const Library& lib = Library::Handle(zone, cls.library());
Parser parser(script, lib, field.token_pos());
return parser.GetFunctionLiteralInitializerRange(field, start, end);
}
bool Parser::GetFunctionLiteralInitializerRange(const Field& field,
TokenPosition* start,
TokenPosition* end) {
ASSERT(field.has_initializer());
// Since |field| has an initializer, skip until '='.
while (CurrentToken() != Token::kASSIGN) {
ConsumeToken();
}
// Skip past the '=' as well.
ConsumeToken();
*start = TokenPos();
if (IsFunctionLiteral()) {
SkipExpr();
*end = PrevTokenPos();
return true;
}
return false;
}
RawObject* Parser::ParseFunctionParameters(const Function& func) {
ASSERT(!func.IsNull());
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
Thread* thread = Thread::Current();
StackZone stack_zone(thread);
Zone* zone = stack_zone.GetZone();
const Script& script = Script::Handle(zone, func.script());
const Class& owner = Class::Handle(zone, func.Owner());
ASSERT(!owner.IsNull());
ParsedFunction* parsed_function =
new ParsedFunction(thread, Function::ZoneHandle(zone, func.raw()));
Parser parser(script, parsed_function, func.token_pos());
parser.SkipFunctionPreamble();
const bool use_function_type_syntax = false;
const bool allow_explicit_default_values = true;
const bool evaluate_metadata = true;
ParamList params;
parser.ParseFormalParameterList(use_function_type_syntax,
allow_explicit_default_values,
evaluate_metadata, &params);
ParamDesc* param = params.parameters->data();
const int param_cnt =
params.num_fixed_parameters + params.num_optional_parameters;
const Array& param_descriptor =
Array::Handle(Array::New(param_cnt * kParameterEntrySize, Heap::kOld));
for (int i = 0, j = 0; i < param_cnt; i++, j += kParameterEntrySize) {
param_descriptor.SetAt(j + kParameterIsFinalOffset,
param[i].is_final ? Bool::True() : Bool::False());
param_descriptor.SetAt(j + kParameterDefaultValueOffset,
(param[i].default_value == NULL)
? Object::null_instance()
: *(param[i].default_value));
const Object* metadata = param[i].metadata;
if ((metadata != NULL) && (*metadata).IsError()) {
return metadata->raw(); // Error evaluating the metadata.
}
param_descriptor.SetAt(j + kParameterMetadataOffset,
(param[i].metadata == NULL)
? Object::null_instance()
: *(param[i].metadata));
}
return param_descriptor.raw();
} else {
Thread* thread = Thread::Current();
Error& error = Error::Handle();
error = thread->sticky_error();
thread->clear_sticky_error();
return error.raw();
}
UNREACHABLE();
return Object::null();
}
bool Parser::ParseFormalParameters(const Function& func, ParamList* params) {
ASSERT(!func.IsNull());
// This is currently only used for constructors. To handle all kinds
// of functions, special cases for getters and possibly other kinds
// need to be added.
ASSERT(func.kind() == RawFunction::kConstructor);
ASSERT(!func.IsRedirectingFactory());
// Implicit constructors have no source, no user-defined formal parameters.
if (func.IsImplicitConstructor()) {
return true;
}
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
const Script& script = Script::Handle(func.script());
const Class& owner = Class::Handle(func.Owner());
ASSERT(!owner.IsNull());
ParsedFunction* parsed_function =
new ParsedFunction(Thread::Current(), Function::ZoneHandle(func.raw()));
Parser parser(script, parsed_function, func.token_pos());
parser.SkipFunctionPreamble();
const bool use_function_type_syntax = false;
const bool allow_explicit_default_values = true;
const bool evaluate_metadata = true;
parser.ParseFormalParameterList(use_function_type_syntax,
allow_explicit_default_values,
evaluate_metadata, params);
return true;
} else {
Thread::Current()->clear_sticky_error();
params->Clear();
return false;
}
UNREACHABLE();
return false;
}
void Parser::ParseFunction(ParsedFunction* parsed_function) {
Thread* thread = parsed_function->thread();
ASSERT(thread == Thread::Current());
Zone* zone = thread->zone();
CSTAT_TIMER_SCOPE(thread, parser_timer);
INC_STAT(thread, num_functions_parsed, 1);
#ifndef PRODUCT
VMTagScope tagScope(thread, VMTag::kCompileParseFunctionTagId,
FLAG_profile_vm);
TimelineDurationScope tds(thread, Timeline::GetCompilerStream(),
"ParseFunction");
#endif // !PRODUCT
ASSERT(thread->long_jump_base()->IsSafeToJump());
ASSERT(parsed_function != NULL);
const Function& func = parsed_function->function();
const Script& script = Script::Handle(zone, func.script());
Parser parser(script, parsed_function, func.token_pos());
#ifndef PRODUCT
if (tds.enabled()) {
tds.SetNumArguments(1);
tds.CopyArgument(0, "function", String::Handle(func.name()).ToCString());
}
#endif // !PRODUCT
SequenceNode* node_sequence = NULL;
switch (func.kind()) {
case RawFunction::kImplicitClosureFunction:
node_sequence = parser.ParseImplicitClosure(func);
break;
case RawFunction::kClosureFunction:
case RawFunction::kRegularFunction:
case RawFunction::kGetterFunction:
case RawFunction::kSetterFunction:
case RawFunction::kConstructor:
// The call to a redirecting factory is redirected.
ASSERT(!func.IsRedirectingFactory());
if (!func.IsImplicitConstructor()) {
parser.SkipFunctionPreamble();
}
node_sequence = parser.ParseFunc(func, false);
break;
case RawFunction::kImplicitGetter:
ASSERT(!func.is_static());
node_sequence = parser.ParseInstanceGetter(func);
break;
case RawFunction::kImplicitSetter:
ASSERT(!func.is_static());
node_sequence = parser.ParseInstanceSetter(func);
break;
case RawFunction::kImplicitStaticFinalGetter:
node_sequence = parser.ParseStaticFinalGetter(func);
INC_STAT(thread, num_implicit_final_getters, 1);
break;
case RawFunction::kMethodExtractor:
node_sequence = parser.ParseMethodExtractor(func);
INC_STAT(thread, num_method_extractors, 1);
break;
case RawFunction::kNoSuchMethodDispatcher:
node_sequence = parser.ParseNoSuchMethodDispatcher(func);
break;
case RawFunction::kInvokeFieldDispatcher:
node_sequence = parser.ParseInvokeFieldDispatcher(func);
break;
case RawFunction::kIrregexpFunction:
UNREACHABLE(); // Irregexp functions have their own parser.
default:
UNREACHABLE();
}
if (parsed_function->has_expression_temp_var()) {
node_sequence->scope()->AddVariable(parsed_function->expression_temp_var());
}
node_sequence->scope()->AddVariable(parsed_function->current_context_var());
if (parsed_function->has_finally_return_temp_var()) {
node_sequence->scope()->AddVariable(
parsed_function->finally_return_temp_var());
}
parsed_function->SetNodeSequence(node_sequence);
// The instantiators may be required at run time for generic type checks or
// allocation of generic types.
if (parser.IsInstantiatorRequired()) {
// In the case of a local function, only set the instantiator if the
// receiver (or type arguments parameter of a factory) was captured.
LocalVariable* instantiator = NULL;
const bool kTestOnly = true;
if (parser.current_function().IsInFactoryScope()) {
instantiator = parser.LookupTypeArgumentsParameter(node_sequence->scope(),
kTestOnly);
} else {
instantiator = parser.LookupReceiver(node_sequence->scope(), kTestOnly);
}
if (!parser.current_function().IsLocalFunction() ||
((instantiator != NULL) && instantiator->is_captured())) {
parsed_function->set_instantiator(instantiator);
}
}
// ParseFunc has recorded the generic function type arguments variable.
ASSERT(!Isolate::Current()->reify_generic_functions() ||
!parser.current_function().IsGeneric() ||
(parsed_function->function_type_arguments() != NULL));
}
RawObject* Parser::ParseMetadata(const Field& meta_data) {
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
Thread* thread = Thread::Current();
StackZone stack_zone(thread);
Zone* zone = stack_zone.GetZone();
const Class& owner_class = Class::Handle(zone, meta_data.Owner());
const Script& script = Script::Handle(zone, meta_data.Script());
const TokenPosition token_pos = meta_data.token_pos();
// Parsing metadata can involve following paths in the parser that are
// normally used for expressions and assume current_function is non-null,
// so we create a fake function to use as the current_function rather than
// scattering special cases throughout the parser.
const Function& fake_function = Function::ZoneHandle(
zone,
Function::New(Symbols::At(), RawFunction::kRegularFunction,
true, // is_static
false, // is_const
false, // is_abstract
false, // is_external
false, // is_native
Object::Handle(zone, meta_data.RawOwner()), token_pos));
fake_function.set_is_debuggable(false);
ParsedFunction* parsed_function = new ParsedFunction(thread, fake_function);
Parser parser(script, parsed_function, token_pos);
parser.set_current_class(owner_class);
parser.OpenFunctionBlock(fake_function);
RawObject* metadata = parser.EvaluateMetadata();
return metadata;
} else {
Thread* thread = Thread::Current();
StackZone stack_zone(thread);
Zone* zone = stack_zone.GetZone();
Error& error = Error::Handle(zone);
error = thread->sticky_error();
thread->clear_sticky_error();
return error.raw();
}
UNREACHABLE();
return Object::null();
}
RawArray* Parser::EvaluateMetadata() {
CheckToken(Token::kAT, "Metadata character '@' expected");
GrowableObjectArray& meta_values =
GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld));
while (CurrentToken() == Token::kAT) {
ConsumeToken();
TokenPosition expr_pos = TokenPos();
if (!IsIdentifier()) {
ExpectIdentifier("identifier expected");
}
// Reject expressions with deferred library prefix eagerly.
Object& obj =
Object::Handle(Z, library_.LookupLocalObject(*CurrentLiteral()));
if (!obj.IsNull() && obj.IsLibraryPrefix()) {
if (LibraryPrefix::Cast(obj).is_deferred_load()) {
ReportError("Metadata must be compile-time constant");
}
}
AstNode* expr = NULL;
if ((LookaheadToken(1) == Token::kLPAREN) ||
((LookaheadToken(1) == Token::kPERIOD) &&
(LookaheadToken(3) == Token::kLPAREN)) ||
((LookaheadToken(1) == Token::kPERIOD) &&
(LookaheadToken(3) == Token::kPERIOD) &&
(LookaheadToken(5) == Token::kLPAREN))) {
expr = ParseNewOperator(Token::kCONST);
} else {
// Can be x, C.x, or L.C.x.
expr = ParsePrimary(); // Consumes x, C or L.C.
Class& cls = Class::Handle(Z);
if (expr->IsPrimaryNode()) {
PrimaryNode* primary_node = expr->AsPrimaryNode();
if (primary_node->primary().IsClass()) {
// If the primary node referred to a class we are loading a
// qualified static field.
cls ^= primary_node->primary().raw();
} else {
ReportError(expr_pos,
"Metadata expressions must refer to a const field "
"or constructor");
}
}
if (CurrentToken() == Token::kPERIOD) {
// C.x or L.C.X.
if (cls.IsNull()) {
ReportError(expr_pos,
"Metadata expressions must refer to a const field "
"or constructor");
}
ConsumeToken();
const TokenPosition ident_pos = TokenPos();
String* ident = ExpectIdentifier("identifier expected");
const Field& field = Field::Handle(Z, cls.LookupStaticField(*ident));
if (field.IsNull()) {
ReportError(ident_pos, "Class '%s' has no field '%s'",
cls.ToCString(), ident->ToCString());
}
if (!field.is_const()) {
ReportError(ident_pos, "Field '%s' of class '%s' is not const",
ident->ToCString(), cls.ToCString());
}
expr = GenerateStaticFieldLookup(field, ident_pos);
}
}
if (expr->EvalConstExpr() == NULL) {
ReportError(expr_pos, "expression must be a compile-time constant");
}
const Instance& val = EvaluateConstExpr(expr_pos, expr);
meta_values.Add(val, Heap::kOld);
}
return Array::MakeFixedLength(meta_values);
}
SequenceNode* Parser::ParseStaticInitializer() {
ExpectIdentifier("field name expected");
CheckToken(Token::kASSIGN, "field initializer expected");
ConsumeToken();
OpenFunctionBlock(parsed_function()->function());
TokenPosition expr_pos = TokenPos();
AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades);
ReturnNode* ret = new (Z) ReturnNode(expr_pos, expr);
current_block_->statements->Add(ret);
return CloseBlock();
}
ParsedFunction* Parser::ParseStaticFieldInitializer(const Field& field) {
ASSERT(field.is_static());
Thread* thread = Thread::Current();
// TODO(koda): Should there be a StackZone here?
Zone* zone = thread->zone();
#ifndef PRODUCT
VMTagScope tagScope(thread, VMTag::kCompileParseFunctionTagId,
FLAG_profile_vm);
TimelineDurationScope tds(thread, Timeline::GetCompilerStream(),
"ParseStaticFieldInitializer");
#endif // !PRODUCT
const String& field_name = String::Handle(zone, field.name());
String& init_name = String::Handle(
zone, Symbols::FromConcat(thread, Symbols::InitPrefix(), field_name));
const Script& script = Script::Handle(zone, field.Script());
Object& initializer_owner = Object::Handle(field.Owner());
initializer_owner = PatchClass::New(Class::Handle(field.Owner()), script);
const Function& initializer = Function::ZoneHandle(
zone, Function::New(init_name, RawFunction::kImplicitStaticFinalGetter,
true, // static
false, // !const
false, // !abstract
false, // !external
false, // !native
initializer_owner, field.token_pos()));
initializer.set_result_type(AbstractType::Handle(zone, field.type()));
// Static initializer functions are hidden from the user.
// Since they are only executed once, we avoid inlining them.
// After the field is initialized, the compiler can eliminate
// the call to the static initializer.
initializer.set_is_reflectable(false);
initializer.set_is_debuggable(false);
initializer.set_is_inlinable(false);
ParsedFunction* parsed_function = new ParsedFunction(thread, initializer);
Parser parser(script, parsed_function, field.token_pos());
SequenceNode* body = parser.ParseStaticInitializer();
parsed_function->SetNodeSequence(body);
if (parsed_function->has_expression_temp_var()) {
body->scope()->AddVariable(parsed_function->expression_temp_var());
}
body->scope()->AddVariable(parsed_function->current_context_var());
if (parsed_function->has_finally_return_temp_var()) {
body->scope()->AddVariable(parsed_function->finally_return_temp_var());
}
// The instantiator is not required in a static expression.
ASSERT(!parser.IsInstantiatorRequired());
return parsed_function;
}
SequenceNode* Parser::ParseStaticFinalGetter(const Function& func) {
TRACE_PARSER("ParseStaticFinalGetter");
ASSERT(func.num_fixed_parameters() == 0); // static.
ASSERT(!func.HasOptionalParameters());
ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
OpenFunctionBlock(func);
TokenPosition ident_pos = TokenPos();
const String& field_name = *ExpectIdentifier("field name expected");
const Class& field_class = Class::Handle(Z, func.Owner());
const Field& field =
Field::ZoneHandle(Z, field_class.LookupStaticField(field_name));
ASSERT(!field.IsNull());
// Static final fields must have an initializer.
ExpectToken(Token::kASSIGN);
const TokenPosition expr_pos = TokenPos();
if (field.is_const()) {
// We don't want to use ParseConstExpr() here because we don't want
// the constant folding code to create, compile and execute a code
// fragment to evaluate the expression. Instead, we just make sure
// the static const field initializer is a constant expression and
// leave the evaluation to the getter function.
AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades);
// This getter will only be called once at compile time.
if (expr->EvalConstExpr() == NULL) {
ReportError(expr_pos, "initializer is not a valid compile-time constant");
}
ReturnNode* return_node = new ReturnNode(ident_pos, expr);
current_block_->statements->Add(return_node);
} else {
// This getter may be called each time the static field is accessed.
// Call runtime support to parse and evaluate the initializer expression.
// The runtime function will detect circular dependencies in expressions
// and handle errors while evaluating the expression.
current_block_->statements->Add(new (Z)
InitStaticFieldNode(ident_pos, field));
ReturnNode* return_node =
new ReturnNode(ident_pos, new LoadStaticFieldNode(ident_pos, field));
current_block_->statements->Add(return_node);
}
return CloseBlock();
}
// Create AstNodes for an implicit instance getter method:
// LoadLocalNode 0 ('this');
// LoadInstanceFieldNode (field_name);
// ReturnNode (field's value);
SequenceNode* Parser::ParseInstanceGetter(const Function& func) {
TRACE_PARSER("ParseInstanceGetter");
ParamList params;
// func.token_pos() points to the name of the field.
const TokenPosition ident_pos = func.token_pos();
ASSERT(current_class().raw() == func.Owner());
params.AddReceiver(ReceiverType(current_class()), ident_pos);
ASSERT(func.num_fixed_parameters() == 1); // receiver.
ASSERT(!func.HasOptionalParameters());
ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
// Receiver is local 0.
LocalVariable* receiver = current_block_->scope->VariableAt(0);
LoadLocalNode* load_receiver = new LoadLocalNode(ident_pos, receiver);
String& field_name = String::Handle(Z, func.name());
field_name = Field::NameFromGetter(field_name);
const Class& field_class = Class::Handle(Z, func.Owner());
const Field& field =
Field::ZoneHandle(Z, field_class.LookupInstanceField(field_name));
ASSERT(!field.IsNull());
LoadInstanceFieldNode* load_field =
new LoadInstanceFieldNode(ident_pos, load_receiver, field);
ReturnNode* return_node = new ReturnNode(ST(ident_pos), load_field);
current_block_->statements->Add(return_node);
return CloseBlock();
}
// Create AstNodes for an implicit instance setter method:
// LoadLocalNode 0 ('this')
// LoadLocalNode 1 ('value')
// SetInstanceField (field_name);
// ReturnNode (void);
SequenceNode* Parser::ParseInstanceSetter(const Function& func) {
TRACE_PARSER("ParseInstanceSetter");
// func.token_pos() points to the name of the field.
const TokenPosition ident_pos = func.token_pos();
const String& field_name = *CurrentLiteral();
const Class& field_class = Class::ZoneHandle(Z, func.Owner());
const Field& field =
Field::ZoneHandle(Z, field_class.LookupInstanceField(field_name));
const AbstractType& field_type = AbstractType::ZoneHandle(Z, field.type());
ParamList params;
ASSERT(current_class().raw() == func.Owner());
params.AddReceiver(ReceiverType(current_class()), ident_pos);
params.AddFinalParameter(ident_pos, &Symbols::Value(), &field_type);
ASSERT(func.num_fixed_parameters() == 2); // receiver, value.
ASSERT(!func.HasOptionalParameters());
ASSERT(AbstractType::Handle(Z, func.result_type()).IsVoidType());
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
LoadLocalNode* receiver =
new LoadLocalNode(ident_pos, current_block_->scope->VariableAt(0));
LoadLocalNode* value =
new LoadLocalNode(ident_pos, current_block_->scope->VariableAt(1));
EnsureExpressionTemp();
StoreInstanceFieldNode* store_field =
new StoreInstanceFieldNode(ident_pos, receiver, field, value,
/* is_initializer = */ false);
current_block_->statements->Add(store_field);
current_block_->statements->Add(new ReturnNode(ST(ident_pos)));
return CloseBlock();
}
SequenceNode* Parser::ParseImplicitClosure(const Function& func) {
TRACE_PARSER("ParseImplicitClosure");
TokenPosition token_pos = func.token_pos();
OpenFunctionBlock(func);
if (parsed_function_->has_arg_desc_var() && FunctionLevel() == 0) {
EnsureExpressionTemp();
current_block_->scope->AddVariable(parsed_function_->arg_desc_var());
}
const Function& parent = Function::Handle(func.parent_function());
intptr_t type_args_len = 0; // Length of type args vector passed to parent.
LocalVariable* type_args_var = NULL;
if (Isolate::Current()->reify_generic_functions()) {
// The parent function of an implicit closure is the original function, i.e.
// non-closurized. It is not an enclosing function in the usual sense of a
// parent function. Do not set parent_type_arguments() in parsed_function_.
ASSERT(func.IsGeneric() == parent.IsGeneric());
if (func.IsGeneric()) {
type_args_len = func.NumTypeParameters();
// Insert function type arguments variable to scope.
type_args_var = new (Z) LocalVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::FunctionTypeArgumentsVar(), Object::dynamic_type());
current_block_->scope->AddVariable(type_args_var);
ASSERT(FunctionLevel() == 0);
parsed_function_->set_function_type_arguments(type_args_var);
}
}
ParamList params;
params.AddFinalParameter(token_pos, &Symbols::ClosureParameter(),
&Object::dynamic_type());
if (parent.IsImplicitSetterFunction()) {
const TokenPosition ident_pos = func.token_pos();
ASSERT(IsIdentifier());
params.AddFinalParameter(ident_pos, &Symbols::Value(),
&Object::dynamic_type());
ASSERT(func.num_fixed_parameters() == 2); // closure, value.
} else if (!parent.IsGetterFunction() && !parent.IsImplicitGetterFunction()) {
// NOTE: For the `kernel -> flowgraph` we don't use the parser.
if (parent.kernel_offset() <= 0) {
SkipFunctionPreamble();
const bool use_function_type_syntax = false;
const bool allow_explicit_default_values = true;
const bool evaluate_metadata = false;
ParseFormalParameterList(use_function_type_syntax,
allow_explicit_default_values, evaluate_metadata,
&params);
FinalizeFormalParameterTypes(&params);
SetupDefaultsForOptionalParams(params);
}
}
// Populate function scope with the formal parameters.
LocalScope* scope = current_block_->scope;
AddFormalParamsToScope(&params, scope);
ArgumentListNode* func_args =
new ArgumentListNode(token_pos, type_args_var, type_args_len);
if (!func.is_static()) {
func_args->Add(LoadReceiver(token_pos));
}
// Skip implicit parameter at 0.
for (intptr_t i = 1; i < func.NumParameters(); ++i) {
func_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
}
if (func.HasOptionalNamedParameters()) {
// TODO(srdjan): Must allocate array in old space, since it
// runs in background compiler. Find a better way.
const Array& arg_names =
Array::ZoneHandle(Array::New(func.NumOptionalParameters(), Heap::kOld));
for (intptr_t i = 0; i < arg_names.Length(); ++i) {
intptr_t index = func.num_fixed_parameters() + i;
arg_names.SetAt(i, String::Handle(func.ParameterNameAt(index)));
}
func_args->set_names(arg_names);
}
const String& func_name = String::ZoneHandle(parent.name());
const Class& owner = Class::Handle(parent.Owner());
Function& target = Function::ZoneHandle(owner.LookupFunction(func_name));
if (target.raw() != parent.raw()) {
NOT_IN_PRODUCT(ASSERT(Isolate::Current()->HasAttemptedReload()));
if (target.IsNull() || (target.is_static() != parent.is_static()) ||
(target.kind() != parent.kind())) {
target = Function::null();
}
}
AstNode* call = NULL;
// Check the target still exists and has compatible parameters. If not,
// throw NSME/call nSM instead of forwarding the call. Note we compare the
// parent not func because func has an extra parameter for the closure
// receiver.
if (!target.IsNull() &&
(parent.num_fixed_parameters() == target.num_fixed_parameters())) {
call = new StaticCallNode(token_pos, target, func_args,
StaticCallNode::kNoRebind);
} else if (!parent.is_static()) {
NOT_IN_PRODUCT(ASSERT(Isolate::Current()->HasAttemptedReload()));
// If a subsequent reload reintroduces the target in the middle of the
// Invocation object being constructed, we won't be able to successfully
// deopt because the generated AST will change.
func.SetIsOptimizable(false);
ArgumentListNode* arguments = BuildNoSuchMethodArguments(
token_pos, func_name, *func_args, NULL, false);
const intptr_t kTypeArgsLen = 0;
const intptr_t kNumArguments = 2; // Receiver, InvocationMirror.
ArgumentsDescriptor args_desc(Array::Handle(
Z, ArgumentsDescriptor::New(kTypeArgsLen, kNumArguments)));
Function& no_such_method =
Function::ZoneHandle(Z, Resolver::ResolveDynamicForReceiverClass(
owner, Symbols::NoSuchMethod(), args_desc));
if (no_such_method.IsNull()) {
// If noSuchMethod(i) is not found, call Object:noSuchMethod.
no_such_method ^= Resolver::ResolveDynamicForReceiverClass(
Class::Handle(Z, I->object_store()->object_class()),
Symbols::NoSuchMethod(), args_desc);
}
call = new StaticCallNode(token_pos, no_such_method, arguments,
StaticCallNode::kStatic);
} else {
NOT_IN_PRODUCT(ASSERT(Isolate::Current()->HasAttemptedReload()));
// If a subsequent reload reintroduces the target in the middle of the
// arguments array being constructed, we won't be able to successfully
// deopt because the generated AST will change.
func.SetIsOptimizable(false);
InvocationMirror::Kind im_kind;
if (parent.IsImplicitGetterFunction()) {
im_kind = InvocationMirror::kGetter;
} else if (parent.IsImplicitSetterFunction()) {
im_kind = InvocationMirror::kSetter;
} else {
im_kind = InvocationMirror::kMethod;
}
call = ThrowNoSuchMethodError(TokenPos(), owner, func_name, func_args,
InvocationMirror::kStatic, im_kind,
NULL); // No existing function.
}
ASSERT(call != NULL);
ReturnNode* return_node = new ReturnNode(token_pos, call);
current_block_->statements->Add(return_node);
return CloseBlock();
}
SequenceNode* Parser::ParseMethodExtractor(const Function& func) {
TRACE_PARSER("ParseMethodExtractor");
ParamList params;
const TokenPosition ident_pos = func.token_pos();
ASSERT(func.token_pos() == TokenPosition::kMethodExtractor);
ASSERT(current_class().raw() == func.Owner());
params.AddReceiver(ReceiverType(current_class()), ident_pos);
ASSERT(func.num_fixed_parameters() == 1); // Receiver.
ASSERT(!func.HasOptionalParameters());
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
// Receiver is local 0.
LocalVariable* receiver = current_block_->scope->VariableAt(0);
LoadLocalNode* load_receiver = new LoadLocalNode(ident_pos, receiver);
ClosureNode* closure = new ClosureNode(
ident_pos, Function::ZoneHandle(Z, func.extracted_method_closure()),
load_receiver, NULL);
ReturnNode* return_node = new ReturnNode(ident_pos, closure);
current_block_->statements->Add(return_node);
return CloseBlock();
}
void Parser::BuildDispatcherScope(const Function& func,
const ArgumentsDescriptor& desc) {
ParamList params;
// Receiver first.
TokenPosition token_pos = func.token_pos();
params.AddReceiver(ReceiverType(current_class()), token_pos);
// Remaining positional parameters.
intptr_t i = 1;
for (; i < desc.PositionalCount(); ++i) {
ParamDesc p;
char name[64];
Utils::SNPrint(name, 64, ":p%" Pd, i);
p.name = &String::ZoneHandle(Z, Symbols::New(T, name));
p.type = &Object::dynamic_type();
params.parameters->Add(p);
params.num_fixed_parameters++;
}
ASSERT(desc.PositionalCount() == params.num_fixed_parameters);
// Named parameters.
for (; i < desc.Count(); ++i) {
ParamDesc p;
intptr_t index = i - desc.PositionalCount();
p.name = &String::ZoneHandle(Z, desc.NameAt(index));
p.type = &Object::dynamic_type();
p.default_value = &Object::null_instance();
params.parameters->Add(p);
params.num_optional_parameters++;
params.has_optional_named_parameters = true;
}
ASSERT(desc.NamedCount() == params.num_optional_parameters);
SetupDefaultsForOptionalParams(params);
// Build local scope for function and populate with the formal parameters.
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
if (desc.TypeArgsLen() > 0) {
ASSERT(func.IsGeneric() && !func.HasGenericParent());
// Insert function type arguments variable to scope.
LocalVariable* type_args_var = new (Z) LocalVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::FunctionTypeArgumentsVar(), Object::dynamic_type());
current_block_->scope->AddVariable(type_args_var);
ASSERT(FunctionLevel() == 0);
parsed_function_->set_function_type_arguments(type_args_var);
}
if (parsed_function_->has_arg_desc_var() && FunctionLevel() == 0) {
EnsureExpressionTemp();
current_block_->scope->AddVariable(parsed_function_->arg_desc_var());
}
}
SequenceNode* Parser::ParseNoSuchMethodDispatcher(const Function& func) {
TRACE_PARSER("ParseNoSuchMethodDispatcher");
ASSERT(FLAG_lazy_dispatchers);
ASSERT(func.IsNoSuchMethodDispatcher());
TokenPosition token_pos = func.token_pos();
ASSERT(func.token_pos() == TokenPosition::kMinSource);
ASSERT(current_class().raw() == func.Owner());
ArgumentsDescriptor desc(Array::Handle(Z, func.saved_args_desc()));
ASSERT(desc.Count() > 0);
// Set up scope for this function.
BuildDispatcherScope(func, desc);
// Receiver is local 0.
LocalScope* scope = current_block_->scope;
ArgumentListNode* func_args = new ArgumentListNode(
token_pos, parsed_function_->function_type_arguments(),
desc.TypeArgsLen());
for (intptr_t i = 0; i < desc.Count(); ++i) {
func_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
}
if (desc.NamedCount() > 0) {
const Array& arg_names =
Array::ZoneHandle(Z, Array::New(desc.NamedCount(), Heap::kOld));
for (intptr_t i = 0; i < arg_names.Length(); ++i) {
arg_names.SetAt(i, String::Handle(Z, desc.NameAt(i)));
}
func_args->set_names(arg_names);
}
const String& func_name = String::ZoneHandle(Z, func.name());
ArgumentListNode* arguments =
BuildNoSuchMethodArguments(token_pos, func_name, *func_args, NULL, false);
const intptr_t kTypeArgsLen = 0;
const intptr_t kNumArguments = 2; // Receiver, InvocationMirror.
ArgumentsDescriptor args_desc(
Array::Handle(Z, ArgumentsDescriptor::New(kTypeArgsLen, kNumArguments)));
Function& no_such_method = Function::ZoneHandle(
Z,
Resolver::ResolveDynamicForReceiverClass(
Class::Handle(Z, func.Owner()), Symbols::NoSuchMethod(), args_desc));
if (no_such_method.IsNull()) {
// If noSuchMethod(i) is not found, call Object:noSuchMethod.
no_such_method ^= Resolver::ResolveDynamicForReceiverClass(
Class::Handle(Z, I->object_store()->object_class()),
Symbols::NoSuchMethod(), args_desc);
}
StaticCallNode* call = new StaticCallNode(
token_pos, no_such_method, arguments, StaticCallNode::kNSMDispatch);
ReturnNode* return_node = new ReturnNode(token_pos, call);
current_block_->statements->Add(return_node);
return CloseBlock();
}
SequenceNode* Parser::ParseInvokeFieldDispatcher(const Function& func) {
TRACE_PARSER("ParseInvokeFieldDispatcher");
ASSERT(func.IsInvokeFieldDispatcher());
TokenPosition token_pos = func.token_pos();
ASSERT(func.token_pos() == TokenPosition::kMinSource);
ASSERT(current_class().raw() == func.Owner());
const Array& args_desc = Array::Handle(Z, func.saved_args_desc());
ArgumentsDescriptor desc(args_desc);
ASSERT(desc.Count() > 0);
// Set up scope for this function.
BuildDispatcherScope(func, desc);
// Receiver is local 0.
LocalScope* scope = current_block_->scope;
ArgumentListNode* no_args = new ArgumentListNode(token_pos);
LoadLocalNode* receiver = new LoadLocalNode(token_pos, scope->VariableAt(0));
const Class& closure_cls =
Class::Handle(Isolate::Current()->object_store()->closure_class());
const Class& owner = Class::Handle(Z, func.Owner());
ASSERT(!owner.IsNull());
const String& name = String::Handle(Z, func.name());
AstNode* function_object = NULL;
if (owner.raw() == closure_cls.raw() && name.Equals(Symbols::Call())) {
function_object = receiver;
} else {
const String& getter_name =
String::ZoneHandle(Z, Field::GetterSymbol(name));
function_object =
new (Z) InstanceCallNode(token_pos, receiver, getter_name, no_args);
}
// Pass arguments 1..n to the closure call.
ArgumentListNode* args = new (Z)
ArgumentListNode(token_pos, parsed_function_->function_type_arguments(),
desc.TypeArgsLen());
const Array& names =
Array::Handle(Z, Array::New(desc.NamedCount(), Heap::kOld));
// Positional parameters.
intptr_t i = 1;
for (; i < desc.PositionalCount(); ++i) {
args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
}
// Named parameters.
for (; i < desc.Count(); i++) {
args->Add(new (Z) LoadLocalNode(token_pos, scope->VariableAt(i)));
intptr_t index = i - desc.PositionalCount();
names.SetAt(index, String::Handle(Z, desc.NameAt(index)));
}
args->set_names(names);
AstNode* result = NULL;
if (owner.raw() == closure_cls.raw() && name.Equals(Symbols::Call())) {
result = new ClosureCallNode(token_pos, function_object, args);
} else {
result = BuildClosureCall(token_pos, function_object, args);
}
ReturnNode* return_node = new ReturnNode(token_pos, result);
current_block_->statements->Add(return_node);
return CloseBlock();
}
AstNode* Parser::BuildClosureCall(TokenPosition token_pos,
AstNode* closure,
ArgumentListNode* arguments) {
return new InstanceCallNode(token_pos, closure, Symbols::Call(), arguments);
}
void Parser::SkipToMatching() {
Token::Kind opening_token = CurrentToken();
ASSERT((opening_token == Token::kLBRACE) ||
(opening_token == Token::kLPAREN));
GrowableArray<Token::Kind> token_stack(8);
GrowableArray<TokenPosition> token_pos_stack(8);
// Adding the first opening brace here, because it will be consumed
// in the loop right away.
token_stack.Add(opening_token);
const TokenPosition start_pos = TokenPos();
TokenPosition opening_pos = start_pos;
token_pos_stack.Add(start_pos);
bool is_match = true;
bool unexpected_token_found = false;
Token::Kind token = opening_token;
TokenPosition token_pos;
do {
ConsumeToken();
token = CurrentToken();
token_pos = TokenPos();
switch (token) {
case Token::kLBRACE:
case Token::kLPAREN:
case Token::kLBRACK:
token_stack.Add(token);
token_pos_stack.Add(token_pos);
break;
case Token::kRBRACE:
opening_token = token_stack.RemoveLast();
opening_pos = token_pos_stack.RemoveLast();
is_match = opening_token == Token::kLBRACE;
break;
case Token::kRPAREN:
opening_token = token_stack.RemoveLast();
opening_pos = token_pos_stack.RemoveLast();
is_match = opening_token == Token::kLPAREN;
break;
case Token::kRBRACK:
opening_token = token_stack.RemoveLast();
opening_pos = token_pos_stack.RemoveLast();
is_match = opening_token == Token::kLBRACK;
break;
case Token::kEOS:
opening_token = token_stack.RemoveLast();
opening_pos = token_pos_stack.RemoveLast();
unexpected_token_found = true;
break;
default:
// nothing.
break;
}
} while (!token_stack.is_empty() && is_match && !unexpected_token_found);
if (!is_match) {
const Error& error = Error::Handle(LanguageError::NewFormatted(
Error::Handle(), script_, opening_pos, Report::AtLocation,
Report::kWarning, allocation_space_, "unbalanced '%s' opens here",
Token::Str(opening_token)));
ReportErrors(error, script_, token_pos, "unbalanced '%s'",
Token::Str(token));
} else if (unexpected_token_found) {
ReportError(start_pos, "unterminated '%s'", Token::Str(opening_token));
}
}
void Parser::SkipBlock() {
ASSERT(CurrentToken() == Token::kLBRACE);
SkipToMatching();
}
// Skips tokens up to and including matching closing parenthesis.
void Parser::SkipToMatchingParenthesis() {
ASSERT(CurrentToken() == Token::kLPAREN);
SkipToMatching();
ASSERT(CurrentToken() == Token::kRPAREN);
ConsumeToken();
}
// Parses a parameter type as defined by the 'parameterTypeList' production.
void Parser::ParseParameterType(ParamList* params) {
TRACE_PARSER("ParseParameterType");
ParamDesc parameter;
parameter.has_explicit_type = true; // The type is required by the syntax.
// It is too early to resolve the type here, since it can be a result type
// referring to a not yet declared function type parameter.
parameter.type = &AbstractType::ZoneHandle(
Z, ParseTypeOrFunctionType(true, ClassFinalizer::kDoNotResolve));
// At this point, we must see an identifier for the parameter name, unless
// we are using the function type syntax (in which case the name is optional,
// unless we expect optional named parameters).
if (IsIdentifier()) {
parameter.name_pos = TokenPos();
parameter.name = CurrentLiteral();
ConsumeToken();
if (params->has_optional_named_parameters &&
(parameter.name->CharAt(0) == Library::kPrivateIdentifierStart)) {
ReportError(parameter.name_pos, "named parameter must not be private");
}
// Check for duplicate formal parameters.
const intptr_t num_existing_parameters =
params->num_fixed_parameters + params->num_optional_parameters;
for (intptr_t i = 0; i < num_existing_parameters; i++) {
ParamDesc& existing_parameter = (*params->parameters)[i];
if (existing_parameter.name->Equals(*parameter.name)) {
ReportError(parameter.name_pos, "duplicate formal parameter '%s'",
parameter.name->ToCString());
}
}
} else if (params->has_optional_named_parameters) {
ExpectIdentifier("parameter name expected");
} else {
parameter.name_pos = TokenPos();
parameter.name = &Symbols::NotNamed();
}
// The function type syntax does not allow the signature type syntax.
// No need to check for IsParameterPart().
if ((CurrentToken() == Token::kASSIGN) || (CurrentToken() == Token::kCOLON)) {
ReportError("parameter must not specify a default value");
} else {
if (params->has_optional_positional_parameters ||
params->has_optional_named_parameters) {
// Implicit default value is null.
params->num_optional_parameters++;
parameter.default_value = &Object::null_instance();
} else {
params->num_fixed_parameters++;
ASSERT(params->num_optional_parameters == 0);
}
}
if (params->implicitly_final) {
parameter.is_final = true;
}
params->parameters->Add(parameter);
if (parameter.is_covariant) {
params->has_covariant = true;
}
}
// Parses a formal parameter as defined by the 'formalParameterList' production.
void Parser::ParseFormalParameter(bool allow_explicit_default_value,
bool evaluate_metadata,
ParamList* params) {
TRACE_PARSER("ParseFormalParameter");
ParamDesc parameter;
bool var_seen = false;
bool final_seen = false;
bool this_seen = false;
if (evaluate_metadata && (CurrentToken() == Token::kAT)) {
parameter.metadata = &Array::ZoneHandle(Z, EvaluateMetadata());
} else {
SkipMetadata();
}
if (CurrentToken() == Token::kCOVARIANT &&
(LookaheadToken(1) == Token::kFINAL || LookaheadToken(1) == Token::kVAR ||
LookaheadToken(1) == Token::kVOID ||
Token::IsIdentifier(LookaheadToken(1)))) {
parameter.is_covariant = true;
ConsumeToken();
}
if (CurrentToken() == Token::kFINAL) {
ConsumeToken();
final_seen = true;
parameter.is_final = true;
} else if (CurrentToken() == Token::kVAR) {
ConsumeToken();
var_seen = true;
// The parameter type is the 'dynamic' type.
// If this is an initializing formal, its type will be set to the type of
// the respective field when the constructor is fully parsed.
parameter.type = &Object::dynamic_type();
}
if (CurrentToken() == Token::kTHIS) {
ConsumeToken();
ExpectToken(Token::kPERIOD);
this_seen = true;
parameter.is_field_initializer = true;
parameter.is_final = true;
}
if ((parameter.type == NULL) && (CurrentToken() == Token::kVOID)) {
ConsumeToken();
// This must later be changed to a closure type if we recognize
// a closure/function type parameter. We check this at the end
// of ParseFormalParameter.
parameter.type = &Object::void_type();
}
if ((parameter.type == NULL) || IsFunctionTypeSymbol()) {
// At this point, we must see an identifier for the type or the
// function parameter. The identifier may be 'Function'.
if (!IsIdentifier()) {
ReportError("parameter name or type expected");
}
// Lookahead to determine whether the next tokens are a return type
// followed by a parameter name.
bool found_type = false;
{
TokenPosScope saved_pos(this);
if (TryParseType(true)) {
if (IsIdentifier() || (CurrentToken() == Token::kTHIS)) {
found_type = true;
}
}
}
if (found_type) {
// The types of formal parameters are never ignored, even in unchecked
// mode, because they are part of the function type of closurized
// functions appearing in type tests with typedefs.
parameter.has_explicit_type = true;
// It is too early to resolve the type here, since it can be a result
// type referring to a not yet declared function type parameter.
if (parameter.type == NULL) {
parameter.type = &AbstractType::ZoneHandle(
Z, ParseTypeOrFunctionType(true, ClassFinalizer::kDoNotResolve));
} else {
parameter.type = &AbstractType::ZoneHandle(
Z,
ParseFunctionType(*parameter.type, ClassFinalizer::kDoNotResolve));
}
} else {
// If this is an initializing formal, its type will be set to the type
// of the respective field when the constructor is fully parsed.
parameter.type = &Object::dynamic_type();
}
}
if (!this_seen && (CurrentToken() == Token::kTHIS)) {
ConsumeToken();
ExpectToken(Token::kPERIOD);
this_seen = true;
parameter.is_field_initializer = true;
parameter.is_final = true;
}
// At this point, we must see an identifier for the parameter name.
parameter.name_pos = TokenPos();
parameter.name = ExpectIdentifier("parameter name expected");
if (parameter.is_field_initializer) {
params->has_field_initializer = true;
}
if (params->has_optional_named_parameters &&
(parameter.name->CharAt(0) == Library::kPrivateIdentifierStart)) {
ReportError(parameter.name_pos, "named parameter must not be private");
}
// Check for duplicate formal parameters.
const intptr_t num_existing_parameters =
params->num_fixed_parameters + params->num_optional_parameters;
for (intptr_t i = 0; i < num_existing_parameters; i++) {
ParamDesc& existing_parameter = (*params->parameters)[i];
if (existing_parameter.name->Equals(*parameter.name)) {
ReportError(parameter.name_pos, "duplicate formal parameter '%s'",
parameter.name->ToCString());
}
}
if (IsParameterPart()) {
// This parameter is probably a closure. If we saw the keyword 'var'
// or 'final', a closure is not legal here and we ignore the
// opening parens.
// TODO(hausner): The language spec appears to allow var and final
// in signature types when used with initializing formals:
// fieldFormalParameter:
// metadata finalConstVarOrType? this ‘.’ identifier formalParameterList? ;
if (!var_seen && !final_seen) {
// The parsed parameter type is actually the function result type.
AbstractType& result_type =
AbstractType::Handle(Z, parameter.type->raw());
// In top-level and mixin functions, the source may be in a different
// script than the script of the current class. However, we never reparse
// signature functions (except typedef signature functions), therefore
// we do not need to keep the correct script via a patch class. Use the
// actual current class as owner of the signature function.
Function& signature_function = Function::Handle(
Z,
Function::NewSignatureFunction(current_class(), innermost_function(),
TokenPosition::kNoSource));
innermost_function_ = signature_function.raw();
// Finish parsing the function type parameter.
if (CurrentToken() == Token::kLT) {
ParseTypeParameters(false); // Not parameterizing class, but function.
}
ASSERT(CurrentToken() == Token::kLPAREN);
ParamList func_params;
// Add implicit closure object parameter.
func_params.AddFinalParameter(TokenPos(), &Symbols::ClosureParameter(),
&Object::dynamic_type());
const bool use_function_type_syntax = false;
const bool allow_explicit_default_values = false;
const bool evaluate_metadata = false;
ParseFormalParameterList(use_function_type_syntax,
allow_explicit_default_values, evaluate_metadata,
&func_params);
signature_function.set_result_type(result_type);
// The result type may refer to the signature function's type parameters,
// but was not parsed in the scope of the signature function. Adjust.
result_type.SetScopeFunction(signature_function);
AddFormalParamsToFunction(&func_params, signature_function);
ASSERT(innermost_function().raw() == signature_function.raw());
innermost_function_ = signature_function.parent_function();
Type& signature_type =
Type::ZoneHandle(Z, signature_function.SignatureType());
// A signature type itself cannot be malformed or malbounded, only its
// signature function's result type or parameter types may be.
ASSERT(!signature_type.IsMalformed());
ASSERT(!signature_type.IsMalbounded());
// The type of the parameter is now the signature type.
parameter.type = &signature_type;
}
}
if ((CurrentToken() == Token::kASSIGN) || (CurrentToken() == Token::kCOLON)) {
if ((!params->has_optional_positional_parameters &&
!params->has_optional_named_parameters) ||
!allow_explicit_default_value) {
ReportError("parameter must not specify a default value");
}
if (params->has_optional_positional_parameters) {
ExpectToken(Token::kASSIGN);
} else {
ConsumeToken();
}
params->num_optional_parameters++;
params->has_explicit_default_values = true; // Also if explicitly NULL.
if (is_top_level_) {
// Skip default value parsing.
SkipExpr();
} else {
const Instance& const_value = ParseConstExpr()->literal();
parameter.default_value = &const_value;
}
} else {
if (params->has_optional_positional_parameters ||
params->has_optional_named_parameters) {
// Implicit default value is null.
params->num_optional_parameters++;
parameter.default_value = &Object::null_instance();
} else {
params->num_fixed_parameters++;
ASSERT(params->num_optional_parameters == 0);
}
}
if (params->implicitly_final) {
parameter.is_final = true;
}
params->parameters->Add(parameter);
if (parameter.is_covariant) {
params->has_covariant = true;
}
}
// Parses a sequence of normal or optional formal parameters.
void Parser::ParseFormalParameters(bool use_function_type_syntax,
bool allow_explicit_default_values,
bool evaluate_metadata,
ParamList* params) {
TRACE_PARSER("ParseFormalParameters");
// Optional parameter lists cannot be empty.
// The completely empty parameter list is handled before getting here.
bool has_seen_parameter = false;
do {
ConsumeToken();
if (!params->has_optional_positional_parameters &&
!params->has_optional_named_parameters &&
(CurrentToken() == Token::kLBRACK)) {
// End of normal parameters, start of optional positional parameters.
params->has_optional_positional_parameters = true;
return;
}
if (!params->has_optional_positional_parameters &&
!params->has_optional_named_parameters &&
(CurrentToken() == Token::kLBRACE)) {
// End of normal parameters, start of optional named parameters.
params->has_optional_named_parameters = true;
return;
}
Token::Kind terminator = params->has_optional_positional_parameters
? Token::kRBRACK
: params->has_optional_named_parameters
? Token::kRBRACE
: Token::kRPAREN;
if (has_seen_parameter && CurrentToken() == terminator) {
// Allow a trailing comma.
break;
}
if (use_function_type_syntax) {
ASSERT(!allow_explicit_default_values && !evaluate_metadata);
ParseParameterType(params);
} else {
ParseFormalParameter(allow_explicit_default_values, evaluate_metadata,
params);
}
has_seen_parameter = true;
} while (CurrentToken() == Token::kCOMMA);
}
void Parser::ParseFormalParameterList(bool use_function_type_syntax,
bool allow_explicit_default_values,
bool evaluate_metadata,
ParamList* params) {
TRACE_PARSER("ParseFormalParameterList");
ASSERT(CurrentToken() == Token::kLPAREN);
if (LookaheadToken(1) != Token::kRPAREN) {
// Parse fixed parameters.
ParseFormalParameters(use_function_type_syntax,
allow_explicit_default_values, evaluate_metadata,
params);
if (params->has_optional_positional_parameters ||
params->has_optional_named_parameters) {
// Parse optional parameters.
ParseFormalParameters(use_function_type_syntax,
allow_explicit_default_values, evaluate_metadata,
params);
if (params->has_optional_positional_parameters) {
CheckToken(Token::kRBRACK, "',' or ']' expected");
} else {
CheckToken(Token::kRBRACE, "',' or '}' expected");
}
ConsumeToken(); // ']' or '}'.
}
if ((CurrentToken() != Token::kRPAREN) &&
!params->has_optional_positional_parameters &&
!params->has_optional_named_parameters) {
ReportError("',' or ')' expected");
}
} else {
ConsumeToken();
}
ExpectToken(Token::kRPAREN);
}
String& Parser::ParseNativeDeclaration() {
TRACE_PARSER("ParseNativeDeclaration");
ASSERT(IsSymbol(Symbols::Native()));
ConsumeToken();
CheckToken(Token::kSTRING, "string literal expected");
String& native_name = *CurrentLiteral();
ConsumeToken();
return native_name;
}
// Resolve and return the dynamic function of the given name in the superclass.
// If it is not found, and resolve_getter is true, try to resolve a getter of
// the same name. If it is still not found, return noSuchMethod and
// set is_no_such_method to true..
RawFunction* Parser::GetSuperFunction(TokenPosition token_pos,
const String& name,
ArgumentListNode* arguments,
bool resolve_getter,
bool* is_no_such_method) {
const Class& super_class = Class::Handle(Z, current_class().SuperClass());
if (super_class.IsNull()) {
ReportError(token_pos, "class '%s' does not have a superclass",
String::Handle(Z, current_class().Name()).ToCString());
}
Function& super_func = Function::Handle(
Z, Resolver::ResolveDynamicAnyArgs(Z, super_class, name));
if (!super_func.IsNull() &&
!super_func.AreValidArguments(arguments->type_args_len(),
arguments->length(), arguments->names(),
NULL)) {
super_func = Function::null();
} else if (super_func.IsNull() && resolve_getter) {
const String& getter_name =
String::ZoneHandle(Z, Field::LookupGetterSymbol(name));
if (!getter_name.IsNull()) {
super_func = Resolver::ResolveDynamicAnyArgs(Z, super_class, getter_name);
ASSERT(super_func.IsNull() ||
(super_func.kind() != RawFunction::kImplicitStaticFinalGetter));
}
}
if (super_func.IsNull()) {
super_func = Resolver::ResolveDynamicAnyArgs(Z, super_class,
Symbols::NoSuchMethod());
ASSERT(!super_func.IsNull());
*is_no_such_method = true;
} else {
*is_no_such_method = false;
}
return super_func.raw();
}
StaticCallNode* Parser::BuildInvocationMirrorAllocation(
TokenPosition call_pos,
const String& function_name,
const ArgumentListNode& function_args,
const LocalVariable* temp_for_last_arg,
bool is_super_invocation) {
const TokenPosition args_pos = function_args.token_pos();
// Build arguments to the call to the static
// InvocationMirror._allocateInvocationMirror method.
ArgumentListNode* arguments = new ArgumentListNode(args_pos);
// The first argument is the original function name.
arguments->Add(new LiteralNode(args_pos, function_name));
// The second argument is the arguments descriptor of the original function.
const Array& args_descriptor = Array::ZoneHandle(
ArgumentsDescriptor::New(function_args.type_args_len(),
function_args.length(), function_args.names()));
arguments->Add(new LiteralNode(args_pos, args_descriptor));
// The third argument is an array containing the original function arguments,
// including the function type arguments and the receiver.
ArrayNode* args_array =
new ArrayNode(args_pos, Type::ZoneHandle(Type::ArrayType()));
// A type_args_var is allocated in the generated body of an implicit
// closure and in the generated body of a noSuchMethodDispatcher.
// Pass the type arguments to the invocation mirror as the first argument.
if (function_args.type_args_var() != NULL) {
ASSERT(function_args.type_arguments().IsNull());
args_array->AddElement(
new LoadLocalNode(args_pos, function_args.type_args_var()));
} else if (!function_args.type_arguments().IsNull()) {
args_array->AddElement(
new LiteralNode(args_pos, function_args.type_arguments()));
}
for (intptr_t i = 0; i < function_args.length(); i++) {
AstNode* arg = function_args.NodeAt(i);
if ((temp_for_last_arg != NULL) && (i == function_args.length() - 1)) {
LetNode* store_arg = new LetNode(arg->token_pos());
store_arg->AddNode(
new StoreLocalNode(arg->token_pos(), temp_for_last_arg, arg));
store_arg->AddNode(
new LoadLocalNode(arg->token_pos(), temp_for_last_arg));
args_array->AddElement(store_arg);
} else {
args_array->AddElement(arg);
}
}
arguments->Add(args_array);
arguments->Add(new LiteralNode(args_pos, Bool::Get(is_super_invocation)));
// Lookup the static InvocationMirror._allocateInvocationMirror method.
const Class& mirror_class =