// 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/bootstrap.h" #include "vm/class_finalizer.h" #include "vm/compiler/aot/precompiler.h" #include "vm/compiler/backend/il_printer.h" #include "vm/compiler/frontend/scope_builder.h" #include "vm/compiler/jit/compiler.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/heap.h" #include "vm/heap/safepoint.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/scopes.h" #include "vm/stack_frame.h" #include "vm/symbols.h" #include "vm/tags.h" #include "vm/timeline.h" #include "vm/zone.h" namespace dart { // Quick access to the current thread, isolate and zone. #define T (thread()) #define I (isolate()) #define Z (zone()) ParsedFunction::ParsedFunction(Thread* thread, const Function& function) : thread_(thread), function_(function), code_(Code::Handle(zone(), function.unoptimized_code())), node_sequence_(NULL), regexp_compile_data_(NULL), instantiator_(NULL), function_type_arguments_(NULL), parent_type_arguments_(NULL), current_context_var_(NULL), arg_desc_var_(NULL), expression_temp_var_(NULL), entry_points_temp_var_(NULL), finally_return_temp_var_(NULL), deferred_prefixes_(new ZoneGrowableArray()), guarded_fields_(new ZoneGrowableArray()), default_parameter_values_(NULL), raw_type_arguments_var_(NULL), first_parameter_index_(), num_stack_locals_(0), have_seen_await_expr_(false), kernel_scopes_(NULL) { ASSERT(function.IsZoneHandle()); // Every function has a local variable for the current context. LocalVariable* temp = new (zone()) LocalVariable(function.token_pos(), function.token_pos(), Symbols::CurrentContextVar(), Object::dynamic_type()); current_context_var_ = temp; const bool reify_generic_argument = function.IsGeneric(); const bool load_optional_arguments = function.HasOptionalParameters(); const bool check_arguments = function_.IsClosureFunction(); const bool need_argument_descriptor = load_optional_arguments || check_arguments || reify_generic_argument; if (need_argument_descriptor) { arg_desc_var_ = new (zone()) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::ArgDescVar(), Object::dynamic_type()); } } 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( DeoptId::kNone, "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::ScopeBuilder 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(); } LocalVariable* ParsedFunction::EnsureEntryPointsTemp() { if (!has_entry_points_temp_var()) { LocalVariable* temp = new (Z) LocalVariable(function_.token_pos(), function_.token_pos(), Symbols::EntryPointsTemp(), Object::dynamic_type()); ASSERT(temp != NULL); set_entry_points_temp_var(temp); } ASSERT(has_entry_points_temp_var()); return entry_points_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; // Before we start allocating indices to variables, we'll setup the // parameters array, which can be used to access the raw parameters (i.e. not // the potentially variables which are in the context) raw_parameters_ = new (Z) ZoneGrowableArray(Z, num_params); for (intptr_t param = 0; param < num_params; ++param) { LocalVariable* raw_parameter = scope->VariableAt(param); if (raw_parameter->is_captured()) { String& tmp = String::ZoneHandle(Z); tmp = Symbols::FromConcat(T, Symbols::OriginalParam(), raw_parameter->name()); RELEASE_ASSERT(scope->LocalLookupVariable(tmp) == NULL); raw_parameter = new LocalVariable(raw_parameter->declaration_token_pos(), raw_parameter->token_pos(), tmp, raw_parameter->type()); if (function().HasOptionalParameters()) { bool ok = scope->AddVariable(raw_parameter); ASSERT(ok); // Currently our optimizer cannot prove liveness of variables properly // when a function has try/catch. It therefore makes the conservative // estimate that all [LocalVariable]s in the frame are live and spills // them before call sites (in some shape or form). // // Since we are guaranteed to not need that, we tell the try/catch // spilling mechanism not to care about this variable. raw_parameter->set_is_captured_parameter(true); } else { raw_parameter->set_index( VariableIndex(function().NumParameters() - param)); } } raw_parameters_->Add(raw_parameter); } if (function_type_arguments_ != NULL) { LocalVariable* raw_type_args_parameter = function_type_arguments_; if (function_type_arguments_->is_captured()) { String& tmp = String::ZoneHandle(Z); tmp = Symbols::FromConcat(T, Symbols::OriginalParam(), function_type_arguments_->name()); ASSERT(scope->LocalLookupVariable(tmp) == NULL); raw_type_args_parameter = new LocalVariable(raw_type_args_parameter->declaration_token_pos(), raw_type_args_parameter->token_pos(), tmp, raw_type_args_parameter->type()); bool ok = scope->AddVariable(raw_type_args_parameter); ASSERT(ok); } raw_type_arguments_var_ = raw_type_args_parameter; } // The copy parameters implementation will still write to local variables // which we assign indices as with the old CopyParams implementation. VariableIndex parameter_index_start; VariableIndex reamining_local_variables_start; { // Compute start indices to parameters and locals, and the number of // parameters to copy. if (num_opt_params == 0) { parameter_index_start = first_parameter_index_ = VariableIndex(num_params); reamining_local_variables_start = VariableIndex(0); } else { parameter_index_start = first_parameter_index_ = VariableIndex(0); reamining_local_variables_start = VariableIndex(-num_params); } } if (function_type_arguments_ != NULL && num_opt_params > 0) { reamining_local_variables_start = VariableIndex(reamining_local_variables_start.value() - 1); } // Allocate parameters and local variables, either in the local frame or // in the context(s). bool found_captured_variables = false; VariableIndex first_local_index = VariableIndex(parameter_index_start.value() > 0 ? 0 : -num_params); VariableIndex next_free_index = scope->AllocateVariables( parameter_index_start, num_params, first_local_index, NULL, &found_captured_variables); num_stack_locals_ = -next_free_index.value(); } 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. first_parameter_index_ = VariableIndex(num_params); // Frame indices are relative to the frame pointer and are decreasing. num_stack_locals_ = num_stack_locals; } void ParsedFunction::AllocateBytecodeVariables(intptr_t num_stack_locals) { ASSERT(!function().IsIrregexpFunction()); first_parameter_index_ = VariableIndex(function().num_fixed_parameters()); num_stack_locals_ = num_stack_locals; } } // namespace dart #endif // DART_PRECOMPILED_RUNTIME