// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "vm/parser.h" #include "vm/flags.h" #ifndef DART_PRECOMPILED_RUNTIME #include "lib/invocation_mirror.h" #include "platform/utils.h" #include "vm/ast_transformer.h" #include "vm/bootstrap.h" #include "vm/class_finalizer.h" #include "vm/compiler/aot/precompiler.h" #include "vm/compiler/frontend/kernel_binary_flowgraph.h" #include "vm/compiler/jit/compiler.h" #include "vm/compiler_stats.h" #include "vm/dart_api_impl.h" #include "vm/dart_entry.h" #include "vm/growable_array.h" #include "vm/handles.h" #include "vm/hash_table.h" #include "vm/heap.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."); // TODO(floitsch): remove the conditional-directive flag, once we publicly // committed to the current version. DEFINE_FLAG(bool, conditional_directives, true, "Enable conditional directives"); DEFINE_FLAG( bool, await_is_keyword, false, "await and yield are treated as proper keywords in synchronous code."); DECLARE_FLAG(bool, profile_vm); DECLARE_FLAG(bool, trace_service); 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& 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::StreamingScopeBuilder builder(this); kernel_scopes_ = builder.BuildScopes(); } return kernel_scopes_; } LocalVariable* ParsedFunction::EnsureExpressionTemp() { if (!has_expression_temp_var()) { LocalVariable* temp = new (Z) LocalVariable(function_.token_pos(), function_.token_pos(), Symbols::ExprTemp(), Object::dynamic_type()); ASSERT(temp != NULL); set_expression_temp_var(temp); } ASSERT(has_expression_temp_var()); return expression_temp_var(); } void ParsedFunction::EnsureFinallyReturnTemp(bool is_async) { if (!has_finally_return_temp_var()) { LocalVariable* temp = new (Z) LocalVariable(function_.token_pos(), function_.token_pos(), Symbols::FinallyRetVal(), Object::dynamic_type()); ASSERT(temp != NULL); temp->set_is_final(); if (is_async) { temp->set_is_captured(); } set_finally_return_temp_var(temp); } ASSERT(has_finally_return_temp_var()); } void ParsedFunction::SetNodeSequence(SequenceNode* node_sequence) { ASSERT(node_sequence_ == NULL); ASSERT(node_sequence != NULL); node_sequence_ = node_sequence; } void ParsedFunction::SetRegExpCompileData( RegExpCompileData* regexp_compile_data) { ASSERT(regexp_compile_data_ == NULL); ASSERT(regexp_compile_data != NULL); regexp_compile_data_ = regexp_compile_data; } void ParsedFunction::AddDeferredPrefix(const LibraryPrefix& prefix) { // 'deferred_prefixes_' are used to invalidate code, but no invalidation is // needed if --load_deferred_eagerly. ASSERT(!FLAG_load_deferred_eagerly); ASSERT(prefix.is_deferred_load()); ASSERT(!prefix.is_loaded()); for (intptr_t i = 0; i < deferred_prefixes_->length(); i++) { if ((*deferred_prefixes_)[i]->raw() == prefix.raw()) { return; } } deferred_prefixes_->Add(&LibraryPrefix::ZoneHandle(Z, prefix.raw())); } void ParsedFunction::AllocateVariables() { ASSERT(!function().IsIrregexpFunction()); LocalScope* scope = node_sequence()->scope(); const intptr_t num_fixed_params = function().num_fixed_parameters(); const intptr_t num_opt_params = function().NumOptionalParameters(); const intptr_t num_params = num_fixed_params + num_opt_params; // 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 inlined_finally_nodes_; TryStack* outer_try_; const intptr_t try_index_; bool inside_catch_; // True when parsing a catch clause of this try. bool inside_finally_; // True when parsing a finally clause of an inner try // of this try. DISALLOW_COPY_AND_ASSIGN(TryStack); }; void Parser::TryStack::AddNodeForFinallyInlining(AstNode* node) { inlined_finally_nodes_.Add(node); } void Parser::TryStack::RemoveJumpToLabel(SourceLabel* label) { int i = 0; while (i < inlined_finally_nodes_.length()) { if (inlined_finally_nodes_[i]->IsJumpNode()) { JumpNode* jump = inlined_finally_nodes_[i]->AsJumpNode(); if (jump->label() == label) { // Shift remaining entries left and delete last entry. for (int j = i + 1; j < inlined_finally_nodes_.length(); j++) { inlined_finally_nodes_[j - 1] = inlined_finally_nodes_[j]; } inlined_finally_nodes_.RemoveLast(); continue; } } i++; } } // For parsing a compilation unit. Parser::Parser(const Script& script, const Library& library, TokenPosition token_pos) : thread_(Thread::Current()), isolate_(thread()->isolate()), allocation_space_(thread_->IsMutatorThread() ? Heap::kNew : Heap::kOld), script_(Script::Handle(zone(), script.raw())), tokens_iterator_(zone(), TokenStream::Handle(zone(), script.tokens()), token_pos), token_kind_(Token::kILLEGAL), current_block_(NULL), is_top_level_(false), await_is_keyword_(false), current_member_(NULL), allow_function_literals_(true), parsed_function_(NULL), innermost_function_(Function::Handle(zone())), literal_token_(LiteralToken::Handle(zone())), current_class_(Class::Handle(zone())), library_(Library::Handle(zone(), library.raw())), try_stack_(NULL), last_used_try_index_(0), unregister_pending_function_(false), async_temp_scope_(NULL), trace_indent_(0), recursion_counter_(0) { ASSERT(tokens_iterator_.IsValid()); ASSERT(!library.IsNull()); } // For parsing a function. Parser::Parser(const Script& script, ParsedFunction* parsed_function, TokenPosition token_pos) : thread_(Thread::Current()), isolate_(thread()->isolate()), allocation_space_(thread_->IsMutatorThread() ? Heap::kNew : Heap::kOld), script_(Script::Handle(zone(), script.raw())), tokens_iterator_(zone(), TokenStream::Handle(zone(), script.tokens()), token_pos), token_kind_(Token::kILLEGAL), current_block_(NULL), is_top_level_(false), await_is_keyword_(false), current_member_(NULL), allow_function_literals_(true), parsed_function_(parsed_function), innermost_function_( Function::Handle(zone(), parsed_function->function().raw())), literal_token_(LiteralToken::Handle(zone())), current_class_( Class::Handle(zone(), parsed_function->function().Owner())), library_(Library::Handle( zone(), Class::Handle(zone(), parsed_function->function().origin()) .library())), try_stack_(NULL), last_used_try_index_(0), unregister_pending_function_(false), async_temp_scope_(NULL), trace_indent_(0), recursion_counter_(0) { ASSERT(tokens_iterator_.IsValid()); ASSERT(!current_function().IsNull()); EnsureExpressionTemp(); } Parser::~Parser() { if (unregister_pending_function_) { const GrowableObjectArray& pending_functions = GrowableObjectArray::Handle(T->pending_functions()); ASSERT(!pending_functions.IsNull()); ASSERT(pending_functions.Length() > 0); ASSERT(pending_functions.At(pending_functions.Length() - 1) == current_function().raw()); pending_functions.RemoveLast(); } } // Each try in this function gets its own try index. // See definition of RawPcDescriptors::PcDescriptor. int16_t Parser::AllocateTryIndex() { if (!Utils::IsInt(16, last_used_try_index_ - 1)) { ReportError("too many nested try statements"); } return last_used_try_index_++; } void Parser::SetScript(const Script& script, TokenPosition token_pos) { script_ = script.raw(); tokens_iterator_.SetStream(TokenStream::Handle(Z, script.tokens()), token_pos); token_kind_ = Token::kILLEGAL; } bool Parser::SetAllowFunctionLiterals(bool value) { bool current_value = allow_function_literals_; allow_function_literals_ = value; return current_value; } const Function& Parser::current_function() const { ASSERT(parsed_function() != NULL); return parsed_function()->function(); } const Function& Parser::innermost_function() const { return innermost_function_; } int Parser::FunctionLevel() const { if (current_block_ != NULL) { return current_block_->scope->function_level(); } return 0; } const Class& Parser::current_class() const { return current_class_; } void Parser::set_current_class(const Class& value) { current_class_ = value.raw(); } void Parser::SetPosition(TokenPosition position) { tokens_iterator_.SetCurrentPosition(position); token_kind_ = Token::kILLEGAL; prev_token_pos_ = position; } // Set state and increments generational count so that thge background compiler // can detect if loading/top-level-parsing occured during compilation. class TopLevelParsingScope : public StackResource { public: explicit TopLevelParsingScope(Thread* thread) : StackResource(thread) { isolate()->IncrTopLevelParsingCount(); } ~TopLevelParsingScope() { isolate()->DecrTopLevelParsingCount(); isolate()->IncrLoadingInvalidationGen(); } }; void Parser::ParseCompilationUnit(const Library& library, const Script& script) { Thread* thread = Thread::Current(); ASSERT(thread->long_jump_base()->IsSafeToJump()); CSTAT_TIMER_SCOPE(thread, parser_timer); #ifndef PRODUCT VMTagScope tagScope(thread, VMTag::kCompileTopLevelTagId); TimelineDurationScope tds(thread, Timeline::GetCompilerStream(), "CompileTopLevel"); if (tds.enabled()) { tds.SetNumArguments(1); tds.CopyArgument(0, "script", String::Handle(script.url()).ToCString()); } #endif TopLevelParsingScope scope(thread); Parser parser(script, library, TokenPosition::kMinSource); parser.ParseTopLevel(); } void Parser::ComputeCurrentToken() { ASSERT(token_kind_ == Token::kILLEGAL); token_kind_ = tokens_iterator_.CurrentTokenKind(); if (token_kind_ == Token::kERROR) { ReportError(TokenPos(), "%s", CurrentLiteral()->ToCString()); } } Token::Kind Parser::LookaheadToken(int num_tokens) { return tokens_iterator_.LookaheadTokenKind(num_tokens); } String* Parser::CurrentLiteral() const { String& result = String::ZoneHandle(Z, tokens_iterator_.CurrentLiteral()); return &result; } RawDouble* Parser::CurrentDoubleLiteral() const { literal_token_ ^= tokens_iterator_.CurrentToken(); ASSERT(literal_token_.kind() == Token::kDOUBLE); return Double::RawCast(literal_token_.value()); } RawInteger* Parser::CurrentIntegerLiteral() const { literal_token_ ^= tokens_iterator_.CurrentToken(); ASSERT(literal_token_.kind() == Token::kINTEGER); RawInteger* ri = Integer::RawCast(literal_token_.value()); return ri; } struct ParamDesc { ParamDesc() : type(NULL), name_pos(TokenPosition::kNoSource), name(NULL), default_value(NULL), metadata(NULL), var(NULL), is_final(false), is_field_initializer(false), has_explicit_type(false), is_covariant(false) {} const AbstractType* type; TokenPosition name_pos; const String* name; const Instance* default_value; // NULL if not an optional parameter. const Object* metadata; // NULL if no metadata or metadata not evaluated. LocalVariable* var; // Scope variable allocated for this parameter. bool is_final; bool is_field_initializer; bool has_explicit_type; bool is_covariant; }; struct ParamList { ParamList() { Clear(); } void Clear() { num_fixed_parameters = 0; num_optional_parameters = 0; has_optional_positional_parameters = false; has_optional_named_parameters = false; has_explicit_default_values = false; has_field_initializer = false; has_covariant = false; implicitly_final = false; skipped = false; this->parameters = new ZoneGrowableArray(); } void AddFinalParameter(TokenPosition name_pos, const String* name, const AbstractType* type) { this->num_fixed_parameters++; ParamDesc param; param.name_pos = name_pos; param.name = name; param.is_final = true; param.type = type; this->parameters->Add(param); } void AddReceiver(const AbstractType* receiver_type, TokenPosition token_pos) { ASSERT(this->parameters->is_empty()); AddFinalParameter(token_pos, &Symbols::This(), receiver_type); } void EraseParameterTypes() { const int num_parameters = parameters->length(); for (int i = 0; i < num_parameters; i++) { (*parameters)[i].type = &Object::dynamic_type(); } } // Make the parameter variables visible/invisible. void SetInvisible(bool invisible) { const intptr_t num_params = parameters->length(); for (int i = 0; i < num_params; i++) { ParamDesc& param = (*parameters)[i]; ASSERT(param.var != NULL); param.var->set_invisible(invisible); } } void HideInitFormals() { const intptr_t num_params = parameters->length(); for (int i = 0; i < num_params; i++) { ParamDesc& param = (*parameters)[i]; if (param.is_field_initializer) { ASSERT(param.var != NULL); param.var->set_invisible(true); } } } void SetImplicitlyFinal() { implicitly_final = true; } int num_fixed_parameters; int num_optional_parameters; bool has_optional_positional_parameters; bool has_optional_named_parameters; bool has_explicit_default_values; bool has_field_initializer; bool has_covariant; bool implicitly_final; bool skipped; ZoneGrowableArray* 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& functions() const { return functions_; } void AddField(const Field& field) { fields_.Add(&Field::ZoneHandle(zone_, field.raw())); } const GrowableArray& 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& 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 functions_; GrowableArray fields_; GrowableArray 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& fields() const { return fields_; } const GrowableArray& functions() const { return functions_; } private: Zone* zone_; GrowableArray fields_; GrowableArray functions_; }; void Parser::ParseClass(const Class& cls) { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); const int64_t num_tokes_before = STAT_VALUE(thread, num_tokens_consumed); #ifndef PRODUCT TimelineDurationScope tds(thread, Timeline::GetCompilerStream(), "ParseClass"); if (tds.enabled()) { tds.SetNumArguments(1); tds.CopyArgument(0, "class", String::Handle(cls.Name()).ToCString()); } #endif if (!cls.is_synthesized_class()) { ASSERT(thread->long_jump_base()->IsSafeToJump()); CSTAT_TIMER_SCOPE(thread, parser_timer); const Script& script = Script::Handle(zone, cls.script()); const Library& lib = Library::Handle(zone, cls.library()); Parser parser(script, lib, cls.token_pos()); parser.ParseClassDefinition(cls); } else if (cls.is_enum_class()) { ASSERT(thread->long_jump_base()->IsSafeToJump()); CSTAT_TIMER_SCOPE(thread, parser_timer); const Script& script = Script::Handle(zone, cls.script()); const Library& lib = Library::Handle(zone, cls.library()); Parser parser(script, lib, cls.token_pos()); parser.ParseEnumDefinition(cls); } const int64_t num_tokes_after = STAT_VALUE(thread, num_tokens_consumed); INC_STAT(thread, num_class_tokens, num_tokes_after - num_tokes_before); } bool Parser::FieldHasFunctionLiteralInitializer(const Field& field, TokenPosition* start, TokenPosition* end) { if (!field.has_initializer()) { return false; } Thread* thread = Thread::Current(); Zone* zone = thread->zone(); const Class& cls = Class::Handle(zone, field.Owner()); const Script& script = Script::Handle(zone, cls.script()); const Library& lib = Library::Handle(zone, cls.library()); Parser parser(script, lib, field.token_pos()); return parser.GetFunctionLiteralInitializerRange(field, start, end); } bool Parser::GetFunctionLiteralInitializerRange(const Field& field, TokenPosition* start, TokenPosition* end) { ASSERT(field.has_initializer()); // Since |field| has an initializer, skip until '='. while (CurrentToken() != Token::kASSIGN) { ConsumeToken(); } // Skip past the '=' as well. ConsumeToken(); *start = TokenPos(); if (IsFunctionLiteral()) { SkipExpr(); *end = PrevTokenPos(); return true; } return false; } RawObject* Parser::ParseFunctionParameters(const Function& func) { ASSERT(!func.IsNull()); LongJumpScope jump; if (setjmp(*jump.Set()) == 0) { Thread* thread = Thread::Current(); StackZone stack_zone(thread); Zone* zone = stack_zone.GetZone(); const Script& script = Script::Handle(zone, func.script()); const Class& owner = Class::Handle(zone, func.Owner()); ASSERT(!owner.IsNull()); ParsedFunction* parsed_function = new ParsedFunction(thread, Function::ZoneHandle(zone, func.raw())); Parser parser(script, parsed_function, func.token_pos()); parser.SkipFunctionPreamble(); const bool use_function_type_syntax = false; const bool allow_explicit_default_values = true; const bool evaluate_metadata = true; ParamList params; parser.ParseFormalParameterList(use_function_type_syntax, allow_explicit_default_values, evaluate_metadata, ¶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::kImplicitClosureFunction: node_sequence = parser.ParseImplicitClosure(func); break; case RawFunction::kClosureFunction: case RawFunction::kRegularFunction: case RawFunction::kGetterFunction: case RawFunction::kSetterFunction: case RawFunction::kConstructor: // The call to a redirecting factory is redirected. ASSERT(!func.IsRedirectingFactory()); if (!func.IsImplicitConstructor()) { parser.SkipFunctionPreamble(); } node_sequence = parser.ParseFunc(func, false); break; case RawFunction::kImplicitGetter: ASSERT(!func.is_static()); node_sequence = parser.ParseInstanceGetter(func); break; case RawFunction::kImplicitSetter: ASSERT(!func.is_static()); node_sequence = parser.ParseInstanceSetter(func); break; case RawFunction::kImplicitStaticFinalGetter: node_sequence = parser.ParseStaticFinalGetter(func); INC_STAT(thread, num_implicit_final_getters, 1); break; case RawFunction::kMethodExtractor: node_sequence = parser.ParseMethodExtractor(func); INC_STAT(thread, num_method_extractors, 1); break; case RawFunction::kNoSuchMethodDispatcher: node_sequence = parser.ParseNoSuchMethodDispatcher(func); break; case RawFunction::kInvokeFieldDispatcher: node_sequence = parser.ParseInvokeFieldDispatcher(func); break; case RawFunction::kIrregexpFunction: UNREACHABLE(); // Irregexp functions have their own parser. default: UNREACHABLE(); } if (parsed_function->has_expression_temp_var()) { node_sequence->scope()->AddVariable(parsed_function->expression_temp_var()); } node_sequence->scope()->AddVariable(parsed_function->current_context_var()); if (parsed_function->has_finally_return_temp_var()) { node_sequence->scope()->AddVariable( parsed_function->finally_return_temp_var()); } parsed_function->SetNodeSequence(node_sequence); // The instantiators may be required at run time for generic type checks or // allocation of generic types. if (parser.IsInstantiatorRequired()) { // In the case of a local function, only set the instantiator if the // receiver (or type arguments parameter of a factory) was captured. LocalVariable* instantiator = NULL; const bool kTestOnly = true; if (parser.current_function().IsInFactoryScope()) { instantiator = parser.LookupTypeArgumentsParameter(node_sequence->scope(), kTestOnly); } else { instantiator = parser.LookupReceiver(node_sequence->scope(), kTestOnly); } if (!parser.current_function().IsLocalFunction() || ((instantiator != NULL) && instantiator->is_captured())) { parsed_function->set_instantiator(instantiator); } } // ParseFunc has recorded the generic function type arguments variable. ASSERT(!Isolate::Current()->reify_generic_functions() || !parser.current_function().IsGeneric() || (parsed_function->function_type_arguments() != NULL)); } RawObject* Parser::ParseMetadata(const Field& meta_data) { LongJumpScope jump; if (setjmp(*jump.Set()) == 0) { Thread* thread = Thread::Current(); StackZone stack_zone(thread); Zone* zone = stack_zone.GetZone(); const Class& owner_class = Class::Handle(zone, meta_data.Owner()); const Script& script = Script::Handle(zone, meta_data.Script()); const TokenPosition token_pos = meta_data.token_pos(); // Parsing metadata can involve following paths in the parser that are // normally used for expressions and assume current_function is non-null, // so we create a fake function to use as the current_function rather than // scattering special cases throughout the parser. const Function& fake_function = Function::ZoneHandle( zone, Function::New(Symbols::At(), RawFunction::kRegularFunction, true, // is_static false, // is_const false, // is_abstract false, // is_external false, // is_native Object::Handle(zone, meta_data.RawOwner()), token_pos)); fake_function.set_is_debuggable(false); ParsedFunction* parsed_function = new ParsedFunction(thread, fake_function); Parser parser(script, parsed_function, token_pos); parser.set_current_class(owner_class); parser.OpenFunctionBlock(fake_function); RawObject* metadata = parser.EvaluateMetadata(); return metadata; } else { Thread* thread = Thread::Current(); StackZone stack_zone(thread); Zone* zone = stack_zone.GetZone(); Error& error = Error::Handle(zone); error = thread->sticky_error(); thread->clear_sticky_error(); return error.raw(); } UNREACHABLE(); return Object::null(); } RawArray* Parser::EvaluateMetadata() { CheckToken(Token::kAT, "Metadata character '@' expected"); GrowableObjectArray& meta_values = GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld)); while (CurrentToken() == Token::kAT) { ConsumeToken(); TokenPosition expr_pos = TokenPos(); if (!IsIdentifier()) { ExpectIdentifier("identifier expected"); } // Reject expressions with deferred library prefix eagerly. Object& obj = Object::Handle(Z, library_.LookupLocalObject(*CurrentLiteral())); if (!obj.IsNull() && obj.IsLibraryPrefix()) { if (LibraryPrefix::Cast(obj).is_deferred_load()) { ReportError("Metadata must be compile-time constant"); } } AstNode* expr = NULL; if ((LookaheadToken(1) == Token::kLPAREN) || ((LookaheadToken(1) == Token::kPERIOD) && (LookaheadToken(3) == Token::kLPAREN)) || ((LookaheadToken(1) == Token::kPERIOD) && (LookaheadToken(3) == Token::kPERIOD) && (LookaheadToken(5) == Token::kLPAREN))) { expr = ParseNewOperator(Token::kCONST); } else { // Can be x, C.x, or L.C.x. expr = ParsePrimary(); // Consumes x, C or L.C. Class& cls = Class::Handle(Z); if (expr->IsPrimaryNode()) { PrimaryNode* primary_node = expr->AsPrimaryNode(); if (primary_node->primary().IsClass()) { // If the primary node referred to a class we are loading a // qualified static field. cls ^= primary_node->primary().raw(); } else { ReportError(expr_pos, "Metadata expressions must refer to a const field " "or constructor"); } } if (CurrentToken() == Token::kPERIOD) { // C.x or L.C.X. if (cls.IsNull()) { ReportError(expr_pos, "Metadata expressions must refer to a const field " "or constructor"); } ConsumeToken(); const TokenPosition ident_pos = TokenPos(); String* ident = ExpectIdentifier("identifier expected"); const Field& field = Field::Handle(Z, cls.LookupStaticField(*ident)); if (field.IsNull()) { ReportError(ident_pos, "Class '%s' has no field '%s'", cls.ToCString(), ident->ToCString()); } if (!field.is_const()) { ReportError(ident_pos, "Field '%s' of class '%s' is not const", ident->ToCString(), cls.ToCString()); } expr = GenerateStaticFieldLookup(field, ident_pos); } } if (expr->EvalConstExpr() == NULL) { ReportError(expr_pos, "expression must be a compile-time constant"); } const Instance& val = EvaluateConstExpr(expr_pos, expr); meta_values.Add(val, Heap::kOld); } return Array::MakeFixedLength(meta_values); } SequenceNode* Parser::ParseStaticInitializer() { ExpectIdentifier("field name expected"); CheckToken(Token::kASSIGN, "field 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::ParseImplicitClosure(const Function& func) { TRACE_PARSER("ParseImplicitClosure"); TokenPosition token_pos = func.token_pos(); OpenFunctionBlock(func); const Function& parent = Function::Handle(func.parent_function()); intptr_t type_args_len = 0; // Length of type args vector passed to parent. LocalVariable* type_args_var = NULL; if (Isolate::Current()->reify_generic_functions()) { // The parent function of an implicit closure is the original function, i.e. // non-closurized. It is not an enclosing function in the usual sense of a // parent function. Do not set parent_type_arguments() in parsed_function_. ASSERT(func.IsGeneric() == parent.IsGeneric()); if (func.IsGeneric()) { type_args_len = func.NumTypeParameters(); // Insert function type arguments variable to scope. type_args_var = new (Z) LocalVariable( TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::FunctionTypeArgumentsVar(), Object::dynamic_type()); current_block_->scope->AddVariable(type_args_var); ASSERT(FunctionLevel() == 0); parsed_function_->set_function_type_arguments(type_args_var); } } ParamList params; params.AddFinalParameter(token_pos, &Symbols::ClosureParameter(), &Object::dynamic_type()); if (parent.IsImplicitSetterFunction()) { const TokenPosition ident_pos = func.token_pos(); ASSERT(IsIdentifier()); params.AddFinalParameter(ident_pos, &Symbols::Value(), &Object::dynamic_type()); ASSERT(func.num_fixed_parameters() == 2); // closure, value. } else if (!parent.IsGetterFunction() && !parent.IsImplicitGetterFunction()) { // NOTE: For the `kernel -> flowgraph` we don't use the parser. if (parent.kernel_offset() <= 0) { SkipFunctionPreamble(); const bool use_function_type_syntax = false; const bool allow_explicit_default_values = true; const bool evaluate_metadata = false; ParseFormalParameterList(use_function_type_syntax, allow_explicit_default_values, evaluate_metadata, ¶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, type_args_var, type_args_len); if (!func.is_static()) { func_args->Add(LoadReceiver(token_pos)); } // Skip implicit parameter at 0. for (intptr_t i = 1; i < func.NumParameters(); ++i) { func_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i))); } if (func.HasOptionalNamedParameters()) { // TODO(srdjan): Must allocate array in old space, since it // runs in background compiler. Find a better way. const Array& arg_names = Array::ZoneHandle(Array::New(func.NumOptionalParameters(), Heap::kOld)); for (intptr_t i = 0; i < arg_names.Length(); ++i) { intptr_t index = func.num_fixed_parameters() + i; arg_names.SetAt(i, String::Handle(func.ParameterNameAt(index))); } func_args->set_names(arg_names); } const String& func_name = String::ZoneHandle(parent.name()); const Class& owner = Class::Handle(parent.Owner()); Function& target = Function::ZoneHandle(owner.LookupFunction(func_name)); if (target.raw() != parent.raw()) { NOT_IN_PRODUCT(ASSERT(Isolate::Current()->HasAttemptedReload())); if (target.IsNull() || (target.is_static() != parent.is_static()) || (target.kind() != parent.kind())) { target = Function::null(); } } AstNode* call = NULL; // Check the target still exists and has compatible parameters. If not, // throw NSME/call nSM instead of forwarding the call. Note we compare the // parent not func because func has an extra parameter for the closure // receiver. if (!target.IsNull() && (parent.num_fixed_parameters() == target.num_fixed_parameters())) { call = new StaticCallNode(token_pos, target, func_args, StaticCallNode::kNoRebind); } else if (!parent.is_static()) { NOT_IN_PRODUCT(ASSERT(Isolate::Current()->HasAttemptedReload())); // If a subsequent reload reintroduces the target in the middle of the // Invocation object being constructed, we won't be able to successfully // deopt because the generated AST will change. func.SetIsOptimizable(false); ArgumentListNode* arguments = BuildNoSuchMethodArguments( token_pos, func_name, *func_args, NULL, false); const intptr_t kTypeArgsLen = 0; const intptr_t kNumArguments = 2; // Receiver, InvocationMirror. ArgumentsDescriptor args_desc(Array::Handle( Z, ArgumentsDescriptor::New(kTypeArgsLen, kNumArguments))); Function& no_such_method = Function::ZoneHandle(Z, Resolver::ResolveDynamicForReceiverClass( owner, Symbols::NoSuchMethod(), args_desc)); if (no_such_method.IsNull()) { // If noSuchMethod(i) is not found, call Object:noSuchMethod. no_such_method ^= Resolver::ResolveDynamicForReceiverClass( Class::Handle(Z, I->object_store()->object_class()), Symbols::NoSuchMethod(), args_desc); } call = new StaticCallNode(token_pos, no_such_method, arguments, StaticCallNode::kStatic); } else { NOT_IN_PRODUCT(ASSERT(Isolate::Current()->HasAttemptedReload())); // If a subsequent reload reintroduces the target in the middle of the // arguments array being constructed, we won't be able to successfully // deopt because the generated AST will change. func.SetIsOptimizable(false); InvocationMirror::Kind im_kind; if (parent.IsImplicitGetterFunction()) { im_kind = InvocationMirror::kGetter; } else if (parent.IsImplicitSetterFunction()) { im_kind = InvocationMirror::kSetter; } else { im_kind = InvocationMirror::kMethod; } call = ThrowNoSuchMethodError(TokenPos(), owner, func_name, func_args, InvocationMirror::kStatic, im_kind, NULL); // No existing function. } ASSERT(call != NULL); ReturnNode* return_node = new ReturnNode(token_pos, call); current_block_->statements->Add(return_node); return CloseBlock(); } SequenceNode* Parser::ParseMethodExtractor(const Function& func) { TRACE_PARSER("ParseMethodExtractor"); ParamList params; const TokenPosition ident_pos = func.token_pos(); ASSERT(func.token_pos() == TokenPosition::kMethodExtractor); ASSERT(current_class().raw() == func.Owner()); params.AddReceiver(ReceiverType(current_class()), ident_pos); ASSERT(func.num_fixed_parameters() == 1); // Receiver. ASSERT(!func.HasOptionalParameters()); // Build local scope for function and populate with the formal parameters. OpenFunctionBlock(func); AddFormalParamsToScope(¶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); if (desc.TypeArgsLen() > 0) { ASSERT(func.IsGeneric() && !func.HasGenericParent()); // Insert function type arguments variable to scope. LocalVariable* type_args_var = new (Z) LocalVariable( TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::FunctionTypeArgumentsVar(), Object::dynamic_type()); current_block_->scope->AddVariable(type_args_var); ASSERT(FunctionLevel() == 0); parsed_function_->set_function_type_arguments(type_args_var); } } SequenceNode* Parser::ParseNoSuchMethodDispatcher(const Function& func) { TRACE_PARSER("ParseNoSuchMethodDispatcher"); ASSERT(FLAG_lazy_dispatchers); ASSERT(func.IsNoSuchMethodDispatcher()); TokenPosition token_pos = func.token_pos(); ASSERT(func.token_pos() == TokenPosition::kMinSource); ASSERT(current_class().raw() == func.Owner()); ArgumentsDescriptor desc(Array::Handle(Z, func.saved_args_desc())); ASSERT(desc.Count() > 0); // Set up scope for this function. BuildDispatcherScope(func, desc); // Receiver is local 0. LocalScope* scope = current_block_->scope; ArgumentListNode* func_args = new ArgumentListNode( token_pos, parsed_function_->function_type_arguments(), desc.TypeArgsLen()); for (intptr_t i = 0; i < desc.Count(); ++i) { func_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i))); } if (desc.NamedCount() > 0) { const Array& arg_names = Array::ZoneHandle(Z, Array::New(desc.NamedCount(), Heap::kOld)); for (intptr_t i = 0; i < arg_names.Length(); ++i) { arg_names.SetAt(i, String::Handle(Z, desc.NameAt(i))); } func_args->set_names(arg_names); } const String& func_name = String::ZoneHandle(Z, func.name()); ArgumentListNode* arguments = BuildNoSuchMethodArguments(token_pos, func_name, *func_args, NULL, false); const intptr_t kTypeArgsLen = 0; const intptr_t kNumArguments = 2; // Receiver, InvocationMirror. ArgumentsDescriptor args_desc( Array::Handle(Z, ArgumentsDescriptor::New(kTypeArgsLen, kNumArguments))); Function& no_such_method = Function::ZoneHandle( Z, Resolver::ResolveDynamicForReceiverClass( Class::Handle(Z, func.Owner()), Symbols::NoSuchMethod(), args_desc)); if (no_such_method.IsNull()) { // If noSuchMethod(i) is not found, call Object:noSuchMethod. no_such_method ^= Resolver::ResolveDynamicForReceiverClass( Class::Handle(Z, I->object_store()->object_class()), Symbols::NoSuchMethod(), args_desc); } StaticCallNode* call = new StaticCallNode( token_pos, no_such_method, arguments, StaticCallNode::kNSMDispatch); ReturnNode* return_node = new ReturnNode(token_pos, call); current_block_->statements->Add(return_node); return CloseBlock(); } SequenceNode* Parser::ParseInvokeFieldDispatcher(const Function& func) { TRACE_PARSER("ParseInvokeFieldDispatcher"); ASSERT(func.IsInvokeFieldDispatcher()); TokenPosition token_pos = func.token_pos(); ASSERT(func.token_pos() == TokenPosition::kMinSource); ASSERT(current_class().raw() == func.Owner()); const Array& args_desc = Array::Handle(Z, func.saved_args_desc()); ArgumentsDescriptor desc(args_desc); ASSERT(desc.Count() > 0); // Set up scope for this function. BuildDispatcherScope(func, desc); // Receiver is local 0. LocalScope* scope = current_block_->scope; ArgumentListNode* no_args = new ArgumentListNode(token_pos); LoadLocalNode* receiver = new LoadLocalNode(token_pos, scope->VariableAt(0)); const Class& closure_cls = Class::Handle(Isolate::Current()->object_store()->closure_class()); const Class& owner = Class::Handle(Z, func.Owner()); ASSERT(!owner.IsNull()); const String& name = String::Handle(Z, func.name()); AstNode* function_object = NULL; if (owner.raw() == closure_cls.raw() && name.Equals(Symbols::Call())) { function_object = receiver; } else { const String& getter_name = String::ZoneHandle(Z, Field::GetterSymbol(name)); function_object = new (Z) InstanceCallNode(token_pos, receiver, getter_name, no_args); } // Pass arguments 1..n to the closure call. ArgumentListNode* args = new (Z) ArgumentListNode(token_pos, parsed_function_->function_type_arguments(), desc.TypeArgsLen()); const Array& names = Array::Handle(Z, Array::New(desc.NamedCount(), Heap::kOld)); // Positional parameters. intptr_t i = 1; for (; i < desc.PositionalCount(); ++i) { args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i))); } // Named parameters. for (; i < desc.Count(); i++) { args->Add(new (Z) LoadLocalNode(token_pos, scope->VariableAt(i))); intptr_t index = i - desc.PositionalCount(); names.SetAt(index, String::Handle(Z, desc.NameAt(index))); } args->set_names(names); AstNode* result = NULL; if (owner.raw() == closure_cls.raw() && name.Equals(Symbols::Call())) { result = new ClosureCallNode(token_pos, function_object, args); } else { result = BuildClosureCall(token_pos, function_object, args); } ReturnNode* return_node = new ReturnNode(token_pos, result); current_block_->statements->Add(return_node); return CloseBlock(); } AstNode* Parser::BuildClosureCall(TokenPosition token_pos, AstNode* closure, ArgumentListNode* arguments) { return new InstanceCallNode(token_pos, closure, Symbols::Call(), arguments); } void Parser::SkipToMatching() { Token::Kind opening_token = CurrentToken(); ASSERT((opening_token == Token::kLBRACE) || (opening_token == Token::kLPAREN)); GrowableArray token_stack(8); GrowableArray token_pos_stack(8); // Adding the first opening brace here, because it will be consumed // in the loop right away. token_stack.Add(opening_token); const TokenPosition start_pos = TokenPos(); TokenPosition opening_pos = start_pos; token_pos_stack.Add(start_pos); bool is_match = true; bool unexpected_token_found = false; Token::Kind token = opening_token; TokenPosition token_pos; do { ConsumeToken(); token = CurrentToken(); token_pos = TokenPos(); switch (token) { case Token::kLBRACE: case Token::kLPAREN: case Token::kLBRACK: token_stack.Add(token); token_pos_stack.Add(token_pos); break; case Token::kRBRACE: opening_token = token_stack.RemoveLast(); opening_pos = token_pos_stack.RemoveLast(); is_match = opening_token == Token::kLBRACE; break; case Token::kRPAREN: opening_token = token_stack.RemoveLast(); opening_pos = token_pos_stack.RemoveLast(); is_match = opening_token == Token::kLPAREN; break; case Token::kRBRACK: opening_token = token_stack.RemoveLast(); opening_pos = token_pos_stack.RemoveLast(); is_match = opening_token == Token::kLBRACK; break; case Token::kEOS: opening_token = token_stack.RemoveLast(); opening_pos = token_pos_stack.RemoveLast(); unexpected_token_found = true; break; default: // nothing. break; } } while (!token_stack.is_empty() && is_match && !unexpected_token_found); if (!is_match) { const Error& error = Error::Handle(LanguageError::NewFormatted( Error::Handle(), script_, opening_pos, Report::AtLocation, Report::kWarning, allocation_space_, "unbalanced '%s' opens here", Token::Str(opening_token))); ReportErrors(error, script_, token_pos, "unbalanced '%s'", Token::Str(token)); } else if (unexpected_token_found) { ReportError(start_pos, "unterminated '%s'", Token::Str(opening_token)); } } void Parser::SkipBlock() { ASSERT(CurrentToken() == Token::kLBRACE); SkipToMatching(); } // Skips tokens up to and including matching closing parenthesis. void Parser::SkipToMatchingParenthesis() { ASSERT(CurrentToken() == Token::kLPAREN); SkipToMatching(); ASSERT(CurrentToken() == Token::kRPAREN); ConsumeToken(); } // Parses a parameter type as defined by the 'parameterTypeList' production. void Parser::ParseParameterType(ParamList* params) { TRACE_PARSER("ParseParameterType"); ParamDesc parameter; parameter.has_explicit_type = true; // The type is required by the syntax. // It is too early to resolve the type here, since it can be a result type // referring to a not yet declared function type parameter. parameter.type = &AbstractType::ZoneHandle( Z, ParseTypeOrFunctionType(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; parameter.is_final = true; } if ((parameter.type == NULL) && (CurrentToken() == Token::kVOID)) { ConsumeToken(); // This must later be changed to a closure type if we recognize // a closure/function type parameter. We check this at the end // of ParseFormalParameter. parameter.type = &Object::void_type(); } if ((parameter.type == NULL) || IsFunctionTypeSymbol()) { // At this point, we must see an identifier for the type or the // function parameter. The identifier may be 'Function'. if (!IsIdentifier()) { ReportError("parameter name or type expected"); } // Lookahead to determine whether the next tokens are a return type // followed by a parameter name. bool found_type = false; { TokenPosScope saved_pos(this); if (TryParseType(true)) { if (IsIdentifier() || (CurrentToken() == Token::kTHIS)) { found_type = true; } } } if (found_type) { // The types of formal parameters are never ignored, even in unchecked // mode, because they are part of the function type of closurized // functions appearing in type tests with typedefs. parameter.has_explicit_type = true; // It is too early to resolve the type here, since it can be a result // type referring to a not yet declared function type parameter. if (parameter.type == NULL) { parameter.type = &AbstractType::ZoneHandle( Z, ParseTypeOrFunctionType(true, ClassFinalizer::kDoNotResolve)); } else { parameter.type = &AbstractType::ZoneHandle( Z, ParseFunctionType(*parameter.type, ClassFinalizer::kDoNotResolve)); } } else { // If this is an initializing formal, its type will be set to the type // of the respective field when the constructor is fully parsed. parameter.type = &Object::dynamic_type(); } } if (!this_seen && (CurrentToken() == Token::kTHIS)) { ConsumeToken(); ExpectToken(Token::kPERIOD); this_seen = true; parameter.is_field_initializer = true; parameter.is_final = true; } // At this point, we must see an identifier for the parameter name. parameter.name_pos = TokenPos(); parameter.name = ExpectIdentifier("parameter name expected"); if (parameter.is_field_initializer) { params->has_field_initializer = true; } if (params->has_optional_named_parameters && (parameter.name->CharAt(0) == Library::kPrivateIdentifierStart)) { ReportError(parameter.name_pos, "named parameter must not be private"); } // Check for duplicate formal parameters. const intptr_t num_existing_parameters = params->num_fixed_parameters + params->num_optional_parameters; for (intptr_t i = 0; i < num_existing_parameters; i++) { ParamDesc& existing_parameter = (*params->parameters)[i]; if (existing_parameter.name->Equals(*parameter.name)) { ReportError(parameter.name_pos, "duplicate formal parameter '%s'", parameter.name->ToCString()); } } if (IsParameterPart()) { // This parameter is probably a closure. If we saw the keyword 'var' // or 'final', a closure is not legal here and we ignore the // opening parens. // TODO(hausner): The language spec appears to allow var and final // in signature types when used with initializing formals: // fieldFormalParameter: // metadata finalConstVarOrType? this ‘.’ identifier formalParameterList? ; if (!var_seen && !final_seen) { // The parsed parameter type is actually the function result type. AbstractType& result_type = AbstractType::Handle(Z, parameter.type->raw()); // In top-level and mixin functions, the source may be in a different // script than the script of the current class. However, we never reparse // signature functions (except typedef signature functions), therefore // we do not need to keep the correct script via a patch class. Use the // actual current class as owner of the signature function. Function& signature_function = Function::Handle( Z, Function::NewSignatureFunction(current_class(), innermost_function(), TokenPosition::kNoSource)); innermost_function_ = signature_function.raw(); // Finish parsing the function type parameter. if (CurrentToken() == Token::kLT) { ParseTypeParameters(false); // Not parameterizing class, but function. } ASSERT(CurrentToken() == Token::kLPAREN); ParamList func_params; // Add implicit closure object parameter. func_params.AddFinalParameter(TokenPos(), &Symbols::ClosureParameter(), &Object::dynamic_type()); const bool use_function_type_syntax = false; const bool allow_explicit_default_values = false; const bool evaluate_metadata = false; ParseFormalParameterList(use_function_type_syntax, allow_explicit_default_values, evaluate_metadata, &func_params); signature_function.set_result_type(result_type); // The result type may refer to the signature function's type parameters, // but was not parsed in the scope of the signature function. Adjust. result_type.SetScopeFunction(signature_function); AddFormalParamsToFunction(&func_params, signature_function); ASSERT(innermost_function().raw() == signature_function.raw()); innermost_function_ = signature_function.parent_function(); Type& signature_type = Type::ZoneHandle(Z, signature_function.SignatureType()); // A signature type itself cannot be malformed or malbounded, only its // signature function's result type or parameter types may be. ASSERT(!signature_type.IsMalformed()); ASSERT(!signature_type.IsMalbounded()); // The type of the parameter is now the signature type. parameter.type = &signature_type; } } if ((CurrentToken() == Token::kASSIGN) || (CurrentToken() == Token::kCOLON)) { if ((!params->has_optional_positional_parameters && !params->has_optional_named_parameters) || !allow_explicit_default_value) { ReportError("parameter must not specify a default value"); } if (params->has_optional_positional_parameters) { ExpectToken(Token::kASSIGN); } else { ConsumeToken(); } params->num_optional_parameters++; params->has_explicit_default_values = true; // Also if explicitly NULL. if (is_top_level_) { // Skip default value parsing. SkipExpr(); } else { const Instance& const_value = ParseConstExpr()->literal(); parameter.default_value = &const_value; } } else { if (params->has_optional_positional_parameters || params->has_optional_named_parameters) { // Implicit default value is null. params->num_optional_parameters++; parameter.default_value = &Object::null_instance(); } else { params->num_fixed_parameters++; ASSERT(params->num_optional_parameters == 0); } } if (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->type_args_len(), arguments->length(), arguments->names(), NULL)) { super_func = Function::null(); } else if (super_func.IsNull() && resolve_getter) { const String& getter_name = String::ZoneHandle(Z, Field::LookupGetterSymbol(name)); if (!getter_name.IsNull()) { super_func = Resolver::ResolveDynamicAnyArgs(Z, super_class, getter_name); ASSERT(super_func.IsNull() || (super_func.kind() != RawFunction::kImplicitStaticFinalGetter)); } } if (super_func.IsNull()) { super_func = Resolver::ResolveDynamicAnyArgs(Z, super_class, Symbols::NoSuchMethod()); ASSERT(!super_func.IsNull()); *is_no_such_method = true; } else { *is_no_such_method = false; } return super_func.raw(); } StaticCallNode* Parser::BuildInvocationMirrorAllocation( TokenPosition call_pos, const String& function_name, const ArgumentListNode& function_args, const LocalVariable* temp_for_last_arg, bool is_super_invocation) { const TokenPosition args_pos = function_args.token_pos(); // Build arguments to the call to the static // InvocationMirror._allocateInvocationMirror method. ArgumentListNode* arguments = new ArgumentListNode(args_pos); // The first argument is the original function name. arguments->Add(new LiteralNode(args_pos, function_name)); // The second argument is the arguments descriptor of the original function. const Array& args_descriptor = Array::ZoneHandle( ArgumentsDescriptor::New(function_args.type_args_len(), function_args.length(), function_args.names())); arguments->Add(new LiteralNode(args_pos, args_descriptor)); // The third argument is an array containing the original function arguments, // including the function type arguments and the receiver. ArrayNode* args_array = new ArrayNode(args_pos, Type::ZoneHandle(Type::ArrayType())); // A type_args_var is allocated in the generated body of an implicit // closure and in the generated body of a noSuchMethodDispatcher. // Pass the type arguments to the invocation mirror as the first argument. if (function_args.type_args_var() != NULL) { ASSERT(function_args.type_arguments().IsNull()); args_array->AddElement( new LoadLocalNode(args_pos, function_args.type_args_var())); } else if (!function_args.type_arguments().IsNull()) { args_array->AddElement( new LiteralNode(args_pos, function_args.type_arguments())); } for (intptr_t i = 0; i < function_args.length(); i++) { AstNode* arg = function_args.NodeAt(i); if ((temp_for_last_arg != NULL) && (i == function_args.length() - 1)) { LetNode* store_arg = new LetNode(arg->token_pos()); store_arg->AddNode( new StoreLocalNode(arg->token_pos(), temp_for_last_arg, arg)); store_arg->AddNode( new LoadLocalNode(arg->token_pos(), temp_for_last_arg)); args_array->AddElement(store_arg); } else { args_array->AddElement(arg); } } arguments->Add(args_array); arguments->Add(new LiteralNode(args_pos, Bool::Get(is_super_invocation))); // Lookup the static InvocationMirror._allocateInvocationMirror method. const Class& mirror_class = 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, StaticCallNode::kStatic); } 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, const TypeArguments& func_type_args) { TRACE_PARSER("ParseSuperCall"); ASSERT(CurrentToken() == Token::kLPAREN); const TokenPosition supercall_pos = TokenPos(); // 'this' parameter is the first argument to super call (after the type args). ArgumentListNode* arguments = new ArgumentListNode(supercall_pos, func_type_args); AstNode* receiver = LoadReceiver(supercall_pos); arguments->Add(receiver); ParseActualParameters(arguments, Object::null_type_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, StaticGetterSetter::kSuper); // 'this' is not passed as parameter to the closure. ArgumentListNode* closure_arguments = new ArgumentListNode(supercall_pos, func_type_args); 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, StaticCallNode::kSuper); } // 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, StaticCallNode::kSuper); } 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, StaticCallNode::kSuper); 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, StaticGetterSetter::kSuper); } 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->type_args_len(), 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::kSuper); } 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, Object::null_type_arguments(), kAllowConst); receiver->set_invisible(false); // Resolve the constructor. const Function& super_ctor = Function::ZoneHandle(Z, super_class.LookupConstructor(ctor_name)); if (super_ctor.IsNull()) { if (super_class.LookupFactory(ctor_name) != Function::null()) { ReportError(supercall_pos, "super class constructor '%s' " "must not be a factory constructor", ctor_name.ToCString()); } ReportError(supercall_pos, "super class constructor '%s' not found", ctor_name.ToCString()); } if (current_function().is_const() && !super_ctor.is_const()) { ReportError(supercall_pos, "super constructor must be const"); } String& error_message = String::Handle(Z); if (!super_ctor.AreValidArguments(arguments->type_args_len(), arguments->length(), arguments->names(), &error_message)) { ReportError(supercall_pos, "invalid arguments passed to super class constructor '%s': %s", ctor_name.ToCString(), error_message.ToCString()); } if (current_function().is_const()) { // No need to check that the type arguments to the super const contructor // are instantiated, because generic constructors are not supported. ASSERT(arguments->type_args_len() == 0); // All arguments to the super const constructor must be potentially const. for (intptr_t i = 0; i < arguments->length(); i++) { AstNode* argument = arguments->NodeAt(i); if (!argument->IsPotentiallyConst()) { ReportError( argument->token_pos(), "super initializer argument must be compile time constant."); } if (argument->EvalConstExpr() != NULL) { // If the expression is a compile-time constant, ensure that it // is evaluated and canonicalized. See issues 27164 and 31106. argument = FoldConstExpr(argument->token_pos(), argument); arguments->SetNodeAt(i, argument); } } } return new StaticCallNode(supercall_pos, super_ctor, arguments, StaticCallNode::kSuper); } AstNode* Parser::ParseInitializer(const Class& cls, LocalVariable* receiver, GrowableArray* initialized_fields) { TRACE_PARSER("ParseInitializer"); const TokenPosition field_pos = TokenPos(); if (CurrentToken() == Token::kASSERT) { // Function literals are allowed in assertion initializer. // "this" must not be accessible in assertion initializer. receiver->set_invisible(true); AstNode* init_assert = ParseAssertStatement(current_function().is_const()); receiver->set_invisible(false); return init_assert; } if (CurrentToken() == Token::kTHIS) { ConsumeToken(); ExpectToken(Token::kPERIOD); } const String& field_name = *ExpectIdentifier("field name expected"); ExpectToken(Token::kASSIGN); TokenPosition expr_pos = TokenPos(); const bool saved_mode = SetAllowFunctionLiterals(false); // "this" must not be accessible in initializer expressions. receiver->set_invisible(true); AstNode* init_expr = ParseConditionalExpr(); if (CurrentToken() == Token::kCASCADE) { init_expr = ParseCascades(init_expr); } receiver->set_invisible(false); SetAllowFunctionLiterals(saved_mode); if (current_function().is_const()) { if (!init_expr->IsPotentiallyConst()) { ReportError(expr_pos, "initializer expression must be compile time constant."); } if (init_expr->EvalConstExpr() != NULL) { // If the expression is a compile-time constant, ensure that it // is evaluated and canonicalized. See issue 27164. init_expr = FoldConstExpr(expr_pos, init_expr); } } Field& field = Field::ZoneHandle(Z, cls.LookupInstanceField(field_name)); if (field.IsNull()) { ReportError(field_pos, "unresolved reference to instance field '%s'", field_name.ToCString()); } EnsureExpressionTemp(); AstNode* instance = new (Z) LoadLocalNode(field_pos, receiver); AstNode* initializer = CheckDuplicateFieldInit(field_pos, initialized_fields, instance, &field, init_expr); if (initializer == NULL) { initializer = new (Z) StoreInstanceFieldNode(field_pos, instance, field, init_expr, /* is_initializer = */ true); } return initializer; } void Parser::CheckFieldsInitialized(const Class& cls) { const Array& fields = Array::Handle(Z, cls.fields()); Field& field = Field::Handle(Z); SequenceNode* initializers = current_block_->statements; for (int field_num = 0; field_num < fields.Length(); field_num++) { field ^= fields.At(field_num); if (field.is_static()) { continue; } bool found = false; for (int i = 0; i < initializers->length(); i++) { found = false; if (initializers->NodeAt(i)->IsStoreInstanceFieldNode()) { StoreInstanceFieldNode* initializer = initializers->NodeAt(i)->AsStoreInstanceFieldNode(); ASSERT(field.IsOriginal()); if (initializer->field().Original() == field.raw()) { found = true; break; } } } if (found) continue; field.RecordStore(Object::null_object()); } } AstNode* Parser::ParseExternalInitializedField(const Field& field) { // Only use this function if the initialized field originates // from a different class. We need to save and restore the // library and token stream (script). // The current_class remains unchanged, so that type arguments // are resolved in the correct scope class. ASSERT(current_class().raw() != field.Origin()); const Library& saved_library = Library::Handle(Z, library().raw()); const Script& saved_script = Script::Handle(Z, script().raw()); const TokenPosition saved_token_pos = TokenPos(); const Class& origin_class = Class::Handle(Z, field.Origin()); set_library(Library::Handle(Z, origin_class.library())); SetScript(Script::Handle(Z, origin_class.script()), field.token_pos()); ASSERT(IsIdentifier()); ConsumeToken(); ExpectToken(Token::kASSIGN); AstNode* init_expr = NULL; TokenPosition expr_pos = TokenPos(); if (field.is_const()) { init_expr = ParseConstExpr(); } else { init_expr = ParseExpr(kAllowConst, kConsumeCascades); if (init_expr->EvalConstExpr() != NULL) { init_expr = FoldConstExpr(expr_pos, init_expr); } } set_library(saved_library); SetScript(saved_script, saved_token_pos); return init_expr; } void Parser::ParseInitializedInstanceFields( const Class& cls, LocalVariable* receiver, GrowableArray* initialized_fields) { TRACE_PARSER("ParseInitializedInstanceFields"); const Array& fields = Array::Handle(Z, cls.fields()); Field& f = Field::Handle(Z); const TokenPosition saved_pos = TokenPos(); for (int i = 0; i < fields.Length(); i++) { f ^= fields.At(i); if (!f.is_static() && f.has_initializer()) { Field& field = Field::ZoneHandle(Z); field ^= fields.At(i); if (field.is_final()) { // Final fields with initializer expression may not be initialized // again by constructors. Remember that this field is already // initialized. initialized_fields->Add(&field); } AstNode* init_expr = NULL; if (current_class().raw() != field.Origin()) { init_expr = ParseExternalInitializedField(field); } else { SetPosition(field.token_pos()); ASSERT(IsIdentifier()); ConsumeToken(); ExpectToken(Token::kASSIGN); if (current_class().is_const()) { // If the class has a const contructor, the initializer // expression must be a compile-time constant. init_expr = ParseConstExpr(); } else { TokenPosition expr_pos = TokenPos(); init_expr = ParseExpr(kAllowConst, kConsumeCascades); if (init_expr->EvalConstExpr() != NULL) { init_expr = FoldConstExpr(expr_pos, init_expr); } } } ASSERT(init_expr != NULL); AstNode* instance = new LoadLocalNode(field.token_pos(), receiver); EnsureExpressionTemp(); AstNode* field_init = new StoreInstanceFieldNode( field.token_pos(), instance, field, init_expr, /* is_initializer = */ true); current_block_->statements->Add(field_init); } } initialized_fields->Add(NULL); // End of inline initializers. SetPosition(saved_pos); } AstNode* Parser::CheckDuplicateFieldInit( TokenPosition init_pos, GrowableArray* initialized_fields, AstNode* instance, Field* field, AstNode* init_value) { ASSERT(!field->is_static()); AstNode* result = NULL; const String& field_name = String::Handle(field->name()); String& initialized_name = String::Handle(Z); // The initializer_list is divided into two sections. The sections // are separated by a NULL entry: [f0, ... fn, NULL, fn+1, ...] // The first fields f0 .. fn are final fields of the class that // have an initializer expression inlined in the class declaration. // The remaining fields are those initialized by the constructor's // initializing formals and initializer list int initializer_idx = 0; while (initializer_idx < initialized_fields->length()) { Field* initialized_field = (*initialized_fields)[initializer_idx]; initializer_idx++; if (initialized_field == NULL) { break; } initialized_name ^= initialized_field->name(); if (initialized_name.Equals(field_name) && field->has_initializer()) { ReportError(init_pos, "final field '%s' is already initialized.", field_name.ToCString()); } if (initialized_field->raw() == field->raw()) { // This final field has been initialized by an inlined // initializer expression. This is a runtime error. // Throw a NoSuchMethodError for the missing setter. ASSERT(field->is_final()); // Build a call to NoSuchMethodError::_throwNew( // Object receiver, // String memberName, // int invocation_type, // Object typeArguments, // List arguments, // List argumentNames); ArgumentListNode* nsm_args = new (Z) ArgumentListNode(init_pos); // Object receiver. nsm_args->Add(instance); // String memberName. String& setter_name = String::ZoneHandle(field->name()); setter_name = Field::SetterSymbol(setter_name); nsm_args->Add(new (Z) LiteralNode(init_pos, setter_name)); // Smi invocation_type. const int invocation_type = InvocationMirror::EncodeType( InvocationMirror::kDynamic, InvocationMirror::kSetter); nsm_args->Add(new (Z) LiteralNode( init_pos, Smi::ZoneHandle(Z, Smi::New(invocation_type)))); // Object typeArguments. nsm_args->Add(new (Z) LiteralNode(init_pos, Object::null_type_arguments())); // List arguments. GrowableArray setter_args; setter_args.Add(init_value); ArrayNode* setter_args_array = new (Z) ArrayNode( init_pos, Type::ZoneHandle(Z, Type::ArrayType()), setter_args); nsm_args->Add(setter_args_array); // List argumentNames. // The missing implicit setter of the field has no argument names. nsm_args->Add(new (Z) LiteralNode(init_pos, Object::null_array())); AstNode* nsm_call = MakeStaticCall( Symbols::NoSuchMethodError(), Library::PrivateCoreLibName(Symbols::ThrowNew()), nsm_args); LetNode* let = new (Z) LetNode(init_pos); let->AddNode(init_value); let->AddNode(nsm_call); result = let; } } // The remaining elements in initialized_fields are fields that // are initialized through initializing formal parameters, or // in the constructor's initializer list. If there is a duplicate, // it is a compile time error. while (initializer_idx < initialized_fields->length()) { Field* initialized_field = (*initialized_fields)[initializer_idx]; initializer_idx++; if (initialized_field->raw() == field->raw()) { ReportError(init_pos, "duplicate initializer for field %s", String::Handle(Z, field->name()).ToCString()); } } initialized_fields->Add(field); return result; } void Parser::ParseInitializers(const Class& cls, LocalVariable* receiver, GrowableArray* initialized_fields) { TRACE_PARSER("ParseInitializers"); bool super_init_is_last = false; intptr_t super_init_index = -1; StaticCallNode* super_init_call = NULL; if (CurrentToken() == Token::kCOLON) { do { ConsumeToken(); // Colon or comma. if (CurrentToken() == Token::kSUPER) { if (super_init_call != NULL) { ReportError("duplicate call to super constructor"); } super_init_call = ParseSuperInitializer(cls, receiver); super_init_index = current_block_->statements->length(); current_block_->statements->Add(super_init_call); super_init_is_last = true; } else { AstNode* init_statement = ParseInitializer(cls, receiver, initialized_fields); super_init_is_last = false; if (init_statement != NULL) { current_block_->statements->Add(init_statement); } } } while (CurrentToken() == Token::kCOMMA); } if (super_init_call == NULL) { // Generate implicit super() if we haven't seen an explicit super call // or constructor redirection. super_init_call = GenerateSuperConstructorCall(cls, TokenPos(), receiver, NULL); if (super_init_call != NULL) { super_init_index = current_block_->statements->length(); current_block_->statements->Add(super_init_call); super_init_is_last = true; } } if ((super_init_call != NULL) && !super_init_is_last) { // If the super initializer call is not at the end of the initializer // list, implicitly move it to the end. The actual parameter values // are evaluated at the original position in the list and preserved // in temporary variables. (The following initializer expressions // could have side effects that alter the arguments to the super // initializer.) E.g: // A(x) : super(x), f = x++ { ... } // is transformed to: // A(x) : temp = x, f = x++, super(temp) { ... } ASSERT(super_init_index >= 0); ArgumentListNode* ctor_args = super_init_call->arguments(); LetNode* saved_args = new (Z) LetNode(super_init_call->token_pos()); // The super initializer call has at least 1 arguments: the // implicit receiver. ASSERT(ctor_args->length() >= 1); for (int i = 1; i < ctor_args->length(); i++) { AstNode* arg = ctor_args->NodeAt(i); LocalVariable* temp = CreateTempConstVariable(arg->token_pos(), "sca"); AstNode* save_temp = new (Z) StoreLocalNode(arg->token_pos(), temp, arg); saved_args->AddNode(save_temp); ctor_args->SetNodeAt(i, new (Z) LoadLocalNode(arg->token_pos(), temp)); } current_block_->statements->ReplaceNodeAt(super_init_index, saved_args); current_block_->statements->Add(super_init_call); } CheckFieldsInitialized(cls); } void Parser::ParseConstructorRedirection(const Class& cls, LocalVariable* receiver) { TRACE_PARSER("ParseConstructorRedirection"); ExpectToken(Token::kCOLON); ASSERT(CurrentToken() == Token::kTHIS); const TokenPosition call_pos = TokenPos(); ConsumeToken(); String& ctor_name = String::Handle(Z, cls.Name()); GrowableHandlePtrArray pieces(Z, 3); pieces.Add(ctor_name); pieces.Add(Symbols::Dot()); if (CurrentToken() == Token::kPERIOD) { ConsumeToken(); pieces.Add(*ExpectIdentifier("constructor name expected")); } ctor_name = Symbols::FromConcatAll(T, pieces); CheckToken(Token::kLPAREN, "parameter list expected"); ArgumentListNode* arguments = new ArgumentListNode(call_pos); // 'this' parameter is the first argument to constructor. AstNode* implicit_argument = new LoadLocalNode(call_pos, receiver); arguments->Add(implicit_argument); receiver->set_invisible(true); ParseActualParameters(arguments, Object::null_type_arguments(), kAllowConst); receiver->set_invisible(false); // Resolve the constructor. const Function& redirect_ctor = Function::ZoneHandle(Z, cls.LookupConstructor(ctor_name)); if (redirect_ctor.IsNull()) { if (cls.LookupFactory(ctor_name) != Function::null()) { ReportError(call_pos, "redirection constructor '%s' must not be a factory", String::Handle(Z, String::ScrubName(ctor_name)).ToCString()); } ReportError(call_pos, "constructor '%s' not found", String::Handle(Z, String::ScrubName(ctor_name)).ToCString()); } if (current_function().is_const() && !redirect_ctor.is_const()) { ReportError(call_pos, "redirection constructor '%s' must be const", String::Handle(Z, redirect_ctor.UserVisibleName()).ToCString()); } String& error_message = String::Handle(Z); if (!redirect_ctor.AreValidArguments(arguments->type_args_len(), arguments->length(), arguments->names(), &error_message)) { ReportError(call_pos, "invalid arguments passed to constructor '%s': %s", String::Handle(Z, redirect_ctor.UserVisibleName()).ToCString(), error_message.ToCString()); } current_block_->statements->Add(new StaticCallNode( call_pos, redirect_ctor, arguments, StaticCallNode::kStatic)); } SequenceNode* Parser::MakeImplicitConstructor(const Function& func) { ASSERT(func.IsGenerativeConstructor()); ASSERT(func.Owner() == current_class().raw()); const TokenPosition ctor_pos = TokenPos(); OpenFunctionBlock(func); LocalVariable* receiver = new LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::This(), *ReceiverType(current_class())); current_block_->scope->InsertParameterAt(0, receiver); // Parse expressions of instance fields that have an explicit // initializer expression. // The receiver must not be visible to field initializer expressions. receiver->set_invisible(true); GrowableArray initialized_fields; ParseInitializedInstanceFields(current_class(), receiver, &initialized_fields); receiver->set_invisible(false); // If the class of this implicit constructor is a mixin application alias, // it is a forwarding constructor of the aliased mixin application class. // If the class of this implicit constructor is a mixin application class, // it is a forwarding constructor of the mixin. The forwarding // constructor initializes the instance fields that have initializer // expressions and then calls the respective super constructor with // the same name and number of parameters. ArgumentListNode* forwarding_args = NULL; if (current_class().is_mixin_app_alias() || current_class().IsMixinApplication()) { // At this point we don't support forwarding constructors // that have optional parameters because we don't know the default // values of the optional parameters. We would have to compile the super // constructor to get the default values. Also, the spec is not clear // whether optional parameters are even allowed in this situation. // TODO(hausner): Remove this limitation if the language spec indeed // allows optional parameters. if (func.HasOptionalParameters()) { const Class& super_class = Class::Handle(Z, current_class().SuperClass()); ReportError(ctor_pos, "cannot generate an implicit mixin application constructor " "forwarding to a super class constructor with optional " "parameters; add a constructor without optional parameters " "to class '%s' that redirects to the constructor with " "optional parameters and invoke it via super from a " "constructor of the class extending the mixin application", String::Handle(Z, super_class.Name()).ToCString()); } // Prepare user-defined arguments to be forwarded to super call. // The first user-defined argument is at position 1. forwarding_args = new ArgumentListNode(ST(ctor_pos)); for (int i = 1; i < func.NumParameters(); i++) { LocalVariable* param = new LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, String::ZoneHandle(Z, func.ParameterNameAt(i)), Object::dynamic_type()); current_block_->scope->InsertParameterAt(i, param); forwarding_args->Add(new LoadLocalNode(ST(ctor_pos), param)); } } AstNode* super_call = GenerateSuperConstructorCall(current_class(), ctor_pos, receiver, forwarding_args); if (super_call != NULL) { current_block_->statements->Add(super_call); } CheckFieldsInitialized(current_class()); // Empty constructor body. current_block_->statements->Add(new ReturnNode(ST(ctor_pos))); SequenceNode* statements = CloseBlock(); return statements; } // Returns a zone allocated string. static char* DumpPendingFunctions( Zone* zone, const GrowableObjectArray& pending_functions) { ASSERT(zone != NULL); char* result = OS::SCreate(zone, "Pending Functions:\n"); for (intptr_t i = 0; i < pending_functions.Length(); i++) { const Function& func = Function::Handle(zone, Function::RawCast(pending_functions.At(i))); const String& fname = String::Handle(zone, func.UserVisibleName()); result = OS::SCreate(zone, "%s%" Pd ": %s\n", result, i, fname.ToCString()); } return result; } void Parser::CheckRecursiveInvocation() { const GrowableObjectArray& pending_functions = GrowableObjectArray::Handle(Z, T->pending_functions()); ASSERT(!pending_functions.IsNull()); for (int i = 0; i < pending_functions.Length(); i++) { if (pending_functions.At(i) == current_function().raw()) { const String& fname = String::Handle(Z, current_function().UserVisibleName()); if (FLAG_trace_service) { const char* pending_function_dump = DumpPendingFunctions(Z, pending_functions); ASSERT(pending_function_dump != NULL); ReportError("circular dependency for function %s\n%s", fname.ToCString(), pending_function_dump); } else { ReportError("circular dependency for function %s", fname.ToCString()); } } } ASSERT(!unregister_pending_function_); pending_functions.Add(current_function(), Heap::kOld); unregister_pending_function_ = true; } // Parser is at the opening parenthesis of the formal parameter declaration // of function. Parse the formal parameters, initializers and code. SequenceNode* Parser::ParseConstructor(const Function& func) { TRACE_PARSER("ParseConstructor"); ASSERT(func.IsGenerativeConstructor()); ASSERT(!func.IsFactory()); ASSERT(!func.is_static()); ASSERT(!func.IsLocalFunction()); const Class& cls = Class::Handle(Z, func.Owner()); ASSERT(!cls.IsNull()); CheckRecursiveInvocation(); if (func.IsImplicitConstructor()) { // Special case: implicit constructor. // The parser adds an implicit default constructor when a class // does not have any explicit constructor or factory (see // Parser::AddImplicitConstructor). // There is no source text to parse. We just build the // sequence node by hand. return MakeImplicitConstructor(func); } OpenFunctionBlock(func); ParamList params; ASSERT(CurrentToken() == Token::kLPAREN); // Add implicit receiver parameter which is passed the allocated // but uninitialized instance to construct. ASSERT(current_class().raw() == func.Owner()); params.AddReceiver(ReceiverType(current_class()), func.token_pos()); if (func.is_const()) { params.SetImplicitlyFinal(); } 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); ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved()); // Now populate function scope with the formal parameters. AddFormalParamsToScope(¶ms, current_block_->scope); const bool is_redirecting_constructor = (CurrentToken() == Token::kCOLON) && ((LookaheadToken(1) == Token::kTHIS) && ((LookaheadToken(2) == Token::kLPAREN) || ((LookaheadToken(2) == Token::kPERIOD) && (LookaheadToken(4) == Token::kLPAREN)))); GrowableArray initialized_fields; LocalVariable* receiver = (*params.parameters)[0].var; OpenBlock(); // If this is not a redirecting constructor, initialize // instance fields that have an explicit initializer expression. if (!is_redirecting_constructor) { // The formal parameter names must not be visible to the instance // field initializer expressions, yet the parameters must be added to // the scope so the expressions use the correct offsets for 'this' when // storing values. We make the formal parameters temporarily invisible // while parsing the instance field initializer expressions. params.SetInvisible(true); ParseInitializedInstanceFields(cls, receiver, &initialized_fields); // Make the parameters (which are in the outer scope) visible again. params.SetInvisible(false); } // Turn formal field parameters into field initializers. if (params.has_field_initializer) { // The first parameter is the implicit receiver. ASSERT(params.parameters->length() >= 1); for (int i = 1; i < params.parameters->length(); i++) { ParamDesc& param = (*params.parameters)[i]; if (param.is_field_initializer) { const String& field_name = *param.name; Field& field = Field::ZoneHandle(Z, cls.LookupInstanceField(field_name)); if (field.IsNull()) { ReportError(param.name_pos, "unresolved reference to instance field '%s'", field_name.ToCString()); } if (is_redirecting_constructor) { ReportError(param.name_pos, "redirecting constructors may not have " "initializing formal parameters"); } if (!param.has_explicit_type) { const AbstractType& field_type = AbstractType::ZoneHandle(Z, field.type()); param.type = &field_type; // Parameter type was already set to dynamic when parsing the class // declaration: fix it. func.SetParameterTypeAt(i, field_type); } AstNode* instance = new LoadLocalNode(param.name_pos, receiver); // Initializing formals cannot be used in the explicit initializer // list, nor can they be used in the constructor body. // Thus, they are set to be invisible when added to the scope. LocalVariable* p = param.var; ASSERT(p != NULL); AstNode* value = new LoadLocalNode(param.name_pos, p); EnsureExpressionTemp(); AstNode* initializer = CheckDuplicateFieldInit( param.name_pos, &initialized_fields, instance, &field, value); if (initializer == NULL) { initializer = new (Z) StoreInstanceFieldNode(param.name_pos, instance, field, value, /* is_initializer = */ true); } current_block_->statements->Add(initializer); } } } if (is_redirecting_constructor) { ParseConstructorRedirection(cls, receiver); } else { ParseInitializers(cls, receiver, &initialized_fields); } SequenceNode* init_statements = CloseBlock(); current_block_->statements->Add(init_statements); // Parsing of initializers done. Now we parse the constructor body. OpenBlock(); // Block to collect constructor body nodes. params.HideInitFormals(); if (CurrentToken() == Token::kLBRACE) { // We checked in the top-level parse phase that a redirecting // constructor does not have a body. ASSERT(!is_redirecting_constructor); ConsumeToken(); ParseStatementSequence(); ExpectToken(Token::kRBRACE); } else if (CurrentToken() == Token::kARROW) { ReportError("constructors may not return a value"); } else if (IsSymbol(Symbols::Native())) { ReportError("native constructors not supported"); } else if (CurrentToken() == Token::kSEMICOLON) { // Some constructors have no function body. ConsumeToken(); if (func.is_external()) { // Body of an external method contains a single throw. const String& function_name = String::ZoneHandle(func.name()); current_block_->statements->Add(ThrowNoSuchMethodError( TokenPos(), cls, function_name, NULL, // No arguments. InvocationMirror::kStatic, InvocationMirror::kMethod, NULL)); // No existing function. } } else { UnexpectedToken(); } SequenceNode* ctor_block = CloseBlock(); if (ctor_block->length() > 0) { current_block_->statements->Add(ctor_block); } current_block_->statements->Add(new ReturnNode(func.end_token_pos())); SequenceNode* statements = CloseBlock(); return statements; } // Parser is at the opening parenthesis of the formal parameter // declaration of the function or constructor. // Parse the formal parameters and code. SequenceNode* Parser::ParseFunc(const Function& func, bool check_semicolon) { TRACE_PARSER("ParseFunc"); ASSERT(innermost_function().raw() == func.raw()); // Save current try index. Try index starts at zero for each function. intptr_t saved_try_index = last_used_try_index_; last_used_try_index_ = 0; // In case of nested async functions we also need to save the scope where // temporaries are added. LocalScope* saved_async_temp_scope = async_temp_scope_; if (func.IsGenerativeConstructor()) { SequenceNode* statements = ParseConstructor(func); last_used_try_index_ = saved_try_index; return statements; } ASSERT(!func.IsGenerativeConstructor()); OpenFunctionBlock(func); // Build local scope for function. if (Isolate::Current()->reify_generic_functions()) { // Lookup function type arguments variable in parent function scope, if any. if (func.HasGenericParent()) { const String* variable_name = &Symbols::FunctionTypeArgumentsVar(); LocalVariable* parent_type_arguments = current_block_->scope->LookupVariable(*variable_name, true); ASSERT(parent_type_arguments != NULL); // TODO(regis): It may be too early to capture parent_type_arguments here. // In case it is never used, we could save capturing and concatenating. current_block_->scope->CaptureVariable(parent_type_arguments); if (FunctionLevel() == 0) { parsed_function_->set_parent_type_arguments(parent_type_arguments); if (!func.IsGeneric() && parent_type_arguments->is_captured()) { parsed_function_->set_function_type_arguments(parent_type_arguments); } } } if (func.IsGeneric()) { // Insert function type arguments variable to scope. LocalVariable* function_type_arguments = new (Z) LocalVariable( TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::FunctionTypeArgumentsVar(), Object::dynamic_type()); current_block_->scope->AddVariable(function_type_arguments); if (FunctionLevel() == 0) { parsed_function_->set_function_type_arguments(function_type_arguments); } } } ParamList params; // An instance closure function may capture and access the receiver, but via // the context and not via the first formal parameter. if (func.IsClosureFunction()) { // The first parameter of a closure function is the closure object. ASSERT(!func.is_const()); // Closure functions cannot be const. params.AddFinalParameter(TokenPos(), &Symbols::ClosureParameter(), &Object::dynamic_type()); } else if (!func.is_static()) { // Static functions do not have a receiver. ASSERT(current_class().raw() == func.Owner()); params.AddReceiver(ReceiverType(current_class()), func.token_pos()); } else if (func.IsFactory()) { // The first parameter of a factory is the TypeArguments vector of // the type of the instance to be allocated. params.AddFinalParameter(TokenPos(), &Symbols::TypeArgumentsParameter(), &Object::dynamic_type()); } // Expect the parameter list unless this is a getter function, or the // body closure of an async or generator getter function. ASSERT((CurrentToken() == Token::kLPAREN) || func.IsGetterFunction() || (func.is_generated_body() && Function::Handle(func.parent_function()).IsGetterFunction())); if (func.IsGetterFunction()) { // Populate function scope with the formal parameters. Since in this case // we are compiling a getter this will at most populate the receiver. AddFormalParamsToScope(¶ms, current_block_->scope); } else if (func.IsAsyncClosure()) { AddAsyncClosureParameters(¶ms); SetupDefaultsForOptionalParams(params); AddFormalParamsToScope(¶ms, current_block_->scope); ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved()); ASSERT(func.NumParameters() == params.parameters->length()); if (!Function::Handle(func.parent_function()).IsGetterFunction()) { // Skip formal parameters. They are accessed as context variables. // Parsing them again (and discarding them) does not work in case of // default values with same name as already parsed formal parameter. SkipToMatchingParenthesis(); } } else if (func.IsSyncGenClosure()) { AddSyncGenClosureParameters(¶ms); SetupDefaultsForOptionalParams(params); AddFormalParamsToScope(¶ms, current_block_->scope); ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved()); if (!Function::Handle(func.parent_function()).IsGetterFunction()) { // Skip formal parameters. They are accessed as context variables. // Parsing them again (and discarding them) does not work in case of // default values with same name as already parsed formal parameter. SkipToMatchingParenthesis(); } } else if (func.IsAsyncGenClosure()) { AddAsyncGenClosureParameters(¶ms); SetupDefaultsForOptionalParams(params); AddFormalParamsToScope(¶ms, current_block_->scope); ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved()); ASSERT(func.NumParameters() == params.parameters->length()); if (!Function::Handle(func.parent_function()).IsGetterFunction()) { // Skip formal parameters. They are accessed as context variables. // Parsing them again (and discarding them) does not work in case of // default values with same name as already parsed formal parameter. SkipToMatchingParenthesis(); } } else { 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); if (!is_top_level_) { FinalizeFormalParameterTypes(¶ms); } // The number of parameters and their type are not yet set in local // functions, since they are not 'top-level' parsed. // However, they are already set when the local function is compiled, since // the local function was parsed when its parent was compiled. if (func.parameter_types() == Object::empty_array().raw()) { AddFormalParamsToFunction(¶ms, func); } ResolveSignatureTypeParameters(func); if (!is_top_level_) { ClassFinalizer::FinalizeSignature(Class::Handle(Z, func.origin()), func); } SetupDefaultsForOptionalParams(params); ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved()); // Populate function scope with the formal parameters. AddFormalParamsToScope(¶ms, current_block_->scope); } const TokenPosition modifier_pos = TokenPos(); RawFunction::AsyncModifier func_modifier = ParseFunctionModifier(); if (!func.is_generated_body()) { // Don't add a modifier to the closure representing the body of // the asynchronous function or generator. func.set_modifier(func_modifier); } OpenBlock(); // Open a nested scope for the outermost function block. Function& generated_body_closure = Function::ZoneHandle(Z); if (func.IsAsyncFunction()) { ASSERT(!func.is_generated_body()); // The code of an async function is synthesized. Disable debugging. func.set_is_debuggable(false); if (FLAG_causal_async_stacks) { // In order to collect causal asynchronous stacks efficiently we rely on // this function not being inlined. func.set_is_inlinable(false); } generated_body_closure = OpenAsyncFunction(func.token_pos()); } else if (func.IsAsyncClosure()) { // The closure containing the body of an async function is debuggable. ASSERT(func.is_debuggable()); if (FLAG_causal_async_stacks) { // In order to collect causal asynchronous stacks efficiently we rely on // this function not being inlined. func.set_is_inlinable(false); } OpenAsyncClosure(); } else if (func.IsSyncGenerator()) { // The code of a sync generator is synthesized. Disable debugging. func.set_is_debuggable(false); generated_body_closure = OpenSyncGeneratorFunction(func.token_pos()); } else if (func.IsSyncGenClosure()) { // The closure containing the body of a sync generator is debuggable. ASSERT(func.is_debuggable()); async_temp_scope_ = current_block_->scope; } else if (func.IsAsyncGenerator()) { func.set_is_debuggable(false); if (FLAG_causal_async_stacks) { // In order to collect causal asynchronous stacks efficiently we rely on // this function not being inlined. func.set_is_inlinable(false); } generated_body_closure = OpenAsyncGeneratorFunction(func.token_pos()); } else if (func.IsAsyncGenClosure()) { // The closure containing the body of an async* function is debuggable. ASSERT(func.is_debuggable()); if (FLAG_causal_async_stacks) { // In order to collect causal asynchronous stacks efficiently we rely on // this function not being inlined. func.set_is_inlinable(false); } OpenAsyncGeneratorClosure(); } // Function level is now correctly set to parse the (possibly async) body. if (I->type_checks() && (FunctionLevel() > 0)) { // We are parsing, but not compiling, a local function. // The instantiator may be required at run time for generic type checks. // Note that the source of this local function may not reference the // generic type explicitly. However, it may assign a value to a captured // variable declared with its generic type in the enclosing function. // Make sure that the receiver of the enclosing instance function // (or implicit first parameter of an enclosing factory) is marked as // captured if type checks are enabled, because they may access it to // instantiate types. // If any enclosing parent of the function being parsed is generic, capture // their function type arguments. CaptureAllInstantiators(); } BoolScope allow_await(&this->await_is_keyword_, func.IsAsyncOrGenerator() || func.is_generated_body()); TokenPosition end_token_pos = TokenPosition::kNoSource; if (CurrentToken() == Token::kLBRACE) { ConsumeToken(); if (String::Handle(Z, func.name()).Equals(Symbols::EqualOperator())) { const Class& owner = Class::Handle(Z, func.Owner()); if (!owner.IsObjectClass()) { AddEqualityNullCheck(); } } ParseStatementSequence(); end_token_pos = TokenPos(); ExpectToken(Token::kRBRACE); } else if (CurrentToken() == Token::kARROW) { if (func.IsGenerator()) { ReportError(modifier_pos, "=> style function may not be sync* or async* generator"); } ConsumeToken(); if (String::Handle(Z, func.name()).Equals(Symbols::EqualOperator())) { const Class& owner = Class::Handle(Z, func.Owner()); if (!owner.IsObjectClass()) { AddEqualityNullCheck(); } } const TokenPosition expr_pos = TokenPos(); AstNode* expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL); ASSERT(expr != NULL); expr = AddAsyncResultTypeCheck(expr_pos, expr); current_block_->statements->Add(new (Z) ReturnNode(expr_pos, expr)); end_token_pos = TokenPos(); if (check_semicolon) { ExpectSemicolon(); } } else if (IsSymbol(Symbols::Native())) { if (String::Handle(Z, func.name()).Equals(Symbols::EqualOperator())) { const Class& owner = Class::Handle(Z, func.Owner()); if (!owner.IsObjectClass()) { AddEqualityNullCheck(); } } ParseNativeFunctionBlock(¶ms, func); end_token_pos = TokenPos(); ExpectSemicolon(); } else if (func.is_external()) { // Body of an external method contains a single throw. const String& function_name = String::ZoneHandle(Z, func.name()); current_block_->statements->Add(ThrowNoSuchMethodError( TokenPos(), Class::Handle(func.Owner()), function_name, NULL, // Ignore arguments. func.is_static() ? InvocationMirror::kStatic : InvocationMirror::kDynamic, InvocationMirror::kMethod, &func)); // Unpatched external function. end_token_pos = TokenPos(); } else { UnexpectedToken(); } ASSERT(func.end_token_pos() == func.token_pos() || func.end_token_pos() == end_token_pos); func.set_end_token_pos(end_token_pos); SequenceNode* body = CloseBlock(); if (Isolate::Current()->reify_generic_functions() && func.IsGeneric() && !generated_body_closure.IsNull()) { LocalVariable* existing_var = body->scope()->LookupVariable( Symbols::FunctionTypeArgumentsVar(), false); ASSERT((existing_var != NULL) && existing_var->is_captured()); } if (func.IsAsyncFunction()) { body = CloseAsyncFunction(generated_body_closure, body); generated_body_closure.set_end_token_pos(end_token_pos); } else if (func.IsAsyncClosure()) { body = CloseAsyncClosure(body, end_token_pos); } else if (func.IsSyncGenerator()) { body = CloseSyncGenFunction(generated_body_closure, body); generated_body_closure.set_end_token_pos(end_token_pos); } else if (func.IsSyncGenClosure()) { // body is unchanged. } else if (func.IsAsyncGenerator()) { body = CloseAsyncGeneratorFunction(generated_body_closure, body); generated_body_closure.set_end_token_pos(end_token_pos); } else if (func.IsAsyncGenClosure()) { body = CloseAsyncGeneratorClosure(body); } EnsureHasReturnStatement(body, end_token_pos); current_block_->statements->Add(body); last_used_try_index_ = saved_try_index; async_temp_scope_ = saved_async_temp_scope; return CloseBlock(); } void Parser::AddEqualityNullCheck() { AstNode* argument = new LoadLocalNode( TokenPosition::kNoSource, current_block_->scope->parent()->VariableAt(1)); LiteralNode* null_operand = new LiteralNode(TokenPosition::kNoSource, Instance::ZoneHandle(Z)); ComparisonNode* check_arg = new ComparisonNode( TokenPosition::kNoSource, Token::kEQ_STRICT, argument, null_operand); ComparisonNode* result = new ComparisonNode(TokenPosition::kNoSource, Token::kEQ_STRICT, LoadReceiver(TokenPosition::kNoSource), null_operand); SequenceNode* arg_is_null = new SequenceNode(TokenPosition::kNoSource, current_block_->scope); arg_is_null->Add(new ReturnNode(TokenPosition::kNoSource, result)); IfNode* if_arg_null = new IfNode(TokenPosition::kNoSource, check_arg, arg_is_null, NULL); current_block_->statements->Add(if_arg_null); } AstNode* Parser::AddAsyncResultTypeCheck(TokenPosition expr_pos, AstNode* expr) { if (I->type_checks() && (((FunctionLevel() == 0) && current_function().IsAsyncClosure()))) { // In checked mode, when the declared result type is Future, verify // that the returned expression is of type T or Future as follows: // return temp = expr, temp is Future ? temp as Future : temp as T; // In case of a mismatch, we need a TypeError and not a CastError, so // we do not actually implement an "as" test, but an "assignable" test. Function& async_func = Function::Handle(Z, current_function().parent_function()); const AbstractType& result_type = AbstractType::ZoneHandle(Z, async_func.result_type()); const Class& future_class = Class::ZoneHandle(Z, I->object_store()->future_class()); ASSERT(!future_class.IsNull()); if (result_type.type_class() == future_class.raw()) { const TypeArguments& result_type_args = TypeArguments::ZoneHandle(Z, result_type.arguments()); if (!result_type_args.IsNull() && (result_type_args.Length() == 1)) { const AbstractType& result_type_arg = AbstractType::ZoneHandle(Z, result_type_args.TypeAt(0)); LetNode* checked_expr = new (Z) LetNode(expr_pos); LocalVariable* temp = checked_expr->AddInitializer(expr); temp->set_is_final(); const AbstractType& future_type = AbstractType::ZoneHandle(Z, future_class.RareType()); AstNode* is_future = new (Z) LoadLocalNode(expr_pos, temp); is_future = new (Z) ComparisonNode(expr_pos, Token::kIS, is_future, new (Z) TypeNode(expr_pos, future_type)); AstNode* as_future_t = new (Z) LoadLocalNode(expr_pos, temp); as_future_t = new (Z) AssignableNode(expr_pos, as_future_t, result_type, Symbols::FunctionResult()); AstNode* as_t = new (Z) LoadLocalNode(expr_pos, temp); as_t = new (Z) AssignableNode(expr_pos, as_t, result_type_arg, Symbols::FunctionResult()); checked_expr->AddNode(new (Z) ConditionalExprNode(expr_pos, is_future, as_future_t, as_t)); expr = checked_expr; } } } return expr; } void Parser::SkipIf(Token::Kind token) { if (CurrentToken() == token) { ConsumeToken(); } } void Parser::SkipInitializers() { ASSERT(CurrentToken() == Token::kCOLON); do { ConsumeToken(); // Colon or comma. if (CurrentToken() == Token::kSUPER) { ConsumeToken(); if (CurrentToken() == Token::kPERIOD) { ConsumeToken(); ExpectIdentifier("identifier expected"); } CheckToken(Token::kLPAREN); SkipToMatchingParenthesis(); } else if (CurrentToken() == Token::kASSERT) { ConsumeToken(); CheckToken(Token::kLPAREN); SkipToMatchingParenthesis(); } else { SkipIf(Token::kTHIS); SkipIf(Token::kPERIOD); ExpectIdentifier("identifier expected"); ExpectToken(Token::kASSIGN); SetAllowFunctionLiterals(false); SkipExpr(); SetAllowFunctionLiterals(true); } } while (CurrentToken() == Token::kCOMMA); } // If the current identifier is a library prefix followed by a period, // consume the identifier and period, and return the resolved library // prefix. RawLibraryPrefix* Parser::ParsePrefix() { ASSERT(IsIdentifier()); // A library prefix can never stand by itself. It must be followed by // a period. Token::Kind next_token = LookaheadToken(1); if (next_token != Token::kPERIOD) { return LibraryPrefix::null(); } const String& ident = *CurrentLiteral(); // It is relatively fast to look up a name in the library dictionary, // compared to searching the nested local scopes. Look up the name // in the library scope and return in the common case where ident is // not a library prefix. LibraryPrefix& prefix = LibraryPrefix::Handle(Z, library_.LookupLocalLibraryPrefix(ident)); if (prefix.IsNull()) { return LibraryPrefix::null(); } // A library prefix with the name exists. Now check whether it is // shadowed by a local definition. if (!is_top_level_ && ResolveIdentInLocalScope(TokenPos(), ident, NULL, NULL)) { return LibraryPrefix::null(); } // Check whether the identifier is shadowed by a function type parameter. if (InGenericFunctionScope() && (innermost_function().LookupTypeParameter( ident, NULL) != TypeParameter::null())) { return LibraryPrefix::null(); } // Check whether the identifier is shadowed by a class type parameter. ASSERT(!current_class().IsNull()); if (current_class().LookupTypeParameter(ident) != TypeParameter::null()) { return LibraryPrefix::null(); } // We have a name that is not shadowed, followed by a period. // Consume the identifier, let the caller consume the period. ConsumeToken(); return prefix.raw(); } void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) { TRACE_PARSER("ParseMethodOrConstructor"); // We are at the beginning of the formal parameters list. ASSERT(CurrentToken() == Token::kLPAREN || CurrentToken() == Token::kLT || method->IsGetter()); ASSERT(method->type != NULL); // May still be unresolved. ASSERT(current_member_ == method); if (method->has_covariant) { ReportError(method->name_pos, "methods and constructors cannot be declared covariant"); } if (method->has_var) { ReportError(method->name_pos, "keyword var not allowed for methods"); } if (method->has_final) { ReportError(method->name_pos, "'final' not allowed for methods"); } if (method->has_abstract && method->has_static) { ReportError(method->name_pos, "static method '%s' cannot be abstract", method->name->ToCString()); } if (method->has_const && !method->IsFactoryOrConstructor()) { ReportError(method->name_pos, "'const' not allowed for methods"); } if (method->has_abstract && method->IsFactoryOrConstructor()) { ReportError(method->name_pos, "constructor cannot be abstract"); } if (method->has_const && method->IsConstructor()) { current_class().set_is_const(); } Function& func = Function::Handle( Z, Function::New(*method->name, // May change. method->kind, method->has_static, method->has_const, method->has_abstract, // May change. method->has_external, method->has_native, // May change. current_class(), method->decl_begin_pos)); ASSERT(innermost_function().IsNull()); innermost_function_ = func.raw(); if (CurrentToken() == Token::kLT) { TokenPosition type_param_pos = TokenPos(); if (method->IsFactoryOrConstructor()) { ReportError(method->name_pos, "constructor cannot be generic"); } if (method->IsGetter() || method->IsSetter()) { ReportError(type_param_pos, "%s cannot be generic", method->IsGetter() ? "getter" : "setter"); } ParseTypeParameters(false); // Not parameterizing class, but function. } // Parse the formal parameters. const TokenPosition formal_param_pos = TokenPos(); method->params.Clear(); // Static functions do not have a receiver. // The first parameter of a factory is the TypeArguments vector of // the type of the instance to be allocated. if (!method->has_static || method->IsConstructor()) { method->params.AddReceiver(ReceiverType(current_class()), formal_param_pos); } else if (method->IsFactory()) { method->params.AddFinalParameter(formal_param_pos, &Symbols::TypeArgumentsParameter(), &Object::dynamic_type()); } if (method->has_const) { method->params.SetImplicitlyFinal(); } if (!method->IsGetter()) { 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, &method->params); } // Now that we know the parameter list, we can distinguish between the // unary and binary operator -. if (method->has_operator) { if ((method->operator_token == Token::kSUB) && (method->params.num_fixed_parameters == 1)) { // Patch up name for unary operator - so it does not clash with the // name for binary operator -. method->operator_token = Token::kNEGATE; *method->name = Symbols::Token(Token::kNEGATE).raw(); } CheckOperatorArity(*method); } // Mangle the name for getter and setter functions and check function // arity. if (method->IsGetter() || method->IsSetter()) { int expected_num_parameters = 0; if (method->IsGetter()) { expected_num_parameters = (method->has_static) ? 0 : 1; method->dict_name = method->name; method->name = &String::ZoneHandle(Z, Field::GetterSymbol(*method->name)); } else { ASSERT(method->IsSetter()); expected_num_parameters = (method->has_static) ? 1 : 2; method->dict_name = &String::ZoneHandle( Z, Symbols::FromConcat(T, *method->name, Symbols::Equals())); method->name = &String::ZoneHandle(Z, Field::SetterSymbol(*method->name)); } if ((method->params.num_fixed_parameters != expected_num_parameters) || (method->params.num_optional_parameters != 0)) { ReportError(method->name_pos, "illegal %s parameters", method->IsGetter() ? "getter" : "setter"); } } // Parse redirecting factory constructor. Type& redirection_type = Type::Handle(Z); String& redirection_identifier = String::Handle(Z); bool is_redirecting = false; if (method->IsFactory() && (CurrentToken() == Token::kASSIGN)) { // Default parameter values are disallowed in redirecting factories. if (method->params.has_explicit_default_values) { ReportError( "redirecting factory '%s' may not specify default values " "for optional parameters", method->name->ToCString()); } if (method->has_external) { ReportError(TokenPos(), "external factory constructor '%s' may not have redirection", method->name->ToCString()); } ConsumeToken(); const TokenPosition type_pos = TokenPos(); is_redirecting = true; const bool consume_unresolved_prefix = (LookaheadToken(3) == Token::kLT) || (LookaheadToken(3) == Token::kPERIOD); const AbstractType& type = AbstractType::Handle( Z, ParseType(ClassFinalizer::kResolveTypeParameters, true, consume_unresolved_prefix)); if (!type.IsMalformed() && type.IsTypeParameter()) { // Replace the type with a malformed type and compile a throw when called. redirection_type = ClassFinalizer::NewFinalizedMalformedType( Error::Handle(Z), // No previous error. script_, type_pos, "factory '%s' may not redirect to type parameter '%s'", method->name->ToCString(), String::Handle(Z, type.UserVisibleName()).ToCString()); } else { // We handle malformed and malbounded redirection type at run time. redirection_type ^= type.raw(); } if (CurrentToken() == Token::kPERIOD) { // Named constructor or factory. ConsumeToken(); redirection_identifier = ExpectIdentifier("identifier expected")->raw(); } } else if (CurrentToken() == Token::kCOLON) { // Parse initializers. if (!method->IsConstructor()) { ReportError("initializers only allowed on constructors"); } if (method->has_external) { ReportError(TokenPos(), "external constructor '%s' may not have initializers", method->name->ToCString()); } if ((LookaheadToken(1) == Token::kTHIS) && ((LookaheadToken(2) == Token::kLPAREN) || LookaheadToken(4) == Token::kLPAREN)) { // Redirected constructor: either this(...) or this.xxx(...). is_redirecting = true; if (method->params.has_field_initializer) { // Constructors that redirect to another constructor must not // initialize any fields using field initializer parameters. ReportError(formal_param_pos, "Redirecting constructor " "may not use field initializer parameters"); } ConsumeToken(); // Colon. ExpectToken(Token::kTHIS); GrowableHandlePtrArray pieces(Z, 3); pieces.Add(members->class_name()); pieces.Add(Symbols::Dot()); if (CurrentToken() == Token::kPERIOD) { ConsumeToken(); pieces.Add(*ExpectIdentifier("constructor name expected")); } String& redir_name = String::ZoneHandle(Z, Symbols::FromConcatAll(T, pieces)); method->redirect_name = &redir_name; CheckToken(Token::kLPAREN); SkipToMatchingParenthesis(); } else { SkipInitializers(); } } // Only constructors can redirect to another method. ASSERT((method->redirect_name == NULL) || method->IsConstructor()); if (method->IsConstructor() && method->has_external && method->params.has_field_initializer) { ReportError(method->name_pos, "external constructor '%s' may not have field initializers", method->name->ToCString()); } const TokenPosition modifier_pos = TokenPos(); RawFunction::AsyncModifier async_modifier = ParseFunctionModifier(); if ((method->IsFactoryOrConstructor() || method->IsSetter()) && (async_modifier != RawFunction::kNoModifier)) { ReportError(modifier_pos, "%s '%s' may not be async, async* or sync*", (method->IsSetter()) ? "setter" : "constructor", method->name->ToCString()); } TokenPosition method_end_pos = TokenPos(); String* native_name = NULL; if ((CurrentToken() == Token::kLBRACE) || (CurrentToken() == Token::kARROW)) { if (method->has_abstract) { ReportError(TokenPos(), "abstract method '%s' may not have a function body", method->name->ToCString()); } else if (method->has_external) { ReportError(TokenPos(), "external %s '%s' may not have a function body", method->IsFactoryOrConstructor() ? "constructor" : "method", method->name->ToCString()); } else if (method->IsConstructor() && method->has_const) { ReportError(TokenPos(), "const constructor '%s' may not have a function body", method->name->ToCString()); } else if (method->IsFactory() && method->has_const) { ReportError(TokenPos(), "const factory '%s' may not have a function body", method->name->ToCString()); } if (method->redirect_name != NULL) { ReportError(method->name_pos, "Constructor with redirection may not have a function body"); } if (CurrentToken() == Token::kLBRACE) { SkipBlock(); method_end_pos = TokenPos(); ExpectToken(Token::kRBRACE); } else { if ((async_modifier & RawFunction::kGeneratorBit) != 0) { ReportError(modifier_pos, "=> style function may not be sync* or async* generator"); } ConsumeToken(); BoolScope allow_await(&this->await_is_keyword_, async_modifier != RawFunction::kNoModifier); SkipExpr(); method_end_pos = TokenPos(); ExpectSemicolon(); } } else if (IsSymbol(Symbols::Native())) { if (method->has_abstract) { ReportError(method->name_pos, "abstract method '%s' may not have a function body", method->name->ToCString()); } else if (method->IsConstructor() && method->has_const) { ReportError(method->name_pos, "const constructor '%s' may not be native", method->name->ToCString()); } if (method->redirect_name != NULL) { ReportError(method->name_pos, "Constructor with redirection may not have a function body"); } native_name = &ParseNativeDeclaration(); method_end_pos = TokenPos(); ExpectSemicolon(); method->has_native = true; } else { // We haven't found a method body. Issue error if one is required. const bool must_have_body = method->has_static && !method->has_external && redirection_type.IsNull(); if (must_have_body) { ReportError(method->name_pos, "function body expected for method '%s'", method->name->ToCString()); } if (CurrentToken() == Token::kSEMICOLON) { ConsumeToken(); if (!method->has_static && !method->has_external && !method->IsConstructor()) { // Methods, getters and setters without a body are // implicitly abstract. method->has_abstract = true; } } else { // Signature is not followed by semicolon or body. Issue an // appropriate error. const bool must_have_semicolon = (method->redirect_name != NULL) || (method->IsConstructor() && method->has_const) || method->has_external; if (must_have_semicolon) { ExpectSemicolon(); } else { ReportError(method->name_pos, "function body or semicolon expected for method '%s'", method->name->ToCString()); } } } if (method->has_abstract && (async_modifier != RawFunction::kNoModifier)) { ReportError(modifier_pos, "abstract function '%s' may not be async, async* or sync*", method->name->ToCString()); } // Update function object. func.set_name(*method->name); func.set_is_abstract(method->has_abstract); func.set_is_native(method->has_native); func.set_result_type(*method->type); // The result type may refer to func's type parameters, // but was not parsed in the scope of func. Adjust. method->type->SetScopeFunction(func); func.set_end_token_pos(method_end_pos); func.set_is_redirecting(is_redirecting); func.set_modifier(async_modifier); if (library_.is_dart_scheme() && library_.IsPrivate(*method->name)) { func.set_is_reflectable(false); } if (is_patch_source() && IsPatchAnnotation(method->metadata_pos)) { // Currently, we just ignore the patch annotation. If the function // name already exists in the patched class, this function will replace // the one in the patched class. method->metadata_pos = TokenPosition::kNoSource; } if (method->metadata_pos.IsReal()) { library_.AddFunctionMetadata(func, method->metadata_pos); } if (method->has_native) { func.set_native_name(*native_name); } // If this method is a redirecting factory, set the redirection information. if (!redirection_type.IsNull()) { ASSERT(func.IsFactory()); func.SetRedirectionType(redirection_type); if (!redirection_identifier.IsNull()) { func.SetRedirectionIdentifier(redirection_identifier); } } ASSERT(is_top_level_); AddFormalParamsToFunction(&method->params, func); ASSERT(innermost_function().raw() == func.raw()); innermost_function_ = Function::null(); ResolveSignatureTypeParameters(func); members->AddFunction(func); } void Parser::ParseFieldDefinition(ClassDesc* members, MemberDesc* field) { TRACE_PARSER("ParseFieldDefinition"); // The parser has read the first field name and is now at the token // after the field name. ASSERT(CurrentToken() == Token::kSEMICOLON || CurrentToken() == Token::kCOMMA || CurrentToken() == Token::kASSIGN); ASSERT(field->type != NULL); ASSERT(field->name_pos.IsReal()); ASSERT(current_member_ == field); // All const fields are also final. ASSERT(!field->has_const || field->has_final); if (field->has_covariant) { if (field->has_static) { ReportError("static fields cannot be declared covariant"); } else if (field->has_final) { ReportError("final fields cannot be declared covariant"); } } if (field->has_abstract) { ReportError("keyword 'abstract' not allowed in field declaration"); } if (field->has_external) { ReportError("keyword 'external' not allowed in field declaration"); } if (field->has_factory) { ReportError("keyword 'factory' not allowed in field declaration"); } if (!field->has_static && field->has_const) { ReportError(field->name_pos, "instance field may not be 'const'"); } Function& getter = Function::Handle(Z); Function& setter = Function::Handle(Z); Field& class_field = Field::ZoneHandle(Z); Instance& init_value = Instance::Handle(Z); while (true) { bool has_initializer = CurrentToken() == Token::kASSIGN; bool has_simple_literal = false; if (has_initializer) { ConsumeToken(); init_value = Object::sentinel().raw(); // For static fields, the initialization expression will be parsed // through the kImplicitStaticFinalGetter method invocation/compilation. // For instance fields, the expression is parsed when a constructor // is compiled. // For static fields with very simple initializer expressions // (e.g. a literal number or string), we optimize away the // kImplicitStaticFinalGetter and initialize the field here. // However, the class finalizer will check the value type for // assignability once the declared field type can be resolved. If the // value is not assignable (assuming checked mode and disregarding actual // mode), the field value is reset and a kImplicitStaticFinalGetter is // created at finalization time. if ((LookaheadToken(1) == Token::kSEMICOLON) || (LookaheadToken(1) == Token::kCOMMA)) { has_simple_literal = IsSimpleLiteral(*field->type, &init_value); } SkipExpr(); } else { // Static const and static final fields must have an initializer. // Static const fields are implicitly final. if (field->has_static && field->has_final) { ReportError(field->name_pos, "static %s field '%s' must have an initializer expression", field->has_const ? "const" : "final", field->name->ToCString()); } } TokenPosition end_token_pos = TokenPos(); // Create the field object. const bool is_reflectable = !(library_.is_dart_scheme() && library_.IsPrivate(*field->name)); class_field = Field::New(*field->name, field->has_static, field->has_final, field->has_const, is_reflectable, current_class(), *field->type, field->name_pos, end_token_pos); class_field.set_has_initializer(has_initializer); members->AddField(class_field); field->field_ = &class_field; if (is_patch_source() && IsPatchAnnotation(field->metadata_pos)) { // Currently, we just ignore the patch annotation on fields. // All fields in the patch class are added to the patched class. field->metadata_pos = TokenPosition::kNoSource; } if ((field->metadata_pos.IsReal())) { library_.AddFieldMetadata(class_field, field->metadata_pos); } // Start tracking types for fields with simple initializers in their // definition. This avoids some of the overhead to track this at runtime // and rules out many fields from being unnecessary unboxing candidates. if (!field->has_static && has_initializer && has_simple_literal) { class_field.RecordStore(init_value); if (!init_value.IsNull() && init_value.IsDouble()) { class_field.set_is_double_initialized(true); } } // For static final fields (this includes static const fields), set value to // "uninitialized" and create a kImplicitStaticFinalGetter getter method. if (field->has_static && has_initializer) { class_field.SetStaticValue(init_value, true); if (!has_simple_literal) { String& getter_name = String::Handle(Z, Field::GetterSymbol(*field->name)); getter = Function::New( getter_name, RawFunction::kImplicitStaticFinalGetter, field->has_static, field->has_const, /* is_abstract = */ false, /* is_external = */ false, /* is_native = */ false, current_class(), field->name_pos); getter.set_result_type(*field->type); getter.set_is_debuggable(false); if (library_.is_dart_scheme() && library_.IsPrivate(*field->name)) { getter.set_is_reflectable(false); } members->AddFunction(getter); } } // For instance fields, we create implicit getter and setter methods. if (!field->has_static) { String& getter_name = String::Handle(Z, Field::GetterSymbol(*field->name)); getter = Function::New(getter_name, RawFunction::kImplicitGetter, field->has_static, field->has_final, /* is_abstract = */ false, /* is_external = */ false, /* is_native = */ false, current_class(), field->name_pos); ParamList params; ASSERT(current_class().raw() == getter.Owner()); params.AddReceiver(ReceiverType(current_class()), field->name_pos); getter.set_result_type(*field->type); getter.set_is_debuggable(false); AddFormalParamsToFunction(¶ms, getter); ResolveSignatureTypeParameters(getter); members->AddFunction(getter); if (!field->has_final) { // Build a setter accessor for non-const fields. String& setter_name = String::Handle(Z, Field::SetterSymbol(*field->name)); setter = Function::New(setter_name, RawFunction::kImplicitSetter, field->has_static, field->has_final, /* is_abstract = */ false, /* is_external = */ false, /* is_native = */ false, current_class(), field->name_pos); ParamList params; ASSERT(current_class().raw() == setter.Owner()); params.AddReceiver(ReceiverType(current_class()), field->name_pos); params.AddFinalParameter(TokenPos(), &Symbols::Value(), field->type); setter.set_result_type(Object::void_type()); setter.set_is_debuggable(false); if (library_.is_dart_scheme() && library_.IsPrivate(*field->name)) { setter.set_is_reflectable(false); } AddFormalParamsToFunction(¶ms, setter); ResolveSignatureTypeParameters(setter); members->AddFunction(setter); } } if (CurrentToken() != Token::kCOMMA) { break; } ConsumeToken(); field->name_pos = this->TokenPos(); field->name = ExpectIdentifier("field name expected"); } ExpectSemicolon(); } void Parser::CheckOperatorArity(const MemberDesc& member) { intptr_t expected_num_parameters; // Includes receiver. Token::Kind op = member.operator_token; if (op == Token::kASSIGN_INDEX) { expected_num_parameters = 3; } else if ((op == Token::kBIT_NOT) || (op == Token::kNEGATE)) { expected_num_parameters = 1; } else { expected_num_parameters = 2; } if ((member.params.num_optional_parameters > 0) || member.params.has_optional_positional_parameters || member.params.has_optional_named_parameters || (member.params.num_fixed_parameters != expected_num_parameters)) { // Subtract receiver when reporting number of expected arguments. ReportError(member.name_pos, "operator %s expects %" Pd " argument(s)", member.name->ToCString(), (expected_num_parameters - 1)); } } void Parser::CheckMemberNameConflict(ClassDesc* members, MemberDesc* member) { const String& name = *member->DictName(); if (name.Equals(members->class_name())) { ReportError(member->name_pos, "%s '%s' conflicts with class name", member->ToCString(), name.ToCString()); } if (members->clazz().LookupTypeParameter(name) != TypeParameter::null()) { ReportError(member->name_pos, "%s '%s' conflicts with type parameter", member->ToCString(), name.ToCString()); } for (int i = 0; i < members->members().length(); i++) { MemberDesc* existing_member = &members->members()[i]; if (name.Equals(*existing_member->DictName())) { ReportError( member->name_pos, "%s '%s' conflicts with previously declared %s", member->ToCString(), name.ToCString(), existing_member->ToCString()); } } } void Parser::ParseClassMemberDefinition(ClassDesc* members, TokenPosition metadata_pos) { TRACE_PARSER("ParseClassMemberDefinition"); MemberDesc member; current_member_ = &member; member.metadata_pos = metadata_pos; member.decl_begin_pos = TokenPos(); if ((CurrentToken() == Token::kEXTERNAL) && (LookaheadToken(1) != Token::kLPAREN)) { ConsumeToken(); member.has_external = true; } if ((CurrentToken() == Token::kSTATIC) && (LookaheadToken(1) != Token::kLPAREN)) { ConsumeToken(); member.has_static = true; } if (CurrentToken() == Token::kCOVARIANT) { ConsumeToken(); member.has_covariant = true; } if (CurrentToken() == Token::kCONST) { ConsumeToken(); member.has_const = true; } else if (CurrentToken() == Token::kFINAL) { ConsumeToken(); member.has_final = true; } if (CurrentToken() == Token::kVAR) { if (member.has_const) { ReportError("identifier expected after 'const'"); } if (member.has_final) { ReportError("identifier expected after 'final'"); } ConsumeToken(); member.has_var = true; // The member type is the 'dynamic' type. member.type = &Object::dynamic_type(); } else if ((CurrentToken() == Token::kFACTORY) && (LookaheadToken(1) != Token::kLPAREN)) { ConsumeToken(); if (member.has_static) { ReportError("factory method cannot be explicitly marked static"); } member.has_factory = true; member.has_static = true; // The result type depends on the name of the factory method. } // Optionally parse a type. bool found_type = false; { // Lookahead to determine whether the next tokens are a return type. TokenPosScope saved_pos(this); if (TryParseType(true)) { if (IsIdentifier() || (CurrentToken() == Token::kGET) || (CurrentToken() == Token::kSET) || (CurrentToken() == Token::kOPERATOR)) { found_type = true; } } } if (found_type) { // 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. member.type = &AbstractType::ZoneHandle( Z, ParseTypeOrFunctionType(true, ClassFinalizer::kDoNotResolve)); } // Optionally parse a (possibly named) constructor name or factory. if (IsIdentifier() && (CurrentLiteral()->Equals(members->class_name()) || member.has_factory)) { member.name_pos = TokenPos(); member.name = CurrentLiteral(); // Unqualified identifier. ConsumeToken(); if (member.has_factory) { // The factory name may be qualified, but the first identifier must match // the name of the immediately enclosing class. if (!member.name->Equals(members->class_name())) { ReportError(member.name_pos, "factory name must be '%s'", members->class_name().ToCString()); } } else if (member.has_static) { ReportError(member.name_pos, "constructor cannot be static"); } if (member.type != NULL) { ReportError(member.name_pos, "constructor must not specify return type"); } // Do not bypass class resolution by using current_class() directly, since // it may be a patch class. const Object& result_type_class = Object::Handle(Z, UnresolvedClass::New(LibraryPrefix::Handle(Z), *member.name, member.name_pos)); // The type arguments of the result type are the type parameters of the // current class. Note that in the case of a patch class, they are copied // from the class being patched. member.type = &Type::ZoneHandle( Z, Type::New(result_type_class, TypeArguments::Handle(Z, current_class().type_parameters()), member.name_pos)); // We must be dealing with a constructor or named constructor. member.kind = RawFunction::kConstructor; GrowableHandlePtrArray to_concat(Z, 3); to_concat.Add(*member.name); to_concat.Add(Symbols::Dot()); if (CurrentToken() == Token::kPERIOD) { // Named constructor. ConsumeToken(); member.dict_name = ExpectIdentifier("identifier expected"); to_concat.Add(*member.dict_name); } *member.name = Symbols::FromConcatAll(T, to_concat); CheckToken(Token::kLPAREN); } else if ((CurrentToken() == Token::kGET) && !member.has_var && (LookaheadToken(1) != Token::kLPAREN) && (LookaheadToken(1) != Token::kLT) && (LookaheadToken(1) != Token::kASSIGN) && (LookaheadToken(1) != Token::kCOMMA) && (LookaheadToken(1) != Token::kSEMICOLON)) { ConsumeToken(); member.kind = RawFunction::kGetterFunction; member.name_pos = this->TokenPos(); member.name = ExpectIdentifier("identifier expected"); // If the result type was not specified, it will be set to DynamicType. } else if ((CurrentToken() == Token::kSET) && !member.has_var && (LookaheadToken(1) != Token::kLPAREN) && (LookaheadToken(1) != Token::kLT) && (LookaheadToken(1) != Token::kASSIGN) && (LookaheadToken(1) != Token::kCOMMA) && (LookaheadToken(1) != Token::kSEMICOLON)) { ConsumeToken(); member.kind = RawFunction::kSetterFunction; member.name_pos = this->TokenPos(); member.name = ExpectIdentifier("identifier expected"); CheckToken(Token::kLPAREN); // The grammar allows a return type, so member.type is not always NULL here. // If no return type is specified, the return type of the setter is dynamic. if (member.type == NULL) { member.type = &Object::dynamic_type(); } } else if ((CurrentToken() == Token::kOPERATOR) && !member.has_var && (LookaheadToken(1) != Token::kLPAREN) && (LookaheadToken(1) != Token::kASSIGN) && (LookaheadToken(1) != Token::kCOMMA) && (LookaheadToken(1) != Token::kSEMICOLON)) { // TODO(hausner): handle the case of a generic function named 'operator': // eg: T operator(a, b) => ... ConsumeToken(); if (!Token::CanBeOverloaded(CurrentToken())) { ReportError("invalid operator overloading"); } if (member.has_static) { ReportError("operator overloading functions cannot be static"); } member.operator_token = CurrentToken(); member.has_operator = true; member.kind = RawFunction::kRegularFunction; member.name_pos = this->TokenPos(); member.name = &String::ZoneHandle(Z, Symbols::Token(member.operator_token).raw()); ConsumeToken(); } else if (IsIdentifier()) { member.name = CurrentLiteral(); member.name_pos = TokenPos(); ConsumeToken(); } else { ReportError("identifier expected"); } ASSERT(member.name != NULL); if (IsParameterPart() || member.IsGetter()) { // Constructor or method. if (member.type == NULL) { member.type = &Object::dynamic_type(); } ASSERT(member.IsFactory() == member.has_factory); // Note that member.type may still be unresolved and may refer to not yet // parsed function type parameters. ParseMethodOrConstructor(members, &member); } else if (CurrentToken() == Token::kSEMICOLON || CurrentToken() == Token::kCOMMA || CurrentToken() == Token::kASSIGN) { // Field definition. if (member.has_const) { // const fields are implicitly final. member.has_final = true; } if (member.type == NULL) { if (member.has_final) { member.type = &Object::dynamic_type(); } else { ReportError( "missing 'var', 'final', 'const' or type" " in field declaration"); } } else if (member.type->IsVoidType()) { ReportError(member.name_pos, "field may not be 'void'"); } if (!member.type->IsResolved()) { AbstractType& type = AbstractType::ZoneHandle(Z, member.type->raw()); ResolveTypeParameters(&type); member.type = &type; } ParseFieldDefinition(members, &member); } else { UnexpectedToken(); } current_member_ = NULL; CheckMemberNameConflict(members, &member); members->AddMember(member); } void Parser::ParseEnumDeclaration(const GrowableObjectArray& pending_classes, const Object& tl_owner, TokenPosition metadata_pos) { TRACE_PARSER("ParseEnumDeclaration"); const TokenPosition declaration_pos = (metadata_pos.IsReal()) ? metadata_pos : TokenPos(); ConsumeToken(); const TokenPosition name_pos = TokenPos(); String* enum_name = ExpectUserDefinedTypeIdentifier("enum type name expected"); if (FLAG_trace_parser) { OS::Print("TopLevel parsing enum '%s'\n", enum_name->ToCString()); } ExpectToken(Token::kLBRACE); if (!IsIdentifier()) { ReportError("Enumeration must have at least one name"); } while (IsIdentifier()) { ConsumeToken(); if (CurrentToken() == Token::kCOMMA) { ConsumeToken(); if (CurrentToken() == Token::kRBRACE) { break; } } else if (CurrentToken() == Token::kRBRACE) { break; } else { ReportError(", or } expected"); } } ExpectToken(Token::kRBRACE); Object& obj = Object::Handle(Z, library_.LookupLocalObject(*enum_name)); if (!obj.IsNull()) { ReportError(name_pos, "'%s' is already defined", enum_name->ToCString()); } Class& cls = Class::Handle(Z); cls = Class::New(library_, *enum_name, script_, declaration_pos); library_.AddClass(cls); cls.set_is_synthesized_class(); cls.set_is_enum_class(); if (metadata_pos.IsReal()) { library_.AddClassMetadata(cls, tl_owner, metadata_pos); } cls.set_super_type(Type::Handle(Z, Type::ObjectType())); pending_classes.Add(cls, Heap::kOld); } void Parser::ParseClassDeclaration(const GrowableObjectArray& pending_classes, const Object& tl_owner, TokenPosition metadata_pos) { TRACE_PARSER("ParseClassDeclaration"); bool is_patch = false; bool is_abstract = false; TokenPosition declaration_pos = metadata_pos.IsReal() ? metadata_pos : TokenPos(); if (is_patch_source() && IsPatchAnnotation(metadata_pos)) { is_patch = true; metadata_pos = TokenPosition::kNoSource; declaration_pos = TokenPos(); } else if (CurrentToken() == Token::kABSTRACT) { is_abstract = true; ConsumeToken(); } ExpectToken(Token::kCLASS); const TokenPosition classname_pos = TokenPos(); String& class_name = *ExpectUserDefinedTypeIdentifier("class name expected"); if (FLAG_trace_parser) { OS::Print("TopLevel parsing class '%s'\n", class_name.ToCString()); } Class& cls = Class::Handle(Z); TypeArguments& orig_type_parameters = TypeArguments::Handle(Z); Object& obj = Object::Handle(Z, library_.LookupLocalObject(class_name)); if (obj.IsNull()) { if (is_patch) { ReportError(classname_pos, "missing class '%s' cannot be patched", class_name.ToCString()); } cls = Class::New(library_, class_name, script_, declaration_pos); library_.AddClass(cls); } else { if (!obj.IsClass()) { ReportError(classname_pos, "'%s' is already defined", class_name.ToCString()); } cls ^= obj.raw(); if (is_patch) { // Preserve and reuse the original type parameters and bounds since the // ones defined in the patch class will not be finalized. orig_type_parameters = cls.type_parameters(); cls = Class::New(library_, class_name, script_, declaration_pos); } else { // Not patching a class, but it has been found. This must be one of the // pre-registered classes from object.cc or a duplicate definition. if (!(cls.is_prefinalized() || cls.IsClosureClass() || RawObject::IsImplicitFieldClassId(cls.id()))) { ReportError(classname_pos, "class '%s' is already defined", class_name.ToCString()); } // Pre-registered classes need their scripts connected at this time. cls.set_script(script_); cls.set_token_pos(declaration_pos); } } ASSERT(!cls.IsNull()); ASSERT(cls.functions() == Object::empty_array().raw()); set_current_class(cls); ParseTypeParameters(true); // Parameterizing current class. if (is_patch) { // Check that the new type parameters are identical to the original ones. const TypeArguments& new_type_parameters = TypeArguments::Handle(Z, cls.type_parameters()); const int new_type_params_count = new_type_parameters.IsNull() ? 0 : new_type_parameters.Length(); const int orig_type_params_count = orig_type_parameters.IsNull() ? 0 : orig_type_parameters.Length(); if (new_type_params_count != orig_type_params_count) { ReportError(classname_pos, "class '%s' must be patched with identical type parameters", class_name.ToCString()); } if (!FLAG_ignore_patch_signature_mismatch) { TypeParameter& new_type_param = TypeParameter::Handle(Z); TypeParameter& orig_type_param = TypeParameter::Handle(Z); String& new_name = String::Handle(Z); String& orig_name = String::Handle(Z); AbstractType& new_bound = AbstractType::Handle(Z); AbstractType& orig_bound = AbstractType::Handle(Z); for (int i = 0; i < new_type_params_count; i++) { new_type_param ^= new_type_parameters.TypeAt(i); orig_type_param ^= orig_type_parameters.TypeAt(i); new_name = new_type_param.name(); orig_name = orig_type_param.name(); if (!new_name.Equals(orig_name)) { ReportError(new_type_param.token_pos(), "type parameter '%s' of patch class '%s' does not match " "original type parameter '%s'", new_name.ToCString(), class_name.ToCString(), orig_name.ToCString()); } new_bound = new_type_param.bound(); orig_bound = orig_type_param.bound(); if (!new_bound.Equals(orig_bound)) { ReportError(new_type_param.token_pos(), "bound '%s' of type parameter '%s' of patch class '%s' " "does not match original type parameter bound '%s'", String::Handle(new_bound.UserVisibleName()).ToCString(), new_name.ToCString(), class_name.ToCString(), String::Handle(orig_bound.UserVisibleName()).ToCString()); } } } cls.set_type_parameters(orig_type_parameters); } if (is_abstract) { cls.set_is_abstract(); } if (metadata_pos.IsReal()) { library_.AddClassMetadata(cls, tl_owner, metadata_pos); } const bool is_mixin_declaration = (CurrentToken() == Token::kASSIGN); if (is_mixin_declaration && is_patch) { ReportError(classname_pos, "mixin application '%s' may not be a patch class", class_name.ToCString()); } AbstractType& super_type = Type::Handle(Z); if ((CurrentToken() == Token::kEXTENDS) || is_mixin_declaration) { ConsumeToken(); // extends or = const TokenPosition type_pos = TokenPos(); super_type = ParseType(ClassFinalizer::kResolveTypeParameters); if (super_type.IsMalformedOrMalbounded()) { ReportError(Error::Handle(Z, super_type.error())); } if (super_type.IsDynamicType()) { // Unlikely here, since super type is not resolved yet. ReportError(type_pos, "class '%s' may not extend 'dynamic'", class_name.ToCString()); } if (super_type.IsTypeParameter()) { ReportError(type_pos, "class '%s' may not extend type parameter '%s'", class_name.ToCString(), String::Handle(Z, super_type.UserVisibleName()).ToCString()); } // The class finalizer will check whether the super type is malbounded. if (is_mixin_declaration) { if (CurrentToken() != Token::kWITH) { ReportError("mixin application clause 'with type' expected"); } cls.set_is_mixin_app_alias(); cls.set_is_synthesized_class(); } if (CurrentToken() == Token::kWITH) { super_type = ParseMixins(super_type); } } else { // No extends clause: implicitly extend Object, unless Object itself. if (!cls.IsObjectClass()) { super_type = Type::ObjectType(); } } ASSERT(!super_type.IsNull() || cls.IsObjectClass()); cls.set_super_type(super_type); if (CurrentToken() == Token::kIMPLEMENTS) { ParseInterfaceList(cls); } if (is_patch) { cls.set_is_patch(); // Apply the changes to the patched class looked up above. ASSERT(obj.raw() == library_.LookupLocalObject(class_name)); const Class& orig_class = Class::Cast(obj); if (orig_class.is_finalized()) { orig_class.SetRefinalizeAfterPatch(); pending_classes.Add(orig_class, Heap::kOld); } library_.AddPatchClass(cls); } pending_classes.Add(cls, Heap::kOld); if (is_mixin_declaration) { ExpectSemicolon(); } else { CheckToken(Token::kLBRACE); SkipBlock(); ExpectToken(Token::kRBRACE); } } void Parser::ParseClassDefinition(const Class& cls) { TRACE_PARSER("ParseClassDefinition"); INC_STAT(thread(), num_classes_parsed, 1); set_current_class(cls); is_top_level_ = true; String& class_name = String::Handle(Z, cls.Name()); SkipMetadata(); if (CurrentToken() == Token::kABSTRACT) { ConsumeToken(); } ExpectToken(Token::kCLASS); const TokenPosition class_pos = TokenPos(); ClassDesc members(Z, cls, class_name, false, class_pos); while (CurrentToken() != Token::kLBRACE) { ConsumeToken(); } ExpectToken(Token::kLBRACE); while (CurrentToken() != Token::kRBRACE) { TokenPosition metadata_pos = SkipMetadata(); ParseClassMemberDefinition(&members, metadata_pos); } ExpectToken(Token::kRBRACE); if (cls.LookupTypeParameter(class_name) != TypeParameter::null()) { ReportError(class_pos, "class name conflicts with type parameter '%s'", class_name.ToCString()); } CheckConstructors(&members); // Need to compute this here since MakeFixedLength() will clear the // functions array in members. const bool need_implicit_constructor = !members.has_constructor() && !cls.is_patch(); cls.AddFields(members.fields()); // Creating a new array for functions marks the class as parsed. Array& array = Array::Handle(Z, members.MakeFunctionsArray()); cls.SetFunctions(array); // Add an implicit constructor if no explicit constructor is present. // No implicit constructors are needed for patch classes. if (need_implicit_constructor) { AddImplicitConstructor(cls); } if (cls.is_patch()) { // Apply the changes to the patched class looked up above. Object& obj = Object::Handle(Z, library_.LookupLocalObject(class_name)); // The patched class must not be finalized yet. const Class& orig_class = Class::Cast(obj); ASSERT(!orig_class.is_finalized()); Error& error = Error::Handle(Z); // Check if this is a case of patching a class after it has already // been finalized. if (orig_class.is_refinalize_after_patch()) { if (!cls.ValidatePostFinalizePatch(orig_class, &error)) { Report::LongJumpF(error, script_, class_pos, "patch validation failed, not applying patch.\n"); } } if (!orig_class.ApplyPatch(cls, &error)) { Report::LongJumpF(error, script_, class_pos, "applying patch failed"); } } } void Parser::ParseEnumDefinition(const Class& cls) { TRACE_PARSER("ParseEnumDefinition"); INC_STAT(thread(), num_classes_parsed, 1); set_current_class(cls); const Class& helper_class = Class::Handle(Z, Library::LookupCoreClass(Symbols::_EnumHelper())); ASSERT(!helper_class.IsNull()); SkipMetadata(); ExpectToken(Token::kENUM); const String& enum_name = String::Handle(Z, cls.ScrubbedName()); ClassDesc enum_members(Z, cls, enum_name, false, cls.token_pos()); // Add instance field 'final int index'. Field& index_field = Field::ZoneHandle(Z); const Type& int_type = Type::Handle(Z, Type::IntType()); index_field = Field::New(Symbols::Index(), false, // Not static. true, // Field is final. false, // Not const. true, // Is reflectable. cls, int_type, cls.token_pos(), cls.token_pos()); enum_members.AddField(index_field); // Add implicit getter for index field. const String& getter_name = String::Handle(Z, Field::GetterSymbol(Symbols::Index())); Function& getter = Function::Handle(Z); getter = Function::New(getter_name, RawFunction::kImplicitGetter, /* is_static = */ false, /* is_const = */ true, /* is_abstract = */ false, /* is_external = */ false, /* is_native = */ false, cls, cls.token_pos()); getter.set_result_type(int_type); getter.set_is_debuggable(false); ParamList params; params.AddReceiver(&Object::dynamic_type(), cls.token_pos()); AddFormalParamsToFunction(¶ms, getter); ResolveSignatureTypeParameters(getter); enum_members.AddFunction(getter); ASSERT(IsIdentifier()); ASSERT(CurrentLiteral()->raw() == cls.Name()); ConsumeToken(); // Enum type name. ExpectToken(Token::kLBRACE); Field& enum_value = Field::Handle(Z); intptr_t i = 0; GrowableArray declared_names(8); while (IsIdentifier()) { String* enum_ident = CurrentLiteral(); // Check for name conflicts. if (enum_ident->raw() == cls.Name()) { ReportError("enum identifier '%s' cannot be equal to enum type name", CurrentLiteral()->ToCString()); } else if (enum_ident->raw() == Symbols::Index().raw()) { ReportError( "enum identifier conflicts with " "implicit instance field 'index'"); } else if (enum_ident->raw() == Symbols::Values().raw()) { ReportError( "enum identifier conflicts with " "implicit static field 'values'"); } else if (enum_ident->raw() == Symbols::toString().raw()) { ReportError( "enum identifier conflicts with " "implicit instance method 'toString()'"); } for (intptr_t n = 0; n < declared_names.length(); n++) { if (enum_ident->Equals(*declared_names[n])) { ReportError("Duplicate name '%s' in enum definition '%s'", enum_ident->ToCString(), enum_name.ToCString()); } } declared_names.Add(enum_ident); // Create the static const field for the enumeration value. // Note that we do not set the field type to E, because we temporarily store // a Smi in the field. The class finalizer would detect the bad type and // reset the value to sentinel. enum_value = Field::New(*enum_ident, /* is_static = */ true, /* is_final = */ true, /* is_const = */ true, /* is_reflectable = */ true, cls, Object::dynamic_type(), cls.token_pos(), cls.token_pos()); enum_value.set_has_initializer(false); enum_members.AddField(enum_value); // Initialize the field with the ordinal value. It will be patched // later with the enum constant instance. const Smi& ordinal_value = Smi::Handle(Z, Smi::New(i)); enum_value.SetStaticValue(ordinal_value, true); enum_value.RecordStore(ordinal_value); i++; ConsumeToken(); // Enum value name. if (CurrentToken() == Token::kCOMMA) { ConsumeToken(); } } ExpectToken(Token::kRBRACE); const Class& array_class = Class::Handle(Z, I->object_store()->array_class()); TypeArguments& values_type_args = TypeArguments::ZoneHandle(Z, TypeArguments::New(1)); const Type& enum_type = Type::Handle(Type::NewNonParameterizedType(cls)); values_type_args.SetTypeAt(0, enum_type); Type& values_type = Type::ZoneHandle( Z, Type::New(array_class, values_type_args, cls.token_pos(), Heap::kOld)); values_type ^= CanonicalizeType(values_type); values_type_args = values_type.arguments(); // Get canonical type arguments. // Add static field 'const List values'. Field& values_field = Field::ZoneHandle(Z); values_field = Field::New(Symbols::Values(), /* is_static = */ true, /* is_final = */ true, /* is_const = */ true, /* is_reflectable = */ true, cls, values_type, cls.token_pos(), cls.token_pos()); enum_members.AddField(values_field); // Add static field 'const _deleted_enum_sentinel'. // This field does not need to be of type E. Field& deleted_enum_sentinel = Field::ZoneHandle(Z); deleted_enum_sentinel = Field::New(Symbols::_DeletedEnumSentinel(), /* is_static = */ true, /* is_final = */ true, /* is_const = */ true, /* is_reflectable = */ false, cls, Object::dynamic_type(), cls.token_pos(), cls.token_pos()); enum_members.AddField(deleted_enum_sentinel); // Allocate the immutable array containing the enumeration values. // The actual enum instance values will be patched in later. const Array& values_array = Array::Handle(Z, Array::New(i, Heap::kOld)); values_array.SetTypeArguments(values_type_args); values_field.SetStaticValue(values_array, true); values_field.RecordStore(values_array); // Clone the _name field from the helper class. Field& _name_field = Field::Handle( Z, helper_class.LookupInstanceFieldAllowPrivate(Symbols::_name())); ASSERT(!_name_field.IsNull()); _name_field = _name_field.Clone(cls); enum_members.AddField(_name_field); // Add an implicit getter function for the _name field. We use the field's // name directly here so that the private key matches those of the other // cloned helper functions and fields. const Type& string_type = Type::Handle(Z, Type::StringType()); const String& name_getter_name = String::Handle( Z, Field::GetterSymbol(String::Handle(_name_field.name()))); Function& name_getter = Function::Handle(Z); name_getter = Function::New(name_getter_name, RawFunction::kImplicitGetter, /* is_static = */ false, /* is_const = */ true, /* is_abstract = */ false, /* is_external = */ false, /* is_native = */ false, cls, cls.token_pos()); name_getter.set_result_type(string_type); name_getter.set_is_debuggable(false); ParamList name_params; name_params.AddReceiver(&Object::dynamic_type(), cls.token_pos()); AddFormalParamsToFunction(&name_params, name_getter); ResolveSignatureTypeParameters(name_getter); enum_members.AddFunction(name_getter); // Clone the toString() function from the helper class. Function& to_string_func = Function::Handle( Z, helper_class.LookupDynamicFunctionAllowPrivate(Symbols::toString())); ASSERT(!to_string_func.IsNull()); to_string_func = to_string_func.Clone(cls); enum_members.AddFunction(to_string_func); // Clone the hashCode getter function from the helper class. Function& hash_code_func = Function::Handle( Z, helper_class.LookupDynamicFunctionAllowPrivate(Symbols::hashCode())); ASSERT(!hash_code_func.IsNull()); hash_code_func = hash_code_func.Clone(cls); enum_members.AddFunction(hash_code_func); cls.AddFields(enum_members.fields()); const Array& functions = Array::Handle(Z, enum_members.MakeFunctionsArray()); cls.SetFunctions(functions); } // Add an implicit constructor to the given class. void Parser::AddImplicitConstructor(const Class& cls) { // The implicit constructor is unnamed, has no explicit parameter. String& ctor_name = String::ZoneHandle(Z, cls.Name()); ctor_name = Symbols::FromDot(T, ctor_name); // To indicate that this is an implicit constructor, we set the // token position and end token position of the function // to the token position of the class. Function& ctor = Function::Handle( Z, Function::New(ctor_name, RawFunction::kConstructor, /* is_static = */ false, /* is_const = */ false, /* is_abstract = */ false, /* is_external = */ false, /* is_native = */ false, cls, cls.token_pos())); ctor.set_end_token_pos(ctor.token_pos()); ctor.set_is_debuggable(false); if (library_.is_dart_scheme() && library_.IsPrivate(ctor_name)) { ctor.set_is_reflectable(false); } ParamList params; // Add implicit 'this' parameter. const AbstractType* receiver_type = ReceiverType(cls); params.AddReceiver(receiver_type, cls.token_pos()); AddFormalParamsToFunction(¶ms, ctor); ctor.set_result_type(Object::dynamic_type()); ResolveSignatureTypeParameters(ctor); // The body of the constructor cannot modify the type of the constructed // instance, which is passed in as the receiver. ctor.set_result_type(*receiver_type); cls.AddFunction(ctor); } void Parser::CheckFinalInitializationConflicts(const ClassDesc* class_desc, const MemberDesc* member) { const ParamList* params = &member->params; if (!params->has_field_initializer) { return; } const ZoneGrowableArray& parameters = *params->parameters; const GrowableArray& fields = class_desc->fields(); String& field_name = String::Handle(Z); for (intptr_t p = 0; p < parameters.length(); p++) { const ParamDesc& current_param = parameters[p]; if (!current_param.is_field_initializer) { continue; } const String& param_name = *current_param.name; for (intptr_t i = 0; i < fields.length(); i++) { const Field* current_field = fields.At(i); if (!current_field->is_final() || !current_field->has_initializer()) { continue; } field_name ^= current_field->name(); if (param_name.Equals(field_name)) { ReportError(current_param.name_pos, "final field '%s' is already initialized.", param_name.ToCString()); } } } } // Check for cycles in constructor redirection. void Parser::CheckConstructors(ClassDesc* class_desc) { // Check for cycles in constructor redirection. const GrowableArray& members = class_desc->members(); for (int i = 0; i < members.length(); i++) { MemberDesc* member = &members[i]; if (member->IsConstructor()) { // Check that our constructors don't try and reinitialize an initialized // final variable. CheckFinalInitializationConflicts(class_desc, member); } if (member->redirect_name == NULL) { continue; } GrowableArray ctors; while ((member != NULL) && (member->redirect_name != NULL)) { ASSERT(member->IsConstructor()); // Check whether we have already seen this member. for (int i = 0; i < ctors.length(); i++) { if (ctors[i] == member) { ReportError(member->name_pos, "cyclic reference in constructor redirection"); } } // We haven't seen this member. Add it to the list and follow // the next redirection. If we can't find the constructor to // which the current one redirects, we ignore the unresolved // reference. We'll catch it later when the constructor gets // compiled. ctors.Add(member); member = class_desc->LookupMember(*member->redirect_name); } } } void Parser::ParseMixinAppAlias(const GrowableObjectArray& pending_classes, const Object& tl_owner, TokenPosition metadata_pos) { TRACE_PARSER("ParseMixinAppAlias"); const TokenPosition classname_pos = TokenPos(); String& class_name = *ExpectUserDefinedTypeIdentifier("class name expected"); if (FLAG_trace_parser) { OS::Print("toplevel parsing mixin application alias class '%s'\n", class_name.ToCString()); } const Object& obj = Object::Handle(Z, library_.LookupLocalObject(class_name)); if (!obj.IsNull()) { ReportError(classname_pos, "'%s' is already defined", class_name.ToCString()); } const Class& mixin_application = Class::Handle( Z, Class::New(library_, class_name, script_, classname_pos)); mixin_application.set_is_mixin_app_alias(); library_.AddClass(mixin_application); set_current_class(mixin_application); ParseTypeParameters(true); // Parameterizing current class. ExpectToken(Token::kASSIGN); if (CurrentToken() == Token::kABSTRACT) { mixin_application.set_is_abstract(); ConsumeToken(); } const TokenPosition type_pos = TokenPos(); AbstractType& type = AbstractType::Handle( Z, ParseType(ClassFinalizer::kResolveTypeParameters)); if (type.IsTypeParameter()) { ReportError(type_pos, "class '%s' may not extend type parameter '%s'", class_name.ToCString(), String::Handle(Z, type.UserVisibleName()).ToCString()); } CheckToken(Token::kWITH, "mixin application 'with Type' expected"); type = ParseMixins(type); mixin_application.set_super_type(type); mixin_application.set_is_synthesized_class(); // This mixin application alias needs an implicit constructor, but it is // too early to call 'AddImplicitConstructor(mixin_application)' here, // because this class should be lazily compiled. if (CurrentToken() == Token::kIMPLEMENTS) { ParseInterfaceList(mixin_application); } ExpectSemicolon(); pending_classes.Add(mixin_application, Heap::kOld); if (metadata_pos.IsReal()) { library_.AddClassMetadata(mixin_application, tl_owner, metadata_pos); } } // Look ahead to detect if we are seeing ident [ TypeParameters ] ("(" | "="). // We need this lookahead to distinguish between the optional return type // and the alias name of a function type alias. // Token position remains unchanged. bool Parser::IsFunctionTypeAliasName(bool* use_function_type_syntax) { if (IsIdentifier()) { const Token::Kind ahead = LookaheadToken(1); if ((ahead == Token::kLPAREN) || (ahead == Token::kASSIGN)) { *use_function_type_syntax = (ahead == Token::kASSIGN); return true; } } const TokenPosScope saved_pos(this); if (IsIdentifier() && (LookaheadToken(1) == Token::kLT)) { ConsumeToken(); if (TryParseTypeParameters()) { const Token::Kind current = CurrentToken(); if ((current == Token::kLPAREN) || (current == Token::kASSIGN)) { *use_function_type_syntax = (current == Token::kASSIGN); return true; } } } *use_function_type_syntax = false; return false; } void Parser::ParseTypedef(const GrowableObjectArray& pending_classes, const Object& tl_owner, TokenPosition metadata_pos) { TRACE_PARSER("ParseTypedef"); TokenPosition declaration_pos = metadata_pos.IsReal() ? metadata_pos : TokenPos(); ExpectToken(Token::kTYPEDEF); // Distinguish between two possible typedef forms: // 1) returnType? identifier typeParameters? formalParameterList ’;’ // 2) identifier typeParameters? '=' functionType ’;’ bool use_function_type_syntax; // Set to false for form 1, true for form 2. // If present, parse the result type of the function type. AbstractType& result_type = Type::Handle(Z); if (CurrentToken() == Token::kVOID) { ConsumeToken(); result_type = Type::VoidType(); use_function_type_syntax = false; } else if (!IsFunctionTypeAliasName(&use_function_type_syntax)) { // Type annotations in typedef are never ignored, even in production mode. // Wait until we have an owner class before resolving the result type. result_type = ParseType(ClassFinalizer::kDoNotResolve); ASSERT(!use_function_type_syntax); } const TokenPosition alias_name_pos = TokenPos(); const String* alias_name = ExpectUserDefinedTypeIdentifier("function alias name expected"); // Lookup alias name and report an error if it is already defined in // the library scope. const Object& obj = Object::Handle(Z, library_.LookupLocalObject(*alias_name)); if (!obj.IsNull()) { ReportError(alias_name_pos, "'%s' is already defined", alias_name->ToCString()); } // Create the function type alias scope class. It will be linked to its // signature function after it has been parsed. The type parameters, in order // to be properly finalized, need to be associated to this scope class as // they are parsed. const Class& function_type_alias = Class::Handle( Z, Class::New(library_, *alias_name, script_, declaration_pos)); function_type_alias.set_is_synthesized_class(); function_type_alias.set_is_abstract(); function_type_alias.set_is_prefinalized(); // Make sure the function type alias can be recognized as a typedef class by // setting its signature function. When use_function_type_syntax is true, this // temporary signature function is replaced while parsing the function type. Function& signature_function = Function::Handle( Z, Function::NewSignatureFunction(function_type_alias, Function::Handle(Z), alias_name_pos)); function_type_alias.set_signature_function(signature_function); library_.AddClass(function_type_alias); ASSERT(function_type_alias.IsTypedefClass()); ASSERT(current_class().IsTopLevel()); set_current_class(function_type_alias); // Parse the type parameters of the typedef class. ParseTypeParameters(true); // Parameterizing current class. ASSERT(innermost_function().IsNull()); if (use_function_type_syntax) { ExpectToken(Token::kASSIGN); ASSERT(result_type.IsNull()); // Not parsed yet. const Type& function_type = Type::Handle( Z, ParseFunctionType(result_type, ClassFinalizer::kDoNotResolve)); signature_function = function_type.signature(); } else { innermost_function_ = signature_function.raw(); ParamList params; // Parse the formal parameters of the function type. CheckToken(Token::kLPAREN, "formal parameter list expected"); // Add implicit closure object parameter. params.AddFinalParameter(TokenPos(), &Symbols::ClosureParameter(), &Object::dynamic_type()); const bool allow_explicit_default_values = false; const bool evaluate_metadata = false; ParseFormalParameterList(use_function_type_syntax, allow_explicit_default_values, evaluate_metadata, ¶ms); if (result_type.IsNull()) { result_type = Type::DynamicType(); } signature_function.set_result_type(result_type); // The result type may refer to the signature function's type parameters, // but was not parsed in the scope of the signature function. Adjust. result_type.SetScopeFunction(signature_function); AddFormalParamsToFunction(¶ms, signature_function); ASSERT(innermost_function().raw() == signature_function.raw()); innermost_function_ = Function::null(); } ExpectSemicolon(); ASSERT(innermost_function().IsNull()); ASSERT(function_type_alias.signature_function() == signature_function.raw()); // At this point, all function type parameters have been parsed and the class // function_type_alias is recognized as a typedef, so we can resolve all type // parameters in the signature type defined by the typedef. AbstractType& function_type = Type::Handle(Z, signature_function.SignatureType()); ASSERT(current_class().raw() == function_type_alias.raw()); ResolveTypeParameters(&function_type); // Resolving does not replace type or signature. ASSERT(function_type_alias.signature_function() == Type::Cast(function_type).signature()); if (FLAG_trace_parser) { OS::Print("TopLevel parsing function type alias '%s'\n", String::Handle(Z, signature_function.Signature()).ToCString()); } // The alias should not be marked as finalized yet, since it needs to be // checked in the class finalizer for illegal self references. ASSERT(!function_type_alias.is_finalized()); pending_classes.Add(function_type_alias, Heap::kOld); if (metadata_pos.IsReal()) { library_.AddClassMetadata(function_type_alias, tl_owner, metadata_pos); } } // Consumes exactly one right angle bracket. If the current token is // a single bracket token, it is consumed normally. However, if it is // a double bracket, it is replaced by a single bracket token without // incrementing the token index. void Parser::ConsumeRightAngleBracket() { if (token_kind_ == Token::kGT) { ConsumeToken(); } else if (token_kind_ == Token::kSHR) { token_kind_ = Token::kGT; } else { UNREACHABLE(); } } bool Parser::IsPatchAnnotation(TokenPosition pos) { if (pos == TokenPosition::kNoSource) { return false; } TokenPosScope saved_pos(this); SetPosition(pos); ExpectToken(Token::kAT); return IsSymbol(Symbols::Patch()); } TokenPosition Parser::SkipMetadata() { if (CurrentToken() != Token::kAT) { return TokenPosition::kNoSource; } TokenPosition metadata_pos = TokenPos(); while (CurrentToken() == Token::kAT) { ConsumeToken(); ExpectIdentifier("identifier expected"); if (CurrentToken() == Token::kPERIOD) { ConsumeToken(); ExpectIdentifier("identifier expected"); if (CurrentToken() == Token::kPERIOD) { ConsumeToken(); ExpectIdentifier("identifier expected"); } } if (CurrentToken() == Token::kLPAREN) { SkipToMatchingParenthesis(); } } return metadata_pos; } void Parser::SkipTypeArguments() { if (CurrentToken() == Token::kLT) { do { ConsumeToken(); SkipTypeOrFunctionType(true); } while (CurrentToken() == Token::kCOMMA); Token::Kind token = CurrentToken(); if ((token == Token::kGT) || (token == Token::kSHR)) { ConsumeRightAngleBracket(); } else { ReportError("right angle bracket expected"); } } } void Parser::SkipTypeParameters() { // Function already parsed. if (IsTypeParameters()) { const bool skipped = TryParseTypeParameters(); ASSERT(skipped); } } void Parser::SkipType(bool allow_void) { if (CurrentToken() == Token::kVOID) { if (!allow_void) { ReportError("'void' not allowed here"); } ConsumeToken(); } else { ExpectIdentifier("type name expected"); if (CurrentToken() == Token::kPERIOD) { ConsumeToken(); ExpectIdentifier("name expected"); } SkipTypeArguments(); } } void Parser::SkipTypeOrFunctionType(bool allow_void) { if (CurrentToken() == Token::kVOID) { TokenPosition void_pos = TokenPos(); ConsumeToken(); // 'void' is always allowed as result type of a function type. if (!allow_void && !IsFunctionTypeSymbol()) { ReportError(void_pos, "'void' not allowed here"); } } else if (!IsFunctionTypeSymbol()) { // Including 'Function' not followed by '(' or '<'. SkipType(false); } while (IsFunctionTypeSymbol()) { ConsumeToken(); SkipTypeArguments(); if (CurrentToken() == Token::kLPAREN) { SkipToMatchingParenthesis(); } else { ReportError("'(' expected"); } } } void Parser::ParseTypeParameters(bool parameterizing_class) { TRACE_PARSER("ParseTypeParameters"); if (CurrentToken() == Token::kLT) { GrowableArray type_parameters_array(Z, 2); intptr_t index = 0; TypeParameter& type_parameter = TypeParameter::Handle(Z); TypeParameter& existing_type_parameter = TypeParameter::Handle(Z); String& existing_type_parameter_name = String::Handle(Z); AbstractType& type_parameter_bound = Type::Handle(Z); do { ConsumeToken(); const TokenPosition metadata_pos = SkipMetadata(); const TokenPosition type_parameter_pos = TokenPos(); const TokenPosition declaration_pos = metadata_pos.IsReal() ? metadata_pos : type_parameter_pos; String& type_parameter_name = *ExpectUserDefinedTypeIdentifier("type parameter expected"); // Check for duplicate type parameters. for (intptr_t i = 0; i < index; i++) { existing_type_parameter ^= type_parameters_array.At(i)->raw(); existing_type_parameter_name = existing_type_parameter.name(); if (existing_type_parameter_name.Equals(type_parameter_name)) { ReportError(type_parameter_pos, "duplicate type parameter '%s'", type_parameter_name.ToCString()); } } if (CurrentToken() == Token::kEXTENDS) { ConsumeToken(); // A bound may refer to the owner of the type parameter it applies to, // i.e. to the class or function currently being parsed. // Postpone resolution in order to avoid resolving the owner and its // type parameters, as they are not fully parsed yet. type_parameter_bound = ParseTypeOrFunctionType(false, ClassFinalizer::kDoNotResolve); } else { type_parameter_bound = I->object_store()->object_type(); } // Note that we cannot yet calculate the final index of a function type // parameter, because we may not have parsed the parent function yet. type_parameter = TypeParameter::New( parameterizing_class ? current_class() : Class::Handle(Z), parameterizing_class ? Function::Handle(Z) : innermost_function(), index, type_parameter_name, type_parameter_bound, declaration_pos); type_parameters_array.Add( &AbstractType::ZoneHandle(Z, type_parameter.raw())); if (metadata_pos.IsReal()) { library_.AddTypeParameterMetadata(type_parameter, metadata_pos); } index++; } while (CurrentToken() == Token::kCOMMA); Token::Kind token = CurrentToken(); if ((token == Token::kGT) || (token == Token::kSHR)) { ConsumeRightAngleBracket(); } else { ReportError("right angle bracket expected"); } const TypeArguments& type_parameters = TypeArguments::Handle(Z, NewTypeArguments(type_parameters_array)); if (parameterizing_class) { current_class().set_type_parameters(type_parameters); } else { innermost_function().set_type_parameters(type_parameters); } // Resolve type parameters referenced by upper bounds. const intptr_t num_types = type_parameters.Length(); for (intptr_t i = 0; i < num_types; i++) { type_parameter ^= type_parameters.TypeAt(i); type_parameter_bound = type_parameter.bound(); ResolveTypeParameters(&type_parameter_bound); type_parameter.set_bound(type_parameter_bound); } } } RawTypeArguments* Parser::ParseTypeArguments( ClassFinalizer::FinalizationKind finalization) { TRACE_PARSER("ParseTypeArguments"); if (CurrentToken() == Token::kLT) { GrowableArray types; AbstractType& type = AbstractType::Handle(Z); do { ConsumeToken(); type = ParseTypeOrFunctionType(true, finalization); // Map a malformed type argument to dynamic. if (type.IsMalformed()) { type = Type::DynamicType(); } types.Add(&AbstractType::ZoneHandle(Z, type.raw())); } while (CurrentToken() == Token::kCOMMA); Token::Kind token = CurrentToken(); if ((token == Token::kGT) || (token == Token::kSHR)) { ConsumeRightAngleBracket(); } else { ReportError("right angle bracket expected"); } if (finalization != ClassFinalizer::kIgnore) { TypeArguments& type_args = TypeArguments::Handle(NewTypeArguments(types)); if (finalization == ClassFinalizer::kCanonicalize) { type_args = type_args.Canonicalize(); } return type_args.raw(); } } return TypeArguments::null(); } // Parse interface list and add to class cls. void Parser::ParseInterfaceList(const Class& cls) { TRACE_PARSER("ParseInterfaceList"); ASSERT(CurrentToken() == Token::kIMPLEMENTS); const GrowableObjectArray& all_interfaces = GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld)); AbstractType& interface = AbstractType::Handle(Z); // First get all the interfaces already implemented by class. Array& cls_interfaces = Array::Handle(Z, cls.interfaces()); for (intptr_t i = 0; i < cls_interfaces.Length(); i++) { interface ^= cls_interfaces.At(i); all_interfaces.Add(interface, Heap::kOld); } // Now parse and add the new interfaces. do { ConsumeToken(); TokenPosition interface_pos = TokenPos(); interface = ParseType(ClassFinalizer::kResolveTypeParameters); if (interface.IsTypeParameter()) { ReportError(interface_pos, "type parameter '%s' may not be used in interface list", String::Handle(Z, interface.UserVisibleName()).ToCString()); } all_interfaces.Add(interface, Heap::kOld); } while (CurrentToken() == Token::kCOMMA); cls_interfaces = Array::MakeFixedLength(all_interfaces); cls.set_interfaces(cls_interfaces); } RawAbstractType* Parser::ParseMixins(const AbstractType& super_type) { TRACE_PARSER("ParseMixins"); ASSERT(CurrentToken() == Token::kWITH); const GrowableObjectArray& mixin_types = GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld)); AbstractType& mixin_type = AbstractType::Handle(Z); do { ConsumeToken(); mixin_type = ParseType(ClassFinalizer::kResolveTypeParameters); if (mixin_type.IsDynamicType()) { // The string 'dynamic' is not resolved yet at this point, but a malformed // type mapped to dynamic can be encountered here. ReportError(mixin_type.token_pos(), "illegal mixin of a malformed type"); } if (mixin_type.IsTypeParameter()) { ReportError(mixin_type.token_pos(), "mixin type '%s' may not be a type parameter", String::Handle(Z, mixin_type.UserVisibleName()).ToCString()); } mixin_types.Add(mixin_type, Heap::kOld); } while (CurrentToken() == Token::kCOMMA); return MixinAppType::New( super_type, Array::Handle(Z, Array::MakeFixedLength(mixin_types))); } void Parser::ParseTopLevelVariable(TopLevel* top_level, const Object& owner, TokenPosition metadata_pos) { TRACE_PARSER("ParseTopLevelVariable"); const bool is_const = (CurrentToken() == Token::kCONST); // Const fields are implicitly final. const bool is_final = is_const || (CurrentToken() == Token::kFINAL); const bool is_static = true; const AbstractType& type = AbstractType::ZoneHandle( Z, ParseConstFinalVarOrType(ClassFinalizer::kResolveTypeParameters)); Field& field = Field::Handle(Z); Function& getter = Function::Handle(Z); while (true) { const TokenPosition name_pos = TokenPos(); String& var_name = *ExpectIdentifier("variable name expected"); if (library_.LookupLocalObject(var_name) != Object::null()) { ReportError(name_pos, "'%s' is already defined", var_name.ToCString()); } // Check whether a getter or setter for this name exists. A const // or final field implies a setter which throws a NoSuchMethodError, // thus we need to check for conflicts with existing setters and // getters. String& accessor_name = String::Handle(Z, Field::LookupGetterSymbol(var_name)); if (!accessor_name.IsNull() && library_.LookupLocalObject(accessor_name) != Object::null()) { ReportError(name_pos, "getter for '%s' is already defined", var_name.ToCString()); } accessor_name = Field::LookupSetterSymbol(var_name); if (!accessor_name.IsNull() && library_.LookupLocalObject(accessor_name) != Object::null()) { ReportError(name_pos, "setter for '%s' is already defined", var_name.ToCString()); } bool has_initializer = CurrentToken() == Token::kASSIGN; bool has_simple_literal = false; Instance& field_value = Instance::Handle(Z, Object::sentinel().raw()); if (has_initializer) { ConsumeToken(); if (LookaheadToken(1) == Token::kSEMICOLON) { has_simple_literal = IsSimpleLiteral(type, &field_value); } SkipExpr(); } else if (is_final) { ReportError(name_pos, "missing initializer for final or const variable"); } TokenPosition end_token_pos = TokenPos(); // Create the field object. const bool is_reflectable = !(library_.is_dart_scheme() && library_.IsPrivate(var_name)); field = Field::NewTopLevel(var_name, is_final, is_const, owner, name_pos, end_token_pos); field.SetFieldType(type); field.set_has_initializer(has_initializer); field.set_is_reflectable(is_reflectable); top_level->AddField(field); library_.AddObject(field, var_name); if (metadata_pos.IsReal()) { library_.AddFieldMetadata(field, metadata_pos); } if (has_initializer) { field.SetStaticValue(field_value, true); if (!has_simple_literal) { // Create a static final getter. String& getter_name = String::Handle(Z, Field::GetterSymbol(var_name)); getter = Function::New(getter_name, RawFunction::kImplicitStaticFinalGetter, is_static, is_const, /* is_abstract = */ false, /* is_external = */ false, /* is_native = */ false, owner, name_pos); getter.set_result_type(type); getter.set_is_debuggable(false); getter.set_is_reflectable(is_reflectable); top_level->AddFunction(getter); } } if (CurrentToken() == Token::kCOMMA) { ConsumeToken(); } else if (CurrentToken() == Token::kSEMICOLON) { ConsumeToken(); break; } else { ExpectSemicolon(); // Reports error. } } } RawFunction::AsyncModifier Parser::ParseFunctionModifier() { if (IsSymbol(Symbols::Async())) { ConsumeToken(); if (CurrentToken() == Token::kMUL) { const bool enableAsyncStar = true; if (!enableAsyncStar) { ReportError("async* generator functions are not yet supported"); } ConsumeToken(); return RawFunction::kAsyncGen; } else { return RawFunction::kAsync; } } else if (IsSymbol(Symbols::Sync()) && (LookaheadToken(1) == Token::kMUL)) { const bool enableSyncStar = true; if (!enableSyncStar) { ReportError("sync* generator functions are not yet supported"); } ConsumeToken(); ConsumeToken(); return RawFunction::kSyncGen; } return RawFunction::kNoModifier; } void Parser::ParseTopLevelFunction(TopLevel* top_level, const Object& owner, TokenPosition metadata_pos) { TRACE_PARSER("ParseTopLevelFunction"); const TokenPosition decl_begin_pos = TokenPos(); AbstractType& result_type = Type::Handle(Z, Type::DynamicType()); bool is_external = false; bool is_patch = false; if (is_patch_source() && IsPatchAnnotation(metadata_pos)) { is_patch = true; metadata_pos = TokenPosition::kNoSource; } else if (CurrentToken() == Token::kEXTERNAL) { ConsumeToken(); is_external = true; } // Parse optional result type. if (IsFunctionReturnType()) { // 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. result_type = ParseTypeOrFunctionType(true, ClassFinalizer::kDoNotResolve); } const TokenPosition name_pos = TokenPos(); const String& func_name = *ExpectIdentifier("function name expected"); bool found = library_.LookupLocalObject(func_name) != Object::null(); if (found && !is_patch) { ReportError(name_pos, "'%s' is already defined", func_name.ToCString()); } else if (!found && is_patch) { ReportError(name_pos, "missing '%s' cannot be patched", func_name.ToCString()); } const String& accessor_name = String::Handle(Z, Field::LookupGetterSymbol(func_name)); if (!accessor_name.IsNull() && library_.LookupLocalObject(accessor_name) != Object::null()) { ReportError(name_pos, "'%s' is already defined as getter", func_name.ToCString()); } // A setter named x= may co-exist with a function named x, thus we do // not need to check setters. Function& func = Function::Handle( Z, Function::New(func_name, RawFunction::kRegularFunction, /* is_static = */ true, /* is_const = */ false, /* is_abstract = */ false, is_external, /* is_native = */ false, // May change. owner, decl_begin_pos)); ASSERT(innermost_function().IsNull()); innermost_function_ = func.raw(); if (CurrentToken() == Token::kLT) { ParseTypeParameters(false); // Not parameterizing class, but function. } CheckToken(Token::kLPAREN); const TokenPosition function_pos = TokenPos(); ParamList params; 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); const TokenPosition modifier_pos = TokenPos(); RawFunction::AsyncModifier func_modifier = ParseFunctionModifier(); TokenPosition function_end_pos = function_pos; bool is_native = false; String* native_name = NULL; if (is_external) { function_end_pos = TokenPos(); ExpectSemicolon(); } else if (CurrentToken() == Token::kLBRACE) { SkipBlock(); function_end_pos = TokenPos(); ExpectToken(Token::kRBRACE); } else if (CurrentToken() == Token::kARROW) { if ((func_modifier & RawFunction::kGeneratorBit) != 0) { ReportError(modifier_pos, "=> style function may not be sync* or async* generator"); } ConsumeToken(); BoolScope allow_await(&this->await_is_keyword_, func_modifier != RawFunction::kNoModifier); SkipExpr(); function_end_pos = TokenPos(); ExpectSemicolon(); } else if (IsSymbol(Symbols::Native())) { native_name = &ParseNativeDeclaration(); function_end_pos = TokenPos(); ExpectSemicolon(); is_native = true; func.set_is_native(true); } else { ReportError("function block expected"); } func.set_result_type(result_type); // The result type may refer to func's type parameters, // but was not parsed in the scope of func. Adjust. result_type.SetScopeFunction(func); func.set_end_token_pos(function_end_pos); func.set_modifier(func_modifier); if (library_.is_dart_scheme() && library_.IsPrivate(func_name)) { func.set_is_reflectable(false); } if (is_native) { func.set_native_name(*native_name); } AddFormalParamsToFunction(¶ms, func); ASSERT(innermost_function().raw() == func.raw()); innermost_function_ = Function::null(); ResolveSignatureTypeParameters(func); top_level->AddFunction(func); if (!is_patch) { library_.AddObject(func, func_name); } else { // Need to remove the previously added function that is being patched. const Class& toplevel_cls = Class::Handle(Z, library_.toplevel_class()); const Function& replaced_func = Function::Handle(Z, toplevel_cls.LookupStaticFunction(func_name)); ASSERT(!replaced_func.IsNull()); toplevel_cls.RemoveFunction(replaced_func); library_.ReplaceObject(func, func_name); } if (metadata_pos.IsReal()) { library_.AddFunctionMetadata(func, metadata_pos); } } void Parser::ParseTopLevelAccessor(TopLevel* top_level, const Object& owner, TokenPosition metadata_pos) { TRACE_PARSER("ParseTopLevelAccessor"); const TokenPosition decl_begin_pos = TokenPos(); const bool is_static = true; bool is_external = false; bool is_patch = false; AbstractType& result_type = AbstractType::Handle(Z); if (is_patch_source() && IsPatchAnnotation(metadata_pos)) { is_patch = true; metadata_pos = TokenPosition::kNoSource; } else if (CurrentToken() == Token::kEXTERNAL) { ConsumeToken(); is_external = true; } bool is_getter = (CurrentToken() == Token::kGET); if (CurrentToken() == Token::kGET || CurrentToken() == Token::kSET) { ConsumeToken(); result_type = Type::DynamicType(); } else { result_type = ParseTypeOrFunctionType(true, ClassFinalizer::kResolveTypeParameters); is_getter = (CurrentToken() == Token::kGET); if (CurrentToken() == Token::kGET || CurrentToken() == Token::kSET) { ConsumeToken(); } else { UnexpectedToken(); } } const TokenPosition name_pos = TokenPos(); const String* field_name = ExpectIdentifier("accessor name expected"); const TokenPosition accessor_pos = TokenPos(); ParamList params; if (!is_getter) { 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); } String& accessor_name = String::ZoneHandle(Z); int expected_num_parameters = -1; if (is_getter) { expected_num_parameters = 0; accessor_name = Field::GetterSymbol(*field_name); } else { expected_num_parameters = 1; accessor_name = Field::SetterSymbol(*field_name); } if ((params.num_fixed_parameters != expected_num_parameters) || (params.num_optional_parameters != 0)) { ReportError(name_pos, "illegal %s parameters", is_getter ? "getter" : "setter"); } // Check whether this getter conflicts with a function or top-level variable // with the same name. if (is_getter && (library_.LookupLocalObject(*field_name) != Object::null())) { ReportError(name_pos, "'%s' is already defined in this library", field_name->ToCString()); } // Check whether this setter conflicts with the implicit setter // of a top-level variable with the same name. if (!is_getter && (library_.LookupLocalField(*field_name) != Object::null())) { ReportError(name_pos, "Variable '%s' is already defined in this library", field_name->ToCString()); } bool found = library_.LookupLocalObject(accessor_name) != Object::null(); if (found && !is_patch) { ReportError(name_pos, "%s for '%s' is already defined", is_getter ? "getter" : "setter", field_name->ToCString()); } else if (!found && is_patch) { ReportError(name_pos, "missing %s for '%s' cannot be patched", is_getter ? "getter" : "setter", field_name->ToCString()); } const TokenPosition modifier_pos = TokenPos(); RawFunction::AsyncModifier func_modifier = ParseFunctionModifier(); if (!is_getter && (func_modifier != RawFunction::kNoModifier)) { ReportError(modifier_pos, "setter function cannot be async, async* or sync*"); } TokenPosition accessor_end_pos = accessor_pos; bool is_native = false; String* native_name = NULL; if (is_external) { accessor_end_pos = TokenPos(); ExpectSemicolon(); } else if (CurrentToken() == Token::kLBRACE) { SkipBlock(); accessor_end_pos = TokenPos(); ExpectToken(Token::kRBRACE); } else if (CurrentToken() == Token::kARROW) { if (is_getter && ((func_modifier & RawFunction::kGeneratorBit) != 0)) { ReportError(modifier_pos, "=> style getter may not be sync* or async* generator"); } ConsumeToken(); BoolScope allow_await(&this->await_is_keyword_, func_modifier != RawFunction::kNoModifier); SkipExpr(); accessor_end_pos = TokenPos(); ExpectSemicolon(); } else if (IsSymbol(Symbols::Native())) { native_name = &ParseNativeDeclaration(); accessor_end_pos = TokenPos(); ExpectSemicolon(); is_native = true; } else { ReportError("function block expected"); } Function& func = Function::Handle( Z, Function::New(accessor_name, is_getter ? RawFunction::kGetterFunction : RawFunction::kSetterFunction, is_static, /* is_const = */ false, /* is_abstract = */ false, is_external, is_native, owner, decl_begin_pos)); func.set_result_type(result_type); // The result type may refer to func's type parameters, // but was not parsed in the scope of func. Adjust. result_type.SetScopeFunction(func); func.set_end_token_pos(accessor_end_pos); func.set_modifier(func_modifier); if (is_native) { func.set_is_debuggable(false); func.set_native_name(*native_name); } if (library_.is_dart_scheme() && library_.IsPrivate(accessor_name)) { func.set_is_reflectable(false); } AddFormalParamsToFunction(¶ms, func); ResolveSignatureTypeParameters(func); top_level->AddFunction(func); if (!is_patch) { library_.AddObject(func, accessor_name); } else { // Need to remove the previously added accessor that is being patched. const Class& toplevel_cls = Class::Handle( Z, owner.IsClass() ? Class::Cast(owner).raw() : PatchClass::Cast(owner).patched_class()); const Function& replaced_func = Function::Handle(Z, toplevel_cls.LookupFunction(accessor_name)); ASSERT(!replaced_func.IsNull()); toplevel_cls.RemoveFunction(replaced_func); library_.ReplaceObject(func, accessor_name); } if (metadata_pos.IsReal()) { library_.AddFunctionMetadata(func, metadata_pos); } } RawObject* Parser::CallLibraryTagHandler(Dart_LibraryTag tag, TokenPosition token_pos, const String& url) { Dart_LibraryTagHandler handler = I->library_tag_handler(); if (handler == NULL) { if (url.StartsWith(Symbols::DartScheme())) { if (tag == Dart_kCanonicalizeUrl) { return url.raw(); } return Object::null(); } ReportError(token_pos, "no library handler registered"); } // Block class finalization attempts when calling into the library // tag handler. I->BlockClassFinalization(); Object& result = Object::Handle(Z); { TransitionVMToNative transition(T); Api::Scope api_scope(T); Dart_Handle retval = handler(tag, Api::NewHandle(T, library_.raw()), Api::NewHandle(T, url.raw())); result = Api::UnwrapHandle(retval); } I->UnblockClassFinalization(); if (result.IsError()) { // In case of an error we append an explanatory error message to the // error obtained from the library tag handler. const Error& prev_error = Error::Cast(result); Report::LongJumpF(prev_error, script_, token_pos, "library handler failed"); } if (tag == Dart_kCanonicalizeUrl) { if (!result.IsString()) { ReportError(token_pos, "library handler failed URI canonicalization"); } } return result.raw(); } void Parser::ParseLibraryName() { ASSERT(CurrentToken() == Token::kLIBRARY); ConsumeToken(); String& lib_name = *ExpectIdentifier("library name expected"); if (CurrentToken() == Token::kPERIOD) { GrowableHandlePtrArray pieces(Z, 3); pieces.Add(lib_name); while (CurrentToken() == Token::kPERIOD) { ConsumeToken(); pieces.Add(Symbols::Dot()); pieces.Add(*ExpectIdentifier("malformed library name")); } lib_name = Symbols::FromConcatAll(T, pieces); } library_.SetName(lib_name); ExpectSemicolon(); } void Parser::ParseIdentList(GrowableObjectArray* names) { if (!IsIdentifier()) { ReportError("identifier expected"); } while (IsIdentifier()) { names->Add(*CurrentLiteral(), allocation_space_); ConsumeToken(); // Identifier. if (CurrentToken() != Token::kCOMMA) { return; } ConsumeToken(); // Comma. } } void Parser::ParseLibraryImportExport(const Object& tl_owner, TokenPosition metadata_pos) { ASSERT(Thread::Current()->IsMutatorThread()); bool is_import = (CurrentToken() == Token::kIMPORT); bool is_export = (CurrentToken() == Token::kEXPORT); ASSERT(is_import || is_export); const TokenPosition import_pos = TokenPos(); ConsumeToken(); CheckToken(Token::kSTRING, "library url expected"); AstNode* url_literal = ParseStringLiteral(false); if (FLAG_conditional_directives) { bool condition_triggered = false; while (CurrentToken() == Token::kIF) { // Conditional import: if (env == val) uri. ConsumeToken(); ExpectToken(Token::kLPAREN); // Parse dotted name. const GrowableObjectArray& pieces = GrowableObjectArray::Handle( Z, GrowableObjectArray::New(allocation_space_)); pieces.Add(*ExpectIdentifier("identifier expected"), allocation_space_); while (CurrentToken() == Token::kPERIOD) { pieces.Add(Symbols::Dot(), allocation_space_); ConsumeToken(); pieces.Add(*ExpectIdentifier("identifier expected"), allocation_space_); } if (I->obfuscate()) { // If we are obfuscating then we need to deobfuscate environment name. Obfuscator::Deobfuscate(T, pieces); } AstNode* valueNode = NULL; if (CurrentToken() == Token::kEQ) { ConsumeToken(); CheckToken(Token::kSTRING, "string literal expected"); valueNode = ParseStringLiteral(false); ASSERT(valueNode->IsLiteralNode()); ASSERT(valueNode->AsLiteralNode()->literal().IsString()); } ExpectToken(Token::kRPAREN); CheckToken(Token::kSTRING, "library url expected"); AstNode* conditional_url_literal = ParseStringLiteral(false); // If there was already a condition that triggered, don't try to match // again. if (condition_triggered) { continue; } // Check if this conditional line overrides the default import. const String& key = String::Handle(String::ConcatAll( Array::Handle(Array::MakeFixedLength(pieces)), allocation_space_)); const String& value = (valueNode == NULL) ? Symbols::True() : String::Cast(valueNode->AsLiteralNode()->literal()); // Call the embedder to supply us with the environment. const String& env_value = String::Handle(Api::GetEnvironmentValue(T, key)); if (!env_value.IsNull() && env_value.Equals(value)) { condition_triggered = true; url_literal = conditional_url_literal; } } } ASSERT(url_literal->IsLiteralNode()); ASSERT(url_literal->AsLiteralNode()->literal().IsString()); const String& url = String::Cast(url_literal->AsLiteralNode()->literal()); if (url.Length() == 0) { ReportError("library url expected"); } bool is_deferred_import = false; if (is_import && (IsSymbol(Symbols::Deferred()))) { is_deferred_import = true; ConsumeToken(); CheckToken(Token::kAS, "'as' expected"); } String& prefix = String::Handle(Z); TokenPosition prefix_pos = TokenPosition::kNoSource; if (is_import && (CurrentToken() == Token::kAS)) { ConsumeToken(); prefix_pos = TokenPos(); prefix = ExpectIdentifier("prefix identifier expected")->raw(); } Array& show_names = Array::Handle(Z); Array& hide_names = Array::Handle(Z); if (is_deferred_import || IsSymbol(Symbols::Show()) || IsSymbol(Symbols::Hide())) { GrowableObjectArray& show_list = GrowableObjectArray::Handle( Z, GrowableObjectArray::New(allocation_space_)); GrowableObjectArray& hide_list = GrowableObjectArray::Handle( Z, GrowableObjectArray::New(allocation_space_)); // Libraries imported through deferred import automatically hide // the name 'loadLibrary'. if (is_deferred_import) { hide_list.Add(Symbols::LoadLibrary()); } for (;;) { if (IsSymbol(Symbols::Show())) { ConsumeToken(); ParseIdentList(&show_list); } else if (IsSymbol(Symbols::Hide())) { ConsumeToken(); ParseIdentList(&hide_list); } else { break; } } if (show_list.Length() > 0) { show_names = Array::MakeFixedLength(show_list); } if (hide_list.Length() > 0) { hide_names = Array::MakeFixedLength(hide_list); } } ExpectSemicolon(); // Canonicalize library URL. const String& canon_url = String::CheckedHandle( CallLibraryTagHandler(Dart_kCanonicalizeUrl, import_pos, url)); // Create a new library if it does not exist yet. Library& library = Library::Handle(Z, Library::LookupLibrary(T, canon_url)); if (library.IsNull()) { library = Library::New(canon_url); library.Register(T); } // If loading hasn't been requested yet, and if this is not a deferred // library import, call the library tag handler to request loading // the library. if (library.LoadNotStarted() && (!is_deferred_import || FLAG_load_deferred_eagerly)) { library.SetLoadRequested(); CallLibraryTagHandler(Dart_kImportTag, import_pos, canon_url); } Namespace& ns = Namespace::Handle(Z, Namespace::New(library, show_names, hide_names)); if (metadata_pos.IsReal()) { ns.AddMetadata(tl_owner, metadata_pos); } // Ensure that private dart:_ libraries are only imported into dart: // libraries, including indirectly through exports. const String& lib_url = String::Handle(Z, library_.url()); if (canon_url.StartsWith(Symbols::DartSchemePrivate()) && !lib_url.StartsWith(Symbols::DartScheme())) { ReportError(import_pos, "private library is not accessible"); } if (!FLAG_enable_mirrors && Symbols::DartMirrors().Equals(canon_url)) { ReportError(import_pos, "import of dart:mirrors with --enable-mirrors=false"); } if (is_import) { if (prefix.IsNull() || (prefix.Length() == 0)) { ASSERT(!is_deferred_import); library_.AddImport(ns); } else { LibraryPrefix& library_prefix = LibraryPrefix::Handle(Z); library_prefix = library_.LookupLocalLibraryPrefix(prefix); if (!library_prefix.IsNull()) { // Check that prefix names of deferred import clauses are // unique. if (!is_deferred_import && library_prefix.is_deferred_load()) { ReportError(prefix_pos, "prefix '%s' already used in a deferred import clause", prefix.ToCString()); } if (is_deferred_import) { ReportError(prefix_pos, "prefix of deferred import must be unique"); } library_prefix.AddImport(ns); } else { library_prefix = LibraryPrefix::New(prefix, ns, is_deferred_import, library_); library_.AddObject(library_prefix, prefix); } } } else { ASSERT(is_export); library_.AddExport(ns); } } void Parser::ParseLibraryPart() { const TokenPosition source_pos = TokenPos(); ConsumeToken(); // Consume "part". if (IsSymbol(Symbols::Of())) { ReportError("part of declarations are not allowed in script files"); } CheckToken(Token::kSTRING, "url expected"); AstNode* url_literal = ParseStringLiteral(false); ASSERT(url_literal->IsLiteralNode()); ASSERT(url_literal->AsLiteralNode()->literal().IsString()); const String& url = String::Cast(url_literal->AsLiteralNode()->literal()); ExpectSemicolon(); const String& canon_url = String::CheckedHandle( CallLibraryTagHandler(Dart_kCanonicalizeUrl, source_pos, url)); CallLibraryTagHandler(Dart_kSourceTag, source_pos, canon_url); } void Parser::ParseLibraryDefinition(const Object& tl_owner) { TRACE_PARSER("ParseLibraryDefinition"); // Handle the script tag. if (CurrentToken() == Token::kSCRIPTTAG) { // Nothing to do for script tags except to skip them. ConsumeToken(); } ASSERT(script_.kind() != RawScript::kSourceTag); // We may read metadata tokens that are part of the toplevel // declaration that follows the library definitions. Therefore, we // need to remember the position of the last token that was // successfully consumed. TokenPosition rewind_pos = TokenPos(); TokenPosition metadata_pos = SkipMetadata(); if (CurrentToken() == Token::kLIBRARY) { if (is_patch_source()) { ReportError("patch cannot override library name"); } ParseLibraryName(); if (metadata_pos.IsReal()) { library_.AddLibraryMetadata(tl_owner, metadata_pos); } rewind_pos = TokenPos(); metadata_pos = SkipMetadata(); } while ((CurrentToken() == Token::kIMPORT) || (CurrentToken() == Token::kEXPORT)) { ParseLibraryImportExport(tl_owner, metadata_pos); rewind_pos = TokenPos(); metadata_pos = SkipMetadata(); } // Core lib has not been explicitly imported, so we implicitly // import it here. if (!library_.ImportsCorelib()) { Library& core_lib = Library::Handle(Z, Library::CoreLibrary()); ASSERT(!core_lib.IsNull()); const Namespace& core_ns = Namespace::Handle( Z, Namespace::New(core_lib, Object::null_array(), Object::null_array())); library_.AddImport(core_ns); } while (CurrentToken() == Token::kPART) { ParseLibraryPart(); rewind_pos = TokenPos(); metadata_pos = SkipMetadata(); } SetPosition(rewind_pos); } void Parser::ParsePartHeader() { SkipMetadata(); CheckToken(Token::kPART, "'part of' expected"); ConsumeToken(); if (!IsSymbol(Symbols::Of())) { ReportError("'part of' expected"); } ConsumeToken(); // The VM is not required to check that the library name or URI matches the // name or URI of the current library, so we ignore them. if (CurrentToken() == Token::kSTRING) { ParseStringLiteral(false); } else { ExpectIdentifier("library name expected"); while (CurrentToken() == Token::kPERIOD) { ConsumeToken(); ExpectIdentifier("malformed library name"); } } ExpectSemicolon(); } void Parser::ParseTopLevel() { TRACE_PARSER("ParseTopLevel"); // Collect the classes found at the top level in this growable array. // They need to be registered with class finalization after parsing // has been completed. ObjectStore* object_store = I->object_store(); const GrowableObjectArray& pending_classes = GrowableObjectArray::Handle(Z, object_store->pending_classes()); SetPosition(TokenPosition::kMinSource); is_top_level_ = true; TopLevel top_level(Z); Object& tl_owner = Object::Handle(Z); Class& toplevel_class = Class::Handle(Z, library_.toplevel_class()); if (toplevel_class.IsNull()) { toplevel_class = Class::New(library_, Symbols::TopLevel(), script_, TokenPos()); toplevel_class.set_library(library_); library_.set_toplevel_class(toplevel_class); tl_owner = toplevel_class.raw(); } else { tl_owner = PatchClass::New(toplevel_class, script_); } if (is_library_source() || is_patch_source()) { set_current_class(toplevel_class); ParseLibraryDefinition(tl_owner); } else if (is_part_source()) { ParsePartHeader(); } const Class& cls = Class::Handle(Z); while (true) { set_current_class(cls); // No current class. TokenPosition metadata_pos = SkipMetadata(); if (CurrentToken() == Token::kCLASS) { ParseClassDeclaration(pending_classes, tl_owner, metadata_pos); } else if (CurrentToken() == Token::kENUM) { ParseEnumDeclaration(pending_classes, tl_owner, metadata_pos); } else if ((CurrentToken() == Token::kTYPEDEF) && (LookaheadToken(1) != Token::kLPAREN)) { set_current_class(toplevel_class); ParseTypedef(pending_classes, tl_owner, metadata_pos); } else if ((CurrentToken() == Token::kABSTRACT) && (LookaheadToken(1) == Token::kCLASS)) { ParseClassDeclaration(pending_classes, tl_owner, metadata_pos); } else { set_current_class(toplevel_class); if (IsVariableDeclaration()) { ParseTopLevelVariable(&top_level, tl_owner, metadata_pos); } else if (IsFunctionDeclaration()) { ParseTopLevelFunction(&top_level, tl_owner, metadata_pos); } else if (IsTopLevelAccessor()) { ParseTopLevelAccessor(&top_level, tl_owner, metadata_pos); } else if (CurrentToken() == Token::kEOS) { break; } else { UnexpectedToken(); } } } if (top_level.fields().length() > 0) { toplevel_class.AddFields(top_level.fields()); } for (intptr_t i = 0; i < top_level.functions().length(); i++) { toplevel_class.AddFunction(*top_level.functions()[i]); } if (toplevel_class.is_finalized()) { toplevel_class.ResetFinalization(); } pending_classes.Add(toplevel_class, Heap::kOld); } void Parser::CheckStack() { uword c_stack_pos = Thread::GetCurrentStackPointer(); uword c_stack_limit = OSThread::Current()->stack_limit_with_headroom(); if (c_stack_pos < c_stack_limit) { ReportError("stack overflow while parsing"); } } void Parser::ChainNewBlock(LocalScope* outer_scope) { Block* block = new (Z) Block(current_block_, outer_scope, new (Z) SequenceNode(TokenPos(), outer_scope)); current_block_ = block; } void Parser::OpenBlock() { ASSERT(current_block_ != NULL); LocalScope* outer_scope = current_block_->scope; ChainNewBlock(new (Z) LocalScope(outer_scope, outer_scope->function_level(), outer_scope->loop_level())); } void Parser::OpenLoopBlock() { ASSERT(current_block_ != NULL); LocalScope* outer_scope = current_block_->scope; ChainNewBlock(new (Z) LocalScope(outer_scope, outer_scope->function_level(), outer_scope->loop_level() + 1)); } void Parser::OpenFunctionBlock(const Function& func) { LocalScope* outer_scope; if (current_block_ == NULL) { if (!func.IsLocalFunction()) { // We are compiling a non-nested function. outer_scope = new (Z) LocalScope(NULL, 0, 0); } else { // We are compiling the function of an invoked closure. // Restore the outer scope containing all captured variables. const ContextScope& context_scope = ContextScope::Handle(Z, func.context_scope()); ASSERT(!context_scope.IsNull()); outer_scope = new (Z) LocalScope(LocalScope::RestoreOuterScope(context_scope), 0, 0); } } else { // We are parsing a nested function while compiling the enclosing function. outer_scope = new (Z) LocalScope( current_block_->scope, current_block_->scope->function_level() + 1, 0); } ChainNewBlock(outer_scope); } void Parser::OpenAsyncClosure() { TRACE_PARSER("OpenAsyncClosure"); async_temp_scope_ = current_block_->scope; OpenAsyncTryBlock(); } SequenceNode* Parser::CloseAsyncGeneratorTryBlock(SequenceNode* body) { TRACE_PARSER("CloseAsyncGeneratorTryBlock"); // The generated try-catch-finally that wraps the async generator function // body is the outermost try statement. ASSERT(try_stack_ != NULL); ASSERT(try_stack_->outer_try() == NULL); // We only get here when parsing an async generator body. ASSERT(innermost_function().IsAsyncGenClosure()); const TokenPosition try_end_pos = innermost_function().end_token_pos(); // The try-block (closure body code) has been parsed. We are now // generating the code for the catch block. LocalScope* try_scope = current_block_->scope; try_stack_->enter_catch(); OpenBlock(); // Catch handler list. OpenBlock(); // Catch block. // Add the exception and stack trace parameters to the scope. CatchParamDesc exception_param; CatchParamDesc stack_trace_param; exception_param.token_pos = TokenPosition::kNoSource; exception_param.type = &Object::dynamic_type(); exception_param.name = &Symbols::ExceptionParameter(); stack_trace_param.token_pos = TokenPosition::kNoSource; stack_trace_param.type = &Object::dynamic_type(); stack_trace_param.name = &Symbols::StackTraceParameter(); AddCatchParamsToScope(&exception_param, &stack_trace_param, current_block_->scope); // Generate code to save the exception object and stack trace // in local variables. LocalVariable* context_var = try_scope->LocalLookupVariable(Symbols::SavedTryContextVar()); ASSERT(context_var != NULL); LocalVariable* exception_var = try_scope->LocalLookupVariable(Symbols::ExceptionVar()); ASSERT(exception_var != NULL); if (exception_param.var != NULL) { // Generate code to load the exception object (:exception_var) into // the exception variable specified in this block. current_block_->statements->Add(new (Z) StoreLocalNode( TokenPosition::kNoSource, exception_param.var, new (Z) LoadLocalNode(TokenPosition::kNoSource, exception_var))); } LocalVariable* stack_trace_var = try_scope->LocalLookupVariable(Symbols::StackTraceVar()); ASSERT(stack_trace_var != NULL); if (stack_trace_param.var != NULL) { // A stack trace variable is specified in this block, so generate code // to load the stack trace object (:stack_trace_var) into the stack // trace variable specified in this block. current_block_->statements->Add(new (Z) StoreLocalNode( TokenPosition::kNoSource, stack_trace_param.var, new (Z) LoadLocalNode(TokenPosition::kNoSource, stack_trace_var))); } LocalVariable* saved_exception_var = try_scope->LocalLookupVariable(Symbols::SavedExceptionVar()); LocalVariable* saved_stack_trace_var = try_scope->LocalLookupVariable(Symbols::SavedStackTraceVar()); SaveExceptionAndStackTrace(current_block_->statements, exception_var, stack_trace_var, saved_exception_var, saved_stack_trace_var); // Catch block: add the error to the stream. // :controller.AddError(:exception, :stack_trace); // return; // The finally block will close the stream. LocalVariable* controller = current_block_->scope->LookupVariable(Symbols::ColonController(), false); ASSERT(controller != NULL); ArgumentListNode* args = new (Z) ArgumentListNode(TokenPosition::kNoSource); args->Add(new (Z) LoadLocalNode(TokenPosition::kNoSource, exception_param.var)); args->Add(new (Z) LoadLocalNode(TokenPosition::kNoSource, stack_trace_param.var)); current_block_->statements->Add(new (Z) InstanceCallNode( try_end_pos, new (Z) LoadLocalNode(TokenPosition::kNoSource, controller), Symbols::AddError(), args)); ReturnNode* return_node = new (Z) ReturnNode(try_end_pos); AddNodeForFinallyInlining(return_node); current_block_->statements->Add(return_node); AstNode* catch_block = CloseBlock(); current_block_->statements->Add(catch_block); SequenceNode* catch_handler_list = CloseBlock(); TryStack* try_statement = PopTry(); ASSERT(try_stack_ == NULL); // We popped the outermost try block. // Finally block: closing the stream and returning. Instead of simply // returning, create an await state and suspend. There may be outstanding // calls to schedule the generator body. This suspension ensures that we // do not repeat any code of the generator body. // :controller.close(); // suspend; // We need to inline this code in all recorded exit points. intptr_t node_index = 0; SequenceNode* finally_clause = NULL; if (try_stack_ != NULL) { try_stack_->enter_finally(); } do { OpenBlock(); ArgumentListNode* no_args = new (Z) ArgumentListNode(TokenPosition::kNoSource); current_block_->statements->Add(new (Z) InstanceCallNode( try_end_pos, new (Z) LoadLocalNode(TokenPosition::kNoSource, controller), Symbols::Close(), no_args)); // Suspend after the close. AwaitMarkerNode* await_marker = new (Z) AwaitMarkerNode( async_temp_scope_, current_block_->scope, TokenPosition::kNoSource); current_block_->statements->Add(await_marker); ReturnNode* continuation_ret = new (Z) ReturnNode(try_end_pos); continuation_ret->set_return_type(ReturnNode::kContinuationTarget); current_block_->statements->Add(continuation_ret); finally_clause = CloseBlock(); AstNode* node_to_inline = try_statement->GetNodeToInlineFinally(node_index); if (node_to_inline != NULL) { InlinedFinallyNode* node = new (Z) InlinedFinallyNode(try_end_pos, finally_clause, context_var, // No outer try statement CatchClauseNode::kInvalidTryIndex); finally_clause = NULL; AddFinallyClauseToNode(true, node_to_inline, node); node_index++; } } while (finally_clause == NULL); if (try_stack_ != NULL) { try_stack_->exit_finally(); } // Catch block handles all exceptions. const Array& handler_types = Array::ZoneHandle(Z, Array::New(1, Heap::kOld)); handler_types.SetAt(0, Object::dynamic_type()); CatchClauseNode* catch_clause = new (Z) CatchClauseNode( TokenPosition::kNoSource, catch_handler_list, handler_types, context_var, exception_var, stack_trace_var, saved_exception_var, saved_stack_trace_var, AllocateTryIndex(), true); const intptr_t try_index = try_statement->try_index(); AstNode* try_catch_node = new (Z) TryCatchNode(TokenPosition::kNoSource, body, context_var, catch_clause, finally_clause, try_index, finally_clause); current_block_->statements->Add(try_catch_node); return CloseBlock(); } SequenceNode* Parser::CloseAsyncTryBlock(SequenceNode* try_block, TokenPosition func_end_pos) { // This is the outermost try-catch of the function. ASSERT(try_stack_ != NULL); ASSERT(try_stack_->outer_try() == NULL); ASSERT(innermost_function().IsAsyncClosure()); LocalScope* try_scope = current_block_->scope; try_stack_->enter_catch(); OpenBlock(); // Catch handler list. OpenBlock(); // Catch block. CatchParamDesc exception_param; CatchParamDesc stack_trace_param; exception_param.token_pos = TokenPosition::kNoSource; exception_param.type = &Object::dynamic_type(); exception_param.name = &Symbols::ExceptionParameter(); stack_trace_param.token_pos = TokenPosition::kNoSource; stack_trace_param.type = &Object::dynamic_type(); stack_trace_param.name = &Symbols::StackTraceParameter(); AddCatchParamsToScope(&exception_param, &stack_trace_param, current_block_->scope); LocalVariable* context_var = try_scope->LocalLookupVariable(Symbols::SavedTryContextVar()); ASSERT(context_var != NULL); LocalVariable* exception_var = try_scope->LocalLookupVariable(Symbols::ExceptionVar()); if (exception_param.var != NULL) { // Generate code to load the exception object (:exception_var) into // the exception variable specified in this block. ASSERT(exception_var != NULL); current_block_->statements->Add(new (Z) StoreLocalNode( TokenPosition::kNoSource, exception_param.var, new (Z) LoadLocalNode(TokenPosition::kNoSource, exception_var))); } LocalVariable* stack_trace_var = try_scope->LocalLookupVariable(Symbols::StackTraceVar()); if (stack_trace_param.var != NULL) { // A stack trace variable is specified in this block, so generate code // to load the stack trace object (:stack_trace_var) into the stack // trace variable specified in this block. ASSERT(stack_trace_var != NULL); current_block_->statements->Add(new (Z) StoreLocalNode( TokenPosition::kNoSource, stack_trace_param.var, new (Z) LoadLocalNode(TokenPosition::kNoSource, stack_trace_var))); } LocalVariable* saved_exception_var = try_scope->LocalLookupVariable(Symbols::SavedExceptionVar()); LocalVariable* saved_stack_trace_var = try_scope->LocalLookupVariable(Symbols::SavedStackTraceVar()); SaveExceptionAndStackTrace(current_block_->statements, exception_var, stack_trace_var, saved_exception_var, saved_stack_trace_var); // Complete the async future with an error. This catch block executes // unconditionally, there is no need to generate a type check for. LocalVariable* async_completer = current_block_->scope->LookupVariable(Symbols::AsyncCompleter(), false); ASSERT(async_completer != NULL); ArgumentListNode* completer_args = new (Z) ArgumentListNode(TokenPosition::kNoSource); completer_args->Add( new (Z) LoadLocalNode(TokenPosition::kNoSource, exception_param.var)); completer_args->Add( new (Z) LoadLocalNode(TokenPosition::kNoSource, stack_trace_param.var)); current_block_->statements->Add(new (Z) InstanceCallNode( TokenPosition::kNoSource, new (Z) LoadLocalNode(TokenPosition::kNoSource, async_completer), Symbols::CompleterCompleteError(), completer_args)); ReturnNode* return_node = new (Z) ReturnNode(TokenPosition::kNoSource); // Behavior like a continuation return, i.e,. don't call a completer. return_node->set_return_type(ReturnNode::kContinuation); current_block_->statements->Add(return_node); AstNode* catch_block = CloseBlock(); current_block_->statements->Add(catch_block); SequenceNode* catch_handler_list = CloseBlock(); const Array& handler_types = Array::ZoneHandle(Z, Array::New(1, Heap::kOld)); handler_types.SetAt(0, *exception_param.type); TryStack* try_statement = PopTry(); const intptr_t try_index = try_statement->try_index(); CatchClauseNode* catch_clause = new (Z) CatchClauseNode( TokenPosition::kNoSource, catch_handler_list, handler_types, context_var, exception_var, stack_trace_var, saved_exception_var, saved_stack_trace_var, CatchClauseNode::kInvalidTryIndex, true); AstNode* try_catch_node = new (Z) TryCatchNode( TokenPosition::kNoSource, try_block, context_var, catch_clause, NULL, // No finally clause. try_index, NULL); // No rethrow-finally clause. current_block_->statements->Add(try_catch_node); return CloseBlock(); } // Wrap the body of the async or async* closure in a try/catch block. void Parser::OpenAsyncTryBlock() { ASSERT(innermost_function().IsAsyncClosure() || innermost_function().IsAsyncGenClosure()); LocalVariable* context_var = NULL; LocalVariable* exception_var = NULL; LocalVariable* stack_trace_var = NULL; LocalVariable* saved_exception_var = NULL; LocalVariable* saved_stack_trace_var = NULL; SetupExceptionVariables(current_block_->scope, true, &context_var, &exception_var, &stack_trace_var, &saved_exception_var, &saved_stack_trace_var); // Open the try block. OpenBlock(); // This is the outermost try-catch in the function. ASSERT(try_stack_ == NULL); PushTry(current_block_); // Validate that we always get try index of 0. ASSERT(try_stack_->try_index() == CatchClauseNode::kImplicitAsyncTryIndex); SetupSavedTryContext(context_var); } void Parser::AddSyncGenClosureParameters(ParamList* params) { // Create the parameter list for the body closure of a sync generator: // 1) Implicit closure parameter; // 2) Iterator // Add implicit closure parameter if not already present. if (params->parameters->length() == 0) { params->AddFinalParameter(TokenPosition::kMinSource, &Symbols::ClosureParameter(), &Object::dynamic_type()); } ParamDesc iterator_param; iterator_param.name = &Symbols::IteratorParameter(); iterator_param.type = &Object::dynamic_type(); params->parameters->Add(iterator_param); params->num_fixed_parameters++; } void Parser::AddAsyncGenClosureParameters(ParamList* params) { // Create the parameter list for the body closure of an async generator. // The closure has the same parameters as an asynchronous non-generator. AddAsyncClosureParameters(params); } RawFunction* Parser::OpenSyncGeneratorFunction(TokenPosition func_pos) { Function& body = Function::Handle(Z); String& body_closure_name = String::Handle(Z); bool is_new_closure = false; AddContinuationVariables(); // Check whether a function for the body of this generator // function has already been created by a previous // compilation. const Function& found_func = Function::Handle( Z, I->LookupClosureFunction(innermost_function(), func_pos)); if (!found_func.IsNull()) { ASSERT(found_func.IsSyncGenClosure()); body = found_func.raw(); body_closure_name = body.name(); } else { // Create the closure containing the body of this generator function. String& generator_name = String::Handle(Z, innermost_function().name()); body_closure_name = Symbols::NewFormatted(T, "<%s_sync_body>", generator_name.ToCString()); body = Function::NewClosureFunction(body_closure_name, innermost_function(), func_pos); body.set_is_generated_body(true); body.set_result_type(Object::dynamic_type()); is_new_closure = true; } ParamList closure_params; AddSyncGenClosureParameters(&closure_params); if (is_new_closure) { // Add the parameters to the newly created closure. AddFormalParamsToFunction(&closure_params, body); ResolveSignatureTypeParameters(body); // Finalize function type. Type& signature_type = Type::Handle(Z, body.SignatureType()); signature_type ^= CanonicalizeType(signature_type); body.SetSignatureType(signature_type); ASSERT(AbstractType::Handle(Z, body.result_type()).IsResolved()); ASSERT(body.NumParameters() == closure_params.parameters->length()); } OpenFunctionBlock(body); AddFormalParamsToScope(&closure_params, current_block_->scope); async_temp_scope_ = current_block_->scope; return body.raw(); } SequenceNode* Parser::CloseSyncGenFunction(const Function& closure, SequenceNode* closure_body) { // Explicitly reference variables of the sync generator function from the // closure body in order to mark them as captured. LocalVariable* existing_var = closure_body->scope()->LookupVariable(Symbols::AwaitJumpVar(), false); ASSERT((existing_var != NULL) && existing_var->is_captured()); existing_var = closure_body->scope()->LookupVariable(Symbols::AwaitContextVar(), false); ASSERT((existing_var != NULL) && existing_var->is_captured()); // :await_jump_var = -1; LocalVariable* jump_var = current_block_->scope->LookupVariable(Symbols::AwaitJumpVar(), false); LiteralNode* init_value = new (Z) LiteralNode(TokenPosition::kNoSource, Smi::ZoneHandle(Smi::New(-1))); current_block_->statements->Add( new (Z) StoreLocalNode(TokenPosition::kNoSource, jump_var, init_value)); // return new SyncIterable(body_closure); const Class& iterable_class = Class::Handle(Z, Library::LookupCoreClass(Symbols::_SyncIterable())); ASSERT(!iterable_class.IsNull()); const Function& iterable_constructor = Function::ZoneHandle(Z, iterable_class.LookupConstructorAllowPrivate( Symbols::_SyncIterableConstructor())); ASSERT(!iterable_constructor.IsNull()); const String& closure_name = String::Handle(Z, closure.name()); ASSERT(closure_name.IsSymbol()); ArgumentListNode* arguments = new (Z) ArgumentListNode(TokenPosition::kNoSource); ClosureNode* closure_obj = new (Z) ClosureNode( TokenPosition::kNoSource, closure, NULL, closure_body->scope()); arguments->Add(closure_obj); ConstructorCallNode* new_iterable = new (Z) ConstructorCallNode( TokenPosition::kNoSource, TypeArguments::ZoneHandle(Z), iterable_constructor, arguments); ReturnNode* return_node = new (Z) ReturnNode(TokenPosition::kNoSource, new_iterable); current_block_->statements->Add(return_node); return CloseBlock(); } void Parser::AddAsyncClosureParameters(ParamList* params) { // Async closures have three optional parameters: // * A continuation result. // * A continuation error. // * A continuation stack trace. ASSERT(params->parameters->length() <= 1); // Add implicit closure parameter if not yet present. if (params->parameters->length() == 0) { params->AddFinalParameter(TokenPosition::kMinSource, &Symbols::ClosureParameter(), &Object::dynamic_type()); } ParamDesc result_param; result_param.name = &Symbols::AsyncOperationParam(); result_param.default_value = &Object::null_instance(); result_param.type = &Object::dynamic_type(); params->parameters->Add(result_param); ParamDesc error_param; error_param.name = &Symbols::AsyncOperationErrorParam(); error_param.default_value = &Object::null_instance(); error_param.type = &Object::dynamic_type(); params->parameters->Add(error_param); ParamDesc stack_trace_param; stack_trace_param.name = &Symbols::AsyncOperationStackTraceParam(); stack_trace_param.default_value = &Object::null_instance(); stack_trace_param.type = &Object::dynamic_type(); params->parameters->Add(stack_trace_param); params->has_optional_positional_parameters = true; params->num_optional_parameters += 3; } RawFunction* Parser::OpenAsyncFunction(TokenPosition async_func_pos) { TRACE_PARSER("OpenAsyncFunction"); AddContinuationVariables(); AddAsyncClosureVariables(); Function& closure = Function::Handle(Z); bool is_new_closure = false; // Check whether a function for the asynchronous function body of // this async function has already been created by a previous // compilation of this function. const Function& found_func = Function::Handle( Z, I->LookupClosureFunction(innermost_function(), async_func_pos)); if (!found_func.IsNull()) { ASSERT(found_func.IsAsyncClosure()); closure = found_func.raw(); } else { // Create the closure containing the body of this async function. const String& async_func_name = String::Handle(Z, innermost_function().name()); String& closure_name = String::Handle(Z, Symbols::NewFormatted(T, "<%s_async_body>", async_func_name.ToCString())); closure = Function::NewClosureFunction(closure_name, innermost_function(), async_func_pos); closure.set_is_generated_body(true); closure.set_result_type(Object::dynamic_type()); is_new_closure = true; } // Create the parameter list for the async body closure. ParamList closure_params; AddAsyncClosureParameters(&closure_params); if (is_new_closure) { // Add the parameters to the newly created closure. AddFormalParamsToFunction(&closure_params, closure); ResolveSignatureTypeParameters(closure); // Finalize function type. Type& signature_type = Type::Handle(Z, closure.SignatureType()); signature_type ^= CanonicalizeType(signature_type); closure.SetSignatureType(signature_type); ASSERT(AbstractType::Handle(Z, closure.result_type()).IsResolved()); ASSERT(closure.NumParameters() == closure_params.parameters->length()); } OpenFunctionBlock(closure); AddFormalParamsToScope(&closure_params, current_block_->scope); async_temp_scope_ = current_block_->scope; return closure.raw(); } void Parser::AddContinuationVariables() { // Add to current block's scope: // var :await_jump_var; // var :await_ctx_var; LocalVariable* await_jump_var = new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::AwaitJumpVar(), Object::dynamic_type()); current_block_->scope->AddVariable(await_jump_var); LocalVariable* await_ctx_var = new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::AwaitContextVar(), Object::dynamic_type()); current_block_->scope->AddVariable(await_ctx_var); } void Parser::AddAsyncClosureVariables() { // Add to current block's scope: // var :async_op; // var :async_then_callback; // var :async_catch_error_callback; // var :async_completer; // var :async_stack_trace; LocalVariable* async_op_var = new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::AsyncOperation(), Object::dynamic_type()); current_block_->scope->AddVariable(async_op_var); LocalVariable* async_then_callback_var = new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::AsyncThenCallback(), Object::dynamic_type()); current_block_->scope->AddVariable(async_then_callback_var); LocalVariable* async_catch_error_callback_var = new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::AsyncCatchErrorCallback(), Object::dynamic_type()); current_block_->scope->AddVariable(async_catch_error_callback_var); LocalVariable* async_completer = new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::AsyncCompleter(), Object::dynamic_type()); current_block_->scope->AddVariable(async_completer); LocalVariable* async_stack_trace = new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::AsyncStackTraceVar(), Object::dynamic_type()); current_block_->scope->AddVariable(async_stack_trace); } void Parser::AddAsyncGeneratorVariables() { // Add to current block's scope: // var :controller; // The :controller variable is used by the async generator closure to // store the StreamController object to which the yielded expressions // are added. // var :async_op; // var :async_then_callback; // var :async_catch_error_callback; // var :async_stack_trace; // var :controller_stream; // These variables are used to store the async generator closure containing // the body of the async* function. They are used by the await operator. LocalVariable* controller_var = new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::ColonController(), Object::dynamic_type()); current_block_->scope->AddVariable(controller_var); LocalVariable* async_op_var = new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::AsyncOperation(), Object::dynamic_type()); current_block_->scope->AddVariable(async_op_var); LocalVariable* async_then_callback_var = new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::AsyncThenCallback(), Object::dynamic_type()); current_block_->scope->AddVariable(async_then_callback_var); LocalVariable* async_catch_error_callback_var = new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::AsyncCatchErrorCallback(), Object::dynamic_type()); current_block_->scope->AddVariable(async_catch_error_callback_var); LocalVariable* async_stack_trace = new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::AsyncStackTraceVar(), Object::dynamic_type()); current_block_->scope->AddVariable(async_stack_trace); LocalVariable* controller_stream = new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::ControllerStream(), Object::dynamic_type()); current_block_->scope->AddVariable(controller_stream); } RawFunction* Parser::OpenAsyncGeneratorFunction(TokenPosition async_func_pos) { TRACE_PARSER("OpenAsyncGeneratorFunction"); AddContinuationVariables(); AddAsyncGeneratorVariables(); Function& closure = Function::Handle(Z); bool is_new_closure = false; // Check whether a function for the asynchronous function body of // this async generator has already been created by a previous // compilation of this function. const Function& found_func = Function::Handle( Z, I->LookupClosureFunction(innermost_function(), async_func_pos)); if (!found_func.IsNull()) { ASSERT(found_func.IsAsyncGenClosure()); closure = found_func.raw(); } else { // Create the closure containing the body of this async generator function. const String& async_generator_name = String::Handle(Z, innermost_function().name()); const String& closure_name = String::Handle( Z, Symbols::NewFormatted(T, "<%s_async_gen_body>", async_generator_name.ToCString())); closure = Function::NewClosureFunction(closure_name, innermost_function(), async_func_pos); closure.set_is_generated_body(true); closure.set_result_type(Object::dynamic_type()); is_new_closure = true; } ParamList closure_params; AddAsyncGenClosureParameters(&closure_params); if (is_new_closure) { // Add the parameters to the newly created closure. AddFormalParamsToFunction(&closure_params, closure); ResolveSignatureTypeParameters(closure); // Finalize function type. Type& signature_type = Type::Handle(Z, closure.SignatureType()); signature_type ^= CanonicalizeType(signature_type); closure.SetSignatureType(signature_type); ASSERT(AbstractType::Handle(Z, closure.result_type()).IsResolved()); ASSERT(closure.NumParameters() == closure_params.parameters->length()); } OpenFunctionBlock(closure); AddFormalParamsToScope(&closure_params, current_block_->scope); async_temp_scope_ = current_block_->scope; return closure.raw(); } // Generate the Ast nodes for the implicit code of the async* function. // // f(...) async* { // var :controller; // var :await_jump_var = -1; // var :await_context_var; // f_async_body() { // ... source code of f ... // } // var :async_op = f_async_body; // var :async_then_callback = _asyncThenWrapperHelper(:async_op); // var :async_catch_error_callback = _asyncCatchErrorWrapperHelper(:async_op); // :controller = new _AsyncStarStreamController(:async_op); // var :controller_stream = :controller.stream; // return :controller_stream; // } SequenceNode* Parser::CloseAsyncGeneratorFunction(const Function& closure_func, SequenceNode* closure_body) { TRACE_PARSER("CloseAsyncGeneratorFunction"); ASSERT(!closure_func.IsNull()); ASSERT(closure_body != NULL); // Explicitly reference variables of the async generator function from the // closure body in order to mark them as captured. LocalVariable* existing_var = closure_body->scope()->LookupVariable(Symbols::AwaitJumpVar(), false); ASSERT((existing_var != NULL) && existing_var->is_captured()); existing_var = closure_body->scope()->LookupVariable(Symbols::AwaitContextVar(), false); ASSERT((existing_var != NULL) && existing_var->is_captured()); existing_var = closure_body->scope()->LookupVariable(Symbols::ColonController(), false); ASSERT((existing_var != NULL) && existing_var->is_captured()); existing_var = closure_body->scope()->LookupVariable(Symbols::AsyncOperation(), false); ASSERT((existing_var != NULL) && existing_var->is_captured()); existing_var = closure_body->scope()->LookupVariable( Symbols::AsyncThenCallback(), false); ASSERT((existing_var != NULL) && existing_var->is_captured()); existing_var = closure_body->scope()->LookupVariable( Symbols::AsyncCatchErrorCallback(), false); ASSERT((existing_var != NULL) && existing_var->is_captured()); existing_var = closure_body->scope()->LookupVariable( Symbols::AsyncStackTraceVar(), false); ASSERT((existing_var != NULL) && existing_var->is_captured()); existing_var = closure_body->scope()->LookupVariable(Symbols::ControllerStream(), false); ASSERT((existing_var != NULL) && existing_var->is_captured()); const Library& async_lib = Library::Handle(Library::AsyncLibrary()); const Class& controller_class = Class::Handle( Z, async_lib.LookupClassAllowPrivate(Symbols::_AsyncStarStreamController())); ASSERT(!controller_class.IsNull()); const Function& controller_constructor = Function::ZoneHandle( Z, controller_class.LookupConstructorAllowPrivate( Symbols::_AsyncStarStreamControllerConstructor())); // :await_jump_var = -1; LocalVariable* jump_var = current_block_->scope->LookupVariable(Symbols::AwaitJumpVar(), false); LiteralNode* init_value = new (Z) LiteralNode(TokenPosition::kNoSource, Smi::ZoneHandle(Smi::New(-1))); current_block_->statements->Add( new (Z) StoreLocalNode(TokenPosition::kNoSource, jump_var, init_value)); TokenPosition token_pos = TokenPosition::kNoSource; // Add to AST: // :async_op = ; (containing the original body) LocalVariable* async_op_var = current_block_->scope->LookupVariable(Symbols::AsyncOperation(), false); ClosureNode* closure_obj = new (Z) ClosureNode( TokenPosition::kNoSource, closure_func, NULL, closure_body->scope()); StoreLocalNode* store_async_op = new (Z) StoreLocalNode(TokenPosition::kNoSource, async_op_var, closure_obj); current_block_->statements->Add(store_async_op); if (FLAG_causal_async_stacks) { // Add to AST: // :async_stack_trace = _asyncStackTraceHelper(:async_op); const Function& async_stack_trace_helper = Function::ZoneHandle( Z, async_lib.LookupFunctionAllowPrivate(Symbols::AsyncStackTraceHelper())); ASSERT(!async_stack_trace_helper.IsNull()); ArgumentListNode* async_stack_trace_helper_args = new (Z) ArgumentListNode(TokenPosition::kNoSource); async_stack_trace_helper_args->Add( new (Z) LoadLocalNode(TokenPosition::kNoSource, async_op_var)); StaticCallNode* async_stack_trace_helper_call = new (Z) StaticCallNode(token_pos, async_stack_trace_helper, async_stack_trace_helper_args, StaticCallNode::kStatic); LocalVariable* async_stack_trace_var = current_block_->scope->LookupVariable(Symbols::AsyncStackTraceVar(), false); StoreLocalNode* store_async_stack_trace = new (Z) StoreLocalNode( token_pos, async_stack_trace_var, async_stack_trace_helper_call); current_block_->statements->Add(store_async_stack_trace); } // :async_then_callback = _asyncThenWrapperHelper(:async_op) const Function& async_then_wrapper_helper = Function::ZoneHandle( Z, async_lib.LookupFunctionAllowPrivate(Symbols::AsyncThenWrapperHelper())); ASSERT(!async_then_wrapper_helper.IsNull()); ArgumentListNode* async_then_wrapper_helper_args = new (Z) ArgumentListNode(TokenPosition::kNoSource); async_then_wrapper_helper_args->Add( new (Z) LoadLocalNode(TokenPosition::kNoSource, async_op_var)); StaticCallNode* then_wrapper_call = new (Z) StaticCallNode(TokenPosition::kNoSource, async_then_wrapper_helper, async_then_wrapper_helper_args, StaticCallNode::kStatic); LocalVariable* async_then_callback_var = current_block_->scope->LookupVariable(Symbols::AsyncThenCallback(), false); StoreLocalNode* store_async_then_callback = new (Z) StoreLocalNode( TokenPosition::kNoSource, async_then_callback_var, then_wrapper_call); current_block_->statements->Add(store_async_then_callback); // :async_catch_error_callback = _asyncErrorWrapperHelper(:async_op) const Function& async_error_wrapper_helper = Function::ZoneHandle( Z, async_lib.LookupFunctionAllowPrivate(Symbols::AsyncErrorWrapperHelper())); ASSERT(!async_error_wrapper_helper.IsNull()); ArgumentListNode* async_error_wrapper_helper_args = new (Z) ArgumentListNode(TokenPosition::kNoSource); async_error_wrapper_helper_args->Add( new (Z) LoadLocalNode(TokenPosition::kNoSource, async_op_var)); StaticCallNode* error_wrapper_call = new (Z) StaticCallNode(TokenPosition::kNoSource, async_error_wrapper_helper, async_error_wrapper_helper_args, StaticCallNode::kStatic); LocalVariable* async_catch_error_callback_var = current_block_->scope->LookupVariable(Symbols::AsyncCatchErrorCallback(), false); StoreLocalNode* store_async_catch_error_callback = new (Z) StoreLocalNode(TokenPosition::kNoSource, async_catch_error_callback_var, error_wrapper_call); current_block_->statements->Add(store_async_catch_error_callback); // :controller = new _AsyncStarStreamController(body_closure); ArgumentListNode* arguments = new (Z) ArgumentListNode(TokenPosition::kNoSource); arguments->Add(new (Z) LoadLocalNode(TokenPosition::kNoSource, async_op_var)); ConstructorCallNode* controller_constructor_call = new (Z) ConstructorCallNode(TokenPosition::kNoSource, TypeArguments::ZoneHandle(Z), controller_constructor, arguments); LocalVariable* controller_var = current_block_->scope->LookupVariable(Symbols::ColonController(), false); StoreLocalNode* store_controller = new (Z) StoreLocalNode( TokenPosition::kNoSource, controller_var, controller_constructor_call); current_block_->statements->Add(store_controller); // Grab :controller.stream InstanceGetterNode* controller_stream = new (Z) InstanceGetterNode( TokenPosition::kNoSource, new (Z) LoadLocalNode(TokenPosition::kNoSource, controller_var), Symbols::Stream()); // Store :controller.stream into :controller_stream inside the closure. // We have to remember the stream because a new instance is generated for // each getter invocation and in order to recreate the linkage, we need the // awaited on instance. LocalVariable* controller_stream_var = current_block_->scope->LookupVariable(Symbols::ControllerStream(), false); ASSERT(controller_stream_var != NULL); StoreLocalNode* store_controller_stream = new (Z) StoreLocalNode( TokenPosition::kNoSource, controller_stream_var, controller_stream); current_block_->statements->Add(store_controller_stream); // return :controller.stream; ReturnNode* return_node = new (Z) ReturnNode( TokenPosition::kNoSource, new (Z) LoadLocalNode(TokenPosition::kNoSource, controller_stream_var)); current_block_->statements->Add(return_node); return CloseBlock(); } void Parser::OpenAsyncGeneratorClosure() { async_temp_scope_ = current_block_->scope; OpenAsyncTryBlock(); } SequenceNode* Parser::CloseAsyncGeneratorClosure(SequenceNode* body) { // We need a temporary expression to store intermediate return values. parsed_function()->EnsureExpressionTemp(); SequenceNode* new_body = CloseAsyncGeneratorTryBlock(body); ASSERT(new_body != NULL); ASSERT(new_body->scope() != NULL); return new_body; } // Add a return node to the sequence if necessary. void Parser::EnsureHasReturnStatement(SequenceNode* seq, TokenPosition return_pos) { if ((seq->length() == 0) || !seq->NodeAt(seq->length() - 1)->IsReturnNode()) { const Function& func = innermost_function(); // The implicit return value of synchronous generator closures is false, // to indicate that there are no more elements in the iterable. // In other cases the implicit return value is null. AstNode* return_value = func.IsSyncGenClosure() ? new LiteralNode(return_pos, Bool::False()) : new LiteralNode(return_pos, Instance::ZoneHandle()); seq->Add(new ReturnNode(return_pos, return_value)); } } SequenceNode* Parser::CloseBlock() { SequenceNode* statements = current_block_->statements; if (current_block_->scope != NULL) { // Record the begin and end token index of the scope. ASSERT(statements != NULL); current_block_->scope->set_begin_token_pos(statements->token_pos()); current_block_->scope->set_end_token_pos(TokenPos()); } current_block_ = current_block_->parent; return statements; } SequenceNode* Parser::CloseAsyncFunction(const Function& closure, SequenceNode* closure_body) { TRACE_PARSER("CloseAsyncFunction"); ASSERT(!closure.IsNull()); ASSERT(closure_body != NULL); // Explicitly reference variables of the async function from the // closure body in order to mark them as captured. LocalVariable* existing_var = closure_body->scope()->LookupVariable(Symbols::AwaitJumpVar(), false); ASSERT((existing_var != NULL) && existing_var->is_captured()); existing_var = closure_body->scope()->LookupVariable(Symbols::AwaitContextVar(), false); ASSERT((existing_var != NULL) && existing_var->is_captured()); existing_var = closure_body->scope()->LookupVariable(Symbols::AsyncCompleter(), false); ASSERT((existing_var != NULL) && existing_var->is_captured()); existing_var = closure_body->scope()->LookupVariable( Symbols::AsyncStackTraceVar(), false); ASSERT((existing_var != NULL) && existing_var->is_captured()); // Create and return a new future that executes a closure with the current // body. // No need to capture parameters or other variables, since they have already // been captured in the corresponding scope as the body has been parsed within // a nested block (contained in the async function's block). const Class& future = Class::ZoneHandle(Z, I->object_store()->future_class()); ASSERT(!future.IsNull()); const Function& constructor = Function::ZoneHandle( Z, future.LookupFunction(Symbols::FutureMicrotask())); ASSERT(!constructor.IsNull()); const Class& completer = Class::ZoneHandle(Z, I->object_store()->completer_class()); ASSERT(!completer.IsNull()); const Function& completer_constructor = Function::ZoneHandle( Z, completer.LookupFunction(Symbols::CompleterSyncConstructor())); ASSERT(!completer_constructor.IsNull()); LocalVariable* async_completer = current_block_->scope->LookupVariable(Symbols::AsyncCompleter(), false); const TokenPosition token_pos = ST(closure_body->token_pos()); // Add to AST: // :async_completer = new Completer.sync(); ArgumentListNode* empty_args = new (Z) ArgumentListNode(token_pos); ConstructorCallNode* completer_constructor_node = new (Z) ConstructorCallNode(token_pos, TypeArguments::ZoneHandle(Z), completer_constructor, empty_args); StoreLocalNode* store_completer = new (Z) StoreLocalNode(token_pos, async_completer, completer_constructor_node); current_block_->statements->Add(store_completer); // :await_jump_var = -1; LocalVariable* jump_var = current_block_->scope->LookupVariable(Symbols::AwaitJumpVar(), false); LiteralNode* init_value = new (Z) LiteralNode(token_pos, Smi::ZoneHandle(Smi::New(-1))); current_block_->statements->Add( new (Z) StoreLocalNode(token_pos, jump_var, init_value)); // Add to AST: // :async_op = ; (containing the original body) LocalVariable* async_op_var = current_block_->scope->LookupVariable(Symbols::AsyncOperation(), false); ClosureNode* cn = new (Z) ClosureNode(token_pos, closure, NULL, closure_body->scope()); StoreLocalNode* store_async_op = new (Z) StoreLocalNode(token_pos, async_op_var, cn); current_block_->statements->Add(store_async_op); const Library& async_lib = Library::Handle(Library::AsyncLibrary()); if (FLAG_causal_async_stacks) { // Add to AST: // :async_stack_trace = _asyncStackTraceHelper(); const Function& async_stack_trace_helper = Function::ZoneHandle( Z, async_lib.LookupFunctionAllowPrivate(Symbols::AsyncStackTraceHelper())); ASSERT(!async_stack_trace_helper.IsNull()); ArgumentListNode* async_stack_trace_helper_args = new (Z) ArgumentListNode(token_pos); async_stack_trace_helper_args->Add( new (Z) LoadLocalNode(token_pos, async_op_var)); StaticCallNode* async_stack_trace_helper_call = new (Z) StaticCallNode(token_pos, async_stack_trace_helper, async_stack_trace_helper_args, StaticCallNode::kStatic); LocalVariable* async_stack_trace_var = current_block_->scope->LookupVariable(Symbols::AsyncStackTraceVar(), false); StoreLocalNode* store_async_stack_trace = new (Z) StoreLocalNode( token_pos, async_stack_trace_var, async_stack_trace_helper_call); current_block_->statements->Add(store_async_stack_trace); } // :async_then_callback = _asyncThenWrapperHelper(:async_op) const Function& async_then_wrapper_helper = Function::ZoneHandle( Z, async_lib.LookupFunctionAllowPrivate(Symbols::AsyncThenWrapperHelper())); ASSERT(!async_then_wrapper_helper.IsNull()); ArgumentListNode* async_then_wrapper_helper_args = new (Z) ArgumentListNode(token_pos); async_then_wrapper_helper_args->Add( new (Z) LoadLocalNode(token_pos, async_op_var)); StaticCallNode* then_wrapper_call = new (Z) StaticCallNode(token_pos, async_then_wrapper_helper, async_then_wrapper_helper_args, StaticCallNode::kStatic); LocalVariable* async_then_callback_var = current_block_->scope->LookupVariable(Symbols::AsyncThenCallback(), false); StoreLocalNode* store_async_then_callback = new (Z) StoreLocalNode(token_pos, async_then_callback_var, then_wrapper_call); current_block_->statements->Add(store_async_then_callback); // :async_catch_error_callback = _asyncErrorWrapperHelper(:async_op) const Function& async_error_wrapper_helper = Function::ZoneHandle( Z, async_lib.LookupFunctionAllowPrivate(Symbols::AsyncErrorWrapperHelper())); ASSERT(!async_error_wrapper_helper.IsNull()); ArgumentListNode* async_error_wrapper_helper_args = new (Z) ArgumentListNode(token_pos); async_error_wrapper_helper_args->Add( new (Z) LoadLocalNode(token_pos, async_op_var)); StaticCallNode* error_wrapper_call = new (Z) StaticCallNode(token_pos, async_error_wrapper_helper, async_error_wrapper_helper_args, StaticCallNode::kStatic); LocalVariable* async_catch_error_callback_var = current_block_->scope->LookupVariable(Symbols::AsyncCatchErrorCallback(), false); StoreLocalNode* store_async_catch_error_callback = new (Z) StoreLocalNode( token_pos, async_catch_error_callback_var, error_wrapper_call); current_block_->statements->Add(store_async_catch_error_callback); // Add to AST: // new Future.microtask(:async_op); ArgumentListNode* arguments = new (Z) ArgumentListNode(token_pos); arguments->Add(new (Z) LoadLocalNode(token_pos, async_op_var)); ConstructorCallNode* future_node = new (Z) ConstructorCallNode( token_pos, TypeArguments::ZoneHandle(Z), constructor, arguments); current_block_->statements->Add(future_node); // Add to AST: // return :async_completer.future; ReturnNode* return_node = new (Z) ReturnNode( token_pos, new (Z) InstanceGetterNode( token_pos, new (Z) LoadLocalNode(token_pos, async_completer), Symbols::CompleterFuture())); current_block_->statements->Add(return_node); return CloseBlock(); } SequenceNode* Parser::CloseAsyncClosure(SequenceNode* body, TokenPosition func_end_pos) { // We need a temporary expression to store intermediate return values. parsed_function()->EnsureExpressionTemp(); SequenceNode* new_body = CloseAsyncTryBlock(body, func_end_pos); ASSERT(new_body != NULL); ASSERT(new_body->scope() != NULL); return new_body; } // Set up default values for all optional parameters to the function. void Parser::SetupDefaultsForOptionalParams(const ParamList& params) { if ((current_function().raw() == innermost_function().raw()) && (params.num_optional_parameters > 0)) { ZoneGrowableArray* default_values = new ZoneGrowableArray(zone(), params.num_optional_parameters); // Build array of default parameter values. const ZoneGrowableArray& parameters = *params.parameters; const int first_opt_param_offset = params.num_fixed_parameters; for (int i = 0; i < params.num_optional_parameters; i++) { const Instance* default_value = parameters[i + first_opt_param_offset].default_value; default_values->Add(default_value); } parsed_function()->set_default_parameter_values(default_values); } } void Parser::FinalizeFormalParameterTypes(const ParamList* params) { ASSERT((params != NULL) && (params->parameters != NULL)); const int num_parameters = params->parameters->length(); AbstractType& type = AbstractType::Handle(Z); for (int i = 0; i < num_parameters; i++) { ParamDesc& param_desc = (*params->parameters)[i]; type = param_desc.type->raw(); ResolveTypeParameters(&type); type = CanonicalizeType(type); if (type.raw() != param_desc.type->raw()) { param_desc.type = &AbstractType::ZoneHandle(Z, type.raw()); } } } // Populate the parameter type array and parameter name array of the function // with the formal parameter types and names. void Parser::AddFormalParamsToFunction(const ParamList* params, const Function& func) { ASSERT((params != NULL) && (params->parameters != NULL)); ASSERT((params->num_optional_parameters > 0) == (params->has_optional_positional_parameters || params->has_optional_named_parameters)); if (!Utils::IsInt(16, params->num_fixed_parameters) || !Utils::IsInt(16, params->num_optional_parameters)) { const Script& script = Script::Handle(Class::Handle(func.Owner()).script()); Report::MessageF(Report::kError, script, func.token_pos(), Report::AtLocation, "too many formal parameters"); } func.set_num_fixed_parameters(params->num_fixed_parameters); func.SetNumOptionalParameters(params->num_optional_parameters, params->has_optional_positional_parameters); const int num_parameters = params->parameters->length(); ASSERT(num_parameters == func.NumParameters()); ASSERT(func.parameter_types() == Object::empty_array().raw()); ASSERT(func.parameter_names() == Object::empty_array().raw()); func.set_parameter_types( Array::Handle(Array::New(num_parameters, Heap::kOld))); func.set_parameter_names( Array::Handle(Array::New(num_parameters, Heap::kOld))); AbstractType& param_type = AbstractType::Handle(); for (int i = 0; i < num_parameters; i++) { ParamDesc& param_desc = (*params->parameters)[i]; param_type = param_desc.type->raw(); if (param_desc.is_covariant) { if (!func.IsDynamicFunction(true)) { ReportError(param_desc.name_pos, "only instance functions may have " "covariant parameters"); } // In non-strong mode, the covariant keyword is ignored. In strong mode, // the parameter type is changed to Object. if (FLAG_strong) { param_type = Type::ObjectType(); } } func.SetParameterTypeAt(i, param_type); func.SetParameterNameAt(i, *param_desc.name); if (param_desc.is_field_initializer && !func.IsGenerativeConstructor()) { // Redirecting constructors are detected later in ParseConstructor. ReportError(param_desc.name_pos, "only generative constructors may have " "initializing formal parameters"); } } } // Populate local scope with the formal parameters. void Parser::AddFormalParamsToScope(const ParamList* params, LocalScope* scope) { ASSERT((params != NULL) && (params->parameters != NULL)); ASSERT(scope != NULL); const int num_parameters = params->parameters->length(); for (int i = 0; i < num_parameters; i++) { ParamDesc& param_desc = (*params->parameters)[i]; const String* name = param_desc.name; LocalVariable* parameter = new (Z) LocalVariable( param_desc.name_pos, param_desc.name_pos, *name, *param_desc.type); if (!scope->InsertParameterAt(i, parameter)) { ReportError(param_desc.name_pos, "name '%s' already exists in scope", param_desc.name->ToCString()); } param_desc.var = parameter; if (param_desc.is_final) { parameter->set_is_final(); } // Field initializer parameters are implicitly final. ASSERT(!param_desc.is_field_initializer || param_desc.is_final); } } // Builds ReturnNode/NativeBodyNode for a native function. void Parser::ParseNativeFunctionBlock(const ParamList* params, const Function& func) { ASSERT(func.is_native()); ASSERT(func.NumParameters() == params->parameters->length()); TRACE_PARSER("ParseNativeFunctionBlock"); // Parse the function name out. const String& native_name = ParseNativeDeclaration(); // Now add the NativeBodyNode and return statement. current_block_->statements->Add(new (Z) ReturnNode( TokenPos(), new (Z) NativeBodyNode(TokenPos(), Function::ZoneHandle(Z, func.raw()), native_name, current_block_->scope, FLAG_link_natives_lazily))); } LocalVariable* Parser::LookupReceiver(LocalScope* from_scope, bool test_only) { ASSERT(!current_function().is_static()); return from_scope->LookupVariable(Symbols::This(), test_only); } LocalVariable* Parser::LookupTypeArgumentsParameter(LocalScope* from_scope, bool test_only) { ASSERT(current_function().IsInFactoryScope()); return from_scope->LookupVariable(Symbols::TypeArgumentsParameter(), test_only); } void Parser::CaptureInstantiator() { ASSERT(FunctionLevel() > 0); const String* variable_name = current_function().IsInFactoryScope() ? &Symbols::TypeArgumentsParameter() : &Symbols::This(); current_block_->scope->CaptureVariable( current_block_->scope->LookupVariable(*variable_name, true)); } void Parser::CaptureFunctionTypeArguments() { ASSERT(InGenericFunctionScope()); ASSERT(FunctionLevel() > 0); if (!Isolate::Current()->reify_generic_functions()) { return; } const String* variable_name = &Symbols::FunctionTypeArgumentsVar(); current_block_->scope->CaptureVariable( current_block_->scope->LookupVariable(*variable_name, true)); } void Parser::CaptureAllInstantiators() { if (IsInstantiatorRequired()) { CaptureInstantiator(); } if (innermost_function().HasGenericParent()) { CaptureFunctionTypeArguments(); } } AstNode* Parser::LoadReceiver(TokenPosition token_pos) { // A nested function may access 'this', referring to the receiver of the // outermost enclosing function. const bool kTestOnly = false; LocalVariable* receiver = LookupReceiver(current_block_->scope, kTestOnly); if (receiver == NULL) { ReportError(token_pos, "illegal implicit access to receiver 'this'"); } return new (Z) LoadLocalNode(TokenPos(), receiver); } InstanceGetterNode* Parser::CallGetter(TokenPosition token_pos, AstNode* object, const String& name) { return new (Z) InstanceGetterNode(token_pos, object, name); } // Returns ast nodes of the variable initialization. AstNode* Parser::ParseVariableDeclaration(const AbstractType& type, bool is_final, bool is_const, SequenceNode** await_preamble) { TRACE_PARSER("ParseVariableDeclaration"); ASSERT(IsIdentifier()); const TokenPosition ident_pos = TokenPos(); const String& ident = *CurrentLiteral(); ConsumeToken(); // Variable identifier. const TokenPosition assign_pos = TokenPos(); AstNode* initialization = NULL; LocalVariable* variable = NULL; if (CurrentToken() == Token::kASSIGN) { // Variable initialization. ConsumeToken(); AstNode* expr = ParseAwaitableExpr(is_const, kConsumeCascades, await_preamble); const TokenPosition expr_end_pos = TokenPos(); variable = new (Z) LocalVariable(ident_pos, expr_end_pos, ident, type); initialization = new (Z) StoreLocalNode(assign_pos, variable, expr); if (is_const) { ASSERT(expr->IsLiteralNode()); variable->SetConstValue(expr->AsLiteralNode()->literal()); } } else if (is_final || is_const) { ReportError(ident_pos, "missing initialization of 'final' or 'const' variable"); } else { // Initialize variable with null. variable = new (Z) LocalVariable(ident_pos, assign_pos, ident, type); AstNode* null_expr = new (Z) LiteralNode(ident_pos, Object::null_instance()); initialization = new (Z) StoreLocalNode(ident_pos, variable, null_expr); } ASSERT(current_block_ != NULL); const TokenPosition previous_pos = current_block_->scope->PreviousReferencePos(ident); if (previous_pos.IsReal()) { ASSERT(!script_.IsNull()); if (previous_pos > ident_pos) { ReportError(ident_pos, "initializer of '%s' may not refer to itself", ident.ToCString()); } else { intptr_t line_number; script_.GetTokenLocation(previous_pos, &line_number, NULL); ReportError(ident_pos, "identifier '%s' previously used in line %" Pd "", ident.ToCString(), line_number); } } // Add variable to scope after parsing the initializer expression. // The expression must not be able to refer to the variable. if (!current_block_->scope->AddVariable(variable)) { LocalVariable* existing_var = current_block_->scope->LookupVariable(variable->name(), true); ASSERT(existing_var != NULL); // Use before define cases have already been detected and reported above. ASSERT(existing_var->owner() == current_block_->scope); ReportError(ident_pos, "identifier '%s' already defined", variable->name().ToCString()); } if (is_final || is_const) { variable->set_is_final(); } return initialization; } // Parses ('var' | 'final' [type] | 'const' [type] | type). // The presence of 'final' or 'const' must be detected and remembered // before the call. If a type is parsed, it may be resolved and finalized // according to the given type finalization mode. RawAbstractType* Parser::ParseConstFinalVarOrType( ClassFinalizer::FinalizationKind finalization) { TRACE_PARSER("ParseConstFinalVarOrType"); if (CurrentToken() == Token::kVAR) { ConsumeToken(); return Type::DynamicType(); } bool type_is_optional = false; if ((CurrentToken() == Token::kFINAL) || (CurrentToken() == Token::kCONST)) { ConsumeToken(); type_is_optional = true; } if ((CurrentToken() == Token::kVOID) || IsFunctionTypeSymbol()) { return ParseFunctionType(AbstractType::Handle(Z), finalization); } if (CurrentToken() != Token::kIDENT) { if (type_is_optional) { return Type::DynamicType(); } else { ReportError("type name expected"); } } if (type_is_optional) { Token::Kind follower = LookaheadToken(1); // We have an identifier followed by a 'follower' token. // We either parse a type or return now. if ((follower != Token::kLT) && // Parameterized type. (follower != Token::kPERIOD) && // Qualified class name of type. !Token::IsIdentifier(follower) && // Variable name following a type. (follower != Token::kTHIS)) { // Field parameter following a type. return Type::DynamicType(); } } return ParseTypeOrFunctionType(false, finalization); } // Returns ast nodes of the variable initialization. Variables without an // explicit initializer are initialized to null. If several variables are // declared, the individual initializers are collected in a sequence node. AstNode* Parser::ParseVariableDeclarationList() { TRACE_PARSER("ParseVariableDeclarationList"); SkipMetadata(); bool is_final = (CurrentToken() == Token::kFINAL); bool is_const = (CurrentToken() == Token::kCONST); const AbstractType& type = AbstractType::ZoneHandle( Z, ParseConstFinalVarOrType(I->type_checks() ? ClassFinalizer::kCanonicalize : ClassFinalizer::kIgnore)); if (!IsIdentifier()) { ReportError("identifier expected"); } SequenceNode* preamble = NULL; AstNode* initializers = ParseVariableDeclaration(type, is_final, is_const, &preamble); ASSERT(initializers != NULL); if (preamble != NULL) { preamble->Add(initializers); initializers = preamble; } while (CurrentToken() == Token::kCOMMA) { ConsumeToken(); if (!IsIdentifier()) { ReportError("identifier expected after comma"); } // We have a second initializer. Allocate a sequence node now. // The sequence does not own the current scope. Set its own scope to NULL. SequenceNode* sequence = NodeAsSequenceNode(initializers->token_pos(), initializers, NULL); preamble = NULL; AstNode* declaration = ParseVariableDeclaration(type, is_final, is_const, &preamble); if (preamble != NULL) { sequence->Add(preamble); } sequence->Add(declaration); initializers = sequence; } return initializers; } AstNode* Parser::ParseFunctionStatement(bool is_literal) { TRACE_PARSER("ParseFunctionStatement"); AbstractType& result_type = AbstractType::Handle(Z, Type::DynamicType()); const String* function_name = NULL; const TokenPosition function_pos = TokenPos(); TokenPosition function_name_pos = TokenPosition::kNoSource; TokenPosition metadata_pos = TokenPosition::kNoSource; if (is_literal) { ASSERT(CurrentToken() == Token::kLPAREN || CurrentToken() == Token::kLT); function_name = &Symbols::AnonymousClosure(); } else { metadata_pos = SkipMetadata(); // Parse optional result type. if (IsFunctionReturnType()) { // 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. result_type = ParseTypeOrFunctionType(true, ClassFinalizer::kDoNotResolve); } function_name_pos = TokenPos(); function_name = ExpectIdentifier("function name expected"); // Check that the function name has not been referenced // before this declaration. ASSERT(current_block_ != NULL); const TokenPosition previous_pos = current_block_->scope->PreviousReferencePos(*function_name); if (previous_pos.IsReal()) { ASSERT(!script_.IsNull()); intptr_t line_number; script_.GetTokenLocation(previous_pos, &line_number, NULL); ReportError(function_name_pos, "identifier '%s' previously used in line %" Pd "", function_name->ToCString(), line_number); } } // Check whether we have parsed this closure function before, in a previous // compilation. If so, reuse the function object, else create a new one // and register it in the current class. // Note that we cannot share the same closure function between the closurized // and non-closurized versions of the same parent function. Function& function = Function::ZoneHandle(Z); bool found_func = true; // TODO(hausner): There could be two different closures at the given // function_pos, one enclosed in a closurized function and one enclosed in the // non-closurized version of this same function. function = I->LookupClosureFunction(innermost_function(), function_pos); if (function.IsNull()) { // The function will be registered in the lookup table by the // EffectGraphVisitor::VisitClosureNode when the newly allocated closure // function has been properly setup. found_func = false; function = Function::NewClosureFunction(*function_name, innermost_function(), function_pos); function.set_result_type(result_type); // The result type may refer to the function's type parameters, // but was not parsed in the scope of the function. Adjust. result_type.SetScopeFunction(function); if (metadata_pos.IsReal()) { library_.AddFunctionMetadata(function, metadata_pos); } } ASSERT(function.parent_function() == innermost_function_.raw()); innermost_function_ = function.raw(); if (CurrentToken() == Token::kLT) { if (!found_func) { ParseTypeParameters(false); // Not parameterizing class, but function. } else { TryParseTypeParameters(); } } CheckToken(Token::kLPAREN); // The function type needs to be finalized at compile time, since the closure // may be type checked at run time when assigned to a function variable, // passed as a function argument, or returned as a function result. LocalVariable* function_variable = NULL; Type& function_type = Type::ZoneHandle(Z); if (!is_literal) { // Since the function type depends on the signature of the closure function, // it cannot be determined before the formal parameter list of the closure // function is parsed. Therefore, we set the function type to a new // function type to be patched after the actual type is known. // We temporarily use the Closure class as scope class. const Class& unknown_scope_class = Class::Handle(Z, I->object_store()->closure_class()); function_type = Type::New(unknown_scope_class, TypeArguments::Handle(Z), function_pos); function_type.set_signature(function); function_type.SetIsFinalized(); // No finalization needed. // Add the function variable to the scope before parsing the function in // order to allow self reference from inside the function. function_variable = new (Z) LocalVariable(function_name_pos, function_pos, *function_name, function_type); function_variable->set_is_final(); ASSERT(current_block_ != NULL); ASSERT(current_block_->scope != NULL); if (!current_block_->scope->AddVariable(function_variable)) { LocalVariable* existing_var = current_block_->scope->LookupVariable( function_variable->name(), true); ASSERT(existing_var != NULL); // Use before define cases have already been detected and reported above. ASSERT(existing_var->owner() == current_block_->scope); ReportError(function_pos, "identifier '%s' already defined", function_variable->name().ToCString()); } } Type& signature_type = Type::ZoneHandle(Z); SequenceNode* statements = NULL; if (!found_func) { // Parse the local function. As a side effect of the parsing, the // variables of this function's scope that are referenced by the local // function (and its inner nested functions) will be marked as captured. ResolveTypeParameters(&result_type); function.set_result_type(result_type); // Update type without scope change. // Type parameters appearing in parameter types are resolved in ParseFunc. statements = Parser::ParseFunc(function, !is_literal); INC_STAT(thread(), num_functions_parsed, 1); // Now that the local function has formal parameters, finalize its signature signature_type = function.SignatureType(); signature_type ^= CanonicalizeType(signature_type); function.SetSignatureType(signature_type); } else { // The local function was parsed before. The captured variables are // saved in the function's context scope. Iterate over the context scope // and mark its variables as captured. const ContextScope& context_scope = ContextScope::Handle(Z, function.context_scope()); ASSERT(!context_scope.IsNull()); String& var_name = String::Handle(Z); for (int i = 0; i < context_scope.num_variables(); i++) { var_name = context_scope.NameAt(i); // We need to look up the name in a way that returns even hidden // variables, e.g. 'this' in an initializer list. LocalVariable* v = current_block_->scope->LookupVariable(var_name, true); ASSERT(v != NULL); current_block_->scope->CaptureVariable(v); } SkipFunctionLiteral(); signature_type = function.SignatureType(); } // Local functions are registered in the enclosing class, but // ignored during class finalization. The enclosing class has // already been finalized. ASSERT(current_class().is_finalized()); ASSERT(signature_type.IsFinalized()); // Make sure that the instantiators are captured. if ((FunctionLevel() > 0) && !signature_type.IsInstantiated()) { CaptureAllInstantiators(); } // A local 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()); if (!is_literal) { // Patch the function type of the variable now that the signature is known. function_type.set_type_class(Class::Handle(Z, signature_type.type_class())); function_type.set_arguments( TypeArguments::Handle(Z, signature_type.arguments())); ASSERT(function_type.signature() == function.raw()); // The function type was initially marked as instantiated, but it may // actually be uninstantiated. function_type.ResetIsFinalized(); // The function variable type should have been patched above. ASSERT(function_variable->type().raw() == function_type.raw()); } // The code generator does not compile the closure function when visiting // a ClosureNode. The generated code allocates a new Closure object containing // the current context. The type of the Closure object refers to the closure // function, which will be compiled on first invocation of the closure object. // Therefore, we ignore the parsed default_parameter_values and the // node_sequence representing the body of the closure function, which will be // parsed again when compiled later. // The only purpose of parsing the function now (besides reporting obvious // errors) is to mark referenced variables of the enclosing scopes as // captured. The captured variables will be recorded along with their // allocation information in a Scope object stored in the function object. // This Scope object is then provided to the compiler when compiling the local // function. It would be too early to record the captured variables here, // since further closure functions may capture more variables. // This Scope object is constructed after all variables have been allocated. // The local scope of the parsed function can be pruned, since contained // variables are not relevant for the compilation of the enclosing function. // This pruning is done by omitting to hook the local scope in its parent // scope in the constructor of LocalScope. AstNode* closure = new (Z) ClosureNode(function_pos, function, NULL, statements != NULL ? statements->scope() : NULL); ASSERT(innermost_function_.raw() == function.raw()); innermost_function_ = function.parent_function(); return is_literal ? closure : new (Z) StoreLocalNode(function_pos, function_variable, closure); } // Returns true if the current and next tokens can be parsed as type // parameters. Current token position is not saved and restored. bool Parser::TryParseTypeParameters() { ASSERT(CurrentToken() == Token::kLT); int nesting_level = 0; do { Token::Kind ct = CurrentToken(); if (ct == Token::kLT) { nesting_level++; } else if (ct == Token::kGT) { nesting_level--; } else if (ct == Token::kSHR) { nesting_level -= 2; } else if (ct == Token::kIDENT) { // Check to see if it is a qualified identifier. if (LookaheadToken(1) == Token::kPERIOD) { // Consume the identifier, the period will be consumed below. ConsumeToken(); } } else if ((ct != Token::kCOMMA) && (ct != Token::kEXTENDS)) { // We are looking at something other than type parameters. return false; } ConsumeToken(); } while (nesting_level > 0); if (nesting_level < 0) { return false; } return true; } // Returns true if the next tokens can be parsed as type parameters. bool Parser::IsTypeParameters() { if (CurrentToken() == Token::kLT) { TokenPosScope param_pos(this); if (!TryParseTypeParameters()) { return false; } return true; } return false; } // Returns true if the next tokens are [ typeParameters ] '('. bool Parser::IsParameterPart() { if (CurrentToken() == Token::kLPAREN) { return true; } if (CurrentToken() == Token::kLT) { TokenPosScope type_arg_pos(this); if (!TryParseTypeParameters()) { return false; } return CurrentToken() == Token::kLPAREN; } return false; } // Returns true if the current and next tokens can be parsed as type // arguments. Current token position is not saved and restored. bool Parser::TryParseTypeArguments() { ASSERT(CurrentToken() == Token::kLT); int nesting_level = 0; do { Token::Kind ct = CurrentToken(); if (ct == Token::kLT) { nesting_level++; } else if (ct == Token::kGT) { nesting_level--; } else if (ct == Token::kSHR) { nesting_level -= 2; } else if (ct == Token::kIDENT) { if (IsFunctionTypeSymbol()) { if (!TryParseType(false)) { return false; } continue; } else { // Check to see if it is a qualified identifier. if (LookaheadToken(1) == Token::kPERIOD) { // Consume the identifier, the period will be consumed below. ConsumeToken(); } } } else if (ct != Token::kCOMMA) { return false; } ConsumeToken(); } while (nesting_level > 0); if (nesting_level < 0) { return false; } return true; } // Returns true if the next tokens are [ typeArguments ] '('. bool Parser::IsArgumentPart() { if (CurrentToken() == Token::kLPAREN) { return true; } if (CurrentToken() == Token::kLT) { TokenPosScope type_arg_pos(this); if (!TryParseTypeArguments()) { return false; } return CurrentToken() == Token::kLPAREN; } return false; } bool Parser::IsSimpleLiteral(const AbstractType& type, Instance* value) { // Assigning null never causes a type error. if (CurrentToken() == Token::kNULL) { *value = Instance::null(); return true; } // If the type of the const field is guaranteed to be instantiated once // resolved at class finalization time, and if the type of the literal is one // of int, double, String, or bool, then preset the field with the value and // perform the type check (in checked mode only) at finalization time. if (type.IsTypeParameter() || (type.arguments() != TypeArguments::null())) { // Type parameters are always resolved eagerly by the parser and never // resolved later by the class finalizer. Therefore, we know here that if // 'type' is not a type parameter (an unresolved type will not get resolved // to a type parameter later) and if 'type' has no type arguments, then it // will be instantiated at class finalization time. Otherwise, we return // false, since the type test would not be possible at finalization time for // an uninstantiated type. return false; } if (CurrentToken() == Token::kINTEGER) { *value = CurrentIntegerLiteral(); return true; } else if (CurrentToken() == Token::kDOUBLE) { *value = CurrentDoubleLiteral(); return true; } else if (CurrentToken() == Token::kSTRING) { *value = CurrentLiteral()->raw(); return true; } else if (CurrentToken() == Token::kTRUE) { *value = Bool::True().raw(); return true; } else if (CurrentToken() == Token::kFALSE) { *value = Bool::False().raw(); return true; } return false; } // Returns true if the current token is kIDENT or a pseudo-keyword. bool Parser::IsIdentifier() { return Token::IsIdentifier(CurrentToken()) && !(await_is_keyword_ && ((CurrentLiteral()->raw() == Symbols::Await().raw()) || (CurrentLiteral()->raw() == Symbols::Async().raw()) || (CurrentLiteral()->raw() == Symbols::YieldKw().raw()))); } bool Parser::IsSymbol(const String& symbol) { return (CurrentLiteral()->raw() == symbol.raw()) && (CurrentToken() == Token::kIDENT); } // Returns true if the current token is 'Function' followed by '<' or '('. // 'Function' not followed by '<' or '(' denotes the Function class. bool Parser::IsFunctionTypeSymbol() { return IsSymbol(Symbols::Function()) && ((LookaheadToken(1) == Token::kLPAREN) || (LookaheadToken(1) == Token::kLT)); } // Returns true if the next tokens can be parsed as a an optionally // qualified identifier: [ident '.'] ident. // Current token position is not restored. bool Parser::TryParseQualIdent() { if (CurrentToken() != Token::kIDENT) { return false; } ConsumeToken(); if (CurrentToken() == Token::kPERIOD) { ConsumeToken(); if (CurrentToken() != Token::kIDENT) { return false; } ConsumeToken(); } return true; } // Returns true if the next tokens can be parsed as a type with optional // type parameters. Current token position is not restored. // Allow 'void' as type if 'allow_void' is true. // Note that 'void Function()' is always allowed, since it is a function type // and not the void type. bool Parser::TryParseType(bool allow_void) { bool found = false; if (CurrentToken() == Token::kVOID) { ConsumeToken(); if (allow_void) { found = true; } else if (!IsFunctionTypeSymbol()) { return false; } } else if ((CurrentToken() == Token::kIDENT) && !IsFunctionTypeSymbol()) { // 'Function' not followed by '(' or '<' means the Function class. if (!TryParseQualIdent()) { return false; } if ((CurrentToken() == Token::kLT) && !TryParseTypeArguments()) { return false; } found = true; } while (IsFunctionTypeSymbol()) { ConsumeToken(); if ((CurrentToken() == Token::kLT) && !TryParseTypeParameters()) { return false; } if (CurrentToken() == Token::kLPAREN) { SkipToMatchingParenthesis(); } else { return false; } found = true; } return found; } // Look ahead to detect whether the next tokens should be parsed as // a variable declaration. Ignores optional metadata. // Returns true if we detect the token pattern: // 'var' // | 'final' // | const [type] ident (';' | '=' | ',') // | type ident (';' | '=' | ',') // Token position remains unchanged. bool Parser::IsVariableDeclaration() { if ((CurrentToken() == Token::kVAR) || (CurrentToken() == Token::kFINAL)) { return true; } // Skip optional metadata. if (CurrentToken() == Token::kAT) { const TokenPosition saved_pos = TokenPos(); SkipMetadata(); const bool is_var_decl = IsVariableDeclaration(); SetPosition(saved_pos); return is_var_decl; } if ((CurrentToken() != Token::kIDENT) && (CurrentToken() != Token::kVOID) && (CurrentToken() != Token::kCONST)) { // Not a legal type identifier or void (result type of function type) // or const keyword or metadata. return false; } const TokenPosition saved_pos = TokenPos(); bool is_var_decl = false; bool have_type = false; if (CurrentToken() == Token::kCONST) { ConsumeToken(); have_type = true; // Type is dynamic if 'const' is not followed by a type. } if ((CurrentToken() == Token::kVOID) || IsFunctionTypeSymbol()) { if (TryParseType(false)) { have_type = true; } } else if (IsIdentifier()) { // Type or variable name. Token::Kind follower = LookaheadToken(1); if ((follower == Token::kLT) || // Parameterized type. (follower == Token::kPERIOD) || // Qualified class name of type. Token::IsIdentifier(follower)) { // Variable name following a type. // We see the beginning of something that could be a type. const TokenPosition type_pos = TokenPos(); if (TryParseType(false)) { have_type = true; } else { SetPosition(type_pos); } } } if (have_type && IsIdentifier()) { ConsumeToken(); if ((CurrentToken() == Token::kSEMICOLON) || (CurrentToken() == Token::kCOMMA) || (CurrentToken() == Token::kASSIGN)) { is_var_decl = true; } } SetPosition(saved_pos); return is_var_decl; } // Look ahead to see if the following tokens are a return type followed // by an identifier. bool Parser::IsFunctionReturnType() { TokenPosScope decl_pos(this); if (TryParseType(true)) { if (IsIdentifier()) { // Return type followed by function name. return true; } } return false; } // Look ahead to detect whether the next tokens should be parsed as // a function declaration. Token position remains unchanged. bool Parser::IsFunctionDeclaration() { bool is_external = false; TokenPosScope decl_pos(this); SkipMetadata(); if ((is_top_level_) && (CurrentToken() == Token::kEXTERNAL)) { // Skip over 'external' for top-level function declarations. is_external = true; ConsumeToken(); } const TokenPosition type_or_name_pos = TokenPos(); if (TryParseType(true)) { if (!IsIdentifier()) { SetPosition(type_or_name_pos); } } else { SetPosition(type_or_name_pos); } // Check for function name followed by optional type parameters. if (!IsIdentifier()) { return false; } ConsumeToken(); if ((CurrentToken() == Token::kLT) && !TryParseTypeParameters()) { return false; } // Optional type, function name and optinal type parameters are parsed. if (CurrentToken() != Token::kLPAREN) { return false; } // Check parameter list and the following token. SkipToMatchingParenthesis(); if ((CurrentToken() == Token::kLBRACE) || (CurrentToken() == Token::kARROW) || (is_top_level_ && IsSymbol(Symbols::Native())) || is_external || IsSymbol(Symbols::Async()) || IsSymbol(Symbols::Sync())) { return true; } return false; } bool Parser::IsTopLevelAccessor() { const TokenPosScope saved_pos(this); if (CurrentToken() == Token::kEXTERNAL) { ConsumeToken(); } if ((CurrentToken() == Token::kGET) || (CurrentToken() == Token::kSET)) { return true; } if (TryParseType(true)) { if ((CurrentToken() == Token::kGET) || (CurrentToken() == Token::kSET)) { if (Token::IsIdentifier(LookaheadToken(1))) { // Accessor name. return true; } } } return false; } bool Parser::IsFunctionLiteral() { if (!allow_function_literals_) { return false; } if ((CurrentToken() == Token::kLPAREN) || (CurrentToken() == Token::kLT)) { TokenPosScope saved_pos(this); if ((CurrentToken() == Token::kLT) && !TryParseTypeParameters()) { return false; } if (CurrentToken() != Token::kLPAREN) { return false; } SkipToMatchingParenthesis(); ParseFunctionModifier(); if ((CurrentToken() == Token::kLBRACE) || (CurrentToken() == Token::kARROW)) { return true; } } return false; } // Current token position is the token after the opening ( of the for // statement. Returns true if we recognize a for ( .. in expr) // statement. bool Parser::IsForInStatement() { const TokenPosScope saved_pos(this); // Allow const modifier as well when recognizing a for-in statement // pattern. We will get an error later if the loop variable is // declared with const. if (CurrentToken() == Token::kVAR || CurrentToken() == Token::kFINAL || CurrentToken() == Token::kCONST) { ConsumeToken(); } if (IsIdentifier()) { if (LookaheadToken(1) == Token::kIN) { return true; } else if (TryParseType(false)) { if (IsIdentifier()) { ConsumeToken(); } return CurrentToken() == Token::kIN; } } return false; } static bool ContainsAbruptCompletingStatement(SequenceNode* seq); static bool IsAbruptCompleting(AstNode* statement) { return statement->IsReturnNode() || statement->IsJumpNode() || statement->IsThrowNode() || (statement->IsSequenceNode() && ContainsAbruptCompletingStatement(statement->AsSequenceNode())); } static bool ContainsAbruptCompletingStatement(SequenceNode* seq) { for (int i = 0; i < seq->length(); i++) { if (IsAbruptCompleting(seq->NodeAt(i))) { return true; } } return false; } void Parser::ParseStatementSequence() { TRACE_PARSER("ParseStatementSequence"); const bool dead_code_allowed = true; bool abrupt_completing_seen = false; RecursionChecker rc(this); while (CurrentToken() != Token::kRBRACE) { const TokenPosition statement_pos = TokenPos(); AstNode* statement = ParseStatement(); // Do not add statements with no effect (e.g., LoadLocalNode). if ((statement != NULL) && statement->IsLoadLocalNode()) { // Skip load local. continue; } if (statement != NULL) { if (!dead_code_allowed && abrupt_completing_seen) { ReportError(statement_pos, "dead code after abrupt completing statement"); } current_block_->statements->Add(statement); abrupt_completing_seen |= IsAbruptCompleting(statement); } } } // Parse nested statement of if, while, for, etc. We automatically generate // a sequence of one statement if there are no curly braces. // The argument 'parsing_loop_body' indicates the parsing of a loop statement. SequenceNode* Parser::ParseNestedStatement(bool parsing_loop_body, SourceLabel* label) { TRACE_PARSER("ParseNestedStatement"); if (parsing_loop_body) { OpenLoopBlock(); } else { OpenBlock(); } if (label != NULL) { current_block_->scope->AddLabel(label); } if (CurrentToken() == Token::kLBRACE) { ConsumeToken(); ParseStatementSequence(); ExpectToken(Token::kRBRACE); } else { RecursionChecker rc(this); AstNode* statement = ParseStatement(); if (statement != NULL) { current_block_->statements->Add(statement); } } SequenceNode* sequence = CloseBlock(); return sequence; } AstNode* Parser::ParseIfStatement(String* label_name) { TRACE_PARSER("ParseIfStatement"); ASSERT(CurrentToken() == Token::kIF); const TokenPosition if_pos = TokenPos(); SourceLabel* label = NULL; if (label_name != NULL) { label = SourceLabel::New(if_pos, label_name, SourceLabel::kStatement); OpenBlock(); current_block_->scope->AddLabel(label); } ConsumeToken(); ExpectToken(Token::kLPAREN); AstNode* cond_expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL); ExpectToken(Token::kRPAREN); const bool parsing_loop_body = false; SequenceNode* true_branch = ParseNestedStatement(parsing_loop_body, NULL); SequenceNode* false_branch = NULL; if (CurrentToken() == Token::kELSE) { ConsumeToken(); false_branch = ParseNestedStatement(parsing_loop_body, NULL); } AstNode* if_node = new (Z) IfNode(if_pos, cond_expr, true_branch, false_branch); if (label != NULL) { current_block_->statements->Add(if_node); SequenceNode* sequence = CloseBlock(); sequence->set_label(label); if_node = sequence; } return if_node; } // Return true if the type class of the given value implements the // == operator. static bool ImplementsEqualOperator(Zone* zone, const Instance& value) { Class& cls = Class::Handle(value.clazz()); const Function& equal_op = Function::Handle( zone, Resolver::ResolveDynamicAnyArgs(zone, cls, Symbols::EqualOperator())); ASSERT(!equal_op.IsNull()); cls = equal_op.Owner(); return !cls.IsObjectClass(); } // Check that all case expressions are of the same type, either int, String, // or any other class that does not override the == operator. // The expressions are compile-time constants and are thus in the form // of a LiteralNode. RawClass* Parser::CheckCaseExpressions( const GrowableArray& values) { const intptr_t num_expressions = values.length(); if (num_expressions == 0) { return Object::dynamic_class(); } const Instance& first_value = values[0]->literal(); for (intptr_t i = 0; i < num_expressions; i++) { const Instance& val = values[i]->literal(); const TokenPosition val_pos = values[i]->token_pos(); if (first_value.IsInteger()) { if (!val.IsInteger()) { ReportError(val_pos, "expected case expression of type int"); } continue; } if (first_value.IsString()) { if (!val.IsString()) { ReportError(val_pos, "expected case expression of type String"); } continue; } if (val.IsDouble()) { ReportError(val_pos, "case expression may not be of type double"); } if (val.clazz() != first_value.clazz()) { ReportError(val_pos, "all case expressions must be of same type"); } if (val.clazz() == I->object_store()->symbol_class()) { continue; } if (i == 0) { // The value is of some type other than int, String or double. // Check that the type class does not override the == operator. // Check this only in the first loop iteration since all values // are of the same type, which we check above. if (ImplementsEqualOperator(Z, val)) { ReportError(val_pos, "type class of case expression must not " "implement operator =="); } } } if (first_value.IsInteger()) { return Type::Handle(Z, Type::IntType()).type_class(); } else if (first_value.IsString()) { return Type::Handle(Z, Type::StringType()).type_class(); } return first_value.clazz(); } CaseNode* Parser::ParseCaseClause(LocalVariable* switch_expr_value, GrowableArray* case_expr_values, SourceLabel* case_label) { TRACE_PARSER("ParseCaseClause"); bool default_seen = false; const TokenPosition case_pos = TokenPos(); // The case expressions node sequence does not own the enclosing scope. SequenceNode* case_expressions = new (Z) SequenceNode(case_pos, NULL); while (CurrentToken() == Token::kCASE || CurrentToken() == Token::kDEFAULT) { if (CurrentToken() == Token::kCASE) { if (default_seen) { ReportError("default clause must be last case"); } ConsumeToken(); // Keyword case. const TokenPosition expr_pos = TokenPos(); AstNode* expr = ParseExpr(kRequireConst, kConsumeCascades); ASSERT(expr->IsLiteralNode()); case_expr_values->Add(expr->AsLiteralNode()); AstNode* switch_expr_load = new (Z) LoadLocalNode(case_pos, switch_expr_value); AstNode* case_comparison = new (Z) ComparisonNode(expr_pos, Token::kEQ, expr, switch_expr_load); case_expressions->Add(case_comparison); } else { if (default_seen) { ReportError("only one default clause is allowed"); } ConsumeToken(); // Keyword default. default_seen = true; // The default case always succeeds. } ExpectToken(Token::kCOLON); } OpenBlock(); bool abrupt_completing_seen = false; while (true) { // Check whether the next statement still belongs to the current case // clause. If we see 'case' or 'default', optionally preceeded by // a label, or closing brace, we stop parsing statements. Token::Kind next_token; if (IsIdentifier() && LookaheadToken(1) == Token::kCOLON) { next_token = LookaheadToken(2); } else { next_token = CurrentToken(); } if (next_token == Token::kRBRACE) { // End of switch statement. break; } if ((next_token == Token::kCASE) || (next_token == Token::kDEFAULT)) { // End of this case clause. If there is a possible fall-through to // the next case clause, throw an implicit FallThroughError. if (!abrupt_completing_seen) { ArgumentListNode* arguments = new (Z) ArgumentListNode(TokenPos()); arguments->Add(new (Z) LiteralNode( TokenPos(), Integer::ZoneHandle( Z, Integer::New(TokenPos().value(), Heap::kOld)))); current_block_->statements->Add(MakeStaticCall( Symbols::FallThroughError(), Library::PrivateCoreLibName(Symbols::ThrowNew()), arguments)); } break; } // The next statement still belongs to this case. AstNode* statement = ParseStatement(); if (statement != NULL) { current_block_->statements->Add(statement); abrupt_completing_seen |= IsAbruptCompleting(statement); } } SequenceNode* statements = CloseBlock(); return new (Z) CaseNode(case_pos, case_label, case_expressions, default_seen, switch_expr_value, statements); } AstNode* Parser::ParseSwitchStatement(String* label_name) { TRACE_PARSER("ParseSwitchStatement"); ASSERT(CurrentToken() == Token::kSWITCH); const TokenPosition switch_pos = TokenPos(); SourceLabel* label = SourceLabel::New(switch_pos, label_name, SourceLabel::kSwitch); ConsumeToken(); ExpectToken(Token::kLPAREN); const TokenPosition expr_pos = TokenPos(); AstNode* switch_expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL); ExpectToken(Token::kRPAREN); ExpectToken(Token::kLBRACE); OpenBlock(); current_block_->scope->AddLabel(label); // Store switch expression in temporary local variable. The type of the // variable is set to dynamic. It will later be patched to match the // type of the case clause expressions. Therefore, we have to allocate // a new type representing dynamic and can't reuse the canonical // type object for dynamic. const Type& temp_var_type = Type::ZoneHandle(Z, Type::New(Class::Handle(Z, Object::dynamic_class()), TypeArguments::Handle(Z), expr_pos)); temp_var_type.SetIsFinalized(); LocalVariable* temp_variable = new (Z) LocalVariable(expr_pos, expr_pos, Symbols::SwitchExpr(), temp_var_type); current_block_->scope->AddVariable(temp_variable); AstNode* save_switch_expr = new (Z) StoreLocalNode(expr_pos, temp_variable, switch_expr); current_block_->statements->Add(save_switch_expr); // Parse case clauses bool default_seen = false; GrowableArray case_expr_values; while (true) { // Check for statement label SourceLabel* case_label = NULL; if (IsIdentifier() && LookaheadToken(1) == Token::kCOLON) { // Case statements start with a label. String* label_name = CurrentLiteral(); const TokenPosition label_pos = TokenPos(); ConsumeToken(); // Consume label identifier. ConsumeToken(); // Consume colon. case_label = current_block_->scope->LocalLookupLabel(*label_name); if (case_label == NULL) { // Label does not exist yet. Add it to scope of switch statement. case_label = new (Z) SourceLabel(label_pos, *label_name, SourceLabel::kCase); current_block_->scope->AddLabel(case_label); } else if (case_label->kind() == SourceLabel::kForward) { // We have seen a 'continue' with this label name. Resolve // the forward reference. case_label->ResolveForwardReference(); RemoveNodesForFinallyInlining(case_label); } else { ReportError(label_pos, "label '%s' already exists in scope", label_name->ToCString()); } ASSERT(case_label->kind() == SourceLabel::kCase); } if (CurrentToken() == Token::kCASE || CurrentToken() == Token::kDEFAULT) { if (default_seen) { ReportError("no case clauses allowed after default clause"); } CaseNode* case_clause = ParseCaseClause(temp_variable, &case_expr_values, case_label); default_seen = case_clause->contains_default(); current_block_->statements->Add(case_clause); } else if (CurrentToken() != Token::kRBRACE) { ReportError("'case' or '}' expected"); } else if (case_label != NULL) { ReportError("expecting at least one case clause after label"); } else { break; } } // Check that all expressions in case clauses are of the same class, // or implement int, double or String. Patch the type of the temporary // variable holding the switch expression to match the type of the // case clause constants. temp_var_type.set_type_class( Class::Handle(Z, CheckCaseExpressions(case_expr_values))); // Check for unresolved label references. SourceLabel* unresolved_label = current_block_->scope->CheckUnresolvedLabels(); if (unresolved_label != NULL) { ReportError("unresolved reference to label '%s'", unresolved_label->name().ToCString()); } SequenceNode* switch_body = CloseBlock(); ExpectToken(Token::kRBRACE); return new (Z) SwitchNode(switch_pos, label, switch_body); } AstNode* Parser::ParseWhileStatement(String* label_name) { TRACE_PARSER("ParseWhileStatement"); const TokenPosition while_pos = TokenPos(); SourceLabel* label = SourceLabel::New(while_pos, label_name, SourceLabel::kWhile); ConsumeToken(); ExpectToken(Token::kLPAREN); SequenceNode* await_preamble = NULL; AstNode* cond_expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, &await_preamble); ExpectToken(Token::kRPAREN); const bool parsing_loop_body = true; SequenceNode* while_body = ParseNestedStatement(parsing_loop_body, label); WhileNode* while_node = new (Z) WhileNode(while_pos, label, cond_expr, await_preamble, while_body); return while_node; } AstNode* Parser::ParseDoWhileStatement(String* label_name) { TRACE_PARSER("ParseDoWhileStatement"); const TokenPosition do_pos = TokenPos(); SourceLabel* label = SourceLabel::New(do_pos, label_name, SourceLabel::kDoWhile); ConsumeToken(); const bool parsing_loop_body = true; SequenceNode* dowhile_body = ParseNestedStatement(parsing_loop_body, label); ExpectToken(Token::kWHILE); ExpectToken(Token::kLPAREN); SequenceNode* await_preamble = NULL; TokenPosition expr_pos = TokenPos(); AstNode* cond_expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, &await_preamble); if (await_preamble != NULL) { // Prepend the preamble to the condition. LetNode* await_cond = new (Z) LetNode(expr_pos); await_cond->AddNode(await_preamble); await_cond->AddNode(cond_expr); cond_expr = await_cond; } ExpectToken(Token::kRPAREN); ExpectSemicolon(); return new (Z) DoWhileNode(do_pos, label, cond_expr, dowhile_body); } static LocalVariable* LookupSavedTryContextVar(LocalScope* scope) { LocalVariable* var = scope->LocalLookupVariable(Symbols::SavedTryContextVar()); ASSERT((var != NULL) && !var->is_captured()); return var; } static LocalVariable* LookupAsyncSavedTryContextVar(Thread* thread, LocalScope* scope, uint16_t try_index) { Zone* zone = thread->zone(); const String& async_saved_try_ctx_name = String::ZoneHandle( zone, Symbols::NewFormatted( thread, "%s%d", Symbols::AsyncSavedTryCtxVarPrefix().ToCString(), try_index)); LocalVariable* var = scope->LocalLookupVariable(async_saved_try_ctx_name); ASSERT(var != NULL); return var; } // If the await or yield being parsed is in a try block, the continuation code // needs to restore the corresponding stack-based variable :saved_try_ctx_var, // and the stack-based variable :saved_try_ctx_var of the outer try block. // The inner :saved_try_ctx_var is used by a finally clause handling an // exception thrown by the continuation code in a try block or catch block. // If no finally clause exists, the catch or finally clause of the outer try // block, if any, uses the outer :saved_try_ctx_var to handle the exception. // // * Try blocks and catch blocks: // Set the context variable for this try block and for the outer try block. // * Finally blocks: // Set the context variable for the outer try block. Note that the try // declaring the finally is popped before parsing the finally clause, so the // outer try block is at the top of the try block list. void Parser::CheckAsyncOpInTryBlock( LocalVariable** saved_try_ctx, LocalVariable** async_saved_try_ctx, LocalVariable** outer_saved_try_ctx, LocalVariable** outer_async_saved_try_ctx) const { *saved_try_ctx = NULL; *async_saved_try_ctx = NULL; *outer_saved_try_ctx = NULL; *outer_async_saved_try_ctx = NULL; if (try_stack_ != NULL) { LocalScope* scope = try_stack_->try_block()->scope; uint16_t try_index = try_stack_->try_index(); const int current_function_level = FunctionLevel(); if (scope->function_level() == current_function_level) { // The block declaring :saved_try_ctx_var variable is the parent of the // pushed try block. *saved_try_ctx = LookupSavedTryContextVar(scope->parent()); *async_saved_try_ctx = LookupAsyncSavedTryContextVar(T, async_temp_scope_, try_index); if ((try_stack_->outer_try() != NULL) && !try_stack_->inside_finally()) { // Collecting the outer try scope is not necessary if we // are in a finally block. scope = try_stack_->outer_try()->try_block()->scope; try_index = try_stack_->outer_try()->try_index(); if (scope->function_level() == current_function_level) { *outer_saved_try_ctx = LookupSavedTryContextVar(scope->parent()); *outer_async_saved_try_ctx = LookupAsyncSavedTryContextVar(T, async_temp_scope_, try_index); } } } } // An async or async* has an implicitly created try-catch around the // function body, so the await or yield inside the async closure should always // be created with a try scope. ASSERT((*saved_try_ctx != NULL) || innermost_function().IsAsyncFunction() || innermost_function().IsAsyncGenerator() || innermost_function().IsSyncGenClosure() || innermost_function().IsSyncGenerator()); } // Build an AST node for static call to Dart function print(str). // Used during debugging to insert print in generated dart code. AstNode* Parser::DartPrint(const char* str) { const Library& lib = Library::Handle(Library::CoreLibrary()); const Function& print_fn = Function::ZoneHandle(Z, lib.LookupFunctionAllowPrivate(Symbols::print())); ASSERT(!print_fn.IsNull()); ArgumentListNode* one_arg = new (Z) ArgumentListNode(TokenPosition::kNoSource); String& msg = String::ZoneHandle(Symbols::NewFormatted(T, "%s", str)); one_arg->Add(new (Z) LiteralNode(TokenPosition::kNoSource, msg)); AstNode* print_call = new (Z) StaticCallNode( TokenPosition::kNoSource, print_fn, one_arg, StaticCallNode::kStatic); return print_call; } AstNode* Parser::ParseAwaitForStatement(String* label_name) { TRACE_PARSER("ParseAwaitForStatement"); ASSERT(IsAwaitKeyword()); const TokenPosition await_for_pos = TokenPos(); ConsumeToken(); // await. ASSERT(CurrentToken() == Token::kFOR); ConsumeToken(); // for. ExpectToken(Token::kLPAREN); if (!innermost_function().IsAsyncFunction() && !innermost_function().IsAsyncClosure() && !innermost_function().IsAsyncGenerator() && !innermost_function().IsAsyncGenClosure()) { ReportError(await_for_pos, "await for loop is only allowed in an asynchronous function"); } // Parse loop variable. bool loop_var_is_final = (CurrentToken() == Token::kFINAL); if (CurrentToken() == Token::kCONST) { ReportError("Loop variable cannot be 'const'"); } bool new_loop_var = false; AbstractType& loop_var_type = AbstractType::ZoneHandle(Z); if (LookaheadToken(1) != Token::kIN) { // Declaration of a new loop variable. // Delay creation of the local variable until we know its actual // position, which is inside the loop body. new_loop_var = true; loop_var_type = ParseConstFinalVarOrType(I->type_checks() ? ClassFinalizer::kCanonicalize : ClassFinalizer::kIgnore); } TokenPosition loop_var_pos = TokenPos(); const String* loop_var_name = ExpectIdentifier("variable name expected"); // Parse stream expression. ExpectToken(Token::kIN); // Open a block for the iterator variable and the try-finally statement // that contains the loop. Ensure that the block starts at a different // token position than the following loop block. Both blocks can allocate // contexts and if they have a matching token position range, // it can be an issue (cf. bug 26941). OpenBlock(); const Block* await_for_block = current_block_; const TokenPosition stream_expr_pos = TokenPos(); AstNode* stream_expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL); ExpectToken(Token::kRPAREN); // Create :stream to store the stream into temporarily. LocalVariable* stream_var = new (Z) LocalVariable(stream_expr_pos, stream_expr_pos, Symbols::ColonStream(), Object::dynamic_type()); current_block_->scope->AddVariable(stream_var); // Store the stream expression into a variable. StoreLocalNode* store_stream_var = new (Z) StoreLocalNode(stream_expr_pos, stream_var, stream_expr); current_block_->statements->Add(store_stream_var); // Register the awaiter on the stream by invoking `_asyncStarListenHelper`. const Library& async_lib = Library::Handle(Library::AsyncLibrary()); const Function& async_star_listen_helper = Function::ZoneHandle( Z, async_lib.LookupFunctionAllowPrivate(Symbols::_AsyncStarListenHelper())); ASSERT(!async_star_listen_helper.IsNull()); LocalVariable* async_op_var = current_block_->scope->LookupVariable(Symbols::AsyncOperation(), false); ASSERT(async_op_var != NULL); ArgumentListNode* async_star_listen_helper_args = new (Z) ArgumentListNode(stream_expr_pos); async_star_listen_helper_args->Add( new (Z) LoadLocalNode(stream_expr_pos, stream_var)); async_star_listen_helper_args->Add( new (Z) LoadLocalNode(stream_expr_pos, async_op_var)); StaticCallNode* async_star_listen_helper_call = new (Z) StaticCallNode(stream_expr_pos, async_star_listen_helper, async_star_listen_helper_args, StaticCallNode::kStatic); current_block_->statements->Add(async_star_listen_helper_call); // Build creation of implicit StreamIterator. // var :for-in-iter = new StreamIterator(stream_expr). const Class& stream_iterator_cls = Class::ZoneHandle(Z, I->object_store()->stream_iterator_class()); ASSERT(!stream_iterator_cls.IsNull()); const Function& iterator_ctor = Function::ZoneHandle( Z, stream_iterator_cls.LookupFunction(Symbols::StreamIteratorConstructor())); ASSERT(!iterator_ctor.IsNull()); ArgumentListNode* ctor_args = new (Z) ArgumentListNode(stream_expr_pos); ctor_args->Add(new (Z) LoadLocalNode(stream_expr_pos, stream_var)); ConstructorCallNode* ctor_call = new (Z) ConstructorCallNode( stream_expr_pos, TypeArguments::ZoneHandle(Z), iterator_ctor, ctor_args); const AbstractType& iterator_type = Object::dynamic_type(); LocalVariable* iterator_var = new (Z) LocalVariable( stream_expr_pos, stream_expr_pos, Symbols::ForInIter(), iterator_type); current_block_->scope->AddVariable(iterator_var); AstNode* iterator_init = new (Z) StoreLocalNode(stream_expr_pos, iterator_var, ctor_call); current_block_->statements->Add(iterator_init); // We need to ensure that the stream is cancelled after the loop. // Thus, wrap the loop in a try-finally that calls :for-in-iter.close() // in the finally clause. It is harmless to call close() if the stream // is already cancelled (when moveNext() returns false). // Note: even though this is async code, we do not need to set up // the closurized saved_exception_var and saved_stack_trace_var because // there can not be a suspend/resume event before the exception is // rethrown in the catch clause. The catch block of the implicit // try-finally is empty. LocalVariable* context_var = NULL; LocalVariable* exception_var = NULL; LocalVariable* stack_trace_var = NULL; LocalVariable* saved_exception_var = NULL; LocalVariable* saved_stack_trace_var = NULL; SetupExceptionVariables(current_block_->scope, false, // Do not create the saved_ vars. &context_var, &exception_var, &stack_trace_var, &saved_exception_var, &saved_stack_trace_var); OpenBlock(); // try block. PushTry(current_block_); SetupSavedTryContext(context_var); // Build while loop condition. // while (await :for-in-iter.moveNext()) LocalVariable* saved_try_ctx; LocalVariable* async_saved_try_ctx; LocalVariable* outer_saved_try_ctx; LocalVariable* outer_async_saved_try_ctx; CheckAsyncOpInTryBlock(&saved_try_ctx, &async_saved_try_ctx, &outer_saved_try_ctx, &outer_async_saved_try_ctx); ArgumentListNode* no_args = new (Z) ArgumentListNode(stream_expr_pos); AstNode* iterator_moveNext = new (Z) InstanceCallNode( stream_expr_pos, new (Z) LoadLocalNode(stream_expr_pos, iterator_var), Symbols::MoveNext(), no_args); OpenBlock(); #if !defined(PRODUCT) // Call '_asyncStarMoveNextHelper' so that the debugger can intercept and // handle single stepping into a async* generator. const Function& async_star_move_next_helper = Function::ZoneHandle( Z, isolate()->object_store()->async_star_move_next_helper()); ASSERT(!async_star_move_next_helper.IsNull()); ArgumentListNode* async_star_move_next_helper_args = new (Z) ArgumentListNode(stream_expr_pos); async_star_move_next_helper_args->Add( new (Z) LoadLocalNode(stream_expr_pos, stream_var)); StaticCallNode* async_star_move_next_helper_call = new (Z) StaticCallNode(stream_expr_pos, async_star_move_next_helper, async_star_move_next_helper_args, StaticCallNode::kStatic); current_block_->statements->Add(async_star_move_next_helper_call); #endif AstNode* await_moveNext = new (Z) AwaitNode( stream_expr_pos, iterator_moveNext, saved_try_ctx, async_saved_try_ctx, outer_saved_try_ctx, outer_async_saved_try_ctx, current_block_->scope); AwaitTransformer at(current_block_->statements, async_temp_scope_); await_moveNext = at.Transform(await_moveNext); SequenceNode* await_preamble = CloseBlock(); // Parse the for loop body. Ideally, we would use ParseNestedStatement() // here, but that does not work well because we have to insert an implicit // variable assignment and potentially a variable declaration in the // loop body. OpenLoopBlock(); SourceLabel* label = SourceLabel::New(await_for_pos, label_name, SourceLabel::kFor); current_block_->scope->AddLabel(label); const TokenPosition loop_var_assignment_pos = TokenPos(); AstNode* iterator_current = new (Z) InstanceGetterNode( loop_var_assignment_pos, new (Z) LoadLocalNode(loop_var_assignment_pos, iterator_var), Symbols::Current()); // Generate assignment of next iterator value to loop variable. AstNode* loop_var_assignment = NULL; if (new_loop_var) { // The for loop variable is new for each iteration. // Create a variable and add it to the loop body scope. // Note that the variable token position needs to be inside the // loop block, so it gets put in the loop context level. LocalVariable* loop_var = new (Z) LocalVariable(loop_var_assignment_pos, loop_var_assignment_pos, *loop_var_name, loop_var_type); if (loop_var_is_final) { loop_var->set_is_final(); } current_block_->scope->AddVariable(loop_var); loop_var_assignment = new (Z) StoreLocalNode(loop_var_assignment_pos, loop_var, iterator_current); } else { AstNode* loop_var_primary = ResolveIdent(loop_var_pos, *loop_var_name, false); ASSERT(!loop_var_primary->IsPrimaryNode()); loop_var_assignment = CreateAssignmentNode(loop_var_primary, iterator_current, loop_var_name, loop_var_assignment_pos); ASSERT(loop_var_assignment != NULL); } current_block_->statements->Add(loop_var_assignment); // Now parse the for-in loop statement or block. if (CurrentToken() == Token::kLBRACE) { ConsumeToken(); ParseStatementSequence(); ExpectToken(Token::kRBRACE); } else { AstNode* statement = ParseStatement(); if (statement != NULL) { current_block_->statements->Add(statement); } } SequenceNode* for_loop_block = CloseBlock(); WhileNode* while_node = new (Z) WhileNode( await_for_pos, label, await_moveNext, await_preamble, for_loop_block); // Add the while loop to the try block. current_block_->statements->Add(while_node); SequenceNode* try_block = CloseBlock(); // Create an empty "catch all" block that rethrows the current // exception and stacktrace. try_stack_->enter_catch(); SequenceNode* catch_block = new (Z) SequenceNode(await_for_pos, NULL); if (outer_saved_try_ctx != NULL) { catch_block->Add(new (Z) StoreLocalNode( TokenPosition::kNoSource, outer_saved_try_ctx, new (Z) LoadLocalNode(TokenPosition::kNoSource, outer_async_saved_try_ctx))); } // We don't need to copy the current exception and stack trace variables // into :saved_exception_var and :saved_stack_trace_var here because there // is no code in the catch clause that could suspend the function. // Rethrow the exception. catch_block->Add(new (Z) ThrowNode( await_for_pos, new (Z) LoadLocalNode(await_for_pos, exception_var), new (Z) LoadLocalNode(await_for_pos, stack_trace_var))); TryStack* try_statement = PopTry(); const intptr_t try_index = try_statement->try_index(); TryStack* outer_try = try_stack_; const intptr_t outer_try_index = (outer_try != NULL) ? outer_try->try_index() : CatchClauseNode::kInvalidTryIndex; // The finally block contains a call to cancel the stream. // :for-in-iter.cancel(); // Inline the finally block to the exit points in the try block. intptr_t node_index = 0; SequenceNode* finally_clause = NULL; if (try_stack_ != NULL) { try_stack_->enter_finally(); } do { OpenBlock(); // Restore the saved try context of the enclosing try block if one // exists. if (outer_saved_try_ctx != NULL) { current_block_->statements->Add(new (Z) StoreLocalNode( TokenPosition::kNoSource, outer_saved_try_ctx, new (Z) LoadLocalNode(TokenPosition::kNoSource, outer_async_saved_try_ctx))); } // :for-in-iter.cancel(); ArgumentListNode* no_args = new (Z) ArgumentListNode(TokenPosition::kNoSource); current_block_->statements->Add(new (Z) InstanceCallNode( ST(await_for_pos), new (Z) LoadLocalNode(TokenPosition::kNoSource, iterator_var), Symbols::Cancel(), no_args)); finally_clause = CloseBlock(); AstNode* node_to_inline = try_statement->GetNodeToInlineFinally(node_index); if (node_to_inline != NULL) { InlinedFinallyNode* node = new (Z) InlinedFinallyNode(TokenPosition::kNoSource, finally_clause, context_var, outer_try_index); finally_clause = NULL; AddFinallyClauseToNode(true, node_to_inline, node); node_index++; } } while (finally_clause == NULL); if (try_stack_ != NULL) { try_stack_->exit_finally(); } // Create the try-statement and add to the current sequence, which is // the block around the loop statement. const Array& handler_types = Array::ZoneHandle(Z, Array::New(1, Heap::kOld)); // Catch block handles all exceptions. handler_types.SetAt(0, Object::dynamic_type()); CatchClauseNode* catch_clause = new (Z) CatchClauseNode( await_for_pos, catch_block, handler_types, context_var, exception_var, stack_trace_var, exception_var, stack_trace_var, AllocateTryIndex(), true); // Needs stack trace. AstNode* try_catch_node = new (Z) TryCatchNode(await_for_pos, try_block, context_var, catch_clause, finally_clause, try_index, finally_clause); ASSERT(current_block_ == await_for_block); await_for_block->statements->Add(try_catch_node); return CloseBlock(); // Implicit block around while loop. } AstNode* Parser::ParseForInStatement(TokenPosition forin_pos, SourceLabel* label) { TRACE_PARSER("ParseForInStatement"); bool loop_var_is_final = (CurrentToken() == Token::kFINAL); if (CurrentToken() == Token::kCONST) { ReportError("Loop variable cannot be 'const'"); } const String* loop_var_name = NULL; TokenPosition loop_var_pos = TokenPosition::kNoSource; bool new_loop_var = false; AbstractType& loop_var_type = AbstractType::ZoneHandle(Z); if (LookaheadToken(1) == Token::kIN) { loop_var_pos = TokenPos(); loop_var_name = ExpectIdentifier("variable name expected"); } else { // The case without a type is handled above, so require a type here. // Delay creation of the local variable until we know its actual // position, which is inside the loop body. new_loop_var = true; loop_var_type = ParseConstFinalVarOrType(I->type_checks() ? ClassFinalizer::kCanonicalize : ClassFinalizer::kIgnore); loop_var_pos = TokenPos(); loop_var_name = ExpectIdentifier("variable name expected"); } ExpectToken(Token::kIN); // Ensure that the block token range contains the call to moveNext and it // also starts the block at a different token position than the following // loop block. Both blocks can allocate contexts and if they have a matching // token position range, it can be an issue (cf. bug 26941). OpenBlock(); // Implicit block around while loop. const TokenPosition collection_pos = TokenPos(); AstNode* collection_expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL); ExpectToken(Token::kRPAREN); // Generate implicit iterator variable and add to scope. // We could set the type of the implicit iterator variable to Iterator // where T is the type of the for loop variable. However, the type error // would refer to the compiler generated iterator and could confuse the user. // It is better to leave the iterator untyped and postpone the type error // until the loop variable is assigned to. const AbstractType& iterator_type = Object::dynamic_type(); LocalVariable* iterator_var = new (Z) LocalVariable( collection_pos, collection_pos, Symbols::ForInIter(), iterator_type); current_block_->scope->AddVariable(iterator_var); // Generate initialization of iterator variable. ArgumentListNode* no_args = new (Z) ArgumentListNode(collection_pos); AstNode* get_iterator = new (Z) InstanceGetterNode(collection_pos, collection_expr, Symbols::Iterator()); AstNode* iterator_init = new (Z) StoreLocalNode(collection_pos, iterator_var, get_iterator); current_block_->statements->Add(iterator_init); // Generate while loop condition. AstNode* iterator_moveNext = new (Z) InstanceCallNode( collection_pos, new (Z) LoadLocalNode(collection_pos, iterator_var), Symbols::MoveNext(), no_args); // Parse the for loop body. Ideally, we would use ParseNestedStatement() // here, but that does not work well because we have to insert an implicit // variable assignment and potentially a variable declaration in the // loop body. OpenLoopBlock(); current_block_->scope->AddLabel(label); const TokenPosition loop_var_assignment_pos = TokenPos(); AstNode* iterator_current = new (Z) InstanceGetterNode( loop_var_assignment_pos, new (Z) LoadLocalNode(loop_var_assignment_pos, iterator_var), Symbols::Current()); // Generate assignment of next iterator value to loop variable. AstNode* loop_var_assignment = NULL; if (new_loop_var) { // The for loop variable is new for each iteration. // Create a variable and add it to the loop body scope. LocalVariable* loop_var = new (Z) LocalVariable( loop_var_pos, loop_var_assignment_pos, *loop_var_name, loop_var_type); if (loop_var_is_final) { loop_var->set_is_final(); } current_block_->scope->AddVariable(loop_var); loop_var_assignment = new (Z) StoreLocalNode(loop_var_assignment_pos, loop_var, iterator_current); } else { AstNode* loop_var_primary = ResolveIdent(loop_var_pos, *loop_var_name, false); ASSERT(!loop_var_primary->IsPrimaryNode()); loop_var_assignment = CreateAssignmentNode(loop_var_primary, iterator_current, loop_var_name, loop_var_assignment_pos); ASSERT(loop_var_assignment != NULL); } current_block_->statements->Add(loop_var_assignment); // Now parse the for-in loop statement or block. if (CurrentToken() == Token::kLBRACE) { ConsumeToken(); ParseStatementSequence(); ExpectToken(Token::kRBRACE); } else { AstNode* statement = ParseStatement(); if (statement != NULL) { current_block_->statements->Add(statement); } } SequenceNode* for_loop_statement = CloseBlock(); AstNode* while_statement = new (Z) WhileNode(forin_pos, label, iterator_moveNext, NULL, for_loop_statement); current_block_->statements->Add(while_statement); return CloseBlock(); // Implicit block around while loop. } AstNode* Parser::ParseForStatement(String* label_name) { TRACE_PARSER("ParseForStatement"); const TokenPosition for_pos = TokenPos(); ConsumeToken(); ExpectToken(Token::kLPAREN); SourceLabel* label = SourceLabel::New(for_pos, label_name, SourceLabel::kFor); if (IsForInStatement()) { return ParseForInStatement(for_pos, label); } // Open a block that contains the loop variable. Make it a loop block so // that we allocate a new context if the loop variable is captured. OpenLoopBlock(); AstNode* initializer = NULL; const TokenPosition init_pos = TokenPos(); LocalScope* init_scope = current_block_->scope; if (CurrentToken() != Token::kSEMICOLON) { if (IsVariableDeclaration()) { initializer = ParseVariableDeclarationList(); } else { initializer = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL); } } ExpectSemicolon(); AstNode* condition = NULL; SequenceNode* condition_preamble = NULL; if (CurrentToken() != Token::kSEMICOLON) { condition = ParseAwaitableExpr(kAllowConst, kConsumeCascades, &condition_preamble); } ExpectSemicolon(); AstNode* increment = NULL; const TokenPosition incr_pos = TokenPos(); if (CurrentToken() != Token::kRPAREN) { increment = ParseAwaitableExprList(); } ExpectToken(Token::kRPAREN); const bool parsing_loop_body = true; SequenceNode* body = ParseNestedStatement(parsing_loop_body, label); // Check whether any of the variables in the initializer part of // the for statement are captured by a closure. If so, we insert a // node that creates a new Context for the loop variable before // the increment expression is evaluated. for (int i = 0; i < init_scope->num_variables(); i++) { if (init_scope->VariableAt(i)->is_captured() && (init_scope->VariableAt(i)->owner() == init_scope)) { SequenceNode* incr_sequence = new (Z) SequenceNode(incr_pos, NULL); incr_sequence->Add(new (Z) CloneContextNode(for_pos, init_scope)); if (increment != NULL) { incr_sequence->Add(increment); } increment = incr_sequence; break; } } AstNode* for_node = new (Z) ForNode(for_pos, label, NodeAsSequenceNode(init_pos, initializer, NULL), condition, condition_preamble, NodeAsSequenceNode(incr_pos, increment, NULL), body); current_block_->statements->Add(for_node); return CloseBlock(); } // Calling VM-internal helpers, uses implementation core library. AstNode* Parser::MakeStaticCall(const String& cls_name, const String& func_name, ArgumentListNode* arguments) { const Class& cls = Class::Handle(Z, Library::LookupCoreClass(cls_name)); ASSERT(!cls.IsNull()); const intptr_t kTypeArgsLen = 0; // Not passing type args to generic func. const Function& func = Function::ZoneHandle( Z, Resolver::ResolveStatic(cls, func_name, kTypeArgsLen, arguments->length(), arguments->names())); ASSERT(!func.IsNull()); return new (Z) StaticCallNode(arguments->token_pos(), func, arguments, StaticCallNode::kStatic); } AstNode* Parser::ParseAssertStatement(bool is_const) { TRACE_PARSER("ParseAssertStatement"); ConsumeToken(); // Consume assert keyword. ExpectToken(Token::kLPAREN); const TokenPosition condition_pos = TokenPos(); if (!I->asserts()) { SkipExpr(); if (CurrentToken() == Token::kCOMMA) { ConsumeToken(); if (CurrentToken() != Token::kRPAREN) { SkipExpr(); if (CurrentToken() == Token::kCOMMA) { // Allow trailing comma. ConsumeToken(); } } } ExpectToken(Token::kRPAREN); return NULL; } BoolScope saved_seen_await(&parsed_function()->have_seen_await_expr_, false); AstNode* condition = ParseExpr(kAllowConst, kConsumeCascades); if (is_const && !condition->IsPotentiallyConst()) { ReportError(condition_pos, "initializer assert expression must be compile time constant."); } const TokenPosition condition_end = TokenPos(); AstNode* message = NULL; TokenPosition message_pos = TokenPosition::kNoSource; if (CurrentToken() == Token::kCOMMA) { ConsumeToken(); if (CurrentToken() != Token::kRPAREN) { message_pos = TokenPos(); message = ParseExpr(kAllowConst, kConsumeCascades); if (is_const && !message->IsPotentiallyConst()) { ReportError( message_pos, "initializer assert expression must be compile time constant."); } if (CurrentToken() == Token::kCOMMA) { // Allow trailing comma. ConsumeToken(); } } } ExpectToken(Token::kRPAREN); if (!is_const) { // Check for assertion condition being a function if not const. ArgumentListNode* arguments = new (Z) ArgumentListNode(condition_pos); arguments->Add(condition); condition = MakeStaticCall( Symbols::AssertionError(), Library::PrivateCoreLibName(Symbols::EvaluateAssertion()), arguments); } AstNode* not_condition = new (Z) UnaryOpNode(condition_pos, Token::kNOT, condition); // Build call to _AsertionError._throwNew(start, end, message) ArgumentListNode* arguments = new (Z) ArgumentListNode(condition_pos); arguments->Add(new (Z) LiteralNode( condition_pos, Integer::ZoneHandle(Z, Integer::New(condition_pos.Pos())))); arguments->Add(new (Z) LiteralNode( condition_end, Integer::ZoneHandle(Z, Integer::New(condition_end.Pos())))); if (message == NULL) { message = new (Z) LiteralNode(condition_end, Instance::ZoneHandle(Z)); } arguments->Add(message); AstNode* assert_throw = MakeStaticCall( Symbols::AssertionError(), Library::PrivateCoreLibName(Symbols::ThrowNew()), arguments); AstNode* assertion_check = NULL; if (parsed_function()->have_seen_await()) { // The await transformation must be done manually because assertions // are parsed as statements, not expressions. Thus, we need to check // explicitly whether the arguments contain await operators. (Note that // we must not parse the arguments with ParseAwaitableExpr(). In the // corner case of assert(await a, await b), this would create two // sibling scopes containing the temporary values for a and b. Both // values would be allocated in the same internal context variable.) // // Build !condition ? _AsertionError._throwNew(...) : null; // We need to use a conditional expression because the await transformer // cannot transform if statements. assertion_check = new (Z) ConditionalExprNode( condition_pos, not_condition, assert_throw, new (Z) LiteralNode(condition_pos, Object::null_instance())); OpenBlock(); AwaitTransformer at(current_block_->statements, async_temp_scope_); AstNode* transformed_assertion = at.Transform(assertion_check); SequenceNode* preamble = CloseBlock(); preamble->Add(transformed_assertion); assertion_check = preamble; } else { // Build if (!condition) _AsertionError._throwNew(...) assertion_check = new (Z) IfNode(condition_pos, not_condition, NodeAsSequenceNode(condition_pos, assert_throw, NULL), NULL); } return assertion_check; } // Populate local scope of the catch block with the catch parameters. void Parser::AddCatchParamsToScope(CatchParamDesc* exception_param, CatchParamDesc* stack_trace_param, LocalScope* scope) { if (exception_param->name != NULL) { LocalVariable* var = new (Z) LocalVariable(exception_param->token_pos, exception_param->token_pos, *exception_param->name, *exception_param->type); var->set_is_final(); bool added_to_scope = scope->AddVariable(var); ASSERT(added_to_scope); exception_param->var = var; } if (stack_trace_param->name != NULL) { LocalVariable* var = new (Z) LocalVariable( stack_trace_param->token_pos, stack_trace_param->token_pos, *stack_trace_param->name, *stack_trace_param->type); var->set_is_final(); bool added_to_scope = scope->AddVariable(var); if (!added_to_scope) { // The name of the exception param is reused for the stack trace param. ReportError(stack_trace_param->token_pos, "name '%s' already exists in scope", stack_trace_param->name->ToCString()); } stack_trace_param->var = var; } } // Generate code to load the exception object (:exception_var) into // the saved exception variable (:saved_exception_var) used to rethrow. // Generate code to load the stack trace object (:stack_trace_var) into // the saved stacktrace variable (:saved_stack_trace_var) used to rethrow. void Parser::SaveExceptionAndStackTrace(SequenceNode* statements, LocalVariable* exception_var, LocalVariable* stack_trace_var, LocalVariable* saved_exception_var, LocalVariable* saved_stack_trace_var) { ASSERT(innermost_function().IsAsyncClosure() || innermost_function().IsAsyncFunction() || innermost_function().IsSyncGenClosure() || innermost_function().IsSyncGenerator() || innermost_function().IsAsyncGenClosure() || innermost_function().IsAsyncGenerator()); ASSERT(saved_exception_var != NULL); ASSERT(exception_var != NULL); statements->Add(new (Z) StoreLocalNode( TokenPosition::kNoSource, saved_exception_var, new (Z) LoadLocalNode(TokenPosition::kNoSource, exception_var))); ASSERT(saved_stack_trace_var != NULL); ASSERT(stack_trace_var != NULL); statements->Add(new (Z) StoreLocalNode( TokenPosition::kNoSource, saved_stack_trace_var, new (Z) LoadLocalNode(TokenPosition::kNoSource, stack_trace_var))); } SequenceNode* Parser::EnsureFinallyClause( bool parse, bool is_async, LocalVariable* exception_var, LocalVariable* stack_trace_var, LocalVariable* rethrow_exception_var, LocalVariable* rethrow_stack_trace_var) { TRACE_PARSER("EnsureFinallyClause"); ASSERT(parse || (is_async && (try_stack_ != NULL))); // Increasing the loop level prevents the reuse of a parent context and forces // the allocation of a local context to hold captured variables declared // inside the finally clause. Otherwise, a captured variable gets allocated at // different slots in the parent context each time the finally clause is // reparsed, which is done to duplicate the ast. Since only one closure is // kept due to canonicalization, it will access the correct slot in only one // copy of the finally clause and the wrong slot in all others. By allocating // a local context, all copies use the same slot in different local contexts. // See issue #26948. This is a temporary fix until we eliminate reparsing. OpenLoopBlock(); if (parse) { ExpectToken(Token::kLBRACE); } if (try_stack_ != NULL) { try_stack_->enter_finally(); } // In case of async closures we need to restore the saved try context of an // outer try block (if it exists). The current try block has already been // removed from the stack of try blocks. if (is_async) { if (try_stack_ != NULL) { LocalScope* scope = try_stack_->try_block()->scope; if (scope->function_level() == current_block_->scope->function_level()) { LocalVariable* saved_try_ctx = LookupSavedTryContextVar(scope->parent()); LocalVariable* async_saved_try_ctx = LookupAsyncSavedTryContextVar( T, async_temp_scope_, try_stack_->try_index()); current_block_->statements->Add(new (Z) StoreLocalNode( TokenPosition::kNoSource, saved_try_ctx, new (Z) LoadLocalNode(TokenPosition::kNoSource, async_saved_try_ctx))); } } // We need to save the exception variables as in catch clauses, whether // there is an outer try or not. Note that this is only necessary if the // finally clause contains an await or yield. // TODO(hausner): Optimize. SaveExceptionAndStackTrace(current_block_->statements, exception_var, stack_trace_var, rethrow_exception_var, rethrow_stack_trace_var); } if (parse) { ParseStatementSequence(); ExpectToken(Token::kRBRACE); } SequenceNode* finally_clause = CloseBlock(); if (try_stack_ != NULL) { try_stack_->exit_finally(); } return finally_clause; } void Parser::PushTry(Block* try_block) { intptr_t try_index = AllocateTryIndex(); try_stack_ = new (Z) TryStack(try_block, try_stack_, try_index); } Parser::TryStack* Parser::PopTry() { TryStack* innermost_try = try_stack_; try_stack_ = try_stack_->outer_try(); return innermost_try; } void Parser::AddNodeForFinallyInlining(AstNode* node) { if (node == NULL) { return; } ASSERT(node->IsReturnNode() || node->IsJumpNode()); const intptr_t func_level = FunctionLevel(); TryStack* iterator = try_stack_; while ((iterator != NULL) && (iterator->try_block()->scope->function_level() == func_level)) { // For continue and break node check if the target label is in scope. if (node->IsJumpNode()) { SourceLabel* label = node->AsJumpNode()->label(); ASSERT(label != NULL); LocalScope* try_scope = iterator->try_block()->scope; // If the label is defined in a scope which is a child (nested scope) // of the try scope then we are not breaking out of this try block // so we do not need to inline the finally code. Otherwise we need // to inline the finally code of this try block and then move on to the // next outer try block. // For unresolved forward jumps to switch cases, we don't yet know // to which scope the label will be resolved. Tentatively add the // jump to all nested try statements and remove the outermost ones // when we know the exact jump target. (See // RemoveNodesForFinallyInlining below.) if (!label->IsUnresolved() && label->owner()->IsNestedWithin(try_scope)) { break; } } iterator->AddNodeForFinallyInlining(node); iterator = iterator->outer_try(); } } void Parser::RemoveNodesForFinallyInlining(SourceLabel* label) { TryStack* iterator = try_stack_; const intptr_t func_level = FunctionLevel(); while ((iterator != NULL) && (iterator->try_block()->scope->function_level() == func_level)) { iterator->RemoveJumpToLabel(label); iterator = iterator->outer_try(); } } // Add the inlined finally clause to the specified node. void Parser::AddFinallyClauseToNode(bool is_async, AstNode* node, InlinedFinallyNode* finally_clause) { ReturnNode* return_node = node->AsReturnNode(); if (return_node != NULL) { if (FunctionLevel() == 0) { parsed_function()->EnsureFinallyReturnTemp(is_async); } return_node->AddInlinedFinallyNode(finally_clause); return; } JumpNode* jump_node = node->AsJumpNode(); ASSERT(jump_node != NULL); jump_node->AddInlinedFinallyNode(finally_clause); } SequenceNode* Parser::ParseCatchClauses( TokenPosition handler_pos, bool is_async, LocalVariable* exception_var, LocalVariable* stack_trace_var, LocalVariable* rethrow_exception_var, LocalVariable* rethrow_stack_trace_var, const GrowableObjectArray& handler_types, bool* needs_stack_trace) { // All catch blocks are merged into an if-then-else sequence of the // different types specified using the 'is' operator. While parsing // record the type tests (either a ComparisonNode or else the LiteralNode // true for a generic catch) and the catch bodies in a pair of parallel // lists. Afterward, construct the nested if-then-else. bool generic_catch_seen = false; GrowableArray type_tests; GrowableArray catch_blocks; while ((CurrentToken() == Token::kCATCH) || IsSymbol(Symbols::On())) { // Open a block that contains the if or an unconditional body. It's // closed in the loop that builds the if-then-else nest. OpenBlock(); const TokenPosition catch_pos = TokenPos(); CatchParamDesc exception_param; CatchParamDesc stack_trace_param; if (IsSymbol(Symbols::On())) { ConsumeToken(); exception_param.type = &AbstractType::ZoneHandle( Z, ParseTypeOrFunctionType(false, ClassFinalizer::kCanonicalize)); } else { exception_param.type = &Object::dynamic_type(); } if (CurrentToken() == Token::kCATCH) { ConsumeToken(); // Consume the 'catch'. ExpectToken(Token::kLPAREN); exception_param.token_pos = TokenPos(); exception_param.name = ExpectIdentifier("identifier expected"); if (CurrentToken() == Token::kCOMMA) { ConsumeToken(); stack_trace_param.type = &Object::dynamic_type(); stack_trace_param.token_pos = TokenPos(); stack_trace_param.name = ExpectIdentifier("identifier expected"); } ExpectToken(Token::kRPAREN); } // Create a block containing the catch clause parameters and the // following code: // 1) Store exception object and stack trace object into user-defined // variables (as needed). // 2) In async code, save exception object and stack trace object into // captured :saved_exception_var and :saved_stack_trace_var. // 3) Nested block with source code from catch clause block. OpenBlock(); AddCatchParamsToScope(&exception_param, &stack_trace_param, current_block_->scope); if (exception_param.var != NULL) { // Generate code to load the exception object (:exception_var) into // the exception variable specified in this block. ASSERT(exception_var != NULL); current_block_->statements->Add(new (Z) StoreLocalNode( catch_pos, exception_param.var, new (Z) LoadLocalNode(catch_pos, exception_var))); } if (stack_trace_param.var != NULL) { // A stack trace variable is specified in this block, so generate code // to load the stack trace object (:stack_trace_var) into the stack // trace variable specified in this block. *needs_stack_trace = true; ASSERT(stack_trace_var != NULL); current_block_->statements->Add(new (Z) StoreLocalNode( catch_pos, stack_trace_param.var, new (Z) LoadLocalNode(catch_pos, stack_trace_var))); } // Add nested block with user-defined code. This block allows // declarations in the body to shadow the catch parameters. CheckToken(Token::kLBRACE); current_block_->statements->Add(ParseNestedStatement(false, NULL)); catch_blocks.Add(CloseBlock()); const bool is_bad_type = exception_param.type->IsMalformed() || exception_param.type->IsMalbounded(); if (exception_param.type->IsDynamicType() || is_bad_type) { // There is no exception type or else it is malformed or malbounded. // In the first case, unconditionally execute the catch body. In the // second case, unconditionally throw. generic_catch_seen = true; type_tests.Add(new (Z) LiteralNode(catch_pos, Bool::True())); if (is_bad_type) { // Replace the body with one that throws. SequenceNode* block = new (Z) SequenceNode(catch_pos, NULL); block->Add(ThrowTypeError(catch_pos, *exception_param.type)); catch_blocks.Last() = block; } // This catch clause will handle all exceptions. We can safely forget // all previous catch clause types. handler_types.SetLength(0); handler_types.Add(*exception_param.type, Heap::kOld); } else { // Has a type specification that is not malformed or malbounded. Now // form an 'if type check' to guard the catch handler code. if (!exception_param.type->IsInstantiated() && (FunctionLevel() > 0)) { // Make sure that the instantiators are captured. CaptureAllInstantiators(); } TypeNode* exception_type = new (Z) TypeNode(catch_pos, *exception_param.type); AstNode* exception_value = new (Z) LoadLocalNode(catch_pos, exception_var); if (!exception_type->type().IsInstantiated()) { EnsureExpressionTemp(); } type_tests.Add(new (Z) ComparisonNode(catch_pos, Token::kIS, exception_value, exception_type)); // Do not add uninstantiated types (e.g. type parameter T or generic // type List), since the debugger won't be able to instantiate it // when walking the stack. // // This means that the debugger is not able to determine whether an // exception is caught if the catch clause uses generic types. It // will report the exception as uncaught when in fact it might be // caught and handled when we unwind the stack. if (!generic_catch_seen && exception_param.type->IsInstantiated()) { handler_types.Add(*exception_param.type, Heap::kOld); } } ASSERT(type_tests.length() == catch_blocks.length()); } // Build the if/then/else nest from the inside out. Keep the AST simple // for the case of a single generic catch clause. The initial value of // current is the last (innermost) else block if there were any catch // clauses. SequenceNode* current = NULL; if (!generic_catch_seen) { // There isn't a generic catch clause so create a clause body that // rethrows the exception. This includes the case that there were no // catch clauses. // An await cannot possibly be executed in between the catch entry and here, // therefore, it is safe to rethrow the stack-based :exception_var instead // of the captured copy :saved_exception_var. current = new (Z) SequenceNode(handler_pos, NULL); current->Add(new (Z) ThrowNode( handler_pos, new (Z) LoadLocalNode(handler_pos, exception_var), new (Z) LoadLocalNode(handler_pos, stack_trace_var))); } else if (type_tests.Last()->IsLiteralNode()) { ASSERT(type_tests.Last()->AsLiteralNode()->literal().raw() == Bool::True().raw()); // The last body is entered unconditionally. Start building the // if/then/else nest with that body as the innermost else block. // Note that it is nested inside an extra block which we opened // before we knew the body was entered unconditionally. type_tests.RemoveLast(); current_block_->statements->Add(catch_blocks.RemoveLast()); current = CloseBlock(); } // If the last body was entered conditionally and there is no need to add // a rethrow, use an empty else body (current = NULL above). while (!type_tests.is_empty()) { AstNode* type_test = type_tests.RemoveLast(); SequenceNode* catch_block = catch_blocks.RemoveLast(); current_block_->statements->Add(new (Z) IfNode( type_test->token_pos(), type_test, catch_block, current)); current = CloseBlock(); } // In case of async closures, restore :saved_try_context_var before executing // the catch clauses. if (is_async && (current != NULL)) { ASSERT(try_stack_ != NULL); SequenceNode* async_code = new (Z) SequenceNode(handler_pos, NULL); const TryStack* try_block = try_stack_->outer_try(); if (try_block != NULL) { LocalScope* scope = try_block->try_block()->scope; if (scope->function_level() == current_block_->scope->function_level()) { LocalVariable* saved_try_ctx = LookupSavedTryContextVar(scope->parent()); LocalVariable* async_saved_try_ctx = LookupAsyncSavedTryContextVar( T, async_temp_scope_, try_block->try_index()); async_code->Add(new (Z) StoreLocalNode( TokenPosition::kNoSource, saved_try_ctx, new (Z) LoadLocalNode(TokenPosition::kNoSource, async_saved_try_ctx))); } } SaveExceptionAndStackTrace(async_code, exception_var, stack_trace_var, rethrow_exception_var, rethrow_stack_trace_var); // The async_code node sequence contains code to restore the context (if // an outer try block is present) and code to save the exception and // stack trace variables. // This async code is inserted before the current node sequence containing // the chain of if/then/else handling all catch clauses. async_code->Add(current); current = async_code; } return current; } void Parser::SetupSavedTryContext(LocalVariable* saved_try_context) { const String& async_saved_try_ctx_name = String::ZoneHandle( Z, Symbols::NewFormatted(T, "%s%d", Symbols::AsyncSavedTryCtxVarPrefix().ToCString(), last_used_try_index_ - 1)); LocalVariable* async_saved_try_ctx = new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, async_saved_try_ctx_name, Object::dynamic_type()); ASSERT(async_temp_scope_ != NULL); async_temp_scope_->AddVariable(async_saved_try_ctx); ASSERT(saved_try_context != NULL); current_block_->statements->Add(new (Z) StoreLocalNode( TokenPosition::kNoSource, async_saved_try_ctx, new (Z) LoadLocalNode(TokenPosition::kNoSource, saved_try_context))); } // We create three variables for exceptions: // ':saved_try_context_var' - Used to save the context before the start of // the try block. The context register is // restored from this variable before // processing the catch block handler. // ':exception_var' - Used to save the current exception object that was // thrown. // ':stack_trace_var' - Used to save the current stack trace object which // the stack trace was copied into when an exception // was thrown. // :exception_var and :stack_trace_var get set with the exception object // and the stack trace object when an exception is thrown. These three // implicit variables can never be captured. // // In case of async code, we create two additional variables: // ':saved_exception_var' - Used to capture the exception object above. // ':saved_stack_trace_var' - Used to capture the stack trace object above. void Parser::SetupExceptionVariables(LocalScope* try_scope, bool is_async, LocalVariable** context_var, LocalVariable** exception_var, LocalVariable** stack_trace_var, LocalVariable** saved_exception_var, LocalVariable** saved_stack_trace_var) { // Consecutive try statements share the same set of variables. *context_var = try_scope->LocalLookupVariable(Symbols::SavedTryContextVar()); if (*context_var == NULL) { *context_var = new (Z) LocalVariable(TokenPos(), TokenPos(), Symbols::SavedTryContextVar(), Object::dynamic_type()); try_scope->AddVariable(*context_var); } *exception_var = try_scope->LocalLookupVariable(Symbols::ExceptionVar()); if (*exception_var == NULL) { *exception_var = new (Z) LocalVariable(TokenPos(), TokenPos(), Symbols::ExceptionVar(), Object::dynamic_type()); try_scope->AddVariable(*exception_var); } *stack_trace_var = try_scope->LocalLookupVariable(Symbols::StackTraceVar()); if (*stack_trace_var == NULL) { *stack_trace_var = new (Z) LocalVariable(TokenPos(), TokenPos(), Symbols::StackTraceVar(), Object::dynamic_type()); try_scope->AddVariable(*stack_trace_var); } if (is_async) { *saved_exception_var = try_scope->LocalLookupVariable(Symbols::SavedExceptionVar()); if (*saved_exception_var == NULL) { *saved_exception_var = new (Z) LocalVariable(TokenPos(), TokenPos(), Symbols::SavedExceptionVar(), Object::dynamic_type()); try_scope->AddVariable(*saved_exception_var); } *saved_stack_trace_var = try_scope->LocalLookupVariable(Symbols::SavedStackTraceVar()); if (*saved_stack_trace_var == NULL) { *saved_stack_trace_var = new (Z) LocalVariable(TokenPos(), TokenPos(), Symbols::SavedStackTraceVar(), Object::dynamic_type()); try_scope->AddVariable(*saved_stack_trace_var); } } } AstNode* Parser::ParseTryStatement(String* label_name) { TRACE_PARSER("ParseTryStatement"); const TokenPosition try_pos = TokenPos(); SourceLabel* try_label = NULL; if (label_name != NULL) { try_label = SourceLabel::New(try_pos, label_name, SourceLabel::kStatement); OpenBlock(); current_block_->scope->AddLabel(try_label); } const bool is_async = innermost_function().IsAsyncClosure() || innermost_function().IsAsyncFunction() || innermost_function().IsSyncGenClosure() || innermost_function().IsSyncGenerator() || innermost_function().IsAsyncGenClosure() || innermost_function().IsAsyncGenerator(); LocalVariable* context_var = NULL; LocalVariable* exception_var = NULL; LocalVariable* stack_trace_var = NULL; LocalVariable* saved_exception_var = NULL; LocalVariable* saved_stack_trace_var = NULL; SetupExceptionVariables(current_block_->scope, is_async, &context_var, &exception_var, &stack_trace_var, &saved_exception_var, &saved_stack_trace_var); ConsumeToken(); // Consume the 'try'. // Now parse the 'try' block. OpenBlock(); PushTry(current_block_); ExpectToken(Token::kLBRACE); if (is_async) { SetupSavedTryContext(context_var); } ParseStatementSequence(); ExpectToken(Token::kRBRACE); SequenceNode* try_block = CloseBlock(); if ((CurrentToken() != Token::kCATCH) && !IsSymbol(Symbols::On()) && (CurrentToken() != Token::kFINALLY)) { ReportError("catch or finally clause expected"); } // Now parse the 'catch' blocks if any. try_stack_->enter_catch(); const TokenPosition handler_pos = TokenPos(); const GrowableObjectArray& handler_types = GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld)); bool needs_stack_trace = false; SequenceNode* catch_handler_list = ParseCatchClauses(handler_pos, is_async, exception_var, stack_trace_var, is_async ? saved_exception_var : exception_var, is_async ? saved_stack_trace_var : stack_trace_var, handler_types, &needs_stack_trace); TryStack* try_statement = PopTry(); const intptr_t try_index = try_statement->try_index(); TryStack* outer_try = try_stack_; const intptr_t outer_try_index = (outer_try != NULL) ? outer_try->try_index() : CatchClauseNode::kInvalidTryIndex; // Finally, parse or generate the 'finally' clause. // A finally clause is required in async code to restore the saved try context // of an existing outer try. Generate a finally clause to this purpose if it // is not declared. SequenceNode* finally_clause = NULL; SequenceNode* rethrow_clause = NULL; const bool parse = CurrentToken() == Token::kFINALLY; if (parse || (is_async && (try_stack_ != NULL))) { if (parse) { ConsumeToken(); // Consume the 'finally'. } const TokenPosition finally_pos = TokenPos(); // Add the finally block to the exit points recorded so far. intptr_t node_index = 0; AstNode* node_to_inline = try_statement->GetNodeToInlineFinally(node_index); while (node_to_inline != NULL) { finally_clause = EnsureFinallyClause( parse, is_async, exception_var, stack_trace_var, is_async ? saved_exception_var : exception_var, is_async ? saved_stack_trace_var : stack_trace_var); InlinedFinallyNode* node = new (Z) InlinedFinallyNode( finally_pos, finally_clause, context_var, outer_try_index); AddFinallyClauseToNode(is_async, node_to_inline, node); node_index += 1; node_to_inline = try_statement->GetNodeToInlineFinally(node_index); tokens_iterator_.SetCurrentPosition(finally_pos); } finally_clause = EnsureFinallyClause(parse, is_async, exception_var, stack_trace_var, is_async ? saved_exception_var : exception_var, is_async ? saved_stack_trace_var : stack_trace_var); if (finally_clause != NULL) { // Re-parse to create a duplicate of finally clause to avoid unintended // sharing of try-indices if the finally-block contains a try-catch. // The flow graph builder emits two copies of the finally-block if the // try-block has a normal exit: one for the exception- and one for the // non-exception case (see EffectGraphVisitor::VisitTryCatchNode) tokens_iterator_.SetCurrentPosition(finally_pos); rethrow_clause = EnsureFinallyClause( parse, is_async, exception_var, stack_trace_var, is_async ? saved_exception_var : exception_var, is_async ? saved_stack_trace_var : stack_trace_var); } } CatchClauseNode* catch_clause = new (Z) CatchClauseNode( handler_pos, catch_handler_list, Array::ZoneHandle(Z, Array::MakeFixedLength(handler_types)), context_var, exception_var, stack_trace_var, is_async ? saved_exception_var : exception_var, is_async ? saved_stack_trace_var : stack_trace_var, (finally_clause != NULL) ? AllocateTryIndex() : CatchClauseNode::kInvalidTryIndex, needs_stack_trace); // Now create the try/catch ast node and return it. If there is a label // on the try/catch, close the block that's embedding the try statement // and attach the label to it. AstNode* try_catch_node = new (Z) TryCatchNode(try_pos, try_block, context_var, catch_clause, finally_clause, try_index, rethrow_clause); if (try_label != NULL) { current_block_->statements->Add(try_catch_node); SequenceNode* sequence = CloseBlock(); sequence->set_label(try_label); try_catch_node = sequence; } return try_catch_node; } AstNode* Parser::ParseJump(String* label_name) { TRACE_PARSER("ParseJump"); ASSERT(CurrentToken() == Token::kBREAK || CurrentToken() == Token::kCONTINUE); Token::Kind jump_kind = CurrentToken(); const TokenPosition jump_pos = TokenPos(); SourceLabel* target = NULL; ConsumeToken(); if (IsIdentifier()) { // Explicit label after break/continue. const String& target_name = *CurrentLiteral(); ConsumeToken(); // Handle pathological cases first. if (label_name != NULL && target_name.Equals(*label_name)) { if (jump_kind == Token::kCONTINUE) { ReportError(jump_pos, "'continue' jump to label '%s' is illegal", target_name.ToCString()); } // L: break L; is a no-op. return NULL; } target = current_block_->scope->LookupLabel(target_name); if (target == NULL && jump_kind == Token::kCONTINUE) { // Either a reference to a non-existent label, or a forward reference // to a case label that we haven't seen yet. If we are inside a switch // statement, create a "forward reference" label in the scope of // the switch statement. LocalScope* switch_scope = current_block_->scope->LookupSwitchScope(); if (switch_scope != NULL) { // We found a switch scope. Enter a forward reference to the label. target = new (Z) SourceLabel(TokenPos(), target_name, SourceLabel::kForward); switch_scope->AddLabel(target); } } if (target == NULL) { ReportError(jump_pos, "label '%s' not found", target_name.ToCString()); } } else if (FLAG_enable_debug_break && (CurrentToken() == Token::kSTRING)) { const char* message = Z->MakeCopyOfString(CurrentLiteral()->ToCString()); ConsumeToken(); return new (Z) StopNode(jump_pos, message); } else { target = current_block_->scope->LookupInnermostLabel(jump_kind); if (target == NULL) { ReportError(jump_pos, "'%s' is illegal here", Token::Str(jump_kind)); } } ASSERT(target != NULL); if (jump_kind == Token::kCONTINUE) { if (target->kind() == SourceLabel::kSwitch) { ReportError(jump_pos, "'continue' jump to switch statement is illegal"); } else if (target->kind() == SourceLabel::kStatement) { ReportError(jump_pos, "'continue' jump to label '%s' is illegal", target->name().ToCString()); } } if (jump_kind == Token::kBREAK && target->kind() == SourceLabel::kCase) { ReportError(jump_pos, "'break' to case clause label is illegal"); } if (target->FunctionLevel() != FunctionLevel()) { ReportError(jump_pos, "'%s' target must be in same function context", Token::Str(jump_kind)); } return new (Z) JumpNode(jump_pos, jump_kind, target); } AstNode* Parser::ParseYieldStatement() { bool is_yield_each = false; const TokenPosition yield_pos = TokenPos(); ConsumeToken(); // yield reserved word. if (CurrentToken() == Token::kMUL) { is_yield_each = true; ConsumeToken(); } if (!innermost_function().IsGenerator() && !innermost_function().IsGeneratorClosure()) { ReportError(yield_pos, "yield%s statement only allowed in generator functions", is_yield_each ? "*" : ""); } AstNode* expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL); LetNode* yield = new (Z) LetNode(yield_pos); if (innermost_function().IsSyncGenerator() || innermost_function().IsSyncGenClosure()) { // Yield statement in sync* function. LocalVariable* iterator_param = LookupLocalScope(Symbols::IteratorParameter()); ASSERT(iterator_param != NULL); AstNode* iterator = new (Z) LoadLocalNode(TokenPosition::kNoSource, iterator_param); if (is_yield_each) { // Generate :iterator._yieldEachIterable = expr; yield->AddNode(new (Z) InstanceSetterNode( TokenPosition::kNoSource, iterator, Library::PrivateCoreLibName(Symbols::_yieldEachIterable()), expr)); } else { // Generate :iterator._current = expr; yield->AddNode(new (Z) InstanceSetterNode( TokenPosition::kNoSource, iterator, Library::PrivateCoreLibName(Symbols::_current()), expr)); } AwaitMarkerNode* await_marker = new (Z) AwaitMarkerNode( async_temp_scope_, current_block_->scope, TokenPosition::kNoSource); yield->AddNode(await_marker); // Return true to indicate that a value has been generated. ReturnNode* return_true = new (Z) ReturnNode(yield_pos, new (Z) LiteralNode(TokenPos(), Bool::True())); return_true->set_return_type(ReturnNode::kContinuationTarget); yield->AddNode(return_true); // If this expression is part of a try block, also append the code for // restoring the saved try context that lives on the stack and possibly the // saved try context of the outer try block. LocalVariable* saved_try_ctx; LocalVariable* async_saved_try_ctx; LocalVariable* outer_saved_try_ctx; LocalVariable* outer_async_saved_try_ctx; CheckAsyncOpInTryBlock(&saved_try_ctx, &async_saved_try_ctx, &outer_saved_try_ctx, &outer_async_saved_try_ctx); if (saved_try_ctx != NULL) { yield->AddNode(new (Z) StoreLocalNode( TokenPosition::kNoSource, saved_try_ctx, new (Z) LoadLocalNode(TokenPosition::kNoSource, async_saved_try_ctx))); if (outer_saved_try_ctx != NULL) { yield->AddNode(new (Z) StoreLocalNode( TokenPosition::kNoSource, outer_saved_try_ctx, new (Z) LoadLocalNode(TokenPosition::kNoSource, outer_async_saved_try_ctx))); } } else { ASSERT(outer_saved_try_ctx == NULL); } } else { // yield statement in async* function. ASSERT(innermost_function().IsAsyncGenerator() || innermost_function().IsAsyncGenClosure()); LocalVariable* controller_var = LookupLocalScope(Symbols::ColonController()); ASSERT(controller_var != NULL); // :controller.add[Stream](expr); ArgumentListNode* add_args = new (Z) ArgumentListNode(yield_pos); add_args->Add(expr); AstNode* add_call = new (Z) InstanceCallNode( yield_pos, new (Z) LoadLocalNode(TokenPosition::kNoSource, controller_var), is_yield_each ? Symbols::AddStream() : Symbols::add(), add_args); // if (:controller.add[Stream](expr)) { // return; // } // await_marker; // continuation_return; // restore saved_try_context SequenceNode* true_branch = new (Z) SequenceNode(TokenPosition::kNoSource, NULL); AstNode* return_from_generator = new (Z) ReturnNode(yield_pos); true_branch->Add(return_from_generator); AddNodeForFinallyInlining(return_from_generator); AstNode* if_is_cancelled = new (Z) IfNode(TokenPosition::kNoSource, add_call, true_branch, NULL); yield->AddNode(if_is_cancelled); AwaitMarkerNode* await_marker = new (Z) AwaitMarkerNode( async_temp_scope_, current_block_->scope, TokenPosition::kNoSource); yield->AddNode(await_marker); ReturnNode* continuation_return = new (Z) ReturnNode(yield_pos); continuation_return->set_return_type(ReturnNode::kContinuationTarget); yield->AddNode(continuation_return); // If this expression is part of a try block, also append the code for // restoring the saved try context that lives on the stack and possibly the // saved try context of the outer try block. LocalVariable* saved_try_ctx; LocalVariable* async_saved_try_ctx; LocalVariable* outer_saved_try_ctx; LocalVariable* outer_async_saved_try_ctx; CheckAsyncOpInTryBlock(&saved_try_ctx, &async_saved_try_ctx, &outer_saved_try_ctx, &outer_async_saved_try_ctx); if (saved_try_ctx != NULL) { yield->AddNode(new (Z) StoreLocalNode( TokenPosition::kNoSource, saved_try_ctx, new (Z) LoadLocalNode(TokenPosition::kNoSource, async_saved_try_ctx))); if (outer_saved_try_ctx != NULL) { yield->AddNode(new (Z) StoreLocalNode( TokenPosition::kNoSource, outer_saved_try_ctx, new (Z) LoadLocalNode(TokenPosition::kNoSource, outer_async_saved_try_ctx))); } } else { ASSERT(outer_saved_try_ctx == NULL); } } return yield; } AstNode* Parser::ParseStatement() { TRACE_PARSER("ParseStatement"); AstNode* statement = NULL; TokenPosition label_pos = TokenPosition::kNoSource; String* label_name = NULL; if (IsIdentifier()) { if (LookaheadToken(1) == Token::kCOLON) { // Statement starts with a label. label_name = CurrentLiteral(); label_pos = TokenPos(); ASSERT(label_pos.IsReal()); ConsumeToken(); // Consume identifier. ConsumeToken(); // Consume colon. } } const TokenPosition statement_pos = TokenPos(); const Token::Kind token = CurrentToken(); if (token == Token::kWHILE) { statement = ParseWhileStatement(label_name); } else if (token == Token::kFOR) { statement = ParseForStatement(label_name); } else if (IsAwaitKeyword() && (LookaheadToken(1) == Token::kFOR)) { statement = ParseAwaitForStatement(label_name); } else if (token == Token::kDO) { statement = ParseDoWhileStatement(label_name); } else if (token == Token::kSWITCH) { statement = ParseSwitchStatement(label_name); } else if (token == Token::kTRY) { statement = ParseTryStatement(label_name); } else if (token == Token::kRETURN) { const TokenPosition return_pos = TokenPos(); ConsumeToken(); if (CurrentToken() != Token::kSEMICOLON) { const TokenPosition expr_pos = TokenPos(); const int function_level = FunctionLevel(); if (current_function().IsGenerativeConstructor() && (function_level == 0)) { ReportError(expr_pos, "return of a value is not allowed in constructors"); } else if (current_function().IsGeneratorClosure() && (function_level == 0)) { ReportError(expr_pos, "generator functions may not return a value"); } AstNode* expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL); expr = AddAsyncResultTypeCheck(expr_pos, expr); statement = new (Z) ReturnNode(statement_pos, expr); } else { if (current_function().IsSyncGenClosure() && (FunctionLevel() == 0)) { // In a synchronous generator, return without an expression // returns false, signaling that the iterator terminates and // did not yield a value. statement = new (Z) ReturnNode( statement_pos, new (Z) LiteralNode(return_pos, Bool::False())); } else { statement = new (Z) ReturnNode(statement_pos); } } AddNodeForFinallyInlining(statement); ExpectSemicolon(); } else if (IsYieldKeyword()) { statement = ParseYieldStatement(); ExpectSemicolon(); } else if (token == Token::kIF) { statement = ParseIfStatement(label_name); } else if (token == Token::kASSERT) { statement = ParseAssertStatement(); ExpectSemicolon(); } else if (IsVariableDeclaration()) { statement = ParseVariableDeclarationList(); ExpectSemicolon(); } else if (IsFunctionDeclaration()) { statement = ParseFunctionStatement(false); } else if (token == Token::kLBRACE) { SourceLabel* label = NULL; OpenBlock(); if (label_name != NULL) { label = SourceLabel::New(label_pos, label_name, SourceLabel::kStatement); current_block_->scope->AddLabel(label); } ConsumeToken(); ParseStatementSequence(); statement = CloseBlock(); if (label != NULL) { statement->AsSequenceNode()->set_label(label); } ExpectToken(Token::kRBRACE); } else if (token == Token::kBREAK) { statement = ParseJump(label_name); if ((statement != NULL) && !statement->IsStopNode()) { AddNodeForFinallyInlining(statement); } ExpectSemicolon(); } else if (token == Token::kCONTINUE) { statement = ParseJump(label_name); AddNodeForFinallyInlining(statement); ExpectSemicolon(); } else if (token == Token::kSEMICOLON) { // Empty statement, nothing to do. ConsumeToken(); } else if (token == Token::kRETHROW) { // Rethrow of current exception. ConsumeToken(); ExpectSemicolon(); // Check if it is ok to do a rethrow. Find the innermost enclosing // catch block. TryStack* try_statement = try_stack_; while ((try_statement != NULL) && !try_statement->inside_catch()) { try_statement = try_statement->outer_try(); } if (try_statement == NULL) { ReportError(statement_pos, "rethrow of an exception is not valid here"); } // If in async code, use :saved_exception_var and :saved_stack_trace_var // instead of :exception_var and :stack_trace_var. // These variables are bound in the block containing the try. // Look in the try scope directly. LocalScope* scope = try_statement->try_block()->scope->parent(); ASSERT(scope != NULL); LocalVariable* excp_var; LocalVariable* trace_var; if (innermost_function().IsAsyncClosure() || innermost_function().IsAsyncFunction() || innermost_function().IsSyncGenClosure() || innermost_function().IsSyncGenerator() || innermost_function().IsAsyncGenClosure() || innermost_function().IsAsyncGenerator()) { excp_var = scope->LocalLookupVariable(Symbols::SavedExceptionVar()); trace_var = scope->LocalLookupVariable(Symbols::SavedStackTraceVar()); } else { excp_var = scope->LocalLookupVariable(Symbols::ExceptionVar()); trace_var = scope->LocalLookupVariable(Symbols::StackTraceVar()); } ASSERT(excp_var != NULL); ASSERT(trace_var != NULL); statement = new (Z) ThrowNode(statement_pos, new (Z) LoadLocalNode(statement_pos, excp_var), new (Z) LoadLocalNode(statement_pos, trace_var)); } else { statement = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL); ExpectSemicolon(); } return statement; } void Parser::ReportError(const Error& error) { Report::LongJump(error); UNREACHABLE(); } void Parser::ReportErrors(const Error& prev_error, const Script& script, TokenPosition token_pos, const char* format, ...) { va_list args; va_start(args, format); Report::LongJumpV(prev_error, script, token_pos, format, args); va_end(args); UNREACHABLE(); } void Parser::ReportError(TokenPosition token_pos, const char* format, ...) const { va_list args; va_start(args, format); Report::MessageV(Report::kError, script_, token_pos, Report::AtLocation, format, args); va_end(args); UNREACHABLE(); } void Parser::ReportErrorBefore(const char* format, ...) { va_list args; va_start(args, format); Report::MessageV(Report::kError, script_, PrevTokenPos(), Report::AfterLocation, format, args); va_end(args); UNREACHABLE(); } void Parser::ReportError(const char* format, ...) const { va_list args; va_start(args, format); Report::MessageV(Report::kError, script_, TokenPos(), Report::AtLocation, format, args); va_end(args); UNREACHABLE(); } void Parser::ReportWarning(TokenPosition token_pos, const char* format, ...) const { va_list args; va_start(args, format); Report::MessageV(Report::kWarning, script_, token_pos, Report::AtLocation, format, args); va_end(args); } void Parser::ReportWarning(const char* format, ...) const { va_list args; va_start(args, format); Report::MessageV(Report::kWarning, script_, TokenPos(), Report::AtLocation, format, args); va_end(args); } void Parser::CheckToken(Token::Kind token_expected, const char* msg) { if (CurrentToken() != token_expected) { if (msg != NULL) { ReportError("%s", msg); } else { ReportError("'%s' expected", Token::Str(token_expected)); } } } void Parser::ExpectToken(Token::Kind token_expected) { if (CurrentToken() != token_expected) { ReportError("'%s' expected", Token::Str(token_expected)); } ConsumeToken(); } void Parser::ExpectSemicolon() { if (CurrentToken() != Token::kSEMICOLON) { ReportErrorBefore("semicolon expected"); } ConsumeToken(); } void Parser::UnexpectedToken() { ReportError("unexpected token '%s'", CurrentToken() == Token::kIDENT ? CurrentLiteral()->ToCString() : Token::Str(CurrentToken())); } String* Parser::ExpectUserDefinedTypeIdentifier(const char* msg) { if (CurrentToken() != Token::kIDENT) { ReportError("%s", msg); } String* ident = CurrentLiteral(); if (ident->Equals("dynamic")) { ReportError("%s", msg); } ConsumeToken(); return ident; } // Check whether current token is an identifier or a built-in identifier. String* Parser::ExpectIdentifier(const char* msg) { if (!IsIdentifier()) { ReportError("%s", msg); } String* ident = CurrentLiteral(); ConsumeToken(); return ident; } bool Parser::IsAwaitKeyword() { return (FLAG_await_is_keyword || await_is_keyword_) && IsSymbol(Symbols::Await()); } bool Parser::IsYieldKeyword() { return (FLAG_await_is_keyword || await_is_keyword_) && IsSymbol(Symbols::YieldKw()); } static bool IsIncrementOperator(Token::Kind token) { return token == Token::kINCR || token == Token::kDECR; } static bool IsPrefixOperator(Token::Kind token) { return (token == Token::kSUB) || (token == Token::kNOT) || (token == Token::kBIT_NOT); } SequenceNode* Parser::NodeAsSequenceNode(TokenPosition sequence_pos, AstNode* node, LocalScope* scope) { if ((node == NULL) || !node->IsSequenceNode()) { SequenceNode* sequence = new SequenceNode(sequence_pos, scope); if (node != NULL) { sequence->Add(node); } return sequence; } return node->AsSequenceNode(); } AstNode* Parser::ThrowTypeError(TokenPosition type_pos, const AbstractType& type, LibraryPrefix* prefix) { ArgumentListNode* arguments = new (Z) ArgumentListNode(type_pos); String& method_name = String::Handle(Z); if (prefix == NULL) { method_name = Library::PrivateCoreLibName(Symbols::ThrowNew()).raw(); } else { arguments->Add(new (Z) LiteralNode(type_pos, *prefix)); method_name = Library::PrivateCoreLibName(Symbols::ThrowNewIfNotLoaded()).raw(); } // Location argument. arguments->Add(new (Z) LiteralNode( type_pos, Integer::ZoneHandle(Z, Integer::New(type_pos.value(), Heap::kOld)))); // Src value argument. arguments->Add(new (Z) LiteralNode(type_pos, Object::null_instance())); // Dst type argument. arguments->Add(new (Z) LiteralNode(type_pos, type)); // Dst name argument. arguments->Add(new (Z) LiteralNode(type_pos, Symbols::Empty())); // Bound error msg argument. arguments->Add(new (Z) LiteralNode(type_pos, Object::null_instance())); return MakeStaticCall(Symbols::TypeError(), method_name, arguments); } // Call _throwNewIfNotLoaded if prefix is not NULL, otherwise call _throwNew. AstNode* Parser::ThrowNoSuchMethodError(TokenPosition call_pos, const Class& cls, const String& function_name, ArgumentListNode* function_arguments, InvocationMirror::Level im_level, InvocationMirror::Kind im_kind, const Function* func, const LibraryPrefix* prefix) { ArgumentListNode* arguments = new (Z) ArgumentListNode(call_pos); String& method_name = String::Handle(Z); if (prefix == NULL || !prefix->is_deferred_load()) { method_name = Library::PrivateCoreLibName(Symbols::ThrowNew()).raw(); } else { arguments->Add(new (Z) LiteralNode(call_pos, *prefix)); method_name = Library::PrivateCoreLibName(Symbols::ThrowNewIfNotLoaded()).raw(); } // Object receiver. // If the function is external and dynamic, pass the actual receiver, // otherwise, pass a class literal of the unresolved method's owner. if ((func != NULL) && !func->IsNull() && func->is_external() && !func->is_static()) { arguments->Add(LoadReceiver(func->token_pos())); } else { AbstractType& type = AbstractType::ZoneHandle(Z); type ^= Type::New(cls, TypeArguments::Handle(Z), call_pos, Heap::kOld); type ^= CanonicalizeType(type); arguments->Add(new (Z) LiteralNode(call_pos, type)); } // String memberName. arguments->Add(new (Z) LiteralNode( call_pos, String::ZoneHandle(Z, Symbols::New(T, function_name)))); // Smi invocation_type. if (cls.IsTopLevel()) { ASSERT(im_level == InvocationMirror::kStatic || im_level == InvocationMirror::kTopLevel); im_level = InvocationMirror::kTopLevel; } arguments->Add(new (Z) LiteralNode( call_pos, Smi::ZoneHandle(Z, Smi::New(InvocationMirror::EncodeType( im_level, im_kind))))); // Type arguments. arguments->Add(new (Z) LiteralNode( call_pos, function_arguments == NULL ? TypeArguments::ZoneHandle(Z, TypeArguments::null()) : function_arguments->type_arguments())); // List arguments. if (function_arguments == NULL) { arguments->Add(new (Z) LiteralNode(call_pos, Object::null_array())); } else { ArrayNode* array = new (Z) ArrayNode(call_pos, Type::ZoneHandle(Z, Type::ArrayType()), function_arguments->nodes()); arguments->Add(array); } // List argumentNames. if (function_arguments == NULL) { arguments->Add(new (Z) LiteralNode(call_pos, Object::null_array())); } else { arguments->Add(new (Z) LiteralNode(call_pos, function_arguments->names())); } return MakeStaticCall(Symbols::NoSuchMethodError(), method_name, arguments); } AstNode* Parser::ParseBinaryExpr(int min_preced) { TRACE_PARSER("ParseBinaryExpr"); ASSERT(min_preced >= Token::Precedence(Token::kIFNULL)); AstNode* left_operand = ParseUnaryExpr(); if (left_operand->IsPrimaryNode() && (left_operand->AsPrimaryNode()->IsSuper())) { ReportError(left_operand->token_pos(), "illegal use of 'super'"); } int current_preced = Token::Precedence(CurrentToken()); while (current_preced >= min_preced) { while (Token::Precedence(CurrentToken()) == current_preced) { Token::Kind op_kind = CurrentToken(); const TokenPosition op_pos = TokenPos(); ConsumeToken(); AstNode* right_operand = NULL; if ((op_kind != Token::kIS) && (op_kind != Token::kAS)) { right_operand = ParseBinaryExpr(current_preced + 1); } else { // For 'is' and 'as' we expect the right operand to be a type. if ((op_kind == Token::kIS) && (CurrentToken() == Token::kNOT)) { ConsumeToken(); op_kind = Token::kISNOT; } const TokenPosition type_pos = TokenPos(); const AbstractType& type = AbstractType::ZoneHandle( Z, ParseTypeOrFunctionType(false, ClassFinalizer::kCanonicalize)); if (!type.IsInstantiated() && (FunctionLevel() > 0)) { // Make sure that the instantiators are captured. CaptureAllInstantiators(); } right_operand = new (Z) TypeNode(type_pos, type); // In production mode, the type may be malformed. // In checked mode, the type may be malformed or malbounded. if (type.IsMalformedOrMalbounded()) { // Note that a type error is thrown in a type test or in // a type cast even if the tested value is null. // We need to evaluate the left operand for potential // side effects. LetNode* let = new (Z) LetNode(left_operand->token_pos()); let->AddNode(left_operand); let->AddNode(ThrowTypeError(type_pos, type)); left_operand = let; break; // Type checks and casts can't be chained. } } if (Token::IsRelationalOperator(op_kind) || Token::IsTypeTestOperator(op_kind) || Token::IsTypeCastOperator(op_kind) || Token::IsEqualityOperator(op_kind)) { left_operand = new (Z) ComparisonNode(op_pos, op_kind, left_operand, right_operand); break; // Equality and relational operators cannot be chained. } else { left_operand = OptimizeBinaryOpNode(op_pos, op_kind, left_operand, right_operand); } } current_preced--; } return left_operand; } AstNode* Parser::ParseAwaitableExprList() { TRACE_PARSER("ParseAwaitableExprList"); SequenceNode* preamble = NULL; AstNode* expressions = ParseAwaitableExpr(kAllowConst, kConsumeCascades, &preamble); if (preamble != NULL) { preamble->Add(expressions); expressions = preamble; } if (CurrentToken() == Token::kCOMMA) { // Collect comma-separated expressions in a non scope owning sequence node. SequenceNode* list = new (Z) SequenceNode(TokenPos(), NULL); list->Add(expressions); while (CurrentToken() == Token::kCOMMA) { ConsumeToken(); preamble = NULL; AstNode* expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, &preamble); if (preamble != NULL) { list->Add(preamble); } list->Add(expr); } expressions = list; } return expressions; } void Parser::EnsureExpressionTemp() { // Temporary used later by the flow_graph_builder. parsed_function()->EnsureExpressionTemp(); } LocalVariable* Parser::CreateTempConstVariable(TokenPosition token_pos, const char* s) { char name[64]; OS::SNPrint(name, 64, ":%s%" Pd "", s, token_pos.value()); LocalVariable* temp = new (Z) LocalVariable( token_pos, token_pos, String::ZoneHandle(Z, Symbols::New(T, name)), Object::dynamic_type()); temp->set_is_final(); current_block_->scope->AddVariable(temp); return temp; } AstNode* Parser::OptimizeBinaryOpNode(TokenPosition op_pos, Token::Kind binary_op, AstNode* lhs, AstNode* rhs) { LiteralNode* lhs_literal = lhs->AsLiteralNode(); LiteralNode* rhs_literal = rhs->AsLiteralNode(); if ((lhs_literal != NULL) && (rhs_literal != NULL)) { if (lhs_literal->literal().IsDouble() && rhs_literal->literal().IsDouble()) { double left_double = Double::Cast(lhs_literal->literal()).value(); double right_double = Double::Cast(rhs_literal->literal()).value(); if (binary_op == Token::kDIV) { const Double& dbl_obj = Double::ZoneHandle( Z, Double::NewCanonical((left_double / right_double))); return new (Z) LiteralNode(op_pos, dbl_obj); } } } if (binary_op == Token::kBIT_AND) { // Normalize so that rhs is a literal if any is. if ((rhs_literal == NULL) && (lhs_literal != NULL)) { // Swap. LiteralNode* temp = rhs_literal; rhs_literal = lhs_literal; lhs_literal = temp; } } if (binary_op == Token::kIFNULL) { // Handle a ?? b. if ((lhs->EvalConstExpr() != NULL) && (rhs->EvalConstExpr() != NULL)) { Instance& expr_value = Instance::ZoneHandle( Z, EvaluateConstExpr(lhs->token_pos(), lhs).raw()); if (expr_value.IsNull()) { expr_value = EvaluateConstExpr(rhs->token_pos(), rhs).raw(); } return new (Z) LiteralNode(op_pos, expr_value); } LetNode* result = new (Z) LetNode(op_pos); LocalVariable* left_temp = result->AddInitializer(lhs); left_temp->set_is_final(); const TokenPosition no_pos = TokenPosition::kNoSource; LiteralNode* null_operand = new (Z) LiteralNode(no_pos, Object::null_instance()); LoadLocalNode* load_left_temp = new (Z) LoadLocalNode(no_pos, left_temp); ComparisonNode* null_compare = new (Z) ComparisonNode(no_pos, Token::kNE_STRICT, load_left_temp, null_operand); // If the expression is a compile-time constant, ensure that it // is evaluated and canonicalized. See issue 31066. if (rhs->EvalConstExpr() != NULL) { rhs = FoldConstExpr(rhs->token_pos(), rhs); } result->AddNode( new (Z) ConditionalExprNode(op_pos, null_compare, load_left_temp, rhs)); return result; } return new (Z) BinaryOpNode(op_pos, binary_op, lhs, rhs); } AstNode* Parser::ExpandAssignableOp(TokenPosition op_pos, Token::Kind assignment_op, AstNode* lhs, AstNode* rhs) { TRACE_PARSER("ExpandAssignableOp"); switch (assignment_op) { case Token::kASSIGN: return rhs; case Token::kASSIGN_ADD: return new (Z) BinaryOpNode(op_pos, Token::kADD, lhs, rhs); case Token::kASSIGN_SUB: return new (Z) BinaryOpNode(op_pos, Token::kSUB, lhs, rhs); case Token::kASSIGN_MUL: return new (Z) BinaryOpNode(op_pos, Token::kMUL, lhs, rhs); case Token::kASSIGN_TRUNCDIV: return new (Z) BinaryOpNode(op_pos, Token::kTRUNCDIV, lhs, rhs); case Token::kASSIGN_DIV: return new (Z) BinaryOpNode(op_pos, Token::kDIV, lhs, rhs); case Token::kASSIGN_MOD: return new (Z) BinaryOpNode(op_pos, Token::kMOD, lhs, rhs); case Token::kASSIGN_SHR: return new (Z) BinaryOpNode(op_pos, Token::kSHR, lhs, rhs); case Token::kASSIGN_SHL: return new (Z) BinaryOpNode(op_pos, Token::kSHL, lhs, rhs); case Token::kASSIGN_OR: return new (Z) BinaryOpNode(op_pos, Token::kBIT_OR, lhs, rhs); case Token::kASSIGN_AND: return new (Z) BinaryOpNode(op_pos, Token::kBIT_AND, lhs, rhs); case Token::kASSIGN_XOR: return new (Z) BinaryOpNode(op_pos, Token::kBIT_XOR, lhs, rhs); case Token::kASSIGN_COND: return new (Z) BinaryOpNode(op_pos, Token::kIFNULL, lhs, rhs); default: ReportError(op_pos, "internal error: ExpandAssignableOp '%s' unimplemented", Token::Name(assignment_op)); UNIMPLEMENTED(); return NULL; } } // Evaluates the value of the compile time constant expression // and returns a literal node for the value. LiteralNode* Parser::FoldConstExpr(TokenPosition expr_pos, AstNode* expr) { if (expr->IsLiteralNode()) { return expr->AsLiteralNode(); } if (expr->EvalConstExpr() == NULL) { ReportError(expr_pos, "expression is not a valid compile-time constant"); } return new (Z) LiteralNode(expr_pos, EvaluateConstExpr(expr_pos, expr)); } LetNode* Parser::PrepareCompoundAssignmentNodes(AstNode** expr) { AstNode* node = *expr; TokenPosition token_pos = node->token_pos(); LetNode* result = new (Z) LetNode(token_pos); if (node->IsLoadIndexedNode()) { LoadIndexedNode* load_indexed = node->AsLoadIndexedNode(); AstNode* array = load_indexed->array(); AstNode* index = load_indexed->index_expr(); if (!IsSimpleLocalOrLiteralNode(load_indexed->array())) { LocalVariable* t0 = result->AddInitializer(load_indexed->array()); array = new (Z) LoadLocalNode(token_pos, t0); } if (!IsSimpleLocalOrLiteralNode(load_indexed->index_expr())) { LocalVariable* t1 = result->AddInitializer(load_indexed->index_expr()); index = new (Z) LoadLocalNode(token_pos, t1); } *expr = new (Z) LoadIndexedNode(token_pos, array, index, load_indexed->super_class()); return result; } if (node->IsInstanceGetterNode()) { InstanceGetterNode* getter = node->AsInstanceGetterNode(); AstNode* receiver = getter->receiver(); if (!IsSimpleLocalOrLiteralNode(getter->receiver())) { LocalVariable* t0 = result->AddInitializer(getter->receiver()); receiver = new (Z) LoadLocalNode(token_pos, t0); } *expr = new (Z) InstanceGetterNode( token_pos, receiver, getter->field_name(), getter->is_conditional()); return result; } return result; } // Check whether the syntax of expression expr is a grammatically legal // assignable expression. This check is used to detect situations where // the expression itself is assignable, but the source is grammatically // wrong. The AST representation of an expression cannot distinguish // between x = 0 and (x) = 0. The latter is illegal. // A syntactically legal assignable expression always ends with an // identifier token or a ] token. We rewind the token iterator and // check whether the token before end_pos is an identifier or ]. bool Parser::IsLegalAssignableSyntax(AstNode* expr, TokenPosition end_pos) { ASSERT(expr->token_pos().IsReal()); ASSERT(expr->token_pos() < end_pos); SetPosition(expr->token_pos()); Token::Kind token = Token::kILLEGAL; while (TokenPos() < end_pos) { token = CurrentToken(); ConsumeToken(); } ASSERT(TokenPos() == end_pos); return Token::IsIdentifier(token) || (token == Token::kRBRACK); } AstNode* Parser::CreateAssignmentNode(AstNode* original, AstNode* rhs, const String* left_ident, TokenPosition left_pos, bool is_compound /* = false */) { AstNode* result = original->MakeAssignmentNode(rhs); if (result == NULL) { String& name = String::ZoneHandle(Z); const Class* target_cls = ¤t_class(); if (original->IsTypeNode()) { name = Symbols::New(T, original->AsTypeNode()->TypeName()); } else if (original->IsLoadStaticFieldNode()) { name = original->AsLoadStaticFieldNode()->field().name(); target_cls = &Class::Handle(Z, original->AsLoadStaticFieldNode()->field().Owner()); } else if ((left_ident != NULL) && (original->IsLiteralNode() || original->IsLoadLocalNode())) { name = left_ident->raw(); } if (name.IsNull()) { ReportError(left_pos, "expression is not assignable"); } ArgumentListNode* error_arguments = new (Z) ArgumentListNode(rhs->token_pos()); error_arguments->Add(rhs); result = ThrowNoSuchMethodError( original->token_pos(), *target_cls, String::Handle(Z, Field::SetterSymbol(name)), error_arguments, InvocationMirror::kStatic, original->IsLoadLocalNode() ? InvocationMirror::kLocalVar : InvocationMirror::kSetter, NULL); // No existing function. } // The compound assignment operator a ??= b is different from other // a op= b assignments. If a is non-null, the assignment to a must be // dropped: // normally: a op= b ==> a = a op b // however: a ??= b ==> a ?? (a = b) // Therefore, we need to transform a = (a ?? b) into a ?? (a = b) if (is_compound && rhs->IsBinaryOpNode() && (rhs->AsBinaryOpNode()->kind() == Token::kIFNULL)) { BinaryOpNode* ifnull = rhs->AsBinaryOpNode(); AstNode* modified_assign = CreateAssignmentNode(original, ifnull->right(), left_ident, left_pos); result = OptimizeBinaryOpNode(ifnull->token_pos(), ifnull->kind(), ifnull->left(), modified_assign); } return result; } AstNode* Parser::ParseCascades(AstNode* expr) { TokenPosition cascade_pos = TokenPos(); LetNode* cascade = new (Z) LetNode(cascade_pos); LocalVariable* cascade_receiver_var = cascade->AddInitializer(expr); while (CurrentToken() == Token::kCASCADE) { cascade_pos = TokenPos(); LoadLocalNode* load_cascade_receiver = new (Z) LoadLocalNode(cascade_pos, cascade_receiver_var); if (Token::IsIdentifier(LookaheadToken(1))) { // Replace .. with . for ParseSelectors(). token_kind_ = Token::kPERIOD; } else if (LookaheadToken(1) == Token::kLBRACK) { ConsumeToken(); } else { ReportError("identifier or [ expected after .."); } String* expr_ident = Token::IsIdentifier(CurrentToken()) ? CurrentLiteral() : NULL; const TokenPosition expr_pos = TokenPos(); expr = ParseSelectors(load_cascade_receiver, true); // Assignments after a cascade are part of the cascade. The // assigned expression must not contain cascades. if (Token::IsAssignmentOperator(CurrentToken())) { Token::Kind assignment_op = CurrentToken(); const TokenPosition assignment_pos = TokenPos(); ConsumeToken(); AstNode* right_expr = ParseExpr(kAllowConst, kNoCascades); if (assignment_op != Token::kASSIGN) { // Compound assignment: store inputs with side effects into // temporary locals. LetNode* let_expr = PrepareCompoundAssignmentNodes(&expr); right_expr = ExpandAssignableOp(assignment_pos, assignment_op, expr, right_expr); AstNode* assign_expr = CreateAssignmentNode(expr, right_expr, expr_ident, expr_pos, true); ASSERT(assign_expr != NULL); let_expr->AddNode(assign_expr); expr = let_expr; } else { right_expr = ExpandAssignableOp(assignment_pos, assignment_op, expr, right_expr); AstNode* assign_expr = CreateAssignmentNode(expr, right_expr, expr_ident, expr_pos); ASSERT(assign_expr != NULL); expr = assign_expr; } } cascade->AddNode(expr); } // The result is an expression with the (side effects of the) cascade // sequence followed by the (value of the) receiver temp variable load. cascade->AddNode(new (Z) LoadLocalNode(cascade_pos, cascade_receiver_var)); return cascade; } // Convert loading of a static const field into a literal node. static AstNode* LiteralIfStaticConst(Zone* zone, AstNode* expr) { if (expr->IsLoadStaticFieldNode()) { const Field& field = expr->AsLoadStaticFieldNode()->field(); if (field.is_const() && !expr->AsLoadStaticFieldNode()->is_deferred_reference()) { ASSERT(field.StaticValue() != Object::sentinel().raw()); ASSERT(field.StaticValue() != Object::transition_sentinel().raw()); return new (zone) LiteralNode( expr->token_pos(), Instance::ZoneHandle(zone, field.StaticValue())); } } return expr; } AstNode* Parser::ParseAwaitableExpr(bool require_compiletime_const, bool consume_cascades, SequenceNode** await_preamble) { TRACE_PARSER("ParseAwaitableExpr"); BoolScope saved_seen_await(&parsed_function()->have_seen_await_expr_, false); AstNode* expr = ParseExpr(require_compiletime_const, consume_cascades); if (parsed_function()->have_seen_await()) { // Make sure we do not reuse the scope to avoid creating contexts that we // are unaware of, i.e, creating contexts that have already been covered. // See FlowGraphBuilder::VisitSequenceNode() for details on when contexts // are created. OpenBlock(); AwaitTransformer at(current_block_->statements, async_temp_scope_); AstNode* result = at.Transform(expr); SequenceNode* preamble = CloseBlock(); if (await_preamble == NULL) { current_block_->statements->Add(preamble); } else { *await_preamble = preamble; } return result; } return expr; } AstNode* Parser::ParseExpr(bool require_compiletime_const, bool consume_cascades) { TRACE_PARSER("ParseExpr"); String* expr_ident = Token::IsIdentifier(CurrentToken()) ? CurrentLiteral() : NULL; const TokenPosition expr_pos = TokenPos(); RecursionChecker rc(this); if (CurrentToken() == Token::kTHROW) { if (require_compiletime_const) { ReportError("'throw expr' is not a valid compile-time constant"); } ConsumeToken(); if (CurrentToken() == Token::kSEMICOLON) { ReportError("expression expected after throw"); } AstNode* expr = ParseExpr(require_compiletime_const, consume_cascades); return new (Z) ThrowNode(expr_pos, expr, NULL); } if (require_compiletime_const) { // Check whether we already have evaluated a compile-time constant // at this source location. Instance& existing_const = Instance::ZoneHandle(Z); if (GetCachedConstant(expr_pos, &existing_const)) { SkipConditionalExpr(); return new (Z) LiteralNode(expr_pos, existing_const); } } AstNode* expr = ParseConditionalExpr(); if (!Token::IsAssignmentOperator(CurrentToken())) { if ((CurrentToken() == Token::kCASCADE) && consume_cascades) { return ParseCascades(expr); } if (require_compiletime_const) { expr = FoldConstExpr(expr_pos, expr); } else { expr = LiteralIfStaticConst(Z, expr); } return expr; } // Assignment expressions. if (!IsLegalAssignableSyntax(expr, TokenPos())) { ReportError(expr_pos, "expression is not assignable"); } const Token::Kind assignment_op = CurrentToken(); const TokenPosition assignment_pos = TokenPos(); if (require_compiletime_const) { ReportError(assignment_pos, "expression is not a valid compile-time constant"); } ConsumeToken(); AstNode* right_expr = ParseExpr(require_compiletime_const, consume_cascades); if (assignment_op != Token::kASSIGN) { // Compound assignment: store inputs with side effects into temp. locals. LetNode* let_expr = PrepareCompoundAssignmentNodes(&expr); AstNode* assigned_value = ExpandAssignableOp(assignment_pos, assignment_op, expr, right_expr); AstNode* assign_expr = CreateAssignmentNode(expr, assigned_value, expr_ident, expr_pos, true); ASSERT(assign_expr != NULL); let_expr->AddNode(assign_expr); return let_expr; } else { AstNode* assigned_value = LiteralIfStaticConst(Z, right_expr); AstNode* assign_expr = CreateAssignmentNode(expr, assigned_value, expr_ident, expr_pos); ASSERT(assign_expr != NULL); return assign_expr; } } LiteralNode* Parser::ParseConstExpr() { TRACE_PARSER("ParseConstExpr"); TokenPosition expr_pos = TokenPos(); AstNode* expr = ParseExpr(kRequireConst, kNoCascades); if (!expr->IsLiteralNode()) { ReportError(expr_pos, "expression must be a compile-time constant"); } return expr->AsLiteralNode(); } AstNode* Parser::ParseConditionalExpr() { TRACE_PARSER("ParseConditionalExpr"); const TokenPosition expr_pos = TokenPos(); AstNode* expr = ParseBinaryExpr(Token::Precedence(Token::kIFNULL)); if (CurrentToken() == Token::kCONDITIONAL) { EnsureExpressionTemp(); ConsumeToken(); AstNode* expr1 = ParseExpr(kAllowConst, kNoCascades); ExpectToken(Token::kCOLON); AstNode* expr2 = ParseExpr(kAllowConst, kNoCascades); expr = new (Z) ConditionalExprNode(expr_pos, expr, expr1, expr2); } return expr; } AstNode* Parser::ParseUnaryExpr() { TRACE_PARSER("ParseUnaryExpr"); AstNode* expr = NULL; const TokenPosition op_pos = TokenPos(); if (IsAwaitKeyword()) { TRACE_PARSER("ParseAwaitExpr"); if (!innermost_function().IsAsyncFunction() && !innermost_function().IsAsyncClosure() && !innermost_function().IsAsyncGenerator() && !innermost_function().IsAsyncGenClosure()) { ReportError("await operator is only allowed in an asynchronous function"); } ConsumeToken(); parsed_function()->record_await(); LocalVariable* saved_try_ctx; LocalVariable* async_saved_try_ctx; LocalVariable* outer_saved_try_ctx; LocalVariable* outer_async_saved_try_ctx; CheckAsyncOpInTryBlock(&saved_try_ctx, &async_saved_try_ctx, &outer_saved_try_ctx, &outer_async_saved_try_ctx); expr = new (Z) AwaitNode(op_pos, ParseUnaryExpr(), saved_try_ctx, async_saved_try_ctx, outer_saved_try_ctx, outer_async_saved_try_ctx, current_block_->scope); } else if (IsPrefixOperator(CurrentToken())) { Token::Kind unary_op = CurrentToken(); if (unary_op == Token::kSUB) { unary_op = Token::kNEGATE; } ConsumeToken(); expr = ParseUnaryExpr(); if (expr->IsPrimaryNode() && (expr->AsPrimaryNode()->IsSuper())) { expr = BuildUnarySuperOperator(unary_op, expr->AsPrimaryNode()); } else { expr = UnaryOpNode::UnaryOpOrLiteral(op_pos, unary_op, expr); } } else if (IsIncrementOperator(CurrentToken())) { Token::Kind incr_op = CurrentToken(); ConsumeToken(); String* expr_ident = Token::IsIdentifier(CurrentToken()) ? CurrentLiteral() : NULL; const TokenPosition expr_pos = TokenPos(); expr = ParseUnaryExpr(); if (!IsLegalAssignableSyntax(expr, TokenPos())) { ReportError(expr_pos, "expression is not assignable"); } // Is prefix. LetNode* let_expr = PrepareCompoundAssignmentNodes(&expr); Token::Kind binary_op = (incr_op == Token::kINCR) ? Token::kADD : Token::kSUB; BinaryOpNode* add = new (Z) BinaryOpNode( op_pos, binary_op, expr, new (Z) LiteralNode(op_pos, Smi::ZoneHandle(Z, Smi::New(1)))); AstNode* store = CreateAssignmentNode(expr, add, expr_ident, expr_pos, true); ASSERT(store != NULL); let_expr->AddNode(store); expr = let_expr; } else { expr = ParsePostfixExpr(); } return expr; } ArgumentListNode* Parser::ParseActualParameters( ArgumentListNode* implicit_arguments, const TypeArguments& func_type_args, bool require_const) { TRACE_PARSER("ParseActualParameters"); ASSERT(CurrentToken() == Token::kLPAREN); const bool saved_mode = SetAllowFunctionLiterals(true); ArgumentListNode* arguments; if (implicit_arguments == NULL) { // When require_const is true, no function type arguments are passed, so // there is no need to check that they are instantiated. ASSERT(!require_const || func_type_args.IsNull()); arguments = new (Z) ArgumentListNode(TokenPos(), func_type_args); } else { // If implicit arguments are provided, they include type arguments (if any). ASSERT(func_type_args.IsNull()); arguments = implicit_arguments; } const GrowableObjectArray& names = GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld)); bool named_argument_seen = false; if (LookaheadToken(1) != Token::kRPAREN) { String& arg_name = String::Handle(Z); do { ASSERT((CurrentToken() == Token::kLPAREN) || (CurrentToken() == Token::kCOMMA)); ConsumeToken(); if (CurrentToken() == Token::kRPAREN) { // Allow trailing comma. break; } if (IsIdentifier() && (LookaheadToken(1) == Token::kCOLON)) { named_argument_seen = true; // The canonicalization of the arguments descriptor array built in // the code generator requires that the names are symbols, i.e. // canonicalized strings. ASSERT(CurrentLiteral()->IsSymbol()); for (int i = 0; i < names.Length(); i++) { arg_name ^= names.At(i); if (CurrentLiteral()->Equals(arg_name)) { ReportError("duplicate named argument"); } } names.Add(*CurrentLiteral(), Heap::kOld); ConsumeToken(); // ident. ConsumeToken(); // colon. } else if (named_argument_seen) { ReportError("named argument expected"); } arguments->Add(ParseExpr(require_const, kConsumeCascades)); } while (CurrentToken() == Token::kCOMMA); } else { ConsumeToken(); } ExpectToken(Token::kRPAREN); SetAllowFunctionLiterals(saved_mode); if (named_argument_seen) { arguments->set_names(Array::Handle(Z, Array::MakeFixedLength(names))); } return arguments; } AstNode* Parser::ParseStaticCall(const Class& cls, const String& func_name, TokenPosition ident_pos, const TypeArguments& func_type_args, const LibraryPrefix* prefix) { TRACE_PARSER("ParseStaticCall"); const TokenPosition call_pos = TokenPos(); ASSERT(CurrentToken() == Token::kLPAREN); ArgumentListNode* arguments = ParseActualParameters(NULL, func_type_args, kAllowConst); const int num_arguments = arguments->length(); const Function& func = Function::ZoneHandle( Z, Resolver::ResolveStatic(cls, func_name, func_type_args.Length(), num_arguments, arguments->names())); if (func.IsNull()) { // Check if there is a static field of the same name, it could be a closure // and so we try and invoke the closure. AstNode* closure = NULL; const Field& field = Field::ZoneHandle(Z, cls.LookupStaticField(func_name)); Function& func = Function::ZoneHandle(Z); if (field.IsNull()) { // No field, check if we have an explicit getter function. const String& getter_name = String::ZoneHandle(Z, Field::LookupGetterSymbol(func_name)); if (!getter_name.IsNull()) { const int kTypeArgsLen = 0; // no type arguments. const int kNumArguments = 0; // no arguments. func = Resolver::ResolveStatic(cls, getter_name, kTypeArgsLen, kNumArguments, Object::empty_array()); if (!func.IsNull()) { ASSERT(func.kind() != RawFunction::kImplicitStaticFinalGetter); closure = new (Z) StaticGetterNode(call_pos, NULL, Class::ZoneHandle(Z, cls.raw()), func_name, StaticGetterSetter::kStatic); return BuildClosureCall(call_pos, closure, arguments); } } } else { closure = GenerateStaticFieldLookup(field, call_pos); return BuildClosureCall(call_pos, closure, arguments); } // Could not resolve static method: throw a NoSuchMethodError. return ThrowNoSuchMethodError(ident_pos, cls, func_name, arguments, InvocationMirror::kStatic, InvocationMirror::kMethod, NULL, // No existing function. prefix); } else if (cls.IsTopLevel() && (cls.library() == Library::CoreLibrary()) && (func.name() == Symbols::Identical().raw()) && func_type_args.IsNull()) { // This is the predefined toplevel function identical(a,b). // Create a comparison node instead of a static call to the function. ASSERT(num_arguments == 2); // If both arguments are constant expressions of type string, // evaluate and canonicalize them. // This guarantees that identical("ab", "a"+"b") is true. // An alternative way to guarantee this would be to introduce // an AST node that canonicalizes a value. AstNode* arg0 = arguments->NodeAt(0); const Instance* val0 = arg0->EvalConstExpr(); if ((val0 != NULL) && (val0->IsString())) { AstNode* arg1 = arguments->NodeAt(1); const Instance* val1 = arg1->EvalConstExpr(); if ((val1 != NULL) && (val1->IsString())) { arguments->SetNodeAt( 0, new (Z) LiteralNode(arg0->token_pos(), EvaluateConstExpr(arg0->token_pos(), arg0))); arguments->SetNodeAt( 1, new (Z) LiteralNode(arg1->token_pos(), EvaluateConstExpr(arg1->token_pos(), arg1))); } } return new (Z) ComparisonNode(ident_pos, Token::kEQ_STRICT, arguments->NodeAt(0), arguments->NodeAt(1)); } return new (Z) StaticCallNode(ident_pos, func, arguments, StaticCallNode::kStatic); } AstNode* Parser::ParseInstanceCall(AstNode* receiver, const String& func_name, TokenPosition ident_pos, const TypeArguments& func_type_args, bool is_conditional) { TRACE_PARSER("ParseInstanceCall"); CheckToken(Token::kLPAREN); ArgumentListNode* arguments = ParseActualParameters(NULL, func_type_args, kAllowConst); return new (Z) InstanceCallNode(ident_pos, receiver, func_name, arguments, is_conditional); } AstNode* Parser::ParseClosureCall(AstNode* closure, const TypeArguments& func_type_args) { TRACE_PARSER("ParseClosureCall"); const TokenPosition call_pos = TokenPos(); ASSERT(CurrentToken() == Token::kLPAREN); ArgumentListNode* arguments = ParseActualParameters(NULL, func_type_args, kAllowConst); return BuildClosureCall(call_pos, closure, arguments); } AstNode* Parser::GenerateStaticFieldLookup(const Field& field, TokenPosition ident_pos) { // If the static field has an initializer, initialize the field at compile // time, which is only possible if the field is const. AstNode* initializing_getter = RunStaticFieldInitializer(field, ident_pos); if (initializing_getter != NULL) { // The field is not yet initialized and could not be initialized at compile // time. The getter will initialize the field. return initializing_getter; } // The field is initialized. ASSERT(field.is_static()); const Class& field_owner = Class::ZoneHandle(Z, field.Owner()); const String& field_name = String::ZoneHandle(Z, field.name()); const String& getter_name = String::Handle(Z, Field::GetterSymbol(field_name)); const Function& getter = Function::Handle(Z, field_owner.LookupStaticFunction(getter_name)); // Never load field directly if there is a getter (deterministic AST). if (getter.IsNull() || field.is_const()) { return new (Z) LoadStaticFieldNode(ident_pos, Field::ZoneHandle(Z, field.raw())); } else { ASSERT(getter.kind() == RawFunction::kImplicitStaticFinalGetter); return new (Z) StaticGetterNode(ident_pos, NULL, // Receiver. field_owner, field_name, StaticGetterSetter::kStatic); } } // Reference to 'field_name' with explicit class as primary. AstNode* Parser::GenerateStaticFieldAccess(const Class& cls, const String& field_name, TokenPosition ident_pos) { AstNode* access = NULL; const Field& field = Field::ZoneHandle(Z, cls.LookupStaticField(field_name)); Function& func = Function::ZoneHandle(Z); if (field.IsNull()) { // No field, check if we have an explicit getter function. func = cls.LookupGetterFunction(field_name); if (func.IsNull() || func.IsDynamicFunction()) { // We might be referring to an implicit closure, check to see if // there is a function of the same name. func = cls.LookupStaticFunction(field_name); if (!func.IsNull()) { access = CreateImplicitClosureNode(func, ident_pos, NULL); } else { // No function to closurize found found. // This field access may turn out to be a call to the setter. // Create a getter call, which may later be turned into // a setter call, or else the backend will generate // a throw NoSuchMethodError(). access = new (Z) StaticGetterNode(ident_pos, NULL, Class::ZoneHandle(Z, cls.raw()), field_name, StaticGetterSetter::kStatic); } } else { ASSERT(func.kind() != RawFunction::kImplicitStaticFinalGetter); access = new (Z) StaticGetterNode(ident_pos, NULL, Class::ZoneHandle(Z, cls.raw()), field_name, StaticGetterSetter::kStatic); } } else { access = GenerateStaticFieldLookup(field, ident_pos); } return access; } AstNode* Parser::LoadFieldIfUnresolved(AstNode* node) { if (!node->IsPrimaryNode()) { return node; } PrimaryNode* primary = node->AsPrimaryNode(); if (primary->primary().IsString()) { if (primary->IsSuper()) { return primary; } // In a static method, evaluation of an unresolved identifier causes a // NoSuchMethodError to be thrown. // In an instance method, we convert this into a getter call // for a field (which may be defined in a subclass.) const String& name = String::Cast(Object::ZoneHandle(primary->primary().raw())); if (primary->is_deferred_reference()) { StaticGetterNode* getter = new (Z) StaticGetterNode(primary->token_pos(), NULL, // No receiver. Class::ZoneHandle(Z, library_.toplevel_class()), name, StaticGetterSetter::kStatic); getter->set_is_deferred(primary->is_deferred_reference()); return getter; } else if (current_function().is_static() || current_function().IsInFactoryScope()) { StaticGetterNode* getter = new (Z) StaticGetterNode(primary->token_pos(), NULL, // No receiver. Class::ZoneHandle(Z, current_class().raw()), name, StaticGetterSetter::kStatic); getter->set_is_deferred(primary->is_deferred_reference()); return getter; } else { AstNode* receiver = LoadReceiver(primary->token_pos()); return CallGetter(node->token_pos(), receiver, name); } } return primary; } AstNode* Parser::LoadClosure(PrimaryNode* primary) { ASSERT(primary->primary().IsFunction()); const Function& func = Function::Cast(Object::ZoneHandle(primary->primary().raw())); const String& funcname = String::ZoneHandle(Z, func.name()); if (func.is_static()) { // Static function access. ClosureNode* closure = CreateImplicitClosureNode(func, primary->token_pos(), NULL); closure->set_is_deferred(primary->is_deferred_reference()); return closure; } else { // Instance function access. if (current_function().is_static() || current_function().IsInFactoryScope()) { ReportError(primary->token_pos(), "cannot access instance method '%s' from static method", funcname.ToCString()); } AstNode* receiver = LoadReceiver(primary->token_pos()); return CallGetter(primary->token_pos(), receiver, funcname); } UNREACHABLE(); return NULL; } AstNode* Parser::LoadTypeParameter(PrimaryNode* primary) { const TokenPosition primary_pos = primary->token_pos(); TypeParameter& type_parameter = TypeParameter::ZoneHandle(Z); type_parameter = TypeParameter::Cast(primary->primary()).raw(); if (type_parameter.IsClassTypeParameter()) { if (ParsingStaticMember()) { const String& name = String::Handle(Z, type_parameter.name()); ReportError(primary_pos, "cannot access type parameter '%s' " "from static function", name.ToCString()); } if (FunctionLevel() > 0) { // Make sure that the class instantiator is captured. CaptureInstantiator(); } type_parameter ^= CanonicalizeType(type_parameter); } else { ASSERT(type_parameter.IsFunctionTypeParameter()); if (!Isolate::Current()->reify_generic_functions()) { Type& type = Type::ZoneHandle(Z, Type::DynamicType()); return new (Z) TypeNode(primary_pos, type); } if ((FunctionLevel() > 0) && innermost_function().HasGenericParent()) { // Make sure that the parent function type arguments are captured. CaptureFunctionTypeArguments(); } } ASSERT(type_parameter.IsFinalized()); ASSERT(!type_parameter.IsMalformed()); return new (Z) TypeNode(primary_pos, type_parameter); } AstNode* Parser::ParseSelectors(AstNode* primary, bool is_cascade) { AstNode* left = primary; while (true) { AstNode* selector = NULL; if ((CurrentToken() == Token::kPERIOD) || (CurrentToken() == Token::kQM_PERIOD)) { // Unconditional or conditional property extraction or method call. bool is_conditional = CurrentToken() == Token::kQM_PERIOD; ConsumeToken(); if (left->IsPrimaryNode()) { PrimaryNode* primary_node = left->AsPrimaryNode(); if (primary_node->primary().IsFunction()) { left = LoadClosure(primary_node); } else if (primary_node->primary().IsTypeParameter()) { left = LoadTypeParameter(primary_node); } else { // Super field access handled in ParseSuperFieldAccess(), // super calls handled in ParseSuperCall(). ASSERT(!primary_node->IsSuper()); left = LoadFieldIfUnresolved(left); } } const TokenPosition ident_pos = TokenPos(); String* ident = ExpectIdentifier("identifier expected"); if (IsArgumentPart()) { // Identifier followed by optional type arguments and opening paren: // method call. TypeArguments& func_type_args = TypeArguments::ZoneHandle(Z); if (CurrentToken() == Token::kLT) { // Type arguments. func_type_args = ParseTypeArguments(ClassFinalizer::kCanonicalize); if (Isolate::Current()->reify_generic_functions()) { if (!func_type_args.IsNull() && !func_type_args.IsInstantiated() && (FunctionLevel() > 0)) { // Make sure that the instantiators are captured. CaptureAllInstantiators(); } } else { func_type_args = TypeArguments::null(); } } PrimaryNode* primary_node = left->AsPrimaryNode(); if ((primary_node != NULL) && primary_node->primary().IsClass()) { // Static method call prefixed with class name. const Class& cls = Class::Cast(primary_node->primary()); selector = ParseStaticCall(cls, *ident, ident_pos, func_type_args, primary_node->prefix()); } else { if ((primary_node != NULL) && primary_node->is_deferred_reference()) { const Class& cls = Class::Handle(library_.toplevel_class()); selector = ParseStaticCall(cls, *ident, ident_pos, func_type_args, primary_node->prefix()); } else { selector = ParseInstanceCall(left, *ident, ident_pos, func_type_args, is_conditional); } } } else { // Field access. Class& cls = Class::Handle(Z); bool is_deferred = false; if (left->IsPrimaryNode()) { PrimaryNode* primary_node = left->AsPrimaryNode(); is_deferred = primary_node->is_deferred_reference(); 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 if (is_deferred) { cls = library_.toplevel_class(); } } if (cls.IsNull()) { // Instance field access. selector = new (Z) InstanceGetterNode(ident_pos, left, *ident, is_conditional); } else { // Static field access. selector = GenerateStaticFieldAccess(cls, *ident, ident_pos); ASSERT(selector != NULL); if (selector->IsLoadStaticFieldNode()) { selector->AsLoadStaticFieldNode()->set_is_deferred(is_deferred); } else if (selector->IsStaticGetterNode()) { selector->AsStaticGetterNode()->set_is_deferred(is_deferred); } } } } else if (CurrentToken() == Token::kLBRACK) { // Super index operator handled in ParseSuperOperator(). ASSERT(!left->IsPrimaryNode() || !left->AsPrimaryNode()->IsSuper()); const TokenPosition bracket_pos = TokenPos(); ConsumeToken(); left = LoadFieldIfUnresolved(left); const bool saved_mode = SetAllowFunctionLiterals(true); AstNode* index = ParseExpr(kAllowConst, kConsumeCascades); SetAllowFunctionLiterals(saved_mode); ExpectToken(Token::kRBRACK); AstNode* array = left; if (left->IsPrimaryNode()) { PrimaryNode* primary_node = left->AsPrimaryNode(); const TokenPosition primary_pos = primary_node->token_pos(); if (primary_node->primary().IsFunction()) { array = LoadClosure(primary_node); } else if (primary_node->primary().IsClass()) { const Class& type_class = Class::Cast(primary_node->primary()); AbstractType& type = Type::ZoneHandle( Z, Type::New(type_class, TypeArguments::Handle(Z), primary_pos, Heap::kOld)); type ^= CanonicalizeType(type); // Type may be malbounded, but not malformed. ASSERT(!type.IsMalformed()); array = new (Z) TypeNode(primary_pos, type, primary_node->is_deferred_reference()); } else if (primary_node->primary().IsTypeParameter()) { array = LoadTypeParameter(primary_node); } else { UNREACHABLE(); // Internal parser error. } } selector = new (Z) LoadIndexedNode(bracket_pos, array, index, Class::ZoneHandle(Z)); } else if (IsArgumentPart()) { TypeArguments& func_type_args = TypeArguments::ZoneHandle(Z); if (CurrentToken() == Token::kLT) { // Type arguments. func_type_args = ParseTypeArguments(ClassFinalizer::kCanonicalize); if (Isolate::Current()->reify_generic_functions()) { if (!func_type_args.IsNull() && !func_type_args.IsInstantiated() && (FunctionLevel() > 0)) { // Make sure that the instantiators are captured. CaptureAllInstantiators(); } } else { func_type_args = TypeArguments::null(); } } if (left->IsPrimaryNode()) { PrimaryNode* primary_node = left->AsPrimaryNode(); const TokenPosition primary_pos = primary_node->token_pos(); if (primary_node->primary().IsFunction()) { const Function& func = Function::Cast(primary_node->primary()); const String& func_name = String::ZoneHandle(Z, func.name()); if (func.is_static()) { // Parse static function call. Class& cls = Class::Handle(Z, func.Owner()); selector = ParseStaticCall(cls, func_name, primary_pos, func_type_args); } else { // Dynamic function call on implicit "this" parameter. if (current_function().is_static()) { ReportError(primary_pos, "cannot access instance method '%s' " "from static function", func_name.ToCString()); } selector = ParseInstanceCall(LoadReceiver(primary_pos), func_name, primary_pos, func_type_args, false /* is_conditional */); } } else if (primary_node->primary().IsString()) { // Primary is an unresolved name. if (primary_node->IsSuper()) { ReportError(primary_pos, "illegal use of super"); } const String& name = String::Cast(Object::ZoneHandle(primary_node->primary().raw())); if (primary_node->is_deferred_reference()) { // The static call will be converted to throwing a NSM error. const Class& cls = Class::Handle(library_.toplevel_class()); selector = ParseStaticCall(cls, name, primary_pos, func_type_args, primary_node->prefix()); } else if (current_function().is_static()) { // The static call will be converted to throwing a NSM error. selector = ParseStaticCall(current_class(), name, primary_pos, func_type_args); } else { // Treat as call to unresolved (instance) method. selector = ParseInstanceCall(LoadReceiver(primary_pos), name, primary_pos, func_type_args, false /* is_conditional */); } } else if (primary_node->primary().IsTypeParameter()) { // The parsed type arguments (if any) are not parameterizing the type // parameter (this would be a compile-time error), but are passed as // the type arguments of a generic closure call with the type // parameter as the closure. This will result in a NSM call, whether // the call is generic or not. selector = LoadTypeParameter(primary_node); selector = ParseClosureCall(selector, func_type_args); } else if (primary_node->primary().IsClass()) { // The parsed type arguments (if any) are not parameterizing the // class, but are passed as the type arguments of a generic closure // call with the class as the closure. This will result in a NSM call, // whether the call is generic or not. const Class& type_class = Class::Cast(primary_node->primary()); AbstractType& type = Type::ZoneHandle( Z, Type::New(type_class, Object::null_type_arguments(), primary_pos, Heap::kOld)); type ^= CanonicalizeType(type); // Type may be malbounded, but not malformed. ASSERT(!type.IsMalformed()); selector = new (Z) TypeNode(primary_pos, type, primary_node->is_deferred_reference()); selector = ParseClosureCall(selector, func_type_args); } else { UNREACHABLE(); // Internal parser error. } } else { // Left is not a primary node; this must be a closure call. AstNode* closure = left; selector = ParseClosureCall(closure, func_type_args); } } else { // No (more) selectors to parse. left = LoadFieldIfUnresolved(left); if (left->IsPrimaryNode()) { PrimaryNode* primary_node = left->AsPrimaryNode(); const TokenPosition primary_pos = primary->token_pos(); if (primary_node->primary().IsFunction()) { // Treat as implicit closure. left = LoadClosure(primary_node); } else if (primary_node->primary().IsClass()) { const Class& type_class = Class::Cast(primary_node->primary()); AbstractType& type = Type::ZoneHandle( Z, Type::New(type_class, TypeArguments::Handle(Z), primary_pos, Heap::kOld)); type = CanonicalizeType(type); // Type may be malbounded, but not malformed. ASSERT(!type.IsMalformed()); left = new (Z) TypeNode(primary_pos, type, primary_node->is_deferred_reference()); } else if (primary_node->primary().IsTypeParameter()) { left = LoadTypeParameter(primary_node); } else if (primary_node->IsSuper()) { // Return "super" to handle unary super operator calls, // or to report illegal use of "super" otherwise. left = primary_node; } else { UNREACHABLE(); // Internal parser error. } } // Done parsing selectors. return left; } ASSERT(selector != NULL); left = selector; } } AstNode* Parser::ParsePostfixExpr() { TRACE_PARSER("ParsePostfixExpr"); String* expr_ident = Token::IsIdentifier(CurrentToken()) ? CurrentLiteral() : NULL; const TokenPosition expr_pos = TokenPos(); AstNode* expr = ParsePrimary(); expr = ParseSelectors(expr, false); if (IsIncrementOperator(CurrentToken())) { TRACE_PARSER("IncrementOperator"); if (!IsLegalAssignableSyntax(expr, TokenPos())) { ReportError(expr_pos, "expression is not assignable"); } Token::Kind incr_op = CurrentToken(); const TokenPosition op_pos = TokenPos(); ConsumeToken(); // Not prefix. LetNode* let_expr = PrepareCompoundAssignmentNodes(&expr); LocalVariable* temp = let_expr->AddInitializer(expr); Token::Kind binary_op = (incr_op == Token::kINCR) ? Token::kADD : Token::kSUB; BinaryOpNode* add = new (Z) BinaryOpNode( op_pos, binary_op, new (Z) LoadLocalNode(op_pos, temp), new (Z) LiteralNode(op_pos, Smi::ZoneHandle(Z, Smi::New(1)))); AstNode* store = CreateAssignmentNode(expr, add, expr_ident, expr_pos, true); ASSERT(store != NULL); // The result is a pair of the (side effects of the) store followed by // the (value of the) initial value temp variable load. let_expr->AddNode(store); let_expr->AddNode(new (Z) LoadLocalNode(op_pos, temp)); return let_expr; } return expr; } // Resolve the type parameters that may appear in the given signature from the // signature function and current class. // Unresolved type classes get resolved later by the class finalizer. void Parser::ResolveSignatureTypeParameters(const Function& signature) { const Function& saved_innermost_function = Function::Handle(Z, innermost_function().raw()); innermost_function_ = signature.raw(); AbstractType& type = AbstractType::Handle(); // Resolve upper bounds of function type parameters. const intptr_t num_type_params = signature.NumTypeParameters(); if (num_type_params > 0) { TypeParameter& type_param = TypeParameter::Handle(); const TypeArguments& type_params = TypeArguments::Handle(signature.type_parameters()); for (intptr_t i = 0; i < num_type_params; i++) { type_param ^= type_params.TypeAt(i); type = type_param.bound(); ResolveTypeParameters(&type); type_param.set_bound(type); } } // Resolve result type. type = signature.result_type(); ResolveTypeParameters(&type); signature.set_result_type(type); // Update type without scope change. // Resolve formal parameter types. const intptr_t num_parameters = signature.NumParameters(); for (intptr_t i = 0; i < num_parameters; i++) { type = signature.ParameterTypeAt(i); ResolveTypeParameters(&type); signature.SetParameterTypeAt(i, type); } innermost_function_ = saved_innermost_function.raw(); } // Resolve the type parameters that may appear in the given type and in its type // arguments from the current function and current class. // Unresolved type classes get resolved later by the class finalizer. void Parser::ResolveTypeParameters(AbstractType* type) { ASSERT(type != NULL); if (type->IsResolved()) { // Some types are resolved by definition, such as a TypeParameter. return; } // Resolve type class. if (!type->HasResolvedTypeClass()) { const UnresolvedClass& unresolved_class = UnresolvedClass::Handle(Z, type->unresolved_class()); const String& unresolved_class_name = String::Handle(Z, unresolved_class.ident()); if (unresolved_class.library_or_library_prefix() == Object::null()) { // First check if the type is a function type parameter. if (InGenericFunctionScope()) { intptr_t type_param_func_level = FunctionLevel(); TypeParameter& type_parameter = TypeParameter::ZoneHandle( Z, innermost_function().LookupTypeParameter( unresolved_class_name, &type_param_func_level)); if (!type_parameter.IsNull()) { // A type parameter cannot be parameterized, so make the type // malformed if type arguments have previously been parsed. if (type->arguments() != TypeArguments::null()) { *type = ClassFinalizer::NewFinalizedMalformedType( Error::Handle(Z), // No previous error. script_, type_parameter.token_pos(), "type parameter '%s' cannot be parameterized", String::Handle(Z, type_parameter.name()).ToCString()); return; } if (Isolate::Current()->reify_generic_functions()) { ASSERT(!type_parameter.IsMalformed()); *type = type_parameter.raw(); } else { *type = Type::DynamicType(); } return; } } // Then check if the type is a class type parameter. const TypeParameter& type_parameter = TypeParameter::Handle( Z, current_class().LookupTypeParameter(unresolved_class_name)); if (!type_parameter.IsNull()) { // A type parameter is considered to be a malformed type when // referenced by a static member. if (ParsingStaticMember()) { *type = ClassFinalizer::NewFinalizedMalformedType( Error::Handle(Z), // No previous error. script_, type->token_pos(), "type parameter '%s' cannot be referenced " "from static member", String::Handle(Z, type_parameter.name()).ToCString()); return; } // A type parameter cannot be parameterized, so make the type // malformed if type arguments have previously been parsed. if (type->arguments() != TypeArguments::null()) { *type = ClassFinalizer::NewFinalizedMalformedType( Error::Handle(Z), // No previous error. script_, type_parameter.token_pos(), "type parameter '%s' cannot be parameterized", String::Handle(Z, type_parameter.name()).ToCString()); return; } *type = type_parameter.raw(); return; } } } // Resolve type arguments, if any. if (type->arguments() != TypeArguments::null()) { const TypeArguments& arguments = TypeArguments::Handle(Z, type->arguments()); // Already resolved if canonical. if (!arguments.IsCanonical()) { const intptr_t num_arguments = arguments.Length(); AbstractType& type_argument = AbstractType::Handle(Z); for (intptr_t i = 0; i < num_arguments; i++) { type_argument = arguments.TypeAt(i); ResolveTypeParameters(&type_argument); arguments.SetTypeAt(i, type_argument); } } } if (type->IsFunctionType()) { const Function& signature = Function::Handle(Z, Type::Cast(*type).signature()); Type& signature_type = Type::Handle(Z, signature.SignatureType()); if (signature_type.raw() != type->raw()) { ResolveTypeParameters(&signature_type); } else { ResolveSignatureTypeParameters(signature); } } } RawAbstractType* Parser::CanonicalizeType(const AbstractType& type) { // If the current class is the result of a mixin application, we must // use the class scope of the class from which the function originates. if (current_class().IsMixinApplication()) { return ClassFinalizer::FinalizeType( Class::Handle(Z, parsed_function()->function().origin()), type); } return ClassFinalizer::FinalizeType(current_class(), type); } LocalVariable* Parser::LookupLocalScope(const String& ident) { if (current_block_ == NULL) { return NULL; } // A found name is treated as accessed and possibly marked as captured. const bool kTestOnly = false; return current_block_->scope->LookupVariable(ident, kTestOnly); } void Parser::CheckInstanceFieldAccess(TokenPosition field_pos, const String& field_name) { // Fields are not accessible from a static function, except from a // constructor, which is considered as non-static by the compiler. if (current_function().is_static()) { ReportError(field_pos, "cannot access instance field '%s' from a static function", field_name.ToCString()); } } bool Parser::ParsingStaticMember() const { if (is_top_level_) { return (current_member_ != NULL) && current_member_->has_static && !current_member_->has_factory; } ASSERT(!current_function().IsNull()); return current_function().is_static() && !current_function().IsInFactoryScope(); } const AbstractType* Parser::ReceiverType(const Class& cls) { ASSERT(!cls.IsNull()); ASSERT(!cls.IsTypedefClass()); // Note that if cls is _Closure, the returned type will be _Closure, // and not the signature type. Type& type = Type::ZoneHandle(Z, cls.CanonicalType()); if (!type.IsNull()) { return &type; } type = Type::New(cls, TypeArguments::Handle(Z, cls.type_parameters()), cls.token_pos(), Heap::kOld); if (cls.is_type_finalized()) { type ^= ClassFinalizer::FinalizeType(cls, type); // Note that the receiver type may now be a malbounded type. cls.SetCanonicalType(type); } return &type; } bool Parser::IsInstantiatorRequired() const { ASSERT(!current_function().IsNull()); if (current_function().is_static() && !current_function().IsInFactoryScope()) { return false; } return current_class().IsGeneric(); } bool Parser::InGenericFunctionScope() const { if (!innermost_function().IsNull()) { // With one more free tag bit in Function, we could cache this information. if (innermost_function().IsGeneric() || innermost_function().HasGenericParent()) { return true; } } return false; } void Parser::InsertCachedConstantValue(const Script& script, TokenPosition token_pos, const Instance& value) { ASSERT(Thread::Current()->IsMutatorThread()); const intptr_t kInitialConstMapSize = 16; ASSERT(!script.InVMHeap()); if (script.compile_time_constants() == Array::null()) { const Array& array = Array::Handle( HashTables::New(kInitialConstMapSize, Heap::kOld)); script.set_compile_time_constants(array); } ConstantsMap constants(script.compile_time_constants()); constants.InsertNewOrGetValue(token_pos, value); script.set_compile_time_constants(constants.Release()); } void Parser::CacheConstantValue(TokenPosition token_pos, const Instance& value) { if (current_function().kind() == RawFunction::kImplicitStaticFinalGetter) { // Don't cache constants in initializer expressions. They get // evaluated only once. return; } InsertCachedConstantValue(script_, token_pos, value); INC_STAT(thread_, num_cached_consts, 1); } bool Parser::GetCachedConstant(TokenPosition token_pos, Instance* value) { bool is_present = false; ASSERT(!script_.InVMHeap()); if (script_.compile_time_constants() == Array::null()) { return false; } ConstantsMap constants(script_.compile_time_constants()); *value ^= constants.GetOrNull(token_pos, &is_present); // Mutator compiler thread may add constants while background compiler // is running, and thus change the value of 'compile_time_constants'; // do not assert that 'compile_time_constants' has not changed. constants.Release(); if (FLAG_compiler_stats && is_present) { thread_->compiler_stats()->num_const_cache_hits++; } return is_present; } RawInstance* Parser::TryCanonicalize(const Instance& instance, TokenPosition token_pos) { if (instance.IsNull()) { return instance.raw(); } const char* error_str = NULL; Instance& result = Instance::Handle(Z, instance.CheckAndCanonicalize(thread(), &error_str)); if (result.IsNull()) { ReportError(token_pos, "Invalid const object %s", error_str); } return result.raw(); } // If the field is already initialized, return no ast (NULL). // Otherwise, if the field is constant, initialize the field and return no ast. // If the field is not initialized and not const, return the ast for the getter. StaticGetterNode* Parser::RunStaticFieldInitializer( const Field& field, TokenPosition field_ref_pos) { ASSERT(field.is_static()); const Class& field_owner = Class::ZoneHandle(Z, field.Owner()); const String& field_name = String::ZoneHandle(Z, field.name()); const String& getter_name = String::Handle(Z, Field::GetterSymbol(field_name)); const Function& getter = Function::Handle(Z, field_owner.LookupStaticFunction(getter_name)); const Instance& value = Instance::Handle(Z, field.StaticValue()); if (value.raw() == Object::transition_sentinel().raw()) { if (field.is_const()) { ReportError("circular dependency while initializing static field '%s'", field_name.ToCString()); } else { // The implicit static getter will throw the exception if necessary. return new (Z) StaticGetterNode(field_ref_pos, NULL, field_owner, field_name, StaticGetterSetter::kStatic); } } else if (value.raw() == Object::sentinel().raw()) { // This field has not been referenced yet and thus the value has // not been evaluated. If the field is const, call the static getter method // to evaluate the expression and canonicalize the value. if (field.is_const()) { NoReloadScope no_reload_scope(isolate(), thread()); NoOOBMessageScope no_msg_scope(thread()); field.SetStaticValue(Object::transition_sentinel()); const int kTypeArgsLen = 0; // No type argument vector. const int kNumArguments = 0; // No arguments. const Function& func = Function::Handle( Z, Resolver::ResolveStatic(field_owner, getter_name, kTypeArgsLen, kNumArguments, Object::empty_array())); ASSERT(!func.IsNull()); ASSERT(func.kind() == RawFunction::kImplicitStaticFinalGetter); Object& const_value = Object::Handle(Z); const_value = DartEntry::InvokeFunction(func, Object::empty_array()); if (const_value.IsError()) { const Error& error = Error::Cast(const_value); if (error.IsUnhandledException()) { // An exception may not occur in every parse attempt, i.e., the // generated AST is not deterministic. Therefore mark the function as // not optimizable. current_function().SetIsOptimizable(false); field.SetStaticValue(Object::null_instance()); // It is a compile-time error if evaluation of a compile-time constant // would raise an exception. const String& field_name = String::Handle(Z, field.name()); ReportErrors(error, script_, field_ref_pos, "error initializing const field '%s'", field_name.ToCString()); } else { ReportError(error); } UNREACHABLE(); } ASSERT(const_value.IsNull() || const_value.IsInstance()); Instance& instance = Instance::Handle(Z); instance ^= const_value.raw(); instance = TryCanonicalize(instance, field_ref_pos); field.SetStaticValue(instance); return NULL; // Constant } else { return new (Z) StaticGetterNode(field_ref_pos, NULL, field_owner, field_name, StaticGetterSetter::kStatic); } } if (getter.IsNull() || (getter.kind() == RawFunction::kImplicitStaticFinalGetter)) { return NULL; } ASSERT(getter.kind() == RawFunction::kImplicitGetter); return new (Z) StaticGetterNode(field_ref_pos, NULL, field_owner, field_name, StaticGetterSetter::kStatic); } RawObject* Parser::EvaluateConstConstructorCall( const Class& type_class, const TypeArguments& type_arguments, const Function& constructor, ArgumentListNode* arguments, bool obfuscate_symbol_instances /* = true */) { NoReloadScope no_reload_scope(isolate(), thread()); NoOOBMessageScope no_msg_scope(thread()); // Factories and constructors are not generic functions. const int kTypeArgsLen = 0; // Factories have one extra argument: the type arguments. // Constructors have one extra arguments: receiver. const int kNumExtraArgs = 1; const int num_arguments = arguments->length() + kNumExtraArgs; const Array& arg_values = Array::Handle(Z, Array::New(num_arguments, allocation_space_)); Instance& instance = Instance::Handle(Z); if (!constructor.IsFactory()) { instance = Instance::New(type_class, allocation_space_); if (!type_arguments.IsNull()) { if (!type_arguments.IsInstantiated()) { ReportError("type must be constant in const constructor"); } instance.SetTypeArguments( TypeArguments::Handle(Z, type_arguments.Canonicalize())); } arg_values.SetAt(0, instance); } else { // Prepend type_arguments to list of arguments to factory. ASSERT(type_arguments.IsZoneHandle()); arg_values.SetAt(0, type_arguments); } for (int i = 0; i < arguments->length(); i++) { AstNode* arg = arguments->NodeAt(i); // Arguments have been evaluated to a literal value already. ASSERT(arg->IsLiteralNode()); arg_values.SetAt((i + kNumExtraArgs), arg->AsLiteralNode()->literal()); } const Array& args_descriptor = Array::Handle(Z, ArgumentsDescriptor::New(kTypeArgsLen, num_arguments, arguments->names())); const Object& result = Object::Handle( Z, DartEntry::InvokeFunction(constructor, arg_values, args_descriptor)); if (result.IsError()) { // An exception may not occur in every parse attempt, i.e., the // generated AST is not deterministic. Therefore mark the function as // not optimizable. current_function().SetIsOptimizable(false); if (result.IsUnhandledException()) { return result.raw(); } else { thread()->long_jump_base()->Jump(1, Error::Cast(result)); UNREACHABLE(); return Object::null(); } } else { if (constructor.IsFactory()) { // The factory method returns the allocated object. instance ^= result.raw(); } if (obfuscate_symbol_instances && I->obfuscate() && (instance.clazz() == I->object_store()->symbol_class())) { Obfuscator::ObfuscateSymbolInstance(T, instance); } return TryCanonicalize(instance, TokenPos()); } } // Do a lookup for the identifier in the block scope and the class scope // return true if the identifier is found, false otherwise. // If node is non NULL return an AST node corresponding to the identifier. bool Parser::ResolveIdentInLocalScope(TokenPosition ident_pos, const String& ident, AstNode** node, intptr_t* function_level) { TRACE_PARSER("ResolveIdentInLocalScope"); // First try to find the identifier in the nested local scopes. LocalVariable* local = LookupLocalScope(ident); if (current_block_ != NULL) { current_block_->scope->AddReferencedName(ident_pos, ident); } if (local != NULL) { if (node != NULL) { *node = new (Z) LoadLocalNode(ident_pos, local); } if (function_level != NULL) { *function_level = local->owner()->function_level(); } return true; } // If we are compiling top-level code, we don't need to look for // the identifier in the current (top-level) class. The class scope // of the top-level class is part of the library scope. if (current_class().IsTopLevel()) { if (node != NULL) { *node = NULL; } return false; } // Try to find the identifier in the class scope of the current class. // If the current class is the result of a mixin application, we must // use the class scope of the class from which the function originates. Class& cls = Class::Handle(Z); if (!current_class().IsMixinApplication()) { cls = current_class().raw(); } else { cls = parsed_function()->function().origin(); } Function& func = Function::Handle(Z, Function::null()); Field& field = Field::Handle(Z, Field::null()); // First check if a field exists. field = cls.LookupField(ident); if (!field.IsNull()) { if (node != NULL) { if (!field.is_static()) { CheckInstanceFieldAccess(ident_pos, ident); *node = CallGetter(ident_pos, LoadReceiver(ident_pos), ident); } else { *node = GenerateStaticFieldLookup(field, ident_pos); } } if (function_level != NULL) { *function_level = 0; } return true; } // Check if an instance/static function exists. func = cls.LookupFunction(ident); if (!func.IsNull() && (func.IsDynamicFunction() || func.IsStaticFunction() || func.is_abstract())) { if (node != NULL) { *node = new (Z) PrimaryNode(ident_pos, Function::ZoneHandle(Z, func.raw())); } return true; } // Now check if a getter/setter method exists for it in which case // it is still a field. // A setter without a corresponding getter binds to the non-existing // getter. (The getter could be followed by an assignment which will // convert it to a setter node. If there is no assignment the non-existing // getter will throw a NoSuchMethodError.) func = cls.LookupGetterFunction(ident); if (func.IsNull()) { func = cls.LookupSetterFunction(ident); } if (!func.IsNull()) { if (func.IsDynamicFunction() || func.is_abstract()) { if (node != NULL) { CheckInstanceFieldAccess(ident_pos, ident); ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved()); *node = CallGetter(ident_pos, LoadReceiver(ident_pos), ident); } return true; } else if (func.IsStaticFunction()) { if (node != NULL) { *node = new (Z) StaticGetterNode(ident_pos, NULL, Class::ZoneHandle(Z, cls.raw()), ident, StaticGetterSetter::kStatic); } return true; } } // Nothing found in scope of current class. if (node != NULL) { *node = NULL; } return false; } // Resolve an identifier by checking the global scope of the current // library. If not found in the current library, then look in the scopes // of all libraries that are imported without a library prefix. AstNode* Parser::ResolveIdentInCurrentLibraryScope(TokenPosition ident_pos, const String& ident) { TRACE_PARSER("ResolveIdentInCurrentLibraryScope"); HANDLESCOPE(thread()); const Object& obj = Object::Handle(Z, library_.ResolveName(ident)); if (obj.IsClass()) { const Class& cls = Class::Cast(obj); return new (Z) PrimaryNode(ident_pos, Class::ZoneHandle(Z, cls.raw())); } else if (obj.IsField()) { const Field& field = Field::Cast(obj); ASSERT(field.is_static()); AstNode* get_field = GenerateStaticFieldLookup(field, ident_pos); if (get_field->IsStaticGetterNode()) { get_field->AsStaticGetterNode()->set_owner(library_); } return get_field; } else if (obj.IsFunction()) { const Function& func = Function::Cast(obj); ASSERT(func.is_static()); if (func.IsGetterFunction() || func.IsSetterFunction()) { StaticGetterNode* getter = new (Z) StaticGetterNode( ident_pos, /* receiver */ NULL, Class::ZoneHandle(Z, func.Owner()), ident, StaticGetterSetter::kStatic); getter->set_owner(library_); return getter; } else { return new (Z) PrimaryNode(ident_pos, Function::ZoneHandle(Z, func.raw())); } } else if (obj.IsLibraryPrefix()) { const LibraryPrefix& prefix = LibraryPrefix::Cast(obj); ReportError(ident_pos, "illegal use of library prefix '%s'", String::Handle(prefix.name()).ToCString()); } else { ASSERT(obj.IsNull()); } // Lexically unresolved primary identifiers are referenced by their name. return new (Z) PrimaryNode(ident_pos, ident); } // Do a lookup for the identifier in the scope of the specified // library prefix. This means trying to resolve it locally in all of the // libraries present in the library prefix. AstNode* Parser::ResolveIdentInPrefixScope(TokenPosition ident_pos, const LibraryPrefix& prefix, const String& ident) { TRACE_PARSER("ResolveIdentInPrefixScope"); HANDLESCOPE(thread()); if (ident.CharAt(0) == Library::kPrivateIdentifierStart) { // Private names are not exported by libraries. The name mangling // of private names with a library-specific suffix usually ensures // that _x in library A is not found when looked up from library B. // In the pathological case where a library imports itself with // a prefix, the name mangling would not help in hiding the private // name, so we need to explicitly reject private names here. return NULL; } Object& obj = Object::Handle(Z); if (prefix.is_loaded() || FLAG_load_deferred_eagerly) { obj = prefix.LookupObject(ident); } else { // Remember that this function depends on an import prefix of an // unloaded deferred library. Note that parsed_function() can be // NULL when parsing expressions outside the scope of a function. if (parsed_function() != NULL) { parsed_function()->AddDeferredPrefix(prefix); } } const bool is_deferred = prefix.is_deferred_load(); if (obj.IsNull()) { // Unresolved prefixed primary identifier. return NULL; } else if (obj.IsClass()) { const Class& cls = Class::Cast(obj); PrimaryNode* primary = new (Z) PrimaryNode(ident_pos, Class::ZoneHandle(Z, cls.raw())); if (is_deferred) { primary->set_prefix(&prefix); } return primary; } else if (obj.IsField()) { const Field& field = Field::Cast(obj); ASSERT(field.is_static()); AstNode* get_field = GenerateStaticFieldLookup(field, ident_pos); ASSERT(get_field != NULL); ASSERT(get_field->IsLoadStaticFieldNode() || get_field->IsStaticGetterNode()); if (get_field->IsLoadStaticFieldNode()) { get_field->AsLoadStaticFieldNode()->set_is_deferred(is_deferred); } else if (get_field->IsStaticGetterNode()) { get_field->AsStaticGetterNode()->set_is_deferred(is_deferred); get_field->AsStaticGetterNode()->set_owner(prefix); } return get_field; } else if (obj.IsFunction()) { const Function& func = Function::Cast(obj); ASSERT(func.is_static()); if (func.IsGetterFunction() || func.IsSetterFunction()) { StaticGetterNode* getter = new (Z) StaticGetterNode( ident_pos, /* receiver */ NULL, Class::ZoneHandle(Z, func.Owner()), ident, StaticGetterSetter::kStatic); getter->set_is_deferred(is_deferred); getter->set_owner(prefix); return getter; } else { PrimaryNode* primary = new (Z) PrimaryNode(ident_pos, Function::ZoneHandle(Z, func.raw())); if (is_deferred) { primary->set_prefix(&prefix); } return primary; } } // All possible object types are handled above. UNREACHABLE(); return NULL; } // Resolve identifier. Issue an error message if // the ident refers to a method and allow_closure_names is false. // If the name cannot be resolved, turn it into an instance field access // if we're compiling an instance method, or generate // throw NoSuchMethodError if we're compiling a static method. AstNode* Parser::ResolveIdent(TokenPosition ident_pos, const String& ident, bool allow_closure_names) { TRACE_PARSER("ResolveIdent"); // First try to find the variable in the local scope (block scope or // class scope). AstNode* resolved = NULL; intptr_t resolved_func_level = 0; ResolveIdentInLocalScope(ident_pos, ident, &resolved, &resolved_func_level); if (InGenericFunctionScope()) { intptr_t type_param_func_level = FunctionLevel(); const TypeParameter& type_parameter = TypeParameter::ZoneHandle(Z, innermost_function().LookupTypeParameter( ident, &type_param_func_level)); if (!type_parameter.IsNull()) { if ((resolved == NULL) || (resolved_func_level < type_param_func_level)) { // The identifier is a function type parameter, possibly shadowing // 'resolved'. if (!Isolate::Current()->reify_generic_functions()) { Type& type = Type::ZoneHandle(Z, Type::DynamicType()); return new (Z) TypeNode(ident_pos, type); } ASSERT(type_parameter.IsFinalized()); ASSERT(!type_parameter.IsMalformed()); if ((FunctionLevel() > 0) && innermost_function().HasGenericParent()) { // Make sure that the parent function type arguments are captured. CaptureFunctionTypeArguments(); } return new (Z) TypeNode(ident_pos, type_parameter); } } } if (resolved == NULL) { // Check whether the identifier is a class type parameter. if (!current_class().IsNull()) { TypeParameter& type_parameter = TypeParameter::ZoneHandle( Z, current_class().LookupTypeParameter(ident)); if (!type_parameter.IsNull()) { if (ParsingStaticMember()) { const String& name = String::Handle(Z, type_parameter.name()); ReportError(ident_pos, "cannot access type parameter '%s' " "from static function", name.ToCString()); } type_parameter ^= CanonicalizeType(type_parameter); ASSERT(!type_parameter.IsMalformed()); if (FunctionLevel() > 0) { // Make sure that the class instantiator is captured. CaptureInstantiator(); } return new (Z) TypeNode(ident_pos, type_parameter); } } // Not found in the local scope, and the name is not a type parameter. // Try finding the variable in the library scope (current library // and all libraries imported by it without a library prefix). resolved = ResolveIdentInCurrentLibraryScope(ident_pos, ident); } if (resolved->IsPrimaryNode()) { PrimaryNode* primary = resolved->AsPrimaryNode(); const TokenPosition primary_pos = primary->token_pos(); if (primary->primary().IsString()) { // We got an unresolved name. If we are compiling a static // method, evaluation of an unresolved identifier causes a // NoSuchMethodError to be thrown. In an instance method, we convert // the unresolved name to an instance field access, since a // subclass might define a field with this name. if (current_function().is_static()) { resolved = ThrowNoSuchMethodError(ident_pos, current_class(), ident, NULL, // No arguments. InvocationMirror::kStatic, InvocationMirror::kField, NULL); // No existing function. } else { // Treat as call to unresolved instance field. resolved = CallGetter(ident_pos, LoadReceiver(ident_pos), ident); } } else if (primary->primary().IsFunction()) { if (allow_closure_names) { resolved = LoadClosure(primary); } else { ReportError(ident_pos, "illegal reference to method '%s'", ident.ToCString()); } } else if (primary->primary().IsClass()) { const Class& type_class = Class::Cast(primary->primary()); AbstractType& type = Type::ZoneHandle(Z, Type::New(type_class, TypeArguments::Handle(Z), primary_pos, Heap::kOld)); type ^= CanonicalizeType(type); // Type may be malbounded, but not malformed. ASSERT(!type.IsMalformed()); resolved = new (Z) TypeNode(primary_pos, type, primary->is_deferred_reference()); } } return resolved; } RawAbstractType* Parser::ParseType( ClassFinalizer::FinalizationKind finalization, bool allow_deferred_type, bool consume_unresolved_prefix) { LibraryPrefix& prefix = LibraryPrefix::Handle(Z); return ParseType(finalization, allow_deferred_type, consume_unresolved_prefix, &prefix); } // Parses and returns a type or a function type. RawAbstractType* Parser::ParseTypeOrFunctionType( bool allow_void, ClassFinalizer::FinalizationKind finalization) { TRACE_PARSER("ParseTypeOrFunctionType"); AbstractType& type = AbstractType::Handle(Z); if (CurrentToken() == Token::kVOID) { TokenPosition void_pos = TokenPos(); type = Type::VoidType(); ConsumeToken(); // 'void' is always allowed as result type of a function type. if (!allow_void && !IsFunctionTypeSymbol()) { ReportError(void_pos, "'void' not allowed here"); } } else if (!IsFunctionTypeSymbol()) { // Including 'Function' not followed by '(' or '<'. // It is too early to resolve the type here, since it can // refer to a not yet declared function type parameter. type = ParseType(ClassFinalizer::kDoNotResolve); } while (IsFunctionTypeSymbol()) { if (type.IsNull()) { type = Type::DynamicType(); } // 'type' is the result type of the function type. type = ParseFunctionType(type, ClassFinalizer::kDoNotResolve); } // At this point, all type parameters have been parsed, resolve the type. if (finalization == ClassFinalizer::kIgnore) { return Type::DynamicType(); } if (finalization >= ClassFinalizer::kResolveTypeParameters) { ResolveTypeParameters(&type); if (finalization >= ClassFinalizer::kCanonicalize) { type ^= CanonicalizeType(type); } } return type.raw(); } // Parses and returns a function type. // If 'result_type' is not null, parsing of the result type is skipped. RawType* Parser::ParseFunctionType( const AbstractType& result_type, ClassFinalizer::FinalizationKind finalization) { TRACE_PARSER("ParseFunctionType"); AbstractType& type = AbstractType::Handle(Z, result_type.raw()); if (type.IsNull()) { if (CurrentToken() == Token::kVOID) { ConsumeToken(); type = Type::VoidType(); } else if (IsFunctionTypeSymbol()) { type = Type::DynamicType(); } else { // Including 'Function' not followed by '(' or '<'. // It is too early to resolve the type here, since it can // refer to a not yet declared function type parameter. type = ParseType(ClassFinalizer::kDoNotResolve); } } if (!IsSymbol(Symbols::Function())) { ReportError("'Function' expected"); } do { ConsumeToken(); const Function& signature_function = Function::Handle( Z, Function::NewSignatureFunction(current_class(), innermost_function(), TokenPosition::kNoSource)); innermost_function_ = signature_function.raw(); signature_function.set_result_type(type); // The result type may refer to the signature function's type parameters, // but was not parsed in the scope of the signature function. Adjust. type.SetScopeFunction(signature_function); // Parse optional type parameters. if (CurrentToken() == Token::kLT) { ParseTypeParameters(false); // Not parameterizing class, but function. } ParamList params; // We do not yet allow Function of any arity, so expect parameter list. CheckToken(Token::kLPAREN, "formal parameter list expected"); // Add implicit closure object parameter. Do not specify a token position, // since it would make no sense after function type canonicalization. params.AddFinalParameter(TokenPosition::kNoSource, &Symbols::ClosureParameter(), &Object::dynamic_type()); const bool use_function_type_syntax = true; const bool allow_explicit_default_values = false; const bool evaluate_metadata = false; ParseFormalParameterList(use_function_type_syntax, allow_explicit_default_values, evaluate_metadata, ¶ms); AddFormalParamsToFunction(¶ms, signature_function); innermost_function_ = innermost_function_.parent_function(); if (innermost_function().IsNull() && current_class().IsTypedefClass() && !IsFunctionTypeSymbol()) { // The last parsed signature function is the typedef signature function. // Set it in the typedef class before building the signature type. current_class().set_signature_function(signature_function); } type = signature_function.SignatureType(); } while (IsFunctionTypeSymbol()); // At this point, all type parameters have been parsed, resolve the type. if (finalization == ClassFinalizer::kIgnore) { return Type::DynamicType(); } if (finalization >= ClassFinalizer::kResolveTypeParameters) { ResolveTypeParameters(&type); if (finalization >= ClassFinalizer::kCanonicalize) { type ^= CanonicalizeType(type); } } return Type::RawCast(type.raw()); } // Parses type = [ident "."] ident ["<" type { "," type } ">"], then resolve and // finalize it according to the given type finalization mode. // Returns type and sets prefix. RawAbstractType* Parser::ParseType( ClassFinalizer::FinalizationKind finalization, bool allow_deferred_type, bool consume_unresolved_prefix, LibraryPrefix* prefix) { TRACE_PARSER("ParseType"); CheckToken(Token::kIDENT, "type name expected"); TokenPosition ident_pos = TokenPos(); String& type_name = String::Handle(Z); if (finalization == ClassFinalizer::kIgnore) { if (!is_top_level_ && (current_block_ != NULL)) { // Add the library prefix or type class name to the list of referenced // names of this scope, even if the type is ignored. current_block_->scope->AddReferencedName(TokenPos(), *CurrentLiteral()); } SkipQualIdent(); } else { *prefix = ParsePrefix(); if (!prefix->IsNull()) { ExpectToken(Token::kPERIOD); } type_name = CurrentLiteral()->raw(); ConsumeToken(); // Check whether we have a malformed qualified type name if the caller // requests to consume unresolved prefix names: // If we didn't see a valid prefix but the identifier is followed by // a period and another identifier, consume the qualified identifier // and create a malformed type. if (consume_unresolved_prefix && prefix->IsNull() && (CurrentToken() == Token::kPERIOD) && (Token::IsIdentifier(LookaheadToken(1)))) { if (!is_top_level_ && (current_block_ != NULL)) { // Add the unresolved prefix name to the list of referenced // names of this scope. current_block_->scope->AddReferencedName(TokenPos(), type_name); } ConsumeToken(); // Period token. ASSERT(IsIdentifier()); String& qualified_name = String::Handle(Z, type_name.raw()); qualified_name = String::Concat(qualified_name, Symbols::Dot(), allocation_space_); qualified_name = String::Concat(qualified_name, *CurrentLiteral(), allocation_space_); ConsumeToken(); // The type is malformed. Skip over its type arguments. ParseTypeArguments(ClassFinalizer::kIgnore); return ClassFinalizer::NewFinalizedMalformedType( Error::Handle(Z), // No previous error. script_, ident_pos, "qualified name '%s' does not refer to a type", qualified_name.ToCString()); } // If parsing inside a local scope, check whether the type name // is shadowed by a local declaration. if (!is_top_level_ && (prefix->IsNull()) && ResolveIdentInLocalScope(ident_pos, type_name, NULL, NULL)) { // The type is malformed. Skip over its type arguments. ParseTypeArguments(ClassFinalizer::kIgnore); return ClassFinalizer::NewFinalizedMalformedType( Error::Handle(Z), // No previous error. script_, ident_pos, "using '%s' in this context is invalid", type_name.ToCString()); } if ((!FLAG_load_deferred_eagerly || !allow_deferred_type) && !prefix->IsNull() && prefix->is_deferred_load()) { // If deferred prefixes are allowed but it is not yet loaded, // remember that this function depends on the prefix. if (allow_deferred_type && !prefix->is_loaded()) { if (parsed_function() != NULL) { parsed_function()->AddDeferredPrefix(*prefix); } } // If the deferred prefixes are not allowed, or if the prefix is not yet // loaded when finalization is requested, return a malformed type. // Otherwise, handle resolution below, as needed. if (!allow_deferred_type || (!prefix->is_loaded() && (finalization > ClassFinalizer::kResolveTypeParameters))) { ParseTypeArguments(ClassFinalizer::kIgnore); return ClassFinalizer::NewFinalizedMalformedType( Error::Handle(Z), // No previous error. script_, ident_pos, !prefix->is_loaded() && allow_deferred_type ? "deferred type '%s.%s' is not yet loaded" : "using deferred type '%s.%s' is invalid", String::Handle(Z, prefix->name()).ToCString(), type_name.ToCString()); } } } Object& type_class = Object::Handle(Z); // Leave type_class as null if type finalization mode is kIgnore. if (finalization != ClassFinalizer::kIgnore) { type_class = UnresolvedClass::New(*prefix, type_name, ident_pos); } TypeArguments& type_arguments = TypeArguments::Handle(Z, ParseTypeArguments(finalization)); if (finalization == ClassFinalizer::kIgnore) { return Type::DynamicType(); } AbstractType& type = AbstractType::Handle( Z, Type::New(type_class, type_arguments, ident_pos, Heap::kOld)); if (finalization >= ClassFinalizer::kResolveTypeParameters) { ResolveTypeParameters(&type); if (finalization >= ClassFinalizer::kCanonicalize) { type ^= CanonicalizeType(type); } } return type.raw(); } void Parser::CheckConstructorCallTypeArguments( TokenPosition pos, const Function& constructor, const TypeArguments& type_arguments) { if (!type_arguments.IsNull()) { const Class& constructor_class = Class::Handle(Z, constructor.Owner()); ASSERT(!constructor_class.IsNull()); ASSERT(constructor_class.is_finalized()); ASSERT(type_arguments.IsCanonical()); // Do not report the expected vs. actual number of type arguments, because // the type argument vector is flattened and raw types are allowed. if (type_arguments.Length() != constructor_class.NumTypeArguments()) { ReportError(pos, "wrong number of type arguments passed to constructor"); } } } // Parse "[" [ expr { "," expr } ["," ] "]". // Note: if the list literal is empty and the brackets have no whitespace // between them, the scanner recognizes the opening and closing bracket // as one token of type Token::kINDEX. AstNode* Parser::ParseListLiteral(TokenPosition type_pos, bool is_const, const TypeArguments& type_arguments) { TRACE_PARSER("ParseListLiteral"); ASSERT(type_pos.IsReal()); ASSERT(CurrentToken() == Token::kLBRACK || CurrentToken() == Token::kINDEX); const TokenPosition literal_pos = TokenPos(); if (is_const) { Instance& existing_const = Instance::ZoneHandle(Z); if (GetCachedConstant(literal_pos, &existing_const)) { SkipListLiteral(); return new (Z) LiteralNode(literal_pos, existing_const); } } bool is_empty_literal = CurrentToken() == Token::kINDEX; ConsumeToken(); AbstractType& element_type = Type::ZoneHandle(Z, Type::DynamicType()); TypeArguments& list_type_arguments = TypeArguments::ZoneHandle(Z, type_arguments.raw()); // If no type argument vector is provided, leave it as null, which is // equivalent to using dynamic as the type argument for the element type. if (!list_type_arguments.IsNull()) { ASSERT(list_type_arguments.Length() > 0); // List literals take a single type argument. if (list_type_arguments.Length() == 1) { element_type = list_type_arguments.TypeAt(0); ASSERT(!element_type.IsMalformed()); // Would be mapped to dynamic. ASSERT(!element_type.IsMalbounded()); // No declared bound in List. if (element_type.IsDynamicType()) { list_type_arguments = TypeArguments::null(); } else if (is_const && !element_type.IsInstantiated()) { ReportError(type_pos, "the type argument of a constant list literal cannot " "include a type variable"); } } else { if (I->error_on_bad_type()) { ReportError(type_pos, "a list literal takes one type argument specifying " "the element type"); } // Ignore type arguments. list_type_arguments = TypeArguments::null(); } } ASSERT(list_type_arguments.IsNull() || (list_type_arguments.Length() == 1)); const Class& array_class = Class::Handle(Z, I->object_store()->array_class()); Type& type = Type::ZoneHandle( Z, Type::New(array_class, list_type_arguments, type_pos, Heap::kOld)); type ^= CanonicalizeType(type); GrowableArray element_list; // Parse the list elements. Note: there may be an optional extra // comma after the last element. if (!is_empty_literal) { const bool saved_mode = SetAllowFunctionLiterals(true); while (CurrentToken() != Token::kRBRACK) { const TokenPosition element_pos = TokenPos(); AstNode* element = ParseExpr(is_const, kConsumeCascades); if (I->type_checks() && !is_const && !element_type.IsDynamicType()) { element = new (Z) AssignableNode(element_pos, element, element_type, Symbols::ListLiteralElement()); } element_list.Add(element); if (CurrentToken() == Token::kCOMMA) { ConsumeToken(); } else if (CurrentToken() != Token::kRBRACK) { ReportError("comma or ']' expected"); } } ExpectToken(Token::kRBRACK); SetAllowFunctionLiterals(saved_mode); } if (is_const) { // Allocate and initialize the const list at compile time. if ((element_list.length() == 0) && list_type_arguments.IsNull()) { return new (Z) LiteralNode(literal_pos, Object::empty_array()); } Array& const_list = Array::ZoneHandle(Z, Array::New(element_list.length(), Heap::kOld)); const_list.SetTypeArguments( TypeArguments::Handle(Z, list_type_arguments.Canonicalize())); Error& bound_error = Error::Handle(Z); for (int i = 0; i < element_list.length(); i++) { AstNode* elem = element_list[i]; // Arguments have been evaluated to a literal value already. ASSERT(elem->IsLiteralNode()); ASSERT(!is_top_level_); // We cannot check unresolved types. if (I->type_checks() && !element_type.IsDynamicType() && (!elem->AsLiteralNode()->literal().IsNull() && !elem->AsLiteralNode()->literal().IsInstanceOf( element_type, Object::null_type_arguments(), Object::null_type_arguments(), &bound_error))) { // If the failure is due to a bound error, display it instead. if (!bound_error.IsNull()) { ReportError(bound_error); } else { ReportError( elem->AsLiteralNode()->token_pos(), "list literal element at index %d must be " "a constant of type '%s'", i, String::Handle(Z, element_type.UserVisibleName()).ToCString()); } } const_list.SetAt(i, elem->AsLiteralNode()->literal()); } const_list.MakeImmutable(); const_list ^= TryCanonicalize(const_list, literal_pos); CacheConstantValue(literal_pos, const_list); return new (Z) LiteralNode(literal_pos, const_list); } else { // Factory call at runtime. const Class& factory_class = Class::Handle(Z, Library::LookupCoreClass(Symbols::List())); ASSERT(!factory_class.IsNull()); const Function& factory_method = Function::ZoneHandle( Z, factory_class.LookupFactory( Library::PrivateCoreLibName(Symbols::ListLiteralFactory()))); ASSERT(!factory_method.IsNull()); if (!list_type_arguments.IsNull() && !list_type_arguments.IsInstantiated() && (FunctionLevel() > 0)) { // Make sure that the instantiators are captured. CaptureAllInstantiators(); } TypeArguments& factory_type_args = TypeArguments::ZoneHandle(Z, list_type_arguments.raw()); // If the factory class extends other parameterized classes, adjust the // type argument vector. if (!factory_type_args.IsNull() && (factory_class.NumTypeArguments() > 1)) { ASSERT(factory_type_args.Length() == 1); Type& factory_type = Type::Handle( Z, Type::New(factory_class, factory_type_args, type_pos, Heap::kOld)); // It is not strictly necessary to canonicalize factory_type, but only its // type argument vector. factory_type ^= CanonicalizeType(factory_type); factory_type_args = factory_type.arguments(); ASSERT(factory_type_args.Length() == factory_class.NumTypeArguments()); ASSERT(factory_type_args.IsCanonical()); } else { factory_type_args = factory_type_args.Canonicalize(); } ArgumentListNode* factory_param = new (Z) ArgumentListNode(literal_pos); if (element_list.length() == 0) { LiteralNode* empty_array_literal = new (Z) LiteralNode(TokenPos(), Object::empty_array()); factory_param->Add(empty_array_literal); } else { ArrayNode* list = new (Z) ArrayNode(TokenPos(), type, element_list); factory_param->Add(list); } return CreateConstructorCallNode(literal_pos, factory_type_args, factory_method, factory_param); } } ConstructorCallNode* Parser::CreateConstructorCallNode( TokenPosition token_pos, const TypeArguments& type_arguments, const Function& constructor, ArgumentListNode* arguments) { if (!type_arguments.IsNull() && !type_arguments.IsInstantiated()) { EnsureExpressionTemp(); } return new (Z) ConstructorCallNode(token_pos, type_arguments, constructor, arguments); } static void AddKeyValuePair(GrowableArray* pairs, bool is_const, AstNode* key, AstNode* value) { if (is_const) { ASSERT(key->IsLiteralNode()); const Instance& new_key = key->AsLiteralNode()->literal(); for (int i = 0; i < pairs->length(); i += 2) { const Instance& key_i = (*pairs)[i]->AsLiteralNode()->literal(); // The keys of a compile time constant map are compile time // constants, i.e. canonicalized values. Thus, we can compare // raw pointers to check for equality. if (new_key.raw() == key_i.raw()) { // Duplicate key found. The new value replaces the previously // defined value. (*pairs)[i + 1] = value; return; } } } pairs->Add(key); pairs->Add(value); } AstNode* Parser::ParseMapLiteral(TokenPosition type_pos, bool is_const, const TypeArguments& type_arguments) { TRACE_PARSER("ParseMapLiteral"); ASSERT(type_pos.IsReal()); ASSERT(CurrentToken() == Token::kLBRACE); const TokenPosition literal_pos = TokenPos(); if (is_const) { Instance& existing_const = Instance::ZoneHandle(Z); if (GetCachedConstant(literal_pos, &existing_const)) { SkipMapLiteral(); return new (Z) LiteralNode(literal_pos, existing_const); } } ConsumeToken(); // Opening brace. AbstractType& key_type = Type::ZoneHandle(Z, Type::DynamicType()); AbstractType& value_type = Type::ZoneHandle(Z, Type::DynamicType()); TypeArguments& map_type_arguments = TypeArguments::ZoneHandle(Z, type_arguments.raw()); // If no type argument vector is provided, leave it as null, which is // equivalent to using dynamic as the type argument for the both key and value // types. if (!map_type_arguments.IsNull()) { ASSERT(map_type_arguments.Length() > 0); // Map literals take two type arguments. if (map_type_arguments.Length() == 2) { key_type = map_type_arguments.TypeAt(0); value_type = map_type_arguments.TypeAt(1); // Malformed type arguments are mapped to dynamic. ASSERT(!key_type.IsMalformed() && !value_type.IsMalformed()); // No declared bounds in Map. ASSERT(!key_type.IsMalbounded() && !value_type.IsMalbounded()); if (key_type.IsDynamicType() && value_type.IsDynamicType()) { map_type_arguments = TypeArguments::null(); } else if (is_const && !type_arguments.IsInstantiated()) { ReportError(type_pos, "the type arguments of a constant map literal cannot " "include a type variable"); } } else { if (I->error_on_bad_type()) { ReportError(type_pos, "a map literal takes two type arguments specifying " "the key type and the value type"); } // Ignore type arguments. map_type_arguments = TypeArguments::null(); } } ASSERT(map_type_arguments.IsNull() || (map_type_arguments.Length() == 2)); map_type_arguments ^= map_type_arguments.Canonicalize(); GrowableArray kv_pairs_list; // Parse the map entries. Note: there may be an optional extra // comma after the last entry. while (CurrentToken() != Token::kRBRACE) { const bool saved_mode = SetAllowFunctionLiterals(true); const TokenPosition key_pos = TokenPos(); AstNode* key = ParseExpr(is_const, kConsumeCascades); if (I->type_checks() && !is_const && !key_type.IsDynamicType()) { key = new (Z) AssignableNode(key_pos, key, key_type, Symbols::ListLiteralElement()); } if (is_const) { ASSERT(key->IsLiteralNode()); const Instance& key_value = key->AsLiteralNode()->literal(); if (key_value.IsDouble()) { ReportError(key_pos, "key value must not be of type double"); } if (!key_value.IsInteger() && !key_value.IsString() && (key_value.clazz() != I->object_store()->symbol_class()) && ImplementsEqualOperator(Z, key_value)) { ReportError(key_pos, "key value must not implement operator =="); } } ExpectToken(Token::kCOLON); const TokenPosition value_pos = TokenPos(); AstNode* value = ParseExpr(is_const, kConsumeCascades); SetAllowFunctionLiterals(saved_mode); if (I->type_checks() && !is_const && !value_type.IsDynamicType()) { value = new (Z) AssignableNode(value_pos, value, value_type, Symbols::ListLiteralElement()); } AddKeyValuePair(&kv_pairs_list, is_const, key, value); if (CurrentToken() == Token::kCOMMA) { ConsumeToken(); } else if (CurrentToken() != Token::kRBRACE) { ReportError("comma or '}' expected"); } } ASSERT(kv_pairs_list.length() % 2 == 0); ExpectToken(Token::kRBRACE); if (is_const) { // Create the key-value pair array, canonicalize it and then create // the immutable map object with it. This all happens at compile time. // The resulting immutable map object is returned as a literal. // First, create the canonicalized key-value pair array. Array& key_value_array = Array::ZoneHandle(Z, Array::New(kv_pairs_list.length(), Heap::kOld)); AbstractType& arg_type = Type::Handle(Z); Error& bound_error = Error::Handle(Z); for (int i = 0; i < kv_pairs_list.length(); i++) { AstNode* arg = kv_pairs_list[i]; // Arguments have been evaluated to a literal value already. ASSERT(arg->IsLiteralNode()); ASSERT(!is_top_level_); // We cannot check unresolved types. if (I->type_checks()) { if ((i % 2) == 0) { // Check key type. arg_type = key_type.raw(); } else { // Check value type. arg_type = value_type.raw(); } if (!arg_type.IsDynamicType() && (!arg->AsLiteralNode()->literal().IsNull() && !arg->AsLiteralNode()->literal().IsInstanceOf( arg_type, Object::null_type_arguments(), Object::null_type_arguments(), &bound_error))) { // If the failure is due to a bound error, display it. if (!bound_error.IsNull()) { ReportError(bound_error); } else { ReportError( arg->AsLiteralNode()->token_pos(), "map literal %s at index %d must be " "a constant of type '%s'", ((i % 2) == 0) ? "key" : "value", i >> 1, String::Handle(Z, arg_type.UserVisibleName()).ToCString()); } } } key_value_array.SetAt(i, arg->AsLiteralNode()->literal()); } key_value_array.MakeImmutable(); key_value_array ^= TryCanonicalize(key_value_array, TokenPos()); // Construct the map object. const Class& immutable_map_class = Class::Handle(Z, Library::LookupCoreClass(Symbols::ImmutableMap())); ASSERT(!immutable_map_class.IsNull()); // If the immutable map class extends other parameterized classes, we need // to adjust the type argument vector. This is currently not the case. ASSERT(immutable_map_class.NumTypeArguments() == 2); ArgumentListNode* constr_args = new (Z) ArgumentListNode(TokenPos()); constr_args->Add(new (Z) LiteralNode(literal_pos, key_value_array)); const Function& map_constr = Function::ZoneHandle( Z, immutable_map_class.LookupConstructorAllowPrivate( Symbols::ImmutableMapConstructor())); ASSERT(!map_constr.IsNull()); const Object& constructor_result = Object::Handle( Z, EvaluateConstConstructorCall(immutable_map_class, map_type_arguments, map_constr, constr_args)); if (constructor_result.IsUnhandledException()) { ReportErrors(Error::Cast(constructor_result), script_, literal_pos, "error executing const Map constructor"); } else { const Instance& const_instance = Instance::Cast(constructor_result); CacheConstantValue(literal_pos, const_instance); return new (Z) LiteralNode(literal_pos, Instance::ZoneHandle(Z, const_instance.raw())); } } else { // Factory call at runtime. const Class& factory_class = Class::Handle(Z, Library::LookupCoreClass(Symbols::Map())); ASSERT(!factory_class.IsNull()); const Function& factory_method = Function::ZoneHandle( Z, factory_class.LookupFactory( Library::PrivateCoreLibName(Symbols::MapLiteralFactory()))); ASSERT(!factory_method.IsNull()); if (!map_type_arguments.IsNull() && !map_type_arguments.IsInstantiated() && (FunctionLevel() > 0)) { // Make sure that the instantiators are captured. CaptureAllInstantiators(); } TypeArguments& factory_type_args = TypeArguments::ZoneHandle(Z, map_type_arguments.raw()); // If the factory class extends other parameterized classes, adjust the // type argument vector. if (!factory_type_args.IsNull() && (factory_class.NumTypeArguments() > 2)) { ASSERT(factory_type_args.Length() == 2); Type& factory_type = Type::Handle( Z, Type::New(factory_class, factory_type_args, type_pos, Heap::kOld)); // It is not strictly necessary to canonicalize factory_type, but only its // type argument vector. factory_type ^= CanonicalizeType(factory_type); factory_type_args = factory_type.arguments(); ASSERT(factory_type_args.Length() == factory_class.NumTypeArguments()); ASSERT(factory_type_args.IsCanonical()); } else { factory_type_args = factory_type_args.Canonicalize(); } ArgumentListNode* factory_param = new (Z) ArgumentListNode(literal_pos); // The kv_pair array is temporary and of element type dynamic. It is passed // to the factory to initialize a properly typed map. Pass a pre-allocated // array for the common empty map literal case. if (kv_pairs_list.length() == 0) { LiteralNode* empty_array_literal = new (Z) LiteralNode(TokenPos(), Object::empty_array()); factory_param->Add(empty_array_literal); } else { ArrayNode* kv_pairs = new (Z) ArrayNode( TokenPos(), Type::ZoneHandle(Z, Type::ArrayType()), kv_pairs_list); factory_param->Add(kv_pairs); } return CreateConstructorCallNode(literal_pos, factory_type_args, factory_method, factory_param); } UNREACHABLE(); return NULL; } AstNode* Parser::ParseCompoundLiteral() { TRACE_PARSER("ParseCompoundLiteral"); bool is_const = false; if (CurrentToken() == Token::kCONST) { is_const = true; ConsumeToken(); } const TokenPosition type_pos = TokenPos(); TypeArguments& type_arguments = TypeArguments::Handle( Z, ParseTypeArguments(ClassFinalizer::kCanonicalize)); // Malformed type arguments are mapped to dynamic, so we will not encounter // them here. // Map and List interfaces do not declare bounds on their type parameters, so // we will not see malbounded type arguments here. AstNode* primary = NULL; if ((CurrentToken() == Token::kLBRACK) || (CurrentToken() == Token::kINDEX)) { primary = ParseListLiteral(type_pos, is_const, type_arguments); } else if (CurrentToken() == Token::kLBRACE) { primary = ParseMapLiteral(type_pos, is_const, type_arguments); } else { UnexpectedToken(); } return primary; } AstNode* Parser::ParseSymbolLiteral() { ASSERT(CurrentToken() == Token::kHASH); ConsumeToken(); TokenPosition symbol_pos = TokenPos(); String& symbol = String::ZoneHandle(Z); if (IsIdentifier()) { symbol = CurrentLiteral()->raw(); ConsumeToken(); GrowableHandlePtrArray pieces(Z, 3); pieces.Add(symbol); while (CurrentToken() == Token::kPERIOD) { pieces.Add(Symbols::Dot()); ConsumeToken(); pieces.Add(*ExpectIdentifier("identifier expected")); } symbol = Symbols::FromConcatAll(T, pieces); } else if (Token::CanBeOverloaded(CurrentToken())) { symbol = Symbols::Token(CurrentToken()).raw(); ConsumeToken(); } else { ReportError("illegal symbol literal"); } ASSERT(symbol.IsSymbol()); Instance& symbol_instance = Instance::ZoneHandle(Z); if (GetCachedConstant(symbol_pos, &symbol_instance)) { return new (Z) LiteralNode(symbol_pos, symbol_instance); } // Call Symbol class constructor to create a symbol instance. const Class& symbol_class = Class::Handle(I->object_store()->symbol_class()); ASSERT(!symbol_class.IsNull()); ArgumentListNode* constr_args = new (Z) ArgumentListNode(symbol_pos); constr_args->Add(new (Z) LiteralNode(symbol_pos, symbol)); const Function& constr = Function::ZoneHandle( Z, symbol_class.LookupConstructor(Symbols::SymbolCtor())); ASSERT(!constr.IsNull()); const Object& result = Object::Handle(Z, EvaluateConstConstructorCall( symbol_class, TypeArguments::Handle(Z), constr, constr_args, /*obfuscate_symbol_instances=*/false)); if (result.IsUnhandledException()) { ReportErrors(Error::Cast(result), script_, symbol_pos, "error executing const Symbol constructor"); } symbol_instance ^= result.raw(); CacheConstantValue(symbol_pos, symbol_instance); return new (Z) LiteralNode(symbol_pos, symbol_instance); } static String& BuildConstructorName(Thread* thread, const String& type_class_name, const String* named_constructor) { // By convention, the static function implementing a named constructor 'C' // for class 'A' is labeled 'A.C', and the static function implementing the // unnamed constructor for class 'A' is labeled 'A.'. // This convention prevents users from explicitly calling constructors. Zone* zone = thread->zone(); String& constructor_name = String::Handle(zone, Symbols::FromDot(thread, type_class_name)); if (named_constructor != NULL) { constructor_name = Symbols::FromConcat(thread, constructor_name, *named_constructor); } return constructor_name; } AstNode* Parser::ParseNewOperator(Token::Kind op_kind) { TRACE_PARSER("ParseNewOperator"); const TokenPosition new_pos = TokenPos(); ASSERT((op_kind == Token::kNEW) || (op_kind == Token::kCONST)); bool is_const = (op_kind == Token::kCONST); if (!IsIdentifier()) { ReportError("type name expected"); } TokenPosition type_pos = TokenPos(); // Can't allocate const objects of a deferred type. const bool allow_deferred_type = !is_const; const Token::Kind la3 = LookaheadToken(3); const bool consume_unresolved_prefix = (la3 == Token::kLT) || (la3 == Token::kPERIOD); LibraryPrefix& prefix = LibraryPrefix::ZoneHandle(Z); AbstractType& type = AbstractType::ZoneHandle( Z, ParseType(ClassFinalizer::kCanonicalize, allow_deferred_type, consume_unresolved_prefix, &prefix)); if (FLAG_load_deferred_eagerly && !prefix.IsNull() && prefix.is_deferred_load() && !prefix.is_loaded()) { // Add runtime check. Type& malformed_type = Type::ZoneHandle(Z); malformed_type = ClassFinalizer::NewFinalizedMalformedType( Error::Handle(Z), // No previous error. script_, type_pos, "deferred type '%s.%s' is not yet loaded", String::Handle(Z, prefix.name()).ToCString(), String::Handle(type.Name()).ToCString()); // Note: Adding a statement to current block is a hack, parsing an // expression should have no side-effect. current_block_->statements->Add( ThrowTypeError(type_pos, malformed_type, &prefix)); } // In case the type is malformed, throw a dynamic type error after finishing // parsing the instance creation expression. if (!type.IsMalformed() && (type.IsTypeParameter() || type.IsDynamicType())) { // Replace the type with a malformed type. type = ClassFinalizer::NewFinalizedMalformedType( Error::Handle(Z), // No previous error. script_, type_pos, "%s'%s' cannot be instantiated", type.IsTypeParameter() ? "type parameter " : "", type.IsTypeParameter() ? String::Handle(Z, type.UserVisibleName()).ToCString() : "dynamic"); } // Attempting to instantiate an enum type is a compile-time error. Class& type_class = Class::Handle(Z, type.type_class()); if (type_class.is_enum_class()) { ReportError(new_pos, "enum type '%s' can not be instantiated", String::Handle(Z, type_class.Name()).ToCString()); } // The type can be followed by an optional named constructor identifier. // Note that we tell ParseType() above not to consume it as part of // a misinterpreted qualified identifier. Only a valid library // prefix is accepted as qualifier. String* named_constructor = NULL; if (CurrentToken() == Token::kPERIOD) { ConsumeToken(); named_constructor = ExpectIdentifier("name of constructor expected"); } // Parse constructor parameters. TokenPosition call_pos = TokenPos(); CheckToken(Token::kLPAREN); ArgumentListNode* arguments = ParseActualParameters(NULL, TypeArguments::ZoneHandle(Z), is_const); // Parsing is complete, so we can return a throw in case of a malformed or // malbounded type or report a compile-time error if the constructor is const. if (type.IsMalformedOrMalbounded()) { if (is_const) { const Error& error = Error::Handle(Z, type.error()); ReportError(error); } if (arguments->length() > 0) { // Evaluate arguments for side-effects and throw. LetNode* error_result = new (Z) LetNode(type_pos); for (intptr_t i = 0; i < arguments->length(); ++i) { error_result->AddNode(arguments->NodeAt(i)); } error_result->AddNode(ThrowTypeError(type_pos, type)); return error_result; } return ThrowTypeError(type_pos, type); } // Resolve the type and optional identifier to a constructor or factory. String& type_class_name = String::Handle(Z, type_class.Name()); TypeArguments& type_arguments = TypeArguments::ZoneHandle(Z, type.arguments()); // A constructor has an implicit 'this' parameter (instance to construct) // and a factory has an implicit 'this' parameter (type_arguments). intptr_t arguments_length = arguments->length() + 1; // An additional type check of the result of a redirecting factory may be // required. AbstractType& type_bound = AbstractType::ZoneHandle(Z); // Make sure that an appropriate constructor exists. String& constructor_name = BuildConstructorName(T, type_class_name, named_constructor); Function& constructor = Function::ZoneHandle(Z, type_class.LookupConstructor(constructor_name)); if (constructor.IsNull()) { constructor = type_class.LookupFactory(constructor_name); if (constructor.IsNull()) { const String& external_constructor_name = (named_constructor ? constructor_name : type_class_name); // Replace the type with a malformed type and compile a throw or report a // compile-time error if the constructor is const. if (is_const) { type = ClassFinalizer::NewFinalizedMalformedType( Error::Handle(Z), // No previous error. script_, call_pos, "class '%s' has no constructor or factory named '%s'", String::Handle(Z, type_class.Name()).ToCString(), external_constructor_name.ToCString()); ReportError(Error::Handle(Z, type.error())); } return ThrowNoSuchMethodError( call_pos, type_class, external_constructor_name, arguments, InvocationMirror::kConstructor, InvocationMirror::kMethod, NULL); // No existing function. } else if (constructor.IsRedirectingFactory()) { ClassFinalizer::ResolveRedirectingFactory(type_class, constructor); Type& redirect_type = Type::ZoneHandle(Z, constructor.RedirectionType()); if (!redirect_type.IsMalformedOrMalbounded() && !redirect_type.IsInstantiated()) { // No generic constructors allowed. ASSERT(redirect_type.IsInstantiated(kFunctions)); // The type arguments of the redirection type are instantiated from the // type arguments of the parsed type of the 'new' or 'const' expression. Error& error = Error::Handle(Z); redirect_type ^= redirect_type.InstantiateFrom( type_arguments, Object::null_type_arguments(), kNoneFree, &error, NULL, // instantiation_trail NULL, // bound_trail Heap::kOld); if (!error.IsNull()) { redirect_type = ClassFinalizer::NewFinalizedMalformedType( error, script_, call_pos, "redirecting factory type '%s' cannot be instantiated", String::Handle(Z, redirect_type.UserVisibleName()).ToCString()); } } if (!redirect_type.HasResolvedTypeClass()) { // If the redirection type is unresolved, we convert the allocation // into throwing a type error. const UnresolvedClass& cls = UnresolvedClass::Handle(Z, redirect_type.unresolved_class()); const LibraryPrefix& prefix = LibraryPrefix::Cast( Object::Handle(Z, cls.library_or_library_prefix())); if (!prefix.IsNull() && !prefix.is_loaded() && !FLAG_load_deferred_eagerly) { // If the redirection type is unresolved because it refers to // an unloaded deferred prefix, mark this function as depending // on the library prefix. It will then get invalidated when the // prefix is loaded. parsed_function()->AddDeferredPrefix(prefix); } redirect_type = ClassFinalizer::NewFinalizedMalformedType( Error::Handle(Z), script_, call_pos, "redirection type '%s' is not loaded", String::Handle(Z, redirect_type.UserVisibleName()).ToCString()); } if (redirect_type.IsMalformedOrMalbounded()) { if (is_const) { ReportError(Error::Handle(Z, redirect_type.error())); } return ThrowTypeError(redirect_type.token_pos(), redirect_type); } if (I->type_checks() && !redirect_type.IsSubtypeOf(type, NULL, NULL, Heap::kOld)) { // Additional type checking of the result is necessary. type_bound = type.raw(); } type = redirect_type.raw(); type_class = type.type_class(); type_class_name = type_class.Name(); type_arguments = type.arguments(); constructor = constructor.RedirectionTarget(); constructor_name = constructor.name(); ASSERT(!constructor.IsNull()); } } ASSERT(!constructor.IsNull()); // It is a compile time error to instantiate a const instance of an // abstract class. Factory methods are ok. if (is_const && type_class.is_abstract() && !constructor.IsFactory()) { ReportError(new_pos, "cannot instantiate abstract class"); } // It is ok to call a factory method of an abstract class, but it is // a dynamic error to instantiate an abstract class. if (type_class.is_abstract() && !constructor.IsFactory()) { // Evaluate arguments before throwing. LetNode* result = new (Z) LetNode(call_pos); for (intptr_t i = 0; i < arguments->length(); ++i) { result->AddNode(arguments->NodeAt(i)); } ArgumentListNode* error_arguments = new (Z) ArgumentListNode(type_pos); error_arguments->Add(new (Z) LiteralNode( TokenPos(), Integer::ZoneHandle(Z, Integer::New(type_pos.value(), Heap::kOld)))); error_arguments->Add(new (Z) LiteralNode( TokenPos(), String::ZoneHandle(Z, type_class_name.raw()))); result->AddNode(MakeStaticCall( Symbols::AbstractClassInstantiationError(), Library::PrivateCoreLibName(Symbols::ThrowNew()), error_arguments)); return result; } type_arguments ^= type_arguments.Canonicalize(); const int kTypeArgsLen = 0; String& error_message = String::Handle(Z); if (!constructor.AreValidArguments(kTypeArgsLen, arguments_length, arguments->names(), &error_message)) { const String& external_constructor_name = (named_constructor ? constructor_name : type_class_name); if (is_const) { ReportError(call_pos, "invalid arguments passed to constructor '%s' " "for class '%s': %s", external_constructor_name.ToCString(), String::Handle(Z, type_class.Name()).ToCString(), error_message.ToCString()); } return ThrowNoSuchMethodError(call_pos, type_class, external_constructor_name, arguments, InvocationMirror::kConstructor, InvocationMirror::kMethod, &constructor); } // Return a throw in case of a malformed or malbounded type or report a // compile-time error if the constructor is const. if (type.IsMalformedOrMalbounded()) { if (is_const) { ReportError(Error::Handle(Z, type.error())); } return ThrowTypeError(type_pos, type); } // Make the constructor call. AstNode* new_object = NULL; if (is_const) { if (!constructor.is_const()) { const String& external_constructor_name = (named_constructor ? constructor_name : type_class_name); ReportError( "non-const constructor '%s' cannot be used in " "const object creation", external_constructor_name.ToCString()); } Instance& const_instance = Instance::ZoneHandle(Z); if (GetCachedConstant(new_pos, &const_instance)) { // Cache hit, nothing else to do. } else { Object& constructor_result = Object::Handle( Z, EvaluateConstConstructorCall(type_class, type_arguments, constructor, arguments)); if (constructor_result.IsUnhandledException()) { // It's a compile-time error if invocation of a const constructor // call fails. ReportErrors(Error::Cast(constructor_result), script_, new_pos, "error while evaluating const constructor"); } const_instance ^= constructor_result.raw(); CacheConstantValue(new_pos, const_instance); } new_object = new (Z) LiteralNode(new_pos, const_instance); if (!type_bound.IsNull()) { ASSERT(!type_bound.IsMalformed()); Error& bound_error = Error::Handle(Z); ASSERT(!is_top_level_); // We cannot check unresolved types. if (!const_instance.IsInstanceOf( type_bound, Object::null_type_arguments(), Object::null_type_arguments(), &bound_error)) { type_bound = ClassFinalizer::NewFinalizedMalformedType( bound_error, script_, new_pos, "const factory result is not an instance of '%s'", String::Handle(Z, type_bound.UserVisibleName()).ToCString()); new_object = ThrowTypeError(new_pos, type_bound); } type_bound = AbstractType::null(); } } else { CheckConstructorCallTypeArguments(new_pos, constructor, type_arguments); if (!type_arguments.IsNull() && !type_arguments.IsInstantiated() && (FunctionLevel() > 0)) { // Make sure that the instantiators are captured. CaptureAllInstantiators(); } // If the type argument vector is not instantiated, we verify in checked // mode at runtime that it is within its declared bounds. new_object = CreateConstructorCallNode(new_pos, type_arguments, constructor, arguments); } if (!type_bound.IsNull()) { new_object = new (Z) AssignableNode(new_pos, new_object, type_bound, Symbols::FactoryResult()); } return new_object; } String& Parser::Interpolate(const GrowableArray& values) { NoReloadScope no_reload_scope(isolate(), thread()); NoOOBMessageScope no_msg_scope(thread()); const Class& cls = Class::Handle(Z, Library::LookupCoreClass(Symbols::StringBase())); ASSERT(!cls.IsNull()); const Function& func = Function::Handle( Z, cls.LookupStaticFunction( Library::PrivateCoreLibName(Symbols::Interpolate()))); ASSERT(!func.IsNull()); // Build the array of literal values to interpolate. const Array& value_arr = Array::Handle(Z, Array::New(values.length(), Heap::kOld)); for (int i = 0; i < values.length(); i++) { ASSERT(values[i]->IsLiteralNode()); value_arr.SetAt(i, values[i]->AsLiteralNode()->literal()); } // Build argument array to pass to the interpolation function. const Array& interpolate_arg = Array::Handle(Z, Array::New(1, Heap::kOld)); interpolate_arg.SetAt(0, value_arr); // Call interpolation function. Object& result = Object::Handle(Z); result = DartEntry::InvokeFunction(func, interpolate_arg); if (result.IsUnhandledException()) { ReportError("%s", Error::Cast(result).ToErrorCString()); } String& concatenated = String::ZoneHandle(Z); concatenated ^= result.raw(); concatenated = Symbols::New(T, concatenated); return concatenated; } // A string literal consists of the concatenation of the next n tokens // that satisfy the EBNF grammar: // literal = kSTRING {{ interpol } kSTRING } // interpol = kINTERPOL_VAR | (kINTERPOL_START expression kINTERPOL_END) // In other words, the scanner breaks down interpolated strings so that // a string literal always begins and ends with a kSTRING token. AstNode* Parser::ParseStringLiteral(bool allow_interpolation) { TRACE_PARSER("ParseStringLiteral"); AstNode* primary = NULL; const TokenPosition literal_start = TokenPos(); ASSERT(CurrentToken() == Token::kSTRING); Token::Kind l1_token = LookaheadToken(1); if ((l1_token != Token::kSTRING) && (l1_token != Token::kINTERPOL_VAR) && (l1_token != Token::kINTERPOL_START)) { // Common case: no interpolation. primary = new (Z) LiteralNode(literal_start, *CurrentLiteral()); ConsumeToken(); return primary; } // String interpolation needed. // First, check whether we've cached a compile-time constant for this // string interpolation. Instance& cached_string = Instance::Handle(Z); if (GetCachedConstant(literal_start, &cached_string)) { SkipStringLiteral(); return new (Z) LiteralNode(literal_start, Instance::ZoneHandle(Z, cached_string.raw())); } bool is_compiletime_const = true; bool has_interpolation = false; GrowableArray values_list; while (CurrentToken() == Token::kSTRING) { if (CurrentLiteral()->Length() > 0) { // Only add non-empty string sections to the values list // that will be concatenated. values_list.Add(new (Z) LiteralNode(TokenPos(), *CurrentLiteral())); } ConsumeToken(); while ((CurrentToken() == Token::kINTERPOL_VAR) || (CurrentToken() == Token::kINTERPOL_START)) { if (!allow_interpolation) { ReportError("string interpolation not allowed in this context"); } has_interpolation = true; AstNode* expr = NULL; const TokenPosition expr_pos = TokenPos(); if (CurrentToken() == Token::kINTERPOL_VAR) { expr = ResolveIdent(TokenPos(), *CurrentLiteral(), true); ConsumeToken(); } else { ASSERT(CurrentToken() == Token::kINTERPOL_START); ConsumeToken(); const bool saved_mode = SetAllowFunctionLiterals(true); expr = ParseExpr(kAllowConst, kConsumeCascades); SetAllowFunctionLiterals(saved_mode); ExpectToken(Token::kINTERPOL_END); } // Check if this interpolated string is still considered a compile time // constant. If it is we need to evaluate if the current string part is // a constant or not. Only strings, numbers, booleans and null values // are allowed in compile time const interpolations. if (is_compiletime_const) { const Object* const_expr = expr->EvalConstExpr(); if ((const_expr != NULL) && (const_expr->IsNumber() || const_expr->IsString() || const_expr->IsBool() || const_expr->IsNull())) { // Change expr into a literal. expr = new (Z) LiteralNode(expr_pos, EvaluateConstExpr(expr_pos, expr)); } else { is_compiletime_const = false; } } values_list.Add(expr); } } if (is_compiletime_const) { if (has_interpolation) { const String& interpolated_string = Interpolate(values_list); primary = new (Z) LiteralNode(literal_start, interpolated_string); CacheConstantValue(literal_start, interpolated_string); } else { GrowableHandlePtrArray pieces(Z, values_list.length()); for (int i = 0; i < values_list.length(); i++) { const Instance& part = values_list[i]->AsLiteralNode()->literal(); ASSERT(part.IsString()); pieces.Add(String::Cast(part)); } const String& lit = String::ZoneHandle(Z, Symbols::FromConcatAll(T, pieces)); primary = new (Z) LiteralNode(literal_start, lit); // Caching of constant not necessary because the symbol lookup will // find the value next time. } } else { ArrayNode* values = new (Z) ArrayNode( TokenPos(), Type::ZoneHandle(Z, Type::ArrayType()), values_list); primary = new (Z) StringInterpolateNode(TokenPos(), values); } return primary; } AstNode* Parser::ParsePrimary() { TRACE_PARSER("ParsePrimary"); ASSERT(!is_top_level_); AstNode* primary = NULL; const Token::Kind token = CurrentToken(); if (IsFunctionLiteral()) { primary = ParseFunctionStatement(true); } else if (IsIdentifier()) { TokenPosition qual_ident_pos = TokenPos(); const LibraryPrefix& prefix = LibraryPrefix::ZoneHandle(Z, ParsePrefix()); if (!prefix.IsNull()) { ExpectToken(Token::kPERIOD); } String& ident = *CurrentLiteral(); ConsumeToken(); if (prefix.IsNull()) { intptr_t primary_func_level = 0; ResolveIdentInLocalScope(qual_ident_pos, ident, &primary, &primary_func_level); // Check whether the identifier is shadowed by a function type parameter. if (InGenericFunctionScope()) { intptr_t type_param_func_level = FunctionLevel(); TypeParameter& type_param = TypeParameter::ZoneHandle( Z, innermost_function().LookupTypeParameter( ident, &type_param_func_level)); if (!type_param.IsNull()) { if ((primary == NULL) || (primary_func_level < type_param_func_level)) { // The identifier is a function type parameter, possibly shadowing // already resolved 'primary'. return new (Z) PrimaryNode(qual_ident_pos, type_param); } } } if (primary == NULL) { // Check whether the identifier is a type parameter. if (!current_class().IsNull()) { TypeParameter& type_param = TypeParameter::ZoneHandle( Z, current_class().LookupTypeParameter(ident)); if (!type_param.IsNull()) { return new (Z) PrimaryNode(qual_ident_pos, type_param); } } // This is a non-local unqualified identifier so resolve the // identifier locally in the main app library and all libraries // imported by it. primary = ResolveIdentInCurrentLibraryScope(qual_ident_pos, ident); } } else { // This is a qualified identifier with a library prefix so resolve // the identifier locally in that library (we do not include the // libraries imported by that library). primary = ResolveIdentInPrefixScope(qual_ident_pos, prefix, ident); // If the identifier could not be resolved, throw a NoSuchMethodError. // Note: unlike in the case of an unqualified identifier, do not // interpret the unresolved identifier as an instance method or // instance getter call when compiling an instance method. if (primary == NULL) { if (prefix.is_deferred_load() && ident.Equals(Symbols::LoadLibrary())) { // Hack Alert: recognize special 'loadLibrary' call on the // prefix object. The prefix is the primary. Rewind parser and // let ParseSelectors() handle the loadLibrary call. SetPosition(qual_ident_pos); ConsumeToken(); // Prefix name. primary = new (Z) LiteralNode(qual_ident_pos, prefix); } else { GrowableHandlePtrArray pieces(Z, 3); pieces.Add(String::Handle(Z, prefix.name())); pieces.Add(Symbols::Dot()); pieces.Add(ident); const String& qualified_name = String::ZoneHandle(Z, Symbols::FromConcatAll(T, pieces)); primary = new (Z) PrimaryNode(qual_ident_pos, qualified_name); if (prefix.is_deferred_load()) { primary->AsPrimaryNode()->set_prefix(&prefix); } } } else if (FLAG_load_deferred_eagerly && prefix.is_deferred_load()) { // primary != NULL. GrowableHandlePtrArray pieces(Z, 3); pieces.Add(String::Handle(Z, prefix.name())); pieces.Add(Symbols::Dot()); pieces.Add(ident); const String& qualified_name = String::ZoneHandle(Z, Symbols::FromConcatAll(T, pieces)); InvocationMirror::Kind call_kind = CurrentToken() == Token::kLPAREN ? InvocationMirror::kMethod : InvocationMirror::kGetter; // Note: Adding a statement to current block is a hack, parsing an // expression should have no side-effect. current_block_->statements->Add(ThrowNoSuchMethodError( qual_ident_pos, current_class(), qualified_name, NULL, // No arguments. InvocationMirror::kTopLevel, call_kind, NULL, // No existing function. &prefix)); } } ASSERT(primary != NULL); } else if (token == Token::kTHIS) { LocalVariable* local = LookupLocalScope(Symbols::This()); if (local == NULL) { ReportError("receiver 'this' is not in scope"); } primary = new (Z) LoadLocalNode(TokenPos(), local); ConsumeToken(); } else if (token == Token::kINTEGER) { const Integer& literal = Integer::ZoneHandle(Z, CurrentIntegerLiteral()); primary = new (Z) LiteralNode(TokenPos(), literal); ConsumeToken(); } else if (token == Token::kTRUE) { primary = new (Z) LiteralNode(TokenPos(), Bool::True()); ConsumeToken(); } else if (token == Token::kFALSE) { primary = new (Z) LiteralNode(TokenPos(), Bool::False()); ConsumeToken(); } else if (token == Token::kNULL) { primary = new (Z) LiteralNode(TokenPos(), Object::null_instance()); ConsumeToken(); } else if (token == Token::kLPAREN) { ConsumeToken(); const bool saved_mode = SetAllowFunctionLiterals(true); primary = ParseExpr(kAllowConst, kConsumeCascades); SetAllowFunctionLiterals(saved_mode); ExpectToken(Token::kRPAREN); } else if (token == Token::kDOUBLE) { const Double& double_value = Double::ZoneHandle(Z, CurrentDoubleLiteral()); if (double_value.IsNull()) { ReportError("invalid double literal"); } primary = new (Z) LiteralNode(TokenPos(), double_value); ConsumeToken(); } else if (token == Token::kSTRING) { primary = ParseStringLiteral(true); } else if (token == Token::kNEW) { ConsumeToken(); primary = ParseNewOperator(Token::kNEW); } else if (token == Token::kCONST) { if ((LookaheadToken(1) == Token::kLT) || (LookaheadToken(1) == Token::kLBRACK) || (LookaheadToken(1) == Token::kINDEX) || (LookaheadToken(1) == Token::kLBRACE)) { primary = ParseCompoundLiteral(); } else { ConsumeToken(); primary = ParseNewOperator(Token::kCONST); } } else if (token == Token::kLT || token == Token::kLBRACK || token == Token::kINDEX || token == Token::kLBRACE) { primary = ParseCompoundLiteral(); } else if (token == Token::kHASH) { primary = ParseSymbolLiteral(); } else if (token == Token::kSUPER) { if (current_function().is_static()) { ReportError("cannot access superclass from static method"); } if (current_class().SuperClass() == Class::null()) { ReportError("class '%s' does not have a superclass", String::Handle(Z, current_class().Name()).ToCString()); } const TokenPosition super_pos = TokenPos(); ConsumeToken(); if (CurrentToken() == Token::kPERIOD) { ConsumeToken(); const TokenPosition ident_pos = TokenPos(); const String& ident = *ExpectIdentifier("identifier expected"); if (IsArgumentPart()) { TypeArguments& func_type_args = TypeArguments::ZoneHandle(Z); if (CurrentToken() == Token::kLT) { // Type arguments. func_type_args = ParseTypeArguments(ClassFinalizer::kCanonicalize); if (Isolate::Current()->reify_generic_functions()) { if (!func_type_args.IsNull() && !func_type_args.IsInstantiated() && (FunctionLevel() > 0)) { // Make sure that the instantiators are captured. CaptureAllInstantiators(); } } else { func_type_args = TypeArguments::null(); } } primary = ParseSuperCall(ident, func_type_args); } else { primary = ParseSuperFieldAccess(ident, ident_pos); } } else if ((CurrentToken() == Token::kLBRACK) || Token::CanBeOverloaded(CurrentToken()) || (CurrentToken() == Token::kNE)) { primary = ParseSuperOperator(); } else if (CurrentToken() == Token::kQM_PERIOD) { ReportError("super call or super getter may not use ?."); } else { primary = new (Z) PrimaryNode(super_pos, Symbols::Super()); } } else { UnexpectedToken(); } return primary; } // Evaluate expression in expr and return the value. The expression must // be a compile time constant. const Instance& Parser::EvaluateConstExpr(TokenPosition expr_pos, AstNode* expr) { NoReloadScope no_reload_scope(isolate(), thread()); NoOOBMessageScope no_msg_scope(thread()); if (expr->IsLiteralNode()) { return expr->AsLiteralNode()->literal(); } else if (expr->IsLoadLocalNode() && expr->AsLoadLocalNode()->local().IsConst()) { return *expr->AsLoadLocalNode()->local().ConstValue(); } else if (expr->IsLoadStaticFieldNode()) { const Field& field = expr->AsLoadStaticFieldNode()->field(); // We already checked that this field is const and has been // initialized. ASSERT(field.is_const()); ASSERT(field.StaticValue() != Object::sentinel().raw()); ASSERT(field.StaticValue() != Object::transition_sentinel().raw()); return Instance::ZoneHandle(Z, field.StaticValue()); } else if (expr->IsTypeNode()) { AbstractType& type = AbstractType::ZoneHandle(Z, expr->AsTypeNode()->type().raw()); ASSERT(type.IsInstantiated() && !type.IsMalformedOrMalbounded()); return type; } else if (expr->IsClosureNode()) { const Function& func = expr->AsClosureNode()->function(); ASSERT((func.IsImplicitStaticClosureFunction())); Instance& closure = Instance::ZoneHandle(Z, func.ImplicitStaticClosure()); closure = TryCanonicalize(closure, expr_pos); return closure; } else { ASSERT(expr->EvalConstExpr() != NULL); Instance& value = Instance::ZoneHandle(Z); if (GetCachedConstant(expr_pos, &value)) { return value; } ReturnNode* ret = new (Z) ReturnNode(expr_pos, expr); // Compile time constant expressions cannot reference anything from a // local scope. LocalScope* empty_scope = new (Z) LocalScope(NULL, 0, 0); SequenceNode* seq = new (Z) SequenceNode(expr_pos, empty_scope); seq->Add(ret); INC_STAT(thread_, num_execute_const, 1); Object& result = Object::Handle(Z, Compiler::ExecuteOnce(seq)); if (result.IsError()) { ReportErrors(Error::Cast(result), script_, expr_pos, "error evaluating constant expression"); } ASSERT(result.IsInstance() || result.IsNull()); value ^= result.raw(); value = TryCanonicalize(value, expr_pos); CacheConstantValue(expr_pos, value); return value; } } void Parser::SkipFunctionLiteral() { if (IsIdentifier()) { if (LookaheadToken(1) != Token::kLPAREN) { SkipTypeOrFunctionType(true); } ExpectIdentifier("function name expected"); } if (CurrentToken() == Token::kLPAREN) { SkipToMatchingParenthesis(); } RawFunction::AsyncModifier async_modifier = ParseFunctionModifier(); BoolScope allow_await(&this->await_is_keyword_, async_modifier != RawFunction::kNoModifier); if (CurrentToken() == Token::kLBRACE) { SkipBlock(); ExpectToken(Token::kRBRACE); } else if (CurrentToken() == Token::kARROW) { ConsumeToken(); SkipExpr(); } } // Skips function/method/constructor/getter/setter preambles until the formal // parameter list. It is enough to skip the tokens, since we have already // previously parsed the function. void Parser::SkipFunctionPreamble() { while (true) { if (IsFunctionTypeSymbol()) { ConsumeToken(); SkipTypeParameters(); SkipToMatchingParenthesis(); continue; } const Token::Kind token = CurrentToken(); if (token == Token::kLPAREN) { return; } if (token == Token::kGET) { if (LookaheadToken(1) == Token::kLT) { // Case: Generic Function/method named get. ConsumeToken(); // Parse away 'get' (the function's name). SkipTypeParameters(); continue; } if (LookaheadToken(1) == Token::kLPAREN) { // Case: Function/method named get. ConsumeToken(); // Parse away 'get' (the function's name). return; } // Case: Getter. ConsumeToken(); // Parse away 'get'. ConsumeToken(); // Parse away the getter name. return; } ConsumeToken(); // Can be static, factory, operator, void, ident, etc... } } void Parser::SkipListLiteral() { if (CurrentToken() == Token::kINDEX) { // Empty list literal. ConsumeToken(); return; } ExpectToken(Token::kLBRACK); while (CurrentToken() != Token::kRBRACK) { SkipNestedExpr(); if (CurrentToken() == Token::kCOMMA) { ConsumeToken(); } else { break; } } ExpectToken(Token::kRBRACK); } void Parser::SkipMapLiteral() { ExpectToken(Token::kLBRACE); while (CurrentToken() != Token::kRBRACE) { SkipNestedExpr(); ExpectToken(Token::kCOLON); SkipNestedExpr(); if (CurrentToken() == Token::kCOMMA) { ConsumeToken(); } else { break; } } ExpectToken(Token::kRBRACE); } void Parser::SkipActualParameters() { if (CurrentToken() == Token::kLT) { SkipTypeArguments(); } ExpectToken(Token::kLPAREN); while (CurrentToken() != Token::kRPAREN) { if (IsIdentifier() && (LookaheadToken(1) == Token::kCOLON)) { // Named actual parameter. ConsumeToken(); ConsumeToken(); } SkipNestedExpr(); if (CurrentToken() == Token::kCOMMA) { ConsumeToken(); } } ExpectToken(Token::kRPAREN); } void Parser::SkipCompoundLiteral() { if (CurrentToken() == Token::kLT) { SkipTypeArguments(); } if ((CurrentToken() == Token::kLBRACK) || (CurrentToken() == Token::kINDEX)) { SkipListLiteral(); } else if (CurrentToken() == Token::kLBRACE) { SkipMapLiteral(); } } void Parser::SkipSymbolLiteral() { ConsumeToken(); // Hash sign. if (IsIdentifier()) { ConsumeToken(); while (CurrentToken() == Token::kPERIOD) { ConsumeToken(); ExpectIdentifier("identifier expected"); } } else if (Token::CanBeOverloaded(CurrentToken())) { ConsumeToken(); } else { UnexpectedToken(); } } void Parser::SkipNewOperator() { ConsumeToken(); // Skip new or const keyword. if (IsIdentifier()) { SkipType(false); if (CurrentToken() == Token::kPERIOD) { ConsumeToken(); ExpectIdentifier("identifier expected"); } if (CurrentToken() == Token::kLPAREN) { SkipActualParameters(); return; } } } void Parser::SkipStringLiteral() { ASSERT(CurrentToken() == Token::kSTRING); while (CurrentToken() == Token::kSTRING) { ConsumeToken(); while (true) { if (CurrentToken() == Token::kINTERPOL_VAR) { ConsumeToken(); } else if (CurrentToken() == Token::kINTERPOL_START) { ConsumeToken(); const bool saved_mode = SetAllowFunctionLiterals(true); SkipExpr(); SetAllowFunctionLiterals(saved_mode); ExpectToken(Token::kINTERPOL_END); } else { break; } } } } void Parser::SkipPrimary() { if (IsFunctionLiteral()) { SkipFunctionLiteral(); return; } switch (CurrentToken()) { case Token::kTHIS: case Token::kSUPER: case Token::kNULL: case Token::kTRUE: case Token::kFALSE: case Token::kINTEGER: case Token::kDOUBLE: ConsumeToken(); break; case Token::kIDENT: ConsumeToken(); break; case Token::kSTRING: SkipStringLiteral(); break; case Token::kLPAREN: ConsumeToken(); SkipNestedExpr(); ExpectToken(Token::kRPAREN); break; case Token::kNEW: SkipNewOperator(); break; case Token::kCONST: if ((LookaheadToken(1) == Token::kLT) || (LookaheadToken(1) == Token::kLBRACE) || (LookaheadToken(1) == Token::kLBRACK) || (LookaheadToken(1) == Token::kINDEX)) { ConsumeToken(); SkipCompoundLiteral(); } else { SkipNewOperator(); } break; case Token::kLT: case Token::kLBRACE: case Token::kLBRACK: case Token::kINDEX: SkipCompoundLiteral(); break; case Token::kHASH: SkipSymbolLiteral(); break; default: if (IsIdentifier()) { ConsumeToken(); // Handle pseudo-keyword identifiers. } else { UnexpectedToken(); UNREACHABLE(); } break; } } void Parser::SkipSelectors() { while (true) { const Token::Kind current_token = CurrentToken(); if (current_token == Token::kCASCADE) { ConsumeToken(); if (CurrentToken() == Token::kLBRACK) { continue; // Consume [ in next loop iteration. } else { ExpectIdentifier("identifier or [ expected after .."); } } else if ((current_token == Token::kPERIOD) || (current_token == Token::kQM_PERIOD)) { ConsumeToken(); ExpectIdentifier("identifier expected"); } else if (current_token == Token::kLBRACK) { ConsumeToken(); SkipNestedExpr(); ExpectToken(Token::kRBRACK); } else if (IsArgumentPart()) { SkipActualParameters(); } else { break; } } } void Parser::SkipPostfixExpr() { SkipPrimary(); SkipSelectors(); if (IsIncrementOperator(CurrentToken())) { ConsumeToken(); } } void Parser::SkipUnaryExpr() { if (IsPrefixOperator(CurrentToken()) || IsIncrementOperator(CurrentToken()) || IsAwaitKeyword()) { ConsumeToken(); SkipUnaryExpr(); } else { SkipPostfixExpr(); } } void Parser::SkipBinaryExpr() { SkipUnaryExpr(); const int min_prec = Token::Precedence(Token::kIFNULL); const int max_prec = Token::Precedence(Token::kMUL); while (((min_prec <= Token::Precedence(CurrentToken())) && (Token::Precedence(CurrentToken()) <= max_prec))) { if (CurrentToken() == Token::kIS) { ConsumeToken(); if (CurrentToken() == Token::kNOT) { ConsumeToken(); } SkipTypeOrFunctionType(false); } else if (CurrentToken() == Token::kAS) { ConsumeToken(); SkipTypeOrFunctionType(false); } else { ConsumeToken(); SkipUnaryExpr(); } } } void Parser::SkipConditionalExpr() { SkipBinaryExpr(); if (CurrentToken() == Token::kCONDITIONAL) { ConsumeToken(); SkipExpr(); ExpectToken(Token::kCOLON); SkipExpr(); } } void Parser::SkipExpr() { while (CurrentToken() == Token::kTHROW) { ConsumeToken(); } SkipConditionalExpr(); if (CurrentToken() == Token::kCASCADE) { SkipSelectors(); } if (Token::IsAssignmentOperator(CurrentToken())) { ConsumeToken(); SkipExpr(); } } void Parser::SkipNestedExpr() { const bool saved_mode = SetAllowFunctionLiterals(true); SkipExpr(); SetAllowFunctionLiterals(saved_mode); } void Parser::SkipQualIdent() { ASSERT(IsIdentifier()); ConsumeToken(); if (CurrentToken() == Token::kPERIOD) { ConsumeToken(); // Consume the kPERIOD token. ExpectIdentifier("identifier expected after '.'"); } } } // namespace dart #else // DART_PRECOMPILED_RUNTIME namespace dart { void ParsedFunction::AddToGuardedFields(const Field* field) const { UNREACHABLE(); } kernel::ScopeBuildingResult* ParsedFunction::EnsureKernelScopes() { UNREACHABLE(); return NULL; } LocalVariable* ParsedFunction::EnsureExpressionTemp() { UNREACHABLE(); return NULL; } void ParsedFunction::SetNodeSequence(SequenceNode* node_sequence) { UNREACHABLE(); } void ParsedFunction::SetRegExpCompileData( RegExpCompileData* regexp_compile_data) { UNREACHABLE(); } void ParsedFunction::AllocateVariables() { UNREACHABLE(); } void ParsedFunction::AllocateIrregexpVariables(intptr_t num_stack_locals) { UNREACHABLE(); } void ParsedFunction::Bailout(const char* origin, const char* reason) const { UNREACHABLE(); } void Parser::ParseCompilationUnit(const Library& library, const Script& script) { UNREACHABLE(); } void Parser::ParseClass(const Class& cls) { UNREACHABLE(); } RawObject* Parser::ParseFunctionParameters(const Function& func) { UNREACHABLE(); return Object::null(); } void Parser::ParseFunction(ParsedFunction* parsed_function) { UNREACHABLE(); } RawObject* Parser::ParseMetadata(const Field& meta_data) { UNREACHABLE(); return Object::null(); } ParsedFunction* Parser::ParseStaticFieldInitializer(const Field& field) { UNREACHABLE(); return NULL; } void Parser::InsertCachedConstantValue(const Script& script, TokenPosition token_pos, const Instance& value) { UNREACHABLE(); } ArgumentListNode* Parser::BuildNoSuchMethodArguments( TokenPosition call_pos, const String& function_name, const ArgumentListNode& function_args, const LocalVariable* temp_for_last_arg, bool is_super_invocation) { UNREACHABLE(); return NULL; } bool Parser::FieldHasFunctionLiteralInitializer(const Field& field, TokenPosition* start, TokenPosition* end) { UNREACHABLE(); return false; } } // namespace dart #endif // DART_PRECOMPILED_RUNTIME