blob: b2a603160680f1472bc6cd3c8e7cbaf4755fe22b [file] [log] [blame]
// Copyright (c) 2018, 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/scope_builder.h"
#include "vm/compiler/backend/il.h" // For CompileType.
#include "vm/compiler/frontend/kernel_translation_helper.h"
namespace dart {
namespace kernel {
#define Z (zone_)
#define H (translation_helper_)
#define T (type_translator_)
#define I Isolate::Current()
#define IG IsolateGroup::Current()
ScopeBuilder::ScopeBuilder(ParsedFunction* parsed_function)
: result_(NULL),
parsed_function_(parsed_function),
translation_helper_(Thread::Current()),
zone_(translation_helper_.zone()),
current_function_scope_(NULL),
scope_(NULL),
depth_(0),
name_index_(0),
needs_expr_temp_(false),
helper_(
zone_,
&translation_helper_,
Script::Handle(Z, parsed_function->function().script()),
ExternalTypedData::Handle(Z,
parsed_function->function().KernelData()),
parsed_function->function().KernelDataProgramOffset()),
constant_reader_(&helper_, &active_class_),
inferred_type_metadata_helper_(&helper_, &constant_reader_),
procedure_attributes_metadata_helper_(&helper_),
type_translator_(&helper_,
&constant_reader_,
&active_class_,
/*finalize=*/true) {
H.InitFromScript(helper_.script());
ASSERT(type_translator_.active_class_ == &active_class_);
}
ScopeBuildingResult* ScopeBuilder::BuildScopes() {
if (result_ != NULL) return result_;
ASSERT(scope_ == NULL && depth_.loop_ == 0 && depth_.function_ == 0);
result_ = new (Z) ScopeBuildingResult();
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(Z, function.Owner());
Function& outermost_function =
Function::Handle(Z, function.GetOutermostFunction());
ActiveClassScope active_class_scope(&active_class_, &klass);
ActiveMemberScope active_member(&active_class_, &outermost_function);
FunctionType& signature = FunctionType::Handle(Z, function.signature());
ActiveTypeParametersScope active_type_params(&active_class_, function,
&signature, Z);
LocalScope* enclosing_scope = NULL;
if (function.IsImplicitClosureFunction() && !function.is_static()) {
// Create artificial enclosing scope for the tear-off that contains
// captured receiver value. This ensure that AssertAssignable will correctly
// load instantiator type arguments if they are needed.
Class& klass = Class::Handle(Z, function.Owner());
Type& klass_type = H.GetDeclarationType(klass);
LocalVariable* receiver_variable =
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::This(), klass_type);
parsed_function_->set_receiver_var(receiver_variable);
receiver_variable->set_is_captured();
enclosing_scope = new (Z) LocalScope(NULL, 0, 0);
enclosing_scope->set_context_level(0);
enclosing_scope->AddVariable(receiver_variable);
enclosing_scope->AddContextVariable(receiver_variable);
} else if (function.HasParent()) {
enclosing_scope = LocalScope::RestoreOuterScope(
ContextScope::Handle(Z, function.context_scope()));
}
current_function_scope_ = scope_ = new (Z) LocalScope(enclosing_scope, 0, 0);
scope_->set_begin_token_pos(function.token_pos());
scope_->set_end_token_pos(function.end_token_pos());
// Add function type arguments variable before current context variable.
if (function.IsGeneric() || function.HasGenericParent()) {
LocalVariable* type_args_var = MakeVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::FunctionTypeArgumentsVar(), AbstractType::dynamic_type());
scope_->AddVariable(type_args_var);
parsed_function_->set_function_type_arguments(type_args_var);
}
if (parsed_function_->has_arg_desc_var()) {
scope_->AddVariable(parsed_function_->arg_desc_var());
}
LocalVariable* context_var = parsed_function_->current_context_var();
context_var->set_is_forced_stack();
scope_->AddVariable(context_var);
parsed_function_->set_scope(scope_);
helper_.SetOffset(function.kernel_offset());
FunctionNodeHelper function_node_helper(&helper_);
const ProcedureAttributesMetadata attrs =
procedure_attributes_metadata_helper_.GetProcedureAttributes(
function.kernel_offset());
switch (function.kind()) {
case UntaggedFunction::kImplicitClosureFunction: {
const auto& parent = Function::Handle(Z, function.parent_function());
const auto& target =
Function::Handle(Z, function.ImplicitClosureTarget(Z));
// For BuildGraphOfNoSuchMethodForwarder, since closures no longer
// require arg_desc_var in all cases.
if (target.IsNull() ||
(parent.num_fixed_parameters() != target.num_fixed_parameters())) {
needs_expr_temp_ = true;
}
}
FALL_THROUGH;
case UntaggedFunction::kClosureFunction:
case UntaggedFunction::kRegularFunction:
case UntaggedFunction::kGetterFunction:
case UntaggedFunction::kSetterFunction:
case UntaggedFunction::kConstructor: {
const Tag tag = helper_.PeekTag();
helper_.ReadUntilFunctionNode();
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kPositionalParameters);
current_function_async_marker_ = function_node_helper.async_marker_;
// NOTE: FunctionNode is read further below the if.
intptr_t pos = 0;
if (function.IsClosureFunction()) {
LocalVariable* closure_parameter = MakeVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::ClosureParameter(), AbstractType::dynamic_type());
closure_parameter->set_is_forced_stack();
scope_->InsertParameterAt(pos++, closure_parameter);
} else if (!function.is_static()) {
// We use [is_static] instead of [IsStaticFunction] because the latter
// returns `false` for constructors.
Class& klass = Class::Handle(Z, function.Owner());
Type& klass_type = H.GetDeclarationType(klass);
LocalVariable* variable =
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::This(), klass_type);
scope_->InsertParameterAt(pos++, variable);
parsed_function_->set_receiver_var(variable);
// We visit instance field initializers because they might contain
// [Let] expressions and we need to have a mapping.
if (tag == kConstructor) {
Class& parent_class = Class::Handle(Z, function.Owner());
Array& class_fields = Array::Handle(Z, parent_class.fields());
Field& class_field = Field::Handle(Z);
for (intptr_t i = 0; i < class_fields.Length(); ++i) {
class_field ^= class_fields.At(i);
if (!class_field.is_static()) {
ExternalTypedData& kernel_data =
ExternalTypedData::Handle(Z, class_field.KernelData());
ASSERT(!kernel_data.IsNull());
intptr_t field_offset = class_field.kernel_offset();
AlternativeReadingScopeWithNewData alt(
&helper_.reader_, &kernel_data, field_offset);
FieldHelper field_helper(&helper_);
field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
Tag initializer_tag =
helper_.ReadTag(); // read first part of initializer.
if (initializer_tag == kSomething) {
EnterScope(field_offset);
VisitExpression(); // read initializer.
ExitScope(field_helper.position_, field_helper.end_position_);
}
}
}
}
} else if (function.IsFactory()) {
LocalVariable* variable = MakeVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::TypeArgumentsParameter(), AbstractType::dynamic_type());
scope_->InsertParameterAt(pos++, variable);
result_->type_arguments_variable = variable;
}
ParameterTypeCheckMode type_check_mode =
kTypeCheckForNonDynamicallyInvokedMethod;
if (function.IsSyncGenClosure()) {
// Don't type check the parameter of sync-yielding since these calls are
// all synthetic and types should always match.
ASSERT_EQUAL(
function.NumParameters() - function.NumImplicitParameters(), 3);
ASSERT(
Class::Handle(
AbstractType::Handle(function.ParameterTypeAt(1)).type_class())
.ScrubbedName() == Symbols::_SyncIterator().ptr());
type_check_mode = kTypeCheckForStaticFunction;
} else if (function.is_static()) {
// In static functions we don't check anything.
type_check_mode = kTypeCheckForStaticFunction;
} else if (function.IsImplicitClosureFunction()) {
// All non-covariant checks are either performed by the type system,
// or by a dynamic closure call dispatcher/mirror if dynamically
// invoked. For covariant checks, static targets never have covariant
// arguments and dynamic targets do their own covariant checking.
// Thus, implicit closure functions perform no checking internally.
type_check_mode = kTypeCheckForImplicitClosureFunction;
}
// Continue reading FunctionNode:
// read positional_parameters and named_parameters.
AddPositionalAndNamedParameters(pos, type_check_mode, attrs);
// We generate a synthetic body for implicit closure functions - which
// will forward the call to the real function.
// -> see BuildGraphOfImplicitClosureFunction
if (!function.IsImplicitClosureFunction()) {
helper_.SetOffset(function.kernel_offset());
first_body_token_position_ = TokenPosition::kNoSource;
VisitNode();
// TODO(jensj): HACK: Push the begin token to after any parameters to
// avoid crash when breaking on definition line of async method in
// debugger. It seems that another scope needs to be added
// in which captures are made, but I can't make that work.
// This 'solution' doesn't crash, but I cannot see the parameters at
// that particular breakpoint either.
// Also push the end token to after the "}" to avoid crashing on
// stepping past the last line (to the "}" character).
if (first_body_token_position_.IsReal()) {
scope_->set_begin_token_pos(first_body_token_position_);
}
if (scope_->end_token_pos().IsReal()) {
scope_->set_end_token_pos(scope_->end_token_pos().Next());
}
}
break;
}
case UntaggedFunction::kImplicitGetter:
case UntaggedFunction::kImplicitSetter: {
ASSERT(helper_.PeekTag() == kField);
const bool is_setter = function.IsImplicitSetterFunction();
const bool is_method = !function.IsStaticFunction();
const auto& field = Field::Handle(Z, function.accessor_field());
intptr_t pos = 0;
if (is_method) {
Class& klass = Class::Handle(Z, function.Owner());
Type& klass_type = H.GetDeclarationType(klass);
LocalVariable* variable =
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::This(), klass_type);
scope_->InsertParameterAt(pos++, variable);
parsed_function_->set_receiver_var(variable);
}
if (is_setter) {
if (CompilerState::Current().is_aot()) {
const intptr_t kernel_offset = field.kernel_offset();
const InferredTypeMetadata parameter_type =
inferred_type_metadata_helper_.GetInferredType(kernel_offset);
result_->setter_value = MakeVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::Value(),
AbstractType::ZoneHandle(Z, function.ParameterTypeAt(pos)),
&parameter_type);
} else {
result_->setter_value = MakeVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::Value(),
AbstractType::ZoneHandle(Z, function.ParameterTypeAt(pos)));
}
scope_->InsertParameterAt(pos++, result_->setter_value);
if (is_method) {
if (field.is_covariant()) {
result_->setter_value->set_is_explicit_covariant_parameter();
} else if (!field.is_generic_covariant_impl() ||
(!attrs.has_non_this_uses && !attrs.has_tearoff_uses)) {
result_->setter_value->set_type_check_mode(
LocalVariable::kTypeCheckedByCaller);
}
}
}
break;
}
case UntaggedFunction::kImplicitStaticGetter: {
ASSERT(helper_.PeekTag() == kField);
ASSERT(function.IsStaticFunction());
// In addition to static field initializers, scopes/local variables
// are needed for implicit getters of static const fields, in order to
// be able to evaluate their initializers in constant evaluator.
if (Field::Handle(Z, function.accessor_field()).is_const()) {
VisitNode();
}
break;
}
case UntaggedFunction::kFieldInitializer: {
ASSERT(helper_.PeekTag() == kField);
if (!function.is_static()) {
Class& klass = Class::Handle(Z, function.Owner());
Type& klass_type = H.GetDeclarationType(klass);
LocalVariable* variable =
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::This(), klass_type);
scope_->InsertParameterAt(0, variable);
parsed_function_->set_receiver_var(variable);
}
VisitNode();
break;
}
case UntaggedFunction::kDynamicInvocationForwarder: {
const String& name = String::Handle(Z, function.name());
ASSERT(Function::IsDynamicInvocationForwarderName(name));
const auto& target = Function::ZoneHandle(Z, function.ForwardingTarget());
ASSERT(!target.IsNull());
if (helper_.PeekTag() == kField) {
// Create [this] variable.
const Class& klass = Class::Handle(Z, function.Owner());
parsed_function_->set_receiver_var(
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::This(), H.GetDeclarationType(klass)));
scope_->InsertParameterAt(0, parsed_function_->receiver_var());
// Create setter value variable.
if (target.IsImplicitSetterFunction()) {
result_->setter_value = MakeVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::Value(),
AbstractType::ZoneHandle(Z, function.ParameterTypeAt(1)));
scope_->InsertParameterAt(1, result_->setter_value);
}
break;
}
// We do not create dyn:* forwarders for method extractors, since those
// can never return unboxed values (they return a closure).
ASSERT(!target.IsMethodExtractor());
helper_.ReadUntilFunctionNode();
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kPositionalParameters);
// Create [this] variable.
intptr_t pos = 0;
Class& klass = Class::Handle(Z, function.Owner());
parsed_function_->set_receiver_var(
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::This(), H.GetDeclarationType(klass)));
scope_->InsertParameterAt(pos++, parsed_function_->receiver_var());
// Create all positional and named parameters.
current_function_async_marker_ = FunctionNodeHelper::kSync;
AddPositionalAndNamedParameters(
pos, kTypeCheckEverythingNotCheckedInNonDynamicallyInvokedMethod,
attrs);
break;
}
case UntaggedFunction::kMethodExtractor: {
// Add a receiver parameter. Though it is captured, we emit code to
// explicitly copy it to a fixed offset in a freshly-allocated context
// instead of using the generic code for regular functions.
// Therefore, it isn't necessary to mark it as captured here.
Class& klass = Class::Handle(Z, function.Owner());
Type& klass_type = H.GetDeclarationType(klass);
LocalVariable* variable =
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::This(), klass_type);
scope_->InsertParameterAt(0, variable);
parsed_function_->set_receiver_var(variable);
break;
}
case UntaggedFunction::kFfiTrampoline: {
needs_expr_temp_ = true;
// Callbacks and calls with handles need try/catch variables.
if ((function.FfiCallbackTarget() != Function::null() ||
function.FfiCSignatureContainsHandles())) {
current_function_async_marker_ = FunctionNodeHelper::kSync;
++depth_.try_;
AddTryVariables();
--depth_.try_;
++depth_.catch_;
AddCatchVariables();
FinalizeCatchVariables();
--depth_.catch_;
}
FALL_THROUGH;
}
case UntaggedFunction::kInvokeFieldDispatcher: {
if (function.IsDynamicClosureCallDispatcher()) {
auto const vars = parsed_function_->EnsureDynamicClosureCallVars();
ASSERT(vars != nullptr);
#define ADD_VAR(Name, _, __) scope_->AddVariable(vars->Name);
FOR_EACH_DYNAMIC_CLOSURE_CALL_VARIABLE(ADD_VAR);
#undef ADD_VAR
for (auto const& v : vars->named_argument_parameter_indices) {
scope_->AddVariable(v);
}
}
}
FALL_THROUGH;
case UntaggedFunction::kNoSuchMethodDispatcher: {
for (intptr_t i = 0; i < function.NumParameters(); ++i) {
LocalVariable* variable = MakeVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
String::ZoneHandle(Z, function.ParameterNameAt(i)),
AbstractType::ZoneHandle(Z, function.IsFfiTrampoline()
? function.ParameterTypeAt(i)
: Object::dynamic_type().ptr()));
bool added = scope_->InsertParameterAt(i, variable);
ASSERT(added);
}
break;
}
case UntaggedFunction::kIrregexpFunction:
UNREACHABLE();
}
if (needs_expr_temp_) {
parsed_function_->EnsureExpressionTemp();
}
if (parsed_function_->has_expression_temp_var()) {
scope_->AddVariable(parsed_function_->expression_temp_var());
}
if (parsed_function_->function().MayHaveUncheckedEntryPoint()) {
scope_->AddVariable(parsed_function_->EnsureEntryPointsTemp());
}
parsed_function_->AllocateVariables();
return result_;
}
void ScopeBuilder::ReportUnexpectedTag(const char* variant, Tag tag) {
H.ReportError(helper_.script(), TokenPosition::kNoSource,
"Unexpected tag %d (%s) in %s, expected %s", tag,
Reader::TagName(tag),
parsed_function_->function().ToQualifiedCString(), variant);
}
void ScopeBuilder::VisitNode() {
Tag tag = helper_.PeekTag();
switch (tag) {
case kConstructor:
VisitConstructor();
return;
case kProcedure:
VisitProcedure();
return;
case kField:
VisitField();
return;
case kFunctionNode:
VisitFunctionNode();
return;
default:
UNIMPLEMENTED();
return;
}
}
void ScopeBuilder::VisitConstructor() {
// Field initializers that come from non-static field declarations are
// compiled as if they appear in the constructor initializer list. This is
// important for closure-valued field initializers because the VM expects the
// corresponding closure functions to appear as if they were nested inside the
// constructor.
ConstructorHelper constructor_helper(&helper_);
constructor_helper.ReadUntilExcluding(ConstructorHelper::kFunction);
{
const Function& function = parsed_function_->function();
Class& parent_class = Class::Handle(Z, function.Owner());
Array& class_fields = Array::Handle(Z, parent_class.fields());
Field& class_field = Field::Handle(Z);
for (intptr_t i = 0; i < class_fields.Length(); ++i) {
class_field ^= class_fields.At(i);
if (!class_field.is_static()) {
ExternalTypedData& kernel_data =
ExternalTypedData::Handle(Z, class_field.KernelData());
ASSERT(!kernel_data.IsNull());
intptr_t field_offset = class_field.kernel_offset();
AlternativeReadingScopeWithNewData alt(&helper_.reader_, &kernel_data,
field_offset);
FieldHelper field_helper(&helper_);
field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
Tag initializer_tag = helper_.ReadTag();
if (initializer_tag == kSomething) {
VisitExpression(); // read initializer.
}
}
}
}
// Visit children (note that there's no reason to visit the name).
VisitFunctionNode();
intptr_t list_length =
helper_.ReadListLength(); // read initializers list length.
for (intptr_t i = 0; i < list_length; i++) {
VisitInitializer();
}
}
void ScopeBuilder::VisitProcedure() {
ProcedureHelper procedure_helper(&helper_);
procedure_helper.ReadUntilExcluding(ProcedureHelper::kFunction);
VisitFunctionNode();
}
void ScopeBuilder::VisitField() {
FieldHelper field_helper(&helper_);
field_helper.ReadUntilExcluding(FieldHelper::kType);
VisitDartType(); // read type.
Tag tag = helper_.ReadTag(); // read initializer (part 1).
if (tag == kSomething) {
VisitExpression(); // read initializer (part 2).
}
}
void ScopeBuilder::VisitFunctionNode() {
FunctionNodeHelper function_node_helper(&helper_);
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
const auto& function = parsed_function_->function();
intptr_t list_length =
helper_.ReadListLength(); // read type_parameters list length.
for (intptr_t i = 0; i < list_length; ++i) {
TypeParameterHelper helper(&helper_);
helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound);
VisitDartType(); // read ith bound.
helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kDefaultType);
VisitDartType(); // read ith default type.
helper.Finish();
}
function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
// Read (but don't visit) the positional and named parameters, because they've
// already been added to the scope.
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kBody);
if (helper_.ReadTag() == kSomething) {
PositionScope scope(&helper_.reader_);
VisitStatement(); // Read body
first_body_token_position_ = helper_.reader_.min_position();
}
// Ensure that :await_jump_var, :await_ctx_var, :async_op, :is_sync and
// :async_future are captured.
if (function_node_helper.async_marker_ == FunctionNodeHelper::kSyncYielding) {
{
LocalVariable* temp = nullptr;
LookupCapturedVariableByName(
(depth_.function_ == 0) ? &result_->yield_jump_variable : &temp,
Symbols::AwaitJumpVar());
}
{
LocalVariable* temp = nullptr;
LookupCapturedVariableByName(
(depth_.function_ == 0) ? &result_->yield_context_variable : &temp,
Symbols::AwaitContextVar());
}
{
LocalVariable* temp =
scope_->LookupVariable(Symbols::AsyncOperation(), true);
if (temp != nullptr) {
scope_->CaptureVariable(temp);
}
}
{
LocalVariable* temp =
scope_->LookupVariable(Symbols::AsyncFuture(), true);
if (temp != nullptr) {
scope_->CaptureVariable(temp);
}
}
{
LocalVariable* temp = scope_->LookupVariable(Symbols::is_sync(), true);
if (temp != nullptr) {
scope_->CaptureVariable(temp);
}
}
{
LocalVariable* temp =
scope_->LookupVariable(Symbols::ControllerStream(), true);
if (temp != nullptr) {
scope_->CaptureVariable(temp);
}
}
}
// Mark known chained futures such as _Future::timeout()'s _future.
if (function.recognized_kind() == MethodRecognizer::kFutureTimeout &&
depth_.function_ == 1) {
LocalVariable* future = scope_->LookupVariable(Symbols::_future(), true);
ASSERT(future != nullptr);
future->set_is_chained_future();
future->set_expected_context_index(Context::kFutureTimeoutFutureIndex);
} else if (function.recognized_kind() == MethodRecognizer::kFutureWait &&
depth_.function_ == 1) {
LocalVariable* future = scope_->LookupVariable(Symbols::_future(), true);
ASSERT(future != nullptr);
future->set_is_chained_future();
future->set_expected_context_index(Context::kFutureWaitFutureIndex);
}
}
void ScopeBuilder::VisitInitializer() {
Tag tag = helper_.ReadTag();
helper_.ReadByte(); // read isSynthetic flag.
switch (tag) {
case kInvalidInitializer:
return;
case kFieldInitializer:
helper_.SkipCanonicalNameReference(); // read field_reference.
VisitExpression(); // read value.
return;
case kSuperInitializer:
helper_.ReadPosition(); // read position.
helper_.SkipCanonicalNameReference(); // read target_reference.
VisitArguments(); // read arguments.
return;
case kRedirectingInitializer:
helper_.ReadPosition(); // read position.
helper_.SkipCanonicalNameReference(); // read target_reference.
VisitArguments(); // read arguments.
return;
case kLocalInitializer:
VisitVariableDeclaration(); // read variable.
return;
case kAssertInitializer:
VisitStatement();
return;
default:
ReportUnexpectedTag("initializer", tag);
UNREACHABLE();
}
}
void ScopeBuilder::VisitExpression() {
uint8_t payload = 0;
Tag tag = helper_.ReadTag(&payload);
switch (tag) {
case kInvalidExpression:
helper_.ReadPosition();
helper_.SkipStringReference();
return;
case kVariableGet: {
helper_.ReadPosition(); // read position.
intptr_t variable_kernel_offset =
helper_.ReadUInt(); // read kernel position.
helper_.ReadUInt(); // read relative variable index.
helper_.SkipOptionalDartType(); // read promoted type.
VisitVariableGet(variable_kernel_offset);
return;
}
case kSpecializedVariableGet: {
helper_.ReadPosition(); // read position.
intptr_t variable_kernel_offset =
helper_.ReadUInt(); // read kernel position.
VisitVariableGet(variable_kernel_offset);
return;
}
case kVariableSet: {
helper_.ReadPosition(); // read position.
intptr_t variable_kernel_offset =
helper_.ReadUInt(); // read kernel position.
helper_.ReadUInt(); // read relative variable index.
LookupVariable(variable_kernel_offset);
VisitExpression(); // read expression.
return;
}
case kSpecializedVariableSet: {
helper_.ReadPosition(); // read position.
intptr_t variable_kernel_offset =
helper_.ReadUInt(); // read kernel position.
LookupVariable(variable_kernel_offset);
VisitExpression(); // read expression.
return;
}
case kInstanceGet:
helper_.ReadByte(); // read kind.
helper_.ReadPosition(); // read position.
VisitExpression(); // read receiver.
helper_.SkipName(); // read name.
helper_.SkipDartType(); // read result_type.
// read interface_target_reference.
helper_.SkipInterfaceMemberNameReference();
return;
case kDynamicGet:
helper_.ReadByte(); // read kind.
helper_.ReadPosition(); // read position.
VisitExpression(); // read receiver.
helper_.SkipName(); // read name.
return;
case kInstanceTearOff:
helper_.ReadByte(); // read kind.
helper_.ReadPosition(); // read position.
VisitExpression(); // read receiver.
helper_.SkipName(); // read name.
helper_.SkipDartType(); // read result_type.
// read interface_target_reference.
helper_.SkipInterfaceMemberNameReference();
return;
case kFunctionTearOff:
helper_.ReadPosition(); // read position.
VisitExpression(); // read receiver.
return;
case kInstanceSet:
helper_.ReadByte(); // read kind.
helper_.ReadPosition(); // read position.
VisitExpression(); // read receiver.
helper_.SkipName(); // read name.
VisitExpression(); // read value.
// read interface_target_reference.
helper_.SkipInterfaceMemberNameReference();
return;
case kDynamicSet:
helper_.ReadByte(); // read kind.
helper_.ReadPosition(); // read position.
VisitExpression(); // read receiver.
helper_.SkipName(); // read name.
VisitExpression(); // read value.
return;
case kSuperPropertyGet:
HandleLoadReceiver();
helper_.ReadPosition(); // read position.
helper_.SkipName(); // read name.
helper_.SkipInterfaceMemberNameReference(); // read target_reference.
return;
case kSuperPropertySet:
HandleLoadReceiver();
helper_.ReadPosition(); // read position.
helper_.SkipName(); // read name.
VisitExpression(); // read value.
helper_.SkipInterfaceMemberNameReference(); // read target_reference.
return;
case kStaticGet:
helper_.ReadPosition(); // read position.
helper_.SkipCanonicalNameReference(); // read target_reference.
return;
case kStaticSet:
helper_.ReadPosition(); // read position.
helper_.SkipCanonicalNameReference(); // read target_reference.
VisitExpression(); // read expression.
return;
case kInstanceInvocation:
helper_.ReadByte(); // read kind.
helper_.ReadFlags(); // read flags.
helper_.ReadPosition(); // read position.
VisitExpression(); // read receiver.
helper_.SkipName(); // read name.
VisitArguments(); // read arguments.
helper_.SkipDartType(); // read function_type.
// read interface_target_reference.
helper_.SkipInterfaceMemberNameReference();
return;
case kDynamicInvocation:
helper_.ReadByte(); // read kind.
helper_.ReadPosition(); // read position.
VisitExpression(); // read receiver.
helper_.SkipName(); // read name.
VisitArguments(); // read arguments.
return;
case kLocalFunctionInvocation: {
helper_.ReadPosition(); // read position.
intptr_t variable_kernel_offset =
helper_.ReadUInt(); // read variable kernel position.
helper_.ReadUInt(); // read relative variable index.
VisitArguments(); // read arguments.
helper_.SkipDartType(); // read function_type.
VisitVariableGet(variable_kernel_offset);
return;
}
case kFunctionInvocation:
helper_.ReadByte(); // read kind.
helper_.ReadPosition(); // read position.
VisitExpression(); // read receiver.
VisitArguments(); // read arguments.
helper_.SkipDartType(); // read function_type.
return;
case kEqualsCall:
helper_.ReadPosition(); // read position.
VisitExpression(); // read left.
VisitExpression(); // read right.
helper_.SkipDartType(); // read function_type.
// read interface_target_reference.
helper_.SkipInterfaceMemberNameReference();
return;
case kEqualsNull:
helper_.ReadPosition(); // read position.
VisitExpression(); // read expression.
return;
case kSuperMethodInvocation:
HandleLoadReceiver();
helper_.ReadPosition(); // read position.
helper_.SkipName(); // read name.
VisitArguments(); // read arguments.
// read interface_target_reference.
helper_.SkipInterfaceMemberNameReference();
return;
case kStaticInvocation:
helper_.ReadPosition(); // read position.
helper_.SkipCanonicalNameReference(); // read procedure_reference.
VisitArguments(); // read arguments.
return;
case kConstructorInvocation:
helper_.ReadPosition(); // read position.
helper_.SkipCanonicalNameReference(); // read target_reference.
VisitArguments(); // read arguments.
return;
case kNot:
VisitExpression(); // read expression.
return;
case kNullCheck:
helper_.ReadPosition(); // read position.
VisitExpression(); // read expression.
return;
case kLogicalExpression:
needs_expr_temp_ = true;
VisitExpression(); // read left.
helper_.SkipBytes(1); // read operator.
VisitExpression(); // read right.
return;
case kConditionalExpression: {
needs_expr_temp_ = true;
VisitExpression(); // read condition.
VisitExpression(); // read then.
VisitExpression(); // read otherwise.
helper_.SkipOptionalDartType(); // read unused static type.
return;
}
case kStringConcatenation: {
helper_.ReadPosition(); // read position.
intptr_t list_length = helper_.ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
VisitExpression(); // read ith expression.
}
return;
}
case kIsExpression:
helper_.ReadPosition(); // read position.
if (translation_helper_.info().kernel_binary_version() >= 38) {
helper_.ReadFlags(); // read flags.
}
VisitExpression(); // read operand.
VisitDartType(); // read type.
return;
case kAsExpression:
helper_.ReadPosition(); // read position.
helper_.ReadFlags(); // read flags.
VisitExpression(); // read operand.
VisitDartType(); // read type.
return;
case kTypeLiteral:
VisitDartType(); // read type.
return;
case kThisExpression:
HandleLoadReceiver();
return;
case kRethrow:
helper_.ReadPosition(); // read position.
return;
case kThrow:
helper_.ReadPosition(); // read position.
VisitExpression(); // read expression.
return;
case kListLiteral: {
helper_.ReadPosition(); // read position.
VisitDartType(); // read type.
intptr_t list_length = helper_.ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
VisitExpression(); // read ith expression.
}
return;
}
case kSetLiteral: {
// Set literals are currently desugared in the frontend and will not
// reach the VM. See http://dartbug.com/35124 for discussion.
UNREACHABLE();
return;
}
case kMapLiteral: {
helper_.ReadPosition(); // read position.
VisitDartType(); // read key type.
VisitDartType(); // read value type.
intptr_t list_length = helper_.ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
VisitExpression(); // read ith key.
VisitExpression(); // read ith value.
}
return;
}
case kFunctionExpression: {
intptr_t offset = helper_.ReaderOffset() - 1; // -1 to include tag byte.
helper_.ReadPosition(); // read position.
HandleLocalFunction(offset); // read function node.
return;
}
case kLet: {
PositionScope scope(&helper_.reader_);
intptr_t offset = helper_.ReaderOffset() - 1; // -1 to include tag byte.
EnterScope(offset);
helper_.ReadPosition(); // read position.
VisitVariableDeclaration(); // read variable declaration.
VisitExpression(); // read expression.
ExitScope(helper_.reader_.min_position(), helper_.reader_.max_position());
return;
}
case kBlockExpression: {
PositionScope scope(&helper_.reader_);
intptr_t offset = helper_.ReaderOffset() - 1; // -1 to include tag byte.
EnterScope(offset);
intptr_t list_length =
helper_.ReadListLength(); // read number of statements.
for (intptr_t i = 0; i < list_length; ++i) {
VisitStatement(); // read ith statement.
}
VisitExpression(); // read expression.
ExitScope(helper_.reader_.min_position(), helper_.reader_.max_position());
return;
}
case kBigIntLiteral:
helper_.SkipStringReference(); // read string reference.
return;
case kStringLiteral:
helper_.SkipStringReference(); // read string reference.
return;
case kSpecializedIntLiteral:
return;
case kNegativeIntLiteral:
helper_.ReadUInt(); // read value.
return;
case kPositiveIntLiteral:
helper_.ReadUInt(); // read value.
return;
case kDoubleLiteral:
helper_.ReadDouble(); // read value.
return;
case kTrueLiteral:
return;
case kFalseLiteral:
return;
case kNullLiteral:
return;
case kConstantExpression:
helper_.ReadPosition();
helper_.SkipDartType();
helper_.SkipConstantReference();
return;
case kInstantiation: {
VisitExpression();
const intptr_t list_length =
helper_.ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
VisitDartType(); // read ith type.
}
return;
}
case kLoadLibrary:
case kCheckLibraryIsLoaded:
helper_.ReadUInt(); // library index
break;
case kConstStaticInvocation:
case kConstConstructorInvocation:
case kConstListLiteral:
case kConstSetLiteral:
case kConstMapLiteral:
case kSymbolLiteral:
case kListConcatenation:
case kSetConcatenation:
case kMapConcatenation:
case kInstanceCreation:
case kFileUriExpression:
case kStaticTearOff:
// These nodes are internal to the front end and
// removed by the constant evaluator.
default:
ReportUnexpectedTag("expression", tag);
UNREACHABLE();
}
}
void ScopeBuilder::VisitStatement() {
Tag tag = helper_.ReadTag(); // read tag.
switch (tag) {
case kExpressionStatement:
VisitExpression(); // read expression.
return;
case kBlock: {
PositionScope scope(&helper_.reader_);
intptr_t offset = helper_.ReaderOffset() - 1; // -1 to include tag byte.
EnterScope(offset);
helper_.ReadPosition(); // read block start offset.
helper_.ReadPosition(); // read block end offset.
intptr_t list_length =
helper_.ReadListLength(); // read number of statements.
for (intptr_t i = 0; i < list_length; ++i) {
VisitStatement(); // read ith statement.
}
ExitScope(helper_.reader_.min_position(), helper_.reader_.max_position());
return;
}
case kEmptyStatement:
return;
case kAssertBlock:
if (IG->asserts()) {
PositionScope scope(&helper_.reader_);
intptr_t offset =
helper_.ReaderOffset() - 1; // -1 to include tag byte.
EnterScope(offset);
intptr_t list_length =
helper_.ReadListLength(); // read number of statements.
for (intptr_t i = 0; i < list_length; ++i) {
VisitStatement(); // read ith statement.
}
ExitScope(helper_.reader_.min_position(),
helper_.reader_.max_position());
} else {
helper_.SkipStatementList();
}
return;
case kAssertStatement:
if (IG->asserts()) {
VisitExpression(); // Read condition.
helper_.ReadPosition(); // read condition start offset.
helper_.ReadPosition(); // read condition end offset.
Tag tag = helper_.ReadTag(); // read (first part of) message.
if (tag == kSomething) {
VisitExpression(); // read (rest of) message.
}
} else {
helper_.SkipExpression(); // Read condition.
helper_.ReadPosition(); // read condition start offset.
helper_.ReadPosition(); // read condition end offset.
Tag tag = helper_.ReadTag(); // read (first part of) message.
if (tag == kSomething) {
helper_.SkipExpression(); // read (rest of) message.
}
}
return;
case kLabeledStatement:
VisitStatement(); // read body.
return;
case kBreakStatement:
helper_.ReadPosition(); // read position.
helper_.ReadUInt(); // read target_index.
return;
case kWhileStatement:
++depth_.loop_;
helper_.ReadPosition(); // read position.
VisitExpression(); // read condition.
VisitStatement(); // read body.
--depth_.loop_;
return;
case kDoStatement:
++depth_.loop_;
helper_.ReadPosition(); // read position.
VisitStatement(); // read body.
VisitExpression(); // read condition.
--depth_.loop_;
return;
case kForStatement: {
PositionScope scope(&helper_.reader_);
intptr_t offset = helper_.ReaderOffset() - 1; // -1 to include tag byte.
++depth_.loop_;
EnterScope(offset);
TokenPosition position = helper_.ReadPosition(); // read position.
intptr_t list_length =
helper_.ReadListLength(); // read number of variables.
for (intptr_t i = 0; i < list_length; ++i) {
VisitVariableDeclaration(); // read ith variable.
}
Tag tag = helper_.ReadTag(); // Read first part of condition.
if (tag == kSomething) {
VisitExpression(); // read rest of condition.
}
list_length = helper_.ReadListLength(); // read number of updates.
for (intptr_t i = 0; i < list_length; ++i) {
VisitExpression(); // read ith update.
}
VisitStatement(); // read body.
ExitScope(position, helper_.reader_.max_position());
--depth_.loop_;
return;
}
case kForInStatement:
case kAsyncForInStatement: {
PositionScope scope(&helper_.reader_);
intptr_t start_offset =
helper_.ReaderOffset() - 1; // -1 to include tag byte.
helper_.ReadPosition(); // read position.
TokenPosition body_position =
helper_.ReadPosition(); // read body position.
// Notice the ordering: We skip the variable, read the iterable, go back,
// re-read the variable, go forward to after having read the iterable.
intptr_t offset = helper_.ReaderOffset();
helper_.SkipVariableDeclaration(); // read variable.
VisitExpression(); // read iterable.
++depth_.for_in_;
AddIteratorVariable();
++depth_.loop_;
EnterScope(start_offset);
{
AlternativeReadingScope alt(&helper_.reader_, offset);
VisitVariableDeclaration(); // read variable.
}
VisitStatement(); // read body.
if (!body_position.IsReal()) {
body_position = helper_.reader_.min_position();
}
// TODO(jensj): From kernel_binary.cc
// forinstmt->variable_->set_end_position(forinstmt->position_);
ExitScope(body_position, helper_.reader_.max_position());
--depth_.loop_;
--depth_.for_in_;
return;
}
case kSwitchStatement: {
AddSwitchVariable();
helper_.ReadPosition(); // read position.
VisitExpression(); // read condition.
int case_count = helper_.ReadListLength(); // read number of cases.
for (intptr_t i = 0; i < case_count; ++i) {
int expression_count =
helper_.ReadListLength(); // read number of expressions.
for (intptr_t j = 0; j < expression_count; ++j) {
helper_.ReadPosition(); // read jth position.
VisitExpression(); // read jth expression.
}
helper_.ReadBool(); // read is_default.
VisitStatement(); // read body.
}
return;
}
case kContinueSwitchStatement:
helper_.ReadPosition(); // read position.
helper_.ReadUInt(); // read target_index.
return;
case kIfStatement:
helper_.ReadPosition(); // read position.
VisitExpression(); // read condition.
VisitStatement(); // read then.
VisitStatement(); // read otherwise.
return;
case kReturnStatement: {
if ((depth_.function_ == 0) && (depth_.finally_ > 0) &&
(result_->finally_return_variable == NULL)) {
const String& name = Symbols::TryFinallyReturnValue();
LocalVariable* variable =
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
name, AbstractType::dynamic_type());
current_function_scope_->AddVariable(variable);
result_->finally_return_variable = variable;
}
helper_.ReadPosition(); // read position
Tag tag = helper_.ReadTag(); // read (first part of) expression.
if (tag == kSomething) {
VisitExpression(); // read (rest of) expression.
}
return;
}
case kTryCatch: {
++depth_.try_;
AddTryVariables();
VisitStatement(); // read body.
--depth_.try_;
++depth_.catch_;
AddCatchVariables();
helper_.ReadByte(); // read flags
intptr_t catch_count =
helper_.ReadListLength(); // read number of catches.
for (intptr_t i = 0; i < catch_count; ++i) {
PositionScope scope(&helper_.reader_);
intptr_t offset = helper_.ReaderOffset(); // Catch has no tag.
EnterScope(offset);
helper_.ReadPosition(); // read position.
VisitDartType(); // Read the guard.
tag = helper_.ReadTag(); // read first part of exception.
if (tag == kSomething) {
VisitVariableDeclaration(); // read exception.
}
tag = helper_.ReadTag(); // read first part of stack trace.
if (tag == kSomething) {
VisitVariableDeclaration(); // read stack trace.
}
VisitStatement(); // read body.
ExitScope(helper_.reader_.min_position(),
helper_.reader_.max_position());
}
FinalizeCatchVariables();
--depth_.catch_;
return;
}
case kTryFinally: {
++depth_.try_;
++depth_.finally_;
AddTryVariables();
VisitStatement(); // read body.
--depth_.finally_;
--depth_.try_;
++depth_.catch_;
AddCatchVariables();
VisitStatement(); // read finalizer.
FinalizeCatchVariables();
--depth_.catch_;
return;
}
case kYieldStatement: {
helper_.ReadPosition(); // read position.
word flags = helper_.ReadByte(); // read flags.
VisitExpression(); // read expression.
ASSERT(flags == kNativeYieldFlags);
if (depth_.function_ == 0) {
AddSwitchVariable();
// Promote all currently visible local variables into the context.
// TODO(27590) CaptureLocalVariables promotes to many variables into
// the scope. Mark those variables as stack_local.
// TODO(27590) we don't need to promote those variables that are
// not used across yields.
scope_->CaptureLocalVariables(current_function_scope_);
}
return;
}
case kVariableDeclaration:
VisitVariableDeclaration(); // read variable declaration.
return;
case kFunctionDeclaration: {
intptr_t offset = helper_.ReaderOffset() - 1; // -1 to include tag byte.
helper_.ReadPosition(); // read position.
VisitVariableDeclaration(); // read variable declaration.
HandleLocalFunction(offset); // read function node.
return;
}
default:
ReportUnexpectedTag("declaration", tag);
UNREACHABLE();
}
}
void ScopeBuilder::VisitArguments() {
helper_.ReadUInt(); // read argument_count.
// Types
intptr_t list_length = helper_.ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
VisitDartType(); // read ith type.
}
// Positional.
list_length = helper_.ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
VisitExpression(); // read ith positional.
}
// Named.
list_length = helper_.ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
helper_.SkipStringReference(); // read ith name index.
VisitExpression(); // read ith expression.
}
}
void ScopeBuilder::VisitVariableDeclaration() {
PositionScope scope(&helper_.reader_);
intptr_t kernel_offset_no_tag = helper_.ReaderOffset();
VariableDeclarationHelper helper(&helper_);
helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
AbstractType& type = BuildAndVisitVariableType();
// In case `declaration->IsConst()` the flow graph building will take care of
// evaluating the constant and setting it via
// `declaration->SetConstantValue()`.
const String& name = (H.StringSize(helper.name_index_) == 0)
? GenerateName(":var", name_index_++)
: H.DartSymbolObfuscate(helper.name_index_);
intptr_t initializer_offset = helper_.ReaderOffset();
Tag tag = helper_.ReadTag(); // read (first part of) initializer.
if (tag == kSomething) {
VisitExpression(); // read (actual) initializer.
}
// Go to next token position so it ends *after* the last potentially
// debuggable position in the initializer.
TokenPosition end_position = helper_.reader_.max_position();
if (end_position.IsReal()) {
end_position = end_position.Next();
}
LocalVariable* variable =
MakeVariable(helper.position_, end_position, name, type);
if (helper.IsFinal()) {
variable->set_is_final();
}
if (helper.IsLate()) {
variable->set_is_late();
variable->set_late_init_offset(initializer_offset);
}
// Lift the special async vars out of the function body scope, into the
// outer function declaration scope.
// This way we can allocate them in the outermost context at fixed indices,
// allowing support for --lazy-async-stacks implementation to find awaiters.
if (name.Equals(Symbols::AwaitJumpVar()) ||
name.Equals(Symbols::AsyncFuture()) || name.Equals(Symbols::is_sync()) ||
name.Equals(Symbols::Controller())) {
scope_->parent()->AddVariable(variable);
} else {
scope_->AddVariable(variable);
}
result_->locals.Insert(helper_.data_program_offset_ + kernel_offset_no_tag,
variable);
}
AbstractType& ScopeBuilder::BuildAndVisitVariableType() {
const intptr_t offset = helper_.ReaderOffset();
AbstractType& type = T.BuildType();
helper_.SetOffset(offset); // rewind
VisitDartType();
return type;
}
void ScopeBuilder::VisitDartType() {
Tag tag = helper_.ReadTag();
switch (tag) {
case kInvalidType:
case kDynamicType:
case kVoidType:
// those contain nothing.
return;
case kNeverType:
helper_.ReadNullability();
return;
case kInterfaceType:
VisitInterfaceType(false);
return;
case kSimpleInterfaceType:
VisitInterfaceType(true);
return;
case kFunctionType:
VisitFunctionType(false);
return;
case kSimpleFunctionType:
VisitFunctionType(true);
return;
case kTypeParameterType:
VisitTypeParameterType();
return;
default:
ReportUnexpectedTag("type", tag);
UNREACHABLE();
}
}
void ScopeBuilder::VisitInterfaceType(bool simple) {
helper_.ReadNullability(); // read nullability.
helper_.ReadUInt(); // read klass_name.
if (!simple) {
intptr_t length = helper_.ReadListLength(); // read number of types.
for (intptr_t i = 0; i < length; ++i) {
VisitDartType(); // read the ith type.
}
}
}
void ScopeBuilder::VisitFunctionType(bool simple) {
helper_.ReadNullability(); // read nullability.
if (!simple) {
intptr_t list_length =
helper_.ReadListLength(); // read type_parameters list length.
for (int i = 0; i < list_length; ++i) {
TypeParameterHelper helper(&helper_);
helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound);
VisitDartType(); // read bound.
helper.ReadUntilExcludingAndSetJustRead(
TypeParameterHelper::kDefaultType);
VisitDartType(); // read default type.
helper.Finish();
}
helper_.ReadUInt(); // read required parameter count.
helper_.ReadUInt(); // read total parameter count.
}
const intptr_t positional_count =
helper_.ReadListLength(); // read positional_parameters list length.
for (intptr_t i = 0; i < positional_count; ++i) {
VisitDartType(); // read ith positional parameter.
}
if (!simple) {
const intptr_t named_count =
helper_.ReadListLength(); // read named_parameters list length.
for (intptr_t i = 0; i < named_count; ++i) {
// read string reference (i.e. named_parameters[i].name).
helper_.SkipStringReference();
VisitDartType(); // read named_parameters[i].type.
helper_.ReadByte(); // read flags
}
}
if (!simple) {
helper_.SkipOptionalDartType(); // read typedef reference.
}
VisitDartType(); // read return type.
}
void ScopeBuilder::VisitTypeParameterType() {
Function& function = Function::Handle(Z, parsed_function_->function().ptr());
while (function.IsClosureFunction()) {
function = function.parent_function();
}
helper_.ReadNullability(); // read nullability.
// The index here is the index identifying the type parameter binding site
// inside the DILL file, which uses a different indexing system than the VM
// uses for its 'TypeParameter's internally. This index includes both class
// and function type parameters.
intptr_t index = helper_.ReadUInt(); // read index for parameter.
if (function.IsFactory()) {
// The type argument vector is passed as the very first argument to the
// factory constructor function.
HandleSpecialLoad(&result_->type_arguments_variable,
Symbols::TypeArgumentsParameter());
} else {
// If the type parameter is a parameter to this or an enclosing function, we
// can read it directly from the function type arguments vector later.
// Otherwise, the type arguments vector we need is stored on the instance
// object, so we need to capture 'this'.
Class& parent_class = Class::Handle(Z, function.Owner());
if (index < parent_class.NumTypeParameters()) {
HandleLoadReceiver();
}
}
helper_.SkipOptionalDartType(); // read bound bound.
}
void ScopeBuilder::HandleLocalFunction(intptr_t parent_kernel_offset) {
// "Peek" ahead into the function node
intptr_t offset = helper_.ReaderOffset();
FunctionNodeHelper function_node_helper(&helper_);
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
LocalScope* saved_function_scope = current_function_scope_;
FunctionNodeHelper::AsyncMarker saved_function_async_marker =
current_function_async_marker_;
DepthState saved_depth_state = depth_;
depth_ = DepthState(depth_.function_ + 1);
EnterScope(parent_kernel_offset);
current_function_scope_ = scope_;
current_function_async_marker_ = function_node_helper.async_marker_;
if (depth_.function_ == 1) {
FunctionScope function_scope = {offset, scope_};
result_->function_scopes.Add(function_scope);
}
int num_type_params = 0;
{
AlternativeReadingScope _(&helper_.reader_);
num_type_params = helper_.ReadListLength();
}
// Adding this scope here informs the type translator the type parameters of
// this function are now in scope, although they are not defined and will be
// filled in with dynamic. This is OK, since their definitions are not needed
// for scope building of the enclosing function.
TypeTranslator::TypeParameterScope scope(&type_translator_, num_type_params);
// read positional_parameters and named_parameters.
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kPositionalParameters);
ProcedureAttributesMetadata default_attrs;
AddPositionalAndNamedParameters(0, kTypeCheckForNonDynamicallyInvokedMethod,
default_attrs);
// "Peek" is now done.
helper_.SetOffset(offset);
VisitFunctionNode(); // read function node.
ExitScope(function_node_helper.position_, function_node_helper.end_position_);
depth_ = saved_depth_state;
current_function_scope_ = saved_function_scope;
current_function_async_marker_ = saved_function_async_marker;
}
void ScopeBuilder::EnterScope(intptr_t kernel_offset) {
scope_ = new (Z) LocalScope(scope_, depth_.function_, depth_.loop_);
ASSERT(kernel_offset >= 0);
result_->scopes.Insert(kernel_offset, scope_);
}
void ScopeBuilder::ExitScope(TokenPosition start_position,
TokenPosition end_position) {
scope_->set_begin_token_pos(start_position);
scope_->set_end_token_pos(end_position);
scope_ = scope_->parent();
}
void ScopeBuilder::AddPositionalAndNamedParameters(
intptr_t pos,
ParameterTypeCheckMode type_check_mode /* = kTypeCheckAllParameters*/,
const ProcedureAttributesMetadata& attrs) {
// List of positional.
intptr_t list_length = helper_.ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
AddVariableDeclarationParameter(pos++, type_check_mode, attrs);
}
// List of named.
list_length = helper_.ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
AddVariableDeclarationParameter(pos++, type_check_mode, attrs);
}
}
void ScopeBuilder::AddVariableDeclarationParameter(
intptr_t pos,
ParameterTypeCheckMode type_check_mode,
const ProcedureAttributesMetadata& attrs) {
intptr_t kernel_offset = helper_.ReaderOffset(); // no tag.
const InferredTypeMetadata parameter_type =
inferred_type_metadata_helper_.GetInferredType(kernel_offset);
VariableDeclarationHelper helper(&helper_);
helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
String& name = H.DartSymbolObfuscate(helper.name_index_);
ASSERT(name.Length() > 0);
AbstractType& type = BuildAndVisitVariableType(); // read type.
helper.SetJustRead(VariableDeclarationHelper::kType);
helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer);
LocalVariable* variable = MakeVariable(helper.position_, helper.position_,
name, type, &parameter_type);
if (helper.IsFinal()) {
variable->set_is_final();
}
if (helper.IsCovariant()) {
variable->set_is_explicit_covariant_parameter();
}
// The :sync_op and :async_op continuations are called multiple times. So we
// don't want the parameters from the first invocation to get stored in the
// context and reused on later invocations with different parameters.
if (current_function_async_marker_ == FunctionNodeHelper::kSyncYielding) {
variable->set_is_forced_stack();
}
const bool needs_covariant_check_in_method =
helper.IsCovariant() ||
(helper.IsGenericCovariantImpl() &&
(attrs.has_non_this_uses || attrs.has_tearoff_uses));
if (needs_covariant_check_in_method) {
variable->set_needs_covariant_check_in_method();
}
switch (type_check_mode) {
case kTypeCheckAllParameters:
variable->set_type_check_mode(LocalVariable::kDoTypeCheck);
break;
case kTypeCheckEverythingNotCheckedInNonDynamicallyInvokedMethod:
if (needs_covariant_check_in_method) {
// Don't type check covariant parameters - they will be checked by
// a function we forward to. Their types however are not known.
variable->set_type_check_mode(LocalVariable::kSkipTypeCheck);
} else {
variable->set_type_check_mode(LocalVariable::kDoTypeCheck);
}
break;
case kTypeCheckForNonDynamicallyInvokedMethod:
if (needs_covariant_check_in_method) {
variable->set_type_check_mode(LocalVariable::kDoTypeCheck);
} else {
// Types of non-covariant parameters are guaranteed to match by
// front-end enforcing strong mode types at call site.
variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
}
break;
case kTypeCheckForImplicitClosureFunction:
if (needs_covariant_check_in_method) {
// Don't type check covariant parameters - they will be checked by
// a function we forward to. Their types however are not known.
variable->set_type_check_mode(LocalVariable::kSkipTypeCheck);
} else {
// All non-covariant checks are either checked by the type system or
// by a dynamic closure call dispatcher.
variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
}
break;
case kTypeCheckForStaticFunction:
variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
break;
}
// TODO(sjindel): We can also skip these checks on dynamic invocations as
// well.
if (parameter_type.IsSkipCheck()) {
variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
}
scope_->InsertParameterAt(pos, variable);
result_->locals.Insert(helper_.data_program_offset_ + kernel_offset,
variable);
// The default value may contain 'let' bindings for which the constant
// evaluator needs scope bindings.
Tag tag = helper_.ReadTag();
if (tag == kSomething) {
VisitExpression(); // read initializer.
}
}
LocalVariable* ScopeBuilder::MakeVariable(
TokenPosition declaration_pos,
TokenPosition token_pos,
const String& name,
const AbstractType& type,
const InferredTypeMetadata* param_type_md /* = NULL */) {
CompileType* param_type = nullptr;
const Object* param_value = nullptr;
if (param_type_md != nullptr && !param_type_md->IsTrivial()) {
param_type = new (Z) CompileType(param_type_md->ToCompileType(Z));
if (param_type_md->IsConstant()) {
param_value = &param_type_md->constant_value;
}
}
return new (Z) LocalVariable(declaration_pos, token_pos, name, type,
param_type, param_value);
}
void ScopeBuilder::AddExceptionVariable(
GrowableArray<LocalVariable*>* variables,
const char* prefix,
intptr_t nesting_depth) {
LocalVariable* v = NULL;
// If we are inside a function with yield points then Kernel transformer
// could have lifted some of the auxiliary exception variables into the
// context to preserve them across yield points because they might
// be needed for rethrow.
// Check if it did and capture such variables instead of introducing
// new local ones.
// Note: function that wrap kSyncYielding function does not contain
// its own try/catches.
if (current_function_async_marker_ == FunctionNodeHelper::kSyncYielding) {
ASSERT(current_function_scope_->parent() != NULL);
v = current_function_scope_->parent()->LocalLookupVariable(
GenerateName(prefix, nesting_depth - 1));
if (v != NULL) {
scope_->CaptureVariable(v);
}
}
// No need to create variables for try/catch-statements inside
// nested functions.
if (depth_.function_ > 0) return;
if (variables->length() >= nesting_depth) return;
// If variable was not lifted by the transformer introduce a new
// one into the current function scope.
if (v == NULL) {
v = MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
GenerateName(prefix, nesting_depth - 1),
AbstractType::dynamic_type());
// If transformer did not lift the variable then there is no need
// to lift it into the context when we encouter a YieldStatement.
v->set_is_forced_stack();
current_function_scope_->AddVariable(v);
}
variables->Add(v);
}
void ScopeBuilder::FinalizeExceptionVariable(
GrowableArray<LocalVariable*>* variables,
GrowableArray<LocalVariable*>* raw_variables,
const String& symbol,
intptr_t nesting_depth) {
// No need to create variables for try/catch-statements inside
// nested functions.
if (depth_.function_ > 0) return;
LocalVariable* variable = (*variables)[nesting_depth - 1];
LocalVariable* raw_variable;
if (variable->is_captured()) {
raw_variable =
new LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
symbol, AbstractType::dynamic_type());
raw_variable->set_is_forced_stack();
const bool ok = scope_->AddVariable(raw_variable);
ASSERT(ok);
} else {
raw_variable = variable;
}
raw_variables->EnsureLength(nesting_depth, nullptr);
(*raw_variables)[nesting_depth - 1] = raw_variable;
}
void ScopeBuilder::AddTryVariables() {
AddExceptionVariable(&result_->catch_context_variables,
":saved_try_context_var", depth_.try_);
}
void ScopeBuilder::AddCatchVariables() {
AddExceptionVariable(&result_->exception_variables, ":exception",
depth_.catch_);
AddExceptionVariable(&result_->stack_trace_variables, ":stack_trace",
depth_.catch_);
}
void ScopeBuilder::FinalizeCatchVariables() {
const intptr_t unique_id = result_->raw_variable_counter_++;
FinalizeExceptionVariable(
&result_->exception_variables, &result_->raw_exception_variables,
GenerateName(":raw_exception", unique_id), depth_.catch_);
FinalizeExceptionVariable(
&result_->stack_trace_variables, &result_->raw_stack_trace_variables,
GenerateName(":raw_stacktrace", unique_id), depth_.catch_);
}
void ScopeBuilder::AddIteratorVariable() {
if (depth_.function_ > 0) return;
if (result_->iterator_variables.length() >= depth_.for_in_) return;
ASSERT(result_->iterator_variables.length() == depth_.for_in_ - 1);
LocalVariable* iterator =
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
GenerateName(":iterator", depth_.for_in_ - 1),
AbstractType::dynamic_type());
current_function_scope_->AddVariable(iterator);
result_->iterator_variables.Add(iterator);
}
void ScopeBuilder::AddSwitchVariable() {
if ((depth_.function_ == 0) && (result_->switch_variable == NULL)) {
LocalVariable* variable =
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::SwitchExpr(), AbstractType::dynamic_type());
variable->set_is_forced_stack();
current_function_scope_->AddVariable(variable);
result_->switch_variable = variable;
}
}
void ScopeBuilder::VisitVariableGet(intptr_t declaration_binary_offset) {
LocalVariable* variable = LookupVariable(declaration_binary_offset);
if (variable->is_late()) {
// Late variable initializer expressions may also contain local variables
// that need to be captured.
AlternativeReadingScope alt(&helper_.reader_, variable->late_init_offset());
if (helper_.ReadTag() != kNothing) {
VisitExpression();
}
}
}
LocalVariable* ScopeBuilder::LookupVariable(
intptr_t declaration_binary_offset) {
LocalVariable* variable = result_->locals.Lookup(declaration_binary_offset);
if (variable == NULL) {
// We have not seen a declaration of the variable, so it must be the
// case that we are compiling a nested function and the variable is
// declared in an outer scope. In that case, look it up in the scope by
// name and add it to the variable map to simplify later lookup.
ASSERT(current_function_scope_->parent() != NULL);
StringIndex var_name = GetNameFromVariableDeclaration(
declaration_binary_offset - helper_.data_program_offset_,
parsed_function_->function());
const String& name = H.DartSymbolObfuscate(var_name);
variable = current_function_scope_->parent()->LookupVariable(name, true);
ASSERT(variable != NULL);
result_->locals.Insert(declaration_binary_offset, variable);
}
if (variable->owner()->function_level() < scope_->function_level()) {
// We call `LocalScope->CaptureVariable(variable)` in two scenarios for two
// different reasons:
// Scenario 1:
// We need to know which variables defined in this function
// are closed over by nested closures in order to ensure we will
// create a [Context] object of appropriate size and store captured
// variables there instead of the stack.
// Scenario 2:
// We need to find out which variables defined in enclosing functions
// are closed over by this function/closure or nested closures. This
// is necessary in order to build a fat flattened [ContextScope]
// object.
scope_->CaptureVariable(variable);
} else {
ASSERT(variable->owner()->function_level() == scope_->function_level());
}
return variable;
}
StringIndex ScopeBuilder::GetNameFromVariableDeclaration(
intptr_t kernel_offset,
const Function& function) {
ExternalTypedData& kernel_data =
ExternalTypedData::Handle(Z, function.KernelData());
ASSERT(!kernel_data.IsNull());
// Temporarily go to the variable declaration, read the name.
AlternativeReadingScopeWithNewData alt(&helper_.reader_, &kernel_data,
kernel_offset);
VariableDeclarationHelper helper(&helper_);
helper.ReadUntilIncluding(VariableDeclarationHelper::kNameIndex);
return helper.name_index_;
}
const String& ScopeBuilder::GenerateName(const char* prefix, intptr_t suffix) {
char name[64];
Utils::SNPrint(name, 64, "%s%" Pd "", prefix, suffix);
return H.DartSymbolObfuscate(name);
}
void ScopeBuilder::HandleLoadReceiver() {
if (!parsed_function_->has_receiver_var() &&
current_function_scope_->parent() != nullptr) {
// Lazily populate receiver variable using the parent function scope.
parsed_function_->set_receiver_var(
current_function_scope_->parent()->LookupVariable(Symbols::This(),
true));
}
if ((current_function_scope_->parent() != nullptr) ||
(scope_->function_level() > 0)) {
// Every scope we use the [receiver] from needs to be notified of the usage
// in order to ensure that preserving the context scope on that particular
// use-site also includes the [receiver].
scope_->CaptureVariable(parsed_function_->receiver_var());
}
}
void ScopeBuilder::HandleSpecialLoad(LocalVariable** variable,
const String& symbol) {
if (current_function_scope_->parent() != NULL) {
// We are building the scope tree of a closure function and saw [node]. We
// lazily populate the variable using the parent function scope.
if (*variable == NULL) {
*variable =
current_function_scope_->parent()->LookupVariable(symbol, true);
ASSERT(*variable != NULL);
}
}
if ((current_function_scope_->parent() != NULL) ||
(scope_->function_level() > 0)) {
// Every scope we use the [variable] from needs to be notified of the usage
// in order to ensure that preserving the context scope on that particular
// use-site also includes the [variable].
scope_->CaptureVariable(*variable);
}
}
void ScopeBuilder::LookupCapturedVariableByName(LocalVariable** variable,
const String& name) {
if (*variable == NULL) {
*variable = scope_->LookupVariable(name, true);
ASSERT(*variable != NULL);
scope_->CaptureVariable(*variable);
}
}
} // namespace kernel
} // namespace dart