blob: 19a77d37515c3c3bb2218ff7b6a1816e3b165617 [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/bootstrap.h"
#include "vm/code_descriptors.h"
#include "vm/compiler/aot/precompiler.h"
#include "vm/compiler/assembler/disassembler_kbc.h"
#include "vm/compiler/frontend/prologue_builder.h"
#include "vm/compiler/jit/compiler.h"
#include "vm/dart_entry.h"
#include "vm/longjump.h"
#include "vm/object_store.h"
#include "vm/resolver.h"
#include "vm/stack_frame.h"
#if !defined(DART_PRECOMPILED_RUNTIME)
namespace dart {
#if defined(DART_USE_INTERPRETER)
DEFINE_FLAG(bool, dump_kernel_bytecode, false, "Dump kernel bytecode");
#endif // defined(DART_USE_INTERPRETER)
namespace kernel {
#define Z (zone_)
#define H (translation_helper_)
#define T (type_translator_)
#define I Isolate::Current()
#define B (flow_graph_builder_)
static bool IsFieldInitializer(const Function& function, Zone* zone) {
return (function.kind() == RawFunction::kImplicitStaticFinalGetter) &&
String::Handle(zone, function.name())
.StartsWith(Symbols::InitPrefix());
}
void FunctionNodeHelper::ReadUntilExcluding(Field field) {
if (field <= next_read_) return;
// Ordered with fall-through.
switch (next_read_) {
case kStart: {
Tag tag = helper_->ReadTag(); // read tag.
ASSERT(tag == kFunctionNode);
if (++next_read_ == field) return;
}
/* Falls through */
case kPosition:
position_ = helper_->ReadPosition(); // read position.
if (++next_read_ == field) return;
/* Falls through */
case kEndPosition:
end_position_ = helper_->ReadPosition(); // read end position.
if (++next_read_ == field) return;
/* Falls through */
case kAsyncMarker:
async_marker_ = static_cast<AsyncMarker>(helper_->ReadByte());
if (++next_read_ == field) return;
/* Falls through */
case kDartAsyncMarker:
dart_async_marker_ = static_cast<AsyncMarker>(
helper_->ReadByte()); // read dart async marker.
if (++next_read_ == field) return;
/* Falls through */
case kTypeParameters:
helper_->SkipTypeParametersList(); // read type parameters.
if (++next_read_ == field) return;
/* Falls through */
case kTotalParameterCount:
total_parameter_count_ =
helper_->ReadUInt(); // read total parameter count.
if (++next_read_ == field) return;
/* Falls through */
case kRequiredParameterCount:
required_parameter_count_ =
helper_->ReadUInt(); // read required parameter count.
if (++next_read_ == field) return;
/* Falls through */
case kPositionalParameters:
helper_->SkipListOfVariableDeclarations(); // read positionals.
if (++next_read_ == field) return;
/* Falls through */
case kNamedParameters:
helper_->SkipListOfVariableDeclarations(); // read named.
if (++next_read_ == field) return;
/* Falls through */
case kReturnType:
helper_->SkipDartType(); // read return type.
if (++next_read_ == field) return;
/* Falls through */
case kBody:
if (helper_->ReadTag() == kSomething)
helper_->SkipStatement(); // read body.
if (++next_read_ == field) return;
/* Falls through */
case kEnd:
return;
}
}
void TypeParameterHelper::ReadUntilExcluding(Field field) {
for (; next_read_ < field; ++next_read_) {
switch (next_read_) {
case kFlags:
flags_ = helper_->ReadFlags();
break;
case kAnnotations:
helper_->SkipListOfExpressions(); // read annotations.
break;
case kName:
name_index_ = helper_->ReadStringReference(); // read name index.
break;
case kBound:
helper_->SkipDartType();
break;
case kDefaultType:
if (helper_->ReadTag() == kSomething) {
helper_->SkipDartType();
}
break;
case kEnd:
return;
}
}
}
void VariableDeclarationHelper::ReadUntilExcluding(Field field) {
if (field <= next_read_) return;
// Ordered with fall-through.
switch (next_read_) {
case kPosition:
position_ = helper_->ReadPosition(); // read position.
if (++next_read_ == field) return;
/* Falls through */
case kEqualPosition:
equals_position_ = helper_->ReadPosition(); // read equals position.
if (++next_read_ == field) return;
/* Falls through */
case kAnnotations:
helper_->SkipListOfExpressions(); // read annotations.
if (++next_read_ == field) return;
/* Falls through */
case kFlags:
flags_ = helper_->ReadFlags();
if (++next_read_ == field) return;
/* Falls through */
case kNameIndex:
name_index_ = helper_->ReadStringReference(); // read name index.
if (++next_read_ == field) return;
/* Falls through */
case kType:
helper_->SkipDartType(); // read type.
if (++next_read_ == field) return;
/* Falls through */
case kInitializer:
if (helper_->ReadTag() == kSomething)
helper_->SkipExpression(); // read initializer.
if (++next_read_ == field) return;
/* Falls through */
case kEnd:
return;
}
}
FieldHelper::FieldHelper(KernelReaderHelper* helper, intptr_t offset)
: helper_(helper),
next_read_(kStart),
has_function_literal_initializer_(false) {
helper_->SetOffset(offset);
}
void FieldHelper::ReadUntilExcluding(Field field,
bool detect_function_literal_initializer) {
if (field <= next_read_) return;
// Ordered with fall-through.
switch (next_read_) {
case kStart: {
Tag tag = helper_->ReadTag(); // read tag.
ASSERT(tag == kField);
if (++next_read_ == field) return;
}
/* Falls through */
case kCanonicalName:
canonical_name_ =
helper_->ReadCanonicalNameReference(); // read canonical_name.
if (++next_read_ == field) return;
/* Falls through */
case kSourceUriIndex:
source_uri_index_ = helper_->ReadUInt(); // read source_uri_index.
helper_->set_current_script_id(source_uri_index_);
if (++next_read_ == field) return;
/* Falls through */
case kPosition:
position_ = helper_->ReadPosition(false); // read position.
helper_->RecordTokenPosition(position_);
if (++next_read_ == field) return;
/* Falls through */
case kEndPosition:
end_position_ = helper_->ReadPosition(false); // read end position.
helper_->RecordTokenPosition(end_position_);
if (++next_read_ == field) return;
/* Falls through */
case kFlags:
flags_ = helper_->ReadFlags();
if (++next_read_ == field) return;
/* Falls through */
case kName:
helper_->SkipName(); // read name.
if (++next_read_ == field) return;
/* Falls through */
case kAnnotations: {
annotation_count_ = helper_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < annotation_count_; ++i) {
helper_->SkipExpression(); // read ith expression.
}
if (++next_read_ == field) return;
}
/* Falls through */
case kType:
helper_->SkipDartType(); // read type.
if (++next_read_ == field) return;
/* Falls through */
case kInitializer:
if (helper_->ReadTag() == kSomething) {
if (detect_function_literal_initializer &&
helper_->PeekTag() == kFunctionExpression) {
AlternativeReadingScope alt(&helper_->reader_);
Tag tag = helper_->ReadTag();
ASSERT(tag == kFunctionExpression);
helper_->ReadPosition(); // read position.
FunctionNodeHelper helper(helper_);
helper.ReadUntilIncluding(FunctionNodeHelper::kEndPosition);
has_function_literal_initializer_ = true;
function_literal_start_ = helper.position_;
function_literal_end_ = helper.end_position_;
}
helper_->SkipExpression(); // read initializer.
}
if (++next_read_ == field) return;
/* Falls through */
case kEnd:
return;
}
}
void ProcedureHelper::ReadUntilExcluding(Field field) {
if (field <= next_read_) return;
// Ordered with fall-through.
switch (next_read_) {
case kStart: {
Tag tag = helper_->ReadTag(); // read tag.
ASSERT(tag == kProcedure);
if (++next_read_ == field) return;
}
/* Falls through */
case kCanonicalName:
canonical_name_ =
helper_->ReadCanonicalNameReference(); // read canonical_name.
if (++next_read_ == field) return;
/* Falls through */
case kSourceUriIndex:
source_uri_index_ = helper_->ReadUInt(); // read source_uri_index.
helper_->set_current_script_id(source_uri_index_);
if (++next_read_ == field) return;
/* Falls through */
case kPosition:
position_ = helper_->ReadPosition(false); // read position.
helper_->RecordTokenPosition(position_);
if (++next_read_ == field) return;
/* Falls through */
case kEndPosition:
end_position_ = helper_->ReadPosition(false); // read end position.
helper_->RecordTokenPosition(end_position_);
if (++next_read_ == field) return;
/* Falls through */
case kKind:
kind_ = static_cast<Kind>(helper_->ReadByte());
if (++next_read_ == field) return;
/* Falls through */
case kFlags:
flags_ = helper_->ReadFlags();
if (++next_read_ == field) return;
/* Falls through */
case kName:
helper_->SkipName(); // read name.
if (++next_read_ == field) return;
/* Falls through */
case kAnnotations: {
annotation_count_ = helper_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < annotation_count_; ++i) {
helper_->SkipExpression(); // read ith expression.
}
if (++next_read_ == field) return;
}
/* Falls through */
case kForwardingStubSuperTarget:
if (helper_->ReadTag() == kSomething) {
forwarding_stub_super_target_ = helper_->ReadCanonicalNameReference();
}
if (++next_read_ == field) return;
/* Falls through */
case kForwardingStubInterfaceTarget:
if (helper_->ReadTag() == kSomething) {
helper_->ReadCanonicalNameReference();
}
if (++next_read_ == field) return;
/* Falls through */
case kFunction:
if (helper_->ReadTag() == kSomething)
helper_->SkipFunctionNode(); // read function node.
if (++next_read_ == field) return;
/* Falls through */
case kEnd:
return;
}
}
void ConstructorHelper::ReadUntilExcluding(Field field) {
if (field <= next_read_) return;
// Ordered with fall-through.
switch (next_read_) {
case kStart: {
Tag tag = helper_->ReadTag(); // read tag.
ASSERT(tag == kConstructor);
if (++next_read_ == field) return;
}
/* Falls through */
case kCanonicalName:
canonical_name_ =
helper_->ReadCanonicalNameReference(); // read canonical_name.
if (++next_read_ == field) return;
/* Falls through */
case kSourceUriIndex:
source_uri_index_ = helper_->ReadUInt(); // read source_uri_index.
helper_->set_current_script_id(source_uri_index_);
if (++next_read_ == field) return;
/* Falls through */
case kPosition:
position_ = helper_->ReadPosition(); // read position.
helper_->RecordTokenPosition(position_);
if (++next_read_ == field) return;
/* Falls through */
case kEndPosition:
end_position_ = helper_->ReadPosition(); // read end position.
helper_->RecordTokenPosition(end_position_);
if (++next_read_ == field) return;
/* Falls through */
case kFlags:
flags_ = helper_->ReadFlags();
if (++next_read_ == field) return;
/* Falls through */
case kName:
helper_->SkipName(); // read name.
if (++next_read_ == field) return;
/* Falls through */
case kAnnotations: {
annotation_count_ = helper_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < annotation_count_; ++i) {
helper_->SkipExpression(); // read ith expression.
}
if (++next_read_ == field) return;
}
/* Falls through */
case kFunction:
helper_->SkipFunctionNode(); // read function.
if (++next_read_ == field) return;
/* Falls through */
case kInitializers: {
intptr_t list_length =
helper_->ReadListLength(); // read initializers list length.
for (intptr_t i = 0; i < list_length; i++) {
helper_->SkipInitializer();
}
if (++next_read_ == field) return;
}
/* Falls through */
case kEnd:
return;
}
}
void ClassHelper::ReadUntilExcluding(Field field) {
if (field <= next_read_) return;
// Ordered with fall-through.
switch (next_read_) {
case kStart: {
Tag tag = helper_->ReadTag(); // read tag.
ASSERT(tag == kClass);
if (++next_read_ == field) return;
}
/* Falls through */
case kCanonicalName:
canonical_name_ =
helper_->ReadCanonicalNameReference(); // read canonical_name.
if (++next_read_ == field) return;
/* Falls through */
case kSourceUriIndex:
source_uri_index_ = helper_->ReadUInt(); // read source_uri_index.
helper_->set_current_script_id(source_uri_index_);
if (++next_read_ == field) return;
/* Falls through */
case kPosition:
position_ = helper_->ReadPosition(false); // read position.
helper_->RecordTokenPosition(position_);
if (++next_read_ == field) return;
/* Falls through */
case kEndPosition:
end_position_ = helper_->ReadPosition(); // read end position.
helper_->RecordTokenPosition(end_position_);
if (++next_read_ == field) return;
/* Falls through */
case kFlags:
flags_ = helper_->ReadFlags(); // read flags.
if (++next_read_ == field) return;
/* Falls through */
case kNameIndex:
name_index_ = helper_->ReadStringReference(); // read name index.
if (++next_read_ == field) return;
/* Falls through */
case kAnnotations: {
annotation_count_ = helper_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < annotation_count_; ++i) {
helper_->SkipExpression(); // read ith expression.
}
if (++next_read_ == field) return;
}
/* Falls through */
case kTypeParameters:
helper_->SkipTypeParametersList(); // read type parameters.
if (++next_read_ == field) return;
/* Falls through */
case kSuperClass: {
Tag type_tag = helper_->ReadTag(); // read super class type (part 1).
if (type_tag == kSomething) {
helper_->SkipDartType(); // read super class type (part 2).
}
if (++next_read_ == field) return;
}
/* Falls through */
case kMixinType: {
Tag type_tag = helper_->ReadTag(); // read mixin type (part 1).
if (type_tag == kSomething) {
helper_->SkipDartType(); // read mixin type (part 2).
}
if (++next_read_ == field) return;
}
/* Falls through */
case kImplementedClasses:
helper_->SkipListOfDartTypes(); // read implemented_classes.
if (++next_read_ == field) return;
/* Falls through */
case kFields: {
intptr_t list_length =
helper_->ReadListLength(); // read fields list length.
for (intptr_t i = 0; i < list_length; i++) {
FieldHelper field_helper(helper_);
field_helper.ReadUntilExcluding(FieldHelper::kEnd); // read field.
}
if (++next_read_ == field) return;
}
/* Falls through */
case kConstructors: {
intptr_t list_length =
helper_->ReadListLength(); // read constructors list length.
for (intptr_t i = 0; i < list_length; i++) {
ConstructorHelper constructor_helper(helper_);
constructor_helper.ReadUntilExcluding(
ConstructorHelper::kEnd); // read constructor.
}
if (++next_read_ == field) return;
}
/* Falls through */
case kProcedures: {
procedure_count_ = helper_->ReadListLength(); // read procedures #.
for (intptr_t i = 0; i < procedure_count_; i++) {
ProcedureHelper procedure_helper(helper_);
procedure_helper.ReadUntilExcluding(
ProcedureHelper::kEnd); // read procedure.
}
if (++next_read_ == field) return;
}
/* Falls through */
case kClassIndex:
// Read class index.
for (intptr_t i = 0; i < procedure_count_; ++i) {
helper_->reader_.ReadUInt32();
}
helper_->reader_.ReadUInt32();
helper_->reader_.ReadUInt32();
if (++next_read_ == field) return;
/* Falls through */
case kEnd:
return;
}
}
void LibraryHelper::ReadUntilExcluding(Field field) {
if (field <= next_read_) return;
// Ordered with fall-through.
switch (next_read_) {
case kFlags: {
flags_ = helper_->ReadFlags();
if (++next_read_ == field) return;
}
/* Falls through */
case kCanonicalName:
canonical_name_ =
helper_->ReadCanonicalNameReference(); // read canonical_name.
if (++next_read_ == field) return;
/* Falls through */
case kName:
name_index_ = helper_->ReadStringReference(); // read name index.
if (++next_read_ == field) return;
/* Falls through */
case kSourceUriIndex:
source_uri_index_ = helper_->ReadUInt(); // read source_uri_index.
helper_->set_current_script_id(source_uri_index_);
if (++next_read_ == field) return;
/* Falls through */
case kAnnotations:
helper_->SkipListOfExpressions(); // read annotations.
if (++next_read_ == field) return;
/* Falls through */
case kDependencies: {
intptr_t dependency_count = helper_->ReadUInt(); // read list length.
for (intptr_t i = 0; i < dependency_count; ++i) {
helper_->SkipLibraryDependency();
}
if (++next_read_ == field) return;
}
/* Falls through */
case kAdditionalExports: {
intptr_t name_count = helper_->ReadUInt();
for (intptr_t i = 0; i < name_count; ++i) {
helper_->SkipCanonicalNameReference();
}
if (++next_read_ == field) return;
}
/* Falls through */
case kParts: {
intptr_t part_count = helper_->ReadUInt(); // read list length.
for (intptr_t i = 0; i < part_count; ++i) {
helper_->SkipLibraryPart();
}
if (++next_read_ == field) return;
}
/* Falls through */
case kTypedefs: {
intptr_t typedef_count = helper_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < typedef_count; i++) {
helper_->SkipLibraryTypedef();
}
if (++next_read_ == field) return;
}
/* Falls through */
case kClasses: {
class_count_ = helper_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < class_count_; ++i) {
ClassHelper class_helper(helper_);
class_helper.ReadUntilExcluding(ClassHelper::kEnd);
}
if (++next_read_ == field) return;
}
/* Falls through */
case kToplevelField: {
intptr_t field_count = helper_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < field_count; ++i) {
FieldHelper field_helper(helper_);
field_helper.ReadUntilExcluding(FieldHelper::kEnd);
}
if (++next_read_ == field) return;
}
/* Falls through */
case kToplevelProcedures: {
procedure_count_ = helper_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < procedure_count_; ++i) {
ProcedureHelper procedure_helper(helper_);
procedure_helper.ReadUntilExcluding(ProcedureHelper::kEnd);
}
if (++next_read_ == field) return;
}
/* Falls through */
case kLibraryIndex:
// Read library index.
for (intptr_t i = 0; i < class_count_; ++i) {
helper_->reader_.ReadUInt32();
}
helper_->reader_.ReadUInt32();
helper_->reader_.ReadUInt32();
for (intptr_t i = 0; i < procedure_count_; ++i) {
helper_->reader_.ReadUInt32();
}
helper_->reader_.ReadUInt32();
helper_->reader_.ReadUInt32();
if (++next_read_ == field) return;
/* Falls through */
case kEnd:
return;
}
}
void LibraryDependencyHelper::ReadUntilExcluding(Field field) {
if (field <= next_read_) return;
// Ordered with fall-through.
switch (next_read_) {
case kFileOffset: {
helper_->ReadPosition();
if (++next_read_ == field) return;
}
/* Falls through */
case kFlags: {
flags_ = helper_->ReadFlags();
if (++next_read_ == field) return;
}
/* Falls through */
case kAnnotations: {
helper_->SkipListOfExpressions();
if (++next_read_ == field) return;
}
/* Falls through */
case kTargetLibrary: {
target_library_canonical_name_ = helper_->ReadCanonicalNameReference();
if (++next_read_ == field) return;
}
/* Falls through */
case kName: {
name_index_ = helper_->ReadStringReference();
if (++next_read_ == field) return;
}
/* Falls through */
case kCombinators: {
intptr_t count = helper_->ReadListLength();
for (intptr_t i = 0; i < count; ++i) {
// Skip flags
helper_->SkipBytes(1);
// Skip list of names.
helper_->SkipListOfStrings();
}
if (++next_read_ == field) return;
}
/* Falls through */
case kEnd:
return;
}
}
MetadataHelper::MetadataHelper(StreamingFlowGraphBuilder* builder)
: builder_(builder),
translation_helper_(builder->translation_helper_),
mappings_offset_(0),
mappings_num_(0),
last_node_offset_(0),
last_mapping_index_(0) {}
void MetadataHelper::SetMetadataMappings(intptr_t mappings_offset,
intptr_t mappings_num) {
ASSERT((mappings_offset_ == 0) && (mappings_num_ == 0));
ASSERT((mappings_offset != 0) && (mappings_num != 0));
mappings_offset_ = mappings_offset;
mappings_num_ = mappings_num;
#ifdef DEBUG
// Verify that node offsets are sorted.
{
Reader reader(H.metadata_mappings());
reader.set_offset(mappings_offset);
intptr_t prev_node_offset = 0;
for (intptr_t i = 0; i < mappings_num; ++i) {
intptr_t node_offset = reader.ReadUInt32();
intptr_t md_offset = reader.ReadUInt32();
ASSERT((node_offset > 0) && (md_offset >= 0));
ASSERT(node_offset > prev_node_offset);
prev_node_offset = node_offset;
}
}
#endif // DEBUG
last_node_offset_ = kIntptrMax;
last_mapping_index_ = 0;
}
intptr_t MetadataHelper::FindMetadataMapping(intptr_t node_offset) {
const intptr_t kUInt32Size = 4;
ASSERT(mappings_num_ > 0);
Reader reader(H.metadata_mappings());
intptr_t left = 0;
intptr_t right = mappings_num_ - 1;
while (left < right) {
intptr_t mid = ((right - left) / 2) + left;
intptr_t mid_node_offset =
reader.ReadUInt32At(mappings_offset_ + mid * 2 * kUInt32Size);
if (node_offset < mid_node_offset) {
right = mid - 1;
} else if (node_offset > mid_node_offset) {
left = mid + 1;
} else {
return mid; // Exact match found.
}
}
ASSERT((0 <= left) && (left <= mappings_num_));
// Approximate match is found. Make sure it has an offset greater or equal
// to the given node offset.
if (left < mappings_num_) {
intptr_t found_node_offset =
reader.ReadUInt32At(mappings_offset_ + left * 2 * kUInt32Size);
if (found_node_offset < node_offset) {
++left;
}
}
ASSERT((left == mappings_num_) ||
static_cast<intptr_t>(reader.ReadUInt32At(
mappings_offset_ + left * 2 * kUInt32Size)) >= node_offset);
return left;
}
intptr_t MetadataHelper::GetNextMetadataPayloadOffset(intptr_t node_offset) {
builder_->EnsureMetadataIsScanned();
if (mappings_num_ == 0) {
return -1; // No metadata.
}
node_offset += builder_->data_program_offset_;
// Nodes are parsed in linear order most of the time, so do the search
// only if looking back.
if (node_offset < last_node_offset_) {
last_mapping_index_ = FindMetadataMapping(node_offset);
}
intptr_t index = last_mapping_index_;
intptr_t mapping_node_offset = 0;
intptr_t mapping_md_offset = -1;
Reader reader(H.metadata_mappings());
const intptr_t kUInt32Size = 4;
reader.set_offset(mappings_offset_ + index * 2 * kUInt32Size);
for (; index < mappings_num_; ++index) {
mapping_node_offset = reader.ReadUInt32();
mapping_md_offset = reader.ReadUInt32();
if (mapping_node_offset >= node_offset) {
break;
}
}
last_mapping_index_ = index;
last_node_offset_ = node_offset;
if ((index < mappings_num_) && (mapping_node_offset == node_offset)) {
ASSERT(mapping_md_offset >= 0);
return mapping_md_offset;
} else {
return -1;
}
}
bool DirectCallMetadataHelper::ReadMetadata(intptr_t node_offset,
NameIndex* target_name,
bool* check_receiver_for_null) {
intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset);
if (md_offset < 0) {
return false;
}
AlternativeReadingScope alt(&builder_->reader_, &H.metadata_payloads(),
md_offset);
*target_name = builder_->ReadCanonicalNameReference();
*check_receiver_for_null = builder_->ReadBool();
return true;
}
DirectCallMetadata DirectCallMetadataHelper::GetDirectTargetForPropertyGet(
intptr_t node_offset) {
NameIndex kernel_name;
bool check_receiver_for_null = false;
if (!ReadMetadata(node_offset, &kernel_name, &check_receiver_for_null)) {
return DirectCallMetadata(Function::null_function(), false);
}
if (H.IsProcedure(kernel_name) && !H.IsGetter(kernel_name)) {
// Tear-off. Use method extractor as direct call target.
const String& method_name = H.DartMethodName(kernel_name);
const Function& target_method = Function::ZoneHandle(
builder_->zone_,
builder_->LookupMethodByMember(kernel_name, method_name));
const String& getter_name = H.DartGetterName(kernel_name);
return DirectCallMetadata(
Function::ZoneHandle(builder_->zone_,
target_method.GetMethodExtractor(getter_name)),
check_receiver_for_null);
} else {
const String& getter_name = H.DartGetterName(kernel_name);
const Function& target = Function::ZoneHandle(
builder_->zone_,
builder_->LookupMethodByMember(kernel_name, getter_name));
ASSERT(target.IsGetterFunction() || target.IsImplicitGetterFunction());
return DirectCallMetadata(target, check_receiver_for_null);
}
}
DirectCallMetadata DirectCallMetadataHelper::GetDirectTargetForPropertySet(
intptr_t node_offset) {
NameIndex kernel_name;
bool check_receiver_for_null = false;
if (!ReadMetadata(node_offset, &kernel_name, &check_receiver_for_null)) {
return DirectCallMetadata(Function::null_function(), false);
}
const String& method_name = H.DartSetterName(kernel_name);
const Function& target = Function::ZoneHandle(
builder_->zone_,
builder_->LookupMethodByMember(kernel_name, method_name));
ASSERT(target.IsSetterFunction() || target.IsImplicitSetterFunction());
return DirectCallMetadata(target, check_receiver_for_null);
}
DirectCallMetadata DirectCallMetadataHelper::GetDirectTargetForMethodInvocation(
intptr_t node_offset) {
NameIndex kernel_name;
bool check_receiver_for_null = false;
if (!ReadMetadata(node_offset, &kernel_name, &check_receiver_for_null)) {
return DirectCallMetadata(Function::null_function(), false);
}
const String& method_name = H.DartProcedureName(kernel_name);
const Function& target = Function::ZoneHandle(
builder_->zone_,
builder_->LookupMethodByMember(kernel_name, method_name));
return DirectCallMetadata(target, check_receiver_for_null);
}
bool ProcedureAttributesMetadataHelper::ReadMetadata(
intptr_t node_offset,
ProcedureAttributesMetadata* metadata) {
intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset);
if (md_offset < 0) {
return false;
}
AlternativeReadingScope alt(&builder_->reader_, &H.metadata_payloads(),
md_offset);
const int kDynamicUsesBit = 1 << 0;
const int kNonThisUsesBit = 1 << 1;
const int kTearOffUsesBit = 1 << 2;
const uint8_t flags = builder_->ReadByte();
metadata->has_dynamic_invocations =
(flags & kDynamicUsesBit) == kDynamicUsesBit;
metadata->has_non_this_uses = (flags & kNonThisUsesBit) == kNonThisUsesBit;
metadata->has_tearoff_uses = (flags & kTearOffUsesBit) == kTearOffUsesBit;
return true;
}
ProcedureAttributesMetadata
ProcedureAttributesMetadataHelper::GetProcedureAttributes(
intptr_t node_offset) {
ProcedureAttributesMetadata metadata;
ReadMetadata(node_offset, &metadata);
return metadata;
}
InferredTypeMetadata InferredTypeMetadataHelper::GetInferredType(
intptr_t node_offset) {
const intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset);
if (md_offset < 0) {
return InferredTypeMetadata(kDynamicCid, true);
}
AlternativeReadingScope alt(&builder_->reader_, &H.metadata_payloads(),
md_offset);
const NameIndex kernel_name = builder_->ReadCanonicalNameReference();
const bool nullable = builder_->ReadBool();
if (H.IsRoot(kernel_name)) {
return InferredTypeMetadata(kDynamicCid, nullable);
}
const Class& klass =
Class::Handle(builder_->zone_, H.LookupClassByKernelClass(kernel_name));
ASSERT(!klass.IsNull());
intptr_t cid = klass.id();
if (cid == kClosureCid) {
// VM uses more specific function types and doesn't expect instances of
// _Closure class, so inferred _Closure class doesn't make sense for the VM.
cid = kDynamicCid;
}
return InferredTypeMetadata(cid, nullable);
}
#if defined(DART_USE_INTERPRETER)
void BytecodeMetadataHelper::ReadMetadata(const Function& function) {
const intptr_t node_offset = function.kernel_offset();
const intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset);
if (md_offset < 0) {
return;
}
AlternativeReadingScope alt(&builder_->reader_, &H.metadata_payloads(),
md_offset);
// Create object pool and read pool entries.
const intptr_t obj_count = builder_->reader_.ReadListLength();
const ObjectPool& pool =
ObjectPool::Handle(builder_->zone_, ObjectPool::New(obj_count));
ReadPoolEntries(function, function, pool, 0);
// Read bytecode and attach to function.
const Code& bytecode = Code::Handle(builder_->zone_, ReadBytecode(pool));
function.AttachBytecode(bytecode);
// Read exceptions table.
ReadExceptionsTable(bytecode);
if (FLAG_dump_kernel_bytecode) {
KernelBytecodeDisassembler::Disassemble(function);
}
// Read closures.
Function& closure = Function::Handle(builder_->zone_);
Code& closure_bytecode = Code::Handle(builder_->zone_);
intptr_t num_closures = builder_->ReadListLength();
for (intptr_t i = 0; i < num_closures; i++) {
intptr_t closure_index = builder_->ReadUInt();
ASSERT(closure_index < obj_count);
closure ^= pool.ObjectAt(closure_index);
// Read closure bytecode and attach to closure function.
closure_bytecode = ReadBytecode(pool);
closure.AttachBytecode(closure_bytecode);
// Read closure exceptions table.
ReadExceptionsTable(closure_bytecode);
if (FLAG_dump_kernel_bytecode) {
KernelBytecodeDisassembler::Disassemble(closure);
}
}
}
intptr_t BytecodeMetadataHelper::ReadPoolEntries(const Function& function,
const Function& inner_function,
const ObjectPool& pool,
intptr_t from_index) {
// These enums and the code below reading the constant pool from kernel must
// be kept in sync with pkg/vm/lib/bytecode/constant_pool.dart.
enum ConstantPoolTag {
kInvalid,
kNull,
kString,
kInt,
kDouble,
kBool,
kArgDesc,
kICData,
kStaticICData,
kField,
kFieldOffset,
kClass,
kTypeArgumentsFieldOffset,
kTearOff,
kType,
kTypeArguments,
kList,
kInstance,
kSymbol,
kTypeArgumentsForInstanceAllocation,
kContextOffset,
kClosureFunction,
kEndClosureFunctionScope,
kNativeEntry,
kSubtypeTestCache,
};
enum InvocationKind {
method, // x.foo(...) or foo(...)
getter, // x.foo
setter // x.foo = ...
};
Object& obj = Object::Handle(builder_->zone_);
Object& elem = Object::Handle(builder_->zone_);
Array& array = Array::Handle(builder_->zone_);
Field& field = Field::Handle(builder_->zone_);
Class& cls = Class::Handle(builder_->zone_);
String& name = String::Handle(builder_->zone_);
TypeArguments& type_args = TypeArguments::Handle(builder_->zone_);
const intptr_t obj_count = pool.Length();
for (intptr_t i = from_index; i < obj_count; ++i) {
const intptr_t tag = builder_->ReadTag();
switch (tag) {
case ConstantPoolTag::kInvalid:
UNREACHABLE();
case ConstantPoolTag::kNull:
obj = Object::null();
break;
case ConstantPoolTag::kString:
obj = H.DartString(builder_->ReadStringReference()).raw();
ASSERT(obj.IsString());
obj = H.Canonicalize(String::Cast(obj));
break;
case ConstantPoolTag::kInt: {
uint32_t low_bits = builder_->ReadUInt32();
int64_t value = builder_->ReadUInt32();
value = (value << 32) | low_bits;
obj = Integer::New(value);
} break;
case ConstantPoolTag::kDouble: {
uint32_t low_bits = builder_->ReadUInt32();
uint64_t bits = builder_->ReadUInt32();
bits = (bits << 32) | low_bits;
double value = bit_cast<double, uint64_t>(bits);
obj = Double::New(value);
} break;
case ConstantPoolTag::kBool:
if (builder_->ReadUInt() == 1) {
obj = Bool::True().raw();
} else {
obj = Bool::False().raw();
}
break;
case ConstantPoolTag::kArgDesc: {
intptr_t num_arguments = builder_->ReadUInt();
intptr_t num_type_args = builder_->ReadUInt();
intptr_t num_arg_names = builder_->ReadListLength();
if (num_arg_names == 0) {
obj = ArgumentsDescriptor::New(num_type_args, num_arguments);
} else {
array = Array::New(num_arg_names);
for (intptr_t j = 0; j < num_arg_names; j++) {
array.SetAt(j, H.DartSymbolPlain(builder_->ReadStringReference()));
}
obj = ArgumentsDescriptor::New(num_type_args, num_arguments, array);
}
} break;
case ConstantPoolTag::kICData: {
InvocationKind kind = static_cast<InvocationKind>(builder_->ReadByte());
if (kind == InvocationKind::getter) {
name = builder_->ReadNameAsGetterName().raw();
} else if (kind == InvocationKind::setter) {
name = builder_->ReadNameAsSetterName().raw();
} else {
ASSERT(kind == InvocationKind::method);
name = builder_->ReadNameAsMethodName().raw();
}
intptr_t arg_desc_index = builder_->ReadUInt();
ASSERT(arg_desc_index < i);
array ^= pool.ObjectAt(arg_desc_index);
// TODO(regis): Should num_args_tested be explicitly provided?
obj = ICData::New(function, name,
array, // Arguments descriptor.
Thread::kNoDeoptId, 1 /* num_args_tested */,
ICData::RebindRule::kInstance);
#if defined(TAG_IC_DATA)
ICData::Cast(obj).set_tag(Instruction::kInstanceCall);
#endif
} break;
case ConstantPoolTag::kStaticICData: {
InvocationKind kind = static_cast<InvocationKind>(builder_->ReadByte());
NameIndex target = builder_->ReadCanonicalNameReference();
if (H.IsConstructor(target)) {
name = H.DartConstructorName(target).raw();
elem = H.LookupConstructorByKernelConstructor(target);
} else if (H.IsField(target)) {
if (kind == InvocationKind::getter) {
name = H.DartGetterName(target).raw();
} else if (kind == InvocationKind::setter) {
name = H.DartSetterName(target).raw();
} else {
ASSERT(kind == InvocationKind::method);
UNIMPLEMENTED(); // TODO(regis): Revisit.
}
field = H.LookupFieldByKernelField(target);
cls = field.Owner();
elem = cls.LookupFunctionAllowPrivate(name);
} else {
if ((kind == InvocationKind::method) && H.IsGetter(target)) {
UNIMPLEMENTED(); // TODO(regis): Revisit.
}
name = H.DartProcedureName(target).raw();
elem = H.LookupStaticMethodByKernelProcedure(target);
}
ASSERT(elem.IsFunction());
intptr_t arg_desc_index = builder_->ReadUInt();
ASSERT(arg_desc_index < i);
array ^= pool.ObjectAt(arg_desc_index);
obj = ICData::New(function, name,
array, // Arguments descriptor.
Thread::kNoDeoptId, 0 /* num_args_tested */,
ICData::RebindRule::kStatic);
ICData::Cast(obj).AddTarget(Function::Cast(elem));
#if defined(TAG_IC_DATA)
ICData::Cast(obj).set_tag(Instruction::kStaticCall);
#endif
} break;
case ConstantPoolTag::kField:
obj =
H.LookupFieldByKernelField(builder_->ReadCanonicalNameReference());
ASSERT(obj.IsField());
break;
case ConstantPoolTag::kFieldOffset:
obj =
H.LookupFieldByKernelField(builder_->ReadCanonicalNameReference());
ASSERT(obj.IsField());
obj = Smi::New(Field::Cast(obj).Offset() / kWordSize);
break;
case ConstantPoolTag::kClass:
obj =
H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference());
ASSERT(obj.IsClass());
break;
case ConstantPoolTag::kTypeArgumentsFieldOffset:
cls =
H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference());
obj = Smi::New(cls.type_arguments_field_offset() / kWordSize);
break;
case ConstantPoolTag::kTearOff:
obj = H.LookupStaticMethodByKernelProcedure(
builder_->ReadCanonicalNameReference());
ASSERT(obj.IsFunction());
obj = Function::Cast(obj).ImplicitClosureFunction();
ASSERT(obj.IsFunction());
obj = Function::Cast(obj).ImplicitStaticClosure();
ASSERT(obj.IsInstance());
obj = H.Canonicalize(Instance::Cast(obj));
break;
case ConstantPoolTag::kType:
obj = builder_->type_translator_.BuildType().raw();
ASSERT(obj.IsAbstractType());
break;
case ConstantPoolTag::kTypeArguments:
obj = builder_->type_translator_
.BuildTypeArguments(builder_->ReadListLength())
.raw();
ASSERT(obj.IsNull() || obj.IsTypeArguments());
break;
case ConstantPoolTag::kList: {
obj = builder_->type_translator_.BuildType().raw();
ASSERT(obj.IsAbstractType());
const intptr_t length = builder_->ReadListLength();
array = Array::New(length, AbstractType::Cast(obj));
for (intptr_t j = 0; j < length; j++) {
intptr_t elem_index = builder_->ReadUInt();
ASSERT(elem_index < i);
elem = pool.ObjectAt(elem_index);
array.SetAt(j, elem);
}
obj = H.Canonicalize(Array::Cast(array));
ASSERT(!obj.IsNull());
} break;
case ConstantPoolTag::kInstance: {
cls =
H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference());
obj = Instance::New(cls, Heap::kOld);
intptr_t type_args_index = builder_->ReadUInt();
ASSERT(type_args_index < i);
type_args ^= pool.ObjectAt(type_args_index);
if (!type_args.IsNull()) {
Instance::Cast(obj).SetTypeArguments(type_args);
}
intptr_t num_fields = builder_->ReadUInt();
for (intptr_t j = 0; j < num_fields; j++) {
NameIndex field_name = builder_->ReadCanonicalNameReference();
ASSERT(H.IsField(field_name));
field = H.LookupFieldByKernelField(field_name);
intptr_t elem_index = builder_->ReadUInt();
ASSERT(elem_index < i);
elem = pool.ObjectAt(elem_index);
Instance::Cast(obj).SetField(field, elem);
}
obj = H.Canonicalize(Instance::Cast(obj));
} break;
case ConstantPoolTag::kSymbol:
obj = H.DartSymbolPlain(builder_->ReadStringReference()).raw();
ASSERT(String::Cast(obj).IsSymbol());
break;
case ConstantPoolTag::kTypeArgumentsForInstanceAllocation: {
cls =
H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference());
obj =
builder_->type_translator_
.BuildInstantiatedTypeArguments(cls, builder_->ReadListLength())
.raw();
ASSERT(obj.IsNull() || obj.IsTypeArguments());
} break;
case ConstantPoolTag::kContextOffset: {
intptr_t index = builder_->ReadUInt();
if (i == 0) {
obj = Smi::New(Context::parent_offset() / kWordSize);
} else {
obj = Smi::New(Context::variable_offset(index - 1) / kWordSize);
}
} break;
case ConstantPoolTag::kClosureFunction: {
name = H.DartSymbolPlain(builder_->ReadStringReference()).raw();
const Function& closure = Function::Handle(
builder_->zone_,
Function::NewClosureFunction(name, inner_function,
TokenPosition::kNoSource));
FunctionNodeHelper function_node_helper(builder_);
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kTypeParameters);
builder_->LoadAndSetupTypeParameters(builder_->active_class(), closure,
builder_->ReadListLength(),
closure);
function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
// Scope remains opened until ConstantPoolTag::kEndClosureFunctionScope.
ActiveTypeParametersScope scope(
builder_->active_class(), &closure,
TypeArguments::Handle(builder_->zone_, closure.type_parameters()),
builder_->zone_);
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kPositionalParameters);
intptr_t required_parameter_count =
function_node_helper.required_parameter_count_;
intptr_t total_parameter_count =
function_node_helper.total_parameter_count_;
intptr_t positional_parameter_count = builder_->ReadListLength();
intptr_t named_parameter_count =
total_parameter_count - positional_parameter_count;
const intptr_t extra_parameters = 1;
closure.set_num_fixed_parameters(extra_parameters +
required_parameter_count);
if (named_parameter_count > 0) {
closure.SetNumOptionalParameters(named_parameter_count, false);
} else {
closure.SetNumOptionalParameters(
positional_parameter_count - required_parameter_count, true);
}
intptr_t parameter_count = extra_parameters + total_parameter_count;
closure.set_parameter_types(Array::Handle(
builder_->zone_, Array::New(parameter_count, Heap::kOld)));
closure.set_parameter_names(Array::Handle(
builder_->zone_, Array::New(parameter_count, Heap::kOld)));
intptr_t pos = 0;
closure.SetParameterTypeAt(pos, AbstractType::dynamic_type());
closure.SetParameterNameAt(pos, Symbols::ClosureParameter());
pos++;
const Library& lib = Library::Handle(
builder_->zone_, builder_->active_class()->klass->library());
for (intptr_t j = 0; j < positional_parameter_count; ++j, ++pos) {
VariableDeclarationHelper helper(builder_);
helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
const AbstractType& type =
builder_->type_translator_.BuildVariableType();
Tag tag = builder_->ReadTag(); // read (first part of) initializer.
if (tag == kSomething) {
builder_->SkipExpression(); // read (actual) initializer.
}
closure.SetParameterTypeAt(pos, type);
closure.SetParameterNameAt(pos,
H.DartIdentifier(lib, helper.name_index_));
}
intptr_t named_parameter_count_check = builder_->ReadListLength();
ASSERT(named_parameter_count_check == named_parameter_count);
for (intptr_t j = 0; j < named_parameter_count; ++j, ++pos) {
VariableDeclarationHelper helper(builder_);
helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
const AbstractType& type =
builder_->type_translator_.BuildVariableType();
Tag tag = builder_->ReadTag(); // read (first part of) initializer.
if (tag == kSomething) {
builder_->SkipExpression(); // read (actual) initializer.
}
closure.SetParameterTypeAt(pos, type);
closure.SetParameterNameAt(pos,
H.DartIdentifier(lib, helper.name_index_));
}
function_node_helper.SetJustRead(FunctionNodeHelper::kNamedParameters);
const AbstractType& return_type =
builder_->type_translator_.BuildVariableType();
closure.set_result_type(return_type);
function_node_helper.SetJustRead(FunctionNodeHelper::kReturnType);
// The closure has no body.
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd);
pool.SetTypeAt(i, ObjectPool::kTaggedObject);
pool.SetObjectAt(i, closure);
// Continue reading the constant pool entries inside the opened
// ActiveTypeParametersScope until the scope gets closed by a
// kEndClosureFunctionScope tag, in which case control returns here.
i = ReadPoolEntries(function, closure, pool, i + 1);
// Pool entry at index i has been set to null, because it was a
// kEndClosureFunctionScope.
ASSERT(pool.ObjectAt(i) == Object::null());
continue;
}
case ConstantPoolTag::kEndClosureFunctionScope: {
// Entry is not used and set to null.
obj = Object::null();
pool.SetTypeAt(i, ObjectPool::kTaggedObject);
pool.SetObjectAt(i, obj);
return i; // The caller will close the scope.
} break;
case ConstantPoolTag::kNativeEntry: {
name = H.DartString(builder_->ReadStringReference()).raw();
obj = NativeEntry(function, name);
} break;
case ConstantPoolTag::kSubtypeTestCache: {
obj = SubtypeTestCache::New();
} break;
default:
UNREACHABLE();
}
pool.SetTypeAt(i, ObjectPool::kTaggedObject);
pool.SetObjectAt(i, obj);
}
// Return the index of the last read pool entry.
return obj_count - 1;
}
RawCode* BytecodeMetadataHelper::ReadBytecode(const ObjectPool& pool) {
// TODO(regis): Avoid copying bytecode from mapped kernel binary.
intptr_t size = builder_->reader_.ReadUInt();
intptr_t offset = builder_->reader_.offset();
uint8_t* data =
builder_->reader_.CopyDataIntoZone(builder_->zone_, offset, size);
builder_->reader_.set_offset(offset + size);
// Create and return code object.
return Code::FinalizeBytecode(reinterpret_cast<void*>(data), size, pool);
}
void BytecodeMetadataHelper::ReadExceptionsTable(const Code& bytecode) {
const intptr_t try_block_count = builder_->reader_.ReadListLength();
if (try_block_count > 0) {
const ObjectPool& pool =
ObjectPool::Handle(builder_->zone_, bytecode.object_pool());
AbstractType& handler_type = AbstractType::Handle(builder_->zone_);
Array& handler_types = Array::Handle(builder_->zone_);
DescriptorList* pc_descriptors_list =
new (builder_->zone_) DescriptorList(64);
ExceptionHandlerList* exception_handlers_list =
new (builder_->zone_) ExceptionHandlerList();
// Encoding of ExceptionsTable is described in
// pkg/vm/lib/bytecode/exceptions.dart.
for (intptr_t try_index = 0; try_index < try_block_count; try_index++) {
intptr_t outer_try_index_plus1 = builder_->reader_.ReadUInt();
intptr_t outer_try_index = outer_try_index_plus1 - 1;
intptr_t start_pc = builder_->reader_.ReadUInt();
intptr_t end_pc = builder_->reader_.ReadUInt();
intptr_t handler_pc = builder_->reader_.ReadUInt();
uint8_t flags = builder_->reader_.ReadByte();
const uint8_t kFlagNeedsStackTrace = 1 << 0;
const uint8_t kFlagIsSynthetic = 1 << 1;
const bool needs_stacktrace = (flags & kFlagNeedsStackTrace) != 0;
const bool is_generated = (flags & kFlagIsSynthetic) != 0;
intptr_t type_count = builder_->reader_.ReadListLength();
ASSERT(type_count > 0);
handler_types = Array::New(type_count, Heap::kOld);
for (intptr_t i = 0; i < type_count; i++) {
intptr_t type_index = builder_->reader_.ReadUInt();
ASSERT(type_index < pool.Length());
handler_type ^= pool.ObjectAt(type_index);
handler_types.SetAt(i, handler_type);
}
pc_descriptors_list->AddDescriptor(RawPcDescriptors::kOther, start_pc,
Thread::kNoDeoptId,
TokenPosition::kNoSource, try_index);
pc_descriptors_list->AddDescriptor(RawPcDescriptors::kOther, end_pc,
Thread::kNoDeoptId,
TokenPosition::kNoSource, -1);
exception_handlers_list->AddHandler(
try_index, outer_try_index, handler_pc, TokenPosition::kNoSource,
is_generated, handler_types, needs_stacktrace);
}
const PcDescriptors& descriptors = PcDescriptors::Handle(
builder_->zone_,
pc_descriptors_list->FinalizePcDescriptors(bytecode.PayloadStart()));
bytecode.set_pc_descriptors(descriptors);
const ExceptionHandlers& handlers = ExceptionHandlers::Handle(
builder_->zone_, exception_handlers_list->FinalizeExceptionHandlers(
bytecode.PayloadStart()));
bytecode.set_exception_handlers(handlers);
} else {
bytecode.set_pc_descriptors(Object::empty_descriptors());
bytecode.set_exception_handlers(Object::empty_exception_handlers());
}
}
RawTypedData* BytecodeMetadataHelper::NativeEntry(const Function& function,
const String& external_name) {
Zone* zone = builder_->zone_;
MethodRecognizer::Kind kind = MethodRecognizer::RecognizeKind(function);
// This list of recognized methods must be kept in sync with the list of
// methods handled specially by the NativeCall bytecode in the interpreter.
switch (kind) {
case MethodRecognizer::kObjectEquals:
case MethodRecognizer::kStringBaseLength:
case MethodRecognizer::kStringBaseIsEmpty:
case MethodRecognizer::kGrowableArrayLength:
case MethodRecognizer::kObjectArrayLength:
case MethodRecognizer::kImmutableArrayLength:
case MethodRecognizer::kTypedDataLength:
case MethodRecognizer::kClassIDgetID:
case MethodRecognizer::kGrowableArrayCapacity:
case MethodRecognizer::kListFactory:
case MethodRecognizer::kObjectArrayAllocate:
case MethodRecognizer::kLinkedHashMap_getIndex:
case MethodRecognizer::kLinkedHashMap_setIndex:
case MethodRecognizer::kLinkedHashMap_getData:
case MethodRecognizer::kLinkedHashMap_setData:
case MethodRecognizer::kLinkedHashMap_getHashMask:
case MethodRecognizer::kLinkedHashMap_setHashMask:
case MethodRecognizer::kLinkedHashMap_getUsedData:
case MethodRecognizer::kLinkedHashMap_setUsedData:
case MethodRecognizer::kLinkedHashMap_getDeletedKeys:
case MethodRecognizer::kLinkedHashMap_setDeletedKeys:
break;
default:
kind = MethodRecognizer::kUnknown;
}
NativeFunctionWrapper trampoline = NULL;
NativeFunction native_function = NULL;
intptr_t argc_tag = 0;
if (kind == MethodRecognizer::kUnknown) {
if (FLAG_link_natives_lazily) {
trampoline = &NativeEntry::BootstrapNativeCallWrapper;
native_function =
reinterpret_cast<NativeFunction>(&NativeEntry::LinkNativeCall);
} else {
const Class& cls = Class::Handle(zone, function.Owner());
const Library& library = Library::Handle(zone, cls.library());
Dart_NativeEntryResolver resolver = library.native_entry_resolver();
const bool is_bootstrap_native = Bootstrap::IsBootstrapResolver(resolver);
const int num_params =
NativeArguments::ParameterCountForResolution(function);
bool is_auto_scope = true;
native_function = NativeEntry::ResolveNative(library, external_name,
num_params, &is_auto_scope);
ASSERT(native_function != NULL); // TODO(regis): Should we throw instead?
if (is_bootstrap_native) {
trampoline = &NativeEntry::BootstrapNativeCallWrapper;
} else if (is_auto_scope) {
trampoline = &NativeEntry::AutoScopeNativeCallWrapper;
} else {
trampoline = &NativeEntry::NoScopeNativeCallWrapper;
}
}
argc_tag = NativeArguments::ComputeArgcTag(function);
}
// TODO(regis): Introduce a new VM class subclassing Object and containing
// these four untagged values.
#ifdef ARCH_IS_32_BIT
const TypedData& native_entry = TypedData::Handle(
zone, TypedData::New(kTypedDataUint32ArrayCid, 4, Heap::kOld));
native_entry.SetUint32(0 << 2, static_cast<uint32_t>(kind));
native_entry.SetUint32(1 << 2, reinterpret_cast<uint32_t>(trampoline));
native_entry.SetUint32(2 << 2, reinterpret_cast<uint32_t>(native_function));
native_entry.SetUint32(3 << 2, static_cast<uint32_t>(argc_tag));
#else
const TypedData& native_entry = TypedData::Handle(
zone, TypedData::New(kTypedDataUint64ArrayCid, 4, Heap::kOld));
native_entry.SetUint64(0 << 3, static_cast<uint64_t>(kind));
native_entry.SetUint64(1 << 3, reinterpret_cast<uint64_t>(trampoline));
native_entry.SetUint64(2 << 3, reinterpret_cast<uint64_t>(native_function));
native_entry.SetUint64(3 << 3, static_cast<uint64_t>(argc_tag));
#endif
return native_entry.raw();
}
#endif // defined(DART_USE_INTERPRETER)
StreamingScopeBuilder::StreamingScopeBuilder(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),
builder_(new StreamingFlowGraphBuilder(
&translation_helper_,
Script::Handle(Z, parsed_function->function().script()),
zone_,
TypedData::Handle(Z, parsed_function->function().KernelData()),
parsed_function->function().KernelDataProgramOffset(),
&active_class_)),
type_translator_(builder_, /*finalize=*/true) {
H.InitFromScript(builder_->script());
ASSERT(type_translator_.active_class_ == &active_class_);
ASSERT(builder_->type_translator_.active_class_ == &active_class_);
}
StreamingScopeBuilder::~StreamingScopeBuilder() {
delete builder_;
}
ScopeBuildingResult* StreamingScopeBuilder::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(zone_, function.Owner());
Function& outermost_function = Function::Handle(Z);
builder_->DiscoverEnclosingElements(Z, function, &outermost_function);
ActiveClassScope active_class_scope(&active_class_, &klass);
ActiveMemberScope active_member(&active_class_, &outermost_function);
ActiveTypeParametersScope active_type_params(&active_class_, function, 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.GetCanonicalType(klass);
result_->this_variable =
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::This(), klass_type);
result_->this_variable->set_index(VariableIndex(0));
result_->this_variable->set_is_captured();
enclosing_scope = new (Z) LocalScope(NULL, 0, 0);
enclosing_scope->set_context_level(0);
enclosing_scope->AddVariable(result_->this_variable);
} else if (function.IsLocalFunction()) {
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 (I->reify_generic_functions() &&
(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()) {
needs_expr_temp_ = true;
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_->SetNodeSequence(
new SequenceNode(TokenPosition::kNoSource, scope_));
builder_->SetOffset(function.kernel_offset());
FunctionNodeHelper function_node_helper(builder_);
const ProcedureAttributesMetadata attrs =
builder_->procedure_attributes_metadata_helper_.GetProcedureAttributes(
function.kernel_offset());
switch (function.kind()) {
case RawFunction::kClosureFunction:
case RawFunction::kImplicitClosureFunction:
case RawFunction::kRegularFunction:
case RawFunction::kGetterFunction:
case RawFunction::kSetterFunction:
case RawFunction::kConstructor: {
const Tag tag = builder_->PeekTag();
builder_->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.GetCanonicalType(klass);
LocalVariable* variable =
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::This(), klass_type);
scope_->InsertParameterAt(pos++, variable);
result_->this_variable = 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()) {
TypedData& kernel_data =
TypedData::Handle(Z, class_field.KernelData());
ASSERT(!kernel_data.IsNull());
intptr_t field_offset = class_field.kernel_offset();
AlternativeReadingScope alt(&builder_->reader_, &kernel_data,
field_offset);
FieldHelper field_helper(builder_);
field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
Tag initializer_tag =
builder_->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 = kTypeCheckAllParameters;
if (function.IsNonImplicitClosureFunction()) {
type_check_mode = kTypeCheckAllParameters;
} else if (function.IsImplicitClosureFunction()) {
if (!attrs.has_dynamic_invocations) {
// This is a tear-off of an instance method that can not be reached
// from any dynamic invocation. The method would not check any
// parameters except covariant ones and those annotated with
// generic-covariant-impl. Which means that we have to check
// the rest in the tear-off itself..
type_check_mode = kTypeCheckForTearOffOfNonDynamicallyInvokedMethod;
}
} else {
if (function.is_static()) {
// In static functions we don't check anything.
type_check_mode = kTypeCheckForStaticFunction;
} else if (!attrs.has_dynamic_invocations) {
// If the current function is never a target of a dynamic invocation
// and this parameter is not marked with generic-covariant-impl
// (which means that among all super-interfaces no type parameters
// ever occur at the position of this parameter) then we don't need
// to check this parameter on the callee side, because strong mode
// guarantees that it was checked at the caller side.
type_check_mode = kTypeCheckForNonDynamicallyInvokedMethod;
}
}
// 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()) {
builder_->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 RawFunction::kImplicitGetter:
case RawFunction::kImplicitStaticFinalGetter:
case RawFunction::kImplicitSetter: {
ASSERT(builder_->PeekTag() == kField);
if (IsFieldInitializer(function, Z)) {
VisitNode();
break;
}
const bool is_setter = function.IsImplicitSetterFunction();
const bool is_method = !function.IsStaticFunction();
intptr_t pos = 0;
if (is_method) {
Class& klass = Class::Handle(Z, function.Owner());
Type& klass_type = H.GetCanonicalType(klass);
LocalVariable* variable =
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::This(), klass_type);
scope_->InsertParameterAt(pos++, variable);
result_->this_variable = variable;
}
if (is_setter) {
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 && !attrs.has_dynamic_invocations) {
FieldHelper field_helper(builder_);
field_helper.ReadUntilIncluding(FieldHelper::kFlags);
if (!field_helper.IsCovariant() &&
(!field_helper.IsGenericCovariantImpl() ||
(!attrs.has_non_this_uses && !attrs.has_tearoff_uses))) {
result_->setter_value->set_type_check_mode(
LocalVariable::kTypeCheckedByCaller);
}
}
}
break;
}
case RawFunction::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.GetCanonicalType(klass);
LocalVariable* variable =
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::This(), klass_type);
scope_->InsertParameterAt(0, variable);
result_->this_variable = variable;
break;
}
case RawFunction::kNoSuchMethodDispatcher:
case RawFunction::kInvokeFieldDispatcher:
for (intptr_t i = 0; i < function.NumParameters(); ++i) {
LocalVariable* variable =
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
String::ZoneHandle(Z, function.ParameterNameAt(i)),
AbstractType::dynamic_type());
scope_->InsertParameterAt(i, variable);
}
break;
case RawFunction::kSignatureFunction:
case RawFunction::kIrregexpFunction:
UNREACHABLE();
}
if (needs_expr_temp_ || function.is_no_such_method_forwarder()) {
scope_->AddVariable(parsed_function_->EnsureExpressionTemp());
}
parsed_function_->AllocateVariables();
return result_;
}
void StreamingScopeBuilder::ReportUnexpectedTag(const char* variant, Tag tag) {
H.ReportError(builder_->script(), TokenPosition::kNoSource,
"Unexpected tag %d (%s) in %s, expected %s", tag,
Reader::TagName(tag),
parsed_function_->function().ToQualifiedCString(), variant);
}
void StreamingScopeBuilder::VisitNode() {
Tag tag = builder_->PeekTag();
switch (tag) {
case kConstructor:
VisitConstructor();
return;
case kProcedure:
VisitProcedure();
return;
case kField:
VisitField();
return;
case kFunctionNode:
VisitFunctionNode();
return;
default:
UNIMPLEMENTED();
return;
}
}
void StreamingScopeBuilder::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(builder_);
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()) {
TypedData& kernel_data = TypedData::Handle(Z, class_field.KernelData());
ASSERT(!kernel_data.IsNull());
intptr_t field_offset = class_field.kernel_offset();
AlternativeReadingScope alt(&builder_->reader_, &kernel_data,
field_offset);
FieldHelper field_helper(builder_);
field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
Tag initializer_tag = builder_->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 =
builder_->ReadListLength(); // read initializers list length.
for (intptr_t i = 0; i < list_length; i++) {
VisitInitializer();
}
}
void StreamingScopeBuilder::VisitProcedure() {
ProcedureHelper procedure_helper(builder_);
procedure_helper.ReadUntilExcluding(ProcedureHelper::kFunction);
if (builder_->ReadTag() == kSomething) {
VisitFunctionNode();
}
}
void StreamingScopeBuilder::VisitField() {
FieldHelper field_helper(builder_);
field_helper.ReadUntilExcluding(FieldHelper::kType);
VisitDartType(); // read type.
Tag tag = builder_->ReadTag(); // read initializer (part 1).
if (tag == kSomething) {
VisitExpression(); // read initializer (part 2).
}
}
void StreamingScopeBuilder::VisitFunctionNode() {
FunctionNodeHelper function_node_helper(builder_);
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
intptr_t list_length =
builder_->ReadListLength(); // read type_parameters list length.
for (intptr_t i = 0; i < list_length; ++i) {
TypeParameterHelper helper(builder_);
helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound);
VisitDartType(); // read ith bound.
helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kDefaultType);
if (builder_->ReadTag() == kSomething) {
VisitDartType(); // read ith default type.
}
helper.Finish();
}
function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
if (FLAG_causal_async_stacks &&
(function_node_helper.dart_async_marker_ == FunctionNodeHelper::kAsync ||
function_node_helper.dart_async_marker_ ==
FunctionNodeHelper::kAsyncStar)) {
LocalVariable* asyncStackTraceVar = MakeVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::AsyncStackTraceVar(), AbstractType::dynamic_type());
scope_->AddVariable(asyncStackTraceVar);
}
if (function_node_helper.async_marker_ == FunctionNodeHelper::kSyncYielding) {
LocalScope* scope = parsed_function_->node_sequence()->scope();
intptr_t offset = parsed_function_->function().num_fixed_parameters();
for (intptr_t i = 0;
i < parsed_function_->function().NumOptionalPositionalParameters();
i++) {
scope->VariableAt(offset + i)->set_is_forced_stack();
}
}
// 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 (builder_->ReadTag() == kSomething) {
PositionScope scope(&builder_->reader_);
VisitStatement(); // Read body
first_body_token_position_ = builder_->reader_.min_position();
}
// Ensure that :await_jump_var, :await_ctx_var, :async_op,
// :async_completer and :async_stack_trace are captured.
if (function_node_helper.async_marker_ == FunctionNodeHelper::kSyncYielding) {
{
LocalVariable* temp = NULL;
LookupCapturedVariableByName(
(depth_.function_ == 0) ? &result_->yield_jump_variable : &temp,
Symbols::AwaitJumpVar());
}
{
LocalVariable* temp = NULL;
LookupCapturedVariableByName(
(depth_.function_ == 0) ? &result_->yield_context_variable : &temp,
Symbols::AwaitContextVar());
}
{
LocalVariable* temp =
scope_->LookupVariable(Symbols::AsyncOperation(), true);
if (temp != NULL) {
scope_->CaptureVariable(temp);
}
}
{
LocalVariable* temp =
scope_->LookupVariable(Symbols::AsyncCompleter(), true);
if (temp != NULL) {
scope_->CaptureVariable(temp);
}
}
if (FLAG_causal_async_stacks) {
LocalVariable* temp =
scope_->LookupVariable(Symbols::AsyncStackTraceVar(), true);
if (temp != NULL) {
scope_->CaptureVariable(temp);
}
}
}
}
void StreamingScopeBuilder::VisitInitializer() {
Tag tag = builder_->ReadTag();
builder_->ReadByte(); // read isSynthetic flag.
switch (tag) {
case kInvalidInitializer:
return;
case kFieldInitializer:
builder_->SkipCanonicalNameReference(); // read field_reference.
VisitExpression(); // read value.
return;
case kSuperInitializer:
builder_->SkipCanonicalNameReference(); // read target_reference.
VisitArguments(); // read arguments.
return;
case kRedirectingInitializer:
builder_->SkipCanonicalNameReference(); // read target_reference.
VisitArguments(); // read arguments.
return;
case kLocalInitializer:
VisitVariableDeclaration(); // read variable.
return;
case kAssertInitializer:
VisitStatement();
return;
default:
ReportUnexpectedTag("initializer", tag);
UNREACHABLE();
}
}
void StreamingScopeBuilder::VisitExpression() {
uint8_t payload = 0;
Tag tag = builder_->ReadTag(&payload);
switch (tag) {
case kInvalidExpression:
builder_->ReadPosition();
builder_->SkipStringReference();
return;
case kVariableGet: {
builder_->ReadPosition(); // read position.
intptr_t variable_kernel_offset =
builder_->ReadUInt(); // read kernel position.
builder_->ReadUInt(); // read relative variable index.
builder_->SkipOptionalDartType(); // read promoted type.
LookupVariable(variable_kernel_offset);
return;
}
case kSpecializedVariableGet: {
builder_->ReadPosition(); // read position.
intptr_t variable_kernel_offset =
builder_->ReadUInt(); // read kernel position.
LookupVariable(variable_kernel_offset);
return;
}
case kVariableSet: {
builder_->ReadPosition(); // read position.
intptr_t variable_kernel_offset =
builder_->ReadUInt(); // read kernel position.
builder_->ReadUInt(); // read relative variable index.
LookupVariable(variable_kernel_offset);
VisitExpression(); // read expression.
return;
}
case kSpecializedVariableSet: {
builder_->ReadPosition(); // read position.
intptr_t variable_kernel_offset =
builder_->ReadUInt(); // read kernel position.
LookupVariable(variable_kernel_offset);
VisitExpression(); // read expression.
return;
}
case kPropertyGet:
builder_->ReadPosition(); // read position.
VisitExpression(); // read receiver.
builder_->SkipName(); // read name.
// read interface_target_reference.
builder_->SkipCanonicalNameReference();
return;
case kPropertySet:
builder_->ReadPosition(); // read position.
VisitExpression(); // read receiver.
builder_->SkipName(); // read name.
VisitExpression(); // read value.
// read interface_target_reference.
builder_->SkipCanonicalNameReference();
return;
case kDirectPropertyGet:
builder_->ReadPosition(); // read position.
VisitExpression(); // read receiver.
builder_->SkipCanonicalNameReference(); // read target_reference.
return;
case kDirectPropertySet:
builder_->ReadPosition(); // read position.
VisitExpression(); // read receiver.
builder_->SkipCanonicalNameReference(); // read target_reference.
VisitExpression(); // read value·
return;
case kSuperPropertyGet:
HandleSpecialLoad(&result_->this_variable, Symbols::This());
builder_->ReadPosition(); // read position.
builder_->SkipName(); // read name.
builder_->SkipCanonicalNameReference(); // read target_reference.
return;
case kSuperPropertySet:
HandleSpecialLoad(&result_->this_variable, Symbols::This());
builder_->ReadPosition(); // read position.
builder_->SkipName(); // read name.
VisitExpression(); // read value.
builder_->SkipCanonicalNameReference(); // read target_reference.
return;
case kStaticGet:
builder_->ReadPosition(); // read position.
builder_->SkipCanonicalNameReference(); // read target_reference.
return;
case kStaticSet:
builder_->ReadPosition(); // read position.
builder_->SkipCanonicalNameReference(); // read target_reference.
VisitExpression(); // read expression.
return;
case kMethodInvocation:
builder_->ReadPosition(); // read position.
VisitExpression(); // read receiver.
builder_->SkipName(); // read name.
VisitArguments(); // read arguments.
// read interface_target_reference.
builder_->SkipCanonicalNameReference();
return;
case kDirectMethodInvocation:
builder_->ReadPosition(); // read position.
VisitExpression(); // read receiver.
builder_->SkipCanonicalNameReference(); // read target_reference.
VisitArguments(); // read arguments.
return;
case kSuperMethodInvocation:
HandleSpecialLoad(&result_->this_variable, Symbols::This());
builder_->ReadPosition(); // read position.
builder_->SkipName(); // read name.
VisitArguments(); // read arguments.
// read interface_target_reference.
builder_->SkipCanonicalNameReference();
return;
case kStaticInvocation:
case kConstStaticInvocation:
builder_->ReadPosition(); // read position.
builder_->SkipCanonicalNameReference(); // read procedure_reference.
VisitArguments(); // read arguments.
return;
case kConstructorInvocation:
case kConstConstructorInvocation:
builder_->ReadPosition(); // read position.
builder_->SkipCanonicalNameReference(); // read target_reference.
VisitArguments(); // read arguments.
return;
case kNot:
VisitExpression(); // read expression.
return;
case kLogicalExpression:
needs_expr_temp_ = true;
VisitExpression(); // read left.
builder_->SkipBytes(1); // read operator.
VisitExpression(); // read right.
return;
case kConditionalExpression: {
needs_expr_temp_ = true;
VisitExpression(); // read condition.
VisitExpression(); // read then.
VisitExpression(); // read otherwise.
builder_->SkipOptionalDartType(); // read unused static type.
return;
}
case kStringConcatenation: {
builder_->ReadPosition(); // read position.
intptr_t list_length = builder_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
VisitExpression(); // read ith expression.
}
return;
}
case kIsExpression:
builder_->ReadPosition(); // read position.
VisitExpression(); // read operand.
VisitDartType(); // read type.
return;
case kAsExpression:
builder_->ReadPosition(); // read position.
builder_->ReadFlags(); // read flags.
VisitExpression(); // read operand.
VisitDartType(); // read type.
return;
case kSymbolLiteral:
builder_->SkipStringReference(); // read index into string table.
return;
case kTypeLiteral:
VisitDartType(); // read type.
return;
case kThisExpression:
HandleSpecialLoad(&result_->this_variable, Symbols::This());
return;
case kRethrow:
builder_->ReadPosition(); // read position.
return;
case kThrow:
builder_->ReadPosition(); // read position.
VisitExpression(); // read expression.
return;
case kListLiteral:
case kConstListLiteral: {
builder_->ReadPosition(); // read position.
VisitDartType(); // read type.
intptr_t list_length = builder_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
VisitExpression(); // read ith expression.
}
return;
}
case kMapLiteral:
case kConstMapLiteral: {
builder_->ReadPosition(); // read position.
VisitDartType(); // read key type.
VisitDartType(); // read value type.
intptr_t list_length = builder_->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 =
builder_->ReaderOffset() - 1; // -1 to include tag byte.
builder_->ReadPosition(); // read position.
HandleLocalFunction(offset); // read function node.
return;
}
case kLet: {
PositionScope scope(&builder_->reader_);
intptr_t offset =
builder_->ReaderOffset() - 1; // -1 to include tag byte.
EnterScope(offset);
VisitVariableDeclaration(); // read variable declaration.
VisitExpression(); // read expression.
ExitScope(builder_->reader_.min_position(),
builder_->reader_.max_position());
return;
}
case kBigIntLiteral:
builder_->SkipStringReference(); // read string reference.
return;
case kStringLiteral:
builder_->SkipStringReference(); // read string reference.
return;
case kSpecializedIntLiteral:
return;
case kNegativeIntLiteral:
builder_->ReadUInt(); // read value.
return;
case kPositiveIntLiteral:
builder_->ReadUInt(); // read value.
return;
case kDoubleLiteral:
builder_->ReadDouble(); // read value.
return;
case kTrueLiteral:
return;
case kFalseLiteral:
return;
case kNullLiteral:
return;
case kConstantExpression: {
builder_->SkipConstantReference();
return;
}
case kInstantiation: {
VisitExpression();
const intptr_t list_length =
builder_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
VisitDartType(); // read ith type.
}
return;
}
case kLoadLibrary:
case kCheckLibraryIsLoaded:
builder_->ReadUInt(); // library index
break;
default:
ReportUnexpectedTag("expression", tag);
UNREACHABLE();
}
}
void StreamingScopeBuilder::VisitStatement() {
Tag tag = builder_->ReadTag(); // read tag.
switch (tag) {
case kExpressionStatement:
VisitExpression(); // read expression.
return;
case kBlock: {
PositionScope scope(&builder_->reader_);
intptr_t offset =
builder_->ReaderOffset() - 1; // -1 to include tag byte.
EnterScope(offset);
intptr_t list_length =
builder_->ReadListLength(); // read number of statements.
for (intptr_t i = 0; i < list_length; ++i) {
VisitStatement(); // read ith statement.
}
ExitScope(builder_->reader_.min_position(),
builder_->reader_.max_position());
return;
}
case kEmptyStatement:
return;
case kAssertBlock:
if (I->asserts()) {
PositionScope scope(&builder_->reader_);
intptr_t offset =
builder_->ReaderOffset() - 1; // -1 to include tag byte.
EnterScope(offset);
intptr_t list_length =
builder_->ReadListLength(); // read number of statements.
for (intptr_t i = 0; i < list_length; ++i) {
VisitStatement(); // read ith statement.
}
ExitScope(builder_->reader_.min_position(),
builder_->reader_.max_position());
} else {
builder_->SkipStatementList();
}
return;
case kAssertStatement:
if (I->asserts()) {
VisitExpression(); // Read condition.
builder_->ReadPosition(); // read condition start offset.
builder_->ReadPosition(); // read condition end offset.
Tag tag = builder_->ReadTag(); // read (first part of) message.
if (tag == kSomething) {
VisitExpression(); // read (rest of) message.
}
} else {
builder_->SkipExpression(); // Read condition.
builder_->ReadPosition(); // read condition start offset.
builder_->ReadPosition(); // read condition end offset.
Tag tag = builder_->ReadTag(); // read (first part of) message.
if (tag == kSomething) {
builder_->SkipExpression(); // read (rest of) message.
}
}
return;
case kLabeledStatement:
VisitStatement(); // read body.
return;
case kBreakStatement:
builder_->ReadPosition(); // read position.
builder_->ReadUInt(); // read target_index.
return;
case kWhileStatement:
++depth_.loop_;
builder_->ReadPosition(); // read position.
VisitExpression(); // read condition.
VisitStatement(); // read body.
--depth_.loop_;
return;
case kDoStatement:
++depth_.loop_;
builder_->ReadPosition(); // read position.
VisitStatement(); // read body.
VisitExpression(); // read condition.
--depth_.loop_;
return;
case kForStatement: {
PositionScope scope(&builder_->reader_);
intptr_t offset =
builder_->ReaderOffset() - 1; // -1 to include tag byte.
++depth_.loop_;
EnterScope(offset);
TokenPosition position = builder_->ReadPosition(); // read position.
intptr_t list_length =
builder_->ReadListLength(); // read number of variables.
for (intptr_t i = 0; i < list_length; ++i) {
VisitVariableDeclaration(); // read ith variable.
}
Tag tag = builder_->ReadTag(); // Read first part of condition.
if (tag == kSomething) {
VisitExpression(); // read rest of condition.
}
list_length = builder_->ReadListLength(); // read number of updates.
for (intptr_t i = 0; i < list_length; ++i) {
VisitExpression(); // read ith update.
}
VisitStatement(); // read body.
ExitScope(position, builder_->reader_.max_position());
--depth_.loop_;
return;
}
case kForInStatement:
case kAsyncForInStatement: {
PositionScope scope(&builder_->reader_);
intptr_t start_offset =
builder_->ReaderOffset() - 1; // -1 to include tag byte.
builder_->ReadPosition(); // read position.
TokenPosition body_position =
builder_->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 = builder_->ReaderOffset();
builder_->SkipVariableDeclaration(); // read variable.
VisitExpression(); // read iterable.
++depth_.for_in_;
AddIteratorVariable();
++depth_.loop_;
EnterScope(start_offset);
{
AlternativeReadingScope alt(&builder_->reader_, offset);
VisitVariableDeclaration(); // read variable.
}
VisitStatement(); // read body.
if (!body_position.IsReal()) {
body_position = builder_->reader_.min_position();
}
// TODO(jensj): From kernel_binary.cc
// forinstmt->variable_->set_end_position(forinstmt->position_);
ExitScope(body_position, builder_->reader_.max_position());
--depth_.loop_;
--depth_.for_in_;
return;
}
case kSwitchStatement: {
AddSwitchVariable();
builder_->ReadPosition(); // read position.
VisitExpression(); // read condition.
int case_count = builder_->ReadListLength(); // read number of cases.
for (intptr_t i = 0; i < case_count; ++i) {
int expression_count =
builder_->ReadListLength(); // read number of expressions.
for (intptr_t j = 0; j < expression_count; ++j) {
builder_->ReadPosition(); // read jth position.
VisitExpression(); // read jth expression.
}
builder_->ReadBool(); // read is_default.
VisitStatement(); // read body.
}
return;
}
case kContinueSwitchStatement:
builder_->ReadPosition(); // read position.
builder_->ReadUInt(); // read target_index.
return;
case kIfStatement:
builder_->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;
}
builder_->ReadPosition(); // read position
Tag tag = builder_->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();
builder_->ReadByte(); // read flags
intptr_t catch_count =
builder_->ReadListLength(); // read number of catches.
for (intptr_t i = 0; i < catch_count; ++i) {
PositionScope scope(&builder_->reader_);
intptr_t offset = builder_->ReaderOffset(); // Catch has no tag.
EnterScope(offset);
builder_->ReadPosition(); // read position.
VisitDartType(); // Read the guard.
tag = builder_->ReadTag(); // read first part of exception.
if (tag == kSomething) {
VisitVariableDeclaration(); // read exception.
}
tag = builder_->ReadTag(); // read first part of stack trace.
if (tag == kSomething) {
VisitVariableDeclaration(); // read stack trace.
}
VisitStatement(); // read body.
ExitScope(builder_->reader_.min_position(),
builder_->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: {
builder_->ReadPosition(); // read position.
word flags = builder_->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 =
builder_->ReaderOffset() - 1; // -1 to include tag byte.
builder_->ReadPosition(); // read position.
VisitVariableDeclaration(); // read variable declaration.
HandleLocalFunction(offset); // read function node.
return;
}
default:
ReportUnexpectedTag("declaration", tag);
UNREACHABLE();
}
}
void StreamingScopeBuilder::VisitArguments() {
builder_->ReadUInt(); // read argument_count.
// Types
intptr_t list_length = builder_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
VisitDartType(); // read ith type.
}
// Positional.
list_length = builder_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
VisitExpression(); // read ith positional.
}
// Named.
list_length = builder_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
builder_->SkipStringReference(); // read ith name index.
VisitExpression(); // read ith expression.
}
}
void StreamingScopeBuilder::VisitVariableDeclaration() {
PositionScope scope(&builder_->reader_);
intptr_t kernel_offset_no_tag = builder_->ReaderOffset();
VariableDeclarationHelper helper(builder_);
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_);
Tag tag = builder_->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 = builder_->reader_.max_position();
if (end_position.IsReal()) {
end_position.Next();
}
LocalVariable* variable =
MakeVariable(helper.position_, end_position, name, type);
if (helper.IsFinal()) {
variable->set_is_final();
}
scope_->AddVariable(variable);
result_->locals.Insert(builder_->data_program_offset_ + kernel_offset_no_tag,
variable);
}
AbstractType& StreamingScopeBuilder::BuildAndVisitVariableType() {
const intptr_t offset = builder_->ReaderOffset();
AbstractType& type = T.BuildVariableType();
builder_->SetOffset(offset); // rewind
VisitDartType();
return type;
}
void StreamingScopeBuilder::VisitDartType() {
Tag tag = builder_->ReadTag();
switch (tag) {
case kInvalidType:
case kDynamicType:
case kVoidType:
case kBottomType:
// those contain nothing.
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 StreamingScopeBuilder::VisitInterfaceType(bool simple) {
builder_->ReadUInt(); // read klass_name.
if (!simple) {
intptr_t length = builder_->ReadListLength(); // read number of types.
for (intptr_t i = 0; i < length; ++i) {
VisitDartType(); // read the ith type.
}
}
}
void StreamingScopeBuilder::VisitFunctionType(bool simple) {
if (!simple) {
intptr_t list_length =
builder_->ReadListLength(); // read type_parameters list length.
for (int i = 0; i < list_length; ++i) {
TypeParameterHelper helper(builder_);
helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound);
VisitDartType(); // read bound.
helper.ReadUntilExcludingAndSetJustRead(
TypeParameterHelper::kDefaultType);
if (builder_->ReadTag() == kSomething) {
VisitDartType(); // read default type.
}
helper.Finish();
}
builder_->ReadUInt(); // read required parameter count.
builder_->ReadUInt(); // read total parameter count.
}
const intptr_t positional_count =
builder_->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 =
builder_->ReadListLength(); // read named_parameters list length.
for (intptr_t i = 0; i < named_count; ++i) {
// read string reference (i.e. named_parameters[i].name).
builder_->SkipStringReference();
VisitDartType(); // read named_parameters[i].type.
}
}
builder_->SkipListOfStrings(); // read positional parameter names.
if (!simple) {
builder_->SkipCanonicalNameReference(); // read typedef reference.
}
VisitDartType(); // read return type.
}
void StreamingScopeBuilder::VisitTypeParameterType() {
Function& function = Function::Handle(Z, parsed_function_->function().raw());
while (function.IsClosureFunction()) {
function = function.parent_function();
}
// 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 = builder_->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()) {
HandleSpecialLoad(&result_->this_variable, Symbols::This());
}
}
builder_->SkipOptionalDartType(); // read bound bound.
}
void StreamingScopeBuilder::HandleLocalFunction(intptr_t parent_kernel_offset) {
// "Peek" ahead into the function node
intptr_t offset = builder_->ReaderOffset();
FunctionNodeHelper function_node_helper(builder_);
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 _(&builder_->reader_);
num_type_params = builder_->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.
StreamingDartTypeTranslator::TypeParameterScope scope(&type_translator_,
num_type_params);
// read positional_parameters and named_parameters.
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kPositionalParameters);
ProcedureAttributesMetadata default_attrs;
AddPositionalAndNamedParameters(0, kTypeCheckAllParameters, default_attrs);
// "Peek" is now done.
builder_->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 StreamingScopeBuilder::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 StreamingScopeBuilder::ExitScope(TokenPosition start_position,
TokenPosition end_position