blob: c2d252c65cd2408f1d3fbce35d6366f8c32f24e4 [file] [log] [blame]
// 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/compiler/ffi/callback.h"
#include "vm/compiler/frontend/bytecode_flow_graph_builder.h"
#include "vm/compiler/frontend/bytecode_reader.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 {
DECLARE_FLAG(bool, enable_interpreter);
namespace kernel {
#define Z (zone_)
#define H (translation_helper_)
#define T (type_translator_)
#define I Isolate::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);
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_);
if (field_helper.IsConst()) {
// This will read the initializer.
body += Constant(
Instance::ZoneHandle(Z, constant_reader_.ReadConstantExpression()));
} else {
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::EvaluateConstFieldValue(const Field& field) {
ASSERT(field.is_const() && field.IsUninitialized());
FieldHelper field_helper(this);
field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
Tag initializer_tag = ReadTag(); // read first part of initializer.
ASSERT(initializer_tag == kSomething);
Instance& value =
Instance::Handle(Z, constant_reader_.ReadConstantExpression());
field.SetStaticValue(value);
}
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()) {
field.RecordStore(Object::null_object());
} else {
ASSERT(field.is_nullable(/* silence_assert = */ true));
}
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()) {
field.RecordStore(Object::null_object());
} else {
ASSERT(field.is_nullable(/* silence_assert = */ true));
}
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) {
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.LookupFieldByKernelField(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.raw()),
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.raw()),
/*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]->raw()),
/*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;
}
void StreamingFlowGraphBuilder::ReadDefaultFunctionTypeArguments(
const Function& function) {
if (!function.IsGeneric()) {
return;
}
AlternativeReadingScope alt(&reader_);
FunctionNodeHelper function_node_helper(this);
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
intptr_t num_type_params = ReadListLength();
ASSERT(num_type_params == function.NumTypeParameters());
TypeArguments& default_types =
TypeArguments::Handle(Z, TypeArguments::New(num_type_params));
for (intptr_t i = 0; i < num_type_params; ++i) {
TypeParameterHelper helper(this);
helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kDefaultType);
if (ReadTag() == kSomething) {
default_types.SetTypeAt(i, T.BuildType());
} else {
default_types.SetTypeAt(i, Object::dynamic_type());
}
helper.Finish();
}
default_types = default_types.Canonicalize();
parsed_function()->SetDefaultFunctionTypeArguments(default_types);
}
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::SetAsyncStackTrace(
const Function& dart_function) {
if (!FLAG_causal_async_stacks ||
!(dart_function.IsAsyncClosure() || dart_function.IsAsyncGenClosure())) {
return {};
}
// The code we are building will be executed right after we enter
// the function and before any nested contexts are allocated.
ASSERT(B->context_depth_ ==
scopes()->yield_jump_variable->owner()->context_level());
Fragment instructions;
LocalScope* scope = parsed_function()->scope();
const Function& target = Function::ZoneHandle(
Z, I->object_store()->async_set_thread_stack_trace());
ASSERT(!target.IsNull());
// Fetch and load :async_stack_trace
LocalVariable* async_stack_trace_var =
scope->LookupVariable(Symbols::AsyncStackTraceVar(), false);
ASSERT((async_stack_trace_var != NULL) &&
async_stack_trace_var->is_captured());
Fragment code;
code += LoadLocal(async_stack_trace_var);
// Call _asyncSetThreadStackTrace
code += StaticCall(TokenPosition::kNoSource, target,
/* argument_count = */ 1, ICData::kStatic);
code += Drop();
return code;
}
Fragment StreamingFlowGraphBuilder::TypeArgumentsHandling(
const Function& dart_function) {
Fragment prologue = B->BuildDefaultTypeHandling(dart_function);
if (dart_function.IsClosureFunction() &&
dart_function.NumParentTypeParameters() > 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.NumParentTypeParameters());
prologue += IntConstant(dart_function.NumTypeParameters() +
dart_function.NumParentTypeParameters());
const Library& dart_internal =
Library::Handle(Z, Library::InternalLibrary());
const Function& prepend_function =
Function::ZoneHandle(Z, dart_internal.LookupFunctionAllowPrivate(
Symbols::PrependTypeArguments()));
ASSERT(!prepend_function.IsNull());
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) {
const auto& offsets = TypedData::ZoneHandle(
Z, TypedData::New(kTypedDataInt32ArrayCid, continuation_count,
Heap::kOld));
dispatch += Constant(offsets);
dispatch += LoadLocal(scopes()->switch_variable);
// Ideally this would just be LoadIndexedTypedData(kTypedDataInt32ArrayCid),
// but that doesn't work in unoptimised code.
// The optimiser will turn this into that in any case.
dispatch += InstanceCall(TokenPosition::kNoSource, Symbols::IndexToken(),
Token::kINDEX, /*argument_count=*/2);
Value* offset_from_start = Pop();
indirect_goto = new (Z) IndirectGotoInstr(&offsets, offset_from_start);
dispatch <<= indirect_goto;
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_->StoreInstanceField(
TokenPosition::kNoSource,
Slot::GetContextVariableSlotFor(thread(), *variable),
StoreInstanceFieldInstr::Kind::kInitializing);
}
}
body += Drop(); // The context.
}
return body;
}
// If we run in checked mode or strong mode, we have to check the type of the
// passed arguments.
//
// TODO(#34162): If we're building an extra entry-point to skip
// type checks, we should substitute Redefinition nodes for the AssertAssignable
// instructions to ensure that the argument types are known.
void StreamingFlowGraphBuilder::CheckArgumentTypesAsNecessary(
const Function& dart_function,
intptr_t type_parameters_offset,
Fragment* explicit_checks,
Fragment* implicit_checks,
Fragment* implicit_redefinitions) {
if (!dart_function.NeedsArgumentTypeChecks()) return;
// Check if parent function was annotated with no-dynamic-invocations.
const ProcedureAttributesMetadata attrs =
procedure_attributes_metadata_helper_.GetProcedureAttributes(
dart_function.kernel_offset());
AlternativeReadingScope _(&reader_);
SetOffset(type_parameters_offset);
B->BuildArgumentTypeChecks(
MethodCanSkipTypeChecksForNonCovariantArguments(dart_function, attrs)
? TypeChecksToBuild::kCheckCovariantTypeParameterBounds
: TypeChecksToBuild::kCheckAllTypeParameterBounds,
explicit_checks, implicit_checks, implicit_redefinitions);
}
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().raw()) &&
(dart_function.Owner() != I->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 += SetAsyncStackTrace(dart_function);
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 explicit_type_checks;
Fragment implicit_type_checks;
Fragment implicit_redefinitions;
CheckArgumentTypesAsNecessary(dart_function, type_parameters_offset,
&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);
ActiveTypeParametersScope active_type_params(active_class(), function, Z);
if (function.is_declared_in_bytecode()) {
bytecode_metadata_helper_.ParseBytecodeFunction(parsed_function());
switch (function.kind()) {
case FunctionLayout::kImplicitClosureFunction:
return B->BuildGraphOfImplicitClosureFunction(function);
case FunctionLayout::kImplicitGetter:
case FunctionLayout::kImplicitSetter:
return B->BuildGraphOfFieldAccessor(function);
case FunctionLayout::kImplicitStaticGetter: {
if (IsStaticFieldGetterGeneratedAsInitializer(function, Z)) {
break;
}
return B->BuildGraphOfFieldAccessor(function);
}
case FunctionLayout::kDynamicInvocationForwarder:
return B->BuildGraphOfDynamicInvocationForwarder(function);
case FunctionLayout::kMethodExtractor:
return B->BuildGraphOfMethodExtractor(function);
case FunctionLayout::kNoSuchMethodDispatcher:
return B->BuildGraphOfNoSuchMethodDispatcher(function);
default:
break;
}
ASSERT(function.HasBytecode());
BytecodeFlowGraphBuilder bytecode_compiler(
flow_graph_builder_, parsed_function(),
&(flow_graph_builder_->ic_data_array_));
if (B->IsRecognizedMethodForFlowGraph(function)) {
bytecode_compiler.CreateParameterVariables();
return B->BuildGraphOfRecognizedMethod(function);
}
return bytecode_compiler.BuildGraph();
}
// Certain special functions could have a VM-internal bytecode
// attached to them.
ASSERT((!function.HasBytecode()) ||
(function.kind() == FunctionLayout::kImplicitGetter) ||
(function.kind() == FunctionLayout::kImplicitSetter) ||
(function.kind() == FunctionLayout::kImplicitStaticGetter) ||
(function.kind() == FunctionLayout::kMethodExtractor) ||
(function.kind() == FunctionLayout::kInvokeFieldDispatcher) ||
(function.kind() == FunctionLayout::kNoSuchMethodDispatcher));
ParseKernelASTFunction();
switch (function.kind()) {
case FunctionLayout::kRegularFunction:
case FunctionLayout::kGetterFunction:
case FunctionLayout::kSetterFunction:
case FunctionLayout::kClosureFunction:
case FunctionLayout::kConstructor: {
if (B->IsRecognizedMethodForFlowGraph(function)) {
return B->BuildGraphOfRecognizedMethod(function);
}
return BuildGraphOfFunction(function.IsGenerativeConstructor());
}
case FunctionLayout::kImplicitGetter:
case FunctionLayout::kImplicitStaticGetter:
case FunctionLayout::kImplicitSetter: {
const Field& field = Field::Handle(Z, function.accessor_field());
if (field.is_const() && field.IsUninitialized()) {
EvaluateConstFieldValue(field);
}
return B->BuildGraphOfFieldAccessor(function);
}
case FunctionLayout::kFieldInitializer:
return BuildGraphOfFieldInitializer();
case FunctionLayout::kDynamicInvocationForwarder:
return B->BuildGraphOfDynamicInvocationForwarder(function);
case FunctionLayout::kMethodExtractor:
return flow_graph_builder_->BuildGraphOfMethodExtractor(function);
case FunctionLayout::kNoSuchMethodDispatcher:
return flow_graph_builder_->BuildGraphOfNoSuchMethodDispatcher(function);
case FunctionLayout::kInvokeFieldDispatcher:
return flow_graph_builder_->BuildGraphOfInvokeFieldDispatcher(function);
case FunctionLayout::kImplicitClosureFunction:
return flow_graph_builder_->BuildGraphOfImplicitClosureFunction(function);
case FunctionLayout::kFfiTrampoline:
return flow_graph_builder_->BuildGraphOfFfiTrampoline(function);
case FunctionLayout::kSignatureFunction:
case FunctionLayout::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 FunctionLayout::kRegularFunction:
case FunctionLayout::kImplicitClosureFunction:
case FunctionLayout::kGetterFunction:
case FunctionLayout::kSetterFunction:
case FunctionLayout::kClosureFunction:
case FunctionLayout::kConstructor:
case FunctionLayout::kDynamicInvocationForwarder:
ReadForwardingStubTarget(function);
break;
default:
break;
}
set_scopes(parsed_function()->EnsureKernelScopes());
switch (function.kind()) {
case FunctionLayout::kRegularFunction:
case FunctionLayout::kGetterFunction:
case FunctionLayout::kSetterFunction:
case FunctionLayout::kClosureFunction:
case FunctionLayout::kConstructor:
case FunctionLayout::kImplicitClosureFunction:
ReadUntilFunctionNode();
SetupDefaultParameterValues();
ReadDefaultFunctionTypeArguments(function);
break;
case FunctionLayout::kImplicitGetter:
case FunctionLayout::kImplicitStaticGetter:
case FunctionLayout::kImplicitSetter:
case FunctionLayout::kFieldInitializer:
case FunctionLayout::kMethodExtractor:
case FunctionLayout::kNoSuchMethodDispatcher:
case FunctionLayout::kInvokeFieldDispatcher:
case FunctionLayout::kFfiTrampoline:
break;
case FunctionLayout::kDynamicInvocationForwarder:
if (PeekTag() != kField) {
ReadUntilFunctionNode();
SetupDefaultParameterValues();
ReadDefaultFunctionTypeArguments(function);
}
break;
case FunctionLayout::kSignatureFunction:
case FunctionLayout::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.forwarding_stub_super_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 kPropertyGet:
return BuildPropertyGet(position);
case kPropertySet:
return BuildPropertySet(position);
case kDirectPropertyGet:
return BuildDirectPropertyGet(position);
case kDirectPropertySet:
return BuildDirectPropertySet(position);
case kSuperPropertyGet:
return BuildSuperPropertyGet(position);
case kSuperPropertySet:
return BuildSuperPropertySet(position);
case kStaticGet:
return BuildStaticGet(position);
case kStaticSet:
return BuildStaticSet(position);
case kMethodInvocation:
return BuildMethodInvocation(position);
case kSuperMethodInvocation:
return BuildSuperMethodInvocation(position);
case kDirectMethodInvocation:
return BuildDirectMethodInvocation(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:
// Const invocations and const literals are removed by the
// constant evaluator.
case kListConcatenation:
case kSetConcatenation:
case kMapConcatenation:
case kInstanceCreation:
case kFileUriExpression:
// Collection concatenation, instance creation operations and
// in-expression URI changes are internal to the front end and
// removed by the constant evaluator.
default:
ReportUnexpectedTag("expression", tag);
UNREACHABLE();
}
return Fragment();
}
Fragment StreamingFlowGraphBuilder::BuildStatement() {
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();
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() {
return flow_graph_builder_->MakeTemporary();
}
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)));
Function& function =
Function::Handle(Z, Resolver::ResolveDynamicForReceiverClassAllowPrivate(
klass, name, args_desc, /*allow_add=*/false));
return function;
}
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::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(
TokenPosition::kNoSource,
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, *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();
initialize += StoreLocal(position, variable);
initialize += Drop();
initialize += Goto(join);
} else {
// The variable has no initializer, so throw a LateInitializationError.
Fragment initialize(is_uninitialized);
initialize += flow_graph_builder_->ThrowLateInitializationError(
position, 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, *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, variable->name());
already_initialized += Goto(join);
}
instructions = Fragment(instructions.entry, join);
} else {
instructions += StoreLocal(position, variable);
}
return instructions;
}
Fragment StreamingFlowGraphBuilder::BuildPropertyGet(TokenPosition* p) {
const intptr_t offset = ReaderOffset() - 1; // Include the tag.
const TokenPosition position = ReadPosition(); // read position.
if (p != NULL) *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 = NULL;
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.
const Function* interface_target = &Function::null_function();
const Function* tearoff_interface_target = &Function::null_function();
const NameIndex itarget_name =
ReadInterfaceMemberNameReference(); // read interface_target_reference.
if (!H.IsRoot(itarget_name) &&
(H.IsGetter(itarget_name) || H.IsField(itarget_name))) {
interface_target = &Function::ZoneHandle(
Z,
H.LookupMethodByMember(itarget_name, H.DartGetterName(itarget_name)));
ASSERT(getter_name.raw() == interface_target->name());
} else if (!H.IsRoot(itarget_name) && H.IsMethod(itarget_name)) {
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);
}
const String* mangled_name = &getter_name;
const Function* direct_call_target = &direct_call.target_;
if (H.IsRoot(itarget_name)) {
mangled_name = &String::ZoneHandle(
Z, Function::CreateDynamicInvocationForwarderName(getter_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, 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, *interface_target,
*tearoff_interface_target, &result_type);
}
if (direct_call.check_receiver_for_null_) {
instructions += DropTempsPreserveTop(1); // Drop receiver, preserve result.
}
return instructions;
}
Fragment StreamingFlowGraphBuilder::BuildPropertySet(TokenPosition* p) {
const intptr_t offset = ReaderOffset() - 1; // Include the tag.
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 Function* interface_target = &Function::null_function();
const NameIndex itarget_name =
ReadInterfaceMemberNameReference(); // read interface_target_reference.
if (!H.IsRoot(itarget_name)) {
interface_target = &Function::ZoneHandle(
Z,
H.LookupMethodByMember(itarget_name, H.DartSetterName(itarget_name)));
ASSERT(setter_name.raw() == interface_target->name());
}
if (direct_call.check_receiver_for_null_) {
instructions += CheckNull(position, receiver, setter_name);
}
const String* mangled_name = &setter_name;
const Function* direct_call_target = &direct_call.target_;
if (H.IsRoot(itarget_name)) {
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, *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;
}
static Function& GetNoSuchMethodOrDie(Thread* thread,
Zone* zone,
const Class& klass) {
Function& nsm_function = Function::Handle(zone);
Class& iterate_klass = Class::Handle(zone, klass.raw());
while (!iterate_klass.IsNull()) {
if (iterate_klass.EnsureIsFinalized(thread) == Error::null()) {
nsm_function =
iterate_klass.LookupDynamicFunction(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.raw()));
// - 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.raw()));
// - 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);
while (!klass.IsNull()) {
if (klass.EnsureIsFinalized(thread()) == Error::null()) {
function = klass.LookupDynamicFunction(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 = klass.LookupDynamicFunction(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.raw()),
/* 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.raw()),
/* 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 = H.LookupDynamicFunction(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.raw()),
/* 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.raw()),
/* 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::BuildDirectPropertyGet(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);
const Tag receiver_tag = PeekTag(); // peek tag for receiver.
Fragment instructions = BuildExpression(); // read receiver.
const NameIndex kernel_name =
ReadInterfaceMemberNameReference(); // read target_reference.
Function& target = Function::ZoneHandle(Z);
if (H.IsProcedure(kernel_name)) {
if (H.IsGetter(kernel_name)) {
target =
H.LookupMethodByMember(kernel_name, H.DartGetterName(kernel_name));
} else if (receiver_tag == kThisExpression) {
// Undo stack change for the BuildExpression.
Pop();
target =
H.LookupMethodByMember(kernel_name, H.DartMethodName(kernel_name));
target = target.ImplicitClosureFunction();
ASSERT(!target.IsNull());
// Generate inline code for allocating closure object with context which
// captures `this`.
return BuildImplicitClosureCreation(target);
} else {
// Need to create implicit closure (tear-off), receiver != this.
// Ensure method extractor exists and call it directly.
const Function& target_method = Function::ZoneHandle(
Z,
H.LookupMethodByMember(kernel_name, H.DartMethodName(kernel_name)));
const String& getter_name = H.DartGetterName(kernel_name);
target = target_method.GetMethodExtractor(getter_name);
}
} else {
ASSERT(H.IsField(kernel_name));
const String& getter_name = H.DartGetterName(kernel_name);
target = H.LookupMethodByMember(kernel_name, getter_name);
ASSERT(target.IsGetterFunction() || target.IsImplicitGetterFunction());
}
// Static calls are marked as "no-rebind", which is currently safe because
// DirectPropertyGet are only used in enums (index in toString) and enums
// can't change their structure during hot reload.
// If there are other sources of DirectPropertyGet in the future, this code
// have to be adjusted.
return instructions + StaticCall(position, target, 1, Array::null_array(),
ICData::kNoRebind, &result_type);
}
Fragment StreamingFlowGraphBuilder::BuildDirectPropertySet(TokenPosition* p) {
const TokenPosition position = ReadPosition(); // read position.
if (p != NULL) *p = position;
Fragment instructions(MakeTemp());
LocalVariable* value = MakeTemporary();
instructions += BuildExpression(); // read receiver.
const NameIndex target_reference =
ReadInterfaceMemberNameReference(); // read target_reference.
const String& method_name = H.DartSetterName(target_reference);
const Function& target = Function::ZoneHandle(
Z, H.LookupMethodByMember(target_reference, method_name));
ASSERT(target.IsSetterFunction() || target.IsImplicitSetterFunction());
instructions += BuildExpression(); // read value.
instructions += StoreLocal(TokenPosition::kNoSource, value);
// Static calls are marked as "no-rebind", which is currently safe because
// DirectPropertyGet are only used in enums (index in toString) and enums
// can't change their structure during hot reload.
// If there are other sources of DirectPropertyGet in the future, this code
// have to be adjusted.
instructions +=
StaticCall(position, target, 2, Array::null_array(), ICData::kNoRebind,
/* result_type = */ NULL);
return instructions + Drop();
}
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.
if (H.IsField(target)) {
const Field& field =
Field::ZoneHandle(Z, H.LookupFieldByKernelField(target));
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().raw());
return Constant(Instance::ZoneHandle(Z, field.StaticValue()));
} 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.
if (H.IsField(target)) {
const Field& field =
Field::ZoneHandle(Z, H.LookupFieldByKernelField(target));
const Class& owner = Class::Handle(Z, field.Owner());
const String& setter_name = H.DartSetterName(target);
const Function& setter =
Function::ZoneHandle(Z, owner.LookupStaticFunction(setter_name));
Fragment instructions = BuildExpression(); // read expression.
if (NeedsDebugStepCheck(stack(), position)) {
instructions = DebugStepCheck(position) + instructions;
}
LocalVariable* variable = MakeTemporary();
instructions += LoadLocal(variable);
if (!setter.IsNull() && field.NeedsSetter()) {
instructions += StaticCall(position, setter, 1, ICData::kStatic);
instructions += Drop();
} else {
instructions += StoreStaticField(position, field);
}
return instructions;
} else {
ASSERT(H.IsProcedure(target));
// Evaluate the expression on the right hand side.
Fragment instructions = BuildExpression(); // read expression.
LocalVariable* variable = MakeTemporary();
// Prepare argument.
instructions += LoadLocal(variable);
// Invoke the setter function.
const Function& function =
Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target));
instructions += StaticCall(position, function, 1, ICData::kStatic);
// Drop the unused result & leave the stored value on the stack.
return instructions + Drop();
}
}
Fragment StreamingFlowGraphBuilder::BuildMethodInvocation(TokenPosition* p) {
const intptr_t offset = ReaderOffset() - 1; // Include the tag.
const TokenPosition position = ReadPosition(); // read position.
if