| // Copyright (c) 2016, 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/compiler/frontend/kernel_binary_flowgraph.h" |
| |
| #include "vm/closure_functions_cache.h" |
| #include "vm/compiler/ffi/callback.h" |
| #include "vm/compiler/frontend/flow_graph_builder.h" // For dart::FlowGraphBuilder::SimpleInstanceOfType. |
| #include "vm/compiler/frontend/prologue_builder.h" |
| #include "vm/compiler/jit/compiler.h" |
| #include "vm/object_store.h" |
| #include "vm/resolver.h" |
| #include "vm/stack_frame.h" |
| |
| namespace dart { |
| namespace kernel { |
| |
| #define Z (zone_) |
| #define H (translation_helper_) |
| #define T (type_translator_) |
| #define I Isolate::Current() |
| #define IG IsolateGroup::Current() |
| #define B (flow_graph_builder_) |
| |
| Class& StreamingFlowGraphBuilder::GetSuperOrDie() { |
| Class& klass = Class::Handle(Z, parsed_function()->function().Owner()); |
| ASSERT(!klass.IsNull()); |
| klass = klass.SuperClass(); |
| ASSERT(!klass.IsNull()); |
| return klass; |
| } |
| |
| FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFieldInitializer() { |
| FieldHelper field_helper(this); |
| field_helper.ReadUntilExcluding(FieldHelper::kInitializer); |
| |
| // Constants are directly accessed at use sites of Dart code. In C++ - if |
| // we need to access static constants - we do so directly using the kernel |
| // evaluation instead of invoking the initializer function in Dart code. |
| // |
| // If the field is marked as @pragma('vm:entry-point') then the embedder might |
| // invoke the getter, so we'll generate the initializer function. |
| ASSERT(!field_helper.IsConst() || |
| Field::Handle(Z, parsed_function()->function().accessor_field()) |
| .VerifyEntryPoint(EntryPointPragma::kGetterOnly) == |
| Error::null()); |
| |
| Tag initializer_tag = ReadTag(); // read first part of initializer. |
| if (initializer_tag != kSomething) { |
| UNREACHABLE(); |
| } |
| |
| B->graph_entry_ = new (Z) GraphEntryInstr(*parsed_function(), B->osr_id_); |
| |
| auto normal_entry = B->BuildFunctionEntry(B->graph_entry_); |
| B->graph_entry_->set_normal_entry(normal_entry); |
| |
| Fragment body(normal_entry); |
| body += B->CheckStackOverflowInPrologue(field_helper.position_); |
| body += SetupCapturedParameters(parsed_function()->function()); |
| body += BuildExpression(); // read initializer. |
| body += Return(TokenPosition::kNoSource); |
| |
| PrologueInfo prologue_info(-1, -1); |
| if (B->IsCompiledForOsr()) { |
| B->graph_entry_->RelinkToOsrEntry(Z, B->last_used_block_id_ + 1); |
| } |
| return new (Z) FlowGraph(*parsed_function(), B->graph_entry_, |
| B->last_used_block_id_, prologue_info); |
| } |
| |
| void StreamingFlowGraphBuilder::SetupDefaultParameterValues() { |
| intptr_t optional_parameter_count = |
| parsed_function()->function().NumOptionalParameters(); |
| if (optional_parameter_count > 0) { |
| ZoneGrowableArray<const Instance*>* default_values = |
| new ZoneGrowableArray<const Instance*>(Z, optional_parameter_count); |
| |
| AlternativeReadingScope alt(&reader_); |
| FunctionNodeHelper function_node_helper(this); |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kPositionalParameters); |
| |
| if (parsed_function()->function().HasOptionalNamedParameters()) { |
| // List of positional. |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| SkipVariableDeclaration(); // read ith variable declaration. |
| } |
| |
| // List of named. |
| list_length = ReadListLength(); // read list length. |
| ASSERT(optional_parameter_count == list_length); |
| ASSERT(!parsed_function()->function().HasOptionalPositionalParameters()); |
| for (intptr_t i = 0; i < list_length; ++i) { |
| Instance* default_value; |
| |
| // Read ith variable declaration |
| VariableDeclarationHelper helper(this); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer); |
| Tag tag = ReadTag(); // read (first part of) initializer. |
| if (tag == kSomething) { |
| // This will read the initializer. |
| default_value = &Instance::ZoneHandle( |
| Z, constant_reader_.ReadConstantExpression()); |
| } else { |
| default_value = &Instance::ZoneHandle(Z, Instance::null()); |
| } |
| default_values->Add(default_value); |
| } |
| } else { |
| // List of positional. |
| intptr_t list_length = ReadListLength(); // read list length. |
| ASSERT(list_length == function_node_helper.required_parameter_count_ + |
| optional_parameter_count); |
| ASSERT(parsed_function()->function().HasOptionalPositionalParameters()); |
| for (intptr_t i = 0; i < function_node_helper.required_parameter_count_; |
| ++i) { |
| SkipVariableDeclaration(); // read ith variable declaration. |
| } |
| for (intptr_t i = 0; i < optional_parameter_count; ++i) { |
| Instance* default_value; |
| |
| // Read ith variable declaration |
| VariableDeclarationHelper helper(this); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer); |
| Tag tag = ReadTag(); // read (first part of) initializer. |
| if (tag == kSomething) { |
| // This will read the initializer. |
| default_value = &Instance::ZoneHandle( |
| Z, constant_reader_.ReadConstantExpression()); |
| } else { |
| default_value = &Instance::ZoneHandle(Z, Instance::null()); |
| } |
| default_values->Add(default_value); |
| } |
| |
| // List of named. |
| list_length = ReadListLength(); // read list length. |
| ASSERT(list_length == 0); |
| } |
| parsed_function()->set_default_parameter_values(default_values); |
| } |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildFieldInitializer( |
| const Field& field, |
| bool only_for_side_effects) { |
| ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull()); |
| if (PeekTag() == kNullLiteral) { |
| SkipExpression(); // read past the null literal. |
| if (H.thread()->IsMutatorThread()) { |
| ASSERT(field.IsOriginal()); |
| LeaveCompilerScope cs(H.thread()); |
| field.RecordStore(Object::null_object()); |
| } else { |
| ASSERT(field.is_nullable_unsafe()); |
| } |
| return Fragment(); |
| } |
| |
| Fragment instructions; |
| if (!only_for_side_effects) { |
| instructions += LoadLocal(parsed_function()->receiver_var()); |
| } |
| // All closures created inside BuildExpression will have |
| // field.RawOwner() as its owner. |
| closure_owner_ = field.RawOwner(); |
| instructions += BuildExpression(); |
| closure_owner_ = Object::null(); |
| if (only_for_side_effects) { |
| instructions += Drop(); |
| } else { |
| instructions += flow_graph_builder_->StoreInstanceFieldGuarded( |
| field, StoreInstanceFieldInstr::Kind::kInitializing); |
| } |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildLateFieldInitializer( |
| const Field& field, |
| bool has_initializer) { |
| if (has_initializer && PeekTag() == kNullLiteral) { |
| SkipExpression(); // read past the null literal. |
| if (H.thread()->IsMutatorThread()) { |
| LeaveCompilerScope cs(H.thread()); |
| field.RecordStore(Object::null_object()); |
| } else { |
| ASSERT(field.is_nullable_unsafe()); |
| } |
| return Fragment(); |
| } |
| |
| Fragment instructions; |
| instructions += LoadLocal(parsed_function()->receiver_var()); |
| instructions += flow_graph_builder_->Constant(Object::sentinel()); |
| instructions += flow_graph_builder_->StoreInstanceField( |
| field, StoreInstanceFieldInstr::Kind::kInitializing); |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildInitializers( |
| const Class& parent_class) { |
| ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull()); |
| Fragment instructions; |
| |
| // Start by getting the position of the constructors initializer. |
| intptr_t initializers_offset = -1; |
| { |
| AlternativeReadingScope alt(&reader_); |
| SkipFunctionNode(); // read constructors function node. |
| initializers_offset = ReaderOffset(); |
| } |
| |
| bool is_redirecting_constructor = false; |
| |
| // Field which will be initialized by the initializer with the given index. |
| GrowableArray<const Field*> initializer_fields(5); |
| |
| // Check if this is a redirecting constructor and collect all fields which |
| // will be initialized by the constructor initializer list. |
| { |
| AlternativeReadingScope alt(&reader_, initializers_offset); |
| |
| const intptr_t list_length = |
| ReadListLength(); // read initializers list length. |
| initializer_fields.EnsureLength(list_length, nullptr); |
| |
| bool has_field_initializers = false; |
| for (intptr_t i = 0; i < list_length; ++i) { |
| if (PeekTag() == kRedirectingInitializer || |
| PeekTag() == kRedirectingFactoryConstructor) { |
| is_redirecting_constructor = true; |
| } else if (PeekTag() == kFieldInitializer) { |
| has_field_initializers = true; |
| ReadTag(); |
| ReadBool(); |
| const NameIndex field_name = ReadCanonicalNameReference(); |
| const Field& field = |
| Field::Handle(Z, H.LookupFieldByKernelGetterOrSetter(field_name)); |
| initializer_fields[i] = &field; |
| SkipExpression(); |
| continue; |
| } |
| SkipInitializer(); |
| } |
| ASSERT(!is_redirecting_constructor || !has_field_initializers); |
| } |
| |
| // These come from: |
| // |
| // class A { |
| // var x = (expr); |
| // } |
| // |
| // We don't want to do that when this is a Redirecting Constructors though |
| // (i.e. has a single initializer being of type kRedirectingInitializer). |
| if (!is_redirecting_constructor) { |
| // Sort list of fields (represented as their kernel offsets) which will |
| // be initialized by the constructor initializer list. We will not emit |
| // StoreInstanceField instructions for those initializers though we will |
| // still evaluate initialization expression for its side effects. |
| GrowableArray<intptr_t> constructor_initialized_field_offsets( |
| initializer_fields.length()); |
| for (auto field : initializer_fields) { |
| if (field != nullptr) { |
| constructor_initialized_field_offsets.Add(field->kernel_offset()); |
| } |
| } |
| |
| constructor_initialized_field_offsets.Sort( |
| [](const intptr_t* a, const intptr_t* b) { |
| return static_cast<int>(*a) - static_cast<int>(*b); |
| }); |
| constructor_initialized_field_offsets.Add(-1); |
| |
| ExternalTypedData& kernel_data = ExternalTypedData::Handle(Z); |
| Array& class_fields = Array::Handle(Z, parent_class.fields()); |
| Field& class_field = Field::Handle(Z); |
| intptr_t next_constructor_initialized_field_index = 0; |
| for (intptr_t i = 0; i < class_fields.Length(); ++i) { |
| class_field ^= class_fields.At(i); |
| if (!class_field.is_static()) { |
| const intptr_t field_offset = class_field.kernel_offset(); |
| |
| // Check if this field will be initialized by the constructor |
| // initializer list. |
| // Note that both class_fields and the list of initialized fields |
| // are sorted by their kernel offset (by construction) - |
| // so we don't need to perform the search. |
| bool is_constructor_initialized = false; |
| const intptr_t constructor_initialized_field_offset = |
| constructor_initialized_field_offsets |
| [next_constructor_initialized_field_index]; |
| if (constructor_initialized_field_offset == field_offset) { |
| next_constructor_initialized_field_index++; |
| is_constructor_initialized = true; |
| } |
| |
| kernel_data = class_field.KernelData(); |
| ASSERT(!kernel_data.IsNull()); |
| AlternativeReadingScopeWithNewData alt(&reader_, &kernel_data, |
| field_offset); |
| FieldHelper field_helper(this); |
| field_helper.ReadUntilExcluding(FieldHelper::kInitializer); |
| const Tag initializer_tag = ReadTag(); |
| if (class_field.is_late()) { |
| if (!is_constructor_initialized) { |
| instructions += BuildLateFieldInitializer( |
| Field::ZoneHandle(Z, class_field.ptr()), |
| initializer_tag == kSomething); |
| } |
| } else if (initializer_tag == kSomething) { |
| EnterScope(field_offset); |
| // If this field is initialized in constructor then we can ignore the |
| // value produced by the field initializer. However we still need to |
| // execute it for its side effects. |
| instructions += BuildFieldInitializer( |
| Field::ZoneHandle(Z, class_field.ptr()), |
| /*only_for_side_effects=*/is_constructor_initialized); |
| ExitScope(field_offset); |
| } |
| } |
| } |
| } |
| |
| // These to come from: |
| // class A { |
| // var x; |
| // var y; |
| // A(this.x) : super(expr), y = (expr); |
| // } |
| { |
| AlternativeReadingScope alt(&reader_, initializers_offset); |
| intptr_t list_length = ReadListLength(); // read initializers list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| Tag tag = ReadTag(); |
| bool isSynthetic = ReadBool(); // read isSynthetic flag. |
| switch (tag) { |
| case kInvalidInitializer: |
| UNIMPLEMENTED(); |
| return Fragment(); |
| case kFieldInitializer: { |
| ReadCanonicalNameReference(); |
| instructions += BuildFieldInitializer( |
| Field::ZoneHandle(Z, initializer_fields[i]->ptr()), |
| /*only_for_size_effects=*/false); |
| break; |
| } |
| case kAssertInitializer: { |
| instructions += BuildStatement(); |
| break; |
| } |
| case kSuperInitializer: { |
| TokenPosition position = ReadPosition(); // read position. |
| NameIndex canonical_target = |
| ReadCanonicalNameReference(); // read target_reference. |
| |
| instructions += LoadLocal(parsed_function()->receiver_var()); |
| |
| // TODO(jensj): ASSERT(init->arguments()->types().length() == 0); |
| Array& argument_names = Array::ZoneHandle(Z); |
| intptr_t argument_count; |
| instructions += BuildArguments( |
| &argument_names, &argument_count, |
| /* positional_parameter_count = */ NULL); // read arguments. |
| argument_count += 1; |
| |
| Class& parent_klass = GetSuperOrDie(); |
| |
| const Function& target = Function::ZoneHandle( |
| Z, H.LookupConstructorByKernelConstructor( |
| parent_klass, H.CanonicalNameString(canonical_target))); |
| instructions += StaticCall( |
| isSynthetic ? TokenPosition::kNoSource : position, target, |
| argument_count, argument_names, ICData::kStatic); |
| instructions += Drop(); |
| break; |
| } |
| case kRedirectingInitializer: { |
| TokenPosition position = ReadPosition(); // read position. |
| NameIndex canonical_target = |
| ReadCanonicalNameReference(); // read target_reference. |
| |
| instructions += LoadLocal(parsed_function()->receiver_var()); |
| |
| // TODO(jensj): ASSERT(init->arguments()->types().length() == 0); |
| Array& argument_names = Array::ZoneHandle(Z); |
| intptr_t argument_count; |
| instructions += BuildArguments( |
| &argument_names, &argument_count, |
| /* positional_parameter_count = */ NULL); // read arguments. |
| argument_count += 1; |
| |
| const Function& target = Function::ZoneHandle( |
| Z, H.LookupConstructorByKernelConstructor(canonical_target)); |
| instructions += StaticCall( |
| isSynthetic ? TokenPosition::kNoSource : position, target, |
| argument_count, argument_names, ICData::kStatic); |
| instructions += Drop(); |
| break; |
| } |
| case kLocalInitializer: { |
| // The other initializers following this one might read the variable. |
| // This is used e.g. for evaluating the arguments to a super call |
| // first, run normal field initializers next and then make the actual |
| // super call: |
| // |
| // The frontend converts |
| // |
| // class A { |
| // var x; |
| // A(a, b) : super(a + b), x = 2*b {} |
| // } |
| // |
| // to |
| // |
| // class A { |
| // var x; |
| // A(a, b) : tmp = a + b, x = 2*b, super(tmp) {} |
| // } |
| // |
| // (This is strictly speaking not what one should do in terms of the |
| // specification but that is how it is currently implemented.) |
| LocalVariable* variable = |
| LookupVariable(ReaderOffset() + data_program_offset_); |
| |
| // Variable declaration |
| VariableDeclarationHelper helper(this); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer); |
| ASSERT(!helper.IsConst()); |
| Tag tag = ReadTag(); // read (first part of) initializer. |
| if (tag != kSomething) { |
| UNREACHABLE(); |
| } |
| |
| instructions += BuildExpression(); // read initializer. |
| instructions += StoreLocal(TokenPosition::kNoSource, variable); |
| instructions += Drop(); |
| break; |
| } |
| default: |
| ReportUnexpectedTag("initializer", tag); |
| UNREACHABLE(); |
| } |
| } |
| } |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::DebugStepCheckInPrologue( |
| const Function& dart_function, |
| TokenPosition position) { |
| if (!NeedsDebugStepCheck(dart_function, position)) { |
| return {}; |
| } |
| |
| // Place this check at the last parameter to ensure parameters |
| // are in scope in the debugger at method entry. |
| const int parameter_count = dart_function.NumParameters(); |
| TokenPosition check_pos = TokenPosition::kNoSource; |
| if (parameter_count > 0) { |
| const LocalVariable& parameter = |
| *parsed_function()->ParameterVariable(parameter_count - 1); |
| check_pos = parameter.token_pos(); |
| } |
| if (!check_pos.IsDebugPause()) { |
| // No parameters or synthetic parameters. |
| check_pos = position; |
| ASSERT(check_pos.IsDebugPause()); |
| } |
| |
| return DebugStepCheck(check_pos); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::TypeArgumentsHandling( |
| const Function& dart_function) { |
| Fragment prologue = B->BuildDefaultTypeHandling(dart_function); |
| |
| if (dart_function.IsClosureFunction() && |
| dart_function.NumParentTypeArguments() > 0) { |
| LocalVariable* closure = parsed_function()->ParameterVariable(0); |
| |
| // Function with yield points can not be generic itself but the outer |
| // function can be. |
| ASSERT(yield_continuations().is_empty() || !dart_function.IsGeneric()); |
| |
| LocalVariable* fn_type_args = parsed_function()->function_type_arguments(); |
| ASSERT(fn_type_args != NULL && closure != NULL); |
| |
| if (dart_function.IsGeneric()) { |
| prologue += LoadLocal(fn_type_args); |
| |
| prologue += LoadLocal(closure); |
| prologue += LoadNativeField(Slot::Closure_function_type_arguments()); |
| |
| prologue += IntConstant(dart_function.NumParentTypeArguments()); |
| |
| prologue += IntConstant(dart_function.NumTypeArguments()); |
| |
| const auto& prepend_function = |
| flow_graph_builder_->PrependTypeArgumentsFunction(); |
| |
| prologue += StaticCall(TokenPosition::kNoSource, prepend_function, 4, |
| ICData::kStatic); |
| prologue += StoreLocal(TokenPosition::kNoSource, fn_type_args); |
| prologue += Drop(); |
| } else { |
| prologue += LoadLocal(closure); |
| prologue += LoadNativeField(Slot::Closure_function_type_arguments()); |
| prologue += StoreLocal(TokenPosition::kNoSource, fn_type_args); |
| prologue += Drop(); |
| } |
| } |
| |
| return prologue; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CompleteBodyWithYieldContinuations( |
| Fragment body) { |
| // The code we are building will be executed right after we enter |
| // the function and before any nested contexts are allocated. |
| // Reset current context_depth_ to match this. |
| const intptr_t current_context_depth = B->context_depth_; |
| B->context_depth_ = scopes()->yield_jump_variable->owner()->context_level(); |
| |
| // Prepend an entry corresponding to normal entry to the function. |
| yield_continuations().InsertAt( |
| 0, YieldContinuation(new (Z) DropTempsInstr(0, NULL), kInvalidTryIndex)); |
| yield_continuations()[0].entry->LinkTo(body.entry); |
| |
| // Load :await_jump_var into a temporary. |
| Fragment dispatch; |
| dispatch += LoadLocal(scopes()->yield_jump_variable); |
| dispatch += StoreLocal(TokenPosition::kNoSource, scopes()->switch_variable); |
| dispatch += Drop(); |
| |
| const intptr_t continuation_count = yield_continuations().length(); |
| |
| IndirectGotoInstr* indirect_goto; |
| if (FLAG_async_igoto_threshold >= 0 && |
| continuation_count >= FLAG_async_igoto_threshold) { |
| dispatch += LoadLocal(scopes()->switch_variable); |
| dispatch += IndirectGoto(continuation_count); |
| indirect_goto = dispatch.current->AsIndirectGoto(); |
| |
| for (intptr_t i = 0; i < continuation_count; i++) { |
| if (i >= 1) { |
| Fragment resumption; |
| // Every continuation after the first is not a normal entry but a |
| // resumption. |
| // Restore :current_context_var from :await_ctx_var. |
| // Note: after this point context_depth_ does not match current context |
| // depth so we should not access any local variables anymore. |
| resumption += LoadLocal(scopes()->yield_context_variable); |
| resumption += StoreLocal(TokenPosition::kNoSource, |
| parsed_function()->current_context_var()); |
| resumption += Drop(); |
| |
| Instruction* next = yield_continuations()[i].entry->next(); |
| yield_continuations()[i].entry->LinkTo(resumption.entry); |
| resumption <<= next; |
| } |
| |
| IndirectEntryInstr* indirect_entry = B->BuildIndirectEntry( |
| /*indirect_id=*/i, yield_continuations()[i].try_index); |
| indirect_entry->LinkTo(yield_continuations()[i].entry->next()); |
| |
| TargetEntryInstr* target = B->BuildTargetEntry(); |
| Fragment(target) + Goto(indirect_entry); |
| |
| indirect_goto->AddSuccessor(target); |
| } |
| } else { |
| BlockEntryInstr* block = nullptr; |
| for (intptr_t i = 0; i < continuation_count; i++) { |
| if (i == 1) { |
| // This is not a normal entry but a resumption. Restore |
| // :current_context_var from :await_ctx_var. |
| // Note: after this point context_depth_ does not match current context |
| // depth so we should not access any local variables anymore. |
| dispatch += LoadLocal(scopes()->yield_context_variable); |
| dispatch += StoreLocal(TokenPosition::kNoSource, |
| parsed_function()->current_context_var()); |
| dispatch += Drop(); |
| } |
| if (i == (continuation_count - 1)) { |
| // We reached the last possibility, no need to build more ifs. |
| // Continue to the last continuation. |
| // Note: continuations start with nop DropTemps instruction |
| // which acts like an anchor, so we need to skip it. |
| block->set_try_index(yield_continuations()[i].try_index); |
| dispatch <<= yield_continuations()[i].entry->next(); |
| break; |
| } |
| |
| // Build comparison: |
| // |
| // if (:await_jump_var == i) { |
| // -> yield_continuations()[i] |
| // } else ... |
| // |
| TargetEntryInstr* then; |
| TargetEntryInstr* otherwise; |
| dispatch += LoadLocal(scopes()->switch_variable); |
| dispatch += IntConstant(i); |
| dispatch += B->BranchIfStrictEqual(&then, &otherwise); |
| |
| // True branch is linked to appropriate continuation point. |
| // Note: continuations start with nop DropTemps instruction |
| // which acts like an anchor, so we need to skip it. |
| then->LinkTo(yield_continuations()[i].entry->next()); |
| then->set_try_index(yield_continuations()[i].try_index); |
| // False branch will contain the next comparison. |
| dispatch = Fragment(dispatch.entry, otherwise); |
| block = otherwise; |
| } |
| } |
| B->context_depth_ = current_context_depth; |
| return dispatch; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CheckStackOverflowInPrologue( |
| const Function& dart_function) { |
| if (dart_function.is_native()) return {}; |
| return B->CheckStackOverflowInPrologue(dart_function.token_pos()); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::SetupCapturedParameters( |
| const Function& dart_function) { |
| Fragment body; |
| const LocalScope* scope = parsed_function()->scope(); |
| if (scope->num_context_variables() > 0) { |
| body += flow_graph_builder_->PushContext(scope); |
| LocalVariable* context = MakeTemporary(); |
| |
| // Copy captured parameters from the stack into the context. |
| LocalScope* scope = parsed_function()->scope(); |
| intptr_t parameter_count = dart_function.NumParameters(); |
| const ParsedFunction& pf = *flow_graph_builder_->parsed_function_; |
| const Function& function = pf.function(); |
| |
| for (intptr_t i = 0; i < parameter_count; ++i) { |
| LocalVariable* variable = pf.ParameterVariable(i); |
| if (variable->is_captured()) { |
| LocalVariable& raw_parameter = *pf.RawParameterVariable(i); |
| ASSERT((function.HasOptionalParameters() && |
| raw_parameter.owner() == scope) || |
| (!function.HasOptionalParameters() && |
| raw_parameter.owner() == NULL)); |
| ASSERT(!raw_parameter.is_captured()); |
| |
| // Copy the parameter from the stack to the context. |
| body += LoadLocal(context); |
| body += LoadLocal(&raw_parameter); |
| body += flow_graph_builder_->StoreNativeField( |
| Slot::GetContextVariableSlotFor(thread(), *variable), |
| StoreInstanceFieldInstr::Kind::kInitializing); |
| } |
| } |
| body += Drop(); // The context. |
| } |
| return body; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::ShortcutForUserDefinedEquals( |
| const Function& dart_function, |
| LocalVariable* first_parameter) { |
| // The specification defines the result of `a == b` to be: |
| // |
| // a) if either side is `null` then the result is `identical(a, b)`. |
| // b) else the result is `a.operator==(b)` |
| // |
| // For user-defined implementations of `operator==` we need therefore |
| // implement the handling of a). |
| // |
| // The default `operator==` implementation in `Object` is implemented in terms |
| // of identical (which we assume here!) which means that case a) is actually |
| // included in b). So we just use the normal implementation in the body. |
| Fragment body; |
| if ((dart_function.NumParameters() == 2) && |
| (dart_function.name() == Symbols::EqualOperator().ptr()) && |
| (dart_function.Owner() != IG->object_store()->object_class())) { |
| TargetEntryInstr* null_entry; |
| TargetEntryInstr* non_null_entry; |
| |
| body += LoadLocal(first_parameter); |
| body += BranchIfNull(&null_entry, &non_null_entry); |
| |
| // The argument was `null` and the receiver is not the null class (we only |
| // go into this branch for user-defined == operators) so we can return |
| // false. |
| Fragment null_fragment(null_entry); |
| null_fragment += Constant(Bool::False()); |
| null_fragment += Return(dart_function.end_token_pos()); |
| |
| body = Fragment(body.entry, non_null_entry); |
| } |
| return body; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildFunctionBody( |
| const Function& dart_function, |
| LocalVariable* first_parameter, |
| bool constructor) { |
| Fragment body; |
| |
| // TODO(27590): Currently the [VariableDeclaration]s from the |
| // initializers will be visible inside the entire body of the constructor. |
| // We should make a separate scope for them. |
| if (constructor) { |
| body += BuildInitializers(Class::Handle(Z, dart_function.Owner())); |
| } |
| |
| if (body.is_closed()) return body; |
| |
| FunctionNodeHelper function_node_helper(this); |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kBody); |
| |
| const bool has_body = ReadTag() == kSomething; // read first part of body. |
| |
| if (dart_function.is_native()) { |
| body += B->NativeFunctionBody(dart_function, first_parameter); |
| } else if (has_body) { |
| body += BuildStatement(); |
| } else if (dart_function.is_external()) { |
| body += ThrowNoSuchMethodError(dart_function); |
| } |
| |
| if (body.is_open()) { |
| body += NullConstant(); |
| body += Return(dart_function.end_token_pos()); |
| } |
| |
| return body; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildEveryTimePrologue( |
| const Function& dart_function, |
| TokenPosition token_position, |
| intptr_t type_parameters_offset) { |
| Fragment F; |
| F += CheckStackOverflowInPrologue(dart_function); |
| F += DebugStepCheckInPrologue(dart_function, token_position); |
| F += B->InitConstantParameters(); |
| return F; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildFirstTimePrologue( |
| const Function& dart_function, |
| LocalVariable* first_parameter, |
| intptr_t type_parameters_offset) { |
| Fragment F; |
| F += SetupCapturedParameters(dart_function); |
| F += ShortcutForUserDefinedEquals(dart_function, first_parameter); |
| return F; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::ClearRawParameters( |
| const Function& dart_function) { |
| const ParsedFunction& pf = *flow_graph_builder_->parsed_function_; |
| Fragment code; |
| for (intptr_t i = 0; i < dart_function.NumParameters(); ++i) { |
| LocalVariable* variable = pf.ParameterVariable(i); |
| |
| if (!variable->is_captured()) continue; |
| |
| // Captured 'this' is immutable, so within the outer method we don't need to |
| // load it from the context. Therefore we don't reset it to null. |
| if (pf.function().HasThisParameter() && pf.has_receiver_var() && |
| variable == pf.receiver_var()) { |
| ASSERT(i == 0); |
| continue; |
| } |
| |
| variable = pf.RawParameterVariable(i); |
| code += NullConstant(); |
| code += StoreLocal(TokenPosition::kNoSource, variable); |
| code += Drop(); |
| } |
| return code; |
| } |
| |
| UncheckedEntryPointStyle StreamingFlowGraphBuilder::ChooseEntryPointStyle( |
| const Function& dart_function, |
| const Fragment& implicit_type_checks, |
| const Fragment& first_time_prologue, |
| const Fragment& every_time_prologue, |
| const Fragment& type_args_handling) { |
| ASSERT(!dart_function.IsImplicitClosureFunction()); |
| if (!dart_function.MayHaveUncheckedEntryPoint() || |
| implicit_type_checks.is_empty()) { |
| return UncheckedEntryPointStyle::kNone; |
| } |
| |
| // Record which entry-point was taken into a variable and test it later if |
| // either: |
| // |
| // 1. There is a non-empty PrologueBuilder-prologue. |
| // |
| // 2. There is a non-empty "first-time" prologue. |
| // |
| // 3. The "every-time" prologue has more than two instructions (DebugStepCheck |
| // and CheckStackOverflow). |
| // |
| // TODO(#34162): For regular closures we can often avoid the |
| // PrologueBuilder-prologue on non-dynamic invocations. |
| if (!PrologueBuilder::HasEmptyPrologue(dart_function) || |
| !type_args_handling.is_empty() || !first_time_prologue.is_empty() || |
| !(every_time_prologue.entry == every_time_prologue.current || |
| every_time_prologue.current->previous() == every_time_prologue.entry)) { |
| return UncheckedEntryPointStyle::kSharedWithVariable; |
| } |
| |
| return UncheckedEntryPointStyle::kSeparate; |
| } |
| |
| FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFunction( |
| bool is_constructor) { |
| const Function& dart_function = parsed_function()->function(); |
| |
| intptr_t type_parameters_offset = 0; |
| LocalVariable* first_parameter = nullptr; |
| TokenPosition token_position = TokenPosition::kNoSource; |
| { |
| AlternativeReadingScope alt(&reader_); |
| FunctionNodeHelper function_node_helper(this); |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kTypeParameters); |
| type_parameters_offset = ReaderOffset(); |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kPositionalParameters); |
| intptr_t list_length = ReadListLength(); // read number of positionals. |
| if (list_length > 0) { |
| intptr_t first_parameter_offset = ReaderOffset() + data_program_offset_; |
| first_parameter = LookupVariable(first_parameter_offset); |
| } |
| token_position = function_node_helper.position_; |
| } |
| |
| auto graph_entry = flow_graph_builder_->graph_entry_ = |
| new (Z) GraphEntryInstr(*parsed_function(), flow_graph_builder_->osr_id_); |
| |
| auto normal_entry = flow_graph_builder_->BuildFunctionEntry(graph_entry); |
| graph_entry->set_normal_entry(normal_entry); |
| |
| PrologueInfo prologue_info(-1, -1); |
| BlockEntryInstr* instruction_cursor = |
| flow_graph_builder_->BuildPrologue(normal_entry, &prologue_info); |
| |
| // The 'every_time_prologue' runs first and is run when resuming from yield |
| // points. |
| const Fragment every_time_prologue = BuildEveryTimePrologue( |
| dart_function, token_position, type_parameters_offset); |
| |
| // The 'first_time_prologue' run after 'every_time_prologue' and is *not* run |
| // when resuming from yield points. |
| const Fragment first_time_prologue = BuildFirstTimePrologue( |
| dart_function, first_parameter, type_parameters_offset); |
| |
| // TODO(#34162): We can remove the default type handling (and |
| // shorten the prologue type handling sequence) for non-dynamic invocations of |
| // regular methods. |
| const Fragment type_args_handling = TypeArgumentsHandling(dart_function); |
| |
| Fragment implicit_type_checks; |
| if (dart_function.NeedsTypeArgumentTypeChecks()) { |
| B->BuildTypeArgumentTypeChecks( |
| TypeChecksToBuild::kCheckCovariantTypeParameterBounds, |
| &implicit_type_checks); |
| } |
| |
| Fragment explicit_type_checks; |
| Fragment implicit_redefinitions; |
| if (dart_function.NeedsArgumentTypeChecks()) { |
| B->BuildArgumentTypeChecks(&explicit_type_checks, &implicit_type_checks, |
| &implicit_redefinitions); |
| } |
| |
| // The RawParameter variables should be set to null to avoid retaining more |
| // objects than necessary during GC. |
| const Fragment body = |
| ClearRawParameters(dart_function) + B->BuildNullAssertions() + |
| BuildFunctionBody(dart_function, first_parameter, is_constructor); |
| |
| auto extra_entry_point_style = ChooseEntryPointStyle( |
| dart_function, implicit_type_checks, first_time_prologue, |
| every_time_prologue, type_args_handling); |
| |
| Fragment function(instruction_cursor); |
| if (yield_continuations().is_empty()) { |
| FunctionEntryInstr* extra_entry = nullptr; |
| switch (extra_entry_point_style) { |
| case UncheckedEntryPointStyle::kNone: { |
| function += every_time_prologue + first_time_prologue + |
| type_args_handling + implicit_type_checks + |
| explicit_type_checks + body; |
| break; |
| } |
| case UncheckedEntryPointStyle::kSeparate: { |
| ASSERT(instruction_cursor == normal_entry); |
| ASSERT(first_time_prologue.is_empty()); |
| ASSERT(type_args_handling.is_empty()); |
| |
| const Fragment prologue_copy = BuildEveryTimePrologue( |
| dart_function, token_position, type_parameters_offset); |
| |
| extra_entry = B->BuildSeparateUncheckedEntryPoint( |
| normal_entry, |
| /*normal_prologue=*/every_time_prologue + implicit_type_checks, |
| /*extra_prologue=*/prologue_copy, |
| /*shared_prologue=*/explicit_type_checks, |
| /*body=*/body); |
| break; |
| } |
| case UncheckedEntryPointStyle::kSharedWithVariable: { |
| Fragment prologue(normal_entry, instruction_cursor); |
| prologue += every_time_prologue; |
| prologue += first_time_prologue; |
| prologue += type_args_handling; |
| prologue += explicit_type_checks; |
| extra_entry = B->BuildSharedUncheckedEntryPoint( |
| /*shared_prologue_linked_in=*/prologue, |
| /*skippable_checks=*/implicit_type_checks, |
| /*redefinitions_if_skipped=*/implicit_redefinitions, |
| /*body=*/body); |
| break; |
| } |
| } |
| if (extra_entry != nullptr) { |
| B->RecordUncheckedEntryPoint(graph_entry, extra_entry); |
| } |
| } else { |
| // If the function's body contains any yield points, build switch statement |
| // that selects a continuation point based on the value of :await_jump_var. |
| ASSERT(explicit_type_checks.is_empty()); |
| |
| // If the function is generic, type_args_handling might require access to |
| // (possibly captured) 'this' for preparing default type arguments, in which |
| // case we can't run it before the 'first_time_prologue'. |
| ASSERT(!dart_function.IsGeneric()); |
| |
| // TODO(#34162): We can probably ignore the implicit checks |
| // here as well since the arguments are passed from generated code. |
| function += every_time_prologue + type_args_handling + |
| CompleteBodyWithYieldContinuations(first_time_prologue + |
| implicit_type_checks + body); |
| } |
| |
| // When compiling for OSR, use a depth first search to find the OSR |
| // entry and make graph entry jump to it instead of normal entry. |
| // Catch entries are always considered reachable, even if they |
| // become unreachable after OSR. |
| if (flow_graph_builder_->IsCompiledForOsr()) { |
| graph_entry->RelinkToOsrEntry(Z, |
| flow_graph_builder_->last_used_block_id_ + 1); |
| } |
| return new (Z) |
| FlowGraph(*parsed_function(), graph_entry, |
| flow_graph_builder_->last_used_block_id_, prologue_info); |
| } |
| |
| FlowGraph* StreamingFlowGraphBuilder::BuildGraph() { |
| ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull()); |
| ASSERT(flow_graph_builder_ != nullptr); |
| |
| const Function& function = parsed_function()->function(); |
| |
| // Setup a [ActiveClassScope] and a [ActiveMemberScope] which will be used |
| // e.g. for type translation. |
| const Class& klass = |
| Class::Handle(zone_, parsed_function()->function().Owner()); |
| Function& outermost_function = |
| Function::Handle(Z, function.GetOutermostFunction()); |
| |
| ActiveClassScope active_class_scope(active_class(), &klass); |
| ActiveMemberScope active_member(active_class(), &outermost_function); |
| FunctionType& signature = FunctionType::Handle(Z, function.signature()); |
| ActiveTypeParametersScope active_type_params(active_class(), function, |
| &signature, Z); |
| |
| ParseKernelASTFunction(); |
| |
| switch (function.kind()) { |
| case UntaggedFunction::kRegularFunction: |
| case UntaggedFunction::kGetterFunction: |
| case UntaggedFunction::kSetterFunction: |
| case UntaggedFunction::kClosureFunction: |
| case UntaggedFunction::kConstructor: { |
| if (B->IsRecognizedMethodForFlowGraph(function)) { |
| return B->BuildGraphOfRecognizedMethod(function); |
| } |
| return BuildGraphOfFunction(function.IsGenerativeConstructor()); |
| } |
| case UntaggedFunction::kImplicitGetter: |
| case UntaggedFunction::kImplicitStaticGetter: |
| case UntaggedFunction::kImplicitSetter: { |
| return B->BuildGraphOfFieldAccessor(function); |
| } |
| case UntaggedFunction::kFieldInitializer: |
| return BuildGraphOfFieldInitializer(); |
| case UntaggedFunction::kDynamicInvocationForwarder: |
| return B->BuildGraphOfDynamicInvocationForwarder(function); |
| case UntaggedFunction::kMethodExtractor: |
| return flow_graph_builder_->BuildGraphOfMethodExtractor(function); |
| case UntaggedFunction::kNoSuchMethodDispatcher: |
| return flow_graph_builder_->BuildGraphOfNoSuchMethodDispatcher(function); |
| case UntaggedFunction::kInvokeFieldDispatcher: |
| return flow_graph_builder_->BuildGraphOfInvokeFieldDispatcher(function); |
| case UntaggedFunction::kImplicitClosureFunction: |
| return flow_graph_builder_->BuildGraphOfImplicitClosureFunction(function); |
| case UntaggedFunction::kFfiTrampoline: |
| return flow_graph_builder_->BuildGraphOfFfiTrampoline(function); |
| case UntaggedFunction::kIrregexpFunction: |
| break; |
| } |
| UNREACHABLE(); |
| return NULL; |
| } |
| |
| void StreamingFlowGraphBuilder::ParseKernelASTFunction() { |
| const Function& function = parsed_function()->function(); |
| |
| const intptr_t kernel_offset = function.kernel_offset(); |
| ASSERT(kernel_offset >= 0); |
| |
| SetOffset(kernel_offset); |
| |
| // Mark forwarding stubs. |
| switch (function.kind()) { |
| case UntaggedFunction::kRegularFunction: |
| case UntaggedFunction::kImplicitClosureFunction: |
| case UntaggedFunction::kGetterFunction: |
| case UntaggedFunction::kSetterFunction: |
| case UntaggedFunction::kClosureFunction: |
| case UntaggedFunction::kConstructor: |
| case UntaggedFunction::kDynamicInvocationForwarder: |
| ReadForwardingStubTarget(function); |
| break; |
| default: |
| break; |
| } |
| |
| set_scopes(parsed_function()->EnsureKernelScopes()); |
| |
| switch (function.kind()) { |
| case UntaggedFunction::kRegularFunction: |
| case UntaggedFunction::kGetterFunction: |
| case UntaggedFunction::kSetterFunction: |
| case UntaggedFunction::kClosureFunction: |
| case UntaggedFunction::kConstructor: |
| case UntaggedFunction::kImplicitClosureFunction: |
| ReadUntilFunctionNode(); |
| SetupDefaultParameterValues(); |
| break; |
| case UntaggedFunction::kImplicitGetter: |
| case UntaggedFunction::kImplicitStaticGetter: |
| case UntaggedFunction::kImplicitSetter: |
| case UntaggedFunction::kFieldInitializer: |
| case UntaggedFunction::kMethodExtractor: |
| case UntaggedFunction::kNoSuchMethodDispatcher: |
| case UntaggedFunction::kInvokeFieldDispatcher: |
| case UntaggedFunction::kFfiTrampoline: |
| break; |
| case UntaggedFunction::kDynamicInvocationForwarder: |
| if (PeekTag() != kField) { |
| ReadUntilFunctionNode(); |
| SetupDefaultParameterValues(); |
| } |
| break; |
| case UntaggedFunction::kIrregexpFunction: |
| UNREACHABLE(); |
| break; |
| } |
| } |
| |
| void StreamingFlowGraphBuilder::ReadForwardingStubTarget( |
| const Function& function) { |
| if (PeekTag() == kProcedure) { |
| AlternativeReadingScope alt(&reader_); |
| ProcedureHelper procedure_helper(this); |
| procedure_helper.ReadUntilExcluding(ProcedureHelper::kFunction); |
| if (procedure_helper.IsForwardingStub() && !procedure_helper.IsAbstract()) { |
| const NameIndex target_name = |
| procedure_helper.concrete_forwarding_stub_target_; |
| ASSERT(target_name != NameIndex::kInvalidName); |
| const String& name = function.IsSetterFunction() |
| ? H.DartSetterName(target_name) |
| : H.DartProcedureName(target_name); |
| const Function* forwarding_target = |
| &Function::ZoneHandle(Z, H.LookupMethodByMember(target_name, name)); |
| ASSERT(!forwarding_target->IsNull()); |
| parsed_function()->MarkForwardingStub(forwarding_target); |
| } |
| } |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildStatementAt(intptr_t kernel_offset) { |
| SetOffset(kernel_offset); |
| return BuildStatement(); // read statement. |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildExpression(TokenPosition* position) { |
| uint8_t payload = 0; |
| Tag tag = ReadTag(&payload); // read tag. |
| switch (tag) { |
| case kInvalidExpression: |
| return BuildInvalidExpression(position); |
| case kVariableGet: |
| return BuildVariableGet(position); |
| case kSpecializedVariableGet: |
| return BuildVariableGet(payload, position); |
| case kVariableSet: |
| return BuildVariableSet(position); |
| case kSpecializedVariableSet: |
| return BuildVariableSet(payload, position); |
| case kInstanceGet: |
| return BuildInstanceGet(position); |
| case kDynamicGet: |
| return BuildDynamicGet(position); |
| case kInstanceTearOff: |
| return BuildInstanceTearOff(position); |
| case kFunctionTearOff: |
| return BuildFunctionTearOff(position); |
| case kInstanceSet: |
| return BuildInstanceSet(position); |
| case kDynamicSet: |
| return BuildDynamicSet(position); |
| case kSuperPropertyGet: |
| return BuildSuperPropertyGet(position); |
| case kSuperPropertySet: |
| return BuildSuperPropertySet(position); |
| case kStaticGet: |
| return BuildStaticGet(position); |
| case kStaticSet: |
| return BuildStaticSet(position); |
| case kInstanceInvocation: |
| return BuildMethodInvocation(position, /*is_dynamic=*/false); |
| case kDynamicInvocation: |
| return BuildMethodInvocation(position, /*is_dynamic=*/true); |
| case kLocalFunctionInvocation: |
| return BuildLocalFunctionInvocation(position); |
| case kFunctionInvocation: |
| return BuildFunctionInvocation(position); |
| case kEqualsCall: |
| return BuildEqualsCall(position); |
| case kEqualsNull: |
| return BuildEqualsNull(position); |
| case kSuperMethodInvocation: |
| return BuildSuperMethodInvocation(position); |
| case kStaticInvocation: |
| return BuildStaticInvocation(position); |
| case kConstructorInvocation: |
| return BuildConstructorInvocation(position); |
| case kNot: |
| return BuildNot(position); |
| case kNullCheck: |
| return BuildNullCheck(position); |
| case kLogicalExpression: |
| return BuildLogicalExpression(position); |
| case kConditionalExpression: |
| return BuildConditionalExpression(position); |
| case kStringConcatenation: |
| return BuildStringConcatenation(position); |
| case kIsExpression: |
| return BuildIsExpression(position); |
| case kAsExpression: |
| return BuildAsExpression(position); |
| case kTypeLiteral: |
| return BuildTypeLiteral(position); |
| case kThisExpression: |
| return BuildThisExpression(position); |
| case kRethrow: |
| return BuildRethrow(position); |
| case kThrow: |
| return BuildThrow(position); |
| case kListLiteral: |
| return BuildListLiteral(position); |
| case kSetLiteral: |
| // Set literals are currently desugared in the frontend and will not |
| // reach the VM. See http://dartbug.com/35124 for discussion. |
| UNREACHABLE(); |
| break; |
| case kMapLiteral: |
| return BuildMapLiteral(position); |
| case kFunctionExpression: |
| return BuildFunctionExpression(); |
| case kLet: |
| return BuildLet(position); |
| case kBlockExpression: |
| return BuildBlockExpression(); |
| case kBigIntLiteral: |
| return BuildBigIntLiteral(position); |
| case kStringLiteral: |
| return BuildStringLiteral(position); |
| case kSpecializedIntLiteral: |
| return BuildIntLiteral(payload, position); |
| case kNegativeIntLiteral: |
| return BuildIntLiteral(true, position); |
| case kPositiveIntLiteral: |
| return BuildIntLiteral(false, position); |
| case kDoubleLiteral: |
| return BuildDoubleLiteral(position); |
| case kTrueLiteral: |
| return BuildBoolLiteral(true, position); |
| case kFalseLiteral: |
| return BuildBoolLiteral(false, position); |
| case kNullLiteral: |
| return BuildNullLiteral(position); |
| case kConstantExpression: |
| return BuildConstantExpression(position, tag); |
| case kInstantiation: |
| return BuildPartialTearoffInstantiation(position); |
| case kLoadLibrary: |
| return BuildLibraryPrefixAction(position, Symbols::LoadLibrary()); |
| case kCheckLibraryIsLoaded: |
| return BuildLibraryPrefixAction(position, Symbols::CheckLoaded()); |
| case kConstStaticInvocation: |
| case kConstConstructorInvocation: |
| case kConstListLiteral: |
| case kConstSetLiteral: |
| case kConstMapLiteral: |
| case kSymbolLiteral: |
| case kListConcatenation: |
| case kSetConcatenation: |
| case kMapConcatenation: |
| case kInstanceCreation: |
| case kFileUriExpression: |
| case kStaticTearOff: |
| // These nodes are internal to the front end and |
| // removed by the constant evaluator. |
| default: |
| ReportUnexpectedTag("expression", tag); |
| UNREACHABLE(); |
| } |
| |
| return Fragment(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildStatement() { |
| intptr_t offset = ReaderOffset(); |
| Tag tag = ReadTag(); // read tag. |
| switch (tag) { |
| case kExpressionStatement: |
| return BuildExpressionStatement(); |
| case kBlock: |
| return BuildBlock(); |
| case kEmptyStatement: |
| return BuildEmptyStatement(); |
| case kAssertBlock: |
| return BuildAssertBlock(); |
| case kAssertStatement: |
| return BuildAssertStatement(); |
| case kLabeledStatement: |
| return BuildLabeledStatement(); |
| case kBreakStatement: |
| return BuildBreakStatement(); |
| case kWhileStatement: |
| return BuildWhileStatement(); |
| case kDoStatement: |
| return BuildDoStatement(); |
| case kForStatement: |
| return BuildForStatement(); |
| case kForInStatement: |
| return BuildForInStatement(false); |
| case kAsyncForInStatement: |
| return BuildForInStatement(true); |
| case kSwitchStatement: |
| return BuildSwitchStatement(); |
| case kContinueSwitchStatement: |
| return BuildContinueSwitchStatement(); |
| case kIfStatement: |
| return BuildIfStatement(); |
| case kReturnStatement: |
| return BuildReturnStatement(); |
| case kTryCatch: |
| return BuildTryCatch(); |
| case kTryFinally: |
| return BuildTryFinally(); |
| case kYieldStatement: |
| return BuildYieldStatement(); |
| case kVariableDeclaration: |
| return BuildVariableDeclaration(); |
| case kFunctionDeclaration: |
| return BuildFunctionDeclaration(offset); |
| default: |
| ReportUnexpectedTag("statement", tag); |
| UNREACHABLE(); |
| } |
| return Fragment(); |
| } |
| |
| void StreamingFlowGraphBuilder::ReportUnexpectedTag(const char* variant, |
| Tag tag) { |
| if ((flow_graph_builder_ == NULL) || (parsed_function() == NULL)) { |
| KernelReaderHelper::ReportUnexpectedTag(variant, tag); |
| } else { |
| H.ReportError(script_, TokenPosition::kNoSource, |
| "Unexpected tag %d (%s) in %s, expected %s", tag, |
| Reader::TagName(tag), |
| parsed_function()->function().ToQualifiedCString(), variant); |
| } |
| } |
| |
| Tag KernelReaderHelper::ReadTag(uint8_t* payload) { |
| return reader_.ReadTag(payload); |
| } |
| |
| Tag KernelReaderHelper::PeekTag(uint8_t* payload) { |
| return reader_.PeekTag(payload); |
| } |
| |
| Nullability KernelReaderHelper::ReadNullability() { |
| return reader_.ReadNullability(); |
| } |
| |
| Variance KernelReaderHelper::ReadVariance() { |
| if (translation_helper_.info().kernel_binary_version() >= 34) { |
| return reader_.ReadVariance(); |
| } |
| return kCovariant; |
| } |
| |
| void StreamingFlowGraphBuilder::loop_depth_inc() { |
| ++flow_graph_builder_->loop_depth_; |
| } |
| |
| void StreamingFlowGraphBuilder::loop_depth_dec() { |
| --flow_graph_builder_->loop_depth_; |
| } |
| |
| intptr_t StreamingFlowGraphBuilder::for_in_depth() { |
| return flow_graph_builder_->for_in_depth_; |
| } |
| |
| void StreamingFlowGraphBuilder::for_in_depth_inc() { |
| ++flow_graph_builder_->for_in_depth_; |
| } |
| |
| void StreamingFlowGraphBuilder::for_in_depth_dec() { |
| --flow_graph_builder_->for_in_depth_; |
| } |
| |
| void StreamingFlowGraphBuilder::catch_depth_inc() { |
| ++flow_graph_builder_->catch_depth_; |
| } |
| |
| void StreamingFlowGraphBuilder::catch_depth_dec() { |
| --flow_graph_builder_->catch_depth_; |
| } |
| |
| void StreamingFlowGraphBuilder::try_depth_inc() { |
| ++flow_graph_builder_->try_depth_; |
| } |
| |
| void StreamingFlowGraphBuilder::try_depth_dec() { |
| --flow_graph_builder_->try_depth_; |
| } |
| |
| intptr_t StreamingFlowGraphBuilder::block_expression_depth() { |
| return flow_graph_builder_->block_expression_depth_; |
| } |
| |
| void StreamingFlowGraphBuilder::block_expression_depth_inc() { |
| ++flow_graph_builder_->block_expression_depth_; |
| } |
| |
| void StreamingFlowGraphBuilder::block_expression_depth_dec() { |
| --flow_graph_builder_->block_expression_depth_; |
| } |
| |
| intptr_t StreamingFlowGraphBuilder::CurrentTryIndex() { |
| return flow_graph_builder_->CurrentTryIndex(); |
| } |
| |
| intptr_t StreamingFlowGraphBuilder::AllocateTryIndex() { |
| return flow_graph_builder_->AllocateTryIndex(); |
| } |
| |
| LocalVariable* StreamingFlowGraphBuilder::CurrentException() { |
| return flow_graph_builder_->CurrentException(); |
| } |
| |
| LocalVariable* StreamingFlowGraphBuilder::CurrentStackTrace() { |
| return flow_graph_builder_->CurrentStackTrace(); |
| } |
| |
| CatchBlock* StreamingFlowGraphBuilder::catch_block() { |
| return flow_graph_builder_->catch_block_; |
| } |
| |
| ActiveClass* StreamingFlowGraphBuilder::active_class() { |
| return active_class_; |
| } |
| |
| ScopeBuildingResult* StreamingFlowGraphBuilder::scopes() { |
| return flow_graph_builder_->scopes_; |
| } |
| |
| void StreamingFlowGraphBuilder::set_scopes(ScopeBuildingResult* scope) { |
| flow_graph_builder_->scopes_ = scope; |
| } |
| |
| ParsedFunction* StreamingFlowGraphBuilder::parsed_function() { |
| return flow_graph_builder_->parsed_function_; |
| } |
| |
| TryFinallyBlock* StreamingFlowGraphBuilder::try_finally_block() { |
| return flow_graph_builder_->try_finally_block_; |
| } |
| |
| SwitchBlock* StreamingFlowGraphBuilder::switch_block() { |
| return flow_graph_builder_->switch_block_; |
| } |
| |
| BreakableBlock* StreamingFlowGraphBuilder::breakable_block() { |
| return flow_graph_builder_->breakable_block_; |
| } |
| |
| GrowableArray<YieldContinuation>& |
| StreamingFlowGraphBuilder::yield_continuations() { |
| return flow_graph_builder_->yield_continuations_; |
| } |
| |
| Value* StreamingFlowGraphBuilder::stack() { |
| return flow_graph_builder_->stack_; |
| } |
| |
| void StreamingFlowGraphBuilder::Push(Definition* definition) { |
| flow_graph_builder_->Push(definition); |
| } |
| |
| Value* StreamingFlowGraphBuilder::Pop() { |
| return flow_graph_builder_->Pop(); |
| } |
| |
| Tag StreamingFlowGraphBuilder::PeekArgumentsFirstPositionalTag() { |
| // read parts of arguments, then go back to before doing so. |
| AlternativeReadingScope alt(&reader_); |
| ReadUInt(); // read number of arguments. |
| |
| SkipListOfDartTypes(); // Read list of types. |
| |
| // List of positional. |
| intptr_t list_length = ReadListLength(); // read list length. |
| if (list_length > 0) { |
| return ReadTag(); // read first tag. |
| } |
| |
| UNREACHABLE(); |
| return kNothing; |
| } |
| |
| const TypeArguments& StreamingFlowGraphBuilder::PeekArgumentsInstantiatedType( |
| const Class& klass) { |
| // read parts of arguments, then go back to before doing so. |
| AlternativeReadingScope alt(&reader_); |
| ReadUInt(); // read argument count. |
| intptr_t list_length = ReadListLength(); // read types list length. |
| return T.BuildInstantiatedTypeArguments(klass, list_length); // read types. |
| } |
| |
| intptr_t StreamingFlowGraphBuilder::PeekArgumentsCount() { |
| return PeekUInt(); |
| } |
| |
| LocalVariable* StreamingFlowGraphBuilder::LookupVariable( |
| intptr_t kernel_offset) { |
| return flow_graph_builder_->LookupVariable(kernel_offset); |
| } |
| |
| LocalVariable* StreamingFlowGraphBuilder::MakeTemporary(const char* suffix) { |
| return flow_graph_builder_->MakeTemporary(suffix); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::DropTemporary(LocalVariable** variable) { |
| return flow_graph_builder_->DropTemporary(variable); |
| } |
| |
| Function& StreamingFlowGraphBuilder::FindMatchingFunction( |
| const Class& klass, |
| const String& name, |
| int type_args_len, |
| int argument_count, |
| const Array& argument_names) { |
| // Search the superclass chain for the selector. |
| ArgumentsDescriptor args_desc( |
| Array::Handle(Z, ArgumentsDescriptor::NewBoxed( |
| type_args_len, argument_count, argument_names))); |
| return Function::Handle(Z, |
| Resolver::ResolveDynamicForReceiverClassAllowPrivate( |
| klass, name, args_desc, /*allow_add=*/false)); |
| } |
| |
| bool StreamingFlowGraphBuilder::NeedsDebugStepCheck(const Function& function, |
| TokenPosition position) { |
| return flow_graph_builder_->NeedsDebugStepCheck(function, position); |
| } |
| |
| bool StreamingFlowGraphBuilder::NeedsDebugStepCheck(Value* value, |
| TokenPosition position) { |
| return flow_graph_builder_->NeedsDebugStepCheck(value, position); |
| } |
| |
| void StreamingFlowGraphBuilder::InlineBailout(const char* reason) { |
| flow_graph_builder_->InlineBailout(reason); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::DebugStepCheck(TokenPosition position) { |
| return flow_graph_builder_->DebugStepCheck(position); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::LoadLocal(LocalVariable* variable) { |
| return flow_graph_builder_->LoadLocal(variable); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::IndirectGoto(intptr_t target_count) { |
| return flow_graph_builder_->IndirectGoto(target_count); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::Return(TokenPosition position, |
| intptr_t yield_index) { |
| return flow_graph_builder_->Return(position, /*omit_result_type_check=*/false, |
| yield_index); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::EvaluateAssertion() { |
| return flow_graph_builder_->EvaluateAssertion(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::RethrowException(TokenPosition position, |
| int catch_try_index) { |
| return flow_graph_builder_->RethrowException(position, catch_try_index); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::ThrowNoSuchMethodError( |
| const Function& target) { |
| return flow_graph_builder_->ThrowNoSuchMethodError(target); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::Constant(const Object& value) { |
| return flow_graph_builder_->Constant(value); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::IntConstant(int64_t value) { |
| return flow_graph_builder_->IntConstant(value); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::LoadStaticField(const Field& field, |
| bool calls_initializer) { |
| return flow_graph_builder_->LoadStaticField(field, calls_initializer); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::RedefinitionWithType( |
| const AbstractType& type) { |
| return flow_graph_builder_->RedefinitionWithType(type); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CheckNull( |
| TokenPosition position, |
| LocalVariable* receiver, |
| const String& function_name, |
| bool clear_the_temp /* = true */) { |
| return flow_graph_builder_->CheckNull(position, receiver, function_name, |
| clear_the_temp); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StaticCall(TokenPosition position, |
| const Function& target, |
| intptr_t argument_count, |
| ICData::RebindRule rebind_rule) { |
| if (!target.AreValidArgumentCounts(0, argument_count, 0, nullptr)) { |
| return flow_graph_builder_->ThrowNoSuchMethodError(target); |
| } |
| return flow_graph_builder_->StaticCall(position, target, argument_count, |
| rebind_rule); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StaticCall( |
| TokenPosition position, |
| const Function& target, |
| intptr_t argument_count, |
| const Array& argument_names, |
| ICData::RebindRule rebind_rule, |
| const InferredTypeMetadata* result_type, |
| intptr_t type_args_count, |
| bool use_unchecked_entry) { |
| if (!target.AreValidArguments(type_args_count, argument_count, argument_names, |
| nullptr)) { |
| return flow_graph_builder_->ThrowNoSuchMethodError(target); |
| } |
| return flow_graph_builder_->StaticCall( |
| position, target, argument_count, argument_names, rebind_rule, |
| result_type, type_args_count, use_unchecked_entry); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::InstanceCall( |
| TokenPosition position, |
| const String& name, |
| Token::Kind kind, |
| intptr_t argument_count, |
| intptr_t checked_argument_count) { |
| const intptr_t kTypeArgsLen = 0; |
| return flow_graph_builder_->InstanceCall(position, name, kind, kTypeArgsLen, |
| argument_count, Array::null_array(), |
| checked_argument_count); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::InstanceCall( |
| TokenPosition position, |
| const String& name, |
| Token::Kind kind, |
| intptr_t type_args_len, |
| intptr_t argument_count, |
| const Array& argument_names, |
| intptr_t checked_argument_count, |
| const Function& interface_target, |
| const Function& tearoff_interface_target, |
| const InferredTypeMetadata* result_type, |
| bool use_unchecked_entry, |
| const CallSiteAttributesMetadata* call_site_attrs, |
| bool receiver_is_not_smi) { |
| return flow_graph_builder_->InstanceCall( |
| position, name, kind, type_args_len, argument_count, argument_names, |
| checked_argument_count, interface_target, tearoff_interface_target, |
| result_type, use_unchecked_entry, call_site_attrs, receiver_is_not_smi); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::ThrowException(TokenPosition position) { |
| return flow_graph_builder_->ThrowException(position); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BooleanNegate() { |
| return flow_graph_builder_->BooleanNegate(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::TranslateInstantiatedTypeArguments( |
| const TypeArguments& type_arguments) { |
| return flow_graph_builder_->TranslateInstantiatedTypeArguments( |
| type_arguments); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StrictCompare(TokenPosition position, |
| Token::Kind kind, |
| bool number_check) { |
| return flow_graph_builder_->StrictCompare(position, kind, number_check); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::AllocateObject(TokenPosition position, |
| const Class& klass, |
| intptr_t argument_count) { |
| return flow_graph_builder_->AllocateObject(position, klass, argument_count); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::AllocateContext( |
| const ZoneGrowableArray<const Slot*>& context_slots) { |
| return flow_graph_builder_->AllocateContext(context_slots); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::LoadNativeField(const Slot& field) { |
| return flow_graph_builder_->LoadNativeField(field); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StoreLocal(TokenPosition position, |
| LocalVariable* variable) { |
| return flow_graph_builder_->StoreLocal(position, variable); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StoreStaticField(TokenPosition position, |
| const Field& field) { |
| return flow_graph_builder_->StoreStaticField(position, field); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StringInterpolate(TokenPosition position) { |
| return flow_graph_builder_->StringInterpolate(position); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StringInterpolateSingle( |
| TokenPosition position) { |
| return flow_graph_builder_->StringInterpolateSingle(position); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::ThrowTypeError() { |
| return flow_graph_builder_->ThrowTypeError(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::LoadInstantiatorTypeArguments() { |
| return flow_graph_builder_->LoadInstantiatorTypeArguments(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::LoadFunctionTypeArguments() { |
| return flow_graph_builder_->LoadFunctionTypeArguments(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::InstantiateType(const AbstractType& type) { |
| return flow_graph_builder_->InstantiateType(type); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CreateArray() { |
| return flow_graph_builder_->CreateArray(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StoreIndexed(intptr_t class_id) { |
| return flow_graph_builder_->StoreIndexed(class_id); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CheckStackOverflow(TokenPosition position) { |
| return flow_graph_builder_->CheckStackOverflow( |
| position, flow_graph_builder_->GetStackDepth(), |
| flow_graph_builder_->loop_depth_); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CloneContext( |
| const ZoneGrowableArray<const Slot*>& context_slots) { |
| return flow_graph_builder_->CloneContext(context_slots); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::TranslateFinallyFinalizers( |
| TryFinallyBlock* outer_finally, |
| intptr_t target_context_depth) { |
| // TranslateFinallyFinalizers can move the readers offset. |
| // Save the current position and restore it afterwards. |
| AlternativeReadingScope alt(&reader_); |
| |
| // Save context. |
| TryFinallyBlock* const saved_finally_block = B->try_finally_block_; |
| TryCatchBlock* const saved_try_catch_block = B->CurrentTryCatchBlock(); |
| const intptr_t saved_context_depth = B->context_depth_; |
| const ProgramState state(B->breakable_block_, B->switch_block_, |
| B->loop_depth_, B->for_in_depth_, B->try_depth_, |
| B->catch_depth_, B->block_expression_depth_); |
| |
| Fragment instructions; |
| |
| // While translating the body of a finalizer we need to set the try-finally |
| // block which is active when translating the body. |
| while (B->try_finally_block_ != outer_finally) { |
| ASSERT(B->try_finally_block_ != nullptr); |
| // Adjust program context to finalizer's position. |
| B->try_finally_block_->state().assignTo(B); |
| |
| // Potentially restore the context to what is expected for the finally |
| // block. |
| instructions += B->AdjustContextTo(B->try_finally_block_->context_depth()); |
| |
| // The to-be-translated finalizer has to have the correct try-index (namely |
| // the one outside the try-finally block). |
| bool changed_try_index = false; |
| intptr_t target_try_index = B->try_finally_block_->try_index(); |
| while (B->CurrentTryIndex() != target_try_index) { |
| B->SetCurrentTryCatchBlock(B->CurrentTryCatchBlock()->outer()); |
| changed_try_index = true; |
| } |
| if (changed_try_index) { |
| JoinEntryInstr* entry = BuildJoinEntry(); |
| instructions += Goto(entry); |
| instructions = Fragment(instructions.entry, entry); |
| } |
| |
| intptr_t finalizer_kernel_offset = |
| B->try_finally_block_->finalizer_kernel_offset(); |
| B->try_finally_block_ = B->try_finally_block_->outer(); |
| instructions += BuildStatementAt(finalizer_kernel_offset); |
| |
| // We only need to make sure that if the finalizer ended normally, we |
| // continue towards the next outer try-finally. |
| if (!instructions.is_open()) break; |
| } |
| |
| if (instructions.is_open() && target_context_depth != -1) { |
| // A target context depth of -1 indicates that the code after this |
| // will not care about the context chain so we can leave it any way we |
| // want after the last finalizer. That is used when returning. |
| instructions += B->AdjustContextTo(target_context_depth); |
| } |
| |
| // Restore. |
| B->try_finally_block_ = saved_finally_block; |
| B->SetCurrentTryCatchBlock(saved_try_catch_block); |
| B->context_depth_ = saved_context_depth; |
| state.assignTo(B); |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BranchIfTrue( |
| TargetEntryInstr** then_entry, |
| TargetEntryInstr** otherwise_entry, |
| bool negate) { |
| return flow_graph_builder_->BranchIfTrue(then_entry, otherwise_entry, negate); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BranchIfEqual( |
| TargetEntryInstr** then_entry, |
| TargetEntryInstr** otherwise_entry, |
| bool negate) { |
| return flow_graph_builder_->BranchIfEqual(then_entry, otherwise_entry, |
| negate); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BranchIfNull( |
| TargetEntryInstr** then_entry, |
| TargetEntryInstr** otherwise_entry, |
| bool negate) { |
| return flow_graph_builder_->BranchIfNull(then_entry, otherwise_entry, negate); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CatchBlockEntry(const Array& handler_types, |
| intptr_t handler_index, |
| bool needs_stacktrace, |
| bool is_synthesized) { |
| return flow_graph_builder_->CatchBlockEntry(handler_types, handler_index, |
| needs_stacktrace, is_synthesized); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::TryCatch(int try_handler_index) { |
| return flow_graph_builder_->TryCatch(try_handler_index); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::Drop() { |
| return flow_graph_builder_->Drop(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::DropTempsPreserveTop( |
| intptr_t num_temps_to_drop) { |
| return flow_graph_builder_->DropTempsPreserveTop(num_temps_to_drop); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::MakeTemp() { |
| return flow_graph_builder_->MakeTemp(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::NullConstant() { |
| return flow_graph_builder_->NullConstant(); |
| } |
| |
| JoinEntryInstr* StreamingFlowGraphBuilder::BuildJoinEntry() { |
| return flow_graph_builder_->BuildJoinEntry(); |
| } |
| |
| JoinEntryInstr* StreamingFlowGraphBuilder::BuildJoinEntry(intptr_t try_index) { |
| return flow_graph_builder_->BuildJoinEntry(try_index); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::Goto(JoinEntryInstr* destination) { |
| return flow_graph_builder_->Goto(destination); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildImplicitClosureCreation( |
| const Function& target) { |
| return flow_graph_builder_->BuildImplicitClosureCreation(target); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CheckBoolean(TokenPosition position) { |
| return flow_graph_builder_->CheckBoolean(position); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CheckArgumentType( |
| LocalVariable* variable, |
| const AbstractType& type) { |
| return flow_graph_builder_->CheckAssignable( |
| type, variable->name(), AssertAssignableInstr::kParameterCheck); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::EnterScope( |
| intptr_t kernel_offset, |
| const LocalScope** scope /* = nullptr */) { |
| return flow_graph_builder_->EnterScope(kernel_offset, scope); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::ExitScope(intptr_t kernel_offset) { |
| return flow_graph_builder_->ExitScope(kernel_offset); |
| } |
| |
| TestFragment StreamingFlowGraphBuilder::TranslateConditionForControl() { |
| // Skip all negations and go directly to the expression. |
| bool negate = false; |
| while (PeekTag() == kNot) { |
| SkipBytes(1); |
| negate = !negate; |
| } |
| |
| TestFragment result; |
| if (PeekTag() == kLogicalExpression) { |
| // Handle '&&' and '||' operators specially to implement short circuit |
| // evaluation. |
| SkipBytes(1); // tag. |
| |
| TestFragment left = TranslateConditionForControl(); |
| LogicalOperator op = static_cast<LogicalOperator>(ReadByte()); |
| TestFragment right = TranslateConditionForControl(); |
| |
| result.entry = left.entry; |
| if (op == kAnd) { |
| left.CreateTrueSuccessor(flow_graph_builder_)->LinkTo(right.entry); |
| result.true_successor_addresses = right.true_successor_addresses; |
| result.false_successor_addresses = left.false_successor_addresses; |
| result.false_successor_addresses->AddArray( |
| *right.false_successor_addresses); |
| } else { |
| ASSERT(op == kOr); |
| left.CreateFalseSuccessor(flow_graph_builder_)->LinkTo(right.entry); |
| result.true_successor_addresses = left.true_successor_addresses; |
| result.true_successor_addresses->AddArray( |
| *right.true_successor_addresses); |
| result.false_successor_addresses = right.false_successor_addresses; |
| } |
| } else { |
| // Other expressions. |
| TokenPosition position = TokenPosition::kNoSource; |
| Fragment instructions = BuildExpression(&position); // read expression. |
| |
| // Check if the top of the stack is already a StrictCompare that |
| // can be merged with a branch. Otherwise compare TOS with |
| // true value and branch on that. |
| BranchInstr* branch; |
| if (stack()->definition()->IsStrictCompare() && |
| stack()->definition() == instructions.current) { |
| StrictCompareInstr* compare = Pop()->definition()->AsStrictCompare(); |
| if (negate) { |
| compare->NegateComparison(); |
| negate = false; |
| } |
| branch = |
| new (Z) BranchInstr(compare, flow_graph_builder_->GetNextDeoptId()); |
| branch->comparison()->ClearTempIndex(); |
| ASSERT(instructions.current->previous() != nullptr); |
| instructions.current = instructions.current->previous(); |
| } else { |
| instructions += CheckBoolean(position); |
| instructions += Constant(Bool::True()); |
| Value* right_value = Pop(); |
| Value* left_value = Pop(); |
| StrictCompareInstr* compare = new (Z) StrictCompareInstr( |
| InstructionSource(), negate ? Token::kNE_STRICT : Token::kEQ_STRICT, |
| left_value, right_value, false, |
| flow_graph_builder_->GetNextDeoptId()); |
| branch = |
| new (Z) BranchInstr(compare, flow_graph_builder_->GetNextDeoptId()); |
| negate = false; |
| } |
| instructions <<= branch; |
| |
| result = TestFragment(instructions.entry, branch); |
| } |
| |
| return result.Negate(negate); |
| } |
| |
| const TypeArguments& StreamingFlowGraphBuilder::BuildTypeArguments() { |
| ReadUInt(); // read arguments count. |
| intptr_t type_count = ReadListLength(); // read type count. |
| return T.BuildTypeArguments(type_count); // read types. |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildArguments(Array* argument_names, |
| intptr_t* argument_count, |
| intptr_t* positional_count) { |
| intptr_t dummy; |
| if (argument_count == NULL) argument_count = &dummy; |
| *argument_count = ReadUInt(); // read arguments count. |
| |
| // List of types. |
| SkipListOfDartTypes(); // read list of types. |
| |
| { |
| AlternativeReadingScope _(&reader_); |
| if (positional_count == NULL) positional_count = &dummy; |
| *positional_count = ReadListLength(); // read length of expression list |
| } |
| return BuildArgumentsFromActualArguments(argument_names); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildArgumentsFromActualArguments( |
| Array* argument_names) { |
| Fragment instructions; |
| |
| // List of positional. |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| instructions += BuildExpression(); // read ith expression. |
| } |
| |
| // List of named. |
| list_length = ReadListLength(); // read list length. |
| if (argument_names != NULL && list_length > 0) { |
| *argument_names = Array::New(list_length, Heap::kOld); |
| } |
| for (intptr_t i = 0; i < list_length; ++i) { |
| String& name = |
| H.DartSymbolObfuscate(ReadStringReference()); // read ith name index. |
| instructions += BuildExpression(); // read ith expression. |
| if (argument_names != NULL) { |
| argument_names->SetAt(i, name); |
| } |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildInvalidExpression( |
| TokenPosition* position) { |
| // The frontend will take care of emitting normal errors (like |
| // [NoSuchMethodError]s) and only emit [InvalidExpression]s in very special |
| // situations (e.g. an invalid annotation). |
| TokenPosition pos = ReadPosition(); |
| if (position != NULL) *position = pos; |
| const String& message = H.DartString(ReadStringReference()); |
| // Invalid expression message has pointer to the source code, no need to |
| // report it twice. |
| H.ReportError(script(), TokenPosition::kNoSource, "%s", message.ToCString()); |
| return Fragment(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildVariableGet(TokenPosition* position) { |
| const TokenPosition pos = ReadPosition(); |
| if (position != nullptr) *position = pos; |
| intptr_t variable_kernel_position = ReadUInt(); // read kernel position. |
| ReadUInt(); // read relative variable index. |
| SkipOptionalDartType(); // read promoted type. |
| return BuildVariableGetImpl(variable_kernel_position, pos); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildVariableGet(uint8_t payload, |
| TokenPosition* position) { |
| const TokenPosition pos = ReadPosition(); |
| if (position != nullptr) *position = pos; |
| intptr_t variable_kernel_position = ReadUInt(); // read kernel position. |
| return BuildVariableGetImpl(variable_kernel_position, pos); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildVariableGetImpl( |
| intptr_t variable_kernel_position, |
| TokenPosition position) { |
| LocalVariable* variable = LookupVariable(variable_kernel_position); |
| if (!variable->is_late()) { |
| return LoadLocal(variable); |
| } |
| |
| // Late variable, so check whether it has been initialized already. |
| Fragment instructions = LoadLocal(variable); |
| TargetEntryInstr* is_uninitialized; |
| TargetEntryInstr* is_initialized; |
| instructions += Constant(Object::sentinel()); |
| instructions += flow_graph_builder_->BranchIfStrictEqual(&is_uninitialized, |
| &is_initialized); |
| JoinEntryInstr* join = BuildJoinEntry(); |
| |
| { |
| AlternativeReadingScope alt(&reader_, variable->late_init_offset()); |
| const bool has_initializer = (ReadTag() != kNothing); |
| |
| if (has_initializer) { |
| // If the variable isn't initialized, call the initializer and set it. |
| Fragment initialize(is_uninitialized); |
| initialize += BuildExpression(); |
| if (variable->is_final()) { |
| // Late final variable, so check whether it has been assigned |
| // during initialization. |
| initialize += LoadLocal(variable); |
| TargetEntryInstr* is_uninitialized_after_init; |
| TargetEntryInstr* is_initialized_after_init; |
| initialize += Constant(Object::sentinel()); |
| initialize += flow_graph_builder_->BranchIfStrictEqual( |
| &is_uninitialized_after_init, &is_initialized_after_init); |
| { |
| // The variable is uninitialized, so store the initializer result. |
| Fragment store_result(is_uninitialized_after_init); |
| store_result += StoreLocal(position, variable); |
| store_result += Drop(); |
| store_result += Goto(join); |
| } |
| |
| { |
| // Already initialized, so throw a LateInitializationError. |
| Fragment already_assigned(is_initialized_after_init); |
| already_assigned += flow_graph_builder_->ThrowLateInitializationError( |
| position, "_throwLocalAssignedDuringInitialization", |
| variable->name()); |
| already_assigned += Goto(join); |
| } |
| } else { |
| // Late non-final variable. Store the initializer result. |
| initialize += StoreLocal(position, variable); |
| initialize += Drop(); |
| initialize += Goto(join); |
| } |
| } else { |
| // The variable has no initializer, so throw a late initialization error. |
| Fragment initialize(is_uninitialized); |
| initialize += flow_graph_builder_->ThrowLateInitializationError( |
| position, "_throwLocalNotInitialized", variable->name()); |
| initialize += Goto(join); |
| } |
| } |
| |
| { |
| // Already initialized, so there's nothing to do. |
| Fragment already_initialized(is_initialized); |
| already_initialized += Goto(join); |
| } |
| |
| Fragment done = Fragment(instructions.entry, join); |
| done += LoadLocal(variable); |
| return done; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildVariableSet(TokenPosition* p) { |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| intptr_t variable_kernel_position = ReadUInt(); // read kernel position. |
| ReadUInt(); // read relative variable index. |
| return BuildVariableSetImpl(position, variable_kernel_position); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildVariableSet(uint8_t payload, |
| TokenPosition* p) { |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| intptr_t variable_kernel_position = ReadUInt(); // read kernel position. |
| return BuildVariableSetImpl(position, variable_kernel_position); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildVariableSetImpl( |
| TokenPosition position, |
| intptr_t variable_kernel_position) { |
| Fragment instructions = BuildExpression(); // read expression. |
| if (NeedsDebugStepCheck(stack(), position)) { |
| instructions = DebugStepCheck(position) + instructions; |
| } |
| |
| LocalVariable* variable = LookupVariable(variable_kernel_position); |
| if (variable->is_late() && variable->is_final()) { |
| // Late final variable, so check whether it has been initialized. |
| LocalVariable* expr_temp = MakeTemporary(); |
| instructions += LoadLocal(variable); |
| TargetEntryInstr* is_uninitialized; |
| TargetEntryInstr* is_initialized; |
| instructions += Constant(Object::sentinel()); |
| instructions += flow_graph_builder_->BranchIfStrictEqual(&is_uninitialized, |
| &is_initialized); |
| JoinEntryInstr* join = BuildJoinEntry(); |
| |
| { |
| // The variable is uninitialized, so store the expression value. |
| Fragment initialize(is_uninitialized); |
| initialize += LoadLocal(expr_temp); |
| initialize += StoreLocal(position, variable); |
| initialize += Drop(); |
| initialize += Goto(join); |
| } |
| |
| { |
| // Already initialized, so throw a LateInitializationError. |
| Fragment already_initialized(is_initialized); |
| already_initialized += flow_graph_builder_->ThrowLateInitializationError( |
| position, "_throwLocalAlreadyInitialized", variable->name()); |
| already_initialized += Goto(join); |
| } |
| |
| instructions = Fragment(instructions.entry, join); |
| } else { |
| instructions += StoreLocal(position, variable); |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildInstanceGet(TokenPosition* p) { |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| ReadByte(); // read kind. |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != nullptr) *p = position; |
| |
| const DirectCallMetadata direct_call = |
| direct_call_metadata_helper_.GetDirectTargetForPropertyGet(offset); |
| const InferredTypeMetadata result_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| |
| Fragment instructions = BuildExpression(); // read receiver. |
| |
| LocalVariable* receiver = nullptr; |
| if (direct_call.check_receiver_for_null_) { |
| // Duplicate receiver for CheckNull before it is consumed by PushArgument. |
| receiver = MakeTemporary(); |
| instructions += LoadLocal(receiver); |
| } |
| |
| const String& getter_name = ReadNameAsGetterName(); // read name. |
| SkipDartType(); // read result_type. |
| const NameIndex itarget_name = |
| ReadInterfaceMemberNameReference(); // read interface_target_reference. |
| ASSERT(!H.IsRoot(itarget_name) && H.IsGetter(itarget_name)); |
| const auto& interface_target = Function::ZoneHandle( |
| Z, H.LookupMethodByMember(itarget_name, H.DartGetterName(itarget_name))); |
| ASSERT(getter_name.ptr() == interface_target.name()); |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += CheckNull(position, receiver, getter_name); |
| } |
| |
| if (!direct_call.target_.IsNull()) { |
| ASSERT(CompilerState::Current().is_aot()); |
| instructions += |
| StaticCall(position, direct_call.target_, 1, Array::null_array(), |
| ICData::kNoRebind, &result_type); |
| } else { |
| const intptr_t kTypeArgsLen = 0; |
| const intptr_t kNumArgsChecked = 1; |
| instructions += |
| InstanceCall(position, getter_name, Token::kGET, kTypeArgsLen, 1, |
| Array::null_array(), kNumArgsChecked, interface_target, |
| Function::null_function(), &result_type); |
| } |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += DropTempsPreserveTop(1); // Drop receiver, preserve result. |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildDynamicGet(TokenPosition* p) { |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| ReadByte(); // read kind. |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != nullptr) *p = position; |
| |
| const DirectCallMetadata direct_call = |
| direct_call_metadata_helper_.GetDirectTargetForPropertyGet(offset); |
| const InferredTypeMetadata result_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| |
| Fragment instructions = BuildExpression(); // read receiver. |
| |
| LocalVariable* receiver = nullptr; |
| if (direct_call.check_receiver_for_null_) { |
| // Duplicate receiver for CheckNull before it is consumed by PushArgument. |
| receiver = MakeTemporary(); |
| instructions += LoadLocal(receiver); |
| } |
| |
| const String& getter_name = ReadNameAsGetterName(); // read name. |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += CheckNull(position, receiver, getter_name); |
| } |
| |
| const auto& mangled_name = String::ZoneHandle( |
| Z, Function::CreateDynamicInvocationForwarderName(getter_name)); |
| const Function* direct_call_target = &direct_call.target_; |
| if (!direct_call_target->IsNull()) { |
| direct_call_target = &Function::ZoneHandle( |
| direct_call.target_.GetDynamicInvocationForwarder(mangled_name)); |
| } |
| |
| if (!direct_call_target->IsNull()) { |
| ASSERT(CompilerState::Current().is_aot()); |
| instructions += |
| StaticCall(position, *direct_call_target, 1, Array::null_array(), |
| ICData::kNoRebind, &result_type); |
| } else { |
| const intptr_t kTypeArgsLen = 0; |
| const intptr_t kNumArgsChecked = 1; |
| instructions += InstanceCall(position, mangled_name, Token::kGET, |
| kTypeArgsLen, 1, Array::null_array(), |
| kNumArgsChecked, Function::null_function(), |
| Function::null_function(), &result_type); |
| } |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += DropTempsPreserveTop(1); // Drop receiver, preserve result. |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildInstanceTearOff(TokenPosition* p) { |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| ReadByte(); // read kind. |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != nullptr) *p = position; |
| |
| const DirectCallMetadata direct_call = |
| direct_call_metadata_helper_.GetDirectTargetForPropertyGet(offset); |
| const InferredTypeMetadata result_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| |
| Fragment instructions = BuildExpression(); // read receiver. |
| |
| LocalVariable* receiver = nullptr; |
| if (direct_call.check_receiver_for_null_) { |
| // Duplicate receiver for CheckNull before it is consumed by PushArgument. |
| receiver = MakeTemporary(); |
| instructions += LoadLocal(receiver); |
| } |
| |
| const String& getter_name = ReadNameAsGetterName(); // read name. |
| SkipDartType(); // read result_type. |
| const NameIndex itarget_name = |
| ReadInterfaceMemberNameReference(); // read interface_target_reference. |
| ASSERT(!H.IsRoot(itarget_name) && H.IsMethod(itarget_name)); |
| const auto& tearoff_interface_target = Function::ZoneHandle( |
| Z, H.LookupMethodByMember(itarget_name, H.DartMethodName(itarget_name))); |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += CheckNull(position, receiver, getter_name); |
| } |
| |
| if (!direct_call.target_.IsNull()) { |
| ASSERT(CompilerState::Current().is_aot()); |
| instructions += |
| StaticCall(position, direct_call.target_, 1, Array::null_array(), |
| ICData::kNoRebind, &result_type); |
| } else { |
| const intptr_t kTypeArgsLen = 0; |
| const intptr_t kNumArgsChecked = 1; |
| instructions += InstanceCall(position, getter_name, Token::kGET, |
| kTypeArgsLen, 1, Array::null_array(), |
| kNumArgsChecked, Function::null_function(), |
| tearoff_interface_target, &result_type); |
| } |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += DropTempsPreserveTop(1); // Drop receiver, preserve result. |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildFunctionTearOff(TokenPosition* p) { |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != nullptr) *p = position; |
| |
| const DirectCallMetadata direct_call = |
| direct_call_metadata_helper_.GetDirectTargetForPropertyGet(offset); |
| const InferredTypeMetadata result_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| |
| Fragment instructions = BuildExpression(); // read receiver. |
| |
| LocalVariable* receiver = nullptr; |
| if (direct_call.check_receiver_for_null_) { |
| // Duplicate receiver for CheckNull before it is consumed by PushArgument. |
| receiver = MakeTemporary(); |
| instructions += LoadLocal(receiver); |
| } |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += CheckNull(position, receiver, Symbols::GetCall()); |
| } |
| |
| if (!direct_call.target_.IsNull()) { |
| ASSERT(CompilerState::Current().is_aot()); |
| instructions += |
| StaticCall(position, direct_call.target_, 1, Array::null_array(), |
| ICData::kNoRebind, &result_type); |
| } else { |
| const intptr_t kTypeArgsLen = 0; |
| const intptr_t kNumArgsChecked = 1; |
| instructions += InstanceCall(position, Symbols::GetCall(), Token::kGET, |
| kTypeArgsLen, 1, Array::null_array(), |
| kNumArgsChecked, Function::null_function(), |
| Function::null_function(), &result_type); |
| } |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += DropTempsPreserveTop(1); // Drop receiver, preserve result. |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildInstanceSet(TokenPosition* p) { |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| ReadByte(); // read kind. |
| |
| const DirectCallMetadata direct_call = |
| direct_call_metadata_helper_.GetDirectTargetForPropertySet(offset); |
| const CallSiteAttributesMetadata call_site_attributes = |
| call_site_attributes_metadata_helper_.GetCallSiteAttributes(offset); |
| const InferredTypeMetadata inferred_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| |
| // True if callee can skip argument type checks. |
| bool is_unchecked_call = inferred_type.IsSkipCheck(); |
| if (call_site_attributes.receiver_type != nullptr && |
| call_site_attributes.receiver_type->HasTypeClass() && |
| !Class::Handle(call_site_attributes.receiver_type->type_class()) |
| .IsGeneric()) { |
| is_unchecked_call = true; |
| } |
| |
| Fragment instructions(MakeTemp()); |
| LocalVariable* variable = MakeTemporary(); |
| |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != nullptr) *p = position; |
| |
| if (PeekTag() == kThisExpression) { |
| is_unchecked_call = true; |
| } |
| instructions += BuildExpression(); // read receiver. |
| |
| LocalVariable* receiver = nullptr; |
| if (direct_call.check_receiver_for_null_) { |
| // Duplicate receiver for CheckNull before it is consumed by PushArgument. |
| receiver = MakeTemporary(); |
| instructions += LoadLocal(receiver); |
| } |
| |
| const String& setter_name = ReadNameAsSetterName(); // read name. |
| |
| instructions += BuildExpression(); // read value. |
| instructions += StoreLocal(TokenPosition::kNoSource, variable); |
| |
| const NameIndex itarget_name = |
| ReadInterfaceMemberNameReference(); // read interface_target_reference. |
| ASSERT(!H.IsRoot(itarget_name)); |
| const auto& interface_target = Function::ZoneHandle( |
| Z, H.LookupMethodByMember(itarget_name, H.DartSetterName(itarget_name))); |
| ASSERT(setter_name.ptr() == interface_target.name()); |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += CheckNull(position, receiver, setter_name); |
| } |
| |
| if (!direct_call.target_.IsNull()) { |
| ASSERT(CompilerState::Current().is_aot()); |
| instructions += |
| StaticCall(position, direct_call.target_, 2, Array::null_array(), |
| ICData::kNoRebind, /*result_type=*/nullptr, |
| /*type_args_count=*/0, |
| /*use_unchecked_entry=*/is_unchecked_call); |
| } else { |
| const intptr_t kTypeArgsLen = 0; |
| const intptr_t kNumArgsChecked = 1; |
| |
| instructions += InstanceCall( |
| position, setter_name, Token::kSET, kTypeArgsLen, 2, |
| Array::null_array(), kNumArgsChecked, interface_target, |
| Function::null_function(), |
| /*result_type=*/nullptr, |
| /*use_unchecked_entry=*/is_unchecked_call, &call_site_attributes); |
| } |
| |
| instructions += Drop(); // Drop result of the setter invocation. |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += Drop(); // Drop receiver. |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildDynamicSet(TokenPosition* p) { |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| ReadByte(); // read kind. |
| |
| const DirectCallMetadata direct_call = |
| direct_call_metadata_helper_.GetDirectTargetForPropertySet(offset); |
| const InferredTypeMetadata inferred_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| |
| // True if callee can skip argument type checks. |
| const bool is_unchecked_call = inferred_type.IsSkipCheck(); |
| |
| Fragment instructions(MakeTemp()); |
| LocalVariable* variable = MakeTemporary(); |
| |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != nullptr) *p = position; |
| |
| instructions += BuildExpression(); // read receiver. |
| |
| LocalVariable* receiver = nullptr; |
| if (direct_call.check_receiver_for_null_) { |
| // Duplicate receiver for CheckNull before it is consumed by PushArgument. |
| receiver = MakeTemporary(); |
| instructions += LoadLocal(receiver); |
| } |
| |
| const String& setter_name = ReadNameAsSetterName(); // read name. |
| |
| instructions += BuildExpression(); // read value. |
| instructions += StoreLocal(TokenPosition::kNoSource, variable); |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += CheckNull(position, receiver, setter_name); |
| } |
| |
| const Function* direct_call_target = &direct_call.target_; |
| const auto& mangled_name = String::ZoneHandle( |
| Z, Function::CreateDynamicInvocationForwarderName(setter_name)); |
| if (!direct_call_target->IsNull()) { |
| direct_call_target = &Function::ZoneHandle( |
| direct_call.target_.GetDynamicInvocationForwarder(mangled_name)); |
| } |
| |
| if (!direct_call_target->IsNull()) { |
| ASSERT(CompilerState::Current().is_aot()); |
| instructions += |
| StaticCall(position, *direct_call_target, 2, Array::null_array(), |
| ICData::kNoRebind, /*result_type=*/nullptr, |
| /*type_args_count=*/0, |
| /*use_unchecked_entry=*/is_unchecked_call); |
| } else { |
| const intptr_t kTypeArgsLen = 0; |
| const intptr_t kNumArgsChecked = 1; |
| |
| instructions += InstanceCall( |
| position, mangled_name, Token::kSET, kTypeArgsLen, 2, |
| Array::null_array(), kNumArgsChecked, Function::null_function(), |
| Function::null_function(), |
| /*result_type=*/nullptr, |
| /*use_unchecked_entry=*/is_unchecked_call, /*call_site_attrs=*/nullptr); |
| } |
| |
| instructions += Drop(); // Drop result of the setter invocation. |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += Drop(); // Drop receiver. |
| } |
| |
| return instructions; |
| } |
| |
| static Function& GetNoSuchMethodOrDie(Thread* thread, |
| Zone* zone, |
| const Class& klass) { |
| Function& nsm_function = Function::Handle(zone); |
| Class& iterate_klass = Class::Handle(zone, klass.ptr()); |
| if (!iterate_klass.IsNull() && |
| iterate_klass.EnsureIsFinalized(thread) == Error::null()) { |
| while (!iterate_klass.IsNull()) { |
| nsm_function = Resolver::ResolveDynamicFunction(zone, iterate_klass, |
| Symbols::NoSuchMethod()); |
| if (!nsm_function.IsNull() && nsm_function.NumParameters() == 2 && |
| nsm_function.NumTypeParameters() == 0) { |
| break; |
| } |
| iterate_klass = iterate_klass.SuperClass(); |
| } |
| } |
| // We are guaranteed to find noSuchMethod of class Object. |
| ASSERT(!nsm_function.IsNull()); |
| |
| return nsm_function; |
| } |
| |
| // Note, that this will always mark `super` flag to true. |
| Fragment StreamingFlowGraphBuilder::BuildAllocateInvocationMirrorCall( |
| TokenPosition position, |
| const String& name, |
| intptr_t num_type_arguments, |
| intptr_t num_arguments, |
| const Array& argument_names, |
| LocalVariable* actuals_array, |
| Fragment build_rest_of_actuals) { |
| Fragment instructions; |
| |
| // Populate array containing the actual arguments. Just add [this] here. |
| instructions += LoadLocal(actuals_array); // array |
| instructions += IntConstant(num_type_arguments == 0 ? 0 : 1); // index |
| instructions += LoadLocal(parsed_function()->receiver_var()); // receiver |
| instructions += StoreIndexed(kArrayCid); |
| instructions += build_rest_of_actuals; |
| |
| // First argument is receiver. |
| instructions += LoadLocal(parsed_function()->receiver_var()); |
| |
| // Push the arguments for allocating the invocation mirror: |
| // - the name. |
| instructions += Constant(String::ZoneHandle(Z, name.ptr())); |
| |
| // - the arguments descriptor. |
| const Array& args_descriptor = |
| Array::Handle(Z, ArgumentsDescriptor::NewBoxed( |
| num_type_arguments, num_arguments, argument_names)); |
| instructions += Constant(Array::ZoneHandle(Z, args_descriptor.ptr())); |
| |
| // - an array containing the actual arguments. |
| instructions += LoadLocal(actuals_array); |
| |
| // - [true] indicating this is a `super` NoSuchMethod. |
| instructions += Constant(Bool::True()); |
| |
| const Class& mirror_class = |
| Class::Handle(Z, Library::LookupCoreClass(Symbols::InvocationMirror())); |
| ASSERT(!mirror_class.IsNull()); |
| const auto& error = mirror_class.EnsureIsFinalized(thread()); |
| ASSERT(error == Error::null()); |
| const Function& allocation_function = Function::ZoneHandle( |
| Z, mirror_class.LookupStaticFunction( |
| Library::PrivateCoreLibName(Symbols::AllocateInvocationMirror()))); |
| ASSERT(!allocation_function.IsNull()); |
| instructions += StaticCall(position, allocation_function, |
| /* argument_count = */ 4, ICData::kStatic); |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildSuperPropertyGet(TokenPosition* p) { |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| const InferredTypeMetadata result_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| |
| Class& klass = GetSuperOrDie(); |
| |
| StringIndex name_index = ReadStringReference(); // read name index. |
| NameIndex library_reference = |
| ((H.StringSize(name_index) >= 1) && H.CharacterAt(name_index, 0) == '_') |
| ? ReadCanonicalNameReference() // read library index. |
| : NameIndex(); |
| const String& getter_name = H.DartGetterName(library_reference, name_index); |
| const String& method_name = H.DartMethodName(library_reference, name_index); |
| |
| SkipInterfaceMemberNameReference(); // skip target_reference. |
| |
| // Search the superclass chain for the selector looking for either getter or |
| // method. |
| Function& function = Function::Handle(Z); |
| if (!klass.IsNull() && klass.EnsureIsFinalized(thread()) == Error::null()) { |
| while (!klass.IsNull()) { |
| function = Resolver::ResolveDynamicFunction(Z, klass, method_name); |
| if (!function.IsNull()) { |
| Function& target = |
| Function::ZoneHandle(Z, function.ImplicitClosureFunction()); |
| ASSERT(!target.IsNull()); |
| // Generate inline code for allocation closure object with context |
| // which captures `this`. |
| return BuildImplicitClosureCreation(target); |
| } |
| function = Resolver::ResolveDynamicFunction(Z, klass, getter_name); |
| if (!function.IsNull()) break; |
| klass = klass.SuperClass(); |
| } |
| } |
| |
| Fragment instructions; |
| if (klass.IsNull()) { |
| instructions += |
| Constant(TypeArguments::ZoneHandle(Z, TypeArguments::null())); |
| instructions += IntConstant(1); // array size |
| instructions += CreateArray(); |
| LocalVariable* actuals_array = MakeTemporary(); |
| |
| Class& parent_klass = GetSuperOrDie(); |
| |
| instructions += BuildAllocateInvocationMirrorCall( |
| position, getter_name, |
| /* num_type_arguments = */ 0, |
| /* num_arguments = */ 1, |
| /* argument_names = */ Object::empty_array(), actuals_array, |
| /* build_rest_of_actuals = */ Fragment()); |
| |
| Function& nsm_function = GetNoSuchMethodOrDie(thread(), Z, parent_klass); |
| instructions += |
| StaticCall(position, Function::ZoneHandle(Z, nsm_function.ptr()), |
| /* argument_count = */ 2, ICData::kNSMDispatch); |
| instructions += DropTempsPreserveTop(1); // Drop array |
| } else { |
| ASSERT(!klass.IsNull()); |
| ASSERT(!function.IsNull()); |
| |
| instructions += LoadLocal(parsed_function()->receiver_var()); |
| |
| instructions += |
| StaticCall(position, Function::ZoneHandle(Z, function.ptr()), |
| /* argument_count = */ 1, Array::null_array(), |
| ICData::kSuper, &result_type); |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildSuperPropertySet(TokenPosition* p) { |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| Class& klass = GetSuperOrDie(); |
| |
| const String& setter_name = ReadNameAsSetterName(); // read name. |
| |
| Function& function = Function::Handle(Z); |
| if (klass.EnsureIsFinalized(thread()) == Error::null()) { |
| function = Resolver::ResolveDynamicFunction(Z, klass, setter_name); |
| } |
| |
| Fragment instructions(MakeTemp()); |
| LocalVariable* value = MakeTemporary(); // this holds RHS value |
| |
| if (function.IsNull()) { |
| instructions += |
| Constant(TypeArguments::ZoneHandle(Z, TypeArguments::null())); |
| instructions += IntConstant(2); // array size |
| instructions += CreateArray(); |
| LocalVariable* actuals_array = MakeTemporary(); |
| |
| Fragment build_rest_of_actuals; |
| build_rest_of_actuals += LoadLocal(actuals_array); // array |
| build_rest_of_actuals += IntConstant(1); // index |
| build_rest_of_actuals += BuildExpression(); // value. |
| build_rest_of_actuals += StoreLocal(position, value); |
| build_rest_of_actuals += StoreIndexed(kArrayCid); |
| |
| instructions += BuildAllocateInvocationMirrorCall( |
| position, setter_name, /* num_type_arguments = */ 0, |
| /* num_arguments = */ 2, |
| /* argument_names = */ Object::empty_array(), actuals_array, |
| build_rest_of_actuals); |
| |
| SkipInterfaceMemberNameReference(); // skip target_reference. |
| |
| Function& nsm_function = GetNoSuchMethodOrDie(thread(), Z, klass); |
| instructions += |
| StaticCall(position, Function::ZoneHandle(Z, nsm_function.ptr()), |
| /* argument_count = */ 2, ICData::kNSMDispatch); |
| instructions += Drop(); // Drop result of NoSuchMethod invocation |
| instructions += Drop(); // Drop array |
| } else { |
| // receiver |
| instructions += LoadLocal(parsed_function()->receiver_var()); |
| |
| instructions += BuildExpression(); // read value. |
| instructions += StoreLocal(position, value); |
| |
| SkipInterfaceMemberNameReference(); // skip target_reference. |
| |
| instructions += StaticCall( |
| position, Function::ZoneHandle(Z, function.ptr()), |
| /* argument_count = */ 2, Array::null_array(), ICData::kSuper, |
| /*result_type=*/nullptr, /*type_args_len=*/0, |
| /*use_unchecked_entry=*/true); |
| instructions += Drop(); // Drop result of the setter invocation. |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildStaticGet(TokenPosition* p) { |
| ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull()); |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| const InferredTypeMetadata result_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| |
| NameIndex target = ReadCanonicalNameReference(); // read target_reference. |
| ASSERT(H.IsGetter(target)); |
| |
| const Field& field = Field::ZoneHandle( |
| Z, H.LookupFieldByKernelGetterOrSetter(target, /*required=*/false)); |
| if (!field.IsNull()) { |
| if (field.is_const()) { |
| // Since the CFE inlines all references to const variables and fields, |
| // it never emits a StaticGet of a const field. |
| // This situation only arises because of the static const fields in |
| // the ClassID class, which are generated internally in the VM |
| // during loading. See also Class::InjectCIDFields. |
| ASSERT(Class::Handle(field.Owner()).library() == |
| Library::InternalLibrary() && |
| Class::Handle(field.Owner()).Name() == Symbols::ClassID().ptr()); |
| return Constant(Instance::ZoneHandle( |
| Z, Instance::RawCast(field.StaticConstFieldValue()))); |
| } else { |
| const Class& owner = Class::Handle(Z, field.Owner()); |
| const String& getter_name = H.DartGetterName(target); |
| const Function& getter = |
| Function::ZoneHandle(Z, owner.LookupStaticFunction(getter_name)); |
| if (!getter.IsNull() && field.NeedsGetter()) { |
| return StaticCall(position, getter, 0, Array::null_array(), |
| ICData::kStatic, &result_type); |
| } else { |
| if (result_type.IsConstant()) { |
| return Constant(result_type.constant_value); |
| } |
| return LoadStaticField(field, /*calls_initializer=*/false); |
| } |
| } |
| } else { |
| const Function& function = |
| Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target)); |
| |
| if (H.IsGetter(target)) { |
| return StaticCall(position, function, 0, Array::null_array(), |
| ICData::kStatic, &result_type); |
| } else if (H.IsMethod(target)) { |
| const auto& closure_function = |
| Function::Handle(Z, function.ImplicitClosureFunction()); |
| const auto& static_closure = |
| Instance::Handle(Z, closure_function.ImplicitStaticClosure()); |
| return Constant(Instance::ZoneHandle(Z, H.Canonicalize(static_closure))); |
| } else { |
| UNIMPLEMENTED(); |
| } |
| } |
| |
| return Fragment(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildStaticSet(TokenPosition* p) { |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| NameIndex target = ReadCanonicalNameReference(); // read target_reference. |
| ASSERT(H.IsSetter(target)); |
| |
| // Evaluate the expression on the right hand side. |
| Fragment instructions = BuildExpression(); // read expression. |
| |
| // Look up the target as a setter first and, if not present, as a field |
| // second. This order is needed to avoid looking up a final field as the |
| // target. |
| const Function& function = Function::ZoneHandle( |
| Z, H.LookupStaticMethodByKernelProcedure(target, /*required=*/false)); |
| |
| if (!function.IsNull()) { |
| LocalVariable* variable = MakeTemporary(); |
| |
| // Prepare argument. |
| instructions += LoadLocal(variable); |
| |
| // Invoke the setter function. |
| instructions += StaticCall(position, function, 1, ICData::kStatic); |
| |
| // Drop the unused result & leave the stored value on the stack. |
| return instructions + Drop(); |
| } else { |
| const Field& field = |
| Field::ZoneHandle(Z, H.LookupFieldByKernelGetterOrSetter(target)); |
| ASSERT(!field.NeedsSetter()); |
| if (NeedsDebugStepCheck(stack(), position)) { |
| instructions = DebugStepCheck(position) + instructions; |
| } |
| LocalVariable* variable = MakeTemporary(); |
| instructions += LoadLocal(variable); |
| instructions += StoreStaticField(position, field); |
| return instructions; |
| } |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildMethodInvocation(TokenPosition* p, |
| bool is_dynamic) { |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| ReadByte(); // read kind. |
| |
| // read flags. |
| const uint8_t flags = is_dynamic ? 0 : ReadFlags(); |
| const bool is_invariant = (flags & kMethodInvocationFlagInvariant) != 0; |
| |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| const DirectCallMetadata direct_call = |
| direct_call_metadata_helper_.GetDirectTargetForMethodInvocation(offset); |
| const InferredTypeMetadata result_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| const CallSiteAttributesMetadata call_site_attributes = |
| call_s
|