| // 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, ¶ms); |
| 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(¶ms, 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(¶ms, 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(¶ms, 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, ¶ms); |
| 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(¶ms, 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, ¶ms); |
| SetupDefaultsForOptionalParams(params); |
| } |
| |
| // Populate function scope with the formal parameters. |
| LocalScope* scope = current_block_->scope; |
| AddFormalParamsToScope(¶ms, 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(¶ms, 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(¶ms, 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 { |
| |