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