| // 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/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/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_asserts, false, "Enable assert statements."); |
| DEFINE_FLAG(bool, enable_type_checks, false, "Enable type checks."); |
| DEFINE_FLAG(bool, trace_parser, false, "Trace parser operations."); |
| DEFINE_FLAG(bool, warn_mixin_typedef, true, "Warning on legacy mixin typedef."); |
| DEFINE_FLAG(bool, enable_async, false, "Enable async operations."); |
| DECLARE_FLAG(bool, error_on_bad_type); |
| DECLARE_FLAG(bool, throw_on_javascript_int_overflow); |
| DECLARE_FLAG(bool, warn_on_javascript_compatibility); |
| |
| static void CheckedModeHandler(bool value) { |
| FLAG_enable_asserts = value; |
| FLAG_enable_type_checks = value; |
| } |
| |
| // --enable-checked-mode and --checked both enable checked mode which is |
| // equivalent to setting --enable-asserts and --enable-type-checks. |
| DEFINE_FLAG_HANDLER(CheckedModeHandler, |
| enable_checked_mode, |
| "Enable checked mode."); |
| |
| DEFINE_FLAG_HANDLER(CheckedModeHandler, |
| checked, |
| "Enable checked mode."); |
| |
| |
| // Quick access to the locally defined isolate() method. |
| #define I (isolate()) |
| |
| |
| #if defined(DEBUG) |
| class TraceParser : public ValueObject { |
| public: |
| TraceParser(intptr_t token_pos, const Script& script, const char* msg) { |
| if (FLAG_trace_parser) { |
| // Skips tracing of bootstrap libraries. |
| if (script.HasSource()) { |
| intptr_t line, column; |
| script.GetTokenLocation(token_pos, &line, &column); |
| PrintIndent(); |
| OS::Print("%s (line %" Pd ", col %" Pd ", token %" Pd ")\n", |
| msg, line, column, token_pos); |
| } |
| indent_++; |
| } |
| } |
| ~TraceParser() { indent_--; } |
| private: |
| void PrintIndent() { |
| for (int i = 0; i < indent_; i++) { OS::Print(". "); } |
| } |
| static int indent_; |
| }; |
| |
| int TraceParser::indent_ = 0; |
| |
| #define TRACE_PARSER(s) \ |
| TraceParser __p__(this->TokenPos(), this->script_, s) |
| |
| #else // not DEBUG |
| #define TRACE_PARSER(s) |
| #endif // DEBUG |
| |
| |
| static RawTypeArguments* NewTypeArguments(const GrowableObjectArray& objs) { |
| const TypeArguments& a = |
| TypeArguments::Handle(TypeArguments::New(objs.Length())); |
| AbstractType& type = AbstractType::Handle(); |
| for (int i = 0; i < objs.Length(); i++) { |
| type ^= objs.At(i); |
| a.SetTypeAt(i, type); |
| } |
| // Cannot canonicalize TypeArgument yet as its types may not have been |
| // finalized yet. |
| return a.raw(); |
| } |
| |
| |
| LocalVariable* ParsedFunction::EnsureExpressionTemp() { |
| if (!has_expression_temp_var()) { |
| LocalVariable* temp = |
| new (I) 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() { |
| if (!has_finally_return_temp_var()) { |
| LocalVariable* temp = new(I) LocalVariable( |
| function_.token_pos(), |
| String::ZoneHandle(I, Symbols::New(":finally_ret_val")), |
| Type::ZoneHandle(I, Type::DynamicType())); |
| ASSERT(temp != NULL); |
| temp->set_is_final(); |
| 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::AddDeferredPrefix(const LibraryPrefix& prefix) { |
| 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(I, prefix.raw())); |
| } |
| |
| |
| void ParsedFunction::AllocateVariables() { |
| LocalScope* scope = node_sequence()->scope(); |
| const intptr_t num_fixed_params = function().num_fixed_parameters(); |
| const intptr_t num_opt_params = function().NumOptionalParameters(); |
| const intptr_t num_params = num_fixed_params + num_opt_params; |
| // Compute start indices to parameters and locals, and the number of |
| // parameters to copy. |
| if (num_opt_params == 0) { |
| // Parameter i will be at fp[kParamEndSlotFromFp + num_params - i] and |
| // local variable j will be at fp[kFirstLocalSlotFromFp - j]. |
| first_parameter_index_ = kParamEndSlotFromFp + num_params; |
| first_stack_local_index_ = kFirstLocalSlotFromFp; |
| num_copied_params_ = 0; |
| } else { |
| // Parameter i will be at fp[kFirstLocalSlotFromFp - i] and local variable |
| // j will be at fp[kFirstLocalSlotFromFp - num_params - j]. |
| first_parameter_index_ = kFirstLocalSlotFromFp; |
| first_stack_local_index_ = first_parameter_index_ - num_params; |
| num_copied_params_ = num_params; |
| } |
| |
| // Allocate parameters and local variables, either in the local frame or |
| // in the context(s). |
| LocalScope* context_owner = NULL; // No context needed yet. |
| bool found_captured_variables = false; |
| int next_free_frame_index = |
| scope->AllocateVariables(first_parameter_index_, |
| num_params, |
| first_stack_local_index_, |
| scope, |
| &context_owner, |
| &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; |
| }; |
| |
| |
| struct Parser::Block : public ZoneAllocated { |
| Block(Block* outer_block, LocalScope* local_scope, SequenceNode* seq) |
| : parent(outer_block), scope(local_scope), statements(seq) { |
| ASSERT(scope != NULL); |
| ASSERT(statements != NULL); |
| } |
| Block* parent; // Enclosing block, or NULL if outermost. |
| LocalScope* scope; |
| SequenceNode* statements; |
| }; |
| |
| |
| // Class which describes an inlined finally block which is used to generate |
| // inlined code for the finally blocks when there is an exit from a try |
| // block using 'return', 'break' or 'continue'. |
| class Parser::TryBlocks : public ZoneAllocated { |
| public: |
| TryBlocks(Block* try_block, TryBlocks* outer_try_block, intptr_t try_index) |
| : try_block_(try_block), |
| inlined_finally_nodes_(), |
| outer_try_block_(outer_try_block), |
| try_index_(try_index), |
| inside_catch_(false) { } |
| |
| TryBlocks* outer_try_block() const { return outer_try_block_; } |
| Block* try_block() const { return try_block_; } |
| intptr_t try_index() const { return try_index_; } |
| bool inside_catch() const { return inside_catch_; } |
| void enter_catch() { inside_catch_ = true; } |
| |
| void AddNodeForFinallyInlining(AstNode* node); |
| AstNode* GetNodeToInlineFinally(int index) { |
| if (0 <= index && index < inlined_finally_nodes_.length()) { |
| return inlined_finally_nodes_[index]; |
| } |
| return NULL; |
| } |
| |
| private: |
| Block* try_block_; |
| GrowableArray<AstNode*> inlined_finally_nodes_; |
| TryBlocks* outer_try_block_; |
| const intptr_t try_index_; |
| bool inside_catch_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TryBlocks); |
| }; |
| |
| |
| void Parser::TryBlocks::AddNodeForFinallyInlining(AstNode* node) { |
| inlined_finally_nodes_.Add(node); |
| } |
| |
| |
| // For parsing a compilation unit. |
| Parser::Parser(const Script& script, const Library& library, intptr_t token_pos) |
| : isolate_(Isolate::Current()), |
| script_(Script::Handle(isolate_, script.raw())), |
| tokens_iterator_(TokenStream::Handle(isolate_, script.tokens()), |
| token_pos), |
| token_kind_(Token::kILLEGAL), |
| current_block_(NULL), |
| is_top_level_(false), |
| await_is_keyword_(false), |
| current_member_(NULL), |
| allow_function_literals_(true), |
| parsed_function_(NULL), |
| innermost_function_(Function::Handle(isolate_)), |
| literal_token_(LiteralToken::Handle(isolate_)), |
| current_class_(Class::Handle(isolate_)), |
| library_(Library::Handle(isolate_, library.raw())), |
| try_blocks_list_(NULL), |
| last_used_try_index_(0), |
| unregister_pending_function_(false), |
| async_temp_scope_(NULL) { |
| ASSERT(tokens_iterator_.IsValid()); |
| ASSERT(!library.IsNull()); |
| } |
| |
| |
| // For parsing a function. |
| Parser::Parser(const Script& script, |
| ParsedFunction* parsed_function, |
| intptr_t token_position) |
| : isolate_(Isolate::Current()), |
| script_(Script::Handle(isolate_, script.raw())), |
| tokens_iterator_(TokenStream::Handle(isolate_, script.tokens()), |
| token_position), |
| token_kind_(Token::kILLEGAL), |
| current_block_(NULL), |
| is_top_level_(false), |
| await_is_keyword_(false), |
| current_member_(NULL), |
| allow_function_literals_(true), |
| parsed_function_(parsed_function), |
| innermost_function_(Function::Handle(isolate_, |
| parsed_function->function().raw())), |
| literal_token_(LiteralToken::Handle(isolate_)), |
| current_class_(Class::Handle(isolate_, |
| parsed_function->function().Owner())), |
| library_(Library::Handle(isolate_, Class::Handle( |
| isolate_, |
| parsed_function->function().origin()).library())), |
| try_blocks_list_(NULL), |
| last_used_try_index_(0), |
| unregister_pending_function_(false), |
| async_temp_scope_(NULL) { |
| ASSERT(tokens_iterator_.IsValid()); |
| ASSERT(!current_function().IsNull()); |
| if (FLAG_enable_type_checks) { |
| EnsureExpressionTemp(); |
| } |
| } |
| |
| |
| Parser::~Parser() { |
| if (unregister_pending_function_) { |
| const GrowableObjectArray& pending_functions = |
| GrowableObjectArray::Handle(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(I, script.tokens()), token_pos); |
| token_kind_ = Token::kILLEGAL; |
| } |
| |
| |
| bool Parser::SetAllowFunctionLiterals(bool value) { |
| bool current_value = allow_function_literals_; |
| allow_function_literals_ = value; |
| return current_value; |
| } |
| |
| |
| const Function& Parser::current_function() const { |
| ASSERT(parsed_function() != NULL); |
| return parsed_function()->function(); |
| } |
| |
| |
| const Function& Parser::innermost_function() const { |
| return innermost_function_; |
| } |
| |
| |
| const Class& Parser::current_class() const { |
| return current_class_; |
| } |
| |
| |
| void Parser::set_current_class(const Class& value) { |
| current_class_ = value.raw(); |
| } |
| |
| |
| void Parser::SetPosition(intptr_t position) { |
| if (position < TokenPos() && position != 0) { |
| CompilerStats::num_tokens_rewind += (TokenPos() - position); |
| } |
| tokens_iterator_.SetCurrentPosition(position); |
| token_kind_ = Token::kILLEGAL; |
| } |
| |
| |
| void Parser::ParseCompilationUnit(const Library& library, |
| const Script& script) { |
| Isolate* isolate = Isolate::Current(); |
| ASSERT(isolate->long_jump_base()->IsSafeToJump()); |
| TimerScope timer(FLAG_compiler_stats, &CompilerStats::parser_timer); |
| VMTagScope tagScope(isolate, 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) { |
| CompilerStats::num_tokens_lookahead++; |
| CompilerStats::num_token_checks++; |
| return tokens_iterator_.LookaheadTokenKind(num_tokens); |
| } |
| |
| |
| String* Parser::CurrentLiteral() const { |
| String& result = |
| String::ZoneHandle(I, 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(I, 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 Object* default_value; // NULL if not an optional parameter. |
| const Object* metadata; // NULL if no metadata or metadata not evaluated. |
| LocalVariable* var; // Scope variable allocated for this parameter. |
| bool is_final; |
| bool is_field_initializer; |
| bool has_explicit_type; |
| }; |
| |
| |
| struct ParamList { |
| ParamList() { |
| Clear(); |
| } |
| |
| void Clear() { |
| num_fixed_parameters = 0; |
| num_optional_parameters = 0; |
| has_optional_positional_parameters = false; |
| has_optional_named_parameters = false; |
| has_explicit_default_values = false; |
| has_field_initializer = false; |
| implicitly_final = false; |
| skipped = false; |
| this->parameters = new ZoneGrowableArray<ParamDesc>(); |
| } |
| |
| void AddFinalParameter(intptr_t name_pos, |
| const String* name, |
| const AbstractType* type) { |
| this->num_fixed_parameters++; |
| ParamDesc param; |
| param.name_pos = name_pos; |
| param.name = name; |
| param.is_final = true; |
| param.type = type; |
| this->parameters->Add(param); |
| } |
| |
| void AddReceiver(const AbstractType* receiver_type, intptr_t token_pos) { |
| ASSERT(this->parameters->is_empty()); |
| AddFinalParameter(token_pos, &Symbols::This(), receiver_type); |
| } |
| |
| |
| // Make the parameter variables visible/invisible. |
| // Field initializer parameters are always invisible. |
| void SetInvisible(bool invisible) { |
| const intptr_t num_params = parameters->length(); |
| for (int i = 0; i < num_params; i++) { |
| ParamDesc& param = (*parameters)[i]; |
| ASSERT(param.var != NULL); |
| if (!param.is_field_initializer) { |
| param.var->set_invisible(invisible); |
| } |
| } |
| } |
| |
| void SetImplicitlyFinal() { |
| implicitly_final = true; |
| } |
| |
| int num_fixed_parameters; |
| int num_optional_parameters; |
| bool has_optional_positional_parameters; |
| bool has_optional_named_parameters; |
| bool has_explicit_default_values; |
| bool has_field_initializer; |
| bool implicitly_final; |
| bool skipped; |
| ZoneGrowableArray<ParamDesc>* parameters; |
| }; |
| |
| |
| struct MemberDesc { |
| MemberDesc() { |
| Clear(); |
| } |
| void Clear() { |
| has_abstract = false; |
| has_external = false; |
| has_final = false; |
| has_const = false; |
| has_static = false; |
| has_var = false; |
| has_factory = false; |
| has_operator = false; |
| has_native = false; |
| metadata_pos = -1; |
| operator_token = Token::kILLEGAL; |
| type = NULL; |
| name_pos = 0; |
| name = NULL; |
| redirect_name = NULL; |
| dict_name = NULL; |
| params.Clear(); |
| kind = RawFunction::kRegularFunction; |
| field_ = NULL; |
| } |
| bool IsConstructor() const { |
| return (kind == RawFunction::kConstructor) && !has_static; |
| } |
| bool IsFactory() const { |
| return (kind == RawFunction::kConstructor) && has_static; |
| } |
| bool IsFactoryOrConstructor() const { |
| return (kind == RawFunction::kConstructor); |
| } |
| bool IsGetter() const { |
| return kind == RawFunction::kGetterFunction; |
| } |
| bool IsSetter() const { |
| return kind == RawFunction::kSetterFunction; |
| } |
| const char* ToCString() const { |
| if (field_ != NULL) { |
| return "field"; |
| } else if (IsConstructor()) { |
| return "constructor"; |
| } else if (IsFactory()) { |
| return "factory"; |
| } else if (IsGetter()) { |
| return "getter"; |
| } else if (IsSetter()) { |
| return "setter"; |
| } |
| return "method"; |
| } |
| String* DictName() const { |
| return (dict_name != NULL) ? dict_name : name; |
| } |
| bool has_abstract; |
| bool has_external; |
| bool has_final; |
| bool has_const; |
| bool has_static; |
| bool has_var; |
| bool has_factory; |
| bool has_operator; |
| bool has_native; |
| intptr_t metadata_pos; |
| Token::Kind operator_token; |
| const AbstractType* type; |
| intptr_t name_pos; |
| intptr_t decl_begin_pos; |
| String* name; |
| // For constructors: NULL or name of redirected to constructor. |
| String* redirect_name; |
| // dict_name is the name used for the class namespace, if it |
| // differs from 'name'. |
| // For constructors: NULL for unnamed constructor, |
| // identifier after classname for named constructors. |
| // For getters and setters: unmangled name. |
| String* dict_name; |
| ParamList params; |
| RawFunction::Kind kind; |
| // NULL for functions, field object for static or instance fields. |
| Field* field_; |
| }; |
| |
| |
| class ClassDesc : public ValueObject { |
| public: |
| ClassDesc(const Class& cls, |
| const String& cls_name, |
| bool is_interface, |
| intptr_t token_pos) |
| : clazz_(cls), |
| class_name_(cls_name), |
| token_pos_(token_pos), |
| functions_(GrowableObjectArray::Handle(GrowableObjectArray::New())), |
| fields_(GrowableObjectArray::Handle(GrowableObjectArray::New())) { |
| } |
| |
| void AddFunction(const Function& function) { |
| functions_.Add(function); |
| } |
| |
| const GrowableObjectArray& functions() const { |
| return functions_; |
| } |
| |
| void AddField(const Field& field) { |
| fields_.Add(field); |
| } |
| |
| const GrowableObjectArray& fields() const { |
| return fields_; |
| } |
| |
| const Class& clazz() const { |
| return clazz_; |
| } |
| |
| const String& class_name() const { |
| return class_name_; |
| } |
| |
| bool has_constructor() const { |
| Function& func = Function::Handle(); |
| for (int i = 0; i < functions_.Length(); i++) { |
| func ^= functions_.At(i); |
| if (func.kind() == RawFunction::kConstructor) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| intptr_t token_pos() const { |
| return token_pos_; |
| } |
| |
| void AddMember(const MemberDesc& member) { |
| members_.Add(member); |
| } |
| |
| const GrowableArray<MemberDesc>& members() const { |
| return members_; |
| } |
| |
| MemberDesc* LookupMember(const String& name) const { |
| for (int i = 0; i < members_.length(); i++) { |
| if (name.Equals(*members_[i].name)) { |
| return &members_[i]; |
| } |
| } |
| return NULL; |
| } |
| |
| private: |
| const Class& clazz_; |
| const String& class_name_; |
| intptr_t token_pos_; // Token index of "class" keyword. |
| GrowableObjectArray& functions_; |
| GrowableObjectArray& fields_; |
| GrowableArray<MemberDesc> members_; |
| }; |
| |
| |
| struct TopLevel { |
| TopLevel() : |
| fields(GrowableObjectArray::Handle(GrowableObjectArray::New())), |
| functions(GrowableObjectArray::Handle(GrowableObjectArray::New())) { } |
| |
| GrowableObjectArray& fields; |
| GrowableObjectArray& functions; |
| }; |
| |
| |
| static bool HasReturnNode(SequenceNode* seq) { |
| if (seq->length() == 0) { |
| return false; |
| } else if ((seq->length()) == 1 && |
| (seq->NodeAt(seq->length() - 1)->IsSequenceNode())) { |
| return HasReturnNode(seq->NodeAt(seq->length() - 1)->AsSequenceNode()); |
| } else { |
| return seq->NodeAt(seq->length() - 1)->IsReturnNode(); |
| } |
| } |
| |
| |
| void Parser::ParseClass(const Class& cls) { |
| if (!cls.is_synthesized_class()) { |
| Isolate* isolate = Isolate::Current(); |
| TimerScope timer(FLAG_compiler_stats, &CompilerStats::parser_timer); |
| ASSERT(isolate->long_jump_base()->IsSafeToJump()); |
| const Script& script = Script::Handle(isolate, cls.script()); |
| const Library& lib = Library::Handle(isolate, cls.library()); |
| Parser parser(script, lib, cls.token_pos()); |
| parser.ParseClassDefinition(cls); |
| } |
| } |
| |
| |
| RawObject* Parser::ParseFunctionParameters(const Function& func) { |
| ASSERT(!func.IsNull()); |
| Isolate* isolate = Isolate::Current(); |
| StackZone zone(isolate); |
| LongJumpScope jump; |
| if (setjmp(*jump.Set()) == 0) { |
| const Script& script = Script::Handle(isolate, func.script()); |
| const Class& owner = Class::Handle(isolate, func.Owner()); |
| ASSERT(!owner.IsNull()); |
| ParsedFunction* parsed_function = new ParsedFunction( |
| isolate, Function::ZoneHandle(isolate, 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(); |
| } |
| |
| |
| void Parser::ParseFunction(ParsedFunction* parsed_function) { |
| Isolate* isolate = Isolate::Current(); |
| TimerScope timer(FLAG_compiler_stats, &CompilerStats::parser_timer); |
| CompilerStats::num_functions_compiled++; |
| ASSERT(isolate->long_jump_base()->IsSafeToJump()); |
| ASSERT(parsed_function != NULL); |
| const Function& func = parsed_function->function(); |
| const Script& script = Script::Handle(isolate, func.script()); |
| Parser parser(script, parsed_function, func.token_pos()); |
| SequenceNode* node_sequence = NULL; |
| Array& default_parameter_values = Array::ZoneHandle(isolate, Array::null()); |
| switch (func.kind()) { |
| case RawFunction::kRegularFunction: |
| case RawFunction::kClosureFunction: |
| case RawFunction::kGetterFunction: |
| case RawFunction::kSetterFunction: |
| case RawFunction::kConstructor: |
| // The call to a redirecting factory is redirected. |
| ASSERT(!func.IsRedirectingFactory()); |
| if (!func.IsImplicitConstructor() && !func.is_async_closure()) { |
| parser.SkipFunctionPreamble(); |
| } |
| node_sequence = parser.ParseFunc(func, &default_parameter_values); |
| break; |
| case RawFunction::kImplicitGetter: |
| ASSERT(!func.is_static()); |
| node_sequence = parser.ParseInstanceGetter(func); |
| break; |
| case RawFunction::kImplicitSetter: |
| ASSERT(!func.is_static()); |
| node_sequence = parser.ParseInstanceSetter(func); |
| break; |
| case RawFunction::kImplicitStaticFinalGetter: |
| node_sequence = parser.ParseStaticFinalGetter(func); |
| CompilerStats::num_implicit_final_getters++; |
| break; |
| case RawFunction::kMethodExtractor: |
| node_sequence = parser.ParseMethodExtractor(func); |
| break; |
| case RawFunction::kNoSuchMethodDispatcher: |
| node_sequence = |
| parser.ParseNoSuchMethodDispatcher(func, &default_parameter_values); |
| break; |
| case RawFunction::kInvokeFieldDispatcher: |
| node_sequence = |
| parser.ParseInvokeFieldDispatcher(func, &default_parameter_values); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| |
| if (!HasReturnNode(node_sequence)) { |
| // Add implicit return node. |
| node_sequence->Add(new ReturnNode(func.end_token_pos())); |
| } |
| if (parsed_function->has_expression_temp_var()) { |
| node_sequence->scope()->AddVariable(parsed_function->expression_temp_var()); |
| } |
| 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); |
| } |
| } |
| |
| parsed_function->set_default_parameter_values(default_parameter_values); |
| } |
| |
| |
| RawObject* Parser::ParseMetadata(const Class& cls, intptr_t token_pos) { |
| Isolate* isolate = Isolate::Current(); |
| StackZone zone(isolate); |
| LongJumpScope jump; |
| if (setjmp(*jump.Set()) == 0) { |
| const Script& script = Script::Handle(isolate, 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(Function::New( |
| Symbols::At(), |
| RawFunction::kRegularFunction, |
| true, // is_static |
| false, // is_const |
| false, // is_abstract |
| false, // is_external |
| false, // is_native |
| cls, |
| token_pos)); |
| ParsedFunction* parsed_function = |
| new ParsedFunction(isolate, 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(isolate); |
| 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(I, GrowableObjectArray::New()); |
| 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(I, |
| 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(I); |
| 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(I, 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); |
| } |
| 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(I) ReturnNode(expr_pos, expr); |
| current_block_->statements->Add(ret); |
| return CloseBlock(); |
| } |
| |
| |
| ParsedFunction* Parser::ParseStaticFieldInitializer(const Field& field) { |
| ASSERT(field.is_static()); |
| ASSERT(field.value() == Object::transition_sentinel().raw()); |
| Isolate* isolate = Isolate::Current(); |
| |
| const Class& script_cls = Class::Handle(isolate, field.origin()); |
| const Script& script = Script::Handle(isolate, script_cls.script()); |
| |
| const String& field_name = String::Handle(isolate, field.name()); |
| String& init_name = String::Handle(isolate, |
| String::Concat(Symbols::InitPrefix(), field_name)); |
| init_name = Symbols::New(init_name); |
| |
| const Function& initializer = Function::ZoneHandle(isolate, |
| Function::New(init_name, |
| RawFunction::kRegularFunction, |
| true, // static |
| false, // !const |
| false, // !abstract |
| false, // !external |
| false, // !native |
| Class::Handle(field.owner()), |
| field.token_pos())); |
| initializer.set_result_type(AbstractType::Handle(isolate, 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_visible(false); |
| initializer.SetIsOptimizable(false); |
| initializer.set_is_inlinable(false); |
| |
| ParsedFunction* parsed_function = new ParsedFunction(isolate, initializer); |
| Parser parser(script, parsed_function, field.token_pos()); |
| |
| SequenceNode* body = parser.ParseStaticInitializer(); |
| parsed_function->SetNodeSequence(body); |
| parsed_function->set_default_parameter_values(Object::null_array()); |
| |
| 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(I, 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(I, func.Owner()); |
| const Field& field = |
| Field::ZoneHandle(I, field_class.LookupStaticField(field_name)); |
| |
| // Static final fields must have an initializer. |
| ExpectToken(Token::kASSIGN); |
| |
| const intptr_t expr_pos = TokenPos(); |
| if (field.is_const()) { |
| // We don't want to use ParseConstExpr() here because we don't want |
| // the constant folding code to create, compile and execute a code |
| // fragment to evaluate the expression. Instead, we just make sure |
| // the static const field initializer is a constant expression and |
| // leave the evaluation to the getter function. |
| AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades); |
| // This getter will only be called once at compile time. |
| if (expr->EvalConstExpr() == NULL) { |
| 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 (I) 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(I, 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); |
| ASSERT(IsIdentifier()); |
| const String& field_name = *CurrentLiteral(); |
| const Class& field_class = Class::Handle(I, func.Owner()); |
| const Field& field = |
| Field::ZoneHandle(I, field_class.LookupInstanceField(field_name)); |
| |
| LoadInstanceFieldNode* load_field = |
| new LoadInstanceFieldNode(ident_pos, load_receiver, field); |
| |
| ReturnNode* return_node = new ReturnNode(Scanner::kNoSourcePos, load_field); |
| current_block_->statements->Add(return_node); |
| return CloseBlock(); |
| } |
| |
| |
| // Create AstNodes for an implicit instance setter method: |
| // LoadLocalNode 0 ('this') |
| // LoadLocalNode 1 ('value') |
| // SetInstanceField (field_name); |
| // ReturnNode (void); |
| SequenceNode* Parser::ParseInstanceSetter(const Function& func) { |
| TRACE_PARSER("ParseInstanceSetter"); |
| // func.token_pos() points to the name of the field. |
| const intptr_t ident_pos = func.token_pos(); |
| const String& field_name = *CurrentLiteral(); |
| const Class& field_class = Class::ZoneHandle(I, func.Owner()); |
| const Field& field = |
| Field::ZoneHandle(I, field_class.LookupInstanceField(field_name)); |
| const AbstractType& field_type = AbstractType::ZoneHandle(I, 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(I, 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::ParseMethodExtractor(const Function& func) { |
| TRACE_PARSER("ParseMethodExtractor"); |
| ParamList params; |
| |
| const intptr_t ident_pos = func.token_pos(); |
| ASSERT(func.token_pos() == 0); |
| ASSERT(current_class().raw() == func.Owner()); |
| params.AddReceiver(ReceiverType(current_class()), ident_pos); |
| ASSERT(func.num_fixed_parameters() == 1); // Receiver. |
| ASSERT(!func.HasOptionalParameters()); |
| |
| // Build local scope for function and populate with the formal parameters. |
| OpenFunctionBlock(func); |
| AddFormalParamsToScope(¶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(I, func.extracted_method_closure()), |
| load_receiver, |
| NULL); |
| |
| ReturnNode* return_node = new ReturnNode(Scanner::kNoSourcePos, closure); |
| current_block_->statements->Add(return_node); |
| return CloseBlock(); |
| } |
| |
| |
| void Parser::BuildDispatcherScope(const Function& func, |
| const ArgumentsDescriptor& desc, |
| Array* default_values) { |
| ParamList params; |
| // Receiver first. |
| intptr_t token_pos = func.token_pos(); |
| params.AddReceiver(ReceiverType(current_class()), token_pos); |
| // Remaining positional parameters. |
| intptr_t i = 1; |
| for (; i < desc.PositionalCount(); ++i) { |
| ParamDesc p; |
| char name[64]; |
| OS::SNPrint(name, 64, ":p%" Pd, i); |
| p.name = &String::ZoneHandle(I, Symbols::New(name)); |
| p.type = &Type::ZoneHandle(I, 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(I, desc.NameAt(index)); |
| p.type = &Type::ZoneHandle(I, Type::DynamicType()); |
| p.default_value = &Object::null_object(); |
| params.parameters->Add(p); |
| params.num_optional_parameters++; |
| params.has_optional_named_parameters = true; |
| } |
| ASSERT(desc.NamedCount() == params.num_optional_parameters); |
| |
| SetupDefaultsForOptionalParams(¶ms, default_values); |
| |
| // Build local scope for function and populate with the formal parameters. |
| OpenFunctionBlock(func); |
| AddFormalParamsToScope(¶ms, current_block_->scope); |
| } |
| |
| SequenceNode* Parser::ParseNoSuchMethodDispatcher(const Function& func, |
| Array* default_values) { |
| TRACE_PARSER("ParseNoSuchMethodDispatcher"); |
| |
| ASSERT(func.IsNoSuchMethodDispatcher()); |
| intptr_t token_pos = func.token_pos(); |
| ASSERT(func.token_pos() == 0); |
| ASSERT(current_class().raw() == func.Owner()); |
| |
| ArgumentsDescriptor desc(Array::Handle(I, func.saved_args_desc())); |
| ASSERT(desc.Count() > 0); |
| |
| // Set up scope for this function. |
| BuildDispatcherScope(func, desc, default_values); |
| |
| // Receiver is local 0. |
| LocalScope* scope = current_block_->scope; |
| ArgumentListNode* func_args = new ArgumentListNode(token_pos); |
| for (intptr_t i = 0; i < desc.Count(); ++i) { |
| func_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i))); |
| } |
| |
| if (desc.NamedCount() > 0) { |
| const Array& arg_names = |
| Array::ZoneHandle(I, Array::New(desc.NamedCount())); |
| for (intptr_t i = 0; i < arg_names.Length(); ++i) { |
| arg_names.SetAt(i, String::Handle(I, desc.NameAt(i))); |
| } |
| func_args->set_names(arg_names); |
| } |
| |
| const String& func_name = String::ZoneHandle(I, 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(I, ArgumentsDescriptor::New(kNumArguments))); |
| Function& no_such_method = Function::ZoneHandle(I, |
| Resolver::ResolveDynamicForReceiverClass(Class::Handle(I, 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(I, 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, |
| Array* default_values) { |
| TRACE_PARSER("ParseInvokeFieldDispatcher"); |
| |
| ASSERT(func.IsInvokeFieldDispatcher()); |
| intptr_t token_pos = func.token_pos(); |
| ASSERT(func.token_pos() == 0); |
| ASSERT(current_class().raw() == func.Owner()); |
| |
| const Array& args_desc = Array::Handle(I, func.saved_args_desc()); |
| ArgumentsDescriptor desc(args_desc); |
| ASSERT(desc.Count() > 0); |
| |
| // Set up scope for this function. |
| BuildDispatcherScope(func, desc, default_values); |
| |
| // Receiver is local 0. |
| LocalScope* scope = current_block_->scope; |
| ArgumentListNode* no_args = new ArgumentListNode(token_pos); |
| LoadLocalNode* receiver = new LoadLocalNode(token_pos, scope->VariableAt(0)); |
| |
| const String& name = String::Handle(I, func.name()); |
| const String& getter_name = String::ZoneHandle(I, |
| Symbols::New(String::Handle(I, Field::GetterName(name)))); |
| InstanceCallNode* getter_call = new(I) InstanceCallNode( |
| token_pos, receiver, getter_name, no_args); |
| |
| // Pass arguments 1..n to the closure call. |
| ArgumentListNode* args = new(I) ArgumentListNode(token_pos); |
| const Array& names = Array::Handle( |
| I, 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(I) LoadLocalNode(token_pos, scope->VariableAt(i))); |
| intptr_t index = i - desc.PositionalCount(); |
| names.SetAt(index, String::Handle(I, desc.NameAt(index))); |
| } |
| args->set_names(names); |
| |
| const Class& owner = Class::Handle(I, func.Owner()); |
| ASSERT(!owner.IsNull()); |
| AstNode* result = NULL; |
| if (owner.IsSignatureClass() && name.Equals(Symbols::Call())) { |
| result = new ClosureCallNode(token_pos, getter_call, args); |
| } else { |
| result = BuildClosureCall(token_pos, getter_call, 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::SkipBlock() { |
| ASSERT(CurrentToken() == Token::kLBRACE); |
| GrowableArray<Token::Kind> token_stack(8); |
| // Adding the first kLBRACE here, because it will be consumed in the loop |
| // right away. |
| token_stack.Add(CurrentToken()); |
| const intptr_t block_start_pos = TokenPos(); |
| bool is_match = true; |
| bool unexpected_token_found = false; |
| Token::Kind token; |
| intptr_t token_pos; |
| do { |
| ConsumeToken(); |
| token = CurrentToken(); |
| token_pos = TokenPos(); |
| switch (token) { |
| case Token::kLBRACE: |
| case Token::kLPAREN: |
| case Token::kLBRACK: |
| token_stack.Add(token); |
| break; |
| case Token::kRBRACE: |
| is_match = token_stack.RemoveLast() == Token::kLBRACE; |
| break; |
| case Token::kRPAREN: |
| is_match = token_stack.RemoveLast() == Token::kLPAREN; |
| break; |
| case Token::kRBRACK: |
| is_match = token_stack.RemoveLast() == Token::kLBRACK; |
| break; |
| case Token::kEOS: |
| unexpected_token_found = true; |
| break; |
| default: |
| // nothing. |
| break; |
| } |
| } while (!token_stack.is_empty() && is_match && !unexpected_token_found); |
| if (!is_match) { |
| ReportError(token_pos, "unbalanced '%s'", Token::Str(token)); |
| } else if (unexpected_token_found) { |
| ReportError(block_start_pos, "unterminated block"); |
| } |
| } |
| |
| |
| void Parser::ParseFormalParameter(bool allow_explicit_default_value, |
| bool evaluate_metadata, |
| ParamList* params) { |
| TRACE_PARSER("ParseFormalParameter"); |
| ParamDesc parameter; |
| bool var_seen = false; |
| bool this_seen = false; |
| |
| if (evaluate_metadata && (CurrentToken() == Token::kAT)) { |
| parameter.metadata = &Array::ZoneHandle(I, 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(I, 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(I, 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(I, |
| 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(I, 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(I, 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(I, 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(I, |
| Function::New(*parameter.name, |
| RawFunction::kSignatureFunction, |
| /* is_static = */ false, |
| /* is_const = */ false, |
| /* is_abstract = */ false, |
| /* is_external = */ false, |
| /* is_native = */ false, |
| current_class(), |
| parameter.name_pos)); |
| signature_function.set_result_type(result_type); |
| AddFormalParamsToFunction(&func_params, signature_function); |
| const String& signature = String::Handle(I, |
| 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(I, |
| library_.LookupLocalClass(signature)); |
| if (signature_class.IsNull()) { |
| signature_class = Class::NewSignatureClass(signature, |
| signature_function, |
| script_, |
| parameter.name_pos); |
| // Record the function signature class in the current library, unless |
| // we are currently skipping a formal parameter list, in which case |
| // the signature class could remain unfinalized. |
| if (!params->skipped) { |
| library_.AddClass(signature_class); |
| } |
| } else { |
| signature_function.set_signature_class(signature_class); |
| } |
| ASSERT(signature_function.signature_class() == signature_class.raw()); |
| Type& signature_type = |
| Type::ZoneHandle(I, signature_class.SignatureType()); |
| if (!is_top_level_ && !signature_type.IsFinalized()) { |
| signature_type ^= ClassFinalizer::FinalizeType( |
| signature_class, signature_type, ClassFinalizer::kCanonicalize); |
| } |
| // A signature type itself cannot be malformed or malbounded, only its |
| // signature function's result type or parameter types may be. |
| ASSERT(!signature_type.IsMalformed()); |
| ASSERT(!signature_type.IsMalbounded()); |
| // The type of the parameter is now the signature type. |
| parameter.type = &signature_type; |
| } |
| } |
| |
| if ((CurrentToken() == Token::kASSIGN) || (CurrentToken() == Token::kCOLON)) { |
| if ((!params->has_optional_positional_parameters && |
| !params->has_optional_named_parameters) || |
| !allow_explicit_default_value) { |
| 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 Object& const_value = ParseConstExpr()->literal(); |
| parameter.default_value = &const_value; |
| } |
| } else { |
| if (params->has_optional_positional_parameters || |
| params->has_optional_named_parameters) { |
| // Implicit default value is null. |
| params->num_optional_parameters++; |
| parameter.default_value = &Object::null_object(); |
| } 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(IsLiteral("native")); |
| ConsumeToken(); |
| CheckToken(Token::kSTRING, "string literal expected"); |
| String& native_name = *CurrentLiteral(); |
| ConsumeToken(); |
| return native_name; |
| } |
| |
| |
| // Resolve and return the dynamic function of the given name in the superclass. |
| // If it is not found, and resolve_getter is true, try to resolve a getter of |
| // the same name. If it is still not found, return noSuchMethod and |
| // set is_no_such_method to true.. |
| RawFunction* Parser::GetSuperFunction(intptr_t token_pos, |
| const String& name, |
| ArgumentListNode* arguments, |
| bool resolve_getter, |
| bool* is_no_such_method) { |
| const Class& super_class = Class::Handle(I, current_class().SuperClass()); |
| if (super_class.IsNull()) { |
| ReportError(token_pos, "class '%s' does not have a superclass", |
| String::Handle(I, current_class().Name()).ToCString()); |
| } |
| Function& super_func = Function::Handle(I, |
| 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(I, 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(I, |
| GetSuperFunction(supercall_pos, |
| function_name, |
| arguments, |
| kResolveGetter, |
| &is_no_such_method)); |
| if (super_function.IsGetterFunction() || |
| super_function.IsImplicitGetterFunction()) { |
| const Class& super_class = |
| Class::ZoneHandle(I, current_class().SuperClass()); |
| AstNode* closure = new StaticGetterNode(supercall_pos, |
| LoadReceiver(supercall_pos), |
| /* is_super_getter */ true, |
| 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(I, 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(I, |
| 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(I, 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(I, Symbols::New(Token::Str(op))); |
| const bool kResolveGetter = false; |
| bool is_no_such_method = false; |
| const Function& super_operator = Function::ZoneHandle(I, |
| 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(I, 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(I, |
| 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(I, current_class().SuperClass()); |
| if (super_class.IsNull()) { |
| ReportError("class '%s' does not have a superclass", |
| String::Handle(I, current_class().Name()).ToCString()); |
| } |
| AstNode* implicit_argument = LoadReceiver(field_pos); |
| |
| const String& getter_name = |
| String::ZoneHandle(I, Field::GetterName(field_name)); |
| const Function& super_getter = Function::ZoneHandle(I, |
| Resolver::ResolveDynamicAnyArgs(super_class, getter_name)); |
| if (super_getter.IsNull()) { |
| const String& setter_name = |
| String::ZoneHandle(I, Field::SetterName(field_name)); |
| const Function& super_setter = Function::ZoneHandle(I, |
| 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(I, |
| Resolver::ResolveDynamicAnyArgs(super_class, field_name)); |
| if (!super_function.IsNull()) { |
| // In case CreateAssignmentNode is called later on this |
| // CreateImplicitClosureNode, it will be replaced by a StaticSetterNode. |
| return CreateImplicitClosureNode(super_function, |
| field_pos, |
| implicit_argument); |
| } |
| // No function or field exists of the specified field_name. |
| // Emit a StaticGetterNode anyway, so that noSuchMethod gets called. |
| } |
| } |
| return new StaticGetterNode( |
| field_pos, implicit_argument, true, super_class, field_name); |
| } |
| |
| |
| void Parser::GenerateSuperConstructorCall(const Class& cls, |
| intptr_t supercall_pos, |
| LocalVariable* receiver, |
| ArgumentListNode* forwarding_args) { |
| const Class& super_class = Class::Handle(I, 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(I, super_class.SuperClass()).IsObjectClass())) { |
| return; |
| } |
| String& super_ctor_name = String::Handle(I, super_class.Name()); |
| super_ctor_name = String::Concat(super_ctor_name, Symbols::Dot()); |
| |
| ArgumentListNode* arguments = new ArgumentListNode(supercall_pos); |
| // Implicit 'this' parameter is the first argument. |
| AstNode* implicit_argument = new LoadLocalNode(supercall_pos, receiver); |
| arguments->Add(implicit_argument); |
| // Implicit construction phase parameter is second argument. |
| AstNode* phase_parameter = |
| new LiteralNode(supercall_pos, |
| Smi::ZoneHandle(I, Smi::New(Function::kCtorPhaseAll))); |
| arguments->Add(phase_parameter); |
| |
| // If this is a super call in a forwarding constructor, add the user- |
| // defined arguments to the super call and adjust the the super |
| // constructor name to the respective named constructor if necessary. |
| if (forwarding_args != NULL) { |
| for (int i = 0; i < forwarding_args->length(); i++) { |
| arguments->Add(forwarding_args->NodeAt(i)); |
| } |
| String& ctor_name = String::Handle(I, current_function().name()); |
| String& class_name = String::Handle(I, cls.Name()); |
| if (ctor_name.Length() > class_name.Length() + 1) { |
| // Generating a forwarding call to a named constructor 'C.n'. |
| // Add the constructor name 'n' to the super constructor. |
| ctor_name = String::SubString(ctor_name, class_name.Length() + 1); |
| super_ctor_name = String::Concat(super_ctor_name, ctor_name); |
| } |
| } |
| |
| // Resolve super constructor function and check arguments. |
| const Function& super_ctor = Function::ZoneHandle(I, |
| super_class.LookupConstructor(super_ctor_name)); |
| if (super_ctor.IsNull()) { |
| ReportError(supercall_pos, |
| "unresolved implicit call to super constructor '%s()'", |
| String::Handle(I, 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(I); |
| if (!super_ctor.AreValidArguments(arguments->length(), |
| arguments->names(), |
| &error_message)) { |
| ReportError(supercall_pos, |
| "invalid arguments passed to super constructor '%s()': %s", |
| String::Handle(I, super_class.Name()).ToCString(), |
| error_message.ToCString()); |
| } |
| current_block_->statements->Add( |
| new StaticCallNode(supercall_pos, super_ctor, arguments)); |
| } |
| |
| |
| AstNode* Parser::ParseSuperInitializer(const Class& cls, |
| LocalVariable* receiver) { |
| TRACE_PARSER("ParseSuperInitializer"); |
| ASSERT(CurrentToken() == Token::kSUPER); |
| const intptr_t supercall_pos = TokenPos(); |
| ConsumeToken(); |
| const Class& super_class = Class::Handle(I, cls.SuperClass()); |
| ASSERT(!super_class.IsNull()); |
| String& ctor_name = String::Handle(I, super_class.Name()); |
| ctor_name = String::Concat(ctor_name, Symbols::Dot()); |
| if (CurrentToken() == Token::kPERIOD) { |
| ConsumeToken(); |
| ctor_name = String::Concat(ctor_name, |
| *ExpectIdentifier("constructor name expected")); |
| } |
| CheckToken(Token::kLPAREN, "parameter list expected"); |
| |
| ArgumentListNode* arguments = new ArgumentListNode(supercall_pos); |
| // 'this' parameter is the first argument to super class constructor. |
| AstNode* implicit_argument = new LoadLocalNode(supercall_pos, receiver); |
| arguments->Add(implicit_argument); |
| // Second implicit parameter is the construction phase. We optimistically |
| // assume that we can execute both the super initializer and the super |
| // constructor body. We may later change this to only execute the |
| // super initializer. |
| AstNode* phase_parameter = |
| new LiteralNode(supercall_pos, |
| Smi::ZoneHandle(I, 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(I, |
| 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(I); |
| 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(I, cls.LookupInstanceField(field_name)); |
| if (field.IsNull()) { |
| ReportError(field_pos, "unresolved reference to instance field '%s'", |
| field_name.ToCString()); |
| } |
| EnsureExpressionTemp(); |
| AstNode* instance = new(I) LoadLocalNode(field_pos, receiver); |
| AstNode* initializer = CheckDuplicateFieldInit(field_pos, |
| initialized_fields, instance, &field, init_expr); |
| if (initializer == NULL) { |
| initializer = |
| new(I) StoreInstanceFieldNode(field_pos, instance, field, init_expr); |
| } |
| return initializer; |
| } |
| |
| |
| void Parser::CheckFieldsInitialized(const Class& cls) { |
| const Array& fields = Array::Handle(I, cls.fields()); |
| Field& field = Field::Handle(I); |
| 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(I)); |
| } |
| } |
| |
| |
| 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(I, current_class().raw()); |
| const Library& saved_library = Library::Handle(I, library().raw()); |
| const Script& saved_script = Script::Handle(I, script().raw()); |
| const intptr_t saved_token_pos = TokenPos(); |
| |
| set_current_class(Class::Handle(I, field.origin())); |
| set_library(Library::Handle(I, current_class().library())); |
| SetScript(Script::Handle(I, current_class().script()), |
| field.token_pos()); |
| |
| ASSERT(IsIdentifier()); |
| ConsumeToken(); |
| ExpectToken(Token::kASSIGN); |
| AstNode* init_expr = NULL; |
| intptr_t expr_pos = TokenPos(); |
| if (field.is_const()) { |
| init_expr = ParseConstExpr(); |
| } else { |
| init_expr = ParseExpr(kAllowConst, kConsumeCascades); |
| if (init_expr->EvalConstExpr() != NULL) { |
| init_expr = |
| new LiteralNode(field.token_pos(), |
| EvaluateConstExpr(expr_pos, init_expr)); |
| } |
| } |
| set_current_class(saved_class); |
| set_library(saved_library); |
| SetScript(saved_script, saved_token_pos); |
| return init_expr; |
| } |
| |
| |
| void Parser::ParseInitializedInstanceFields(const Class& cls, |
| LocalVariable* receiver, |
| GrowableArray<Field*>* initialized_fields) { |
| TRACE_PARSER("ParseInitializedInstanceFields"); |
| const Array& fields = Array::Handle(I, cls.fields()); |
| Field& f = Field::Handle(I); |
| 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(I); |
| 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) { |
| init_expr = new LiteralNode(field.token_pos(), |
| EvaluateConstExpr(expr_pos, init_expr)); |
| } |
| } |
| } |
| ASSERT(init_expr != NULL); |
| AstNode* instance = new LoadLocalNode(field.token_pos(), receiver); |
| EnsureExpressionTemp(); |
| AstNode* field_init = |
| new StoreInstanceFieldNode(field.token_pos(), |
| instance, |
| field, |
| init_expr); |
| current_block_->statements->Add(field_init); |
| } |
| } |
| 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(I) 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(I) LiteralNode(init_pos, setter_name)); |
| |
| // Smi invocation_type. |
| const int invocation_type = |
| InvocationMirror::EncodeType(InvocationMirror::kDynamic, |
| InvocationMirror::kSetter); |
| nsm_args->Add(new(I) LiteralNode( |
| init_pos, Smi::ZoneHandle(I, Smi::New(invocation_type)))); |
| |
| // List arguments. |
| GrowableArray<AstNode*> setter_args; |
| setter_args.Add(init_value); |
| ArrayNode* setter_args_array = new(I) ArrayNode( |
| init_pos, |
| Type::ZoneHandle(I, 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(I) LiteralNode(init_pos, Array::ZoneHandle(I))); |
| |
| // List existingArgumentNames. |
| // There is no setter for the final field, thus there are |
| // no existing names. |
| nsm_args->Add(new(I) LiteralNode(init_pos, Array::ZoneHandle(I))); |
| |
| AstNode* nsm_call = |
| MakeStaticCall(Symbols::NoSuchMethodError(), |
| Library::PrivateCoreLibName(Symbols::ThrowNew()), |
| nsm_args); |
| |
| LetNode* let = new(I) 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(I, 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_seen = false; |
| if (CurrentToken() == Token::kCOLON) { |
| do { |
| ConsumeToken(); // Colon or comma. |
| AstNode* init_statement; |
| if (CurrentToken() == Token::kSUPER) { |
| if (super_init_seen) { |
| ReportError("duplicate call to super constructor"); |
| } |
| init_statement = ParseSuperInitializer(cls, receiver); |
| super_init_seen = true; |
| } else { |
| init_statement = ParseInitializer(cls, receiver, initialized_fields); |
| } |
| current_block_->statements->Add(init_statement); |
| } while (CurrentToken() == Token::kCOMMA); |
| } |
| if (!super_init_seen) { |
| // Generate implicit super() if we haven't seen an explicit super call |
| // or constructor redirection. |
| GenerateSuperConstructorCall(cls, TokenPos(), receiver, NULL); |
| } |
| CheckFieldsInitialized(cls); |
| } |
| |
| |
| void Parser::ParseConstructorRedirection(const Class& cls, |
| LocalVariable* receiver) { |
| TRACE_PARSER("ParseConstructorRedirection"); |
| ExpectToken(Token::kCOLON); |
| ASSERT(CurrentToken() == Token::kTHIS); |
| const intptr_t call_pos = TokenPos(); |
| ConsumeToken(); |
| String& ctor_name = String::Handle(I, cls.Name()); |
| |
| ctor_name = String::Concat(ctor_name, Symbols::Dot()); |
| if (CurrentToken() == Token::kPERIOD) { |
| ConsumeToken(); |
| ctor_name = String::Concat(ctor_name, |
| *ExpectIdentifier("constructor name expected")); |
| } |
| CheckToken(Token::kLPAREN, "parameter list expected"); |
| |
| ArgumentListNode* arguments = new ArgumentListNode(call_pos); |
| // 'this' parameter is the first argument to constructor. |
| AstNode* implicit_argument = new LoadLocalNode(call_pos, receiver); |
| arguments->Add(implicit_argument); |
| // Construction phase parameter is second argument. |
| LocalVariable* phase_param = LookupPhaseParameter(); |
| ASSERT(phase_param != NULL); |
| AstNode* phase_argument = new LoadLocalNode(call_pos, phase_param); |
| arguments->Add(phase_argument); |
| receiver->set_invisible(true); |
| ParseActualParameters(arguments, kAllowConst); |
| receiver->set_invisible(false); |
| // Resolve the constructor. |
| const Function& redirect_ctor = Function::ZoneHandle(I, |
| cls.LookupConstructor(ctor_name)); |
| if (redirect_ctor.IsNull()) { |
| ReportError(call_pos, "constructor '%s' not found", ctor_name.ToCString()); |
| } |
| String& error_message = String::Handle(I); |
| if (!redirect_ctor.AreValidArguments(arguments->length(), |
| arguments->names(), |
| &error_message)) { |
| ReportError(call_pos, |
| "invalid arguments passed to constructor '%s': %s", |
| ctor_name.ToCString(), |
| error_message.ToCString()); |
| } |
| current_block_->statements->Add( |
| new StaticCallNode(call_pos, redirect_ctor, arguments)); |
| } |
| |
| |
| SequenceNode* Parser::MakeImplicitConstructor(const Function& func) { |
| ASSERT(func.IsConstructor()); |
| ASSERT(func.Owner() == current_class().raw()); |
| const intptr_t ctor_pos = TokenPos(); |
| OpenFunctionBlock(func); |
| |
| LocalVariable* receiver = new LocalVariable( |
| Scanner::kNoSourcePos, Symbols::This(), *ReceiverType(current_class())); |
| current_block_->scope->InsertParameterAt(0, receiver); |
| |
| LocalVariable* phase_parameter = |
| new LocalVariable(Scanner::kNoSourcePos, |
| Symbols::PhaseParameter(), |
| Type::ZoneHandle(I, Type::SmiType())); |
| current_block_->scope->InsertParameterAt(1, phase_parameter); |
| |
| // Parse expressions of instance fields that have an explicit |
| // initializer expression. |
| // The receiver must not be visible to field initializer expressions. |
| receiver->set_invisible(true); |
| GrowableArray<Field*> initialized_fields; |
| ParseInitializedInstanceFields( |
| current_class(), receiver, &initialized_fields); |
| receiver->set_invisible(false); |
| |
| // If the class of this implicit constructor is a mixin application alias, |
| // it is a forwarding constructor of the aliased mixin application class. |
| // If the class of this implicit constructor is a mixin application class, |
| // it is a forwarding constructor of the mixin. The forwarding |
| // constructor initializes the instance fields that have initializer |
| // expressions and then calls the respective super constructor with |
| // the same name and number of parameters. |
| ArgumentListNode* forwarding_args = NULL; |
| if (current_class().is_mixin_app_alias() || |
| current_class().IsMixinApplication()) { |
| // At this point we don't support forwarding constructors |
| // that have optional parameters because we don't know the default |
| // values of the optional parameters. We would have to compile the super |
| // constructor to get the default values. Also, the spec is not clear |
| // whether optional parameters are even allowed in this situation. |
| // TODO(hausner): Remove this limitation if the language spec indeed |
| // allows optional parameters. |
| if (func.HasOptionalParameters()) { |
| const Class& super_class = Class::Handle(I, current_class().SuperClass()); |
| ReportError(ctor_pos, |
| "cannot generate an implicit mixin application constructor " |
| "forwarding to a super class constructor with optional " |
| "parameters; add a constructor without optional parameters " |
| "to class '%s' that redirects to the constructor with " |
| "optional parameters and invoke it via super from a " |
| "constructor of the class extending the mixin application", |
| String::Handle(I, super_class.Name()).ToCString()); |
| } |
| |
| // Prepare user-defined arguments to be forwarded to super call. |
| // The first user-defined argument is at position 2. |
| forwarding_args = new ArgumentListNode(Scanner::kNoSourcePos); |
| for (int i = 2; i < func.NumParameters(); i++) { |
| LocalVariable* param = new LocalVariable( |
| Scanner::kNoSourcePos, |
| String::ZoneHandle(I, func.ParameterNameAt(i)), |
| Type::ZoneHandle(I, Type::DynamicType())); |
| current_block_->scope->InsertParameterAt(i, param); |
| forwarding_args->Add(new LoadLocalNode(Scanner::kNoSourcePos, param)); |
| } |
| } |
| |
| GenerateSuperConstructorCall(current_class(), |
| Scanner::kNoSourcePos, |
| receiver, |
| forwarding_args); |
| CheckFieldsInitialized(current_class()); |
| |
| // Empty constructor body. |
| current_block_->statements->Add(new ReturnNode(Scanner::kNoSourcePos)); |
| SequenceNode* statements = CloseBlock(); |
| return statements; |
| } |
| |
| |
| void Parser::CheckRecursiveInvocation() { |
| const GrowableObjectArray& pending_functions = |
| GrowableObjectArray::Handle(I, |
| I->object_store()->pending_functions()); |
| for (int i = 0; i < pending_functions.Length(); i++) { |
| if (pending_functions.At(i) == current_function().raw()) { |
| const String& fname = |
| String::Handle(I, current_function().UserVisibleName()); |
| ReportError("circular dependency for function %s", fname.ToCString()); |
| } |
| } |
| ASSERT(!unregister_pending_function_); |
| pending_functions.Add(current_function()); |
| unregister_pending_function_ = true; |
| } |
| |
| |
| // Parser is at the opening parenthesis of the formal parameter declaration |
| // of function. Parse the formal parameters, initializers and code. |
| SequenceNode* Parser::ParseConstructor(const Function& func, |
| Array* default_parameter_values) { |
| TRACE_PARSER("ParseConstructor"); |
| ASSERT(func.IsConstructor()); |
| ASSERT(!func.IsFactory()); |
| ASSERT(!func.is_static()); |
| ASSERT(!func.IsLocalFunction()); |
| const Class& cls = Class::Handle(I, func.Owner()); |
| ASSERT(!cls.IsNull()); |
| |
| CheckRecursiveInvocation(); |
| |
| if (func.IsImplicitConstructor()) { |
| // Special case: implicit constructor. |
| // The parser adds an implicit default constructor when a class |
| // does not have any explicit constructor or factory (see |
| // Parser::AddImplicitConstructor). |
| // There is no source text to parse. We just build the |
| // sequence node by hand. |
| return MakeImplicitConstructor(func); |
| } |
| |
| OpenFunctionBlock(func); |
| ParamList params; |
| const bool allow_explicit_default_values = true; |
| ASSERT(CurrentToken() == Token::kLPAREN); |
| |
| // Add implicit receiver parameter which is passed the allocated |
| // but uninitialized instance to construct. |
| ASSERT(current_class().raw() == func.Owner()); |
| params.AddReceiver(ReceiverType(current_class()), func.token_pos()); |
| |
| // Add implicit parameter for construction phase. |
| params.AddFinalParameter( |
| TokenPos(), |
| &Symbols::PhaseParameter(), |
| &Type::ZoneHandle(I, Type::SmiType())); |
| |
| if (func.is_const()) { |
| params.SetImplicitlyFinal(); |
| } |
| ParseFormalParameterList(allow_explicit_default_values, false, ¶ms); |
| |
| SetupDefaultsForOptionalParams(¶ms, default_parameter_values); |
| ASSERT(AbstractType::Handle(I, func.result_type()).IsResolved()); |
| ASSERT(func.NumParameters() == params.parameters->length()); |
| |
| // Now populate function scope with the formal parameters. |
| AddFormalParamsToScope(¶ms, current_block_->scope); |
| |
| const bool is_redirecting_constructor = |
| (CurrentToken() == Token::kCOLON) && |
| ((LookaheadToken(1) == Token::kTHIS) && |
| ((LookaheadToken(2) == Token::kLPAREN) || |
| ((LookaheadToken(2) == Token::kPERIOD) && |
| (LookaheadToken(4) == Token::kLPAREN)))); |
| |
| GrowableArray<Field*> initialized_fields; |
| LocalVariable* receiver = (*params.parameters)[0].var; |
| OpenBlock(); |
| |
| // If this is not a redirecting constructor, initialize |
| // instance fields that have an explicit initializer expression. |
| if (!is_redirecting_constructor) { |
| // The formal parameter names must not be visible to the instance |
| // field initializer expressions, yet the parameters must be added to |
| // the scope so the expressions use the correct offsets for 'this' when |
| // storing values. We make the formal parameters temporarily invisible |
| // while parsing the instance field initializer expressions. |
| params.SetInvisible(true); |
| ParseInitializedInstanceFields(cls, receiver, &initialized_fields); |
| // Make the parameters (which are in the outer scope) visible again. |
| params.SetInvisible(false); |
| } |
| |
| // Turn formal field parameters into field initializers. |
| if (params.has_field_initializer) { |
| // First two parameters are implicit receiver and phase. |
| ASSERT(params.parameters->length() >= 2); |
| for (int i = 2; i < params.parameters->length(); i++) { |
| ParamDesc& param = (*params.parameters)[i]; |
| if (param.is_field_initializer) { |
| const String& field_name = *param.name; |
| Field& field = |
| Field::ZoneHandle(I, cls.LookupInstanceField(field_name)); |
| if (field.IsNull()) { |
| ReportError(param.name_pos, |
| "unresolved reference to instance field '%s'", |
| field_name.ToCString()); |
| } |
| if (is_redirecting_constructor) { |
| ReportError(param.name_pos, |
| "redirecting constructors may not have " |
| "initializing formal parameters"); |
| } |
| |
| if (!param.has_explicit_type) { |
| const AbstractType& field_type = |
| AbstractType::ZoneHandle(I, field.type()); |
| param.type = &field_type; |
| // Parameter type was already set to dynamic when parsing the class |
| // declaration: fix it. |
| func.SetParameterTypeAt(i, field_type); |
| } |
| |
| AstNode* instance = new LoadLocalNode(param.name_pos, receiver); |
| // Initializing formals cannot be used in the explicit initializer |
| // list, nor can they be used in the constructor body. |
| // Thus, they are set to be invisible when added to the scope. |
| LocalVariable* p = param.var; |
| ASSERT(p != NULL); |
| ASSERT(p->is_invisible()); |
| AstNode* value = new LoadLocalNode(param.name_pos, p); |
| EnsureExpressionTemp(); |
| AstNode* initializer = |
| CheckDuplicateFieldInit(param.name_pos, |
| &initialized_fields, |
| instance, |
| &field, |
| value); |
| if (initializer == NULL) { |
| initializer = new(I) StoreInstanceFieldNode( |
| param.name_pos, instance, field, value); |
| } |
| current_block_->statements->Add(initializer); |
| } |
| } |
| } |
| |
| if (is_redirecting_constructor) { |
| ParseConstructorRedirection(cls, receiver); |
| } else { |
| ParseInitializers(cls, receiver, &initialized_fields); |
| } |
| |
| SequenceNode* init_statements = CloseBlock(); |
| if (is_redirecting_constructor) { |
| // A redirecting super constructor simply passes the phase parameter on to |
| // the target which executes the corresponding phase. |
| current_block_->statements->Add(init_statements); |
| } else if (init_statements->length() > 0) { |
| // Generate guard around the initializer code. |
| LocalVariable* phase_param = LookupPhaseParameter(); |
| AstNode* phase_value = new |
| LoadLocalNode(Scanner::kNoSourcePos, phase_param); |
| AstNode* phase_check = new BinaryOpNode( |
| Scanner::kNoSourcePos, Token::kBIT_AND, phase_value, |
| new LiteralNode(Scanner::kNoSourcePos, |
| Smi::ZoneHandle(I, Smi::New(Function::kCtorPhaseInit)))); |
| AstNode* comparison = |
| new ComparisonNode(Scanner::kNoSourcePos, |
| Token::kNE_STRICT, |
| phase_check, |
| new LiteralNode(TokenPos(), |
| Smi::ZoneHandle(I, Smi::New(0)))); |
| AstNode* guarded_init_statements = |
| new IfNode(Scanner::kNoSourcePos, |
| comparison, |
| init_statements, |
| NULL); |
| current_block_->statements->Add(guarded_init_statements); |
| } |
| |
| // Parsing of initializers done. Now we parse the constructor body |
| // and add the implicit super call to the super constructor's body |
| // if necessary. |
| StaticCallNode* super_call = NULL; |
| // Look for the super initializer call in the sequence of initializer |
|