// 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" #if !defined(DART_PRECOMPILED_RUNTIME) #include "lib/invocation_mirror.h" #include "platform/utils.h" #include "vm/bit_vector.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/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/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), scope_(nullptr), regexp_compile_data_(nullptr), function_type_arguments_(nullptr), parent_type_arguments_(nullptr), current_context_var_(nullptr), arg_desc_var_(nullptr), expression_temp_var_(nullptr), entry_points_temp_var_(nullptr), finally_return_temp_var_(nullptr), dynamic_closure_call_vars_(nullptr), guarded_fields_(), default_parameter_values_(nullptr), raw_type_arguments_var_(nullptr), first_parameter_index_(), num_stack_locals_(0), have_seen_await_expr_(false), kernel_scopes_(nullptr) { DEBUG_ASSERT(function.IsNotTemporaryScopedHandle()); // 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; if (function.PrologueNeedsArgumentsDescriptor()) { arg_desc_var_ = new (zone()) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::ArgDescVar(), Object::dynamic_type()); } // The code generated by the prologue builder for loading optional arguments // requires the expression temporary variable. if (function.HasOptionalParameters()) { EnsureExpressionTemp(); } } void ParsedFunction::AddToGuardedFields(const Field* field) const { if ((field->guarded_cid() == kDynamicCid) || (field->guarded_cid() == kIllegalCid)) { return; } const Field** other = guarded_fields_.Lookup(field); if (other != nullptr) { ASSERT(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 optimizing // 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() == !CompilerState::Current().should_clone_fields()); guarded_fields_.Insert(&Field::ZoneHandle(Z, field->ptr())); } 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_ == nullptr) { 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 != nullptr); 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 != nullptr); 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 != nullptr); 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::SetRegExpCompileData( RegExpCompileData* regexp_compile_data) { ASSERT(regexp_compile_data_ == nullptr); ASSERT(regexp_compile_data != nullptr); regexp_compile_data_ = regexp_compile_data; } void ParsedFunction::AllocateVariables() { ASSERT(!function().IsIrregexpFunction()); LocalScope* scope = this->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; const bool copy_parameters = function().MakesCopyOfParameters(); // 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* variable = ParameterVariable(param); LocalVariable* raw_parameter = variable; if (variable->is_captured()) { String& tmp = String::ZoneHandle(Z); tmp = Symbols::FromConcat(T, Symbols::OriginalParam(), variable->name()); RELEASE_ASSERT(scope->LocalLookupVariable( tmp, LocalVariable::kNoKernelOffset) == nullptr); raw_parameter = new LocalVariable( variable->declaration_token_pos(), variable->token_pos(), tmp, variable->static_type(), LocalVariable::kNoKernelOffset, variable->inferred_type(), variable->inferred_arg_type(), variable->inferred_arg_value()); raw_parameter->set_annotations_offset(variable->annotations_offset()); if (variable->is_explicit_covariant_parameter()) { raw_parameter->set_is_explicit_covariant_parameter(); } if (variable->needs_covariant_check_in_method()) { raw_parameter->set_needs_covariant_check_in_method(); } raw_parameter->set_type_check_mode(variable->type_check_mode()); if (copy_parameters) { bool ok = scope->AddVariable(raw_parameter); ASSERT(ok); } else { raw_parameter->set_index( VariableIndex(function().NumParameters() - param)); } } raw_parameters_->Add(raw_parameter); } if (function_type_arguments_ != nullptr) { 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, LocalVariable::kNoKernelOffset) == nullptr); raw_type_args_parameter = new LocalVariable(function_type_arguments_->declaration_token_pos(), function_type_arguments_->token_pos(), tmp, function_type_arguments_->static_type(), LocalVariable::kNoKernelOffset); 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 first_local_index; { // Compute start indices to parameters and locals, and the number of // parameters to copy. if (!copy_parameters) { ASSERT(suspend_state_var() == nullptr); first_parameter_index_ = VariableIndex(num_params); first_local_index = VariableIndex(0); } else { // :suspend_state variable is inserted at the fixed slot // before the copied parameters. const intptr_t reserved_var_slot_count = (suspend_state_var() != nullptr) ? 1 : 0; first_parameter_index_ = VariableIndex(-reserved_var_slot_count); first_local_index = VariableIndex(first_parameter_index_.value() - num_params); } } // Allocate parameters and local variables, either in the local frame or // in the context(s). bool found_captured_variables = false; VariableIndex next_free_index = scope->AllocateVariables( function(), first_parameter_index_, num_params, first_local_index, nullptr, &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::SetCovariantParameters( const BitVector* covariant_parameters) { ASSERT(covariant_parameters_ == nullptr); ASSERT(covariant_parameters->length() == function_.NumParameters()); covariant_parameters_ = covariant_parameters; } void ParsedFunction::SetGenericCovariantImplParameters( const BitVector* generic_covariant_impl_parameters) { ASSERT(generic_covariant_impl_parameters_ == nullptr); ASSERT(generic_covariant_impl_parameters->length() == function_.NumParameters()); generic_covariant_impl_parameters_ = generic_covariant_impl_parameters; } bool ParsedFunction::IsCovariantParameter(intptr_t i) const { ASSERT(covariant_parameters_ != nullptr); ASSERT((i >= 0) && (i < function_.NumParameters())); return covariant_parameters_->Contains(i); } bool ParsedFunction::IsGenericCovariantImplParameter(intptr_t i) const { ASSERT(generic_covariant_impl_parameters_ != nullptr); ASSERT((i >= 0) && (i < function_.NumParameters())); return generic_covariant_impl_parameters_->Contains(i); } ParsedFunction::DynamicClosureCallVars* ParsedFunction::EnsureDynamicClosureCallVars() { ASSERT(function().IsDynamicClosureCallDispatcher()); if (dynamic_closure_call_vars_ != nullptr) return dynamic_closure_call_vars_; const auto& saved_args_desc = Array::Handle(zone(), function().saved_args_desc()); const ArgumentsDescriptor descriptor(saved_args_desc); dynamic_closure_call_vars_ = new (zone()) DynamicClosureCallVars(zone(), descriptor.NamedCount()); auto const pos = function().token_pos(); const auto& type_Dynamic = Object::dynamic_type(); const auto& type_Function = Type::ZoneHandle(zone(), Type::DartFunctionType()); const auto& type_Smi = Type::ZoneHandle(zone(), Type::SmiType()); #define INIT_FIELD(Name, TypeName, Symbol) \ dynamic_closure_call_vars_->Name = new (zone()) LocalVariable( \ pos, pos, Symbols::DynamicCall##Symbol##Var(), type_##TypeName); FOR_EACH_DYNAMIC_CLOSURE_CALL_VARIABLE(INIT_FIELD); #undef INIT_FIELD for (intptr_t i = 0; i < descriptor.NamedCount(); i++) { auto const name = OS::SCreate( zone(), ":dyn_call_named_argument_%" Pd "_parameter_index", i); auto const var = new (zone()) LocalVariable( pos, pos, String::ZoneHandle(zone(), Symbols::New(thread(), name)), type_Smi); dynamic_closure_call_vars_->named_argument_parameter_indices.Add(var); } return dynamic_closure_call_vars_; } } // namespace dart #endif // !defined(DART_PRECOMPILED_RUNTIME)