| // 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/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), | |
| 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::SetRegExpCompileData( | |
| RegExpCompileData* regexp_compile_data) { | |
| ASSERT(regexp_compile_data_ == nullptr); | |
| ASSERT(regexp_compile_data != nullptr); | |
| regexp_compile_data_ = regexp_compile_data; | |
| } | |
| void ParsedFunction::AllocateVariables() { | |
| 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<LocalVariable*>(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::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::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) |