| // 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 "vm/bigint_operations.h" |
| #include "vm/class_finalizer.h" |
| #include "vm/compiler.h" |
| #include "vm/compiler_stats.h" |
| #include "vm/dart_api_impl.h" |
| #include "vm/dart_entry.h" |
| #include "vm/flags.h" |
| #include "vm/growable_array.h" |
| #include "vm/longjump.h" |
| #include "vm/native_entry.h" |
| #include "vm/object.h" |
| #include "vm/object_store.h" |
| #include "vm/resolver.h" |
| #include "vm/scopes.h" |
| #include "vm/symbols.h" |
| |
| namespace dart { |
| |
| DEFINE_FLAG(bool, enable_asserts, false, "Enable assert statements."); |
| DEFINE_FLAG(bool, enable_type_checks, false, "Enable type checks."); |
| DEFINE_FLAG(bool, trace_parser, false, "Trace parser operations."); |
| DEFINE_FLAG(bool, warning_as_error, false, "Treat warnings as errors."); |
| DEFINE_FLAG(bool, silent_warnings, false, "Silence warnings."); |
| |
| 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."); |
| |
| #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(); |
| } |
| |
| |
| static ThrowNode* GenerateRethrow(intptr_t token_pos, const Object& obj) { |
| const UnhandledException& excp = UnhandledException::Cast(obj); |
| Instance& exception = Instance::ZoneHandle(excp.exception()); |
| if (exception.IsNew()) { |
| exception ^= Object::Clone(exception, Heap::kOld); |
| } |
| Instance& stack_trace = Instance::ZoneHandle(excp.stacktrace()); |
| if (stack_trace.IsNew()) { |
| stack_trace ^= Object::Clone(stack_trace, Heap::kOld); |
| } |
| return new ThrowNode(token_pos, |
| new LiteralNode(token_pos, exception), |
| new LiteralNode(token_pos, stack_trace)); |
| } |
| |
| |
| LocalVariable* ParsedFunction::CreateExpressionTempVar(intptr_t token_pos) { |
| return new LocalVariable(token_pos, |
| Symbols::ExprTemp(), |
| Type::ZoneHandle(Type::DynamicType())); |
| } |
| |
| |
| LocalVariable* ParsedFunction::CreateArrayLiteralVar(intptr_t token_pos) { |
| return new LocalVariable(token_pos, |
| Symbols::ArrayLiteralVar(), |
| Type::ZoneHandle(Type::ArrayType())); |
| } |
| |
| |
| void ParsedFunction::SetNodeSequence(SequenceNode* node_sequence) { |
| ASSERT(node_sequence_ == NULL); |
| ASSERT(node_sequence != NULL); |
| node_sequence_ = node_sequence; |
| } |
| |
| |
| LocalVariable* ParsedFunction::GetSavedArgumentsDescriptorVar() const { |
| const int num_parameters = function().NumParameters(); |
| LocalScope* scope = node_sequence()->scope(); |
| if (scope->num_variables() > num_parameters) { |
| LocalVariable* saved_args_desc_var = scope->VariableAt(num_parameters); |
| ASSERT(saved_args_desc_var != NULL); |
| // The scope of the formal parameters may also contain at this position |
| // an alias for the saved arguments descriptor variable of the enclosing |
| // function (check its scope owner) or an internal variable such as the |
| // expression temp variable or the saved entry context variable (check its |
| // name). |
| if ((saved_args_desc_var->owner() == scope) && |
| saved_args_desc_var->name().StartsWith( |
| Symbols::SavedArgDescVarPrefix())) { |
| return saved_args_desc_var; |
| } |
| } |
| return NULL; |
| } |
| |
| |
| 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(); |
| 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[kLastParamSlotIndex + num_params - 1 - i] and |
| // local variable j will be at fp[kFirstLocalSlotIndex - j]. |
| ASSERT(GetSavedArgumentsDescriptorVar() == NULL); |
| first_parameter_index_ = kLastParamSlotIndex + num_params - 1; |
| first_stack_local_index_ = kFirstLocalSlotIndex; |
| num_copied_params_ = 0; |
| } else { |
| // Parameter i will be at fp[kFirstLocalSlotIndex - i] and local variable |
| // j will be at fp[kFirstLocalSlotIndex - num_params - j]. |
| // The saved arguments descriptor variable must be allocated similarly to |
| // a parameter, so that it gets both a frame slot and a context slot when |
| // captured. |
| if (GetSavedArgumentsDescriptorVar() != NULL) { |
| num_params += 1; |
| } |
| first_parameter_index_ = kFirstLocalSlotIndex; |
| 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. |
| int next_free_frame_index = |
| scope->AllocateVariables(first_parameter_index_, |
| num_params, |
| first_stack_local_index_, |
| scope, |
| &context_owner); |
| |
| // If this function allocates context variables, but none of its enclosing |
| // functions do, the context on entry is not linked as parent of the allocated |
| // context but saved on entry and restored on exit as to prevent memory leaks. |
| // Add and allocate a local variable to this purpose. |
| if (context_owner != NULL) { |
| const ContextScope& context_scope = |
| ContextScope::Handle(function().context_scope()); |
| if (context_scope.IsNull() || (context_scope.num_variables() == 0)) { |
| LocalVariable* context_var = |
| new LocalVariable(function().token_pos(), |
| Symbols::SavedEntryContextVar(), |
| Type::ZoneHandle(Type::DynamicType())); |
| context_var->set_index(next_free_frame_index--); |
| scope->AddVariable(context_var); |
| set_saved_entry_context_var(context_var); |
| } |
| } |
| |
| // 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 Parser::Block : public ZoneAllocated { |
| Block(Block* outer_block, LocalScope* local_scope, SequenceNode* seq) |
| : parent(outer_block), scope(local_scope), statements(seq) { |
| ASSERT(scope != NULL); |
| ASSERT(statements != NULL); |
| } |
| Block* parent; // Enclosing block, or NULL if outermost. |
| LocalScope* scope; |
| SequenceNode* statements; |
| }; |
| |
| |
| // Class which describes an inlined finally block which is used to generate |
| // inlined code for the finally blocks when there is an exit from a try |
| // block using 'return', 'break' or 'continue'. |
| class Parser::TryBlocks : public ZoneAllocated { |
| public: |
| TryBlocks(Block* try_block, TryBlocks* outer_try_block) |
| : try_block_(try_block), |
| inlined_finally_nodes_(), |
| outer_try_block_(outer_try_block) { } |
| |
| TryBlocks* outer_try_block() const { return outer_try_block_; } |
| Block* try_block() const { return try_block_; } |
| |
| void AddNodeForFinallyInlining(AstNode* node); |
| AstNode* GetNodeToInlineFinally(int index) { |
| if (0 <= index && index < inlined_finally_nodes_.length()) { |
| return inlined_finally_nodes_[index]; |
| } |
| return NULL; |
| } |
| |
| private: |
| Block* try_block_; |
| GrowableArray<AstNode*> inlined_finally_nodes_; |
| TryBlocks* outer_try_block_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TryBlocks); |
| }; |
| |
| |
| void Parser::TryBlocks::AddNodeForFinallyInlining(AstNode* node) { |
| inlined_finally_nodes_.Add(node); |
| } |
| |
| |
| // For parsing a compilation unit. |
| Parser::Parser(const Script& script, const Library& library) |
| : script_(Script::Handle(script.raw())), |
| tokens_iterator_(TokenStream::Handle(script.tokens()), 0), |
| token_kind_(Token::kILLEGAL), |
| current_block_(NULL), |
| is_top_level_(false), |
| current_member_(NULL), |
| allow_function_literals_(true), |
| parsed_function_(NULL), |
| innermost_function_(Function::Handle()), |
| current_class_(Class::Handle()), |
| library_(Library::Handle(library.raw())), |
| try_blocks_list_(NULL) { |
| ASSERT(tokens_iterator_.IsValid()); |
| ASSERT(!library.IsNull()); |
| } |
| |
| |
| // For parsing a function. |
| Parser::Parser(const Script& script, |
| ParsedFunction* parsed_function, |
| intptr_t token_position) |
| : script_(Script::Handle(script.raw())), |
| tokens_iterator_(TokenStream::Handle(script.tokens()), token_position), |
| token_kind_(Token::kILLEGAL), |
| current_block_(NULL), |
| is_top_level_(false), |
| current_member_(NULL), |
| allow_function_literals_(true), |
| parsed_function_(parsed_function), |
| innermost_function_(Function::Handle(parsed_function->function().raw())), |
| current_class_(Class::Handle(parsed_function->function().Owner())), |
| library_(Library::Handle(Class::Handle( |
| parsed_function->function().origin()).library())), |
| try_blocks_list_(NULL) { |
| ASSERT(tokens_iterator_.IsValid()); |
| ASSERT(!current_function().IsNull()); |
| if (FLAG_enable_type_checks) { |
| EnsureExpressionTemp(); |
| } |
| } |
| |
| |
| void Parser::SetScript(const Script & script, intptr_t token_pos) { |
| script_ = script.raw(); |
| tokens_iterator_.SetStream(TokenStream::Handle(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) { |
| ASSERT(Isolate::Current()->long_jump_base()->IsSafeToJump()); |
| TimerScope timer(FLAG_compiler_stats, &CompilerStats::parser_timer); |
| Parser parser(script, library); |
| parser.ParseTopLevel(); |
| } |
| |
| |
| Token::Kind Parser::CurrentToken() { |
| if (token_kind_ == Token::kILLEGAL) { |
| token_kind_ = tokens_iterator_.CurrentTokenKind(); |
| if (token_kind_ == Token::kERROR) { |
| ErrorMsg(TokenPos(), "%s", CurrentLiteral()->ToCString()); |
| } |
| } |
| CompilerStats::num_token_checks++; |
| return token_kind_; |
| } |
| |
| |
| 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(); |
| result = tokens_iterator_.CurrentLiteral(); |
| return &result; |
| } |
| |
| |
| RawDouble* Parser::CurrentDoubleLiteral() const { |
| LiteralToken& token = LiteralToken::Handle(); |
| token ^= tokens_iterator_.CurrentToken(); |
| ASSERT(token.kind() == Token::kDOUBLE); |
| return reinterpret_cast<RawDouble*>(token.value()); |
| } |
| |
| |
| RawInteger* Parser::CurrentIntegerLiteral() const { |
| LiteralToken& token = LiteralToken::Handle(); |
| token ^= tokens_iterator_.CurrentToken(); |
| ASSERT(token.kind() == Token::kINTEGER); |
| return reinterpret_cast<RawInteger*>(token.value()); |
| } |
| |
| |
| // A QualIdent is an optionally qualified identifier. |
| struct QualIdent { |
| QualIdent() { |
| Clear(); |
| } |
| void Clear() { |
| lib_prefix = NULL; |
| ident_pos = 0; |
| ident = NULL; |
| } |
| LibraryPrefix* lib_prefix; |
| intptr_t ident_pos; |
| String* ident; |
| }; |
| |
| |
| struct ParamDesc { |
| ParamDesc() |
| : type(NULL), |
| name_pos(0), |
| name(NULL), |
| default_value(NULL), |
| is_final(false), |
| is_field_initializer(false) { } |
| const AbstractType* type; |
| intptr_t name_pos; |
| const String* name; |
| const Object* default_value; // NULL if not an optional parameter. |
| bool is_final; |
| bool is_field_initializer; |
| }; |
| |
| |
| struct ParamList { |
| ParamList() { |
| Clear(); |
| } |
| |
| void Clear() { |
| num_fixed_parameters = 0; |
| num_optional_parameters = 0; |
| has_optional_positional_parameters = false; |
| has_optional_named_parameters = 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 Type* receiver_type) { |
| ASSERT(this->parameters->is_empty()); |
| AddFinalParameter(receiver_type->token_pos(), |
| &Symbols::This(), |
| receiver_type); |
| } |
| |
| 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_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; |
| operator_token = Token::kILLEGAL; |
| type = NULL; |
| name_pos = 0; |
| name = NULL; |
| redirect_name = NULL; |
| constructor_name = NULL; |
| params.Clear(); |
| kind = RawFunction::kRegularFunction; |
| } |
| bool IsConstructor() const { |
| return (kind == RawFunction::kConstructor) && !has_static; |
| } |
| bool IsFactory() const { |
| return (kind == RawFunction::kConstructor) && has_static; |
| } |
| bool IsFactoryOrConstructor() const { |
| return (kind == RawFunction::kConstructor); |
| } |
| bool IsGetter() const { |
| return kind == RawFunction::kGetterFunction; |
| } |
| bool IsSetter() const { |
| return kind == RawFunction::kSetterFunction; |
| } |
| bool has_abstract; |
| bool has_external; |
| bool has_final; |
| bool has_const; |
| bool has_static; |
| bool has_var; |
| bool has_factory; |
| bool has_operator; |
| Token::Kind operator_token; |
| const AbstractType* type; |
| intptr_t name_pos; |
| String* name; |
| // For constructors: NULL or name of redirected to constructor. |
| String* redirect_name; |
| // For constructors: NULL for unnamed constructor, |
| // identifier after classname for named constructors. |
| String* constructor_name; |
| ParamList params; |
| RawFunction::Kind kind; |
| }; |
| |
| |
| class ClassDesc : public ValueObject { |
| public: |
| ClassDesc(const Class& cls, |
| const String& cls_name, |
| bool is_interface, |
| intptr_t token_pos) |
| : clazz_(cls), |
| class_name_(cls_name), |
| token_pos_(token_pos), |
| functions_(GrowableObjectArray::Handle(GrowableObjectArray::New())), |
| fields_(GrowableObjectArray::Handle(GrowableObjectArray::New())) { |
| } |
| |
| // Parameter 'name' is the unmangled name, i.e. without the setter |
| // name mangling. |
| bool FunctionNameExists(const String& name, RawFunction::Kind kind) const { |
| // First check if a function or field of same name exists. |
| if ((kind != RawFunction::kSetterFunction) && FunctionExists(name)) { |
| return true; |
| } |
| // Now check whether there is a field and whether its implicit getter |
| // or setter collides with the name. |
| Field* field = LookupField(name); |
| if (field != NULL) { |
| if (kind == RawFunction::kSetterFunction) { |
| // It's ok to have an implicit getter, it does not collide with |
| // this setter function. |
| if (!field->is_final()) { |
| return true; |
| } |
| } else { |
| // The implicit getter of the field collides with the name. |
| return true; |
| } |
| } |
| |
| String& accessor_name = String::Handle(); |
| if (kind == RawFunction::kSetterFunction) { |
| // Check if a setter function of same name exists. |
| accessor_name = Field::SetterName(name); |
| if (FunctionExists(accessor_name)) { |
| return true; |
| } |
| } else { |
| // Check if a getter function of same name exists. |
| accessor_name = Field::GetterName(name); |
| if (FunctionExists(accessor_name)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool FieldNameExists(const String& name, bool check_setter) const { |
| // First check if a function or field of same name exists. |
| if (FunctionExists(name) || FieldExists(name)) { |
| return true; |
| } |
| // Now check if a getter/setter function of same name exists. |
| String& getter_name = String::Handle(Field::GetterName(name)); |
| if (FunctionExists(getter_name)) { |
| return true; |
| } |
| if (check_setter) { |
| String& setter_name = String::Handle(Field::SetterName(name)); |
| if (FunctionExists(setter_name)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void AddFunction(const Function& function) { |
| ASSERT(!FunctionExists(String::Handle(function.name()))); |
| functions_.Add(function); |
| } |
| |
| const GrowableObjectArray& functions() const { |
| return functions_; |
| } |
| |
| void AddField(const Field& field) { |
| ASSERT(!FieldExists(String::Handle(field.name()))); |
| fields_.Add(field); |
| } |
| |
| const GrowableObjectArray& fields() const { |
| return fields_; |
| } |
| |
| RawClass* clazz() const { |
| return clazz_.raw(); |
| } |
| |
| 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: |
| Field* LookupField(const String& name) const { |
| String& test_name = String::Handle(); |
| Field& field = Field::Handle(); |
| for (int i = 0; i < fields_.Length(); i++) { |
| field ^= fields_.At(i); |
| test_name = field.name(); |
| if (name.Equals(test_name)) { |
| return &field; |
| } |
| } |
| return NULL; |
| } |
| |
| bool FieldExists(const String& name) const { |
| return LookupField(name) != NULL; |
| } |
| |
| Function* LookupFunction(const String& name) const { |
| String& test_name = String::Handle(); |
| Function& func = Function::Handle(); |
| for (int i = 0; i < functions_.Length(); i++) { |
| func ^= functions_.At(i); |
| test_name = func.name(); |
| if (name.Equals(test_name)) { |
| return &func; |
| } |
| } |
| return NULL; |
| } |
| |
| bool FunctionExists(const String& name) const { |
| return LookupFunction(name) != NULL; |
| } |
| |
| 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::ParseFunction(ParsedFunction* parsed_function) { |
| TimerScope timer(FLAG_compiler_stats, &CompilerStats::parser_timer); |
| Isolate* isolate = Isolate::Current(); |
| 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()); |
| node_sequence = parser.ParseFunc(func, default_parameter_values); |
| break; |
| case RawFunction::kImplicitGetter: |
| ASSERT(!func.is_static()); |
| node_sequence = parser.ParseInstanceGetter(func); |
| break; |
| case RawFunction::kImplicitSetter: |
| ASSERT(!func.is_static()); |
| node_sequence = parser.ParseInstanceSetter(func); |
| break; |
| case RawFunction::kConstImplicitGetter: |
| node_sequence = parser.ParseStaticConstGetter(func); |
| break; |
| case RawFunction::kMethodExtractor: |
| node_sequence = parser.ParseMethodExtractor(func); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| |
| parsed_function->set_array_literal_var( |
| ParsedFunction::CreateArrayLiteralVar(func.token_pos())); |
| node_sequence->scope()->AddVariable(parsed_function->array_literal_var()); |
| |
| 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()); |
| } |
| if (parsed_function->has_saved_current_context_var()) { |
| node_sequence->scope()->AddVariable( |
| parsed_function->saved_current_context_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( |
| new LoadLocalNode(node_sequence->token_pos(), instantiator)); |
| } |
| } |
| |
| parsed_function->set_default_parameter_values(default_parameter_values); |
| } |
| |
| |
| // TODO(regis): Since a const variable is implicitly final, |
| // rename ParseStaticConstGetter to ParseStaticFinalGetter and |
| // rename kConstImplicitGetter to kImplicitFinalGetter. |
| SequenceNode* Parser::ParseStaticConstGetter(const Function& func) { |
| TRACE_PARSER("ParseStaticConstGetter"); |
| ParamList params; |
| ASSERT(func.num_fixed_parameters() == 0); // static. |
| ASSERT(!func.HasOptionalParameters()); |
| ASSERT(AbstractType::Handle(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(func.Owner()); |
| const Field& field = |
| Field::ZoneHandle(field_class.LookupStaticField(field_name)); |
| |
| // Static const fields must have an initializer. |
| ExpectToken(Token::kASSIGN); |
| |
| // We don't want to use ParseConstExpr() here because we don't want |
| // the constant folding code to create, compile and execute a code |
| // fragment to evaluate the expression. Instead, we just make sure |
| // the static const field initializer is a constant expression and |
| // leave the evaluation to the getter function. |
| const intptr_t expr_pos = TokenPos(); |
| AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades); |
| |
| if (field.is_const()) { |
| // This getter will only be called once at compile time. |
| if (expr->EvalConstExpr() == NULL) { |
| ErrorMsg(expr_pos, "initializer must be a 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. |
| // The following generated code lazily initializes the field: |
| // if (field.value === transition_sentinel) { |
| // field.value = null; |
| // throw("circular dependency in field initialization"); |
| // } |
| // if (field.value === sentinel) { |
| // field.value = transition_sentinel; |
| // field.value = expr; |
| // } |
| // return field.value; // Type check is executed here in checked mode. |
| |
| // Generate code checking for circular dependency in field initialization. |
| AstNode* compare_circular = new ComparisonNode( |
| ident_pos, |
| Token::kEQ_STRICT, |
| new LoadStaticFieldNode(ident_pos, field), |
| new LiteralNode(ident_pos, Object::transition_sentinel())); |
| // Set field to null prior to throwing exception, so that subsequent |
| // accesses to the field do not throw again, since initializers should only |
| // be executed once. |
| SequenceNode* report_circular = new SequenceNode(ident_pos, NULL); |
| report_circular->Add( |
| new StoreStaticFieldNode( |
| ident_pos, |
| field, |
| new LiteralNode(ident_pos, Instance::ZoneHandle()))); |
| // TODO(regis): Exception to throw is not specified by spec. |
| const String& circular_error = String::ZoneHandle( |
| Symbols::New("circular dependency in field initialization")); |
| report_circular->Add( |
| new ThrowNode(ident_pos, |
| new LiteralNode(ident_pos, circular_error), |
| NULL)); |
| AstNode* circular_check = |
| new IfNode(ident_pos, compare_circular, report_circular, NULL); |
| current_block_->statements->Add(circular_check); |
| |
| // Generate code checking for uninitialized field. |
| AstNode* compare_uninitialized = new ComparisonNode( |
| ident_pos, |
| Token::kEQ_STRICT, |
| new LoadStaticFieldNode(ident_pos, field), |
| new LiteralNode(ident_pos, Object::sentinel())); |
| SequenceNode* initialize_field = new SequenceNode(ident_pos, NULL); |
| initialize_field->Add( |
| new StoreStaticFieldNode( |
| ident_pos, |
| field, |
| new LiteralNode(ident_pos, Object::transition_sentinel()))); |
| // TODO(hausner): If evaluation of the field value throws an exception, |
| // we leave the field value as 'transition_sentinel', which is wrong. |
| // A second reference to the field later throws a circular dependency |
| // exception. The field should instead be set to null after an exception. |
| initialize_field->Add(new StoreStaticFieldNode(ident_pos, field, expr)); |
| AstNode* uninitialized_check = |
| new IfNode(ident_pos, compare_uninitialized, initialize_field, NULL); |
| current_block_->statements->Add(uninitialized_check); |
| |
| // Generate code returning the field value. |
| 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. |
| intptr_t ident_pos = func.token_pos(); |
| ASSERT(current_class().raw() == func.Owner()); |
| params.AddReceiver(ReceiverType(ident_pos)); |
| ASSERT(func.num_fixed_parameters() == 1); // receiver. |
| ASSERT(!func.HasOptionalParameters()); |
| ASSERT(AbstractType::Handle(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(func.Owner()); |
| const Field& field = |
| Field::ZoneHandle(field_class.LookupInstanceField(field_name)); |
| |
| LoadInstanceFieldNode* load_field = |
| new LoadInstanceFieldNode(ident_pos, load_receiver, field); |
| |
| ReturnNode* return_node = new ReturnNode(ident_pos, load_field); |
| current_block_->statements->Add(return_node); |
| return CloseBlock(); |
| } |
| |
| |
| // Create AstNodes for an implicit instance setter method: |
| // LoadLocalNode 0 ('this') |
| // LoadLocalNode 1 ('value') |
| // SetInstanceField (field_name); |
| // ReturnNode (void); |
| SequenceNode* Parser::ParseInstanceSetter(const Function& func) { |
| TRACE_PARSER("ParseInstanceSetter"); |
| // func.token_pos() points to the name of the field. |
| intptr_t ident_pos = func.token_pos(); |
| const String& field_name = *CurrentLiteral(); |
| const Class& field_class = Class::ZoneHandle(func.Owner()); |
| const Field& field = |
| Field::ZoneHandle(field_class.LookupInstanceField(field_name)); |
| const AbstractType& field_type = AbstractType::ZoneHandle(field.type()); |
| |
| ParamList params; |
| ASSERT(current_class().raw() == func.Owner()); |
| params.AddReceiver(ReceiverType(ident_pos)); |
| params.AddFinalParameter(ident_pos, |
| &Symbols::Value(), |
| &field_type); |
| ASSERT(func.num_fixed_parameters() == 2); // receiver, value. |
| ASSERT(!func.HasOptionalParameters()); |
| ASSERT(AbstractType::Handle(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(ident_pos)); |
| 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(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(func.extracted_method_closure()), |
| load_receiver, |
| NULL); |
| |
| ReturnNode* return_node = new ReturnNode(ident_pos, closure); |
| current_block_->statements->Add(return_node); |
| return CloseBlock(); |
| } |
| |
| |
| void Parser::SkipBlock() { |
| ASSERT(CurrentToken() == Token::kLBRACE); |
| GrowableArray<Token::Kind> token_stack(8); |
| const intptr_t block_start_pos = TokenPos(); |
| bool is_match = true; |
| bool unexpected_token_found = false; |
| Token::Kind token; |
| intptr_t token_pos; |
| do { |
| 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; |
| } |
| ConsumeToken(); |
| } while (!token_stack.is_empty() && is_match && !unexpected_token_found); |
| if (!is_match) { |
| ErrorMsg(token_pos, "unbalanced '%s'", Token::Str(token)); |
| } else if (unexpected_token_found) { |
| ErrorMsg(block_start_pos, "unterminated block"); |
| } |
| } |
| |
| |
| void Parser::ParseFormalParameter(bool allow_explicit_default_value, |
| ParamList* params) { |
| TRACE_PARSER("ParseFormalParameter"); |
| ParamDesc parameter; |
| bool var_seen = false; |
| bool this_seen = false; |
| |
| 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. |
| parameter.type = &Type::ZoneHandle(Type::DynamicType()); |
| } |
| if (CurrentToken() == Token::kTHIS) { |
| ConsumeToken(); |
| ExpectToken(Token::kPERIOD); |
| this_seen = true; |
| parameter.is_field_initializer = true; |
| } |
| if (params->implicitly_final) { |
| parameter.is_final = true; |
| } |
| if ((parameter.type == NULL) && (CurrentToken() == Token::kVOID)) { |
| ConsumeToken(); |
| // This must later be changed to a closure type if we recognize |
| // a closure/function type parameter. We check this at the end |
| // of ParseFormalParameter. |
| parameter.type = &Type::ZoneHandle(Type::VoidType()); |
| } |
| if (parameter.type == NULL) { |
| // At this point, we must see an identifier for the type or the |
| // function parameter. |
| if (!IsIdentifier()) { |
| ErrorMsg("parameter name or type expected"); |
| } |
| // We have not seen a parameter type yet, so we check if the next |
| // identifier could represent a type before parsing it. |
| Token::Kind follower = LookaheadToken(1); |
| // We have an identifier followed by a 'follower' token. |
| // We either parse a type or assume that no type is specified. |
| if ((follower == Token::kLT) || // Parameterized type. |
| (follower == Token::kPERIOD) || // Qualified class name of type. |
| 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.type = &AbstractType::ZoneHandle( |
| ParseType(is_top_level_ ? ClassFinalizer::kTryResolve : |
| ClassFinalizer::kCanonicalize)); |
| } else { |
| parameter.type = &Type::ZoneHandle(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; |
| } |
| |
| // 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)) { |
| ErrorMsg(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(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(Type::DynamicType())); |
| |
| const bool no_explicit_default_values = false; |
| ParseFormalParameterList(no_explicit_default_values, &func_params); |
| |
| // The field 'is_static' has no meaning for signature functions. |
| const Function& signature_function = Function::Handle( |
| Function::New(*parameter.name, |
| RawFunction::kSignatureFunction, |
| /* is_static = */ false, |
| /* is_const = */ false, |
| /* is_abstract = */ false, |
| /* is_external = */ false, |
| current_class(), |
| parameter.name_pos)); |
| signature_function.set_result_type(result_type); |
| AddFormalParamsToFunction(&func_params, signature_function); |
| const String& signature = String::Handle(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( |
| 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(signature_class.SignatureType()); |
| if (!is_top_level_ && !signature_type.IsFinalized()) { |
| signature_type ^= ClassFinalizer::FinalizeType( |
| signature_class, signature_type, ClassFinalizer::kCanonicalize); |
| } |
| // 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) { |
| ErrorMsg("parameter must not specify a default value"); |
| } |
| if (params->has_optional_positional_parameters) { |
| ExpectToken(Token::kASSIGN); |
| } else { |
| ExpectToken(Token::kCOLON); |
| } |
| params->num_optional_parameters++; |
| if (is_top_level_) { |
| // Skip default value parsing. |
| SkipExpr(); |
| } else { |
| const Object& const_value = ParseConstExpr()->literal(); |
| parameter.default_value = &const_value; |
| } |
| } else { |
| if (params->has_optional_positional_parameters || |
| params->has_optional_named_parameters) { |
| // Implicit default value is null. |
| params->num_optional_parameters++; |
| parameter.default_value = &Object::ZoneHandle(); |
| } else { |
| params->num_fixed_parameters++; |
| ASSERT(params->num_optional_parameters == 0); |
| } |
| } |
| if (parameter.type->IsVoidType()) { |
| ErrorMsg("parameter '%s' may not be 'void'", parameter.name->ToCString()); |
| } |
| params->parameters->Add(parameter); |
| } |
| |
| |
| // Parses a sequence of normal or optional formal parameters. |
| void Parser::ParseFormalParameters(bool allow_explicit_default_values, |
| 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, params); |
| } while (CurrentToken() == Token::kCOMMA); |
| } |
| |
| |
| void Parser::ParseFormalParameterList(bool allow_explicit_default_values, |
| ParamList* params) { |
| TRACE_PARSER("ParseFormalParameterList"); |
| ASSERT(CurrentToken() == Token::kLPAREN); |
| |
| if (LookaheadToken(1) != Token::kRPAREN) { |
| // Parse fixed parameters. |
| ParseFormalParameters(allow_explicit_default_values, params); |
| if (params->has_optional_positional_parameters || |
| params->has_optional_named_parameters) { |
| // Parse optional parameters. |
| ParseFormalParameters(allow_explicit_default_values, params); |
| if (params->has_optional_positional_parameters) { |
| if (CurrentToken() != Token::kRBRACK) { |
| ErrorMsg("',' or ']' expected"); |
| } |
| } else { |
| if (CurrentToken() != Token::kRBRACE) { |
| ErrorMsg("',' or '}' expected"); |
| } |
| } |
| ConsumeToken(); // ']' or '}'. |
| } |
| if ((CurrentToken() != Token::kRPAREN) && |
| !params->has_optional_positional_parameters && |
| !params->has_optional_named_parameters) { |
| ErrorMsg("',' or ')' expected"); |
| } |
| } else { |
| ConsumeToken(); |
| } |
| ExpectToken(Token::kRPAREN); |
| } |
| |
| |
| String& Parser::ParseNativeDeclaration() { |
| TRACE_PARSER("ParseNativeDeclaration"); |
| ASSERT(IsLiteral("native")); |
| ConsumeToken(); |
| if (CurrentToken() != Token::kSTRING) { |
| ErrorMsg("string literal expected"); |
| } |
| String& native_name = *CurrentLiteral(); |
| ConsumeToken(); |
| ExpectSemicolon(); |
| return native_name; |
| } |
| |
| |
| // 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(current_class().SuperClass()); |
| if (super_class.IsNull()) { |
| ErrorMsg(token_pos, "class '%s' does not have a superclass", |
| String::Handle(current_class().Name()).ToCString()); |
| } |
| Function& super_func = Function::Handle( |
| 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(Field::GetterName(name)); |
| super_func = Resolver::ResolveDynamicAnyArgs(super_class, getter_name); |
| ASSERT(super_func.IsNull() || |
| (super_func.kind() != RawFunction::kConstImplicitGetter)); |
| } |
| 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(); |
| } |
| |
| |
| // Lookup class in the core lib which also contains various VM |
| // helper methods and classes. Allow look up of private classes. |
| static RawClass* LookupCoreClass(const String& class_name) { |
| const Library& core_lib = Library::Handle(Library::CoreLibrary()); |
| String& name = String::Handle(class_name.raw()); |
| if (class_name.CharAt(0) == Scanner::kPrivateIdentifierStart) { |
| // Private identifiers are mangled on a per script basis. |
| name = String::Concat(name, String::Handle(core_lib.private_key())); |
| name = Symbols::New(name); |
| } |
| return core_lib.LookupClass(name); |
| } |
| |
| |
| static const String& PrivateCoreLibName(const String& str) { |
| const Library& core_lib = Library::Handle(Library::CoreLibrary()); |
| const String& private_name = String::ZoneHandle(core_lib.PrivateName(str)); |
| return private_name; |
| } |
| |
| |
| LocalVariable* Parser::BuildArrayTempLocal(intptr_t token_pos) { |
| char name[64]; |
| OS::SNPrint(name, 64, ":arrlit%"Pd, token_pos); |
| LocalVariable* temp = |
| new LocalVariable(token_pos, |
| String::ZoneHandle(Symbols::New(name)), |
| Type::ZoneHandle(Type::ArrayType())); |
| current_block_->scope->AddVariable(temp); |
| return temp; |
| } |
| |
| |
| StaticCallNode* Parser::BuildInvocationMirrorAllocation( |
| intptr_t call_pos, |
| const String& function_name, |
| const ArgumentListNode& function_args) { |
| 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()), |
| *BuildArrayTempLocal(call_pos)); |
| for (intptr_t i = 0; i < function_args.length(); i++) { |
| args_array->AddElement(function_args.NodeAt(i)); |
| } |
| arguments->Add(args_array); |
| // Lookup the static InvocationMirror._allocateInvocationMirror method. |
| const Class& mirror_class = |
| Class::Handle(LookupCoreClass(Symbols::InvocationMirror())); |
| ASSERT(!mirror_class.IsNull()); |
| const Function& allocation_function = Function::ZoneHandle( |
| mirror_class.LookupStaticFunction( |
| 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) { |
| 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)); |
| 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( |
| GetSuperFunction(supercall_pos, |
| function_name, |
| arguments, |
| kResolveGetter, |
| &is_no_such_method)); |
| if (super_function.IsGetterFunction() || |
| super_function.IsImplicitGetterFunction()) { |
| const Class& super_class = Class::ZoneHandle(current_class().SuperClass()); |
| AstNode* closure = new StaticGetterNode(supercall_pos, |
| LoadReceiver(supercall_pos), |
| /* is_super_getter */ true, |
| super_class, |
| function_name); |
| EnsureSavedCurrentContext(); |
| // '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 new ClosureCallNode(supercall_pos, closure, closure_arguments); |
| } |
| if (is_no_such_method) { |
| arguments = BuildNoSuchMethodArguments( |
| supercall_pos, function_name, *arguments); |
| } |
| return new StaticCallNode(supercall_pos, super_function, arguments); |
| } |
| |
| |
| // Simple test if a node is side effect free. |
| static bool IsSimpleLocalOrLiteralNode(AstNode* node) { |
| if (node->IsLiteralNode()) { |
| return true; |
| } |
| if (node->IsLoadLocalNode() && !node->AsLoadLocalNode()->HasPseudo()) { |
| return true; |
| } |
| return false; |
| } |
| |
| |
| 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(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( |
| 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); |
| } |
| super_op = new StaticCallNode(super_pos, super_operator, op_arguments); |
| } else { |
| ErrorMsg(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(current_class().SuperClass()); |
| ASSERT(!super_class.IsNull()); |
| super_op = |
| new LoadIndexedNode(operator_pos, receiver, index_expr, super_class); |
| } else if (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::kBIT_OR)); |
| 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(Symbols::New(Token::Str(op))); |
| const bool kResolveGetter = false; |
| bool is_no_such_method = false; |
| const Function& super_operator = Function::ZoneHandle( |
| 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); |
| } |
| 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; |
| } |
| |
| |
| AstNode* Parser::CreateImplicitClosureNode(const Function& func, |
| intptr_t token_pos, |
| AstNode* receiver) { |
| Function& implicit_closure_function = |
| Function::ZoneHandle(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( |
| 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) { |
| TRACE_PARSER("ParseSuperFieldAccess"); |
| const intptr_t field_pos = TokenPos(); |
| const Class& super_class = Class::ZoneHandle(current_class().SuperClass()); |
| if (super_class.IsNull()) { |
| ErrorMsg("class '%s' does not have a superclass", |
| String::Handle(current_class().Name()).ToCString()); |
| } |
| AstNode* implicit_argument = LoadReceiver(field_pos); |
| |
| const String& getter_name = |
| String::ZoneHandle(Field::GetterName(field_name)); |
| const Function& super_getter = Function::ZoneHandle( |
| Resolver::ResolveDynamicAnyArgs(super_class, getter_name)); |
| if (super_getter.IsNull()) { |
| const String& setter_name = |
| String::ZoneHandle(Field::SetterName(field_name)); |
| const Function& super_setter = Function::ZoneHandle( |
| Resolver::ResolveDynamicAnyArgs(super_class, setter_name)); |
| if (!super_setter.IsNull()) { |
| return new StaticGetterNode( |
| field_pos, implicit_argument, true, super_class, field_name); |
| } |
| } |
| if (super_getter.IsNull()) { |
| // Check if this is an access to an implicit closure using 'super'. |
| // If a function exists of the specified field_name then try |
| // accessing it as a getter, at runtime we will handle this by |
| // creating an implicit closure of the function and returning it. |
| const Function& super_function = Function::ZoneHandle( |
| 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, |
| LocalVariable* receiver) { |
| const intptr_t supercall_pos = TokenPos(); |
| const Class& super_class = Class::Handle(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(super_class.SuperClass()).IsObjectClass())) { |
| return; |
| } |
| String& ctor_name = String::Handle(super_class.Name()); |
| ctor_name = String::Concat(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(Smi::New(Function::kCtorPhaseAll))); |
| arguments->Add(phase_parameter); |
| const Function& super_ctor = Function::ZoneHandle( |
| super_class.LookupConstructor(ctor_name)); |
| if (super_ctor.IsNull()) { |
| ErrorMsg(supercall_pos, |
| "unresolved implicit call to super constructor '%s()'", |
| String::Handle(super_class.Name()).ToCString()); |
| } |
| String& error_message = String::Handle(); |
| if (!super_ctor.AreValidArguments(arguments->length(), |
| arguments->names(), |
| &error_message)) { |
| ErrorMsg(supercall_pos, |
| "invalid arguments passed to super constructor '%s()': %s", |
| String::Handle(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(cls.SuperClass()); |
| ASSERT(!super_class.IsNull()); |
| String& ctor_name = String::Handle(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")); |
| } |
| if (CurrentToken() != Token::kLPAREN) { |
| ErrorMsg("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(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( |
| super_class.LookupConstructor(ctor_name)); |
| if (super_ctor.IsNull()) { |
| ErrorMsg(supercall_pos, |
| "super class constructor '%s' not found", |
| ctor_name.ToCString()); |
| } |
| String& error_message = String::Handle(); |
| if (!super_ctor.AreValidArguments(arguments->length(), |
| arguments->names(), |
| &error_message)) { |
| ErrorMsg(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); |
| Field& field = Field::ZoneHandle(cls.LookupInstanceField(field_name)); |
| if (field.IsNull()) { |
| ErrorMsg(field_pos, "unresolved reference to instance field '%s'", |
| field_name.ToCString()); |
| } |
| CheckDuplicateFieldInit(field_pos, initialized_fields, &field); |
| AstNode* instance = new LoadLocalNode(field_pos, receiver); |
| EnsureExpressionTemp(); |
| return new StoreInstanceFieldNode(field_pos, instance, field, init_expr); |
| } |
| |
| |
| void Parser::CheckConstFieldsInitialized(const Class& cls) { |
| const Array& fields = Array::Handle(cls.fields()); |
| Field& field = Field::Handle(); |
| SequenceNode* initializers = current_block_->statements; |
| for (int field_num = 0; field_num < fields.Length(); field_num++) { |
| field ^= fields.At(field_num); |
| if (field.is_static()) { |
| 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; |
| |
| if (field.is_final()) { |
| ErrorMsg("final field '%s' not initialized", |
| String::Handle(field.name()).ToCString()); |
| } else { |
| field.UpdateCid(kNullCid); |
| } |
| } |
| } |
| |
| |
| 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(current_class().raw()); |
| const Library& saved_library = Library::Handle(library().raw()); |
| const Script& saved_script = Script::Handle(script().raw()); |
| const intptr_t saved_token_pos = TokenPos(); |
| |
| set_current_class(Class::Handle(field.origin())); |
| set_library(Library::Handle(current_class().library())); |
| SetScript(Script::Handle(current_class().script()), field.token_pos()); |
| |
| ASSERT(IsIdentifier()); |
| ConsumeToken(); |
| ExpectToken(Token::kASSIGN); |
| AstNode* init_expr = NULL; |
| 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(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(cls.fields()); |
| Field& f = Field::Handle(); |
| 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(); |
| 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 (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(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); |
| } |
| } |
| SetPosition(saved_pos); |
| } |
| |
| |
| void Parser::CheckDuplicateFieldInit(intptr_t init_pos, |
| GrowableArray<Field*>* initialized_fields, |
| Field* field) { |
| ASSERT(!field->is_static()); |
| for (int i = 0; i < initialized_fields->length(); i++) { |
| Field* initialized_field = (*initialized_fields)[i]; |
| if (initialized_field->raw() == field->raw()) { |
| ErrorMsg(init_pos, |
| "duplicate initialization for field %s", |
| String::Handle(field->name()).ToCString()); |
| } |
| } |
| initialized_fields->Add(field); |
| } |
| |
| |
| void Parser::ParseInitializers(const Class& cls, |
| LocalVariable* receiver, |
| GrowableArray<Field*>* initialized_fields) { |
| TRACE_PARSER("ParseInitializers"); |
| bool super_init_seen = false; |
| if (CurrentToken() == Token::kCOLON) { |
| if ((LookaheadToken(1) == Token::kTHIS) && |
| ((LookaheadToken(2) == Token::kLPAREN) || |
| ((LookaheadToken(2) == Token::kPERIOD) && |
| (LookaheadToken(4) == Token::kLPAREN)))) { |
| // Either we see this(...) or this.xxx(...) which is a |
| // redirected constructor. We don't need to check whether |
| // const fields are initialized. The other constructor will |
| // guarantee that. |
| ConsumeToken(); // Colon. |
| ParseConstructorRedirection(cls, receiver); |
| return; |
| } |
| do { |
| ConsumeToken(); // Colon or comma. |
| AstNode* init_statement; |
| if (CurrentToken() == Token::kSUPER) { |
| if (super_init_seen) { |
| ErrorMsg("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, receiver); |
| } |
| CheckConstFieldsInitialized(cls); |
| } |
| |
| |
| void Parser::ParseConstructorRedirection(const Class& cls, |
| LocalVariable* receiver) { |
| TRACE_PARSER("ParseConstructorRedirection"); |
| ASSERT(CurrentToken() == Token::kTHIS); |
| const intptr_t call_pos = TokenPos(); |
| ConsumeToken(); |
| String& ctor_name = String::Handle(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")); |
| } |
| if (CurrentToken() != Token::kLPAREN) { |
| ErrorMsg("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( |
| cls.LookupConstructor(ctor_name)); |
| if (redirect_ctor.IsNull()) { |
| ErrorMsg(call_pos, "constructor '%s' not found", ctor_name.ToCString()); |
| } |
| String& error_message = String::Handle(); |
| if (!redirect_ctor.AreValidArguments(arguments->length(), |
| arguments->names(), |
| &error_message)) { |
| ErrorMsg(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()); |
| const intptr_t ctor_pos = TokenPos(); |
| OpenFunctionBlock(func); |
| const Class& cls = Class::Handle(func.Owner()); |
| LocalVariable* receiver = new LocalVariable( |
| ctor_pos, |
| Symbols::This(), |
| Type::ZoneHandle(Type::DynamicType())); |
| current_block_->scope->AddVariable(receiver); |
| |
| LocalVariable* phase_parameter = new LocalVariable( |
| ctor_pos, |
| Symbols::PhaseParameter(), |
| Type::ZoneHandle(Type::SmiType())); |
| current_block_->scope->AddVariable(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(cls, receiver, &initialized_fields); |
| receiver->set_invisible(false); |
| |
| GenerateSuperConstructorCall(cls, receiver); |
| CheckConstFieldsInitialized(cls); |
| |
| // Empty constructor body. |
| SequenceNode* statements = CloseBlock(); |
| return statements; |
| } |
| |
| |
| // Helper function to make the first num_variables variables in the |
| // given scope visible/invisible. |
| static void SetInvisible(LocalScope* scope, int num_variables, bool invisible) { |
| ASSERT(num_variables <= scope->num_variables()); |
| for (int i = 0; i < num_variables; i++) { |
| scope->VariableAt(i)->set_invisible(invisible); |
| } |
| } |
| |
| |
| // 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(func.Owner()); |
| ASSERT(!cls.IsNull()); |
| |
| 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(TokenPos())); |
| |
| // Add implicit parameter for construction phase. |
| params.AddFinalParameter( |
| TokenPos(), |
| &Symbols::PhaseParameter(), |
| &Type::ZoneHandle(Type::SmiType())); |
| |
| if (func.is_const()) { |
| params.SetImplicitlyFinal(); |
| } |
| ParseFormalParameterList(allow_explicit_default_values, ¶ms); |
| |
| SetupDefaultsForOptionalParams(¶ms, default_parameter_values); |
| ASSERT(AbstractType::Handle(func.result_type()).IsResolved()); |
| ASSERT(func.NumParameters() == params.parameters->length()); |
| |
| // Now populate function scope with the formal parameters. |
| AddFormalParamsToScope(¶ms, current_block_->scope); |
| |
| // Initialize instance fields that have an explicit initializer expression. |
| // 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. |
| SetInvisible(current_block_->scope, params.parameters->length(), true); |
| GrowableArray<Field*> initialized_fields; |
| LocalVariable* receiver = current_block_->scope->VariableAt(0); |
| OpenBlock(); |
| ParseInitializedInstanceFields(cls, receiver, &initialized_fields); |
| // Make the parameters (which are in the outer scope) visible again. |
| SetInvisible(current_block_->scope->parent(), |
| params.parameters->length(), false); |
| |
| // Turn formal field parameters into field initializers or report error |
| // if the function is not a constructor. |
| if (params.has_field_initializer) { |
| for (int i = 0; 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(cls.LookupInstanceField(field_name)); |
| if (field.IsNull()) { |
| ErrorMsg(param.name_pos, |
| "unresolved reference to instance field '%s'", |
| field_name.ToCString()); |
| } |
| CheckDuplicateFieldInit(param.name_pos, &initialized_fields, &field); |
| AstNode* instance = new LoadLocalNode(param.name_pos, receiver); |
| LocalVariable* p = |
| current_block_->scope->LookupVariable(*param.name, false); |
| ASSERT(p != NULL); |
| // Initializing formals cannot be used in the explicit initializer |
| // list, nor can they be used in the constructor body. |
| // Thus, make the parameter invisible. |
| p->set_invisible(true); |
| AstNode* value = new LoadLocalNode(param.name_pos, p); |
| EnsureExpressionTemp(); |
| AstNode* initializer = new StoreInstanceFieldNode( |
| param.name_pos, instance, field, value); |
| current_block_->statements->Add(initializer); |
| } |
| } |
| } |
| |
| // Now parse the explicit initializer list or constructor redirection. |
| ParseInitializers(cls, receiver, &initialized_fields); |
| |
| SequenceNode* init_statements = CloseBlock(); |
| if (init_statements->length() > 0) { |
| // Generate guard around the initializer code. |
| LocalVariable* phase_param = LookupPhaseParameter(); |
| AstNode* phase_value = new LoadLocalNode(TokenPos(), phase_param); |
| AstNode* phase_check = new BinaryOpNode( |
| TokenPos(), Token::kBIT_AND, phase_value, |
| new LiteralNode(TokenPos(), |
| Smi::ZoneHandle(Smi::New(Function::kCtorPhaseInit)))); |
| AstNode* comparison = |
| new ComparisonNode(TokenPos(), Token::kNE_STRICT, |
| phase_check, |
| new LiteralNode(TokenPos(), |
| Smi::ZoneHandle(Smi::New(0)))); |
| AstNode* guarded_init_statements = |
| new IfNode(TokenPos(), 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 |
| // statements. If it exists and is not the last initializer statement, |
| // we need to create an implicit super call to the super constructor's |
| // body. |
| // Thus, iterate over all but the last initializer to see whether |
| // it's a super constructor call. |
| for (int i = 0; i < init_statements->length() - 1; i++) { |
| if (init_statements->NodeAt(i)->IsStaticCallNode()) { |
| StaticCallNode* static_call = |
| init_statements->NodeAt(i)->AsStaticCallNode(); |
| if (static_call->function().IsConstructor()) { |
| super_call = static_call; |
| break; |
| } |
| } |
| } |
| if (super_call != NULL) { |
| // Generate an implicit call to the super constructor's body. |
| // We need to patch the super _initializer_ call so that it |
| // saves the evaluated actual arguments in temporary variables. |
| // The temporary variables are necessary so that the argument |
| // expressions are not evaluated twice. |
| ArgumentListNode* ctor_args = super_call->arguments(); |
| // The super initializer call has at least 2 arguments: the |
| // implicit receiver, and the hidden construction phase. |
| ASSERT(ctor_args->length() >= 2); |
| for (int i = 2; i < ctor_args->length(); i++) { |
| AstNode* arg = ctor_args->NodeAt(i); |
| if (!IsSimpleLocalOrLiteralNode(arg)) { |
| LocalVariable* temp = |
| CreateTempConstVariable(arg->token_pos(), "sca"); |
| AstNode* save_temp = new StoreLocalNode(arg->token_pos(), temp, arg); |
| ctor_args->SetNodeAt(i, save_temp); |
| } |
| } |
| } |
| OpenBlock(); // Block to collect constructor body nodes. |
| |
| // Insert the implicit super call to the super constructor body. |
| if (super_call != NULL) { |
| ArgumentListNode* initializer_args = super_call->arguments(); |
| const Function& super_ctor = super_call->function(); |
| // Patch the initializer call so it only executes the super initializer. |
| initializer_args->SetNodeAt(1, |
| new LiteralNode(TokenPos(), |
| Smi::ZoneHandle(Smi::New(Function::kCtorPhaseInit)))); |
| |
| ArgumentListNode* super_call_args = new ArgumentListNode(TokenPos()); |
| // First argument is the receiver. |
| super_call_args->Add(new LoadLocalNode(TokenPos(), receiver)); |
| // Second argument is the construction phase argument. |
| AstNode* phase_parameter = |
| new LiteralNode(TokenPos(), |
| Smi::ZoneHandle(Smi::New(Function::kCtorPhaseBody))); |
| super_call_args->Add(phase_parameter); |
| super_call_args->set_names(initializer_args->names()); |
| for (int i = 2; i < initializer_args->length(); i++) { |
| AstNode* arg = initializer_args->NodeAt(i); |
| if (arg->IsLiteralNode()) { |
| LiteralNode* lit = arg->AsLiteralNode(); |
| super_call_args->Add(new LiteralNode(TokenPos(), lit->literal())); |
| } else { |
| ASSERT(arg->IsLoadLocalNode() || arg->IsStoreLocalNode()); |
| if (arg->IsLoadLocalNode()) { |
| const LocalVariable& temp = arg->AsLoadLocalNode()->local(); |
| super_call_args->Add(new LoadLocalNode(TokenPos(), &temp)); |
| } else if (arg->IsStoreLocalNode()) { |
| const LocalVariable& temp = arg->AsStoreLocalNode()->local(); |
| super_call_args->Add(new LoadLocalNode(TokenPos(), &temp)); |
| } |
| } |
| } |
| ASSERT(super_ctor.AreValidArguments(super_call_args->length(), |
| super_call_args->names(), |
| NULL)); |
| current_block_->statements->Add( |
| new StaticCallNode(TokenPos(), super_ctor, super_call_args)); |
| } |
| |
| if (CurrentToken() == Token::kLBRACE) { |
| ConsumeToken(); |
| ParseStatementSequence(); |
| ExpectToken(Token::kRBRACE); |
| } else if (CurrentToken() == Token::kARROW) { |
| ErrorMsg("constructors may not return a value"); |
| } else if (IsLiteral("native")) { |
| ErrorMsg("native constructors not supported"); |
| } else if (CurrentToken() == Token::kSEMICOLON) { |
| // Some constructors have no function body. |
| ConsumeToken(); |
| } else { |
| UnexpectedToken(); |
| } |
| |
| SequenceNode* ctor_block = CloseBlock(); |
| if (ctor_block->length() > 0) { |
| // Generate guard around the constructor body code. |
| LocalVariable* phase_param = LookupPhaseParameter(); |
| AstNode* phase_value = new LoadLocalNode(TokenPos(), phase_param); |
| AstNode* phase_check = |
| new BinaryOpNode(TokenPos(), Token::kBIT_AND, |
| phase_value, |
| new LiteralNode(TokenPos(), |
| Smi::ZoneHandle(Smi::New(Function::kCtorPhaseBody)))); |
| AstNode* comparison = |
| new ComparisonNode(TokenPos(), Token::kNE_STRICT, |
| phase_check, |
| new LiteralNode(TokenPos(), |
| Smi::ZoneHandle(Smi::New(0)))); |
| AstNode* guarded_block_statements = |
| new IfNode(TokenPos(), comparison, ctor_block, NULL); |
| current_block_->statements->Add(guarded_block_statements); |
| } |
| |
| SequenceNode* statements = CloseBlock(); |
| return statements; |
| } |
| |
| |
| // Parser is at the opening parenthesis of the formal parameter |
| // declaration of the function or constructor. |
| // Parse the formal parameters and code. |
| SequenceNode* Parser::ParseFunc(const Function& func, |
| Array& default_parameter_values) { |
| TRACE_PARSER("ParseFunc"); |
| Function& saved_innermost_function = |
| Function::Handle(innermost_function().raw()); |
| innermost_function_ = func.raw(); |
| |
| // Check to ensure we don't have classes with native fields in libraries |
| // which do not have a native resolver. This check is delayed until the class |
| // is actually used. Invocation of a function in the class is the first point |
| // of use. Access of const static fields in the class do not trigger an error. |
| if (current_class().num_native_fields() != 0) { |
| const Library& lib = Library::Handle(current_class().library()); |
| if (lib.native_entry_resolver() == NULL) { |
| const String& cls_name = String::Handle(current_class().Name()); |
| const String& lib_name = String::Handle(lib.url()); |
| ErrorMsg(current_class().token_pos(), |
| "class '%s' is trying to extend a native fields class, " |
| "but library '%s' has no native resolvers", |
| cls_name.ToCString(), lib_name.ToCString()); |
| } |
| } |
| |
| if (func.IsConstructor()) { |
| SequenceNode* statements = ParseConstructor(func, default_parameter_values); |
| innermost_function_ = saved_innermost_function.raw(); |
| return statements; |
| } |
| |
| ASSERT(!func.IsConstructor()); |
| OpenFunctionBlock(func); // Build local scope for function. |
| |
| ParamList params; |
| // An instance closure function may capture and access the receiver, but via |
| // the context and not via the first formal parameter. |
| if (func.IsClosureFunction()) { |
| // The first parameter of a closure function is the closure object. |
| ASSERT(!func.is_const()); // Closure functions cannot be const. |
| params.AddFinalParameter( |
| TokenPos(), |
| &Symbols::ClosureParameter(), |
| &Type::ZoneHandle(Type::DynamicType())); |
| } else if (!func.is_static()) { |
| // Static functions do not have a receiver. |
| ASSERT(current_class().raw() == func.Owner()); |
| params.AddReceiver(ReceiverType(TokenPos())); |
| } else if (func.IsFactory()) { |
| // The first parameter of a factory is the AbstractTypeArguments vector of |
| // the type of the instance to be allocated. |
| params.AddFinalParameter( |
| TokenPos(), |
| &Symbols::TypeArgumentsParameter(), |
| &Type::ZoneHandle(Type::DynamicType())); |
| } |
| ASSERT((CurrentToken() == Token::kLPAREN) || func.IsGetterFunction()); |
| const bool allow_explicit_default_values = true; |
| if (!func.IsGetterFunction()) { |
| ParseFormalParameterList(allow_explicit_default_values, ¶ms); |
| } else { |
| // TODO(hausner): Remove this once we no longer support the old |
| // getter syntax with explicit empty parameter list. |
| if (CurrentToken() == Token::kLPAREN) { |
| ConsumeToken(); |
| ExpectToken(Token::kRPAREN); |
| } |
| } |
| |
| // The number of parameters and their type are not yet set in local functions, |
| // since they are not 'top-level' parsed. |
| if (func.IsLocalFunction()) { |
| AddFormalParamsToFunction(¶ms, func); |
| } |
| SetupDefaultsForOptionalParams(¶ms, default_parameter_values); |
| ASSERT(AbstractType::Handle(func.result_type()).IsResolved()); |
| ASSERT(func.NumParameters() == params.parameters->length()); |
| |
| // Check whether the function has any field initializer formal parameters, |
| // which are not allowed in non-constructor functions. |
| if (params.has_field_initializer) { |
| for (int i = 0; i < params.parameters->length(); i++) { |
| ParamDesc& param = (*params.parameters)[i]; |
| if (param.is_field_initializer) { |
| ErrorMsg(param.name_pos, |
| "field initializer only allowed in constructors"); |
| } |
| } |
| } |
| // Populate function scope with the formal parameters. |
| AddFormalParamsToScope(¶ms, current_block_->scope); |
| |
| if (FLAG_enable_type_checks && |
| (current_block_->scope->function_level() > 0)) { |
| // We are parsing, but not compiling, a local function. |
| // The instantiator may be required at run time for generic type checks. |
| if (IsInstantiatorRequired()) { |
| // Make sure that the receiver of the enclosing instance function |
| // (or implicit first parameter of an enclosing factory) is marked as |
| // captured if type checks are enabled, because they may access it to |
| // instantiate types. |
| CaptureInstantiator(); |
| } |
| } |
| |
| OpenBlock(); // Open a nested scope for the outermost function block. |
| if (CurrentToken() == Token::kLBRACE) { |
| ConsumeToken(); |
| ParseStatementSequence(); |
| ExpectToken(Token::kRBRACE); |
| } else if (CurrentToken() == Token::kARROW) { |
| ConsumeToken(); |
| const intptr_t expr_pos = TokenPos(); |
| AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades); |
| ASSERT(expr != NULL); |
| current_block_->statements->Add(new ReturnNode(expr_pos, expr)); |
| } else if (IsLiteral("native")) { |
| ParseNativeFunctionBlock(¶ms, func); |
| } else if (func.is_external()) { |
| // Body of an external method contains a single throw. |
| const String& function_name = String::ZoneHandle(func.name()); |
| // TODO(regis): For an instance function, pass the receiver to |
| // NoSuchMethodError. |
| current_block_->statements->Add( |
| ThrowNoSuchMethodError(TokenPos(), |
| current_class(), |
| function_name, |
| func.is_static() ? |
| InvocationMirror::kStatic : |
| InvocationMirror::kDynamic, |
| InvocationMirror::kMethod)); |
| } else { |
| UnexpectedToken(); |
| } |
| SequenceNode* body = CloseBlock(); |
| current_block_->statements->Add(body); |
| innermost_function_ = saved_innermost_function.raw(); |
| return CloseBlock(); |
| } |
| |
| |
| void Parser::SkipIf(Token::Kind token) { |
| if (CurrentToken() == token) { |
| ConsumeToken(); |
| } |
| } |
| |
| |
| // Skips tokens up to matching closing parenthesis. |
| void Parser::SkipToMatchingParenthesis() { |
| ASSERT(CurrentToken() == Token::kLPAREN); |
| int level = 0; |
| do { |
| if (CurrentToken() == Token::kLPAREN) { |
| level++; |
| } else if (CurrentToken() == Token::kRPAREN) { |
| level--; |
| } |
| ConsumeToken(); |
| } while ((level > 0) && (CurrentToken() != Token::kEOS)); |
| } |
| |
| |
| void Parser::SkipInitializers() { |
| ASSERT(CurrentToken() == Token::kCOLON); |
| do { |
| ConsumeToken(); // Colon or comma. |
| if (CurrentToken() == Token::kSUPER) { |
| ConsumeToken(); |
| if (CurrentToken() == Token::kPERIOD) { |
| ConsumeToken(); |
| ExpectIdentifier("identifier expected"); |
| } |
| if (CurrentToken() != Token::kLPAREN) { |
| ErrorMsg("'(' expected"); |
| } |
| SkipToMatchingParenthesis(); |
| } else { |
| SkipIf(Token::kTHIS); |
| SkipIf(Token::kPERIOD); |
| ExpectIdentifier("identifier expected"); |
| ExpectToken(Token::kASSIGN); |
| SetAllowFunctionLiterals(false); |
| SkipExpr(); |
| SetAllowFunctionLiterals(true); |
| } |
| } while (CurrentToken() == Token::kCOMMA); |
| } |
| |
| |
| void Parser::ParseQualIdent(QualIdent* qual_ident) { |
| TRACE_PARSER("ParseQualIdent"); |
| ASSERT(IsIdentifier()); |
| ASSERT(!current_class().IsNull()); |
| qual_ident->ident_pos = TokenPos(); |
| qual_ident->ident = CurrentLiteral(); |
| qual_ident->lib_prefix = NULL; |
| ConsumeToken(); |
| if (CurrentToken() == Token::kPERIOD) { |
| // An identifier cannot be resolved in a local scope when top level parsing. |
| if (is_top_level_ || |
| !ResolveIdentInLocalScope(qual_ident->ident_pos, |
| *(qual_ident->ident), |
| NULL)) { |
| LibraryPrefix& lib_prefix = LibraryPrefix::ZoneHandle(); |
| lib_prefix = current_class().LookupLibraryPrefix(*(qual_ident->ident)); |
| if (!lib_prefix.IsNull()) { |
| // We have a library prefix qualified identifier, unless the prefix is |
| // shadowed by a type parameter in scope. |
| if (current_class().IsNull() || |
| (current_class().LookupTypeParameter(*(qual_ident->ident), |
| TokenPos()) == |
| TypeParameter::null())) { |
| ConsumeToken(); // Consume the kPERIOD token. |
| qual_ident->lib_prefix = &lib_prefix; |
| qual_ident->ident_pos = TokenPos(); |
| qual_ident->ident = |
| ExpectIdentifier("identifier expected after '.'"); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) { |
| TRACE_PARSER("ParseMethodOrConstructor"); |
| ASSERT(CurrentToken() == Token::kLPAREN || method->IsGetter()); |
| intptr_t method_pos = this->TokenPos(); |
| ASSERT(method->type != NULL); |
| ASSERT(method->name_pos > 0); |
| ASSERT(current_member_ == method); |
| |
| if (method->has_var) { |
| ErrorMsg(method->name_pos, "keyword var not allowed for methods"); |
| } |
| if (method->has_final) { |
| ErrorMsg(method->name_pos, "'final' not allowed for methods"); |
| } |
| if (method->has_abstract && method->has_static) { |
| ErrorMsg(method->name_pos, |
| "static method '%s' cannot be abstract", |
| method->name->ToCString()); |
| } |
| if (method->has_const && !method->IsFactoryOrConstructor()) { |
| ErrorMsg(method->name_pos, "'const' not allowed for methods"); |
| } |
| if (method->has_abstract && method->IsFactoryOrConstructor()) { |
| ErrorMsg(method->name_pos, "constructor cannot be abstract"); |
| } |
| if (method->has_const && method->IsConstructor()) { |
| Class& cls = Class::Handle(library_.LookupClass(members->class_name())); |
| cls.set_is_const(); |
| } |
| |
| // Parse the formal parameters. |
| const bool are_implicitly_final = method->has_const; |
| const bool allow_explicit_default_values = true; |
| const intptr_t formal_param_pos = TokenPos(); |
| method->params.Clear(); |
| // Static functions do not have a receiver. |
| // The first parameter of a factory is the AbstractTypeArguments vector of |
| // the type of the instance to be allocated. |
| if (!method->has_static || method->IsConstructor()) { |
| method->params.AddReceiver(ReceiverType(formal_param_pos)); |
| } else if (method->IsFactory()) { |
| method->params.AddFinalParameter( |
| formal_param_pos, |
| &Symbols::TypeArgumentsParameter(), |
| &Type::ZoneHandle(Type::DynamicType())); |
| } |
| // Constructors have an implicit parameter for the construction phase. |
| if (method->IsConstructor()) { |
| method->params.AddFinalParameter( |
| TokenPos(), |
| &Symbols::PhaseParameter(), |
| &Type::ZoneHandle(Type::SmiType())); |
| } |
| if (are_implicitly_final) { |
| method->params.SetImplicitlyFinal(); |
| } |
| if (!method->IsGetter()) { |
| ParseFormalParameterList(allow_explicit_default_values, &method->params); |
| } |
| |
| // Now that we know the parameter list, we can distinguish between the |
| // unary and binary operator -. |
| if (method->has_operator) { |
| if ((method->operator_token == Token::kSUB) && |
| (method->params.num_fixed_parameters == 1)) { |
| // Patch up name for unary operator - so it does not clash with the |
| // name for binary operator -. |
| method->operator_token = Token::kNEGATE; |
| *method->name = Symbols::New(Token::Str(Token::kNEGATE)); |
| } |
| CheckOperatorArity(*method); |
| } |
| |
| if (members->FunctionNameExists(*method->name, method->kind)) { |
| ErrorMsg(method->name_pos, |
| "field or method '%s' already defined", method->name->ToCString()); |
| } |
| |
| // Mangle the name for getter and setter functions and check function |
| // arity. |
| if (method->IsGetter() || method->IsSetter()) { |
| int expected_num_parameters = 0; |
| if (method->IsGetter()) { |
| expected_num_parameters = (method->has_static) ? 0 : 1; |
| method->name = &String::ZoneHandle(Field::GetterSymbol(*method->name)); |
| } else { |
| ASSERT(method->IsSetter()); |
| expected_num_parameters = (method->has_static) ? 1 : 2; |
| method->name = &String::ZoneHandle(Field::SetterSymbol(*method->name)); |
| } |
| if ((method->params.num_fixed_parameters != expected_num_parameters) || |
| (method->params.num_optional_parameters != 0)) { |
| ErrorMsg(method->name_pos, "illegal %s parameters", |
| method->IsGetter() ? "getter" : "setter"); |
| } |
| } |
| |
| // Parse redirecting factory constructor. |
| Type& redirection_type = Type::Handle(); |
| String& redirection_identifier = String::Handle(); |
| if (method->IsFactory() && (CurrentToken() == Token::kASSIGN)) { |
| ConsumeToken(); |
| const intptr_t type_pos = TokenPos(); |
| const AbstractType& type = AbstractType::Handle( |
| ParseType(ClassFinalizer::kTryResolve)); |
| if (!type.IsMalformed() && |
| (type.IsTypeParameter() || type.IsDynamicType())) { |
| // Replace the type with a malformed type and compile a throw when called. |
| redirection_type = ClassFinalizer::NewFinalizedMalformedType( |
| Error::Handle(), // No previous error. |
| current_class(), |
| type_pos, |
| ClassFinalizer::kTryResolve, // No compile-time error. |
| "factory '%s' may not redirect to %s'%s'", |
| method->name->ToCString(), |
| type.IsTypeParameter() ? "type parameter " : "", |
| type.IsTypeParameter() ? |
| String::Handle(type.UserVisibleName()).ToCString() : "dynamic"); |
| } else { |
| redirection_type ^= type.raw(); |
| } |
| if (CurrentToken() == Token::kPERIOD) { |
| // Named constructor or factory. |
| ConsumeToken(); |
| redirection_identifier = ExpectIdentifier("identifier expected")->raw(); |
| } |
| } else if (CurrentToken() == Token::kCOLON) { |
| // Parse initializers. |
| if (!method->IsConstructor()) { |
| ErrorMsg("initializers only allowed on constructors"); |
| } |
| if ((LookaheadToken(1) == Token::kTHIS) && |
| ((LookaheadToken(2) == Token::kLPAREN) || |
| LookaheadToken(4) == Token::kLPAREN)) { |
| // Redirected constructor: either this(...) or this.xxx(...). |
| if (method->params.has_field_initializer) { |
| // Constructors that redirect to another constructor must not |
| // initialize any fields using field initializer parameters. |
| ErrorMsg(formal_param_pos, "Redirecting constructor " |
| "may not use field initializer parameters"); |
| } |
| ConsumeToken(); // Colon. |
| ExpectToken(Token::kTHIS); |
| String& redir_name = String::ZoneHandle( |
| String::Concat(members->class_name(), Symbols::Dot())); |
| if (CurrentToken() == Token::kPERIOD) { |
| ConsumeToken(); |
| redir_name = String::Concat(redir_name, |
| *ExpectIdentifier("constructor name expected")); |
| } |
| method->redirect_name = &redir_name; |
| if (CurrentToken() != Token::kLPAREN) { |
| ErrorMsg("'(' expected"); |
| } |
| SkipToMatchingParenthesis(); |
| } else { |
| SkipInitializers(); |
| } |
| } |
| |
| // Only constructors can redirect to another method. |
| ASSERT((method->redirect_name == NULL) || method->IsConstructor()); |
| |
| intptr_t method_end_pos = method_pos; |
| if ((CurrentToken() == Token::kLBRACE) || |
| (CurrentToken() == Token::kARROW)) { |
| if (method->has_abstract) { |
| ErrorMsg(method->name_pos, |
| "abstract method '%s' may not have a function body", |
| method->name->ToCString()); |
| } else if (method->has_external) { |
| ErrorMsg(method->name_pos, |
| "external method '%s' may not have a function body", |
| method->name->ToCString()); |
| } else if (method->IsFactoryOrConstructor() && method->has_const) { |
| ErrorMsg(method->name_pos, |
| "const constructor or factory '%s' may not have a function body", |
| method->name->ToCString()); |
| } |
| if (method->redirect_name != NULL) { |
| ErrorMsg(method->name_pos, |
| "Constructor with redirection may not have a function body"); |
| } |
| if (CurrentToken() == Token::kLBRACE) { |
| SkipBlock(); |
| } else { |
| ConsumeToken(); |
| SkipExpr(); |
| ExpectSemicolon(); |
| } |
| method_end_pos = TokenPos() - 1; |
| } else if (IsLiteral("native")) { |
| if (method->has_abstract) { |
| ErrorMsg(method->name_pos, |
| "abstract method '%s' may not have a function body", |
| method->name->ToCString()); |
| } else if (method->IsFactoryOrConstructor() && method->has_const) { |
| ErrorMsg(method->name_pos, |
| "const constructor or factory '%s' may not be native", |
| method->name->ToCString()); |
| } |
| if (method->redirect_name != NULL) { |
| ErrorMsg(method->name_pos, |
| "Constructor with redirection may not have a function body"); |
| } |
| ParseNativeDeclaration(); |
| } else { |
| // We haven't found a method body. Issue error if one is required. |
| const bool must_have_body = |
| method->has_static && |
| !method->has_external && |
| redirection_type.IsNull(); |
| if (must_have_body) { |
| ErrorMsg(method->name_pos, |
| "function body expected for method '%s'", |
| method->name->ToCString()); |
| } |
| |
| if (CurrentToken() == Token::kSEMICOLON) { |
| ConsumeToken(); |
| if (!method->has_static && |
| !method->has_external && |
| !method->IsConstructor()) { |
| // Methods, getters and setters without a body are |
| // implicitly abstract. |
| method->has_abstract = true; |
<
|