7bf835b9d5
Summary:
Previously, we use the "Vector" type in the kernel tree for the result of the
"VectorCreation" operation as as the parameter type for converted closure
functions. In the VM, we use the "Context" type instead, which the VM treats a
little differently than normal Dart-visible types, and it doesn't not handle
type checks against it correctly, breaking all closure converted code running in
checked mode.
Now, Since we are forced to use dynamic to represent the types of elements of
the context, we may as well just use dynamic for the context type itself.
Previously, we did not correct handle converted closure type checks for closures
that capture type parameters. The way we handled these type checks also had
several latent bugs that prevented type parameters being handled properly.
Now, we handle type parameters for converted closures similarly to normal
closures, and the places we treat them specially are fewer and more integrated
with the rest of the closure type checking code.
There is still a problem where assignments to captured variables are not
checked, because they are transformed to assignments into the context, whose
elements are necessarily untyped. This breaks many co19 tests, which expect type
errors on these assignments. The example below should error in checked mode, but
after closure conversion is runs with no errors.
int b;
bool c;
(() { b = c; })()
Test Plan:
- All test cases in "pkg/kernel/testcases/closures" now run in checked mode.
- Added a test "closures_types.dart" to check that captured type parameters are
handled correctly in the converted closures' signature types.
R=dmitryas@google.com
Review-Url: https://codereview.chromium.org/3007623002 .
7838 lines
280 KiB
C++
7838 lines
280 KiB
C++
// 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/kernel_binary_flowgraph.h"
|
|
#include "vm/compiler.h"
|
|
#include "vm/longjump.h"
|
|
#include "vm/object_store.h"
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
namespace dart {
|
|
namespace kernel {
|
|
|
|
#define Z (zone_)
|
|
#define H (translation_helper_)
|
|
#define T (type_translator_)
|
|
#define I Isolate::Current()
|
|
|
|
static bool IsStaticInitializer(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 = builder_->ReadTag(); // read tag.
|
|
ASSERT(tag == kFunctionNode);
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kPosition:
|
|
position_ = builder_->ReadPosition(); // read position.
|
|
if (++next_read_ == field) return;
|
|
case kEndPosition:
|
|
end_position_ = builder_->ReadPosition(); // read end position.
|
|
if (++next_read_ == field) return;
|
|
case kAsyncMarker:
|
|
async_marker_ = static_cast<AsyncMarker>(builder_->ReadByte());
|
|
if (++next_read_ == field) return;
|
|
case kDartAsyncMarker:
|
|
dart_async_marker_ = static_cast<AsyncMarker>(
|
|
builder_->ReadByte()); // read dart async marker.
|
|
if (++next_read_ == field) return;
|
|
case kTypeParameters:
|
|
builder_->SkipTypeParametersList(); // read type parameters.
|
|
if (++next_read_ == field) return;
|
|
case kTotalParameterCount:
|
|
total_parameter_count_ =
|
|
builder_->ReadUInt(); // read total parameter count.
|
|
if (++next_read_ == field) return;
|
|
case kRequiredParameterCount:
|
|
required_parameter_count_ =
|
|
builder_->ReadUInt(); // read required parameter count.
|
|
if (++next_read_ == field) return;
|
|
case kPositionalParameters:
|
|
builder_->SkipListOfVariableDeclarations(); // read positionals.
|
|
if (++next_read_ == field) return;
|
|
case kNamedParameters:
|
|
builder_->SkipListOfVariableDeclarations(); // read named.
|
|
if (++next_read_ == field) return;
|
|
case kReturnType:
|
|
builder_->SkipDartType(); // read return type.
|
|
if (++next_read_ == field) return;
|
|
case kBody:
|
|
if (builder_->ReadTag() == kSomething)
|
|
builder_->SkipStatement(); // read body.
|
|
if (++next_read_ == field) return;
|
|
case kEnd:
|
|
return;
|
|
}
|
|
}
|
|
|
|
void VariableDeclarationHelper::ReadUntilExcluding(Field field) {
|
|
if (field <= next_read_) return;
|
|
|
|
// Ordered with fall-through.
|
|
switch (next_read_) {
|
|
case kPosition:
|
|
position_ = builder_->ReadPosition(); // read position.
|
|
if (++next_read_ == field) return;
|
|
case kEqualPosition:
|
|
equals_position_ = builder_->ReadPosition(); // read equals position.
|
|
if (++next_read_ == field) return;
|
|
case kFlags:
|
|
flags_ = builder_->ReadFlags();
|
|
if (++next_read_ == field) return;
|
|
case kNameIndex:
|
|
name_index_ = builder_->ReadStringReference(); // read name index.
|
|
if (++next_read_ == field) return;
|
|
case kType:
|
|
builder_->SkipDartType(); // read type.
|
|
if (++next_read_ == field) return;
|
|
case kInitializer:
|
|
if (builder_->ReadTag() == kSomething)
|
|
builder_->SkipExpression(); // read initializer.
|
|
if (++next_read_ == field) return;
|
|
case kEnd:
|
|
return;
|
|
}
|
|
}
|
|
|
|
FieldHelper::FieldHelper(StreamingFlowGraphBuilder* builder, intptr_t offset)
|
|
: builder_(builder),
|
|
next_read_(kStart),
|
|
has_function_literal_initializer_(false) {
|
|
builder_->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 = builder_->ReadTag(); // read tag.
|
|
ASSERT(tag == kField);
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kCanonicalName:
|
|
canonical_name_ =
|
|
builder_->ReadCanonicalNameReference(); // read canonical_name.
|
|
if (++next_read_ == field) return;
|
|
case kPosition:
|
|
position_ = builder_->ReadPosition(false); // read position.
|
|
if (++next_read_ == field) return;
|
|
case kEndPosition:
|
|
end_position_ = builder_->ReadPosition(false); // read end position.
|
|
if (++next_read_ == field) return;
|
|
case kFlags:
|
|
flags_ = builder_->ReadFlags();
|
|
if (++next_read_ == field) return;
|
|
case kName:
|
|
builder_->SkipName(); // read name.
|
|
if (++next_read_ == field) return;
|
|
case kSourceUriIndex:
|
|
source_uri_index_ = builder_->ReadUInt(); // read source_uri_index.
|
|
builder_->current_script_id_ = source_uri_index_;
|
|
builder_->record_token_position(position_);
|
|
builder_->record_token_position(end_position_);
|
|
if (++next_read_ == field) return;
|
|
case kDocumentationCommentIndex:
|
|
builder_->ReadStringReference();
|
|
if (++next_read_ == field) return;
|
|
case kAnnotations: {
|
|
annotation_count_ = builder_->ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < annotation_count_; ++i) {
|
|
builder_->SkipExpression(); // read ith expression.
|
|
}
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kType:
|
|
builder_->SkipDartType(); // read type.
|
|
if (++next_read_ == field) return;
|
|
case kInitializer:
|
|
if (builder_->ReadTag() == kSomething) {
|
|
if (detect_function_literal_initializer &&
|
|
builder_->PeekTag() == kFunctionExpression) {
|
|
AlternativeReadingScope alt(builder_->reader_);
|
|
Tag tag = builder_->ReadTag();
|
|
ASSERT(tag == kFunctionExpression);
|
|
builder_->ReadPosition(); // read position.
|
|
|
|
FunctionNodeHelper helper(builder_);
|
|
helper.ReadUntilIncluding(FunctionNodeHelper::kEndPosition);
|
|
|
|
has_function_literal_initializer_ = true;
|
|
function_literal_start_ = helper.position_;
|
|
function_literal_end_ = helper.end_position_;
|
|
}
|
|
builder_->SkipExpression(); // read initializer.
|
|
}
|
|
if (++next_read_ == field) return;
|
|
case kEnd:
|
|
return;
|
|
}
|
|
}
|
|
|
|
void ProcedureHelper::ReadUntilExcluding(Field field) {
|
|
if (field <= next_read_) return;
|
|
|
|
// Ordered with fall-through.
|
|
switch (next_read_) {
|
|
case kStart: {
|
|
Tag tag = builder_->ReadTag(); // read tag.
|
|
ASSERT(tag == kProcedure);
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kCanonicalName:
|
|
canonical_name_ =
|
|
builder_->ReadCanonicalNameReference(); // read canonical_name.
|
|
if (++next_read_ == field) return;
|
|
case kPosition:
|
|
position_ = builder_->ReadPosition(false); // read position.
|
|
if (++next_read_ == field) return;
|
|
case kEndPosition:
|
|
end_position_ = builder_->ReadPosition(false); // read end position.
|
|
if (++next_read_ == field) return;
|
|
case kKind:
|
|
kind_ = static_cast<Kind>(builder_->ReadByte());
|
|
if (++next_read_ == field) return;
|
|
case kFlags:
|
|
flags_ = builder_->ReadFlags();
|
|
if (++next_read_ == field) return;
|
|
case kName:
|
|
builder_->SkipName(); // read name.
|
|
if (++next_read_ == field) return;
|
|
case kSourceUriIndex:
|
|
source_uri_index_ = builder_->ReadUInt(); // read source_uri_index.
|
|
builder_->current_script_id_ = source_uri_index_;
|
|
builder_->record_token_position(position_);
|
|
builder_->record_token_position(end_position_);
|
|
if (++next_read_ == field) return;
|
|
case kDocumentationCommentIndex:
|
|
builder_->ReadStringReference();
|
|
if (++next_read_ == field) return;
|
|
case kAnnotations: {
|
|
annotation_count_ = builder_->ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < annotation_count_; ++i) {
|
|
builder_->SkipExpression(); // read ith expression.
|
|
}
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kFunction:
|
|
if (builder_->ReadTag() == kSomething)
|
|
builder_->SkipFunctionNode(); // read function node.
|
|
if (++next_read_ == field) return;
|
|
case kEnd:
|
|
return;
|
|
}
|
|
}
|
|
|
|
void ConstructorHelper::ReadUntilExcluding(Field field) {
|
|
if (field <= next_read_) return;
|
|
|
|
// Ordered with fall-through.
|
|
switch (next_read_) {
|
|
case kStart: {
|
|
Tag tag = builder_->ReadTag(); // read tag.
|
|
ASSERT(tag == kConstructor);
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kCanonicalName:
|
|
canonical_name_ =
|
|
builder_->ReadCanonicalNameReference(); // read canonical_name.
|
|
if (++next_read_ == field) return;
|
|
case kPosition:
|
|
position_ = builder_->ReadPosition(); // read position.
|
|
if (++next_read_ == field) return;
|
|
case kEndPosition:
|
|
end_position_ = builder_->ReadPosition(); // read end position.
|
|
if (++next_read_ == field) return;
|
|
case kFlags:
|
|
flags_ = builder_->ReadFlags();
|
|
if (++next_read_ == field) return;
|
|
case kName:
|
|
builder_->SkipName(); // read name.
|
|
if (++next_read_ == field) return;
|
|
case kDocumentationCommentIndex:
|
|
builder_->ReadStringReference();
|
|
if (++next_read_ == field) return;
|
|
case kAnnotations: {
|
|
annotation_count_ = builder_->ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < annotation_count_; ++i) {
|
|
builder_->SkipExpression(); // read ith expression.
|
|
}
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kFunction:
|
|
builder_->SkipFunctionNode(); // read function.
|
|
if (++next_read_ == field) return;
|
|
case kInitializers: {
|
|
intptr_t list_length =
|
|
builder_->ReadListLength(); // read initializers list length.
|
|
for (intptr_t i = 0; i < list_length; i++) {
|
|
Tag tag = builder_->ReadTag();
|
|
builder_->ReadByte(); // read isSynthetic.
|
|
switch (tag) {
|
|
case kInvalidInitializer:
|
|
continue;
|
|
case kFieldInitializer:
|
|
builder_->SkipCanonicalNameReference(); // read field_reference.
|
|
builder_->SkipExpression(); // read value.
|
|
continue;
|
|
case kSuperInitializer:
|
|
builder_->SkipCanonicalNameReference(); // read target_reference.
|
|
builder_->SkipArguments(); // read arguments.
|
|
continue;
|
|
case kRedirectingInitializer:
|
|
builder_->SkipCanonicalNameReference(); // read target_reference.
|
|
builder_->SkipArguments(); // read arguments.
|
|
continue;
|
|
case kLocalInitializer:
|
|
builder_->SkipVariableDeclaration(); // read variable.
|
|
continue;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kEnd:
|
|
return;
|
|
}
|
|
}
|
|
|
|
void ClassHelper::ReadUntilExcluding(Field field) {
|
|
if (field <= next_read_) return;
|
|
|
|
// Ordered with fall-through.
|
|
switch (next_read_) {
|
|
case kStart: {
|
|
Tag tag = builder_->ReadTag(); // read tag.
|
|
ASSERT(tag == kClass);
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kCanonicalName:
|
|
canonical_name_ =
|
|
builder_->ReadCanonicalNameReference(); // read canonical_name.
|
|
if (++next_read_ == field) return;
|
|
case kPosition:
|
|
position_ = builder_->ReadPosition(false); // read position.
|
|
if (++next_read_ == field) return;
|
|
case kEndPosition:
|
|
end_position_ = builder_->ReadPosition(); // read end position.
|
|
if (++next_read_ == field) return;
|
|
case kIsAbstract:
|
|
is_abstract_ = builder_->ReadBool(); // read is_abstract.
|
|
if (++next_read_ == field) return;
|
|
case kNameIndex:
|
|
name_index_ = builder_->ReadStringReference(); // read name index.
|
|
if (++next_read_ == field) return;
|
|
case kSourceUriIndex:
|
|
source_uri_index_ = builder_->ReadUInt(); // read source_uri_index.
|
|
builder_->current_script_id_ = source_uri_index_;
|
|
builder_->record_token_position(position_);
|
|
if (++next_read_ == field) return;
|
|
case kDocumentationCommentIndex:
|
|
builder_->ReadStringReference();
|
|
if (++next_read_ == field) return;
|
|
case kAnnotations: {
|
|
annotation_count_ = builder_->ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < annotation_count_; ++i) {
|
|
builder_->SkipExpression(); // read ith expression.
|
|
}
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kTypeParameters:
|
|
builder_->SkipTypeParametersList(); // read type parameters.
|
|
if (++next_read_ == field) return;
|
|
case kSuperClass: {
|
|
Tag type_tag = builder_->ReadTag(); // read super class type (part 1).
|
|
if (type_tag == kSomething) {
|
|
builder_->SkipDartType(); // read super class type (part 2).
|
|
}
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kMixinType: {
|
|
Tag type_tag = builder_->ReadTag(); // read mixin type (part 1).
|
|
if (type_tag == kSomething) {
|
|
builder_->SkipDartType(); // read mixin type (part 2).
|
|
}
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kImplementedClasses:
|
|
builder_->SkipListOfDartTypes(); // read implemented_classes.
|
|
if (++next_read_ == field) return;
|
|
case kFields: {
|
|
intptr_t list_length =
|
|
builder_->ReadListLength(); // read fields list length.
|
|
for (intptr_t i = 0; i < list_length; i++) {
|
|
FieldHelper field_helper(builder_);
|
|
field_helper.ReadUntilExcluding(FieldHelper::kEnd); // read field.
|
|
}
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kConstructors: {
|
|
intptr_t list_length =
|
|
builder_->ReadListLength(); // read constructors list length.
|
|
for (intptr_t i = 0; i < list_length; i++) {
|
|
ConstructorHelper constructor_helper(builder_);
|
|
constructor_helper.ReadUntilExcluding(
|
|
ConstructorHelper::kEnd); // read constructor.
|
|
}
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kProcedures: {
|
|
intptr_t list_length =
|
|
builder_->ReadListLength(); // read procedures list length.
|
|
for (intptr_t i = 0; i < list_length; i++) {
|
|
ProcedureHelper procedure_helper(builder_);
|
|
procedure_helper.ReadUntilExcluding(
|
|
ProcedureHelper::kEnd); // read procedure.
|
|
}
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kEnd:
|
|
return;
|
|
}
|
|
}
|
|
|
|
void LibraryHelper::ReadUntilExcluding(Field field) {
|
|
if (field <= next_read_) return;
|
|
|
|
// Ordered with fall-through.
|
|
switch (next_read_) {
|
|
case kFlags: {
|
|
flags_ = builder_->ReadFlags();
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kCanonicalName:
|
|
canonical_name_ =
|
|
builder_->ReadCanonicalNameReference(); // read canonical_name.
|
|
if (++next_read_ == field) return;
|
|
case kName:
|
|
name_index_ = builder_->ReadStringReference(); // read name index.
|
|
if (++next_read_ == field) return;
|
|
case kSourceUriIndex:
|
|
source_uri_index_ = builder_->ReadUInt(); // read source_uri_index.
|
|
builder_->current_script_id_ = source_uri_index_;
|
|
if (++next_read_ == field) return;
|
|
case kAnnotations:
|
|
builder_->SkipListOfExpressions(); // read annotations.
|
|
if (++next_read_ == field) return;
|
|
case kDependencies: {
|
|
intptr_t dependency_count = builder_->ReadUInt(); // read list length.
|
|
for (intptr_t i = 0; i < dependency_count; ++i) {
|
|
builder_->SkipLibraryDependency();
|
|
}
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kParts: {
|
|
intptr_t part_count = builder_->ReadUInt(); // read list length.
|
|
for (intptr_t i = 0; i < part_count; ++i) {
|
|
builder_->SkipLibraryPart();
|
|
}
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kTypedefs: {
|
|
intptr_t typedef_count = builder_->ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < typedef_count; i++) {
|
|
builder_->SkipLibraryTypedef();
|
|
}
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kClasses: {
|
|
int class_count = builder_->ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < class_count; ++i) {
|
|
ClassHelper class_helper(builder_);
|
|
class_helper.ReadUntilExcluding(ClassHelper::kEnd);
|
|
}
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kToplevelField: {
|
|
intptr_t field_count = builder_->ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < field_count; ++i) {
|
|
FieldHelper field_helper(builder_);
|
|
field_helper.ReadUntilExcluding(FieldHelper::kEnd);
|
|
}
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kToplevelProcedures: {
|
|
intptr_t procedure_count =
|
|
builder_->ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < procedure_count; ++i) {
|
|
ProcedureHelper procedure_helper(builder_);
|
|
procedure_helper.ReadUntilExcluding(ProcedureHelper::kEnd);
|
|
}
|
|
if (++next_read_ == field) return;
|
|
}
|
|
case kEnd:
|
|
return;
|
|
}
|
|
}
|
|
|
|
StreamingScopeBuilder::StreamingScopeBuilder(ParsedFunction* parsed_function,
|
|
intptr_t relative_kernel_offset,
|
|
const TypedData& data)
|
|
: result_(NULL),
|
|
parsed_function_(parsed_function),
|
|
relative_kernel_offset_(relative_kernel_offset),
|
|
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_,
|
|
zone_,
|
|
relative_kernel_offset,
|
|
data)),
|
|
type_translator_(builder_, /*finalize=*/true) {
|
|
Script& script = Script::Handle(Z, parsed_function->function().script());
|
|
H.SetStringOffsets(TypedData::Handle(Z, script.kernel_string_offsets()));
|
|
H.SetStringData(TypedData::Handle(Z, script.kernel_string_data()));
|
|
H.SetCanonicalNames(TypedData::Handle(Z, script.kernel_canonical_names()));
|
|
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_, parsed_function_->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);
|
|
|
|
LocalScope* enclosing_scope = NULL;
|
|
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 (FLAG_reify_generic_functions && function.IsGeneric()) {
|
|
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);
|
|
}
|
|
|
|
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(0);
|
|
|
|
FunctionNodeHelper function_node_helper(builder_);
|
|
|
|
switch (function.kind()) {
|
|
case RawFunction::kClosureFunction:
|
|
case RawFunction::kImplicitClosureFunction:
|
|
case RawFunction::kConvertedClosureFunction:
|
|
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* variable = MakeVariable(
|
|
TokenPosition::kNoSource, TokenPosition::kNoSource,
|
|
Symbols::ClosureParameter(), AbstractType::dynamic_type());
|
|
variable->set_is_forced_stack();
|
|
scope_->InsertParameterAt(pos++, variable);
|
|
} 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.kernel_data());
|
|
ASSERT(!kernel_data.IsNull());
|
|
AlternativeReadingScope alt(builder_->reader_, &kernel_data, 0);
|
|
intptr_t saved_relative_kernel_offset_ = relative_kernel_offset_;
|
|
relative_kernel_offset_ = class_field.kernel_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(class_field.kernel_offset());
|
|
VisitExpression(); // read initializer.
|
|
ExitScope(field_helper.position_, field_helper.end_position_);
|
|
}
|
|
relative_kernel_offset_ = saved_relative_kernel_offset_;
|
|
}
|
|
}
|
|
}
|
|
} 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;
|
|
}
|
|
|
|
// Continue reading FunctionNode:
|
|
// read positional_parameters and named_parameters.
|
|
AddPositionalAndNamedParameters(pos);
|
|
|
|
// 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(0);
|
|
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 (IsStaticInitializer(function, Z)) {
|
|
VisitNode();
|
|
break;
|
|
}
|
|
bool is_setter = function.IsImplicitSetterFunction();
|
|
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::dynamic_type());
|
|
scope_->InsertParameterAt(pos++, result_->setter_value);
|
|
}
|
|
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_) {
|
|
scope_->AddVariable(parsed_function_->EnsureExpressionTemp());
|
|
}
|
|
parsed_function_->AllocateVariables();
|
|
|
|
return result_;
|
|
}
|
|
|
|
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.kernel_data());
|
|
ASSERT(!kernel_data.IsNull());
|
|
AlternativeReadingScope alt(builder_->reader_, &kernel_data, 0);
|
|
intptr_t saved_relative_kernel_offset_ = relative_kernel_offset_;
|
|
relative_kernel_offset_ = class_field.kernel_offset();
|
|
FieldHelper field_helper(builder_);
|
|
field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
|
|
Tag initializer_tag = builder_->ReadTag();
|
|
if (initializer_tag == kSomething) {
|
|
VisitExpression(); // read initializer.
|
|
}
|
|
relative_kernel_offset_ = saved_relative_kernel_offset_;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
builder_->SkipStringReference(); // read ith name index.
|
|
VisitDartType(); // read ith bound.
|
|
}
|
|
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 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);
|
|
}
|
|
}
|
|
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;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void StreamingScopeBuilder::VisitExpression() {
|
|
uint8_t payload = 0;
|
|
Tag tag = builder_->ReadTag(&payload);
|
|
switch (tag) {
|
|
case kInvalidExpression:
|
|
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 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:
|
|
VisitExpression(); // read receiver.
|
|
builder_->SkipCanonicalNameReference(); // read target_reference.
|
|
VisitArguments(); // read arguments.
|
|
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.
|
|
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(relative_kernel_offset_ + 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 kSpecialIntLiteral:
|
|
return;
|
|
case kNegativeIntLiteral:
|
|
builder_->ReadUInt(); // read value.
|
|
return;
|
|
case kPositiveIntLiteral:
|
|
builder_->ReadUInt(); // read value.
|
|
return;
|
|
case kDoubleLiteral:
|
|
builder_->SkipStringReference(); // read index into string table.
|
|
return;
|
|
case kTrueLiteral:
|
|
return;
|
|
case kFalseLiteral:
|
|
return;
|
|
case kNullLiteral:
|
|
return;
|
|
case kVectorCreation:
|
|
builder_->ReadUInt(); // read size.
|
|
return;
|
|
case kVectorGet:
|
|
VisitExpression(); // read expression.
|
|
builder_->ReadUInt(); // read index.
|
|
return;
|
|
case kVectorSet:
|
|
VisitExpression(); // read vector expression.
|
|
builder_->ReadUInt(); // read index.
|
|
VisitExpression(); // read value.
|
|
return;
|
|
case kVectorCopy:
|
|
VisitExpression(); // read vector expression.
|
|
return;
|
|
case kClosureCreation:
|
|
builder_->SkipCanonicalNameReference(); // read function reference.
|
|
VisitExpression(); // read context vector.
|
|
VisitDartType(); // read function type of the closure.
|
|
builder_->SkipListOfDartTypes(); // read type arguments.
|
|
return;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void StreamingScopeBuilder::VisitStatement() {
|
|
Tag tag = builder_->ReadTag(); // read tag.
|
|
switch (tag) {
|
|
case kInvalidStatement:
|
|
return;
|
|
case kExpressionStatement:
|
|
VisitExpression(); // read expression.
|
|
return;
|
|
case kBlock: {
|
|
PositionScope scope(builder_->reader_);
|
|
intptr_t offset =
|
|
builder_->ReaderOffset() - 1; // -1 to include tag byte.
|
|
|
|
EnterScope(relative_kernel_offset_ + 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 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.
|
|
|
|
EnterScope(relative_kernel_offset_ + 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.
|
|
}
|
|
|
|
++depth_.loop_;
|
|
|
|
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.
|
|
|
|
--depth_.loop_;
|
|
|
|
ExitScope(position, builder_->reader_->max_position());
|
|
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(relative_kernel_offset_ + 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:
|
|
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 = H.DartSymbol(":try_finally_return_value");
|
|
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_->ReadBool(); // read any_catch_needs_stack_trace.
|
|
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(relative_kernel_offset_ + offset);
|
|
|
|
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());
|
|
}
|
|
--depth_.catch_;
|
|
return;
|
|
}
|
|
case kTryFinally: {
|
|
++depth_.try_;
|
|
++depth_.finally_;
|
|
AddTryVariables();
|
|
|
|
VisitStatement(); // read body.
|
|
|
|
--depth_.finally_;
|
|
--depth_.try_;
|
|
++depth_.catch_;
|
|
AddCatchVariables();
|
|
|
|
VisitStatement(); // read finalizer.
|
|
|
|
--depth_.catch_;
|
|
return;
|
|
}
|
|
case kYieldStatement: {
|
|
builder_->ReadPosition(); // read position.
|
|
word flags = builder_->ReadByte(); // read flags.
|
|
builder_->SkipExpression(); // 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:
|
|
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);
|
|
intptr_t offset_for_type = builder_->ReaderOffset();
|
|
AbstractType& type = T.BuildVariableType(); // read type.
|
|
|
|
// 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.DartSymbol(helper.name_index_);
|
|
// We also need to visit the type.
|
|
builder_->SetOffset(offset_for_type);
|
|
VisitDartType(); // read type.
|
|
|
|
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(relative_kernel_offset_ + kernel_offset_no_tag,
|
|
variable);
|
|
}
|
|
|
|
void StreamingScopeBuilder::VisitDartType() {
|
|
Tag tag = builder_->ReadTag();
|
|
switch (tag) {
|
|
case kInvalidType:
|
|
case kDynamicType:
|
|
case kVoidType:
|
|
case kBottomType:
|
|
case kVectorType:
|
|
// 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:
|
|
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) {
|
|
builder_->SkipStringReference(); // read string index (name).
|
|
VisitDartType(); // read dart type.
|
|
}
|
|
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();
|
|
}
|
|
|
|
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 (!function.IsGeneric()) {
|
|
// TODO(30455): Kernel generic methods undone. Currently we only support
|
|
// generic toplevel methods. For no special load needs to be prepared for
|
|
// these, because the 'LocalVariable' for the type arguments is set on the
|
|
// 'ParsedFunction' directly. So we do nothing for generic methods.
|
|
|
|
// The type argument vector is stored on the
|
|
// instance object. We therefore need to capture `this`.
|
|
HandleSpecialLoad(&result_->this_variable, Symbols::This());
|
|
}
|
|
|
|
builder_->ReadUInt(); // read index for parameter.
|
|
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::kPositionalParameters);
|
|
|
|
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(relative_kernel_offset_ + parent_kernel_offset);
|
|
current_function_scope_ = scope_;
|
|
current_function_async_marker_ = function_node_helper.async_marker_;
|
|
if (depth_.function_ == 1) {
|
|
FunctionScope function_scope = {relative_kernel_offset_ + offset, scope_};
|
|
result_->function_scopes.Add(function_scope);
|
|
}
|
|
|
|
// read positional_parameters and named_parameters.
|
|
AddPositionalAndNamedParameters();
|
|
|
|
// "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) {
|
|
scope_->set_begin_token_pos(start_position);
|
|
scope_->set_end_token_pos(end_position);
|
|
scope_ = scope_->parent();
|
|
}
|
|
|
|
void StreamingScopeBuilder::AddPositionalAndNamedParameters(intptr_t pos) {
|
|
// List of positional.
|
|
intptr_t list_length = builder_->ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
AddVariableDeclarationParameter(pos++); // read ith positional parameter.
|
|
}
|
|
|
|
// List of named.
|
|
list_length = builder_->ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
AddVariableDeclarationParameter(pos++); // read ith named parameter.
|
|
}
|
|
}
|
|
|
|
void StreamingScopeBuilder::AddVariableDeclarationParameter(intptr_t pos) {
|
|
intptr_t kernel_offset = builder_->ReaderOffset(); // no tag.
|
|
VariableDeclarationHelper helper(builder_);
|
|
helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
|
|
String& name = H.DartSymbol(helper.name_index_);
|
|
AbstractType& type = T.BuildVariableType(); // read type.
|
|
helper.SetJustRead(VariableDeclarationHelper::kType);
|
|
helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer);
|
|
|
|
LocalVariable* variable =
|
|
MakeVariable(helper.position_, helper.position_, name, type);
|
|
if (helper.IsFinal()) {
|
|
variable->set_is_final();
|
|
}
|
|
if (variable->name().raw() == Symbols::IteratorParameter().raw()) {
|
|
variable->set_is_forced_stack();
|
|
}
|
|
scope_->InsertParameterAt(pos, variable);
|
|
result_->locals.Insert(relative_kernel_offset_ + kernel_offset, variable);
|
|
|
|
// The default value may contain 'let' bindings for which the constant
|
|
// evaluator needs scope bindings.
|
|
Tag tag = builder_->ReadTag();
|
|
if (tag == kSomething) {
|
|
VisitExpression(); // read initializer.
|
|
}
|
|
}
|
|
|
|
LocalVariable* StreamingScopeBuilder::MakeVariable(
|
|
TokenPosition declaration_pos,
|
|
TokenPosition token_pos,
|
|
const String& name,
|
|
const AbstractType& type) {
|
|
return new (Z) LocalVariable(declaration_pos, token_pos, name, type);
|
|
}
|
|
|
|
void StreamingScopeBuilder::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 StreamingScopeBuilder::AddTryVariables() {
|
|
AddExceptionVariable(&result_->catch_context_variables,
|
|
":saved_try_context_var", depth_.try_);
|
|
}
|
|
|
|
void StreamingScopeBuilder::AddCatchVariables() {
|
|
AddExceptionVariable(&result_->exception_variables, ":exception",
|
|
depth_.catch_);
|
|
AddExceptionVariable(&result_->stack_trace_variables, ":stack_trace",
|
|
depth_.catch_);
|
|
}
|
|
|
|
void StreamingScopeBuilder::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 StreamingScopeBuilder::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 StreamingScopeBuilder::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 = builder_->GetNameFromVariableDeclaration(
|
|
declaration_binary_offset, parsed_function_->function());
|
|
|
|
const String& name = H.DartSymbol(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());
|
|
}
|
|
}
|
|
|
|
const String& StreamingScopeBuilder::GenerateName(const char* prefix,
|
|
intptr_t suffix) {
|
|
char name[64];
|
|
OS::SNPrint(name, 64, "%s%" Pd "", prefix, suffix);
|
|
return H.DartSymbol(name);
|
|
}
|
|
|
|
void StreamingScopeBuilder::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 StreamingScopeBuilder::LookupCapturedVariableByName(
|
|
LocalVariable** variable,
|
|
const String& name) {
|
|
if (*variable == NULL) {
|
|
*variable = scope_->LookupVariable(name, true);
|
|
ASSERT(*variable != NULL);
|
|
scope_->CaptureVariable(*variable);
|
|
}
|
|
}
|
|
|
|
StreamingDartTypeTranslator::StreamingDartTypeTranslator(
|
|
StreamingFlowGraphBuilder* builder,
|
|
bool finalize)
|
|
: builder_(builder),
|
|
translation_helper_(builder->translation_helper_),
|
|
active_class_(builder->active_class()),
|
|
type_parameter_scope_(NULL),
|
|
zone_(translation_helper_.zone()),
|
|
result_(AbstractType::Handle(translation_helper_.zone())),
|
|
finalize_(finalize) {}
|
|
|
|
AbstractType& StreamingDartTypeTranslator::BuildType() {
|
|
BuildTypeInternal();
|
|
|
|
// We return a new `ZoneHandle` here on purpose: The intermediate language
|
|
// instructions do not make a copy of the handle, so we do it.
|
|
return AbstractType::ZoneHandle(Z, result_.raw());
|
|
}
|
|
|
|
AbstractType& StreamingDartTypeTranslator::BuildTypeWithoutFinalization() {
|
|
bool saved_finalize = finalize_;
|
|
finalize_ = false;
|
|
BuildTypeInternal();
|
|
finalize_ = saved_finalize;
|
|
|
|
// We return a new `ZoneHandle` here on purpose: The intermediate language
|
|
// instructions do not make a copy of the handle, so we do it.
|
|
return AbstractType::ZoneHandle(Z, result_.raw());
|
|
}
|
|
|
|
AbstractType& StreamingDartTypeTranslator::BuildVariableType() {
|
|
AbstractType& abstract_type = BuildType();
|
|
|
|
// We return a new `ZoneHandle` here on purpose: The intermediate language
|
|
// instructions do not make a copy of the handle, so we do it.
|
|
AbstractType& type = Type::ZoneHandle(Z);
|
|
|
|
if (abstract_type.IsMalformed()) {
|
|
type = AbstractType::dynamic_type().raw();
|
|
} else {
|
|
type = result_.raw();
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
void StreamingDartTypeTranslator::BuildTypeInternal() {
|
|
Tag tag = builder_->ReadTag();
|
|
switch (tag) {
|
|
case kInvalidType:
|
|
result_ = ClassFinalizer::NewFinalizedMalformedType(
|
|
Error::Handle(Z), // No previous error.
|
|
Script::Handle(Z, Script::null()), TokenPosition::kNoSource,
|
|
"[InvalidType] in Kernel IR.");
|
|
break;
|
|
case kDynamicType:
|
|
result_ = Object::dynamic_type().raw();
|
|
break;
|
|
case kVoidType:
|
|
result_ = Object::void_type().raw();
|
|
break;
|
|
case kVectorType:
|
|
result_ = Object::vector_type().raw();
|
|
break;
|
|
case kBottomType:
|
|
result_ =
|
|
Class::Handle(Z, I->object_store()->null_class()).CanonicalType();
|
|
break;
|
|
case kInterfaceType:
|
|
BuildInterfaceType(false);
|
|
break;
|
|
case kSimpleInterfaceType:
|
|
BuildInterfaceType(true);
|
|
break;
|
|
case kFunctionType:
|
|
BuildFunctionType(false);
|
|
break;
|
|
case kSimpleFunctionType:
|
|
BuildFunctionType(true);
|
|
break;
|
|
case kTypeParameterType:
|
|
BuildTypeParameterType();
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void StreamingDartTypeTranslator::BuildInterfaceType(bool simple) {
|
|
// NOTE: That an interface type like `T<A, B>` is considered to be
|
|
// malformed iff `T` is malformed.
|
|
// => We therefore ignore errors in `A` or `B`.
|
|
|
|
NameIndex klass_name =
|
|
builder_->ReadCanonicalNameReference(); // read klass_name.
|
|
|
|
intptr_t length;
|
|
if (simple) {
|
|
length = 0;
|
|
} else {
|
|
length = builder_->ReadListLength(); // read type_arguments list length.
|
|
}
|
|
const TypeArguments& type_arguments =
|
|
BuildTypeArguments(length); // read type arguments.
|
|
|
|
Object& klass = Object::Handle(Z, H.LookupClassByKernelClass(klass_name));
|
|
result_ = Type::New(klass, type_arguments, TokenPosition::kNoSource);
|
|
if (finalize_) {
|
|
ASSERT(active_class_->klass != NULL);
|
|
result_ = ClassFinalizer::FinalizeType(*active_class_->klass, result_);
|
|
}
|
|
}
|
|
|
|
void StreamingDartTypeTranslator::BuildFunctionType(bool simple) {
|
|
intptr_t list_length = 0;
|
|
if (!simple) {
|
|
list_length =
|
|
builder_->ReadListLength(); // read type_parameters list length
|
|
for (int i = 0; i < list_length; ++i) {
|
|
builder_->SkipStringReference(); // read string index (name).
|
|
builder_->SkipDartType(); // read dart type.
|
|
}
|
|
}
|
|
|
|
// The spec describes in section "19.1 Static Types":
|
|
//
|
|
// Any use of a malformed type gives rise to a static warning. A
|
|
// malformed type is then interpreted as dynamic by the static type
|
|
// checker and the runtime unless explicitly specified otherwise.
|
|
//
|
|
// So we convert malformed return/parameter types to `dynamic`.
|
|
TypeParameterScope scope(this, list_length);
|
|
|
|
Function& signature_function = Function::ZoneHandle(
|
|
Z,
|
|
Function::NewSignatureFunction(*active_class_->klass, Function::Handle(Z),
|
|
TokenPosition::kNoSource));
|
|
|
|
intptr_t required_count;
|
|
intptr_t all_count;
|
|
intptr_t positional_count;
|
|
if (!simple) {
|
|
required_count = builder_->ReadUInt(); // read required parameter count.
|
|
all_count = builder_->ReadUInt(); // read total parameter count.
|
|
positional_count =
|
|
builder_->ReadListLength(); // read positional_parameters list length.
|
|
} else {
|
|
positional_count =
|
|
builder_->ReadListLength(); // read positional_parameters list length.
|
|
required_count = positional_count;
|
|
all_count = positional_count;
|
|
}
|
|
|
|
const Array& parameter_types =
|
|
Array::Handle(Z, Array::New(1 + all_count, Heap::kOld));
|
|
signature_function.set_parameter_types(parameter_types);
|
|
const Array& parameter_names =
|
|
Array::Handle(Z, Array::New(1 + all_count, Heap::kOld));
|
|
signature_function.set_parameter_names(parameter_names);
|
|
|
|
intptr_t pos = 0;
|
|
parameter_types.SetAt(pos, AbstractType::dynamic_type());
|
|
parameter_names.SetAt(pos, H.DartSymbol("_receiver_"));
|
|
++pos;
|
|
for (intptr_t i = 0; i < positional_count; ++i, ++pos) {
|
|
BuildTypeInternal(); // read ith positional parameter.
|
|
if (result_.IsMalformed()) {
|
|
result_ = AbstractType::dynamic_type().raw();
|
|
}
|
|
parameter_types.SetAt(pos, result_);
|
|
parameter_names.SetAt(pos, H.DartSymbol("noname"));
|
|
}
|
|
|
|
// The additional first parameter is the receiver type (set to dynamic).
|
|
signature_function.set_num_fixed_parameters(1 + required_count);
|
|
signature_function.SetNumOptionalParameters(
|
|
all_count - required_count, positional_count > required_count);
|
|
|
|
if (!simple) {
|
|
const intptr_t named_count =
|
|
builder_->ReadListLength(); // read named_parameters list length.
|
|
for (intptr_t i = 0; i < named_count; ++i, ++pos) {
|
|
// read string reference (i.e. named_parameters[i].name).
|
|
String& name = H.DartSymbol(builder_->ReadStringReference());
|
|
BuildTypeInternal(); // read named_parameters[i].type.
|
|
if (result_.IsMalformed()) {
|
|
result_ = AbstractType::dynamic_type().raw();
|
|
}
|
|
parameter_types.SetAt(pos, result_);
|
|
parameter_names.SetAt(pos, name);
|
|
}
|
|
}
|
|
|
|
builder_->SkipListOfStrings(); // read positional parameter names.
|
|
|
|
if (!simple) {
|
|
builder_->SkipCanonicalNameReference(); // read typedef reference.
|
|
}
|
|
|
|
BuildTypeInternal(); // read return type.
|
|
if (result_.IsMalformed()) {
|
|
result_ = AbstractType::dynamic_type().raw();
|
|
}
|
|
signature_function.set_result_type(result_);
|
|
|
|
Type& signature_type =
|
|
Type::ZoneHandle(Z, signature_function.SignatureType());
|
|
|
|
if (finalize_) {
|
|
signature_type ^=
|
|
ClassFinalizer::FinalizeType(*active_class_->klass, signature_type);
|
|
// Do not refer to signature_function anymore, since it may have been
|
|
// replaced during canonicalization.
|
|
signature_function = Function::null();
|
|
}
|
|
|
|
result_ = signature_type.raw();
|
|
}
|
|
|
|
void StreamingDartTypeTranslator::BuildTypeParameterType() {
|
|
intptr_t parameter_index = builder_->ReadUInt(); // read parameter index.
|
|
builder_->SkipOptionalDartType(); // read bound.
|
|
|
|
const TypeArguments& class_types =
|
|
TypeArguments::Handle(Z, active_class_->klass->type_parameters());
|
|
if (parameter_index < class_types.Length()) {
|
|
// The index of the type parameter in [parameters] is
|
|
// the same index into the `klass->type_parameters()` array.
|
|
result_ ^= class_types.TypeAt(parameter_index);
|
|
return;
|
|
}
|
|
parameter_index -= class_types.Length();
|
|
|
|
if (active_class_->HasMember()) {
|
|
if (active_class_->MemberIsFactoryProcedure()) {
|
|
//
|
|
// WARNING: This is a little hackish:
|
|
//
|
|
// We have a static factory constructor. The kernel IR gives the factory
|
|
// constructor function it's own type parameters (which are equal in name
|
|
// and number to the ones of the enclosing class).
|
|
// I.e.,
|
|
//
|
|
// class A<T> {
|
|
// factory A.x() { return new B<T>(); }
|
|
// }
|
|
//
|
|
// is basically translated to this:
|
|
//
|
|
// class A<T> {
|
|
// static A.x<T'>() { return new B<T'>(); }
|
|
// }
|
|
//
|
|
if (class_types.Length() > parameter_index) {
|
|
result_ ^= class_types.TypeAt(parameter_index);
|
|
return;
|
|
}
|
|
parameter_index -= class_types.Length();
|
|
}
|
|
|
|
intptr_t procedure_type_parameter_count =
|
|
active_class_->MemberIsProcedure()
|
|
? active_class_->MemberTypeParameterCount(Z)
|
|
: 0;
|
|
if (procedure_type_parameter_count > 0) {
|
|
if (procedure_type_parameter_count > parameter_index) {
|
|
if (FLAG_reify_generic_functions) {
|
|
result_ ^=
|
|
TypeArguments::Handle(Z, active_class_->member->type_parameters())
|
|
.TypeAt(parameter_index);
|
|
} else {
|
|
result_ ^= Type::DynamicType();
|
|
}
|
|
return;
|
|
}
|
|
parameter_index -= procedure_type_parameter_count;
|
|
}
|
|
}
|
|
|
|
if (type_parameter_scope_ != NULL && parameter_index >= 0 &&
|
|
parameter_index < type_parameter_scope_->outer_parameter_count() +
|
|
type_parameter_scope_->parameter_count()) {
|
|
result_ ^= Type::DynamicType();
|
|
return;
|
|
}
|
|
|
|
H.ReportError("Unexpected input. Please report this at dartbug.com.");
|
|
}
|
|
|
|
const TypeArguments& StreamingDartTypeTranslator::BuildTypeArguments(
|
|
intptr_t length) {
|
|
bool only_dynamic = true;
|
|
intptr_t offset = builder_->ReaderOffset();
|
|
for (intptr_t i = 0; i < length; ++i) {
|
|
if (builder_->ReadTag() != kDynamicType) { // Read the ith types tag.
|
|
only_dynamic = false;
|
|
builder_->SetOffset(offset);
|
|
break;
|
|
}
|
|
}
|
|
TypeArguments& type_arguments = TypeArguments::ZoneHandle(Z);
|
|
if (!only_dynamic) {
|
|
type_arguments = TypeArguments::New(length);
|
|
for (intptr_t i = 0; i < length; ++i) {
|
|
BuildTypeInternal(); // read ith type.
|
|
if (result_.IsMalformed()) {
|
|
type_arguments = TypeArguments::null();
|
|
// Skip the rest of the arguments.
|
|
for (++i; i < length; ++i) {
|
|
builder_->SkipDartType();
|
|
}
|
|
return type_arguments;
|
|
}
|
|
type_arguments.SetTypeAt(i, result_);
|
|
}
|
|
|
|
if (finalize_) {
|
|
type_arguments = type_arguments.Canonicalize();
|
|
}
|
|
}
|
|
return type_arguments;
|
|
}
|
|
|
|
const TypeArguments&
|
|
StreamingDartTypeTranslator::BuildInstantiatedTypeArguments(
|
|
const Class& receiver_class,
|
|
intptr_t length) {
|
|
const TypeArguments& type_arguments = BuildTypeArguments(length);
|
|
|
|
// If type_arguments is null all arguments are dynamic.
|
|
// If, however, this class doesn't specify all the type arguments directly we
|
|
// still need to finalize the type below in order to get any non-dynamic types
|
|
// from any super. See http://www.dartbug.com/29537.
|
|
if (type_arguments.IsNull() && receiver_class.NumTypeArguments() == length) {
|
|
return type_arguments;
|
|
}
|
|
|
|
// We make a temporary [Type] object and use `ClassFinalizer::FinalizeType` to
|
|
// finalize the argument types.
|
|
// (This can for example make the [type_arguments] vector larger)
|
|
Type& type = Type::Handle(
|
|
Z, Type::New(receiver_class, type_arguments, TokenPosition::kNoSource));
|
|
if (finalize_) {
|
|
type ^= ClassFinalizer::FinalizeType(*active_class_->klass, type);
|
|
}
|
|
|
|
const TypeArguments& instantiated_type_arguments =
|
|
TypeArguments::ZoneHandle(Z, type.arguments());
|
|
return instantiated_type_arguments;
|
|
}
|
|
|
|
const Type& StreamingDartTypeTranslator::ReceiverType(const Class& klass) {
|
|
ASSERT(!klass.IsNull());
|
|
ASSERT(!klass.IsTypedefClass());
|
|
// Note that if klass is _Closure, the returned type will be _Closure,
|
|
// and not the signature type.
|
|
Type& type = Type::ZoneHandle(Z, klass.CanonicalType());
|
|
if (!type.IsNull()) {
|
|
return type;
|
|
}
|
|
type = Type::New(klass, TypeArguments::Handle(Z, klass.type_parameters()),
|
|
klass.token_pos());
|
|
if (klass.is_type_finalized()) {
|
|
type ^= ClassFinalizer::FinalizeType(klass, type);
|
|
klass.SetCanonicalType(type);
|
|
}
|
|
return type;
|
|
}
|
|
|
|
StreamingConstantEvaluator::StreamingConstantEvaluator(
|
|
StreamingFlowGraphBuilder* builder)
|
|
: builder_(builder),
|
|
isolate_(Isolate::Current()),
|
|
zone_(builder_->zone_),
|
|
translation_helper_(builder_->translation_helper_),
|
|
type_translator_(builder_->type_translator_),
|
|
script_(Script::Handle(
|
|
zone_,
|
|
// TODO(jensj): This was added to temporarily be able to let the scope
|
|
// builder have a StreamingFlowGraphBuilder to get access to
|
|
// reading functions.
|
|
(builder == NULL || builder_->flow_graph_builder_ == NULL)
|
|
? Script::null()
|
|
: builder_->parsed_function()->function().script())),
|
|
result_(Instance::Handle(zone_)) {}
|
|
|
|
Instance& StreamingConstantEvaluator::EvaluateExpression(intptr_t offset,
|
|
bool reset_position) {
|
|
if (!GetCachedConstant(offset, &result_)) {
|
|
intptr_t original_offset = builder_->ReaderOffset();
|
|
builder_->SetOffset(offset);
|
|
uint8_t payload = 0;
|
|
Tag tag = builder_->ReadTag(&payload); // read tag.
|
|
switch (tag) {
|
|
case kVariableGet:
|
|
EvaluateVariableGet();
|
|
break;
|
|
case kSpecializedVariableGet:
|
|
EvaluateVariableGet(payload);
|
|
break;
|
|
case kPropertyGet:
|
|
EvaluatePropertyGet();
|
|
break;
|
|
case kStaticGet:
|
|
EvaluateStaticGet();
|
|
break;
|
|
case kMethodInvocation:
|
|
EvaluateMethodInvocation();
|
|
break;
|
|
case kStaticInvocation:
|
|
case kConstStaticInvocation:
|
|
EvaluateStaticInvocation();
|
|
break;
|
|
case kConstructorInvocation:
|
|
case kConstConstructorInvocation:
|
|
EvaluateConstructorInvocationInternal();
|
|
break;
|
|
case kNot:
|
|
EvaluateNot();
|
|
break;
|
|
case kLogicalExpression:
|
|
EvaluateLogicalExpression();
|
|
break;
|
|
case kConditionalExpression:
|
|
EvaluateConditionalExpression();
|
|
break;
|
|
case kStringConcatenation:
|
|
EvaluateStringConcatenation();
|
|
break;
|
|
case kSymbolLiteral:
|
|
EvaluateSymbolLiteral();
|
|
break;
|
|
case kTypeLiteral:
|
|
EvaluateTypeLiteral();
|
|
break;
|
|
case kListLiteral:
|
|
case kConstListLiteral:
|
|
EvaluateListLiteralInternal();
|
|
break;
|
|
case kMapLiteral:
|
|
case kConstMapLiteral:
|
|
EvaluateMapLiteralInternal();
|
|
break;
|
|
case kLet:
|
|
EvaluateLet();
|
|
break;
|
|
case kBigIntLiteral:
|
|
EvaluateBigIntLiteral();
|
|
break;
|
|
case kStringLiteral:
|
|
EvaluateStringLiteral();
|
|
break;
|
|
case kSpecialIntLiteral:
|
|
EvaluateIntLiteral(payload);
|
|
break;
|
|
case kNegativeIntLiteral:
|
|
EvaluateIntLiteral(true);
|
|
break;
|
|
case kPositiveIntLiteral:
|
|
EvaluateIntLiteral(false);
|
|
break;
|
|
case kDoubleLiteral:
|
|
EvaluateDoubleLiteral();
|
|
break;
|
|
case kTrueLiteral:
|
|
EvaluateBoolLiteral(true);
|
|
break;
|
|
case kFalseLiteral:
|
|
EvaluateBoolLiteral(false);
|
|
break;
|
|
case kNullLiteral:
|
|
EvaluateNullLiteral();
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
|
|
CacheConstantValue(offset, result_);
|
|
if (reset_position) builder_->SetOffset(original_offset);
|
|
}
|
|
// We return a new `ZoneHandle` here on purpose: The intermediate language
|
|
// instructions do not make a copy of the handle, so we do it.
|
|
return Instance::ZoneHandle(Z, result_.raw());
|
|
}
|
|
|
|
Instance& StreamingConstantEvaluator::EvaluateListLiteral(intptr_t offset,
|
|
bool reset_position) {
|
|
if (!GetCachedConstant(offset, &result_)) {
|
|
intptr_t original_offset = builder_->ReaderOffset();
|
|
builder_->SetOffset(offset);
|
|
builder_->ReadTag(); // skip tag.
|
|
EvaluateListLiteralInternal();
|
|
|
|
CacheConstantValue(offset, result_);
|
|
if (reset_position) builder_->SetOffset(original_offset);
|
|
}
|
|
// We return a new `ZoneHandle` here on purpose: The intermediate language
|
|
// instructions do not make a copy of the handle, so we do it.
|
|
return Instance::ZoneHandle(Z, result_.raw());
|
|
}
|
|
|
|
Instance& StreamingConstantEvaluator::EvaluateMapLiteral(intptr_t offset,
|
|
bool reset_position) {
|
|
if (!GetCachedConstant(offset, &result_)) {
|
|
intptr_t original_offset = builder_->ReaderOffset();
|
|
builder_->SetOffset(offset);
|
|
builder_->ReadTag(); // skip tag.
|
|
EvaluateMapLiteralInternal();
|
|
|
|
CacheConstantValue(offset, result_);
|
|
if (reset_position) builder_->SetOffset(original_offset);
|
|
}
|
|
// We return a new `ZoneHandle` here on purpose: The intermediate language
|
|
// instructions do not make a copy of the handle, so we do it.
|
|
return Instance::ZoneHandle(Z, result_.raw());
|
|
}
|
|
|
|
Instance& StreamingConstantEvaluator::EvaluateConstructorInvocation(
|
|
intptr_t offset,
|
|
bool reset_position) {
|
|
if (!GetCachedConstant(offset, &result_)) {
|
|
intptr_t original_offset = builder_->ReaderOffset();
|
|
builder_->SetOffset(offset);
|
|
builder_->ReadTag(); // skip tag.
|
|
EvaluateConstructorInvocationInternal();
|
|
|
|
CacheConstantValue(offset, result_);
|
|
if (reset_position) builder_->SetOffset(original_offset);
|
|
}
|
|
// We return a new `ZoneHandle` here on purpose: The intermediate language
|
|
// instructions do not make a copy of the handle, so we do it.
|
|
return Instance::ZoneHandle(Z, result_.raw());
|
|
}
|
|
|
|
Object& StreamingConstantEvaluator::EvaluateExpressionSafe(intptr_t offset) {
|
|
LongJumpScope jump;
|
|
if (setjmp(*jump.Set()) == 0) {
|
|
return EvaluateExpression(offset);
|
|
} else {
|
|
Thread* thread = H.thread();
|
|
Error& error = Error::Handle(Z);
|
|
error = thread->sticky_error();
|
|
thread->clear_sticky_error();
|
|
return error;
|
|
}
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateVariableGet() {
|
|
// When we see a [VariableGet] the corresponding [VariableDeclaration] must've
|
|
// been executed already. It therefore must have a constant object associated
|
|
// with it.
|
|
builder_->ReadPosition(); // read position.
|
|
intptr_t variable_kernel_position =
|
|
builder_->ReadUInt(); // read kernel position.
|
|
builder_->ReadUInt(); // read relative variable index.
|
|
builder_->SkipOptionalDartType(); // read promoted type.
|
|
LocalVariable* variable = builder_->LookupVariable(variable_kernel_position);
|
|
ASSERT(variable->IsConst());
|
|
result_ = variable->ConstValue()->raw();
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateVariableGet(uint8_t payload) {
|
|
// When we see a [VariableGet] the corresponding [VariableDeclaration] must've
|
|
// been executed already. It therefore must have a constant object associated
|
|
// with it.
|
|
builder_->ReadPosition(); // read position.
|
|
intptr_t variable_kernel_position =
|
|
builder_->ReadUInt(); // read kernel position.
|
|
LocalVariable* variable = builder_->LookupVariable(variable_kernel_position);
|
|
ASSERT(variable->IsConst());
|
|
result_ = variable->ConstValue()->raw();
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluatePropertyGet() {
|
|
builder_->ReadPosition(); // read position.
|
|
intptr_t expression_offset = builder_->ReaderOffset();
|
|
builder_->SkipExpression(); // read receiver.
|
|
StringIndex name = builder_->ReadNameAsStringIndex(); // read name.
|
|
builder_->SkipCanonicalNameReference(); // read interface_target_reference.
|
|
|
|
if (H.StringEquals(name, "length")) {
|
|
EvaluateExpression(expression_offset);
|
|
if (result_.IsString()) {
|
|
const String& str = String::Handle(Z, String::RawCast(result_.raw()));
|
|
result_ = Integer::New(str.Length());
|
|
} else {
|
|
H.ReportError(
|
|
"Constant expressions can only call "
|
|
"'length' on string constants.");
|
|
}
|
|
} else {
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateStaticGet() {
|
|
builder_->ReadPosition(); // read position.
|
|
NameIndex target =
|
|
builder_->ReadCanonicalNameReference(); // read target_reference.
|
|
|
|
if (H.IsField(target)) {
|
|
const Field& field = Field::Handle(Z, H.LookupFieldByKernelField(target));
|
|
if (field.StaticValue() == Object::sentinel().raw() ||
|
|
field.StaticValue() == Object::transition_sentinel().raw()) {
|
|
field.EvaluateInitializer();
|
|
result_ = field.StaticValue();
|
|
result_ = H.Canonicalize(result_);
|
|
field.SetStaticValue(result_, true);
|
|
} else {
|
|
result_ = field.StaticValue();
|
|
}
|
|
} else if (H.IsProcedure(target)) {
|
|
const Function& function =
|
|
Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target));
|
|
|
|
if (H.IsMethod(target)) {
|
|
Function& closure_function =
|
|
Function::ZoneHandle(Z, function.ImplicitClosureFunction());
|
|
result_ = closure_function.ImplicitStaticClosure();
|
|
result_ = H.Canonicalize(result_);
|
|
} else if (H.IsGetter(target)) {
|
|
UNIMPLEMENTED();
|
|
} else {
|
|
UNIMPLEMENTED();
|
|
}
|
|
}
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateMethodInvocation() {
|
|
builder_->ReadPosition(); // read position.
|
|
// This method call wasn't cached, so receiver et al. isn't cached either.
|
|
const Instance& receiver =
|
|
EvaluateExpression(builder_->ReaderOffset(), false); // read receiver.
|
|
Class& klass =
|
|
Class::Handle(Z, isolate_->class_table()->At(receiver.GetClassId()));
|
|
ASSERT(!klass.IsNull());
|
|
|
|
// Search the superclass chain for the selector.
|
|
Function& function = Function::Handle(Z);
|
|
const String& method_name = builder_->ReadNameAsMethodName(); // read name.
|
|
while (!klass.IsNull()) {
|
|
function = klass.LookupDynamicFunctionAllowPrivate(method_name);
|
|
if (!function.IsNull()) break;
|
|
klass = klass.SuperClass();
|
|
}
|
|
|
|
// The frontend should guarantee that [MethodInvocation]s inside constant
|
|
// expressions are always valid.
|
|
ASSERT(!function.IsNull());
|
|
|
|
// Read first parts of arguments: count and list of types.
|
|
intptr_t argument_count = builder_->PeekArgumentsCount();
|
|
// Dart does not support generic methods yet.
|
|
ASSERT(builder_->PeekArgumentsTypeCount() == 0);
|
|
builder_->SkipArgumentsBeforeActualArguments();
|
|
|
|
// Run the method and canonicalize the result.
|
|
const Object& result = RunFunction(function, argument_count, &receiver, NULL);
|
|
result_ ^= result.raw();
|
|
result_ = H.Canonicalize(result_);
|
|
|
|
builder_->SkipCanonicalNameReference(); // read interface_target_reference.
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateStaticInvocation() {
|
|
builder_->ReadPosition(); // read position.
|
|
NameIndex procedue_reference =
|
|
builder_->ReadCanonicalNameReference(); // read procedure reference.
|
|
|
|
const Function& function = Function::ZoneHandle(
|
|
Z, H.LookupStaticMethodByKernelProcedure(procedue_reference));
|
|
Class& klass = Class::Handle(Z, function.Owner());
|
|
|
|
intptr_t argument_count =
|
|
builder_->ReadUInt(); // read arguments part #1: arguments count.
|
|
|
|
// Build the type arguments vector (if necessary).
|
|
const TypeArguments* type_arguments =
|
|
TranslateTypeArguments(function, &klass); // read argument types.
|
|
|
|
// read positional and named parameters.
|
|
const Object& result =
|
|
RunFunction(function, argument_count, NULL, type_arguments);
|
|
result_ ^= result.raw();
|
|
result_ = H.Canonicalize(result_);
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateConstructorInvocationInternal() {
|
|
builder_->ReadPosition(); // read position.
|
|
|
|
NameIndex target = builder_->ReadCanonicalNameReference(); // read target.
|
|
const Function& constructor =
|
|
Function::Handle(Z, H.LookupConstructorByKernelConstructor(target));
|
|
Class& klass = Class::Handle(Z, constructor.Owner());
|
|
|
|
intptr_t argument_count =
|
|
builder_->ReadUInt(); // read arguments part #1: arguments count.
|
|
|
|
// Build the type arguments vector (if necessary).
|
|
const TypeArguments* type_arguments =
|
|
TranslateTypeArguments(constructor, &klass); // read argument types.
|
|
|
|
// Prepare either the instance or the type argument vector for the constructor
|
|
// call.
|
|
Instance* receiver = NULL;
|
|
const TypeArguments* type_arguments_argument = NULL;
|
|
if (!constructor.IsFactory()) {
|
|
receiver = &Instance::ZoneHandle(Z, Instance::New(klass, Heap::kOld));
|
|
if (type_arguments != NULL) {
|
|
receiver->SetTypeArguments(*type_arguments);
|
|
}
|
|
} else {
|
|
type_arguments_argument = type_arguments;
|
|
}
|
|
|
|
// read positional and named parameters.
|
|
const Object& result = RunFunction(constructor, argument_count, receiver,
|
|
type_arguments_argument);
|
|
|
|
if (constructor.IsFactory()) {
|
|
// Factories return the new object.
|
|
result_ ^= result.raw();
|
|
result_ = H.Canonicalize(result_);
|
|
} else {
|
|
ASSERT(!receiver->IsNull());
|
|
result_ = H.Canonicalize(*receiver);
|
|
}
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateNot() {
|
|
result_ ^= Bool::Get(!EvaluateBooleanExpressionHere()).raw();
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateLogicalExpression() {
|
|
bool left = EvaluateBooleanExpressionHere(); // read left.
|
|
LogicalOperator op = static_cast<LogicalOperator>(builder_->ReadByte());
|
|
if (op == kAnd) {
|
|
if (left) {
|
|
EvaluateBooleanExpressionHere(); // read right.
|
|
} else {
|
|
builder_->SkipExpression(); // read right.
|
|
}
|
|
} else {
|
|
ASSERT(op == kOr);
|
|
if (!left) {
|
|
EvaluateBooleanExpressionHere(); // read right.
|
|
} else {
|
|
builder_->SkipExpression(); // read right.
|
|
}
|
|
}
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateConditionalExpression() {
|
|
bool condition = EvaluateBooleanExpressionHere();
|
|
if (condition) {
|
|
EvaluateExpression(builder_->ReaderOffset(), false); // read then.
|
|
builder_->SkipExpression(); // read otherwise.
|
|
} else {
|
|
builder_->SkipExpression(); // read then.
|
|
EvaluateExpression(builder_->ReaderOffset(), false); // read otherwise.
|
|
}
|
|
builder_->SkipOptionalDartType(); // read unused static type.
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateStringConcatenation() {
|
|
builder_->ReadPosition(); // read position.
|
|
intptr_t length = builder_->ReadListLength(); // read list length.
|
|
|
|
bool all_string = true;
|
|
const Array& strings = Array::Handle(Z, Array::New(length));
|
|
for (intptr_t i = 0; i < length; ++i) {
|
|
EvaluateExpression(builder_->ReaderOffset(),
|
|
false); // read ith expression.
|
|
strings.SetAt(i, result_);
|
|
all_string = all_string && result_.IsString();
|
|
}
|
|
if (all_string) {
|
|
result_ = String::ConcatAll(strings, Heap::kOld);
|
|
result_ = H.Canonicalize(result_);
|
|
} else {
|
|
// Get string interpolation function.
|
|
const Class& cls =
|
|
Class::Handle(Z, Library::LookupCoreClass(Symbols::StringBase()));
|
|
ASSERT(!cls.IsNull());
|
|
const Function& func = Function::Handle(
|
|
Z, cls.LookupStaticFunction(
|
|
Library::PrivateCoreLibName(Symbols::Interpolate())));
|
|
ASSERT(!func.IsNull());
|
|
|
|
// Build argument array to pass to the interpolation function.
|
|
const Array& interpolate_arg = Array::Handle(Z, Array::New(1, Heap::kOld));
|
|
interpolate_arg.SetAt(0, strings);
|
|
|
|
// Run and canonicalize.
|
|
const Object& result =
|
|
RunFunction(func, interpolate_arg, Array::null_array());
|
|
result_ = H.Canonicalize(String::Cast(result));
|
|
}
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateSymbolLiteral() {
|
|
const String& symbol_value = H.DartSymbol(
|
|
builder_->ReadStringReference()); // read index into string table.
|
|
|
|
const Class& symbol_class =
|
|
Class::ZoneHandle(Z, I->object_store()->symbol_class());
|
|
ASSERT(!symbol_class.IsNull());
|
|
const Function& symbol_constructor = Function::ZoneHandle(
|
|
Z, symbol_class.LookupConstructor(Symbols::SymbolCtor()));
|
|
ASSERT(!symbol_constructor.IsNull());
|
|
result_ ^= EvaluateConstConstructorCall(
|
|
symbol_class, TypeArguments::Handle(Z), symbol_constructor, symbol_value);
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateTypeLiteral() {
|
|
const AbstractType& type = T.BuildType();
|
|
if (type.IsMalformed()) {
|
|
H.ReportError("Malformed type literal in constant expression.");
|
|
}
|
|
result_ = type.raw();
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateListLiteralInternal() {
|
|
builder_->ReadPosition(); // read position.
|
|
const TypeArguments& type_arguments = T.BuildTypeArguments(1); // read type.
|
|
intptr_t length = builder_->ReadListLength(); // read list length.
|
|
const Array& const_list =
|
|
Array::ZoneHandle(Z, Array::New(length, Heap::kOld));
|
|
const_list.SetTypeArguments(type_arguments);
|
|
for (intptr_t i = 0; i < length; ++i) {
|
|
const Instance& expression = EvaluateExpression(
|
|
builder_->ReaderOffset(), false); // read ith expression.
|
|
const_list.SetAt(i, expression);
|
|
}
|
|
const_list.MakeImmutable();
|
|
result_ = H.Canonicalize(const_list);
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateMapLiteralInternal() {
|
|
builder_->ReadPosition(); // read position.
|
|
const TypeArguments& type_arguments =
|
|
T.BuildTypeArguments(2); // read key type and value type.
|
|
|
|
intptr_t length = builder_->ReadListLength(); // read length of entries.
|
|
|
|
// This MapLiteral wasn't cached, so content isn't cached either.
|
|
Array& const_kv_array =
|
|
Array::ZoneHandle(Z, Array::New(2 * length, Heap::kOld));
|
|
for (intptr_t i = 0; i < length; ++i) {
|
|
const_kv_array.SetAt(2 * i + 0, EvaluateExpression(builder_->ReaderOffset(),
|
|
false)); // read key.
|
|
const_kv_array.SetAt(2 * i + 1, EvaluateExpression(builder_->ReaderOffset(),
|
|
false)); // read value.
|
|
}
|
|
|
|
const_kv_array.MakeImmutable();
|
|
const_kv_array ^= H.Canonicalize(const_kv_array);
|
|
|
|
const Class& map_class =
|
|
Class::Handle(Z, Library::LookupCoreClass(Symbols::ImmutableMap()));
|
|
ASSERT(!map_class.IsNull());
|
|
ASSERT(map_class.NumTypeArguments() == 2);
|
|
|
|
const Field& field = Field::Handle(
|
|
Z, map_class.LookupInstanceFieldAllowPrivate(H.DartSymbol("_kvPairs")));
|
|
ASSERT(!field.IsNull());
|
|
|
|
// NOTE: This needs to be kept in sync with `runtime/lib/immutable_map.dart`!
|
|
result_ = Instance::New(map_class, Heap::kOld);
|
|
ASSERT(!result_.IsNull());
|
|
result_.SetTypeArguments(type_arguments);
|
|
result_.SetField(field, const_kv_array);
|
|
result_ = H.Canonicalize(result_);
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateLet() {
|
|
intptr_t kernel_position =
|
|
builder_->ReaderOffset() + builder_->relative_kernel_offset_;
|
|
LocalVariable* local = builder_->LookupVariable(kernel_position);
|
|
|
|
// read variable declaration.
|
|
VariableDeclarationHelper helper(builder_);
|
|
helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer);
|
|
Tag tag = builder_->ReadTag(); // read (first part of) initializer.
|
|
if (tag == kNothing) {
|
|
local->SetConstValue(Instance::ZoneHandle(Z, Instance::null()));
|
|
} else {
|
|
local->SetConstValue(EvaluateExpression(
|
|
builder_->ReaderOffset(), false)); // read rest of initializer.
|
|
}
|
|
|
|
EvaluateExpression(builder_->ReaderOffset(), false); // read body
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateBigIntLiteral() {
|
|
const String& value =
|
|
H.DartString(builder_->ReadStringReference()); // read string reference.
|
|
result_ = Integer::New(value, Heap::kOld);
|
|
if (result_.IsNull()) {
|
|
H.ReportError("Integer literal %s is out of range", value.ToCString());
|
|
UNREACHABLE();
|
|
}
|
|
result_ = H.Canonicalize(result_);
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateStringLiteral() {
|
|
result_ = H.DartSymbol(builder_->ReadStringReference())
|
|
.raw(); // read string reference.
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateIntLiteral(uint8_t payload) {
|
|
int64_t value = static_cast<int32_t>(payload) - SpecializedIntLiteralBias;
|
|
result_ = Integer::New(value, Heap::kOld);
|
|
result_ = H.Canonicalize(result_);
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateIntLiteral(bool is_negative) {
|
|
int64_t value = is_negative ? -static_cast<int64_t>(builder_->ReadUInt())
|
|
: builder_->ReadUInt(); // read value.
|
|
result_ = Integer::New(value, Heap::kOld);
|
|
result_ = H.Canonicalize(result_);
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateDoubleLiteral() {
|
|
result_ = Double::New(H.DartString(builder_->ReadStringReference()),
|
|
Heap::kOld); // read string reference.
|
|
result_ = H.Canonicalize(result_);
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateBoolLiteral(bool value) {
|
|
result_ = Bool::Get(value).raw();
|
|
}
|
|
|
|
void StreamingConstantEvaluator::EvaluateNullLiteral() {
|
|
result_ = Instance::null();
|
|
}
|
|
|
|
// This depends on being about to read the list of positionals on arguments.
|
|
const Object& StreamingConstantEvaluator::RunFunction(
|
|
const Function& function,
|
|
intptr_t argument_count,
|
|
const Instance* receiver,
|
|
const TypeArguments* type_args) {
|
|
// We do not support generic methods yet.
|
|
ASSERT((receiver == NULL) || (type_args == NULL));
|
|
intptr_t extra_arguments =
|
|
(receiver != NULL ? 1 : 0) + (type_args != NULL ? 1 : 0);
|
|
|
|
// Build up arguments.
|
|
const Array& arguments =
|
|
Array::ZoneHandle(Z, Array::New(extra_arguments + argument_count));
|
|
intptr_t pos = 0;
|
|
if (receiver != NULL) {
|
|
arguments.SetAt(pos++, *receiver);
|
|
}
|
|
if (type_args != NULL) {
|
|
arguments.SetAt(pos++, *type_args);
|
|
}
|
|
|
|
// List of positional.
|
|
intptr_t list_length = builder_->ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
EvaluateExpression(builder_->ReaderOffset(),
|
|
false); // read ith expression.
|
|
arguments.SetAt(pos++, result_);
|
|
}
|
|
|
|
// List of named.
|
|
list_length = builder_->ReadListLength(); // read list length.
|
|
const Array& names = Array::ZoneHandle(Z, Array::New(list_length));
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
String& name =
|
|
H.DartSymbol(builder_->ReadStringReference()); // read ith name index.
|
|
names.SetAt(i, name);
|
|
EvaluateExpression(builder_->ReaderOffset(),
|
|
false); // read ith expression.
|
|
arguments.SetAt(pos++, result_);
|
|
}
|
|
|
|
return RunFunction(function, arguments, names);
|
|
}
|
|
|
|
const Object& StreamingConstantEvaluator::RunFunction(const Function& function,
|
|
const Array& arguments,
|
|
const Array& names) {
|
|
// We do not support generic methods yet.
|
|
const int kTypeArgsLen = 0;
|
|
const Array& args_descriptor = Array::Handle(
|
|
Z, ArgumentsDescriptor::New(kTypeArgsLen, arguments.Length(), names));
|
|
const Object& result = Object::Handle(
|
|
Z, DartEntry::InvokeFunction(function, arguments, args_descriptor));
|
|
if (result.IsError()) {
|
|
H.ReportError(Error::Cast(result), "error evaluating constant constructor");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
RawObject* StreamingConstantEvaluator::EvaluateConstConstructorCall(
|
|
const Class& type_class,
|
|
const TypeArguments& type_arguments,
|
|
const Function& constructor,
|
|
const Object& argument) {
|
|
// Factories have one extra argument: the type arguments.
|
|
// Constructors have 1 extra arguments: receiver.
|
|
const int kTypeArgsLen = 0;
|
|
const int kNumArgs = 1;
|
|
const int kNumExtraArgs = 1;
|
|
const int argument_count = kNumArgs + kNumExtraArgs;
|
|
const Array& arg_values =
|
|
Array::Handle(Z, Array::New(argument_count, Heap::kOld));
|
|
Instance& instance = Instance::Handle(Z);
|
|
if (!constructor.IsFactory()) {
|
|
instance = Instance::New(type_class, Heap::kOld);
|
|
if (!type_arguments.IsNull()) {
|
|
ASSERT(type_arguments.IsInstantiated());
|
|
instance.SetTypeArguments(
|
|
TypeArguments::Handle(Z, type_arguments.Canonicalize()));
|
|
}
|
|
arg_values.SetAt(0, instance);
|
|
} else {
|
|
// Prepend type_arguments to list of arguments to factory.
|
|
ASSERT(type_arguments.IsZoneHandle());
|
|
arg_values.SetAt(0, type_arguments);
|
|
}
|
|
arg_values.SetAt((0 + kNumExtraArgs), argument);
|
|
const Array& args_descriptor =
|
|
Array::Handle(Z, ArgumentsDescriptor::New(kTypeArgsLen, argument_count,
|
|
Object::empty_array()));
|
|
const Object& result = Object::Handle(
|
|
Z, DartEntry::InvokeFunction(constructor, arg_values, args_descriptor));
|
|
ASSERT(!result.IsError());
|
|
if (constructor.IsFactory()) {
|
|
// The factory method returns the allocated object.
|
|
instance ^= result.raw();
|
|
}
|
|
return H.Canonicalize(instance);
|
|
}
|
|
|
|
const TypeArguments* StreamingConstantEvaluator::TranslateTypeArguments(
|
|
const Function& target,
|
|
Class* target_klass) {
|
|
intptr_t type_count = builder_->ReadListLength(); // read type count.
|
|
|
|
const TypeArguments* type_arguments = NULL;
|
|
if (type_count > 0) {
|
|
type_arguments = &T.BuildInstantiatedTypeArguments(
|
|
*target_klass, type_count); // read types.
|
|
|
|
if (!(type_arguments->IsNull() || type_arguments->IsInstantiated())) {
|
|
H.ReportError("Type must be constant in const constructor.");
|
|
}
|
|
} else if (target.IsFactory() && type_arguments == NULL) {
|
|
// All factories take a type arguments vector as first argument (independent
|
|
// of whether the class is generic or not).
|
|
type_arguments = &TypeArguments::ZoneHandle(Z, TypeArguments::null());
|
|
}
|
|
return type_arguments;
|
|
}
|
|
|
|
bool StreamingConstantEvaluator::EvaluateBooleanExpressionHere() {
|
|
EvaluateExpression(builder_->ReaderOffset(), false);
|
|
AssertBool();
|
|
return result_.raw() == Bool::True().raw();
|
|
}
|
|
|
|
bool StreamingConstantEvaluator::GetCachedConstant(intptr_t kernel_offset,
|
|
Instance* value) {
|
|
if (builder_ == NULL || builder_->flow_graph_builder_ == NULL) return false;
|
|
|
|
const Function& function = builder_->parsed_function()->function();
|
|
if (function.kind() == RawFunction::kImplicitStaticFinalGetter) {
|
|
// Don't cache constants in initializer expressions. They get
|
|
// evaluated only once.
|
|
return false;
|
|
}
|
|
|
|
bool is_present = false;
|
|
ASSERT(!script_.InVMHeap());
|
|
if (script_.compile_time_constants() == Array::null()) {
|
|
return false;
|
|
}
|
|
KernelConstantsMap constants(script_.compile_time_constants());
|
|
*value ^= constants.GetOrNull(
|
|
kernel_offset + builder_->relative_kernel_offset_, &is_present);
|
|
// Mutator compiler thread may add constants while background compiler
|
|
// is running, and thus change the value of 'compile_time_constants';
|
|
// do not assert that 'compile_time_constants' has not changed.
|
|
constants.Release();
|
|
if (FLAG_compiler_stats && is_present) {
|
|
++H.thread()->compiler_stats()->num_const_cache_hits;
|
|
}
|
|
return is_present;
|
|
}
|
|
|
|
void StreamingConstantEvaluator::CacheConstantValue(intptr_t kernel_offset,
|
|
const Instance& value) {
|
|
ASSERT(Thread::Current()->IsMutatorThread());
|
|
|
|
if (builder_ == NULL || builder_->flow_graph_builder_ == NULL) return;
|
|
|
|
const Function& function = builder_->parsed_function()->function();
|
|
if (function.kind() == RawFunction::kImplicitStaticFinalGetter) {
|
|
// Don't cache constants in initializer expressions. They get
|
|
// evaluated only once.
|
|
return;
|
|
}
|
|
const intptr_t kInitialConstMapSize = 16;
|
|
ASSERT(!script_.InVMHeap());
|
|
if (script_.compile_time_constants() == Array::null()) {
|
|
const Array& array = Array::Handle(
|
|
HashTables::New<KernelConstantsMap>(kInitialConstMapSize, Heap::kNew));
|
|
script_.set_compile_time_constants(array);
|
|
}
|
|
KernelConstantsMap constants(script_.compile_time_constants());
|
|
constants.InsertNewOrGetValue(
|
|
kernel_offset + builder_->relative_kernel_offset_, value);
|
|
script_.set_compile_time_constants(constants.Release());
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::DiscoverEnclosingElements(
|
|
Zone* zone,
|
|
const Function& function,
|
|
Function* outermost_function) {
|
|
// Find out if there is an enclosing kernel class (which will be used to
|
|
// resolve type parameters).
|
|
*outermost_function = function.raw();
|
|
while (outermost_function->parent_function() != Object::null()) {
|
|
*outermost_function = outermost_function->parent_function();
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::ReadUntilFunctionNode() {
|
|
const Tag tag = PeekTag();
|
|
if (tag == kProcedure) {
|
|
ProcedureHelper procedure_helper(this);
|
|
procedure_helper.ReadUntilExcluding(ProcedureHelper::kFunction);
|
|
if (ReadTag() == kNothing) { // read function node tag.
|
|
// Running a procedure without a function node doesn't make sense.
|
|
UNREACHABLE();
|
|
}
|
|
return;
|
|
// Now at start of FunctionNode.
|
|
} else if (tag == kConstructor) {
|
|
ConstructorHelper constructor_helper(this);
|
|
constructor_helper.ReadUntilExcluding(ConstructorHelper::kFunction);
|
|
return;
|
|
// Now at start of FunctionNode.
|
|
// Notice that we also have a list of initializers after that!
|
|
} else if (tag == kFunctionNode) {
|
|
// Already at start of FunctionNode.
|
|
} else {
|
|
UNREACHABLE();
|
|
}
|
|
return;
|
|
}
|
|
|
|
StringIndex StreamingFlowGraphBuilder::GetNameFromVariableDeclaration(
|
|
intptr_t kernel_offset,
|
|
const Function& function) {
|
|
Function& function_or_parent = Function::Handle(Z, function.raw());
|
|
intptr_t function_start_relative =
|
|
function_or_parent.kernel_offset() - kernel_offset;
|
|
while (function_start_relative > 0) {
|
|
function_or_parent = function_or_parent.parent_function();
|
|
function_start_relative =
|
|
function_or_parent.kernel_offset() - kernel_offset;
|
|
}
|
|
TypedData& kernel_data =
|
|
TypedData::Handle(Z, function_or_parent.kernel_data());
|
|
ASSERT(!kernel_data.IsNull());
|
|
|
|
// Temporarily go to the variable declaration, read the name.
|
|
AlternativeReadingScope alt(
|
|
reader_, &kernel_data,
|
|
kernel_offset - function_or_parent.kernel_offset());
|
|
VariableDeclarationHelper helper(this);
|
|
helper.ReadUntilIncluding(VariableDeclarationHelper::kNameIndex);
|
|
return helper.name_index_;
|
|
}
|
|
|
|
FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfStaticFieldInitializer() {
|
|
FieldHelper field_helper(this);
|
|
field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
|
|
ASSERT(field_helper.IsStatic());
|
|
|
|
Tag initializer_tag = ReadTag(); // read first part of initializer.
|
|
if (initializer_tag != kSomething) {
|
|
UNREACHABLE();
|
|
}
|
|
|
|
TargetEntryInstr* normal_entry = flow_graph_builder_->BuildTargetEntry();
|
|
flow_graph_builder_->graph_entry_ = new (Z) GraphEntryInstr(
|
|
*parsed_function(), normal_entry, Compiler::kNoOSRDeoptId);
|
|
|
|
Fragment body(normal_entry);
|
|
body += flow_graph_builder_->CheckStackOverflowInPrologue();
|
|
if (field_helper.IsConst()) {
|
|
// this will (potentially) read the initializer, but reset the position.
|
|
body += Constant(constant_evaluator_.EvaluateExpression(ReaderOffset()));
|
|
SkipExpression(); // read the initializer.
|
|
} else {
|
|
body += BuildExpression(); // read initializer.
|
|
}
|
|
body += Return(TokenPosition::kNoSource);
|
|
|
|
return new (Z)
|
|
FlowGraph(*parsed_function(), flow_graph_builder_->graph_entry_,
|
|
flow_graph_builder_->next_block_id_ - 1);
|
|
}
|
|
|
|
FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFieldAccessor(
|
|
LocalVariable* setter_value) {
|
|
FieldHelper field_helper(this);
|
|
field_helper.ReadUntilIncluding(FieldHelper::kCanonicalName);
|
|
|
|
const Function& function = parsed_function()->function();
|
|
|
|
bool is_setter = function.IsImplicitSetterFunction();
|
|
bool is_method = !function.IsStaticFunction();
|
|
Field& field = Field::ZoneHandle(
|
|
Z, H.LookupFieldByKernelField(field_helper.canonical_name_));
|
|
|
|
TargetEntryInstr* normal_entry = flow_graph_builder_->BuildTargetEntry();
|
|
flow_graph_builder_->graph_entry_ = new (Z) GraphEntryInstr(
|
|
*parsed_function(), normal_entry, Compiler::kNoOSRDeoptId);
|
|
|
|
Fragment body(normal_entry);
|
|
if (is_setter) {
|
|
if (is_method) {
|
|
body += LoadLocal(scopes()->this_variable);
|
|
body += LoadLocal(setter_value);
|
|
body += flow_graph_builder_->StoreInstanceFieldGuarded(field, false);
|
|
} else {
|
|
body += LoadLocal(setter_value);
|
|
body += StoreStaticField(TokenPosition::kNoSource, field);
|
|
}
|
|
body += NullConstant();
|
|
} else if (is_method) {
|
|
body += LoadLocal(scopes()->this_variable);
|
|
body += flow_graph_builder_->LoadField(field);
|
|
} else if (field.is_const()) {
|
|
field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
|
|
Tag initializer_tag = ReadTag(); // read first part of initializer.
|
|
|
|
// If the parser needs to know the value of an uninitialized constant field
|
|
// it will set the value to the transition sentinel (used to detect circular
|
|
// initialization) and then call the implicit getter. Thus, the getter
|
|
// cannot contain the InitStaticField instruction that normal static getters
|
|
// contain because it would detect spurious circular initialization when it
|
|
// checks for the transition sentinel.
|
|
ASSERT(initializer_tag == kSomething);
|
|
body += Constant(constant_evaluator_.EvaluateExpression(ReaderOffset()));
|
|
} else {
|
|
// The field always has an initializer because static fields without
|
|
// initializers are initialized eagerly and do not have implicit getters.
|
|
ASSERT(field.has_initializer());
|
|
body += Constant(field);
|
|
body += flow_graph_builder_->InitStaticField(field);
|
|
body += Constant(field);
|
|
body += LoadStaticField();
|
|
}
|
|
body += Return(TokenPosition::kNoSource);
|
|
|
|
return new (Z)
|
|
FlowGraph(*parsed_function(), flow_graph_builder_->graph_entry_,
|
|
flow_graph_builder_->next_block_id_ - 1);
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SetupDefaultParameterValues() {
|
|
intptr_t optional_parameter_count =
|
|
parsed_function()->function().NumOptionalParameters();
|
|
if (optional_parameter_count > 0) {
|
|
ZoneGrowableArray<const Instance*>* default_values =
|
|
new ZoneGrowableArray<const Instance*>(Z, optional_parameter_count);
|
|
|
|
AlternativeReadingScope alt(reader_);
|
|
FunctionNodeHelper function_node_helper(this);
|
|
function_node_helper.ReadUntilExcluding(
|
|
FunctionNodeHelper::kPositionalParameters);
|
|
|
|
if (parsed_function()->function().HasOptionalNamedParameters()) {
|
|
// List of positional.
|
|
intptr_t list_length = ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
SkipVariableDeclaration(); // read ith variable declaration.
|
|
}
|
|
|
|
// List of named.
|
|
list_length = ReadListLength(); // read list length.
|
|
ASSERT(optional_parameter_count == list_length);
|
|
ASSERT(!parsed_function()->function().HasOptionalPositionalParameters());
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
Instance* default_value;
|
|
|
|
// Read ith variable declaration
|
|
VariableDeclarationHelper helper(this);
|
|
helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer);
|
|
Tag tag = ReadTag(); // read (first part of) initializer.
|
|
if (tag == kSomething) {
|
|
// this will (potentially) read the initializer,
|
|
// but reset the position.
|
|
default_value =
|
|
&constant_evaluator_.EvaluateExpression(ReaderOffset());
|
|
SkipExpression(); // read (actual) initializer.
|
|
} else {
|
|
default_value = &Instance::ZoneHandle(Z, Instance::null());
|
|
}
|
|
default_values->Add(default_value);
|
|
}
|
|
} else {
|
|
// List of positional.
|
|
intptr_t list_length = ReadListLength(); // read list length.
|
|
ASSERT(list_length == function_node_helper.required_parameter_count_ +
|
|
optional_parameter_count);
|
|
ASSERT(parsed_function()->function().HasOptionalPositionalParameters());
|
|
for (intptr_t i = 0; i < function_node_helper.required_parameter_count_;
|
|
++i) {
|
|
SkipVariableDeclaration(); // read ith variable declaration.
|
|
}
|
|
for (intptr_t i = 0; i < optional_parameter_count; ++i) {
|
|
Instance* default_value;
|
|
|
|
// Read ith variable declaration
|
|
VariableDeclarationHelper helper(this);
|
|
helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer);
|
|
Tag tag = ReadTag(); // read (first part of) initializer.
|
|
if (tag == kSomething) {
|
|
// this will (potentially) read the initializer,
|
|
// but reset the position.
|
|
default_value =
|
|
&constant_evaluator_.EvaluateExpression(ReaderOffset());
|
|
SkipExpression(); // read (actual) initializer.
|
|
} else {
|
|
default_value = &Instance::ZoneHandle(Z, Instance::null());
|
|
}
|
|
default_values->Add(default_value);
|
|
}
|
|
|
|
// List of named.
|
|
list_length = ReadListLength(); // read list length.
|
|
ASSERT(list_length == 0);
|
|
}
|
|
parsed_function()->set_default_parameter_values(default_values);
|
|
}
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildFieldInitializer(
|
|
NameIndex canonical_name) {
|
|
Field& field =
|
|
Field::ZoneHandle(Z, H.LookupFieldByKernelField(canonical_name));
|
|
if (PeekTag() == kNullLiteral) {
|
|
SkipExpression(); // read past the null literal.
|
|
field.RecordStore(Object::null_object());
|
|
return Fragment();
|
|
}
|
|
|
|
Fragment instructions;
|
|
instructions += LoadLocal(scopes()->this_variable);
|
|
instructions += BuildExpression();
|
|
instructions += flow_graph_builder_->StoreInstanceFieldGuarded(field, true);
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildInitializers(
|
|
const Class& parent_class) {
|
|
Fragment instructions;
|
|
|
|
// Start by getting the position of the constructors initializer.
|
|
intptr_t initializers_offset = -1;
|
|
{
|
|
AlternativeReadingScope alt(reader_);
|
|
SkipFunctionNode(); // read constructors function node.
|
|
initializers_offset = ReaderOffset();
|
|
}
|
|
|
|
// These come from:
|
|
// class A {
|
|
// var x = (expr);
|
|
// }
|
|
// We don't want to do that when this is a Redirecting Constructors though
|
|
// (i.e. has a single initializer being of type kRedirectingInitializer).
|
|
bool is_redirecting_constructor = false;
|
|
{
|
|
AlternativeReadingScope alt(reader_, initializers_offset);
|
|
intptr_t list_length = ReadListLength(); // read initializers list length.
|
|
bool no_field_initializers = true;
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
if (PeekTag() == kRedirectingInitializer) {
|
|
is_redirecting_constructor = true;
|
|
} else if (PeekTag() == kFieldInitializer) {
|
|
no_field_initializers = false;
|
|
}
|
|
SkipInitializer();
|
|
}
|
|
ASSERT(is_redirecting_constructor ? no_field_initializers : true);
|
|
}
|
|
|
|
if (!is_redirecting_constructor) {
|
|
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.kernel_data());
|
|
ASSERT(!kernel_data.IsNull());
|
|
AlternativeReadingScope alt(reader_, &kernel_data, 0);
|
|
intptr_t saved_relative_kernel_offset_ = relative_kernel_offset_;
|
|
relative_kernel_offset_ = class_field.kernel_offset();
|
|
FieldHelper field_helper(this);
|
|
field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
|
|
Tag initializer_tag = ReadTag(); // read first part of initializer.
|
|
if (initializer_tag == kSomething) {
|
|
EnterScope(class_field.kernel_offset());
|
|
instructions += BuildFieldInitializer(
|
|
field_helper.canonical_name_); // read initializer.
|
|
ExitScope(class_field.kernel_offset());
|
|
}
|
|
relative_kernel_offset_ = saved_relative_kernel_offset_;
|
|
}
|
|
}
|
|
}
|
|
|
|
// These to come from:
|
|
// class A {
|
|
// var x;
|
|
// var y;
|
|
// A(this.x) : super(expr), y = (expr);
|
|
// }
|
|
{
|
|
AlternativeReadingScope alt(reader_, initializers_offset);
|
|
intptr_t list_length = ReadListLength(); // read initializers list length.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
Tag tag = ReadTag();
|
|
ReadByte(); // read isSynthetic flag.
|
|
switch (tag) {
|
|
case kInvalidInitializer:
|
|
UNIMPLEMENTED();
|
|
return Fragment();
|
|
case kFieldInitializer: {
|
|
NameIndex canonical_name =
|
|
ReadCanonicalNameReference(); // read field_reference.
|
|
instructions += BuildFieldInitializer(canonical_name); // read value.
|
|
break;
|
|
}
|
|
case kSuperInitializer: {
|
|
NameIndex canonical_target =
|
|
ReadCanonicalNameReference(); // read target_reference.
|
|
|
|
instructions += LoadLocal(scopes()->this_variable);
|
|
instructions += PushArgument();
|
|
|
|
// TODO(jensj): ASSERT(init->arguments()->types().length() == 0);
|
|
Array& argument_names = Array::ZoneHandle(Z);
|
|
intptr_t argument_count;
|
|
instructions += BuildArguments(&argument_names,
|
|
&argument_count); // read arguments.
|
|
argument_count += 1;
|
|
|
|
const Function& target = Function::ZoneHandle(
|
|
Z, H.LookupConstructorByKernelConstructor(canonical_target));
|
|
instructions += StaticCall(TokenPosition::kNoSource, target,
|
|
argument_count, argument_names);
|
|
instructions += Drop();
|
|
break;
|
|
}
|
|
case kRedirectingInitializer: {
|
|
NameIndex canonical_target =
|
|
ReadCanonicalNameReference(); // read target_reference.
|
|
|
|
instructions += LoadLocal(scopes()->this_variable);
|
|
instructions += PushArgument();
|
|
|
|
// TODO(jensj): ASSERT(init->arguments()->types().length() == 0);
|
|
Array& argument_names = Array::ZoneHandle(Z);
|
|
intptr_t argument_count;
|
|
instructions += BuildArguments(&argument_names,
|
|
&argument_count); // read arguments.
|
|
argument_count += 1;
|
|
|
|
const Function& target = Function::ZoneHandle(
|
|
Z, H.LookupConstructorByKernelConstructor(canonical_target));
|
|
instructions += StaticCall(TokenPosition::kNoSource, target,
|
|
argument_count, argument_names);
|
|
instructions += Drop();
|
|
break;
|
|
}
|
|
case kLocalInitializer: {
|
|
// The other initializers following this one might read the variable.
|
|
// This is used e.g. for evaluating the arguments to a super call
|
|
// first, run normal field initializers next and then make the actual
|
|
// super call:
|
|
//
|
|
// The frontend converts
|
|
//
|
|
// class A {
|
|
// var x;
|
|
// A(a, b) : super(a + b), x = 2*b {}
|
|
// }
|
|
//
|
|
// to
|
|
//
|
|
// class A {
|
|
// var x;
|
|
// A(a, b) : tmp = a + b, x = 2*b, super(tmp) {}
|
|
// }
|
|
//
|
|
// (This is strictly speaking not what one should do in terms of the
|
|
// specification but that is how it is currently implemented.)
|
|
LocalVariable* variable =
|
|
LookupVariable(ReaderOffset() + relative_kernel_offset_);
|
|
|
|
// Variable declaration
|
|
VariableDeclarationHelper helper(this);
|
|
helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer);
|
|
ASSERT(!helper.IsConst());
|
|
Tag tag = ReadTag(); // read (first part of) initializer.
|
|
if (tag != kSomething) {
|
|
UNREACHABLE();
|
|
}
|
|
|
|
instructions += BuildExpression(); // read initializer.
|
|
instructions += StoreLocal(TokenPosition::kNoSource, variable);
|
|
instructions += Drop();
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
}
|
|
return instructions;
|
|
}
|
|
|
|
FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfImplicitClosureFunction(
|
|
const Function& function) {
|
|
const Function& target = Function::ZoneHandle(Z, function.parent_function());
|
|
|
|
TargetEntryInstr* normal_entry = flow_graph_builder_->BuildTargetEntry();
|
|
flow_graph_builder_->graph_entry_ = new (Z) GraphEntryInstr(
|
|
*parsed_function(), normal_entry, Compiler::kNoOSRDeoptId);
|
|
SetupDefaultParameterValues();
|
|
|
|
Fragment body(normal_entry);
|
|
body += flow_graph_builder_->CheckStackOverflowInPrologue();
|
|
|
|
// Load all the arguments.
|
|
if (!target.is_static()) {
|
|
// The context has a fixed shape: a single variable which is the
|
|
// closed-over receiver.
|
|
body += LoadLocal(parsed_function()->current_context_var());
|
|
body += flow_graph_builder_->LoadField(Context::variable_offset(0));
|
|
body += PushArgument();
|
|
}
|
|
|
|
FunctionNodeHelper function_node_helper(this);
|
|
function_node_helper.ReadUntilExcluding(
|
|
FunctionNodeHelper::kPositionalParameters);
|
|
|
|
// Positional.
|
|
intptr_t positional_argument_count = ReadListLength();
|
|
for (intptr_t i = 0; i < positional_argument_count; ++i) {
|
|
body += LoadLocal(LookupVariable(
|
|
ReaderOffset() + relative_kernel_offset_)); // ith variable offset.
|
|
body += PushArgument();
|
|
SkipVariableDeclaration(); // read ith variable.
|
|
}
|
|
|
|
// Named.
|
|
intptr_t named_argument_count = ReadListLength();
|
|
Array& argument_names = Array::ZoneHandle(Z);
|
|
if (named_argument_count > 0) {
|
|
argument_names = Array::New(named_argument_count);
|
|
for (intptr_t i = 0; i < named_argument_count; ++i) {
|
|
// ith variable offset.
|
|
body +=
|
|
LoadLocal(LookupVariable(ReaderOffset() + relative_kernel_offset_));
|
|
body += PushArgument();
|
|
|
|
// read ith variable.
|
|
VariableDeclarationHelper helper(this);
|
|
helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd);
|
|
|
|
argument_names.SetAt(i, H.DartSymbol(helper.name_index_));
|
|
}
|
|
}
|
|
|
|
// Forward them to the target.
|
|
intptr_t argument_count = positional_argument_count + named_argument_count;
|
|
if (!target.is_static()) ++argument_count;
|
|
body += StaticCall(TokenPosition::kNoSource, target, argument_count,
|
|
argument_names);
|
|
|
|
// Return the result.
|
|
body += Return(function_node_helper.end_position_);
|
|
|
|
return new (Z)
|
|
FlowGraph(*parsed_function(), flow_graph_builder_->graph_entry_,
|
|
flow_graph_builder_->next_block_id_ - 1);
|
|
}
|
|
|
|
LocalVariable* StreamingFlowGraphBuilder::LookupParameterDirect(
|
|
intptr_t kernel_offset,
|
|
intptr_t parameter_index) {
|
|
LocalVariable* var = LookupVariable(kernel_offset);
|
|
LocalVariable* parameter =
|
|
new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
|
|
Symbols::TempParam(), var->type());
|
|
parameter->set_index(parameter_index);
|
|
if (var->is_captured()) parameter->set_is_captured_parameter(true);
|
|
return parameter;
|
|
}
|
|
// This method follows the logic of
|
|
// StreamingFlowGraphBuilder::BuildGraphOfImplicitClosureFunction. For
|
|
// additional details on converted closure functions, please, see the comment on
|
|
// the method Function::ConvertedClosureFunction.
|
|
FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfConvertedClosureFunction(
|
|
const Function& function) {
|
|
const Function& target = Function::ZoneHandle(Z, function.parent_function());
|
|
|
|
TargetEntryInstr* normal_entry = flow_graph_builder_->BuildTargetEntry();
|
|
flow_graph_builder_->graph_entry_ = new (Z) GraphEntryInstr(
|
|
*parsed_function(), normal_entry, Compiler::kNoOSRDeoptId);
|
|
SetupDefaultParameterValues();
|
|
|
|
Fragment body(normal_entry);
|
|
body += flow_graph_builder_->CheckStackOverflowInPrologue();
|
|
body += NullConstant();
|
|
LocalVariable* result = MakeTemporary();
|
|
|
|
// Load all the arguments.
|
|
ASSERT(target.is_static());
|
|
|
|
// TODO(30455): Kernel generic methods undone. Since the frontend can't yet
|
|
// emit generic methods into kernel, all type parameters to the target must
|
|
// come from the context. When generic methods are fully supported, we will
|
|
// need to get the type arguments provided by the caller and append them to
|
|
// the captured type arguments via 'prependTypeArguments'.
|
|
|
|
FunctionNodeHelper function_node_helper(this);
|
|
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
|
|
intptr_t type_param_count = PeekUInt();
|
|
function_node_helper.ReadUntilExcluding(
|
|
FunctionNodeHelper::kPositionalParameters);
|
|
|
|
// Positional.
|
|
const intptr_t positional_argument_count = ReadListLength();
|
|
|
|
// The first argument is the instance of the closure class. For converted
|
|
// closures its context field contains the context vector that is used by the
|
|
// converted top-level function (target) explicitly and that should be passed
|
|
// to that function as the first parameter.
|
|
intptr_t parameter_index = parsed_function()->first_parameter_index();
|
|
LocalVariable* parameter = LookupParameterDirect(
|
|
ReaderOffset() + relative_kernel_offset_, parameter_index--);
|
|
body += LoadLocal(parameter); // 0th variable offset.
|
|
body += flow_graph_builder_->LoadField(Closure::context_offset());
|
|
LocalVariable* context = MakeTemporary();
|
|
|
|
if (type_param_count > 0) {
|
|
body += LoadLocal(context);
|
|
body += flow_graph_builder_->LoadField(Context::variable_offset(0));
|
|
body += PushArgument();
|
|
}
|
|
|
|
body += LoadLocal(context);
|
|
body += PushArgument();
|
|
SkipVariableDeclaration(); // read 0th variable.
|
|
|
|
// The rest of the parameters are the same for the method of the Closure class
|
|
// being invoked and the top-level function (target).
|
|
for (intptr_t i = 1; i < positional_argument_count; i++) {
|
|
LocalVariable* parameter = LookupParameterDirect(
|
|
ReaderOffset() + relative_kernel_offset_, parameter_index--);
|
|
body += LoadLocal(parameter); // ith variable offset.
|
|
body += PushArgument();
|
|
SkipVariableDeclaration(); // read ith variable.
|
|
}
|
|
|
|
// Named.
|
|
const intptr_t named_argument_count = ReadListLength();
|
|
Array& argument_names = Array::ZoneHandle(Z);
|
|
if (named_argument_count > 0) {
|
|
argument_names = Array::New(named_argument_count);
|
|
for (intptr_t i = 0; i < named_argument_count; i++) {
|
|
// ith variable offset.
|
|
body +=
|
|
LoadLocal(LookupVariable(ReaderOffset() + relative_kernel_offset_));
|
|
body += PushArgument();
|
|
|
|
// read ith variable.
|
|
VariableDeclarationHelper helper(this);
|
|
helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd);
|
|
|
|
argument_names.SetAt(i, H.DartSymbol(helper.name_index_));
|
|
}
|
|
}
|
|
|
|
// Forward them to the target.
|
|
intptr_t argument_count = positional_argument_count + named_argument_count;
|
|
if (type_param_count) ++argument_count;
|
|
body += StaticCall(TokenPosition::kNoSource, target, argument_count,
|
|
argument_names, type_param_count);
|
|
|
|
// Return the result.
|
|
body += StoreLocal(TokenPosition::kNoSource, result);
|
|
body += Drop();
|
|
body += Drop();
|
|
body += Return(function_node_helper.end_position_);
|
|
|
|
return new (Z)
|
|
FlowGraph(*parsed_function(), flow_graph_builder_->graph_entry_,
|
|
flow_graph_builder_->next_block_id_ - 1);
|
|
}
|
|
|
|
FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFunction(bool constructor) {
|
|
const Function& dart_function = parsed_function()->function();
|
|
TargetEntryInstr* normal_entry = flow_graph_builder_->BuildTargetEntry();
|
|
flow_graph_builder_->graph_entry_ = new (Z) GraphEntryInstr(
|
|
*parsed_function(), normal_entry, flow_graph_builder_->osr_id_);
|
|
|
|
SetupDefaultParameterValues();
|
|
|
|
Fragment body;
|
|
if (!dart_function.is_native())
|
|
body += flow_graph_builder_->CheckStackOverflowInPrologue();
|
|
intptr_t context_size =
|
|
parsed_function()->node_sequence()->scope()->num_context_variables();
|
|
if (context_size > 0) {
|
|
body += flow_graph_builder_->PushContext(context_size);
|
|
LocalVariable* context = MakeTemporary();
|
|
|
|
// Copy captured parameters from the stack into the context.
|
|
LocalScope* scope = parsed_function()->node_sequence()->scope();
|
|
intptr_t parameter_count = dart_function.NumParameters();
|
|
intptr_t parameter_index = parsed_function()->first_parameter_index();
|
|
|
|
for (intptr_t i = 0; i < parameter_count; ++i, --parameter_index) {
|
|
LocalVariable* variable = scope->VariableAt(i);
|
|
if (variable->is_captured()) {
|
|
// There is no LocalVariable describing the on-stack parameter so
|
|
// create one directly and use the same type.
|
|
LocalVariable* parameter = new (Z)
|
|
LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
|
|
Symbols::TempParam(), variable->type());
|
|
parameter->set_index(parameter_index);
|
|
// Mark the stack variable so it will be ignored by the code for
|
|
// try/catch.
|
|
parameter->set_is_captured_parameter(true);
|
|
|
|
// Copy the parameter from the stack to the context. Overwrite it
|
|
// with a null constant on the stack so the original value is
|
|
// eligible for garbage collection.
|
|
body += LoadLocal(context);
|
|
body += LoadLocal(parameter);
|
|
body += flow_graph_builder_->StoreInstanceField(
|
|
TokenPosition::kNoSource,
|
|
Context::variable_offset(variable->index()));
|
|
body += NullConstant();
|
|
body += StoreLocal(TokenPosition::kNoSource, parameter);
|
|
body += Drop();
|
|
}
|
|
}
|
|
body += Drop(); // The context.
|
|
}
|
|
if (constructor) {
|
|
// TODO(27590): Currently the [VariableDeclaration]s from the
|
|
// initializers will be visible inside the entire body of the constructor.
|
|
// We should make a separate scope for them.
|
|
body += BuildInitializers(Class::Handle(Z, dart_function.Owner()));
|
|
}
|
|
|
|
FunctionNodeHelper function_node_helper(this);
|
|
function_node_helper.ReadUntilExcluding(
|
|
FunctionNodeHelper::kPositionalParameters);
|
|
intptr_t first_parameter_offset = -1;
|
|
{
|
|
AlternativeReadingScope alt(reader_);
|
|
intptr_t list_length = ReadListLength(); // read number of positionals.
|
|
if (list_length > 0) {
|
|
first_parameter_offset = ReaderOffset() + relative_kernel_offset_;
|
|
}
|
|
}
|
|
// Current position: About to read list of positionals.
|
|
|
|
// The specification defines the result of `a == b` to be:
|
|
//
|
|
// a) if either side is `null` then the result is `identical(a, b)`.
|
|
// b) else the result is `a.operator==(b)`
|
|
//
|
|
// For user-defined implementations of `operator==` we need therefore
|
|
// implement the handling of a).
|
|
//
|
|
// The default `operator==` implementation in `Object` is implemented in terms
|
|
// of identical (which we assume here!) which means that case a) is actually
|
|
// included in b). So we just use the normal implementation in the body.
|
|
if ((dart_function.NumParameters() == 2) &&
|
|
(dart_function.name() == Symbols::EqualOperator().raw()) &&
|
|
(dart_function.Owner() != I->object_store()->object_class())) {
|
|
LocalVariable* parameter = LookupVariable(first_parameter_offset);
|
|
|
|
TargetEntryInstr* null_entry;
|
|
TargetEntryInstr* non_null_entry;
|
|
|
|
body += LoadLocal(parameter);
|
|
body += BranchIfNull(&null_entry, &non_null_entry);
|
|
|
|
// The argument was `null` and the receiver is not the null class (we only
|
|
// go into this branch for user-defined == operators) so we can return
|
|
// false.
|
|
Fragment null_fragment(null_entry);
|
|
null_fragment += Constant(Bool::False());
|
|
null_fragment += Return(dart_function.end_token_pos());
|
|
|
|
body = Fragment(body.entry, non_null_entry);
|
|
}
|
|
|
|
// If we run in checked mode, we have to check the type of the passed
|
|
// arguments.
|
|
if (I->type_checks()) {
|
|
// Positional.
|
|
intptr_t list_length = ReadListLength();
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
// ith variable offset.
|
|
body +=
|
|
LoadLocal(LookupVariable(ReaderOffset() + relative_kernel_offset_));
|
|
body += CheckVariableTypeInCheckedMode(ReaderOffset() +
|
|
relative_kernel_offset_);
|
|
body += Drop();
|
|
SkipVariableDeclaration(); // read ith variable.
|
|
}
|
|
|
|
// Named.
|
|
list_length = ReadListLength();
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
// ith variable offset.
|
|
body +=
|
|
LoadLocal(LookupVariable(ReaderOffset() + relative_kernel_offset_));
|
|
body += CheckVariableTypeInCheckedMode(ReaderOffset() +
|
|
relative_kernel_offset_);
|
|
body += Drop();
|
|
SkipVariableDeclaration(); // read ith variable.
|
|
}
|
|
|
|
function_node_helper.SetJustRead(FunctionNodeHelper::kNamedParameters);
|
|
}
|
|
|
|
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kBody);
|
|
|
|
bool has_body = ReadTag() == kSomething; // read first part of body.
|
|
|
|
if (dart_function.is_native()) {
|
|
body += flow_graph_builder_->NativeFunctionBody(first_parameter_offset,
|
|
dart_function);
|
|
} else if (has_body) {
|
|
body += BuildStatement(); // read body.
|
|
}
|
|
if (body.is_open()) {
|
|
body += NullConstant();
|
|
body += Return(dart_function.end_token_pos());
|
|
}
|
|
|
|
// If functions body contains any yield points build switch statement that
|
|
// selects a continuation point based on the value of :await_jump_var.
|
|
if (!yield_continuations().is_empty()) {
|
|
// The code we are building will be executed right after we enter
|
|
// the function and before any nested contexts are allocated.
|
|
// Reset current context_depth_ to match this.
|
|
const intptr_t current_context_depth = flow_graph_builder_->context_depth_;
|
|
flow_graph_builder_->context_depth_ =
|
|
scopes()->yield_jump_variable->owner()->context_level();
|
|
|
|
// Prepend an entry corresponding to normal entry to the function.
|
|
yield_continuations().InsertAt(
|
|
0, YieldContinuation(new (Z) DropTempsInstr(0, NULL),
|
|
CatchClauseNode::kInvalidTryIndex));
|
|
yield_continuations()[0].entry->LinkTo(body.entry);
|
|
|
|
// Build a switch statement.
|
|
Fragment dispatch;
|
|
|
|
// Load :await_jump_var into a temporary.
|
|
dispatch += LoadLocal(scopes()->yield_jump_variable);
|
|
dispatch += StoreLocal(TokenPosition::kNoSource, scopes()->switch_variable);
|
|
dispatch += Drop();
|
|
|
|
BlockEntryInstr* block = NULL;
|
|
for (intptr_t i = 0; i < yield_continuations().length(); i++) {
|
|
if (i == 1) {
|
|
// This is not a normal entry but a resumption. Restore
|
|
// :current_context_var from :await_ctx_var.
|
|
// Note: after this point context_depth_ does not match current context
|
|
// depth so we should not access any local variables anymore.
|
|
dispatch += LoadLocal(scopes()->yield_context_variable);
|
|
dispatch += StoreLocal(TokenPosition::kNoSource,
|
|
parsed_function()->current_context_var());
|
|
dispatch += Drop();
|
|
}
|
|
if (i == (yield_continuations().length() - 1)) {
|
|
// We reached the last possility, no need to build more ifs.
|
|
// Continue to the last continuation.
|
|
// Note: continuations start with nop DropTemps instruction
|
|
// which acts like an anchor, so we need to skip it.
|
|
block->set_try_index(yield_continuations()[i].try_index);
|
|
dispatch <<= yield_continuations()[i].entry->next();
|
|
break;
|
|
}
|
|
|
|
// Build comparison:
|
|
//
|
|
// if (:await_ctx_var == i) {
|
|
// -> yield_continuations()[i]
|
|
// } else ...
|
|
//
|
|
TargetEntryInstr* then;
|
|
TargetEntryInstr* otherwise;
|
|
dispatch += LoadLocal(scopes()->switch_variable);
|
|
dispatch += IntConstant(i);
|
|
dispatch += flow_graph_builder_->BranchIfStrictEqual(&then, &otherwise);
|
|
|
|
// True branch is linked to appropriate continuation point.
|
|
// Note: continuations start with nop DropTemps instruction
|
|
// which acts like an anchor, so we need to skip it.
|
|
then->LinkTo(yield_continuations()[i].entry->next());
|
|
then->set_try_index(yield_continuations()[i].try_index);
|
|
// False branch will contain the next comparison.
|
|
dispatch = Fragment(dispatch.entry, otherwise);
|
|
block = otherwise;
|
|
}
|
|
body = dispatch;
|
|
|
|
flow_graph_builder_->context_depth_ = current_context_depth;
|
|
}
|
|
|
|
if (FLAG_causal_async_stacks &&
|
|
(dart_function.IsAsyncClosure() || dart_function.IsAsyncGenClosure())) {
|
|
// The code we are building will be executed right after we enter
|
|
// the function and before any nested contexts are allocated.
|
|
// Reset current context_depth_ to match this.
|
|
const intptr_t current_context_depth = flow_graph_builder_->context_depth_;
|
|
flow_graph_builder_->context_depth_ =
|
|
scopes()->yield_jump_variable->owner()->context_level();
|
|
|
|
Fragment instructions;
|
|
LocalScope* scope = parsed_function()->node_sequence()->scope();
|
|
|
|
const Function& target = Function::ZoneHandle(
|
|
Z, I->object_store()->async_set_thread_stack_trace());
|
|
ASSERT(!target.IsNull());
|
|
|
|
// Fetch and load :async_stack_trace
|
|
LocalVariable* async_stack_trace_var =
|
|
scope->LookupVariable(Symbols::AsyncStackTraceVar(), false);
|
|
ASSERT((async_stack_trace_var != NULL) &&
|
|
async_stack_trace_var->is_captured());
|
|
instructions += LoadLocal(async_stack_trace_var);
|
|
instructions += PushArgument();
|
|
|
|
// Call _asyncSetThreadStackTrace
|
|
instructions += StaticCall(TokenPosition::kNoSource, target, 1);
|
|
instructions += Drop();
|
|
|
|
// TODO(29737): This sequence should be generated in order.
|
|
body = instructions + body;
|
|
flow_graph_builder_->context_depth_ = current_context_depth;
|
|
}
|
|
|
|
if (NeedsDebugStepCheck(dart_function, function_node_helper.position_)) {
|
|
const intptr_t current_context_depth = flow_graph_builder_->context_depth_;
|
|
flow_graph_builder_->context_depth_ = 0;
|
|
// If a switch was added above: Start the switch by injecting a debuggable
|
|
// safepoint so stepping over an await works.
|
|
// If not, still start the body with a debuggable safepoint to ensure
|
|
// breaking on a method always happens, even if there are no
|
|
// assignments/calls/runtimecalls in the first basic block.
|
|
// Place this check at the last parameter to ensure parameters
|
|
// are in scope in the debugger at method entry.
|
|
const int parameter_count = dart_function.NumParameters();
|
|
TokenPosition check_pos = TokenPosition::kNoSource;
|
|
if (parameter_count > 0) {
|
|
LocalScope* scope = parsed_function()->node_sequence()->scope();
|
|
const LocalVariable& parameter = *scope->VariableAt(parameter_count - 1);
|
|
check_pos = parameter.token_pos();
|
|
}
|
|
if (!check_pos.IsDebugPause()) {
|
|
// No parameters or synthetic parameters.
|
|
check_pos = function_node_helper.position_;
|
|
ASSERT(check_pos.IsDebugPause());
|
|
}
|
|
|
|
// TODO(29737): This sequence should be generated in order.
|
|
body = DebugStepCheck(check_pos) + body;
|
|
flow_graph_builder_->context_depth_ = current_context_depth;
|
|
}
|
|
|
|
normal_entry->LinkTo(body.entry);
|
|
|
|
GraphEntryInstr* graph_entry = flow_graph_builder_->graph_entry_;
|
|
// When compiling for OSR, use a depth first search to find the OSR
|
|
// entry and make graph entry jump to it instead of normal entry.
|
|
// Catch entries are always considered reachable, even if they
|
|
// become unreachable after OSR.
|
|
if (flow_graph_builder_->osr_id_ != Compiler::kNoOSRDeoptId) {
|
|
graph_entry->RelinkToOsrEntry(Z, flow_graph_builder_->next_block_id_);
|
|
}
|
|
return new (Z) FlowGraph(*parsed_function(), graph_entry,
|
|
flow_graph_builder_->next_block_id_ - 1);
|
|
}
|
|
|
|
FlowGraph* StreamingFlowGraphBuilder::BuildGraph(intptr_t kernel_offset) {
|
|
const Function& function = parsed_function()->function();
|
|
|
|
// Setup a [ActiveClassScope] and a [ActiveMemberScope] which will be used
|
|
// e.g. for type translation.
|
|
const Class& klass =
|
|
Class::Handle(zone_, parsed_function()->function().Owner());
|
|
|
|
Function& outermost_function = Function::Handle(Z);
|
|
DiscoverEnclosingElements(Z, function, &outermost_function);
|
|
|
|
ActiveClassScope active_class_scope(active_class(), &klass);
|
|
ActiveMemberScope active_member(active_class(), &outermost_function);
|
|
|
|
// The IR builder will create its own local variables and scopes, and it
|
|
// will not need an AST. The code generator will assume that there is a
|
|
// local variable stack slot allocated for the current context and (I
|
|
// think) that the runtime will expect it to be at a fixed offset which
|
|
// requires allocating an unused expression temporary variable.
|
|
set_scopes(parsed_function()->EnsureKernelScopes());
|
|
|
|
SetOffset(kernel_offset);
|
|
|
|
switch (function.kind()) {
|
|
case RawFunction::kClosureFunction:
|
|
case RawFunction::kImplicitClosureFunction:
|
|
case RawFunction::kConvertedClosureFunction:
|
|
case RawFunction::kRegularFunction:
|
|
case RawFunction::kGetterFunction:
|
|
case RawFunction::kSetterFunction: {
|
|
ReadUntilFunctionNode(); // read until function node.
|
|
if (function.IsImplicitClosureFunction()) {
|
|
return BuildGraphOfImplicitClosureFunction(function);
|
|
} else if (function.IsConvertedClosureFunction()) {
|
|
return BuildGraphOfConvertedClosureFunction(function);
|
|
}
|
|
return BuildGraphOfFunction(false);
|
|
}
|
|
case RawFunction::kConstructor: {
|
|
ReadUntilFunctionNode(); // read until function node.
|
|
return BuildGraphOfFunction(!function.IsFactory());
|
|
}
|
|
case RawFunction::kImplicitGetter:
|
|
case RawFunction::kImplicitStaticFinalGetter:
|
|
case RawFunction::kImplicitSetter: {
|
|
return IsStaticInitializer(function, Z)
|
|
? BuildGraphOfStaticFieldInitializer()
|
|
: BuildGraphOfFieldAccessor(scopes()->setter_value);
|
|
}
|
|
case RawFunction::kMethodExtractor:
|
|
return flow_graph_builder_->BuildGraphOfMethodExtractor(function);
|
|
case RawFunction::kNoSuchMethodDispatcher:
|
|
return flow_graph_builder_->BuildGraphOfNoSuchMethodDispatcher(function);
|
|
case RawFunction::kInvokeFieldDispatcher:
|
|
return flow_graph_builder_->BuildGraphOfInvokeFieldDispatcher(function);
|
|
case RawFunction::kSignatureFunction:
|
|
case RawFunction::kIrregexpFunction:
|
|
break;
|
|
}
|
|
UNREACHABLE();
|
|
return NULL;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildStatementAt(intptr_t kernel_offset) {
|
|
SetOffset(kernel_offset);
|
|
return BuildStatement(); // read statement.
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildExpression(TokenPosition* position) {
|
|
uint8_t payload = 0;
|
|
Tag tag = ReadTag(&payload); // read tag.
|
|
switch (tag) {
|
|
case kInvalidExpression:
|
|
return BuildInvalidExpression(position);
|
|
case kVariableGet:
|
|
return BuildVariableGet(position);
|
|
case kSpecializedVariableGet:
|
|
return BuildVariableGet(payload, position);
|
|
case kVariableSet:
|
|
return BuildVariableSet(position);
|
|
case kSpecializedVariableSet:
|
|
return BuildVariableSet(payload, position);
|
|
case kPropertyGet:
|
|
return BuildPropertyGet(position);
|
|
case kPropertySet:
|
|
return BuildPropertySet(position);
|
|
case kDirectPropertyGet:
|
|
return BuildDirectPropertyGet(position);
|
|
case kDirectPropertySet:
|
|
return BuildDirectPropertySet(position);
|
|
case kStaticGet:
|
|
return BuildStaticGet(position);
|
|
case kStaticSet:
|
|
return BuildStaticSet(position);
|
|
case kMethodInvocation:
|
|
return BuildMethodInvocation(position);
|
|
case kDirectMethodInvocation:
|
|
return BuildDirectMethodInvocation(position);
|
|
case kStaticInvocation:
|
|
return BuildStaticInvocation(false, position);
|
|
case kConstStaticInvocation:
|
|
return BuildStaticInvocation(true, position);
|
|
case kConstructorInvocation:
|
|
return BuildConstructorInvocation(false, position);
|
|
case kConstConstructorInvocation:
|
|
return BuildConstructorInvocation(true, position);
|
|
case kNot:
|
|
return BuildNot(position);
|
|
case kLogicalExpression:
|
|
return BuildLogicalExpression(position);
|
|
case kConditionalExpression:
|
|
return BuildConditionalExpression(position);
|
|
case kStringConcatenation:
|
|
return BuildStringConcatenation(position);
|
|
case kIsExpression:
|
|
return BuildIsExpression(position);
|
|
case kAsExpression:
|
|
return BuildAsExpression(position);
|
|
case kSymbolLiteral:
|
|
return BuildSymbolLiteral(position);
|
|
case kTypeLiteral:
|
|
return BuildTypeLiteral(position);
|
|
case kThisExpression:
|
|
return BuildThisExpression(position);
|
|
case kRethrow:
|
|
return BuildRethrow(position);
|
|
case kThrow:
|
|
return BuildThrow(position);
|
|
case kListLiteral:
|
|
return BuildListLiteral(false, position);
|
|
case kConstListLiteral:
|
|
return BuildListLiteral(true, position);
|
|
case kMapLiteral:
|
|
return BuildMapLiteral(false, position);
|
|
case kConstMapLiteral:
|
|
return BuildMapLiteral(true, position);
|
|
case kFunctionExpression:
|
|
return BuildFunctionExpression();
|
|
case kLet:
|
|
return BuildLet(position);
|
|
case kBigIntLiteral:
|
|
return BuildBigIntLiteral(position);
|
|
case kStringLiteral:
|
|
return BuildStringLiteral(position);
|
|
case kSpecialIntLiteral:
|
|
return BuildIntLiteral(payload, position);
|
|
case kNegativeIntLiteral:
|
|
return BuildIntLiteral(true, position);
|
|
case kPositiveIntLiteral:
|
|
return BuildIntLiteral(false, position);
|
|
case kDoubleLiteral:
|
|
return BuildDoubleLiteral(position);
|
|
case kTrueLiteral:
|
|
return BuildBoolLiteral(true, position);
|
|
case kFalseLiteral:
|
|
return BuildBoolLiteral(false, position);
|
|
case kNullLiteral:
|
|
return BuildNullLiteral(position);
|
|
case kVectorCreation:
|
|
return BuildVectorCreation(position);
|
|
case kVectorGet:
|
|
return BuildVectorGet(position);
|
|
case kVectorSet:
|
|
return BuildVectorSet(position);
|
|
case kVectorCopy:
|
|
return BuildVectorCopy(position);
|
|
case kClosureCreation:
|
|
return BuildClosureCreation(position);
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
|
|
return Fragment();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildStatement() {
|
|
Tag tag = ReadTag(); // read tag.
|
|
switch (tag) {
|
|
case kInvalidStatement:
|
|
return BuildInvalidStatement();
|
|
case kExpressionStatement:
|
|
return BuildExpressionStatement();
|
|
case kBlock:
|
|
return BuildBlock();
|
|
case kEmptyStatement:
|
|
return BuildEmptyStatement();
|
|
case kAssertStatement:
|
|
return BuildAssertStatement();
|
|
case kLabeledStatement:
|
|
return BuildLabeledStatement();
|
|
case kBreakStatement:
|
|
return BuildBreakStatement();
|
|
case kWhileStatement:
|
|
return BuildWhileStatement();
|
|
case kDoStatement:
|
|
return BuildDoStatement();
|
|
case kForStatement:
|
|
return BuildForStatement();
|
|
case kForInStatement:
|
|
return BuildForInStatement(false);
|
|
case kAsyncForInStatement:
|
|
return BuildForInStatement(true);
|
|
case kSwitchStatement:
|
|
return BuildSwitchStatement();
|
|
case kContinueSwitchStatement:
|
|
return BuildContinueSwitchStatement();
|
|
case kIfStatement:
|
|
return BuildIfStatement();
|
|
case kReturnStatement:
|
|
return BuildReturnStatement();
|
|
case kTryCatch:
|
|
return BuildTryCatch();
|
|
case kTryFinally:
|
|
return BuildTryFinally();
|
|
case kYieldStatement:
|
|
return BuildYieldStatement();
|
|
case kVariableDeclaration:
|
|
return BuildVariableDeclaration();
|
|
case kFunctionDeclaration:
|
|
return BuildFunctionDeclaration();
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
return Fragment();
|
|
}
|
|
|
|
intptr_t StreamingFlowGraphBuilder::ReaderOffset() {
|
|
return reader_->offset();
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SetOffset(intptr_t offset) {
|
|
reader_->set_offset(offset);
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipBytes(intptr_t bytes) {
|
|
reader_->set_offset(ReaderOffset() + bytes);
|
|
}
|
|
|
|
bool StreamingFlowGraphBuilder::ReadBool() {
|
|
return reader_->ReadBool();
|
|
}
|
|
|
|
uint8_t StreamingFlowGraphBuilder::ReadByte() {
|
|
return reader_->ReadByte();
|
|
}
|
|
|
|
uint32_t StreamingFlowGraphBuilder::ReadUInt() {
|
|
return reader_->ReadUInt();
|
|
}
|
|
|
|
uint32_t StreamingFlowGraphBuilder::PeekUInt() {
|
|
AlternativeReadingScope alt(reader_);
|
|
return reader_->ReadUInt();
|
|
}
|
|
|
|
intptr_t StreamingFlowGraphBuilder::ReadListLength() {
|
|
return reader_->ReadListLength();
|
|
}
|
|
|
|
StringIndex StreamingFlowGraphBuilder::ReadStringReference() {
|
|
return StringIndex(ReadUInt());
|
|
}
|
|
|
|
NameIndex StreamingFlowGraphBuilder::ReadCanonicalNameReference() {
|
|
return reader_->ReadCanonicalNameReference();
|
|
}
|
|
|
|
StringIndex StreamingFlowGraphBuilder::ReadNameAsStringIndex() {
|
|
StringIndex name_index = ReadStringReference(); // read name index.
|
|
if ((H.StringSize(name_index) >= 1) && H.CharacterAt(name_index, 0) == '_') {
|
|
ReadUInt(); // read library index.
|
|
}
|
|
return name_index;
|
|
}
|
|
|
|
const String& StreamingFlowGraphBuilder::ReadNameAsMethodName() {
|
|
StringIndex name_index = ReadStringReference(); // read name index.
|
|
if ((H.StringSize(name_index) >= 1) && H.CharacterAt(name_index, 0) == '_') {
|
|
NameIndex library_reference =
|
|
ReadCanonicalNameReference(); // read library index.
|
|
return H.DartMethodName(library_reference, name_index);
|
|
} else {
|
|
return H.DartMethodName(NameIndex(), name_index);
|
|
}
|
|
}
|
|
|
|
const String& StreamingFlowGraphBuilder::ReadNameAsSetterName() {
|
|
StringIndex name_index = ReadStringReference(); // read name index.
|
|
if ((H.StringSize(name_index) >= 1) && H.CharacterAt(name_index, 0) == '_') {
|
|
NameIndex library_reference =
|
|
ReadCanonicalNameReference(); // read library index.
|
|
return H.DartSetterName(library_reference, name_index);
|
|
} else {
|
|
return H.DartSetterName(NameIndex(), name_index);
|
|
}
|
|
}
|
|
|
|
const String& StreamingFlowGraphBuilder::ReadNameAsGetterName() {
|
|
StringIndex name_index = ReadStringReference(); // read name index.
|
|
if ((H.StringSize(name_index) >= 1) && H.CharacterAt(name_index, 0) == '_') {
|
|
NameIndex library_reference =
|
|
ReadCanonicalNameReference(); // read library index.
|
|
return H.DartGetterName(library_reference, name_index);
|
|
} else {
|
|
return H.DartGetterName(NameIndex(), name_index);
|
|
}
|
|
}
|
|
|
|
const String& StreamingFlowGraphBuilder::ReadNameAsFieldName() {
|
|
StringIndex name_index = ReadStringReference(); // read name index.
|
|
if ((H.StringSize(name_index) >= 1) && H.CharacterAt(name_index, 0) == '_') {
|
|
NameIndex library_reference =
|
|
ReadCanonicalNameReference(); // read library index.
|
|
return H.DartFieldName(library_reference, name_index);
|
|
} else {
|
|
return H.DartFieldName(NameIndex(), name_index);
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipStringReference() {
|
|
ReadUInt();
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipCanonicalNameReference() {
|
|
ReadUInt();
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipDartType() {
|
|
Tag tag = ReadTag();
|
|
switch (tag) {
|
|
case kInvalidType:
|
|
case kDynamicType:
|
|
case kVoidType:
|
|
case kBottomType:
|
|
case kVectorType:
|
|
// those contain nothing.
|
|
return;
|
|
case kInterfaceType:
|
|
SkipInterfaceType(false);
|
|
return;
|
|
case kSimpleInterfaceType:
|
|
SkipInterfaceType(true);
|
|
return;
|
|
case kFunctionType:
|
|
SkipFunctionType(false);
|
|
return;
|
|
case kSimpleFunctionType:
|
|
SkipFunctionType(true);
|
|
return;
|
|
case kTypeParameterType:
|
|
ReadUInt(); // read index for parameter.
|
|
SkipOptionalDartType(); // read bound bound.
|
|
return;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipOptionalDartType() {
|
|
Tag tag = ReadTag(); // read tag.
|
|
if (tag == kNothing) {
|
|
return;
|
|
}
|
|
ASSERT(tag == kSomething);
|
|
|
|
SkipDartType(); // read type.
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipInterfaceType(bool simple) {
|
|
ReadUInt(); // read klass_name.
|
|
if (!simple) {
|
|
SkipListOfDartTypes(); // read list of types.
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipFunctionType(bool simple) {
|
|
if (!simple) {
|
|
SkipTypeParametersList(); // read type_parameters.
|
|
ReadUInt(); // read required parameter count.
|
|
ReadUInt(); // read total parameter count.
|
|
}
|
|
|
|
SkipListOfDartTypes(); // read positional_parameters types.
|
|
|
|
if (!simple) {
|
|
const intptr_t named_count =
|
|
ReadListLength(); // read named_parameters list length.
|
|
for (intptr_t i = 0; i < named_count; ++i) {
|
|
// read string reference (i.e. named_parameters[i].name).
|
|
SkipStringReference();
|
|
SkipDartType(); // read named_parameters[i].type.
|
|
}
|
|
}
|
|
|
|
SkipListOfStrings(); // read positional parameter names.
|
|
|
|
if (!simple) {
|
|
SkipCanonicalNameReference(); // read typedef reference.
|
|
}
|
|
|
|
SkipDartType(); // read return type.
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipListOfExpressions() {
|
|
intptr_t list_length = ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
SkipExpression(); // read ith expression.
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipListOfDartTypes() {
|
|
intptr_t list_length = ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
SkipDartType(); // read ith type.
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipListOfStrings() {
|
|
intptr_t list_length = ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
SkipStringReference(); // read ith string index.
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipListOfVariableDeclarations() {
|
|
intptr_t list_length = ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
SkipVariableDeclaration(); // read ith variable declaration.
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipTypeParametersList() {
|
|
intptr_t list_length = ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
SkipStringReference(); // read ith name index.
|
|
SkipDartType(); // read ith bound.
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipInitializer() {
|
|
Tag tag = ReadTag();
|
|
ReadByte(); // read isSynthetic flag.
|
|
switch (tag) {
|
|
case kInvalidInitializer:
|
|
return;
|
|
case kFieldInitializer:
|
|
SkipCanonicalNameReference(); // read field_reference.
|
|
SkipExpression(); // read value.
|
|
return;
|
|
case kSuperInitializer:
|
|
SkipCanonicalNameReference(); // read target_reference.
|
|
SkipArguments(); // read arguments.
|
|
return;
|
|
case kRedirectingInitializer:
|
|
SkipCanonicalNameReference(); // read target_reference.
|
|
SkipArguments(); // read arguments.
|
|
return;
|
|
case kLocalInitializer:
|
|
SkipVariableDeclaration(); // read variable.
|
|
return;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipExpression() {
|
|
uint8_t payload = 0;
|
|
Tag tag = ReadTag(&payload);
|
|
switch (tag) {
|
|
case kInvalidExpression:
|
|
return;
|
|
case kVariableGet:
|
|
ReadPosition(); // read position.
|
|
ReadUInt(); // read kernel position.
|
|
ReadUInt(); // read relative variable index.
|
|
SkipOptionalDartType(); // read promoted type.
|
|
return;
|
|
case kSpecializedVariableGet:
|
|
ReadPosition(); // read position.
|
|
ReadUInt(); // read kernel position.
|
|
return;
|
|
case kVariableSet:
|
|
ReadPosition(); // read position.
|
|
ReadUInt(); // read kernel position.
|
|
ReadUInt(); // read relative variable index.
|
|
SkipExpression(); // read expression.
|
|
return;
|
|
case kSpecializedVariableSet:
|
|
ReadPosition(); // read position.
|
|
ReadUInt(); // read kernel position.
|
|
SkipExpression(); // read expression.
|
|
return;
|
|
case kPropertyGet:
|
|
ReadPosition(); // read position.
|
|
SkipExpression(); // read receiver.
|
|
SkipName(); // read name.
|
|
SkipCanonicalNameReference(); // read interface_target_reference.
|
|
return;
|
|
case kPropertySet:
|
|
ReadPosition(); // read position.
|
|
SkipExpression(); // read receiver.
|
|
SkipName(); // read name.
|
|
SkipExpression(); // read value.
|
|
SkipCanonicalNameReference(); // read interface_target_reference.
|
|
return;
|
|
case kDirectPropertyGet:
|
|
ReadPosition(); // read position.
|
|
SkipExpression(); // read receiver.
|
|
SkipCanonicalNameReference(); // read target_reference.
|
|
return;
|
|
case kDirectPropertySet:
|
|
ReadPosition(); // read position.
|
|
SkipExpression(); // read receiver.
|
|
SkipCanonicalNameReference(); // read target_reference.
|
|
SkipExpression(); // read value·
|
|
return;
|
|
case kStaticGet:
|
|
ReadPosition(); // read position.
|
|
SkipCanonicalNameReference(); // read target_reference.
|
|
return;
|
|
case kStaticSet:
|
|
ReadPosition(); // read position.
|
|
SkipCanonicalNameReference(); // read target_reference.
|
|
SkipExpression(); // read expression.
|
|
return;
|
|
case kMethodInvocation:
|
|
ReadPosition(); // read position.
|
|
SkipExpression(); // read receiver.
|
|
SkipName(); // read name.
|
|
SkipArguments(); // read arguments.
|
|
SkipCanonicalNameReference(); // read interface_target_reference.
|
|
return;
|
|
case kDirectMethodInvocation:
|
|
SkipExpression(); // read receiver.
|
|
SkipCanonicalNameReference(); // read target_reference.
|
|
SkipArguments(); // read arguments.
|
|
return;
|
|
case kStaticInvocation:
|
|
case kConstStaticInvocation:
|
|
ReadPosition(); // read position.
|
|
SkipCanonicalNameReference(); // read procedure_reference.
|
|
SkipArguments(); // read arguments.
|
|
return;
|
|
case kConstructorInvocation:
|
|
case kConstConstructorInvocation:
|
|
ReadPosition(); // read position.
|
|
SkipCanonicalNameReference(); // read target_reference.
|
|
SkipArguments(); // read arguments.
|
|
return;
|
|
case kNot:
|
|
SkipExpression(); // read expression.
|
|
return;
|
|
case kLogicalExpression:
|
|
SkipExpression(); // read left.
|
|
SkipBytes(1); // read operator.
|
|
SkipExpression(); // read right.
|
|
return;
|
|
case kConditionalExpression:
|
|
SkipExpression(); // read condition.
|
|
SkipExpression(); // read then.
|
|
SkipExpression(); // read otherwise.
|
|
SkipOptionalDartType(); // read unused static type.
|
|
return;
|
|
case kStringConcatenation:
|
|
ReadPosition(); // read position.
|
|
SkipListOfExpressions(); // read list of expressions.
|
|
return;
|
|
case kIsExpression:
|
|
ReadPosition(); // read position.
|
|
SkipExpression(); // read operand.
|
|
SkipDartType(); // read type.
|
|
return;
|
|
case kAsExpression:
|
|
ReadPosition(); // read position.
|
|
SkipExpression(); // read operand.
|
|
SkipDartType(); // read type.
|
|
return;
|
|
case kSymbolLiteral:
|
|
SkipStringReference(); // read index into string table.
|
|
return;
|
|
case kTypeLiteral:
|
|
SkipDartType(); // read type.
|
|
return;
|
|
case kThisExpression:
|
|
return;
|
|
case kRethrow:
|
|
ReadPosition(); // read position.
|
|
return;
|
|
case kThrow:
|
|
ReadPosition(); // read position.
|
|
SkipExpression(); // read expression.
|
|
return;
|
|
case kListLiteral:
|
|
case kConstListLiteral:
|
|
ReadPosition(); // read position.
|
|
SkipDartType(); // read type.
|
|
SkipListOfExpressions(); // read list of expressions.
|
|
return;
|
|
case kMapLiteral:
|
|
case kConstMapLiteral: {
|
|
ReadPosition(); // read position.
|
|
SkipDartType(); // read key type.
|
|
SkipDartType(); // read value type.
|
|
intptr_t list_length = ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
SkipExpression(); // read ith key.
|
|
SkipExpression(); // read ith value.
|
|
}
|
|
return;
|
|
}
|
|
case kFunctionExpression:
|
|
ReadPosition(); // read position.
|
|
SkipFunctionNode(); // read function node.
|
|
return;
|
|
case kLet:
|
|
SkipVariableDeclaration(); // read variable declaration.
|
|
SkipExpression(); // read expression.
|
|
return;
|
|
case kVectorCreation:
|
|
ReadUInt(); // read value.
|
|
return;
|
|
case kVectorGet:
|
|
SkipExpression(); // read vector expression.
|
|
ReadUInt(); // read index.
|
|
return;
|
|
case kVectorSet:
|
|
SkipExpression(); // read vector expression.
|
|
ReadUInt(); // read index.
|
|
SkipExpression(); // read value.
|
|
return;
|
|
case kVectorCopy:
|
|
SkipExpression(); // read vector expression.
|
|
return;
|
|
case kClosureCreation:
|
|
SkipCanonicalNameReference(); // read top-level function reference.
|
|
SkipExpression(); // read context vector.
|
|
SkipDartType(); // read function type.
|
|
SkipListOfDartTypes(); // read type arguments.
|
|
return;
|
|
case kBigIntLiteral:
|
|
SkipStringReference(); // read string reference.
|
|
return;
|
|
case kStringLiteral:
|
|
SkipStringReference(); // read string reference.
|
|
return;
|
|
case kSpecialIntLiteral:
|
|
return;
|
|
case kNegativeIntLiteral:
|
|
ReadUInt(); // read value.
|
|
return;
|
|
case kPositiveIntLiteral:
|
|
ReadUInt(); // read value.
|
|
return;
|
|
case kDoubleLiteral:
|
|
SkipStringReference(); // read index into string table.
|
|
return;
|
|
case kTrueLiteral:
|
|
return;
|
|
case kFalseLiteral:
|
|
return;
|
|
case kNullLiteral:
|
|
return;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipStatement() {
|
|
Tag tag = ReadTag(); // read tag.
|
|
switch (tag) {
|
|
case kInvalidStatement:
|
|
return;
|
|
case kExpressionStatement:
|
|
SkipExpression(); // read expression.
|
|
return;
|
|
case kBlock: {
|
|
intptr_t list_length = ReadListLength(); // read number of statements.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
SkipStatement(); // read ith statement.
|
|
}
|
|
return;
|
|
}
|
|
case kEmptyStatement:
|
|
return;
|
|
case kAssertStatement: {
|
|
SkipExpression(); // Read condition.
|
|
ReadPosition(); // read condition start offset.
|
|
ReadPosition(); // read condition end offset.
|
|
Tag tag = ReadTag(); // read (first part of) message.
|
|
if (tag == kSomething) {
|
|
SkipExpression(); // read (rest of) message.
|
|
}
|
|
return;
|
|
}
|
|
case kLabeledStatement:
|
|
SkipStatement(); // read body.
|
|
return;
|
|
case kBreakStatement:
|
|
ReadPosition(); // read position.
|
|
ReadUInt(); // read target_index.
|
|
return;
|
|
case kWhileStatement:
|
|
ReadPosition(); // read position.
|
|
SkipExpression(); // read condition.
|
|
SkipStatement(); // read body.
|
|
return;
|
|
case kDoStatement:
|
|
ReadPosition(); // read position.
|
|
SkipStatement(); // read body.
|
|
SkipExpression(); // read condition.
|
|
return;
|
|
case kForStatement: {
|
|
ReadPosition(); // read position.
|
|
SkipListOfVariableDeclarations(); // read variables.
|
|
Tag tag = ReadTag(); // Read first part of condition.
|
|
if (tag == kSomething) {
|
|
SkipExpression(); // read rest of condition.
|
|
}
|
|
SkipListOfExpressions(); // read updates.
|
|
SkipStatement(); // read body.
|
|
return;
|
|
}
|
|
case kForInStatement:
|
|
case kAsyncForInStatement:
|
|
ReadPosition(); // read position.
|
|
ReadPosition(); // read body position.
|
|
SkipVariableDeclaration(); // read variable.
|
|
SkipExpression(); // read iterable.
|
|
SkipStatement(); // read body.
|
|
return;
|
|
case kSwitchStatement: {
|
|
ReadPosition(); // read position.
|
|
SkipExpression(); // read condition.
|
|
int case_count = ReadListLength(); // read number of cases.
|
|
for (intptr_t i = 0; i < case_count; ++i) {
|
|
int expression_count = ReadListLength(); // read number of expressions.
|
|
for (intptr_t j = 0; j < expression_count; ++j) {
|
|
ReadPosition(); // read jth position.
|
|
SkipExpression(); // read jth expression.
|
|
}
|
|
ReadBool(); // read is_default.
|
|
SkipStatement(); // read body.
|
|
}
|
|
return;
|
|
}
|
|
case kContinueSwitchStatement:
|
|
ReadPosition(); // read position.
|
|
ReadUInt(); // read target_index.
|
|
return;
|
|
case kIfStatement:
|
|
SkipExpression(); // read condition.
|
|
SkipStatement(); // read then.
|
|
SkipStatement(); // read otherwise.
|
|
return;
|
|
case kReturnStatement: {
|
|
ReadPosition(); // read position
|
|
Tag tag = ReadTag(); // read (first part of) expression.
|
|
if (tag == kSomething) {
|
|
SkipExpression(); // read (rest of) expression.
|
|
}
|
|
return;
|
|
}
|
|
case kTryCatch: {
|
|
SkipStatement(); // read body.
|
|
ReadBool(); // read any_catch_needs_stack_trace.
|
|
intptr_t catch_count = ReadListLength(); // read number of catches.
|
|
for (intptr_t i = 0; i < catch_count; ++i) {
|
|
SkipDartType(); // read guard.
|
|
tag = ReadTag(); // read first part of exception.
|
|
if (tag == kSomething) {
|
|
SkipVariableDeclaration(); // read exception.
|
|
}
|
|
tag = ReadTag(); // read first part of stack trace.
|
|
if (tag == kSomething) {
|
|
SkipVariableDeclaration(); // read stack trace.
|
|
}
|
|
SkipStatement(); // read body.
|
|
}
|
|
return;
|
|
}
|
|
case kTryFinally:
|
|
SkipStatement(); // read body.
|
|
SkipStatement(); // read finalizer.
|
|
return;
|
|
case kYieldStatement: {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
record_yield_position(position);
|
|
ReadByte(); // read flags.
|
|
SkipExpression(); // read expression.
|
|
return;
|
|
}
|
|
case kVariableDeclaration:
|
|
SkipVariableDeclaration(); // read variable declaration.
|
|
return;
|
|
case kFunctionDeclaration:
|
|
ReadPosition(); // read position.
|
|
SkipVariableDeclaration(); // read variable.
|
|
SkipFunctionNode(); // read function node.
|
|
return;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipFunctionNode() {
|
|
FunctionNodeHelper function_node_helper(this);
|
|
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd);
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipName() {
|
|
StringIndex name_index = ReadStringReference(); // read name index.
|
|
if ((H.StringSize(name_index) >= 1) && H.CharacterAt(name_index, 0) == '_') {
|
|
SkipCanonicalNameReference(); // read library index.
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipArguments() {
|
|
ReadUInt(); // read argument count.
|
|
|
|
SkipListOfDartTypes(); // read list of types.
|
|
SkipListOfExpressions(); // read positionals.
|
|
|
|
// List of named.
|
|
intptr_t list_length = ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
SkipStringReference(); // read ith name index.
|
|
SkipExpression(); // read ith expression.
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipVariableDeclaration() {
|
|
VariableDeclarationHelper helper(this);
|
|
helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd);
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipLibraryCombinator() {
|
|
ReadBool(); // read is_show.
|
|
intptr_t name_count = ReadUInt(); // read list length.
|
|
for (intptr_t j = 0; j < name_count; ++j) {
|
|
ReadUInt(); // read ith entry of name_indices.
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipLibraryDependency() {
|
|
ReadFlags();
|
|
SkipListOfExpressions(); // Annotations.
|
|
ReadCanonicalNameReference();
|
|
ReadStringReference(); // Name.
|
|
intptr_t combinator_count = ReadListLength();
|
|
for (intptr_t i = 0; i < combinator_count; ++i) {
|
|
SkipLibraryCombinator();
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipLibraryPart() {
|
|
SkipListOfExpressions(); // Read annotations.
|
|
ReadStringReference(); // read uri_index.
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipLibraryTypedef() {
|
|
SkipCanonicalNameReference(); // read canonical name.
|
|
ReadPosition(); // read position.
|
|
SkipStringReference(); // read name index.
|
|
ReadUInt(); // read source_uri_index.
|
|
SkipTypeParametersList(); // read type parameters.
|
|
SkipDartType(); // read type.
|
|
}
|
|
|
|
TokenPosition StreamingFlowGraphBuilder::ReadPosition(bool record) {
|
|
TokenPosition position = reader_->ReadPosition();
|
|
if (record) {
|
|
record_token_position(position);
|
|
}
|
|
return position;
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::record_token_position(TokenPosition position) {
|
|
if (record_for_script_id_ == current_script_id_ &&
|
|
record_token_positions_into_ != NULL) {
|
|
record_token_positions_into_->Add(position.value());
|
|
}
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::record_yield_position(TokenPosition position) {
|
|
if (record_for_script_id_ == current_script_id_ &&
|
|
record_yield_positions_into_ != NULL) {
|
|
record_yield_positions_into_->Add(position.value());
|
|
}
|
|
}
|
|
|
|
Tag StreamingFlowGraphBuilder::ReadTag(uint8_t* payload) {
|
|
return reader_->ReadTag(payload);
|
|
}
|
|
|
|
Tag StreamingFlowGraphBuilder::PeekTag(uint8_t* payload) {
|
|
return reader_->PeekTag(payload);
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::loop_depth_inc() {
|
|
++flow_graph_builder_->loop_depth_;
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::loop_depth_dec() {
|
|
--flow_graph_builder_->loop_depth_;
|
|
}
|
|
|
|
intptr_t StreamingFlowGraphBuilder::for_in_depth() {
|
|
return flow_graph_builder_->for_in_depth_;
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::for_in_depth_inc() {
|
|
++flow_graph_builder_->for_in_depth_;
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::for_in_depth_dec() {
|
|
--flow_graph_builder_->for_in_depth_;
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::catch_depth_inc() {
|
|
++flow_graph_builder_->catch_depth_;
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::catch_depth_dec() {
|
|
--flow_graph_builder_->catch_depth_;
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::try_depth_inc() {
|
|
++flow_graph_builder_->try_depth_;
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::try_depth_dec() {
|
|
--flow_graph_builder_->try_depth_;
|
|
}
|
|
|
|
intptr_t StreamingFlowGraphBuilder::CurrentTryIndex() {
|
|
return flow_graph_builder_->CurrentTryIndex();
|
|
}
|
|
|
|
intptr_t StreamingFlowGraphBuilder::AllocateTryIndex() {
|
|
return flow_graph_builder_->AllocateTryIndex();
|
|
}
|
|
|
|
LocalVariable* StreamingFlowGraphBuilder::CurrentException() {
|
|
return flow_graph_builder_->CurrentException();
|
|
}
|
|
|
|
LocalVariable* StreamingFlowGraphBuilder::CurrentStackTrace() {
|
|
return flow_graph_builder_->CurrentStackTrace();
|
|
}
|
|
|
|
CatchBlock* StreamingFlowGraphBuilder::catch_block() {
|
|
return flow_graph_builder_->catch_block_;
|
|
}
|
|
|
|
ActiveClass* StreamingFlowGraphBuilder::active_class() {
|
|
return &flow_graph_builder_->active_class_;
|
|
}
|
|
|
|
ScopeBuildingResult* StreamingFlowGraphBuilder::scopes() {
|
|
return flow_graph_builder_->scopes_;
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::set_scopes(ScopeBuildingResult* scope) {
|
|
flow_graph_builder_->scopes_ = scope;
|
|
}
|
|
|
|
ParsedFunction* StreamingFlowGraphBuilder::parsed_function() {
|
|
return flow_graph_builder_->parsed_function_;
|
|
}
|
|
|
|
TryFinallyBlock* StreamingFlowGraphBuilder::try_finally_block() {
|
|
return flow_graph_builder_->try_finally_block_;
|
|
}
|
|
|
|
SwitchBlock* StreamingFlowGraphBuilder::switch_block() {
|
|
return flow_graph_builder_->switch_block_;
|
|
}
|
|
|
|
BreakableBlock* StreamingFlowGraphBuilder::breakable_block() {
|
|
return flow_graph_builder_->breakable_block_;
|
|
}
|
|
|
|
GrowableArray<YieldContinuation>&
|
|
StreamingFlowGraphBuilder::yield_continuations() {
|
|
return flow_graph_builder_->yield_continuations_;
|
|
}
|
|
|
|
Value* StreamingFlowGraphBuilder::stack() {
|
|
return flow_graph_builder_->stack_;
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::Push(Definition* definition) {
|
|
flow_graph_builder_->Push(definition);
|
|
}
|
|
|
|
Value* StreamingFlowGraphBuilder::Pop() {
|
|
return flow_graph_builder_->Pop();
|
|
}
|
|
|
|
Tag StreamingFlowGraphBuilder::PeekArgumentsFirstPositionalTag() {
|
|
// read parts of arguments, then go back to before doing so.
|
|
AlternativeReadingScope alt(reader_);
|
|
ReadUInt(); // read number of arguments.
|
|
|
|
SkipListOfDartTypes(); // Read list of types.
|
|
|
|
// List of positional.
|
|
intptr_t list_length = ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
return ReadTag(); // read first tag.
|
|
}
|
|
|
|
UNREACHABLE();
|
|
return kNothing;
|
|
}
|
|
|
|
const TypeArguments& StreamingFlowGraphBuilder::PeekArgumentsInstantiatedType(
|
|
const Class& klass) {
|
|
// read parts of arguments, then go back to before doing so.
|
|
AlternativeReadingScope alt(reader_);
|
|
ReadUInt(); // read argument count.
|
|
intptr_t list_length = ReadListLength(); // read types list length.
|
|
return T.BuildInstantiatedTypeArguments(klass, list_length); // read types.
|
|
}
|
|
|
|
intptr_t StreamingFlowGraphBuilder::PeekArgumentsCount() {
|
|
return PeekUInt();
|
|
}
|
|
|
|
intptr_t StreamingFlowGraphBuilder::PeekArgumentsTypeCount() {
|
|
AlternativeReadingScope alt(reader_);
|
|
ReadUInt(); // read arguments count.
|
|
return ReadListLength(); // read length of types list.
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SkipArgumentsBeforeActualArguments() {
|
|
ReadUInt(); // read arguments count.
|
|
SkipListOfDartTypes(); // read list of types.
|
|
}
|
|
|
|
LocalVariable* StreamingFlowGraphBuilder::LookupVariable(
|
|
intptr_t kernel_offset) {
|
|
return flow_graph_builder_->LookupVariable(kernel_offset);
|
|
}
|
|
|
|
LocalVariable* StreamingFlowGraphBuilder::MakeTemporary() {
|
|
return flow_graph_builder_->MakeTemporary();
|
|
}
|
|
|
|
Token::Kind StreamingFlowGraphBuilder::MethodKind(const String& name) {
|
|
return flow_graph_builder_->MethodKind(name);
|
|
}
|
|
|
|
RawFunction* StreamingFlowGraphBuilder::LookupMethodByMember(
|
|
NameIndex target,
|
|
const String& method_name) {
|
|
return flow_graph_builder_->LookupMethodByMember(target, method_name);
|
|
}
|
|
|
|
bool StreamingFlowGraphBuilder::NeedsDebugStepCheck(const Function& function,
|
|
TokenPosition position) {
|
|
return flow_graph_builder_->NeedsDebugStepCheck(function, position);
|
|
}
|
|
|
|
bool StreamingFlowGraphBuilder::NeedsDebugStepCheck(Value* value,
|
|
TokenPosition position) {
|
|
return flow_graph_builder_->NeedsDebugStepCheck(value, position);
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::InlineBailout(const char* reason) {
|
|
flow_graph_builder_->InlineBailout(reason);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::DebugStepCheck(TokenPosition position) {
|
|
return flow_graph_builder_->DebugStepCheck(position);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::LoadLocal(LocalVariable* variable) {
|
|
return flow_graph_builder_->LoadLocal(variable);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::Return(TokenPosition position) {
|
|
return flow_graph_builder_->Return(position);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::PushArgument() {
|
|
return flow_graph_builder_->PushArgument();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::EvaluateAssertion() {
|
|
return flow_graph_builder_->EvaluateAssertion();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::RethrowException(TokenPosition position,
|
|
int catch_try_index) {
|
|
return flow_graph_builder_->RethrowException(position, catch_try_index);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::ThrowNoSuchMethodError() {
|
|
return flow_graph_builder_->ThrowNoSuchMethodError();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::Constant(const Object& value) {
|
|
return flow_graph_builder_->Constant(value);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::IntConstant(int64_t value) {
|
|
return flow_graph_builder_->IntConstant(value);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::LoadStaticField() {
|
|
return flow_graph_builder_->LoadStaticField();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::StaticCall(TokenPosition position,
|
|
const Function& target,
|
|
intptr_t argument_count) {
|
|
return flow_graph_builder_->StaticCall(position, target, argument_count);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::StaticCall(TokenPosition position,
|
|
const Function& target,
|
|
intptr_t argument_count,
|
|
const Array& argument_names,
|
|
intptr_t type_args_count) {
|
|
return flow_graph_builder_->StaticCall(position, target, argument_count,
|
|
argument_names, type_args_count);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::InstanceCall(
|
|
TokenPosition position,
|
|
const String& name,
|
|
Token::Kind kind,
|
|
intptr_t argument_count,
|
|
intptr_t checked_argument_count) {
|
|
const intptr_t kTypeArgsLen = 0;
|
|
return flow_graph_builder_->InstanceCall(
|
|
position, name, kind, kTypeArgsLen, argument_count, Array::null_array(),
|
|
checked_argument_count, Function::null_function());
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::InstanceCall(
|
|
TokenPosition position,
|
|
const String& name,
|
|
Token::Kind kind,
|
|
intptr_t type_args_len,
|
|
intptr_t argument_count,
|
|
const Array& argument_names,
|
|
intptr_t checked_argument_count,
|
|
const Function& interface_target) {
|
|
return flow_graph_builder_->InstanceCall(
|
|
position, name, kind, type_args_len, argument_count, argument_names,
|
|
checked_argument_count, interface_target);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::ThrowException(TokenPosition position) {
|
|
return flow_graph_builder_->ThrowException(position);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BooleanNegate() {
|
|
return flow_graph_builder_->BooleanNegate();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::TranslateInstantiatedTypeArguments(
|
|
const TypeArguments& type_arguments) {
|
|
return flow_graph_builder_->TranslateInstantiatedTypeArguments(
|
|
type_arguments);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::StrictCompare(Token::Kind kind,
|
|
bool number_check) {
|
|
return flow_graph_builder_->StrictCompare(kind, number_check);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::AllocateObject(TokenPosition position,
|
|
const Class& klass,
|
|
intptr_t argument_count) {
|
|
return flow_graph_builder_->AllocateObject(position, klass, argument_count);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::AllocateObject(
|
|
const Class& klass,
|
|
const Function& closure_function) {
|
|
return flow_graph_builder_->AllocateObject(klass, closure_function);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::AllocateContext(intptr_t size) {
|
|
return flow_graph_builder_->AllocateContext(size);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::LoadField(intptr_t offset) {
|
|
return flow_graph_builder_->LoadField(offset);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::StoreLocal(TokenPosition position,
|
|
LocalVariable* variable) {
|
|
return flow_graph_builder_->StoreLocal(position, variable);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::StoreStaticField(TokenPosition position,
|
|
const Field& field) {
|
|
return flow_graph_builder_->StoreStaticField(position, field);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::StoreInstanceField(TokenPosition position,
|
|
intptr_t offset) {
|
|
return flow_graph_builder_->StoreInstanceField(position, offset);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::StringInterpolate(TokenPosition position) {
|
|
return flow_graph_builder_->StringInterpolate(position);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::StringInterpolateSingle(
|
|
TokenPosition position) {
|
|
return flow_graph_builder_->StringInterpolateSingle(position);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::ThrowTypeError() {
|
|
return flow_graph_builder_->ThrowTypeError();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::LoadInstantiatorTypeArguments() {
|
|
return flow_graph_builder_->LoadInstantiatorTypeArguments();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::LoadFunctionTypeArguments() {
|
|
return flow_graph_builder_->LoadFunctionTypeArguments();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::InstantiateType(const AbstractType& type) {
|
|
return flow_graph_builder_->InstantiateType(type);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::CreateArray() {
|
|
return flow_graph_builder_->CreateArray();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::StoreIndexed(intptr_t class_id) {
|
|
return flow_graph_builder_->StoreIndexed(class_id);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::CheckStackOverflow() {
|
|
return flow_graph_builder_->CheckStackOverflow();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::CloneContext() {
|
|
return flow_graph_builder_->CloneContext();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::TranslateFinallyFinalizers(
|
|
TryFinallyBlock* outer_finally,
|
|
intptr_t target_context_depth) {
|
|
// TranslateFinallyFinalizers can move the readers offset.
|
|
// Save the current position and restore it afterwards.
|
|
AlternativeReadingScope alt(reader_);
|
|
return flow_graph_builder_->TranslateFinallyFinalizers(outer_finally,
|
|
target_context_depth);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BranchIfTrue(
|
|
TargetEntryInstr** then_entry,
|
|
TargetEntryInstr** otherwise_entry,
|
|
bool negate) {
|
|
return flow_graph_builder_->BranchIfTrue(then_entry, otherwise_entry, negate);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BranchIfEqual(
|
|
TargetEntryInstr** then_entry,
|
|
TargetEntryInstr** otherwise_entry,
|
|
bool negate) {
|
|
return flow_graph_builder_->BranchIfEqual(then_entry, otherwise_entry,
|
|
negate);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BranchIfNull(
|
|
TargetEntryInstr** then_entry,
|
|
TargetEntryInstr** otherwise_entry,
|
|
bool negate) {
|
|
return flow_graph_builder_->BranchIfNull(then_entry, otherwise_entry, negate);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::CatchBlockEntry(const Array& handler_types,
|
|
intptr_t handler_index,
|
|
bool needs_stacktrace) {
|
|
return flow_graph_builder_->CatchBlockEntry(handler_types, handler_index,
|
|
needs_stacktrace);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::TryCatch(int try_handler_index) {
|
|
return flow_graph_builder_->TryCatch(try_handler_index);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::Drop() {
|
|
return flow_graph_builder_->Drop();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::NullConstant() {
|
|
return flow_graph_builder_->NullConstant();
|
|
}
|
|
|
|
JoinEntryInstr* StreamingFlowGraphBuilder::BuildJoinEntry() {
|
|
return flow_graph_builder_->BuildJoinEntry();
|
|
}
|
|
|
|
JoinEntryInstr* StreamingFlowGraphBuilder::BuildJoinEntry(intptr_t try_index) {
|
|
return flow_graph_builder_->BuildJoinEntry(try_index);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::Goto(JoinEntryInstr* destination) {
|
|
return flow_graph_builder_->Goto(destination);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildImplicitClosureCreation(
|
|
const Function& target) {
|
|
return flow_graph_builder_->BuildImplicitClosureCreation(target);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::CheckBooleanInCheckedMode() {
|
|
return flow_graph_builder_->CheckBooleanInCheckedMode();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::CheckAssignableInCheckedMode(
|
|
const AbstractType& dst_type,
|
|
const String& dst_name) {
|
|
return flow_graph_builder_->CheckAssignableInCheckedMode(dst_type, dst_name);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::CheckVariableTypeInCheckedMode(
|
|
intptr_t variable_kernel_position) {
|
|
if (I->type_checks()) {
|
|
LocalVariable* variable = LookupVariable(variable_kernel_position);
|
|
return flow_graph_builder_->CheckVariableTypeInCheckedMode(
|
|
variable->type(), variable->name());
|
|
}
|
|
return Fragment();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::CheckVariableTypeInCheckedMode(
|
|
const AbstractType& dst_type,
|
|
const String& name_symbol) {
|
|
return flow_graph_builder_->CheckVariableTypeInCheckedMode(dst_type,
|
|
name_symbol);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::EnterScope(intptr_t kernel_offset,
|
|
bool* new_context) {
|
|
return flow_graph_builder_->EnterScope(kernel_offset, new_context);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::ExitScope(intptr_t kernel_offset) {
|
|
return flow_graph_builder_->ExitScope(kernel_offset);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::TranslateCondition(bool* negate) {
|
|
*negate = PeekTag() == kNot;
|
|
if (*negate) {
|
|
SkipBytes(1); // Skip Not tag, thus go directly to the inner expression.
|
|
}
|
|
Fragment instructions = BuildExpression(); // read expression.
|
|
instructions += CheckBooleanInCheckedMode();
|
|
return instructions;
|
|
}
|
|
|
|
const TypeArguments& StreamingFlowGraphBuilder::BuildTypeArguments() {
|
|
ReadUInt(); // read arguments count.
|
|
intptr_t type_count = ReadListLength(); // read type count.
|
|
return T.BuildTypeArguments(type_count); // read types.
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildArguments(Array* argument_names,
|
|
intptr_t* argument_count,
|
|
bool skip_push_arguments,
|
|
bool do_drop) {
|
|
intptr_t dummy;
|
|
if (argument_count == NULL) argument_count = &dummy;
|
|
*argument_count = ReadUInt(); // read arguments count.
|
|
|
|
// List of types.
|
|
SkipListOfDartTypes(); // read list of types.
|
|
|
|
return BuildArgumentsFromActualArguments(argument_names, skip_push_arguments,
|
|
do_drop);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildArgumentsFromActualArguments(
|
|
Array* argument_names,
|
|
bool skip_push_arguments,
|
|
bool do_drop) {
|
|
Fragment instructions;
|
|
|
|
// List of positional.
|
|
intptr_t list_length = ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
instructions += BuildExpression(); // read ith expression.
|
|
if (!skip_push_arguments) instructions += PushArgument();
|
|
if (do_drop) instructions += Drop();
|
|
}
|
|
|
|
// List of named.
|
|
list_length = ReadListLength(); // read list length.
|
|
if (argument_names != NULL && list_length > 0) {
|
|
*argument_names ^= Array::New(list_length, Heap::kOld);
|
|
}
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
String& name = H.DartSymbol(ReadStringReference()); // read ith name index.
|
|
instructions += BuildExpression(); // read ith expression.
|
|
if (!skip_push_arguments) instructions += PushArgument();
|
|
if (do_drop) instructions += Drop();
|
|
if (argument_names != NULL) {
|
|
argument_names->SetAt(i, name);
|
|
}
|
|
}
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildInvalidExpression(
|
|
TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
// The frontend will take care of emitting normal errors (like
|
|
// [NoSuchMethodError]s) and only emit [InvalidExpression]s in very special
|
|
// situations (e.g. an invalid annotation).
|
|
return ThrowNoSuchMethodError();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildVariableGet(TokenPosition* position) {
|
|
(position != NULL) ? * position = ReadPosition()
|
|
: ReadPosition(); // read position.
|
|
intptr_t variable_kernel_position = ReadUInt(); // read kernel position.
|
|
ReadUInt(); // read relative variable index.
|
|
SkipOptionalDartType(); // read promoted type.
|
|
|
|
return LoadLocal(LookupVariable(variable_kernel_position));
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildVariableGet(uint8_t payload,
|
|
TokenPosition* position) {
|
|
(position != NULL) ? * position = ReadPosition()
|
|
: ReadPosition(); // read position.
|
|
intptr_t variable_kernel_position = ReadUInt(); // read kernel position.
|
|
return LoadLocal(LookupVariable(variable_kernel_position));
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildVariableSet(TokenPosition* p) {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
intptr_t variable_kernel_position = ReadUInt(); // read kernel position.
|
|
ReadUInt(); // read relative variable index.
|
|
Fragment instructions = BuildExpression(); // read expression.
|
|
|
|
if (NeedsDebugStepCheck(stack(), position)) {
|
|
instructions = DebugStepCheck(position) + instructions;
|
|
}
|
|
instructions += CheckVariableTypeInCheckedMode(variable_kernel_position);
|
|
instructions +=
|
|
StoreLocal(position, LookupVariable(variable_kernel_position));
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildVariableSet(uint8_t payload,
|
|
TokenPosition* p) {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
intptr_t variable_kernel_position = ReadUInt(); // read kernel position.
|
|
Fragment instructions = BuildExpression(); // read expression.
|
|
|
|
if (NeedsDebugStepCheck(stack(), position)) {
|
|
instructions = DebugStepCheck(position) + instructions;
|
|
}
|
|
instructions += CheckVariableTypeInCheckedMode(variable_kernel_position);
|
|
instructions +=
|
|
StoreLocal(position, LookupVariable(variable_kernel_position));
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildPropertyGet(TokenPosition* p) {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
Fragment instructions = BuildExpression(); // read receiver.
|
|
instructions += PushArgument();
|
|
|
|
const String& getter_name = ReadNameAsGetterName(); // read name.
|
|
|
|
const Function* interface_target = &Function::null_function();
|
|
NameIndex itarget_name =
|
|
ReadCanonicalNameReference(); // read interface_target_reference.
|
|
if (FLAG_experimental_strong_mode && !H.IsRoot(itarget_name) &&
|
|
(H.IsGetter(itarget_name) || H.IsField(itarget_name))) {
|
|
interface_target = &Function::ZoneHandle(
|
|
Z, LookupMethodByMember(itarget_name, H.DartGetterName(itarget_name)));
|
|
ASSERT(getter_name.raw() == interface_target->name());
|
|
}
|
|
|
|
const intptr_t kTypeArgsLen = 0;
|
|
const intptr_t kNumArgsChecked = 1;
|
|
return instructions + InstanceCall(position, getter_name, Token::kGET,
|
|
kTypeArgsLen, 1, Array::null_array(),
|
|
kNumArgsChecked, *interface_target);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildPropertySet(TokenPosition* p) {
|
|
Fragment instructions(NullConstant());
|
|
LocalVariable* variable = MakeTemporary();
|
|
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
instructions += BuildExpression(); // read receiver.
|
|
instructions += PushArgument();
|
|
|
|
const String& setter_name = ReadNameAsSetterName(); // read name.
|
|
|
|
instructions += BuildExpression(); // read value.
|
|
instructions += StoreLocal(TokenPosition::kNoSource, variable);
|
|
instructions += PushArgument();
|
|
|
|
const Function* interface_target = &Function::null_function();
|
|
NameIndex itarget_name =
|
|
ReadCanonicalNameReference(); // read interface_target_reference.
|
|
if (FLAG_experimental_strong_mode && !H.IsRoot(itarget_name)) {
|
|
interface_target = &Function::ZoneHandle(
|
|
Z, LookupMethodByMember(itarget_name, H.DartSetterName(itarget_name)));
|
|
ASSERT(setter_name.raw() == interface_target->name());
|
|
}
|
|
|
|
const intptr_t kTypeArgsLen = 0;
|
|
const intptr_t kNumArgsChecked = 1;
|
|
instructions +=
|
|
InstanceCall(position, setter_name, Token::kSET, kTypeArgsLen, 2,
|
|
Array::null_array(), kNumArgsChecked, *interface_target);
|
|
|
|
return instructions + Drop();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildDirectPropertyGet(TokenPosition* p) {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
Fragment instructions = BuildExpression(); // read receiver.
|
|
NameIndex kernel_name =
|
|
ReadCanonicalNameReference(); // read target_reference.
|
|
|
|
Function& target = Function::ZoneHandle(Z);
|
|
if (H.IsProcedure(kernel_name)) {
|
|
if (H.IsGetter(kernel_name)) {
|
|
target = LookupMethodByMember(kernel_name, H.DartGetterName(kernel_name));
|
|
} else {
|
|
// Undo stack change for the BuildExpression.
|
|
Pop();
|
|
|
|
target = LookupMethodByMember(kernel_name, H.DartMethodName(kernel_name));
|
|
target = target.ImplicitClosureFunction();
|
|
ASSERT(!target.IsNull());
|
|
return BuildImplicitClosureCreation(target);
|
|
}
|
|
} else {
|
|
ASSERT(H.IsField(kernel_name));
|
|
const String& getter_name = H.DartGetterName(kernel_name);
|
|
target = LookupMethodByMember(kernel_name, getter_name);
|
|
ASSERT(target.IsGetterFunction() || target.IsImplicitGetterFunction());
|
|
}
|
|
|
|
instructions += PushArgument();
|
|
return instructions + StaticCall(position, target, 1);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildDirectPropertySet(TokenPosition* p) {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
Fragment instructions(NullConstant());
|
|
LocalVariable* value = MakeTemporary();
|
|
|
|
instructions += BuildExpression(); // read receiver.
|
|
instructions += PushArgument();
|
|
|
|
NameIndex target_reference =
|
|
ReadCanonicalNameReference(); // read target_reference.
|
|
const String& method_name = H.DartSetterName(target_reference);
|
|
const Function& target = Function::ZoneHandle(
|
|
Z, LookupMethodByMember(target_reference, method_name));
|
|
ASSERT(target.IsSetterFunction() || target.IsImplicitSetterFunction());
|
|
|
|
instructions += BuildExpression(); // read value.
|
|
instructions += StoreLocal(TokenPosition::kNoSource, value);
|
|
instructions += PushArgument();
|
|
|
|
instructions += StaticCall(position, target, 2);
|
|
|
|
return instructions + Drop();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildStaticGet(TokenPosition* p) {
|
|
intptr_t offset = ReaderOffset() - 1; // Include the tag.
|
|
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
NameIndex target = ReadCanonicalNameReference(); // read target_reference.
|
|
|
|
if (H.IsField(target)) {
|
|
const Field& field =
|
|
Field::ZoneHandle(Z, H.LookupFieldByKernelField(target));
|
|
if (field.is_const()) {
|
|
return Constant(constant_evaluator_.EvaluateExpression(offset));
|
|
} else {
|
|
const Class& owner = Class::Handle(Z, field.Owner());
|
|
const String& getter_name = H.DartGetterName(target);
|
|
const Function& getter =
|
|
Function::ZoneHandle(Z, owner.LookupStaticFunction(getter_name));
|
|
if (getter.IsNull() || !field.has_initializer()) {
|
|
Fragment instructions = Constant(field);
|
|
return instructions + LoadStaticField();
|
|
} else {
|
|
return StaticCall(position, getter, 0);
|
|
}
|
|
}
|
|
} else {
|
|
const Function& function =
|
|
Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target));
|
|
|
|
if (H.IsGetter(target)) {
|
|
return StaticCall(position, function, 0);
|
|
} else if (H.IsMethod(target)) {
|
|
return Constant(constant_evaluator_.EvaluateExpression(offset));
|
|
} else {
|
|
UNIMPLEMENTED();
|
|
}
|
|
}
|
|
|
|
return Fragment();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildStaticSet(TokenPosition* p) {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
NameIndex target = ReadCanonicalNameReference(); // read target_reference.
|
|
|
|
if (H.IsField(target)) {
|
|
const Field& field =
|
|
Field::ZoneHandle(Z, H.LookupFieldByKernelField(target));
|
|
const AbstractType& dst_type = AbstractType::ZoneHandle(Z, field.type());
|
|
Fragment instructions = BuildExpression(); // read expression.
|
|
if (NeedsDebugStepCheck(stack(), position)) {
|
|
instructions = DebugStepCheck(position) + instructions;
|
|
}
|
|
instructions += CheckAssignableInCheckedMode(
|
|
dst_type, String::ZoneHandle(Z, field.name()));
|
|
LocalVariable* variable = MakeTemporary();
|
|
instructions += LoadLocal(variable);
|
|
return instructions + StoreStaticField(position, field);
|
|
} else {
|
|
ASSERT(H.IsProcedure(target));
|
|
|
|
// Evaluate the expression on the right hand side.
|
|
Fragment instructions = BuildExpression(); // read expression.
|
|
LocalVariable* variable = MakeTemporary();
|
|
|
|
// Prepare argument.
|
|
instructions += LoadLocal(variable);
|
|
instructions += PushArgument();
|
|
|
|
// Invoke the setter function.
|
|
const Function& function =
|
|
Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target));
|
|
instructions += StaticCall(position, function, 1);
|
|
|
|
// Drop the unused result & leave the stored value on the stack.
|
|
return instructions + Drop();
|
|
}
|
|
}
|
|
|
|
static bool IsNumberLiteral(Tag tag) {
|
|
return tag == kNegativeIntLiteral || tag == kPositiveIntLiteral ||
|
|
tag == kSpecialIntLiteral || tag == kDoubleLiteral;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildMethodInvocation(TokenPosition* p) {
|
|
intptr_t offset = ReaderOffset() - 1; // Include the tag.
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
Tag receiver_tag = PeekTag(); // peek tag for receiver.
|
|
if (IsNumberLiteral(receiver_tag)) {
|
|
intptr_t before_branch_offset = ReaderOffset();
|
|
|
|
SkipExpression(); // read receiver (it's just a number literal).
|
|
|
|
const String& name = ReadNameAsMethodName(); // read name.
|
|
const Token::Kind token_kind = MethodKind(name);
|
|
intptr_t argument_count = PeekArgumentsCount() + 1;
|
|
|
|
if ((argument_count == 1) && (token_kind == Token::kNEGATE)) {
|
|
const Object& result = constant_evaluator_.EvaluateExpressionSafe(offset);
|
|
if (!result.IsError()) {
|
|
SkipArguments(); // read arguments.
|
|
SkipCanonicalNameReference(); // read interface_target_reference.
|
|
return Constant(result);
|
|
}
|
|
} else if ((argument_count == 2) &&
|
|
Token::IsBinaryArithmeticOperator(token_kind) &&
|
|
IsNumberLiteral(PeekArgumentsFirstPositionalTag())) {
|
|
const Object& result = constant_evaluator_.EvaluateExpressionSafe(offset);
|
|
if (!result.IsError()) {
|
|
SkipArguments(); // read arguments.
|
|
SkipCanonicalNameReference(); // read interface_target_reference.
|
|
return Constant(result);
|
|
}
|
|
}
|
|
|
|
SetOffset(before_branch_offset);
|
|
}
|
|
|
|
Fragment instructions = BuildExpression(); // read receiver.
|
|
|
|
const String& name = ReadNameAsMethodName(); // read name.
|
|
const Token::Kind token_kind = MethodKind(name);
|
|
|
|
// Detect comparison with null.
|
|
if ((token_kind == Token::kEQ || token_kind == Token::kNE) &&
|
|
PeekArgumentsCount() == 1 &&
|
|
(receiver_tag == kNullLiteral ||
|
|
PeekArgumentsFirstPositionalTag() == kNullLiteral)) {
|
|
// "==" or "!=" with null on either side.
|
|
instructions += BuildArguments(NULL, NULL, true); // read arguments.
|
|
SkipCanonicalNameReference(); // read interface_target_reference.
|
|
Token::Kind strict_cmp_kind =
|
|
token_kind == Token::kEQ ? Token::kEQ_STRICT : Token::kNE_STRICT;
|
|
return instructions +
|
|
StrictCompare(strict_cmp_kind, /*number_check = */ true);
|
|
}
|
|
|
|
instructions += PushArgument(); // push receiver as argument.
|
|
|
|
// TODO(28109) Support generic methods in the VM or reify them away.
|
|
const intptr_t kTypeArgsLen = 0;
|
|
Array& argument_names = Array::ZoneHandle(Z);
|
|
intptr_t argument_count;
|
|
instructions +=
|
|
BuildArguments(&argument_names, &argument_count); // read arguments.
|
|
++argument_count;
|
|
|
|
intptr_t checked_argument_count = 1;
|
|
// If we have a special operation (e.g. +/-/==) we mark both arguments as
|
|
// to be checked.
|
|
if (token_kind != Token::kILLEGAL) {
|
|
ASSERT(argument_count <= 2);
|
|
checked_argument_count = argument_count;
|
|
}
|
|
|
|
const Function* interface_target = &Function::null_function();
|
|
NameIndex itarget_name =
|
|
ReadCanonicalNameReference(); // read interface_target_reference.
|
|
if (FLAG_experimental_strong_mode && !H.IsRoot(itarget_name) &&
|
|
!H.IsField(itarget_name)) {
|
|
interface_target = &Function::ZoneHandle(
|
|
Z,
|
|
LookupMethodByMember(itarget_name, H.DartProcedureName(itarget_name)));
|
|
ASSERT(name.raw() == interface_target->name());
|
|
}
|
|
|
|
instructions +=
|
|
InstanceCall(position, name, token_kind, kTypeArgsLen, argument_count,
|
|
argument_names, checked_argument_count, *interface_target);
|
|
// Later optimization passes assume that result of a x.[]=(...) call is not
|
|
// used. We must guarantee this invariant because violation will lead to an
|
|
// illegal IL once we replace x.[]=(...) with a sequence that does not
|
|
// actually produce any value. See http://dartbug.com/29135 for more details.
|
|
if (name.raw() == Symbols::AssignIndexToken().raw()) {
|
|
instructions += Drop();
|
|
instructions += NullConstant();
|
|
}
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildDirectMethodInvocation(
|
|
TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
// TODO(28109) Support generic methods in the VM or reify them away.
|
|
Tag receiver_tag = PeekTag(); // peek tag for receiver.
|
|
Fragment instructions = BuildExpression(); // read receiver.
|
|
|
|
NameIndex kernel_name =
|
|
ReadCanonicalNameReference(); // read target_reference.
|
|
const String& method_name = H.DartProcedureName(kernel_name);
|
|
const Token::Kind token_kind = MethodKind(method_name);
|
|
|
|
// Detect comparison with null.
|
|
if ((token_kind == Token::kEQ || token_kind == Token::kNE) &&
|
|
PeekArgumentsCount() == 1 &&
|
|
(receiver_tag == kNullLiteral ||
|
|
PeekArgumentsFirstPositionalTag() == kNullLiteral)) {
|
|
// "==" or "!=" with null on either side.
|
|
instructions += BuildArguments(NULL, NULL, true); // read arguments.
|
|
Token::Kind strict_cmp_kind =
|
|
token_kind == Token::kEQ ? Token::kEQ_STRICT : Token::kNE_STRICT;
|
|
return instructions +
|
|
StrictCompare(strict_cmp_kind, /*number_check = */ true);
|
|
}
|
|
|
|
instructions += PushArgument(); // push receiver as argument.
|
|
|
|
const Function& target =
|
|
Function::ZoneHandle(Z, LookupMethodByMember(kernel_name, method_name));
|
|
|
|
Array& argument_names = Array::ZoneHandle(Z);
|
|
intptr_t argument_count;
|
|
instructions +=
|
|
BuildArguments(&argument_names, &argument_count); // read arguments.
|
|
++argument_count;
|
|
return instructions + StaticCall(TokenPosition::kNoSource, target,
|
|
argument_count, argument_names);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildStaticInvocation(bool is_const,
|
|
TokenPosition* p) {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
NameIndex procedue_reference =
|
|
ReadCanonicalNameReference(); // read procedure reference.
|
|
intptr_t argument_count = PeekArgumentsCount();
|
|
const Function& target = Function::ZoneHandle(
|
|
Z, H.LookupStaticMethodByKernelProcedure(procedue_reference));
|
|
const Class& klass = Class::ZoneHandle(Z, target.Owner());
|
|
if (target.IsGenerativeConstructor() || target.IsFactory()) {
|
|
// The VM requires a TypeArguments object as first parameter for
|
|
// every factory constructor.
|
|
++argument_count;
|
|
}
|
|
|
|
Fragment instructions;
|
|
LocalVariable* instance_variable = NULL;
|
|
|
|
// If we cross the Kernel -> VM core library boundary, a [StaticInvocation]
|
|
// can appear, but the thing we're calling is not a static method, but a
|
|
// factory constructor.
|
|
// The `H.LookupStaticmethodByKernelProcedure` will potentially resolve to the
|
|
// forwarded constructor.
|
|
// In that case we'll make an instance and pass it as first argument.
|
|
//
|
|
// TODO(27590): Get rid of this after we're using core libraries compiled
|
|
// into Kernel.
|
|
if (target.IsGenerativeConstructor()) {
|
|
if (klass.NumTypeArguments() > 0) {
|
|
const TypeArguments& type_arguments =
|
|
PeekArgumentsInstantiatedType(klass);
|
|
instructions += TranslateInstantiatedTypeArguments(type_arguments);
|
|
instructions += PushArgument();
|
|
instructions += AllocateObject(position, klass, 1);
|
|
} else {
|
|
instructions += AllocateObject(position, klass, 0);
|
|
}
|
|
|
|
instance_variable = MakeTemporary();
|
|
|
|
instructions += LoadLocal(instance_variable);
|
|
instructions += PushArgument();
|
|
} else if (target.IsFactory()) {
|
|
// The VM requires currently a TypeArguments object as first parameter for
|
|
// every factory constructor :-/ !
|
|
//
|
|
// TODO(27590): Get rid of this after we're using core libraries compiled
|
|
// into Kernel.
|
|
const TypeArguments& type_arguments = PeekArgumentsInstantiatedType(klass);
|
|
instructions += TranslateInstantiatedTypeArguments(type_arguments);
|
|
instructions += PushArgument();
|
|
} else {
|
|
// TODO(28109) Support generic methods in the VM or reify them away.
|
|
}
|
|
|
|
bool special_case_identical = klass.IsTopLevel() &&
|
|
(klass.library() == Library::CoreLibrary()) &&
|
|
(target.name() == Symbols::Identical().raw());
|
|
|
|
Array& argument_names = Array::ZoneHandle(Z);
|
|
instructions += BuildArguments(&argument_names, NULL,
|
|
special_case_identical); // read arguments.
|
|
const int kTypeArgsLen = 0;
|
|
ASSERT(target.AreValidArguments(kTypeArgsLen, argument_count, argument_names,
|
|
NULL));
|
|
|
|
// Special case identical(x, y) call.
|
|
// TODO(27590) consider moving this into the inliner and force inline it
|
|
// there.
|
|
if (special_case_identical) {
|
|
ASSERT(argument_count == 2);
|
|
instructions += StrictCompare(Token::kEQ_STRICT, /*number_check=*/true);
|
|
} else {
|
|
instructions +=
|
|
StaticCall(position, target, argument_count, argument_names);
|
|
if (target.IsGenerativeConstructor()) {
|
|
// Drop the result of the constructor call and leave [instance_variable]
|
|
// on top-of-stack.
|
|
instructions += Drop();
|
|
}
|
|
}
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildConstructorInvocation(
|
|
bool is_const,
|
|
TokenPosition* p) {
|
|
if (is_const) {
|
|
intptr_t offset = ReaderOffset() - 1; // Include the tag.
|
|
(p != NULL) ? * p = ReadPosition() : ReadPosition(); // read position.
|
|
|
|
SetOffset(offset);
|
|
SkipExpression(); // read past this ConstructorInvocation.
|
|
return Constant(constant_evaluator_.EvaluateConstructorInvocation(offset));
|
|
}
|
|
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
NameIndex kernel_name =
|
|
ReadCanonicalNameReference(); // read target_reference.
|
|
|
|
Class& klass = Class::ZoneHandle(
|
|
Z, H.LookupClassByKernelClass(H.EnclosingName(kernel_name)));
|
|
|
|
Fragment instructions;
|
|
|
|
// Check for malbounded-ness of type.
|
|
if (I->type_checks()) {
|
|
intptr_t offset = ReaderOffset();
|
|
|
|
const TypeArguments& type_arguments = BuildTypeArguments();
|
|
|
|
AbstractType& type = AbstractType::Handle(
|
|
Z, Type::New(klass, type_arguments, TokenPosition::kNoSource));
|
|
type = ClassFinalizer::FinalizeType(klass, type);
|
|
|
|
if (type.IsMalbounded()) {
|
|
// Evaluate expressions for correctness.
|
|
instructions +=
|
|
BuildArgumentsFromActualArguments(NULL, false, /*do_drop*/ true);
|
|
|
|
// Throw an error & keep the [Value] on the stack.
|
|
instructions += ThrowTypeError();
|
|
|
|
// Bail out early.
|
|
return instructions;
|
|
}
|
|
|
|
SetOffset(offset);
|
|
}
|
|
|
|
if (klass.NumTypeArguments() > 0) {
|
|
if (!klass.IsGeneric()) {
|
|
Type& type = Type::ZoneHandle(Z, T.ReceiverType(klass).raw());
|
|
|
|
// TODO(27590): Can we move this code into [ReceiverType]?
|
|
type ^= ClassFinalizer::FinalizeType(*active_class()->klass, type,
|
|
ClassFinalizer::kFinalize);
|
|
ASSERT(!type.IsMalformedOrMalbounded());
|
|
|
|
TypeArguments& canonicalized_type_arguments =
|
|
TypeArguments::ZoneHandle(Z, type.arguments());
|
|
canonicalized_type_arguments =
|
|
canonicalized_type_arguments.Canonicalize();
|
|
instructions += Constant(canonicalized_type_arguments);
|
|
} else {
|
|
const TypeArguments& type_arguments =
|
|
PeekArgumentsInstantiatedType(klass);
|
|
instructions += TranslateInstantiatedTypeArguments(type_arguments);
|
|
}
|
|
|
|
instructions += PushArgument();
|
|
instructions += AllocateObject(position, klass, 1);
|
|
} else {
|
|
instructions += AllocateObject(position, klass, 0);
|
|
}
|
|
LocalVariable* variable = MakeTemporary();
|
|
|
|
instructions += LoadLocal(variable);
|
|
instructions += PushArgument();
|
|
|
|
Array& argument_names = Array::ZoneHandle(Z);
|
|
intptr_t argument_count;
|
|
instructions +=
|
|
BuildArguments(&argument_names, &argument_count); // read arguments.
|
|
|
|
const Function& target = Function::ZoneHandle(
|
|
Z, H.LookupConstructorByKernelConstructor(klass, kernel_name));
|
|
++argument_count;
|
|
instructions += StaticCall(position, target, argument_count, argument_names);
|
|
return instructions + Drop();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildNot(TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
Fragment instructions = BuildExpression(); // read expression.
|
|
instructions += CheckBooleanInCheckedMode();
|
|
instructions += BooleanNegate();
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildLogicalExpression(
|
|
TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
bool negate;
|
|
Fragment instructions = TranslateCondition(&negate); // read left.
|
|
|
|
TargetEntryInstr* right_entry;
|
|
TargetEntryInstr* constant_entry;
|
|
LogicalOperator op = static_cast<LogicalOperator>(ReadByte());
|
|
|
|
if (op == kAnd) {
|
|
instructions += BranchIfTrue(&right_entry, &constant_entry, negate);
|
|
} else {
|
|
instructions += BranchIfTrue(&constant_entry, &right_entry, negate);
|
|
}
|
|
|
|
Value* top = stack();
|
|
Fragment right_fragment(right_entry);
|
|
right_fragment += TranslateCondition(&negate); // read right.
|
|
|
|
right_fragment += Constant(Bool::True());
|
|
right_fragment +=
|
|
StrictCompare(negate ? Token::kNE_STRICT : Token::kEQ_STRICT);
|
|
right_fragment += StoreLocal(TokenPosition::kNoSource,
|
|
parsed_function()->expression_temp_var());
|
|
right_fragment += Drop();
|
|
|
|
ASSERT(top == stack());
|
|
Fragment constant_fragment(constant_entry);
|
|
constant_fragment += Constant(Bool::Get(op == kOr));
|
|
constant_fragment += StoreLocal(TokenPosition::kNoSource,
|
|
parsed_function()->expression_temp_var());
|
|
constant_fragment += Drop();
|
|
|
|
JoinEntryInstr* join = BuildJoinEntry();
|
|
right_fragment += Goto(join);
|
|
constant_fragment += Goto(join);
|
|
|
|
return Fragment(instructions.entry, join) +
|
|
LoadLocal(parsed_function()->expression_temp_var());
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildConditionalExpression(
|
|
TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
bool negate;
|
|
Fragment instructions = TranslateCondition(&negate); // read condition.
|
|
|
|
TargetEntryInstr* then_entry;
|
|
TargetEntryInstr* otherwise_entry;
|
|
instructions += BranchIfTrue(&then_entry, &otherwise_entry, negate);
|
|
|
|
Value* top = stack();
|
|
Fragment then_fragment(then_entry);
|
|
then_fragment += BuildExpression(); // read then.
|
|
then_fragment += StoreLocal(TokenPosition::kNoSource,
|
|
parsed_function()->expression_temp_var());
|
|
then_fragment += Drop();
|
|
ASSERT(stack() == top);
|
|
|
|
Fragment otherwise_fragment(otherwise_entry);
|
|
otherwise_fragment += BuildExpression(); // read otherwise.
|
|
otherwise_fragment += StoreLocal(TokenPosition::kNoSource,
|
|
parsed_function()->expression_temp_var());
|
|
otherwise_fragment += Drop();
|
|
ASSERT(stack() == top);
|
|
|
|
JoinEntryInstr* join = BuildJoinEntry();
|
|
then_fragment += Goto(join);
|
|
otherwise_fragment += Goto(join);
|
|
|
|
SkipOptionalDartType(); // read unused static type.
|
|
|
|
return Fragment(instructions.entry, join) +
|
|
LoadLocal(parsed_function()->expression_temp_var());
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildStringConcatenation(TokenPosition* p) {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
intptr_t length = ReadListLength(); // read list length.
|
|
// Note: there will be "length" expressions.
|
|
|
|
Fragment instructions;
|
|
if (length == 1) {
|
|
instructions += BuildExpression(); // read expression.
|
|
instructions += StringInterpolateSingle(position);
|
|
} else {
|
|
// The type arguments for CreateArray.
|
|
instructions += Constant(TypeArguments::ZoneHandle(Z));
|
|
instructions += IntConstant(length);
|
|
instructions += CreateArray();
|
|
LocalVariable* array = MakeTemporary();
|
|
|
|
for (intptr_t i = 0; i < length; ++i) {
|
|
instructions += LoadLocal(array);
|
|
instructions += IntConstant(i);
|
|
instructions += BuildExpression(); // read ith expression.
|
|
instructions += StoreIndexed(kArrayCid);
|
|
instructions += Drop();
|
|
}
|
|
|
|
instructions += StringInterpolate(position);
|
|
}
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildIsExpression(TokenPosition* p) {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
Fragment instructions = BuildExpression(); // read operand.
|
|
|
|
const AbstractType& type = T.BuildType(); // read type.
|
|
|
|
// The VM does not like an instanceOf call with a dynamic type. We need to
|
|
// special case this situation.
|
|
const Type& object_type = Type::Handle(Z, Type::ObjectType());
|
|
|
|
if (type.IsMalformed()) {
|
|
instructions += Drop();
|
|
instructions += ThrowTypeError();
|
|
return instructions;
|
|
}
|
|
|
|
if (type.IsInstantiated() &&
|
|
object_type.IsSubtypeOf(type, NULL, NULL, Heap::kOld)) {
|
|
// Evaluate the expression on the left but ignore it's result.
|
|
instructions += Drop();
|
|
|
|
// Let condition be always true.
|
|
instructions += Constant(Bool::True());
|
|
} else {
|
|
instructions += PushArgument();
|
|
|
|
// See if simple instanceOf is applicable.
|
|
if (dart::FlowGraphBuilder::SimpleInstanceOfType(type)) {
|
|
instructions += Constant(type);
|
|
instructions += PushArgument(); // Type.
|
|
instructions += InstanceCall(
|
|
position, Library::PrivateCoreLibName(Symbols::_simpleInstanceOf()),
|
|
Token::kIS, 2, 2); // 2 checked arguments.
|
|
return instructions;
|
|
}
|
|
|
|
if (!type.IsInstantiated(kCurrentClass)) {
|
|
instructions += LoadInstantiatorTypeArguments();
|
|
} else {
|
|
instructions += NullConstant();
|
|
}
|
|
instructions += PushArgument(); // Instantiator type arguments.
|
|
|
|
if (!type.IsInstantiated(kFunctions)) {
|
|
instructions += LoadFunctionTypeArguments();
|
|
} else {
|
|
instructions += NullConstant();
|
|
}
|
|
instructions += PushArgument(); // Function type arguments.
|
|
|
|
instructions += Constant(type);
|
|
instructions += PushArgument(); // Type.
|
|
|
|
instructions += InstanceCall(
|
|
position, Library::PrivateCoreLibName(Symbols::_instanceOf()),
|
|
Token::kIS, 4);
|
|
}
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildAsExpression(TokenPosition* p) {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
Fragment instructions = BuildExpression(); // read operand.
|
|
|
|
const AbstractType& type = T.BuildType(); // read type.
|
|
|
|
// The VM does not like an Object_as call with a dynamic type. We need to
|
|
// special case this situation.
|
|
const Type& object_type = Type::Handle(Z, Type::ObjectType());
|
|
|
|
if (type.IsMalformed()) {
|
|
instructions += Drop();
|
|
instructions += ThrowTypeError();
|
|
return instructions;
|
|
}
|
|
|
|
if (type.IsInstantiated() &&
|
|
object_type.IsSubtypeOf(type, NULL, NULL, Heap::kOld)) {
|
|
// We already evaluated the operand on the left and just leave it there as
|
|
// the result of the `obj as dynamic` expression.
|
|
} else {
|
|
instructions += PushArgument();
|
|
|
|
if (!type.IsInstantiated(kCurrentClass)) {
|
|
instructions += LoadInstantiatorTypeArguments();
|
|
} else {
|
|
instructions += NullConstant();
|
|
}
|
|
instructions += PushArgument(); // Instantiator type arguments.
|
|
|
|
if (!type.IsInstantiated(kFunctions)) {
|
|
instructions += LoadFunctionTypeArguments();
|
|
} else {
|
|
instructions += NullConstant();
|
|
}
|
|
instructions += PushArgument(); // Function type arguments.
|
|
|
|
instructions += Constant(type);
|
|
instructions += PushArgument(); // Type.
|
|
|
|
instructions += InstanceCall(
|
|
position, Library::PrivateCoreLibName(Symbols::_as()), Token::kAS, 4);
|
|
}
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildSymbolLiteral(
|
|
TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
intptr_t offset = ReaderOffset() - 1; // EvaluateExpression needs the tag.
|
|
SkipStringReference(); // read index into string table.
|
|
return Constant(constant_evaluator_.EvaluateExpression(offset));
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildTypeLiteral(TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
const AbstractType& type = T.BuildType(); // read type.
|
|
if (type.IsMalformed()) H.ReportError("Malformed type literal");
|
|
|
|
Fragment instructions;
|
|
if (type.IsInstantiated()) {
|
|
instructions += Constant(type);
|
|
} else {
|
|
if (!type.IsInstantiated(kCurrentClass)) {
|
|
instructions += LoadInstantiatorTypeArguments();
|
|
} else {
|
|
instructions += NullConstant();
|
|
}
|
|
if (!type.IsInstantiated(kFunctions)) {
|
|
instructions += LoadFunctionTypeArguments();
|
|
} else {
|
|
instructions += NullConstant();
|
|
}
|
|
instructions += InstantiateType(type);
|
|
}
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildThisExpression(
|
|
TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
return LoadLocal(scopes()->this_variable);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildRethrow(TokenPosition* p) {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
Fragment instructions = DebugStepCheck(position);
|
|
instructions += LoadLocal(catch_block()->exception_var());
|
|
instructions += PushArgument();
|
|
instructions += LoadLocal(catch_block()->stack_trace_var());
|
|
instructions += PushArgument();
|
|
instructions += RethrowException(position, catch_block()->catch_try_index());
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildThrow(TokenPosition* p) {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
Fragment instructions;
|
|
|
|
instructions += BuildExpression(); // read expression.
|
|
|
|
if (NeedsDebugStepCheck(stack(), position)) {
|
|
instructions = DebugStepCheck(position) + instructions;
|
|
}
|
|
instructions += PushArgument();
|
|
instructions += ThrowException(position);
|
|
ASSERT(instructions.is_closed());
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildListLiteral(bool is_const,
|
|
TokenPosition* p) {
|
|
if (is_const) {
|
|
intptr_t offset = ReaderOffset() - 1; // Include the tag.
|
|
(p != NULL) ? * p = ReadPosition() : ReadPosition(); // read position.
|
|
|
|
SetOffset(offset);
|
|
SkipExpression(); // read past the ListLiteral.
|
|
return Constant(constant_evaluator_.EvaluateListLiteral(offset));
|
|
}
|
|
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
const TypeArguments& type_arguments = T.BuildTypeArguments(1); // read type.
|
|
intptr_t length = ReadListLength(); // read list length.
|
|
// Note: there will be "length" expressions.
|
|
|
|
// The type argument for the factory call.
|
|
Fragment instructions = TranslateInstantiatedTypeArguments(type_arguments);
|
|
instructions += PushArgument();
|
|
if (length == 0) {
|
|
instructions += Constant(Object::empty_array());
|
|
} else {
|
|
// The type arguments for CreateArray.
|
|
instructions += Constant(type_arguments);
|
|
instructions += IntConstant(length);
|
|
instructions += CreateArray();
|
|
AbstractType& list_type = AbstractType::ZoneHandle(Z);
|
|
if (I->type_checks()) {
|
|
if (type_arguments.IsNull()) {
|
|
// It was dynamic.
|
|
list_type = Object::dynamic_type().raw();
|
|
} else {
|
|
list_type = type_arguments.TypeAt(0);
|
|
}
|
|
}
|
|
|
|
LocalVariable* array = MakeTemporary();
|
|
for (intptr_t i = 0; i < length; ++i) {
|
|
instructions += LoadLocal(array);
|
|
instructions += IntConstant(i);
|
|
instructions += BuildExpression(); // read ith expression.
|
|
instructions += CheckAssignableInCheckedMode(
|
|
list_type, Symbols::ListLiteralElement());
|
|
instructions += StoreIndexed(kArrayCid);
|
|
instructions += Drop();
|
|
}
|
|
}
|
|
instructions += PushArgument(); // The array.
|
|
|
|
const Class& factory_class =
|
|
Class::Handle(Z, Library::LookupCoreClass(Symbols::List()));
|
|
const Function& factory_method = Function::ZoneHandle(
|
|
Z, factory_class.LookupFactory(
|
|
Library::PrivateCoreLibName(Symbols::ListLiteralFactory())));
|
|
|
|
return instructions + StaticCall(position, factory_method, 2);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildMapLiteral(bool is_const,
|
|
TokenPosition* p) {
|
|
if (is_const) {
|
|
intptr_t offset = ReaderOffset() - 1; // Include the tag.
|
|
(p != NULL) ? * p = ReadPosition() : ReadPosition();
|
|
|
|
SetOffset(offset);
|
|
SkipExpression(); // Read past the MapLiteral.
|
|
return Constant(constant_evaluator_.EvaluateMapLiteral(offset));
|
|
}
|
|
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
if (p != NULL) *p = position;
|
|
|
|
const TypeArguments& type_arguments =
|
|
T.BuildTypeArguments(2); // read key_type and value_type.
|
|
|
|
// The type argument for the factory call `new Map<K, V>._fromLiteral(List)`.
|
|
Fragment instructions = TranslateInstantiatedTypeArguments(type_arguments);
|
|
instructions += PushArgument();
|
|
|
|
intptr_t length = ReadListLength(); // read list length.
|
|
// Note: there will be "length" map entries (i.e. key and value expressions).
|
|
|
|
if (length == 0) {
|
|
instructions += Constant(Object::empty_array());
|
|
} else {
|
|
// The type arguments for `new List<X>(int len)`.
|
|
instructions += Constant(TypeArguments::ZoneHandle(Z));
|
|
|
|
// We generate a list of tuples, i.e. [key1, value1, ..., keyN, valueN].
|
|
instructions += IntConstant(2 * length);
|
|
instructions += CreateArray();
|
|
|
|
LocalVariable* array = MakeTemporary();
|
|
for (intptr_t i = 0; i < length; ++i) {
|
|
instructions += LoadLocal(array);
|
|
instructions += IntConstant(2 * i);
|
|
instructions += BuildExpression(); // read ith key.
|
|
instructions += StoreIndexed(kArrayCid);
|
|
instructions += Drop();
|
|
|
|
instructions += LoadLocal(array);
|
|
instructions += IntConstant(2 * i + 1);
|
|
instructions += BuildExpression(); // read ith value.
|
|
instructions += StoreIndexed(kArrayCid);
|
|
instructions += Drop();
|
|
}
|
|
}
|
|
instructions += PushArgument(); // The array.
|
|
|
|
const Class& map_class =
|
|
Class::Handle(Z, Library::LookupCoreClass(Symbols::Map()));
|
|
const Function& factory_method = Function::ZoneHandle(
|
|
Z, map_class.LookupFactory(
|
|
Library::PrivateCoreLibName(Symbols::MapLiteralFactory())));
|
|
|
|
return instructions + StaticCall(position, factory_method, 2);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildFunctionExpression() {
|
|
ReadPosition(); // read position.
|
|
return BuildFunctionNode(TokenPosition::kNoSource, StringIndex());
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildLet(TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
Fragment instructions = BuildVariableDeclaration(); // read variable.
|
|
instructions += BuildExpression(); // read body.
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildBigIntLiteral(
|
|
TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
const String& value =
|
|
H.DartString(ReadStringReference()); // read index into string table.
|
|
const Integer& integer =
|
|
Integer::ZoneHandle(Z, Integer::New(value, Heap::kOld));
|
|
if (integer.IsNull()) {
|
|
H.ReportError("Integer literal %s is out of range", value.ToCString());
|
|
UNREACHABLE();
|
|
}
|
|
return Constant(integer);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildStringLiteral(
|
|
TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
return Constant(
|
|
H.DartSymbol(ReadStringReference())); // read index into string table.
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildIntLiteral(uint8_t payload,
|
|
TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
int64_t value = static_cast<int32_t>(payload) - SpecializedIntLiteralBias;
|
|
return IntConstant(value);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildIntLiteral(bool is_negative,
|
|
TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
int64_t value = is_negative ? -static_cast<int64_t>(ReadUInt())
|
|
: ReadUInt(); // read value.
|
|
return IntConstant(value);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildDoubleLiteral(
|
|
TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
intptr_t offset = ReaderOffset() - 1; // EvaluateExpression needs the tag.
|
|
SkipStringReference(); // read index into string table.
|
|
return Constant(constant_evaluator_.EvaluateExpression(offset));
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildBoolLiteral(bool value,
|
|
TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
return Constant(Bool::Get(value));
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildNullLiteral(TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
return Constant(Instance::ZoneHandle(Z, Instance::null()));
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildVectorCreation(
|
|
TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
intptr_t size = ReadUInt(); // read size.
|
|
return AllocateContext(size);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildVectorGet(TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
Fragment instructions = BuildExpression(); // read expression.
|
|
intptr_t index = ReadUInt(); // read index.
|
|
instructions += LoadField(Context::variable_offset(index));
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildVectorSet(TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
Fragment instructions = NullConstant();
|
|
LocalVariable* result = MakeTemporary();
|
|
|
|
instructions += BuildExpression(); // read vector expression.
|
|
intptr_t index = ReadUInt(); // read index.
|
|
instructions += BuildExpression(); // read value expression.
|
|
instructions += StoreLocal(TokenPosition::kNoSource, result);
|
|
|
|
instructions += StoreInstanceField(TokenPosition::kNoSource,
|
|
Context::variable_offset(index));
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildVectorCopy(TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
Fragment instructions = BuildExpression(); // read vector expression.
|
|
Value* context_to_copy = Pop();
|
|
CloneContextInstr* clone_instruction =
|
|
new (Z) CloneContextInstr(TokenPosition::kNoSource, context_to_copy,
|
|
Thread::Current()->GetNextDeoptId());
|
|
instructions <<= clone_instruction;
|
|
Push(clone_instruction);
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildClosureCreation(
|
|
TokenPosition* position) {
|
|
if (position != NULL) *position = TokenPosition::kNoSource;
|
|
|
|
NameIndex function_reference =
|
|
ReadCanonicalNameReference(); // read function reference.
|
|
Function& function = Function::ZoneHandle(
|
|
Z, H.LookupStaticMethodByKernelProcedure(function_reference));
|
|
function = function.ConvertedClosureFunction();
|
|
ASSERT(!function.IsNull());
|
|
|
|
const Class& closure_class =
|
|
Class::ZoneHandle(Z, I->object_store()->closure_class());
|
|
Fragment instructions = AllocateObject(closure_class, function);
|
|
LocalVariable* closure = MakeTemporary();
|
|
|
|
instructions += BuildExpression(); // read context vector.
|
|
LocalVariable* context = MakeTemporary();
|
|
|
|
instructions += LoadLocal(closure);
|
|
instructions += Constant(function);
|
|
instructions +=
|
|
StoreInstanceField(TokenPosition::kNoSource, Closure::function_offset());
|
|
|
|
instructions += LoadLocal(closure);
|
|
instructions += LoadLocal(context);
|
|
instructions +=
|
|
StoreInstanceField(TokenPosition::kNoSource, Closure::context_offset());
|
|
|
|
SkipDartType(); // skip function type of the closure.
|
|
|
|
// TODO(30455): Kernel generic methods undone. When generic methods are
|
|
// fully supported in kernel, we'll need to store a NULL in the type arguments
|
|
// slot when type arguments are absent, so the wrapper for the target function
|
|
// can tell how many type args are captured vs. provided by the caller of the
|
|
// closure.
|
|
|
|
intptr_t types_count = ReadListLength(); // read type count.
|
|
if (types_count > 0) {
|
|
const TypeArguments& type_args =
|
|
T.BuildTypeArguments(types_count); // read list of type arguments.
|
|
instructions += TranslateInstantiatedTypeArguments(type_args);
|
|
LocalVariable* type_args_slot = MakeTemporary();
|
|
|
|
instructions += LoadLocal(context);
|
|
instructions += LoadLocal(type_args_slot);
|
|
instructions += StoreInstanceField(TokenPosition::kNoSource,
|
|
Context::variable_offset(0));
|
|
|
|
instructions += LoadLocal(closure);
|
|
instructions += LoadLocal(type_args_slot);
|
|
instructions += StoreInstanceField(
|
|
TokenPosition::kNoSource, Closure::function_type_arguments_offset());
|
|
|
|
instructions += Drop(); // type args
|
|
}
|
|
|
|
instructions += Drop(); // context
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildInvalidStatement() {
|
|
H.ReportError("Invalid statements not implemented yet!");
|
|
return Fragment();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildExpressionStatement() {
|
|
Fragment instructions = BuildExpression(); // read expression.
|
|
instructions += Drop();
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildBlock() {
|
|
intptr_t offset = ReaderOffset() - 1; // Include the tag.
|
|
|
|
Fragment instructions;
|
|
|
|
instructions += EnterScope(offset + relative_kernel_offset_);
|
|
intptr_t list_length = ReadListLength(); // read number of statements.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
if (instructions.is_open()) {
|
|
instructions += BuildStatement(); // read ith statement.
|
|
} else {
|
|
SkipStatement(); // read ith statement.
|
|
}
|
|
}
|
|
instructions += ExitScope(offset + relative_kernel_offset_);
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildEmptyStatement() {
|
|
return Fragment();
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildAssertStatement() {
|
|
if (!I->asserts()) {
|
|
SetOffset(ReaderOffset() - 1); // Include the tag.
|
|
SkipStatement(); // read this statement.
|
|
return Fragment();
|
|
}
|
|
|
|
TargetEntryInstr* then;
|
|
TargetEntryInstr* otherwise;
|
|
|
|
Fragment instructions;
|
|
// Asserts can be of the following two kinds:
|
|
//
|
|
// * `assert(expr)`
|
|
// * `assert(() { ... })`
|
|
//
|
|
// The call to `_AssertionError._evaluateAssertion()` will take care of both
|
|
// and returns a boolean.
|
|
instructions += BuildExpression(); // read condition.
|
|
instructions += PushArgument();
|
|
instructions += EvaluateAssertion();
|
|
instructions += CheckBooleanInCheckedMode();
|
|
instructions += Constant(Bool::True());
|
|
instructions += BranchIfEqual(&then, &otherwise, false);
|
|
|
|
TokenPosition condition_start_offset =
|
|
ReadPosition(); // read condition start offset.
|
|
TokenPosition condition_end_offset =
|
|
ReadPosition(); // read condition end offset.
|
|
|
|
const Class& klass =
|
|
Class::ZoneHandle(Z, Library::LookupCoreClass(Symbols::AssertionError()));
|
|
ASSERT(!klass.IsNull());
|
|
const Function& target = Function::ZoneHandle(
|
|
Z, klass.LookupStaticFunctionAllowPrivate(Symbols::ThrowNew()));
|
|
ASSERT(!target.IsNull());
|
|
|
|
// Build call to _AsertionError._throwNew(start, end, message)
|
|
Fragment otherwise_fragment(otherwise);
|
|
otherwise_fragment += IntConstant(condition_start_offset.Pos());
|
|
otherwise_fragment += PushArgument(); // start
|
|
otherwise_fragment += IntConstant(condition_end_offset.Pos());
|
|
otherwise_fragment += PushArgument(); // end
|
|
Tag tag = ReadTag(); // read (first part of) message.
|
|
if (tag == kSomething) {
|
|
otherwise_fragment += BuildExpression(); // read (rest of) message.
|
|
} else {
|
|
otherwise_fragment += Constant(Instance::ZoneHandle(Z)); // null.
|
|
}
|
|
otherwise_fragment += PushArgument(); // message
|
|
|
|
otherwise_fragment += StaticCall(TokenPosition::kNoSource, target, 3);
|
|
otherwise_fragment += Drop();
|
|
|
|
return Fragment(instructions.entry, then);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildLabeledStatement() {
|
|
// There can be serveral cases:
|
|
//
|
|
// * the body contains a break
|
|
// * the body doesn't contain a break
|
|
//
|
|
// * translating the body results in a closed fragment
|
|
// * translating the body results in a open fragment
|
|
//
|
|
// => We will only know which case we are in after the body has been
|
|
// traversed.
|
|
|
|
BreakableBlock block(flow_graph_builder_);
|
|
Fragment instructions = BuildStatement(); // read body.
|
|
if (block.HadJumper()) {
|
|
if (instructions.is_open()) {
|
|
instructions += Goto(block.destination());
|
|
}
|
|
return Fragment(instructions.entry, block.destination());
|
|
} else {
|
|
return instructions;
|
|
}
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildBreakStatement() {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
intptr_t target_index = ReadUInt(); // read target index.
|
|
|
|
TryFinallyBlock* outer_finally = NULL;
|
|
intptr_t target_context_depth = -1;
|
|
JoinEntryInstr* destination = breakable_block()->BreakDestination(
|
|
target_index, &outer_finally, &target_context_depth);
|
|
|
|
Fragment instructions;
|
|
instructions +=
|
|
TranslateFinallyFinalizers(outer_finally, target_context_depth);
|
|
if (instructions.is_open()) {
|
|
if (NeedsDebugStepCheck(parsed_function()->function(), position)) {
|
|
instructions += DebugStepCheck(position);
|
|
}
|
|
instructions += Goto(destination);
|
|
}
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildWhileStatement() {
|
|
loop_depth_inc();
|
|
ReadPosition(); // read position.
|
|
|
|
bool negate;
|
|
Fragment condition = TranslateCondition(&negate); // read condition.
|
|
TargetEntryInstr* body_entry;
|
|
TargetEntryInstr* loop_exit;
|
|
condition += BranchIfTrue(&body_entry, &loop_exit, negate);
|
|
|
|
Fragment body(body_entry);
|
|
body += BuildStatement(); // read body.
|
|
|
|
Instruction* entry;
|
|
if (body.is_open()) {
|
|
JoinEntryInstr* join = BuildJoinEntry();
|
|
body += Goto(join);
|
|
|
|
Fragment loop(join);
|
|
loop += CheckStackOverflow();
|
|
loop += condition;
|
|
entry = new (Z) GotoInstr(join, Thread::Current()->GetNextDeoptId());
|
|
} else {
|
|
entry = condition.entry;
|
|
}
|
|
|
|
loop_depth_dec();
|
|
return Fragment(entry, loop_exit);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildDoStatement() {
|
|
loop_depth_inc();
|
|
ReadPosition(); // read position.
|
|
Fragment body = BuildStatement(); // read body.
|
|
|
|
if (body.is_closed()) {
|
|
SkipExpression(); // read condition.
|
|
loop_depth_dec();
|
|
return body;
|
|
}
|
|
|
|
bool negate;
|
|
JoinEntryInstr* join = BuildJoinEntry();
|
|
Fragment loop(join);
|
|
loop += CheckStackOverflow();
|
|
loop += body;
|
|
loop += TranslateCondition(&negate); // read condition.
|
|
TargetEntryInstr* loop_repeat;
|
|
TargetEntryInstr* loop_exit;
|
|
loop += BranchIfTrue(&loop_repeat, &loop_exit, negate);
|
|
|
|
Fragment repeat(loop_repeat);
|
|
repeat += Goto(join);
|
|
|
|
loop_depth_dec();
|
|
return Fragment(new (Z) GotoInstr(join, Thread::Current()->GetNextDeoptId()),
|
|
loop_exit);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildForStatement() {
|
|
intptr_t offset = ReaderOffset() - 1; // Include the tag.
|
|
|
|
ReadPosition(); // read position.
|
|
|
|
Fragment declarations;
|
|
|
|
bool new_context = false;
|
|
declarations += EnterScope(offset + relative_kernel_offset_, &new_context);
|
|
|
|
intptr_t list_length = ReadListLength(); // read number of variables.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
declarations += BuildVariableDeclaration(); // read ith variable.
|
|
}
|
|
|
|
loop_depth_inc();
|
|
bool negate = false;
|
|
Tag tag = ReadTag(); // Read first part of condition.
|
|
Fragment condition =
|
|
tag == kNothing ? Constant(Bool::True())
|
|
: TranslateCondition(&negate); // read rest of condition.
|
|
TargetEntryInstr* body_entry;
|
|
TargetEntryInstr* loop_exit;
|
|
condition += BranchIfTrue(&body_entry, &loop_exit, negate);
|
|
|
|
Fragment updates;
|
|
list_length = ReadListLength(); // read number of updates.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
updates += BuildExpression(); // read ith update.
|
|
updates += Drop();
|
|
}
|
|
|
|
Fragment body(body_entry);
|
|
body += BuildStatement(); // read body.
|
|
|
|
if (body.is_open()) {
|
|
// We allocated a fresh context before the loop which contains captured
|
|
// [ForStatement] variables. Before jumping back to the loop entry we clone
|
|
// the context object (at same depth) which ensures the next iteration of
|
|
// the body gets a fresh set of [ForStatement] variables (with the old
|
|
// (possibly updated) values).
|
|
if (new_context) body += CloneContext();
|
|
|
|
body += updates;
|
|
JoinEntryInstr* join = BuildJoinEntry();
|
|
declarations += Goto(join);
|
|
body += Goto(join);
|
|
|
|
Fragment loop(join);
|
|
loop += CheckStackOverflow();
|
|
loop += condition;
|
|
} else {
|
|
declarations += condition;
|
|
}
|
|
|
|
Fragment loop(declarations.entry, loop_exit);
|
|
loop_depth_dec();
|
|
|
|
loop += ExitScope(offset + relative_kernel_offset_);
|
|
|
|
return loop;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildForInStatement(bool async) {
|
|
intptr_t offset = ReaderOffset() - 1; // Include the tag.
|
|
|
|
ReadPosition(); // read position.
|
|
TokenPosition body_position = ReadPosition(); // read body position.
|
|
intptr_t variable_kernel_position = ReaderOffset() + relative_kernel_offset_;
|
|
SkipVariableDeclaration(); // read variable.
|
|
|
|
TokenPosition iterable_position = TokenPosition::kNoSource;
|
|
Fragment instructions =
|
|
BuildExpression(&iterable_position); // read iterable.
|
|
instructions += PushArgument();
|
|
|
|
const String& iterator_getter =
|
|
String::ZoneHandle(Z, Field::GetterSymbol(Symbols::Iterator()));
|
|
instructions +=
|
|
InstanceCall(iterable_position, iterator_getter, Token::kGET, 1);
|
|
LocalVariable* iterator = scopes()->iterator_variables[for_in_depth()];
|
|
instructions += StoreLocal(TokenPosition::kNoSource, iterator);
|
|
instructions += Drop();
|
|
|
|
for_in_depth_inc();
|
|
loop_depth_inc();
|
|
Fragment condition = LoadLocal(iterator);
|
|
condition += PushArgument();
|
|
condition +=
|
|
InstanceCall(iterable_position, Symbols::MoveNext(), Token::kILLEGAL, 1);
|
|
TargetEntryInstr* body_entry;
|
|
TargetEntryInstr* loop_exit;
|
|
condition += BranchIfTrue(&body_entry, &loop_exit, false);
|
|
|
|
Fragment body(body_entry);
|
|
body += EnterScope(offset + relative_kernel_offset_);
|
|
body += LoadLocal(iterator);
|
|
body += PushArgument();
|
|
const String& current_getter =
|
|
String::ZoneHandle(Z, Field::GetterSymbol(Symbols::Current()));
|
|
body += InstanceCall(body_position, current_getter, Token::kGET, 1);
|
|
body += StoreLocal(TokenPosition::kNoSource,
|
|
LookupVariable(variable_kernel_position));
|
|
body += Drop();
|
|
body += BuildStatement(); // read body.
|
|
body += ExitScope(offset + relative_kernel_offset_);
|
|
|
|
if (body.is_open()) {
|
|
JoinEntryInstr* join = BuildJoinEntry();
|
|
instructions += Goto(join);
|
|
body += Goto(join);
|
|
|
|
Fragment loop(join);
|
|
loop += CheckStackOverflow();
|
|
loop += condition;
|
|
} else {
|
|
instructions += condition;
|
|
}
|
|
|
|
loop_depth_dec();
|
|
for_in_depth_dec();
|
|
return Fragment(instructions.entry, loop_exit);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildSwitchStatement() {
|
|
ReadPosition(); // read position.
|
|
// We need the number of cases. So start by getting that, then go back.
|
|
intptr_t offset = ReaderOffset();
|
|
SkipExpression(); // temporarily skip condition
|
|
int case_count = ReadListLength(); // read number of cases.
|
|
SetOffset(offset);
|
|
|
|
SwitchBlock block(flow_graph_builder_, case_count);
|
|
|
|
// Instead of using a variable we should reuse the expression on the stack,
|
|
// since it won't be assigned again, we don't need phi nodes.
|
|
Fragment head_instructions = BuildExpression(); // read condition.
|
|
head_instructions +=
|
|
StoreLocal(TokenPosition::kNoSource, scopes()->switch_variable);
|
|
head_instructions += Drop();
|
|
|
|
case_count = ReadListLength(); // read number of cases.
|
|
|
|
// Phase 1: Generate bodies and try to find out whether a body will be target
|
|
// of a jump due to:
|
|
// * `continue case_label`
|
|
// * `case e1: case e2: body`
|
|
Fragment* body_fragments = new Fragment[case_count];
|
|
intptr_t* case_expression_offsets = new intptr_t[case_count];
|
|
int default_case = -1;
|
|
|
|
for (intptr_t i = 0; i < case_count; ++i) {
|
|
case_expression_offsets[i] = ReaderOffset();
|
|
int expression_count = ReadListLength(); // read number of expressions.
|
|
for (intptr_t j = 0; j < expression_count; ++j) {
|
|
ReadPosition(); // read jth position.
|
|
SkipExpression(); // read jth expression.
|
|
}
|
|
bool is_default = ReadBool(); // read is_default.
|
|
if (is_default) default_case = i;
|
|
Fragment& body_fragment = body_fragments[i] =
|
|
BuildStatement(); // read body.
|
|
|
|
if (body_fragment.entry == NULL) {
|
|
// Make a NOP in order to ensure linking works properly.
|
|
body_fragment = NullConstant();
|
|
body_fragment += Drop();
|
|
}
|
|
|
|
// The Dart language specification mandates fall-throughs in [SwitchCase]es
|
|
// to be runtime errors.
|
|
if (!is_default && body_fragment.is_open() && (i < (case_count - 1))) {
|
|
const Class& klass = Class::ZoneHandle(
|
|
Z, Library::LookupCoreClass(Symbols::FallThroughError()));
|
|
ASSERT(!klass.IsNull());
|
|
const Function& constructor = Function::ZoneHandle(
|
|
Z, klass.LookupConstructorAllowPrivate(
|
|
H.DartSymbol("FallThroughError._create")));
|
|
ASSERT(!constructor.IsNull());
|
|
const String& url = H.DartString(
|
|
parsed_function()->function().ToLibNamePrefixedQualifiedCString(),
|
|
Heap::kOld);
|
|
|
|
// Create instance of _FallThroughError
|
|
body_fragment += AllocateObject(TokenPosition::kNoSource, klass, 0);
|
|
LocalVariable* instance = MakeTemporary();
|
|
|
|
// Call _FallThroughError._create constructor.
|
|
body_fragment += LoadLocal(instance);
|
|
body_fragment += PushArgument(); // this
|
|
|
|
body_fragment += Constant(url);
|
|
body_fragment += PushArgument(); // url
|
|
|
|
body_fragment += NullConstant();
|
|
body_fragment += PushArgument(); // line
|
|
|
|
body_fragment += StaticCall(TokenPosition::kNoSource, constructor, 3);
|
|
body_fragment += Drop();
|
|
|
|
// Throw the exception
|
|
body_fragment += PushArgument();
|
|
body_fragment += ThrowException(TokenPosition::kNoSource);
|
|
body_fragment += Drop();
|
|
}
|
|
|
|
// If there is an implicit fall-through we have one [SwitchCase] and
|
|
// multiple expressions, e.g.
|
|
//
|
|
// switch(expr) {
|
|
// case a:
|
|
// case b:
|
|
// <stmt-body>
|
|
// }
|
|
//
|
|
// This means that the <stmt-body> will have more than 1 incoming edge (one
|
|
// from `a == expr` and one from `a != expr && b == expr`). The
|
|
// `block.Destination()` records the additional jump.
|
|
if (expression_count > 1) {
|
|
block.DestinationDirect(i);
|
|
}
|
|
}
|
|
|
|
intptr_t end_offset = ReaderOffset();
|
|
|
|
// Phase 2: Generate everything except the real bodies:
|
|
// * jump directly to a body (if there is no jumper)
|
|
// * jump to a wrapper block which jumps to the body (if there is a jumper)
|
|
Fragment current_instructions = head_instructions;
|
|
for (intptr_t i = 0; i < case_count; ++i) {
|
|
SetOffset(case_expression_offsets[i]);
|
|
int expression_count = ReadListLength(); // read length of expressions.
|
|
|
|
if (i == default_case) {
|
|
ASSERT(i == (case_count - 1));
|
|
|
|
// Evaluate the conditions for the default [SwitchCase] just for the
|
|
// purpose of potentially triggering a compile-time error.
|
|
|
|
for (intptr_t j = 0; j < expression_count; ++j) {
|
|
ReadPosition(); // read jth position.
|
|
// this reads the expression, but doesn't skip past it.
|
|
constant_evaluator_.EvaluateExpression(ReaderOffset());
|
|
SkipExpression(); // read jth expression.
|
|
}
|
|
|
|
if (block.HadJumper(i)) {
|
|
// There are several branches to the body, so we will make a goto to
|
|
// the join block (and prepend a join instruction to the real body).
|
|
JoinEntryInstr* join = block.DestinationDirect(i);
|
|
current_instructions += Goto(join);
|
|
|
|
current_instructions = Fragment(current_instructions.entry, join);
|
|
current_instructions += body_fragments[i];
|
|
} else {
|
|
current_instructions += body_fragments[i];
|
|
}
|
|
} else {
|
|
JoinEntryInstr* body_join = NULL;
|
|
if (block.HadJumper(i)) {
|
|
body_join = block.DestinationDirect(i);
|
|
body_fragments[i] = Fragment(body_join) + body_fragments[i];
|
|
}
|
|
|
|
for (intptr_t j = 0; j < expression_count; ++j) {
|
|
TargetEntryInstr* then;
|
|
TargetEntryInstr* otherwise;
|
|
|
|
TokenPosition position = ReadPosition(); // read jth position.
|
|
current_instructions +=
|
|
Constant(constant_evaluator_.EvaluateExpression(ReaderOffset()));
|
|
SkipExpression(); // read jth expression.
|
|
current_instructions += PushArgument();
|
|
current_instructions += LoadLocal(scopes()->switch_variable);
|
|
current_instructions += PushArgument();
|
|
current_instructions +=
|
|
InstanceCall(position, Symbols::EqualOperator(), Token::kEQ,
|
|
/*argument_count=*/2,
|
|
/*checked_argument_count=*/2);
|
|
current_instructions += BranchIfTrue(&then, &otherwise, false);
|
|
|
|
Fragment then_fragment(then);
|
|
|
|
if (body_join != NULL) {
|
|
// There are several branches to the body, so we will make a goto to
|
|
// the join block (the real body has already been prepended with a
|
|
// join instruction).
|
|
then_fragment += Goto(body_join);
|
|
} else {
|
|
// There is only a signle branch to the body, so we will just append
|
|
// the body fragment.
|
|
then_fragment += body_fragments[i];
|
|
}
|
|
|
|
current_instructions = Fragment(otherwise);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (case_count > 0 && default_case < 0) {
|
|
// There is no default, which means we have an open [current_instructions]
|
|
// (which is a [TargetEntryInstruction] for the last "otherwise" branch).
|
|
//
|
|
// Furthermore the last [SwitchCase] can be open as well. If so, we need
|
|
// to join these two.
|
|
Fragment& last_body = body_fragments[case_count - 1];
|
|
if (last_body.is_open()) {
|
|
ASSERT(current_instructions.is_open());
|
|
ASSERT(current_instructions.current->IsTargetEntry());
|
|
|
|
// Join the last "otherwise" branch and the last [SwitchCase] fragment.
|
|
JoinEntryInstr* join = BuildJoinEntry();
|
|
current_instructions += Goto(join);
|
|
last_body += Goto(join);
|
|
|
|
current_instructions = Fragment(join);
|
|
}
|
|
} else {
|
|
// All non-default cases will be closed (i.e. break/continue/throw/return)
|
|
// So it is fine to just let more statements after the switch append to the
|
|
// default case.
|
|
}
|
|
|
|
delete[] body_fragments;
|
|
delete[] case_expression_offsets;
|
|
|
|
SetOffset(end_offset);
|
|
return Fragment(head_instructions.entry, current_instructions.current);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildContinueSwitchStatement() {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
intptr_t target_index = ReadUInt(); // read target index.
|
|
|
|
TryFinallyBlock* outer_finally = NULL;
|
|
intptr_t target_context_depth = -1;
|
|
JoinEntryInstr* entry = switch_block()->Destination(
|
|
target_index, &outer_finally, &target_context_depth);
|
|
|
|
Fragment instructions;
|
|
instructions +=
|
|
TranslateFinallyFinalizers(outer_finally, target_context_depth);
|
|
if (instructions.is_open()) {
|
|
if (NeedsDebugStepCheck(parsed_function()->function(), position)) {
|
|
instructions += DebugStepCheck(position);
|
|
}
|
|
instructions += Goto(entry);
|
|
}
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildIfStatement() {
|
|
bool negate;
|
|
Fragment instructions = TranslateCondition(&negate); // read condition.
|
|
TargetEntryInstr* then_entry;
|
|
TargetEntryInstr* otherwise_entry;
|
|
instructions += BranchIfTrue(&then_entry, &otherwise_entry, negate);
|
|
|
|
Fragment then_fragment(then_entry);
|
|
then_fragment += BuildStatement(); // read then.
|
|
|
|
Fragment otherwise_fragment(otherwise_entry);
|
|
otherwise_fragment += BuildStatement(); // read otherwise.
|
|
|
|
if (then_fragment.is_open()) {
|
|
if (otherwise_fragment.is_open()) {
|
|
JoinEntryInstr* join = BuildJoinEntry();
|
|
then_fragment += Goto(join);
|
|
otherwise_fragment += Goto(join);
|
|
return Fragment(instructions.entry, join);
|
|
} else {
|
|
return Fragment(instructions.entry, then_fragment.current);
|
|
}
|
|
} else if (otherwise_fragment.is_open()) {
|
|
return Fragment(instructions.entry, otherwise_fragment.current);
|
|
} else {
|
|
return instructions.closed();
|
|
}
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildReturnStatement() {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
Tag tag = ReadTag(); // read first part of expression.
|
|
|
|
bool inside_try_finally = try_finally_block() != NULL;
|
|
|
|
Fragment instructions = tag == kNothing
|
|
? NullConstant()
|
|
: BuildExpression(); // read rest of expression.
|
|
|
|
if (instructions.is_open()) {
|
|
if (inside_try_finally) {
|
|
ASSERT(scopes()->finally_return_variable != NULL);
|
|
const Function& function = parsed_function()->function();
|
|
if (NeedsDebugStepCheck(function, position)) {
|
|
instructions += DebugStepCheck(position);
|
|
}
|
|
instructions += StoreLocal(position, scopes()->finally_return_variable);
|
|
instructions += Drop();
|
|
instructions += TranslateFinallyFinalizers(NULL, -1);
|
|
if (instructions.is_open()) {
|
|
instructions += LoadLocal(scopes()->finally_return_variable);
|
|
instructions += Return(TokenPosition::kNoSource);
|
|
}
|
|
} else {
|
|
instructions += Return(position);
|
|
}
|
|
} else {
|
|
Pop();
|
|
}
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildTryCatch() {
|
|
InlineBailout("kernel::FlowgraphBuilder::VisitTryCatch");
|
|
|
|
intptr_t try_handler_index = AllocateTryIndex();
|
|
Fragment try_body = TryCatch(try_handler_index);
|
|
JoinEntryInstr* after_try = BuildJoinEntry();
|
|
|
|
// Fill in the body of the try.
|
|
try_depth_inc();
|
|
{
|
|
TryCatchBlock block(flow_graph_builder_, try_handler_index);
|
|
try_body += BuildStatement(); // read body.
|
|
try_body += Goto(after_try);
|
|
}
|
|
try_depth_dec();
|
|
|
|
bool needs_stacktrace = ReadBool(); // read any_catch_needs_stack_trace
|
|
|
|
catch_depth_inc();
|
|
intptr_t catch_count = ReadListLength(); // read number of catches.
|
|
const Array& handler_types =
|
|
Array::ZoneHandle(Z, Array::New(catch_count, Heap::kOld));
|
|
Fragment catch_body =
|
|
CatchBlockEntry(handler_types, try_handler_index, needs_stacktrace);
|
|
// Fill in the body of the catch.
|
|
for (intptr_t i = 0; i < catch_count; ++i) {
|
|
intptr_t catch_offset = ReaderOffset(); // Catch has no tag.
|
|
Tag tag = PeekTag(); // peek guard type.
|
|
AbstractType* type_guard = NULL;
|
|
if (tag != kDynamicType) {
|
|
type_guard = &T.BuildType(); // read guard.
|
|
handler_types.SetAt(i, *type_guard);
|
|
} else {
|
|
SkipDartType(); // read guard.
|
|
handler_types.SetAt(i, Object::dynamic_type());
|
|
}
|
|
|
|
Fragment catch_handler_body =
|
|
EnterScope(catch_offset + relative_kernel_offset_);
|
|
|
|
tag = ReadTag(); // read first part of exception.
|
|
if (tag == kSomething) {
|
|
catch_handler_body += LoadLocal(CurrentException());
|
|
catch_handler_body +=
|
|
StoreLocal(TokenPosition::kNoSource,
|
|
LookupVariable(ReaderOffset() + relative_kernel_offset_));
|
|
catch_handler_body += Drop();
|
|
SkipVariableDeclaration(); // read exception.
|
|
}
|
|
|
|
tag = ReadTag(); // read first part of stack trace.
|
|
if (tag == kSomething) {
|
|
catch_handler_body += LoadLocal(CurrentStackTrace());
|
|
catch_handler_body +=
|
|
StoreLocal(TokenPosition::kNoSource,
|
|
LookupVariable(ReaderOffset() + relative_kernel_offset_));
|
|
catch_handler_body += Drop();
|
|
SkipVariableDeclaration(); // read stack trace.
|
|
}
|
|
|
|
{
|
|
CatchBlock block(flow_graph_builder_, CurrentException(),
|
|
CurrentStackTrace(), try_handler_index);
|
|
|
|
catch_handler_body += BuildStatement(); // read body.
|
|
|
|
// Note: ExitScope adjusts context_depth_ so even if catch_handler_body
|
|
// is closed we still need to execute ExitScope for its side effect.
|
|
catch_handler_body += ExitScope(catch_offset + relative_kernel_offset_);
|
|
if (catch_handler_body.is_open()) {
|
|
catch_handler_body += Goto(after_try);
|
|
}
|
|
}
|
|
|
|
if (type_guard != NULL) {
|
|
if (type_guard->IsMalformed()) {
|
|
catch_body += ThrowTypeError();
|
|
catch_body += Drop();
|
|
} else {
|
|
catch_body += LoadLocal(CurrentException());
|
|
catch_body += PushArgument(); // exception
|
|
if (!type_guard->IsInstantiated(kCurrentClass)) {
|
|
catch_body += LoadInstantiatorTypeArguments();
|
|
} else {
|
|
catch_body += NullConstant();
|
|
}
|
|
catch_body += PushArgument(); // instantiator type arguments
|
|
if (!type_guard->IsInstantiated(kFunctions)) {
|
|
catch_body += LoadFunctionTypeArguments();
|
|
} else {
|
|
catch_body += NullConstant();
|
|
}
|
|
catch_body += PushArgument(); // function type arguments
|
|
catch_body += Constant(*type_guard);
|
|
catch_body += PushArgument(); // guard type
|
|
catch_body += InstanceCall(
|
|
TokenPosition::kNoSource,
|
|
Library::PrivateCoreLibName(Symbols::_instanceOf()), Token::kIS, 4);
|
|
|
|
TargetEntryInstr* catch_entry;
|
|
TargetEntryInstr* next_catch_entry;
|
|
catch_body += BranchIfTrue(&catch_entry, &next_catch_entry, false);
|
|
|
|
Fragment(catch_entry) + catch_handler_body;
|
|
catch_body = Fragment(next_catch_entry);
|
|
}
|
|
} else {
|
|
catch_body += catch_handler_body;
|
|
}
|
|
}
|
|
|
|
// In case the last catch body was not handling the exception and branching to
|
|
// after the try block, we will rethrow the exception (i.e. no default catch
|
|
// handler).
|
|
if (catch_body.is_open()) {
|
|
catch_body += LoadLocal(CurrentException());
|
|
catch_body += PushArgument();
|
|
catch_body += LoadLocal(CurrentStackTrace());
|
|
catch_body += PushArgument();
|
|
catch_body += RethrowException(TokenPosition::kNoSource, try_handler_index);
|
|
Drop();
|
|
}
|
|
catch_depth_dec();
|
|
|
|
return Fragment(try_body.entry, after_try);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildTryFinally() {
|
|
// Note on streaming:
|
|
// We only stream this TryFinally if we can stream everything inside it,
|
|
// so creating a "TryFinallyBlock" with a kernel binary offset instead of an
|
|
// AST node isn't a problem.
|
|
|
|
InlineBailout("kernel::FlowgraphBuilder::VisitTryFinally");
|
|
|
|
// There are 5 different cases where we need to execute the finally block:
|
|
//
|
|
// a) 1/2/3th case: Special control flow going out of `node->body()`:
|
|
//
|
|
// * [BreakStatement] transfers control to a [LabledStatement]
|
|
// * [ContinueSwitchStatement] transfers control to a [SwitchCase]
|
|
// * [ReturnStatement] returns a value
|
|
//
|
|
// => All three cases will automatically append all finally blocks
|
|
// between the branching point and the destination (so we don't need to
|
|
// do anything here).
|
|
//
|
|
// b) 4th case: Translating the body resulted in an open fragment (i.e. body
|
|
// executes without any control flow out of it)
|
|
//
|
|
// => We are responsible for jumping out of the body to a new block (with
|
|
// different try index) and execute the finalizer.
|
|
//
|
|
// c) 5th case: An exception occurred inside the body.
|
|
//
|
|
// => We are responsible for catching it, executing the finally block and
|
|
// rethrowing the exception.
|
|
intptr_t try_handler_index = AllocateTryIndex();
|
|
Fragment try_body = TryCatch(try_handler_index);
|
|
JoinEntryInstr* after_try = BuildJoinEntry();
|
|
|
|
intptr_t offset = ReaderOffset();
|
|
SkipStatement(); // temporarily read body.
|
|
intptr_t finalizer_offset = ReaderOffset();
|
|
SetOffset(offset);
|
|
|
|
// Fill in the body of the try.
|
|
try_depth_inc();
|
|
{
|
|
TryFinallyBlock tfb(flow_graph_builder_, finalizer_offset);
|
|
TryCatchBlock tcb(flow_graph_builder_, try_handler_index);
|
|
try_body += BuildStatement(); // read body.
|
|
}
|
|
try_depth_dec();
|
|
|
|
if (try_body.is_open()) {
|
|
// Please note: The try index will be on level out of this block,
|
|
// thereby ensuring if there's an exception in the finally block we
|
|
// won't run it twice.
|
|
JoinEntryInstr* finally_entry = BuildJoinEntry();
|
|
|
|
try_body += Goto(finally_entry);
|
|
|
|
Fragment finally_body(finally_entry);
|
|
finally_body += BuildStatement(); // read finalizer.
|
|
finally_body += Goto(after_try);
|
|
}
|
|
|
|
// Fill in the body of the catch.
|
|
catch_depth_inc();
|
|
const Array& handler_types = Array::ZoneHandle(Z, Array::New(1, Heap::kOld));
|
|
handler_types.SetAt(0, Object::dynamic_type());
|
|
// Note: rethrow will actually force mark the handler as needing a stacktrace.
|
|
Fragment finally_body = CatchBlockEntry(handler_types, try_handler_index,
|
|
/* needs_stacktrace = */ false);
|
|
SetOffset(finalizer_offset);
|
|
finally_body += BuildStatement(); // read finalizer
|
|
if (finally_body.is_open()) {
|
|
finally_body += LoadLocal(CurrentException());
|
|
finally_body += PushArgument();
|
|
finally_body += LoadLocal(CurrentStackTrace());
|
|
finally_body += PushArgument();
|
|
finally_body +=
|
|
RethrowException(TokenPosition::kNoSource, try_handler_index);
|
|
Drop();
|
|
}
|
|
catch_depth_dec();
|
|
|
|
return Fragment(try_body.entry, after_try);
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildYieldStatement() {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
uint8_t flags = ReadByte(); // read flags.
|
|
|
|
ASSERT(flags == kNativeYieldFlags); // Must have been desugared.
|
|
|
|
// Setup yield/continue point:
|
|
//
|
|
// ...
|
|
// :await_jump_var = index;
|
|
// :await_ctx_var = :current_context_var
|
|
// return <expr>
|
|
//
|
|
// Continuation<index>:
|
|
// Drop(1)
|
|
// ...
|
|
//
|
|
// BuildGraphOfFunction will create a dispatch that jumps to
|
|
// Continuation<:await_jump_var> upon entry to the function.
|
|
//
|
|
Fragment instructions = IntConstant(yield_continuations().length() + 1);
|
|
instructions +=
|
|
StoreLocal(TokenPosition::kNoSource, scopes()->yield_jump_variable);
|
|
instructions += Drop();
|
|
instructions += LoadLocal(parsed_function()->current_context_var());
|
|
instructions +=
|
|
StoreLocal(TokenPosition::kNoSource, scopes()->yield_context_variable);
|
|
instructions += Drop();
|
|
instructions += BuildExpression(); // read expression.
|
|
instructions += Return(TokenPosition::kNoSource);
|
|
|
|
// Note: DropTempsInstr serves as an anchor instruction. It will not
|
|
// be linked into the resulting graph.
|
|
DropTempsInstr* anchor = new (Z) DropTempsInstr(0, NULL);
|
|
yield_continuations().Add(YieldContinuation(anchor, CurrentTryIndex()));
|
|
|
|
Fragment continuation(instructions.entry, anchor);
|
|
|
|
if (parsed_function()->function().IsAsyncClosure() ||
|
|
parsed_function()->function().IsAsyncGenClosure()) {
|
|
// If function is async closure or async gen closure it takes three
|
|
// parameters where the second and the third are exception and stack_trace.
|
|
// Check if exception is non-null and rethrow it.
|
|
//
|
|
// :async_op([:result, :exception, :stack_trace]) {
|
|
// ...
|
|
// Continuation<index>:
|
|
// if (:exception != null) rethrow(:exception, :stack_trace);
|
|
// ...
|
|
// }
|
|
//
|
|
LocalScope* scope = parsed_function()->node_sequence()->scope();
|
|
LocalVariable* exception_var = scope->VariableAt(2);
|
|
LocalVariable* stack_trace_var = scope->VariableAt(3);
|
|
ASSERT(exception_var->name().raw() == Symbols::ExceptionParameter().raw());
|
|
ASSERT(stack_trace_var->name().raw() ==
|
|
Symbols::StackTraceParameter().raw());
|
|
|
|
TargetEntryInstr* no_error;
|
|
TargetEntryInstr* error;
|
|
|
|
continuation += LoadLocal(exception_var);
|
|
continuation += BranchIfNull(&no_error, &error);
|
|
|
|
Fragment rethrow(error);
|
|
rethrow += LoadLocal(exception_var);
|
|
rethrow += PushArgument();
|
|
rethrow += LoadLocal(stack_trace_var);
|
|
rethrow += PushArgument();
|
|
rethrow += RethrowException(position, CatchClauseNode::kInvalidTryIndex);
|
|
Drop();
|
|
|
|
continuation = Fragment(continuation.entry, no_error);
|
|
}
|
|
|
|
return continuation;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildVariableDeclaration() {
|
|
intptr_t kernel_position_no_tag = ReaderOffset() + relative_kernel_offset_;
|
|
LocalVariable* variable = LookupVariable(kernel_position_no_tag);
|
|
|
|
VariableDeclarationHelper helper(this);
|
|
helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
|
|
String& name = H.DartSymbol(helper.name_index_);
|
|
AbstractType& type = T.BuildType(); // read type.
|
|
Tag tag = ReadTag(); // read (first part of) initializer.
|
|
|
|
Fragment instructions;
|
|
if (tag == kNothing) {
|
|
instructions += NullConstant();
|
|
} else {
|
|
if (helper.IsConst()) {
|
|
const Instance& constant_value = constant_evaluator_.EvaluateExpression(
|
|
ReaderOffset()); // read initializer form current position.
|
|
variable->SetConstValue(constant_value);
|
|
instructions += Constant(constant_value);
|
|
SkipExpression(); // skip initializer.
|
|
} else {
|
|
// Initializer
|
|
instructions += BuildExpression(); // read (actual) initializer.
|
|
instructions += CheckVariableTypeInCheckedMode(type, name);
|
|
}
|
|
}
|
|
|
|
// Use position of equal sign if it exists. If the equal sign does not exist
|
|
// use the position of the identifier.
|
|
TokenPosition debug_position =
|
|
Utils::Maximum(helper.position_, helper.equals_position_);
|
|
if (NeedsDebugStepCheck(stack(), debug_position)) {
|
|
instructions = DebugStepCheck(debug_position) + instructions;
|
|
}
|
|
instructions += StoreLocal(helper.position_, variable);
|
|
instructions += Drop();
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildFunctionDeclaration() {
|
|
TokenPosition position = ReadPosition(); // read position.
|
|
intptr_t variable_offset = ReaderOffset() + relative_kernel_offset_;
|
|
|
|
// read variable declaration.
|
|
VariableDeclarationHelper helper(this);
|
|
helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd);
|
|
|
|
Fragment instructions = DebugStepCheck(position);
|
|
instructions += BuildFunctionNode(position, helper.name_index_);
|
|
instructions += StoreLocal(position, LookupVariable(variable_offset));
|
|
instructions += Drop();
|
|
return instructions;
|
|
}
|
|
|
|
Fragment StreamingFlowGraphBuilder::BuildFunctionNode(
|
|
TokenPosition parent_position,
|
|
StringIndex name_index) {
|
|
intptr_t offset = ReaderOffset() + relative_kernel_offset_;
|
|
|
|
FunctionNodeHelper function_node_helper(this);
|
|
function_node_helper.ReadUntilExcluding(
|
|
FunctionNodeHelper::kTotalParameterCount);
|
|
TokenPosition position = function_node_helper.position_;
|
|
|
|
bool declaration = name_index >= 0;
|
|
|
|
if (declaration) {
|
|
position = parent_position;
|
|
}
|
|
if (!position.IsReal()) {
|
|
// Positions has to be unique in regards to the parent.
|
|
// A non-real at this point is probably -1, we cannot blindly use that
|
|
// as others might use it too. Create a new dummy non-real TokenPosition.
|
|
position = TokenPosition(offset).ToSynthetic();
|
|
}
|
|
|
|
// The VM has a per-isolate table of functions indexed by the enclosing
|
|
// function and token position.
|
|
Function& function = Function::ZoneHandle(Z);
|
|
|
|
// NOTE: This is not TokenPosition in the general sense!
|
|
function = I->LookupClosureFunction(parsed_function()->function(), position);
|
|
if (function.IsNull()) {
|
|
for (intptr_t i = 0; i < scopes()->function_scopes.length(); ++i) {
|
|
if (scopes()->function_scopes[i].kernel_offset != offset) {
|
|
continue;
|
|
}
|
|
|
|
const String* name;
|
|
if (declaration) {
|
|
name = &H.DartSymbol(name_index);
|
|
} else {
|
|
name = &Symbols::AnonymousClosure();
|
|
}
|
|
// NOTE: This is not TokenPosition in the general sense!
|
|
function = Function::NewClosureFunction(
|
|
*name, parsed_function()->function(), position);
|
|
|
|
function.set_is_debuggable(function_node_helper.dart_async_marker_ ==
|
|
FunctionNodeHelper::kSync);
|
|
switch (function_node_helper.dart_async_marker_) {
|
|
case FunctionNodeHelper::kSyncStar:
|
|
function.set_modifier(RawFunction::kSyncGen);
|
|
break;
|
|
case FunctionNodeHelper::kAsync:
|
|
function.set_modifier(RawFunction::kAsync);
|
|
function.set_is_inlinable(!FLAG_causal_async_stacks);
|
|
break;
|
|
case FunctionNodeHelper::kAsyncStar:
|
|
function.set_modifier(RawFunction::kAsyncGen);
|
|
function.set_is_inlinable(!FLAG_causal_async_stacks);
|
|
break;
|
|
default:
|
|
// no special modifier
|
|
break;
|
|
}
|
|
function.set_is_generated_body(function_node_helper.async_marker_ ==
|
|
FunctionNodeHelper::kSyncYielding);
|
|
if (function.IsAsyncClosure() || function.IsAsyncGenClosure()) {
|
|
function.set_is_inlinable(!FLAG_causal_async_stacks);
|
|
}
|
|
|
|
function.set_end_token_pos(function_node_helper.end_position_);
|
|
LocalScope* scope = scopes()->function_scopes[i].scope;
|
|
const ContextScope& context_scope = ContextScope::Handle(
|
|
Z, scope->PreserveOuterScope(flow_graph_builder_->context_depth_));
|
|
function.set_context_scope(context_scope);
|
|
function.set_kernel_offset(offset);
|
|
SetupFunctionParameters(Class::Handle(Z), function,
|
|
false, // is_method
|
|
true, // is_closure
|
|
&function_node_helper);
|
|
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd);
|
|
TypedData& kernel_data = reader_->CopyDataToVMHeap(
|
|
Z, offset - relative_kernel_offset_, ReaderOffset());
|
|
function.set_kernel_data(kernel_data);
|
|
|
|
// Finalize function type.
|
|
Type& signature_type = Type::Handle(Z, function.SignatureType());
|
|
signature_type ^=
|
|
ClassFinalizer::FinalizeType(*active_class()->klass, signature_type);
|
|
function.SetSignatureType(signature_type);
|
|
|
|
I->AddClosureFunction(function);
|
|
break;
|
|
}
|
|
}
|
|
|
|
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd);
|
|
|
|
const Class& closure_class =
|
|
Class::ZoneHandle(Z, I->object_store()->closure_class());
|
|
ASSERT(!closure_class.IsNull());
|
|
Fragment instructions =
|
|
flow_graph_builder_->AllocateObject(closure_class, function);
|
|
LocalVariable* closure = MakeTemporary();
|
|
|
|
// The function signature can have uninstantiated class type parameters.
|
|
//
|
|
// TODO(regis): Also handle the case of a function signature that has
|
|
// uninstantiated function type parameters.
|
|
if (!function.HasInstantiatedSignature(kCurrentClass)) {
|
|
instructions += LoadLocal(closure);
|
|
instructions += LoadInstantiatorTypeArguments();
|
|
instructions += flow_graph_builder_->StoreInstanceField(
|
|
TokenPosition::kNoSource,
|
|
Closure::instantiator_type_arguments_offset());
|
|
}
|
|
|
|
// Store the function and the context in the closure.
|
|
instructions += LoadLocal(closure);
|
|
instructions += Constant(function);
|
|
instructions += flow_graph_builder_->StoreInstanceField(
|
|
TokenPosition::kNoSource, Closure::function_offset());
|
|
|
|
instructions += LoadLocal(closure);
|
|
instructions += LoadLocal(parsed_function()->current_context_var());
|
|
instructions += flow_graph_builder_->StoreInstanceField(
|
|
TokenPosition::kNoSource, Closure::context_offset());
|
|
|
|
return instructions;
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::SetupFunctionParameters(
|
|
const Class& klass,
|
|
const Function& function,
|
|
bool is_method,
|
|
bool is_closure,
|
|
FunctionNodeHelper* function_node_helper) {
|
|
ASSERT(!(is_method && is_closure));
|
|
bool is_factory = function.IsFactory();
|
|
intptr_t extra_parameters = (is_method || is_closure || is_factory) ? 1 : 0;
|
|
|
|
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 = ReadListLength(); // read list length.
|
|
|
|
intptr_t named_parameter_count =
|
|
total_parameter_count - positional_parameter_count;
|
|
|
|
function.set_num_fixed_parameters(extra_parameters +
|
|
required_parameter_count);
|
|
if (named_parameter_count > 0) {
|
|
function.SetNumOptionalParameters(named_parameter_count, false);
|
|
} else {
|
|
function.SetNumOptionalParameters(
|
|
positional_parameter_count - required_parameter_count, true);
|
|
}
|
|
intptr_t parameter_count = extra_parameters + total_parameter_count;
|
|
function.set_parameter_types(
|
|
Array::Handle(Z, Array::New(parameter_count, Heap::kOld)));
|
|
function.set_parameter_names(
|
|
Array::Handle(Z, Array::New(parameter_count, Heap::kOld)));
|
|
intptr_t pos = 0;
|
|
if (is_method) {
|
|
ASSERT(!klass.IsNull());
|
|
function.SetParameterTypeAt(pos, H.GetCanonicalType(klass));
|
|
function.SetParameterNameAt(pos, Symbols::This());
|
|
pos++;
|
|
} else if (is_closure) {
|
|
function.SetParameterTypeAt(pos, AbstractType::dynamic_type());
|
|
function.SetParameterNameAt(pos, Symbols::ClosureParameter());
|
|
pos++;
|
|
} else if (is_factory) {
|
|
function.SetParameterTypeAt(pos, AbstractType::dynamic_type());
|
|
function.SetParameterNameAt(pos, Symbols::TypeArgumentsParameter());
|
|
pos++;
|
|
}
|
|
|
|
for (intptr_t i = 0; i < positional_parameter_count; ++i, ++pos) {
|
|
// Read ith variable declaration.
|
|
VariableDeclarationHelper helper(this);
|
|
helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
|
|
const AbstractType& type = T.BuildTypeWithoutFinalization(); // read type.
|
|
Tag tag = ReadTag(); // read (first part of) initializer.
|
|
if (tag == kSomething) {
|
|
SkipExpression(); // read (actual) initializer.
|
|
}
|
|
|
|
function.SetParameterTypeAt(
|
|
pos, type.IsMalformed() ? Type::dynamic_type() : type);
|
|
function.SetParameterNameAt(pos, H.DartSymbol(helper.name_index_));
|
|
}
|
|
|
|
intptr_t named_parameter_count_check = ReadListLength(); // read list length.
|
|
ASSERT(named_parameter_count_check == named_parameter_count);
|
|
for (intptr_t i = 0; i < named_parameter_count; ++i, ++pos) {
|
|
// Read ith variable declaration.
|
|
VariableDeclarationHelper helper(this);
|
|
helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
|
|
const AbstractType& type = T.BuildTypeWithoutFinalization(); // read type.
|
|
Tag tag = ReadTag(); // read (first part of) initializer.
|
|
if (tag == kSomething) {
|
|
SkipExpression(); // read (actual) initializer.
|
|
}
|
|
|
|
function.SetParameterTypeAt(
|
|
pos, type.IsMalformed() ? Type::dynamic_type() : type);
|
|
function.SetParameterNameAt(pos, H.DartSymbol(helper.name_index_));
|
|
}
|
|
|
|
function_node_helper->SetJustRead(FunctionNodeHelper::kNamedParameters);
|
|
|
|
// The result type for generative constructors has already been set.
|
|
if (!function.IsGenerativeConstructor()) {
|
|
const AbstractType& return_type =
|
|
T.BuildTypeWithoutFinalization(); // read return type.
|
|
function.set_result_type(return_type.IsMalformed() ? Type::dynamic_type()
|
|
: return_type);
|
|
function_node_helper->SetJustRead(FunctionNodeHelper::kReturnType);
|
|
}
|
|
}
|
|
|
|
RawObject* StreamingFlowGraphBuilder::BuildParameterDescriptor(
|
|
intptr_t kernel_offset) {
|
|
SetOffset(kernel_offset);
|
|
ReadUntilFunctionNode(); // read until function node.
|
|
FunctionNodeHelper function_node_helper(this);
|
|
function_node_helper.ReadUntilExcluding(
|
|
FunctionNodeHelper::kPositionalParameters);
|
|
intptr_t param_count = function_node_helper.total_parameter_count_;
|
|
intptr_t positional_count = ReadListLength(); // read list length.
|
|
intptr_t named_parameter_count = param_count - positional_count;
|
|
|
|
const Array& param_descriptor = Array::Handle(
|
|
Array::New(param_count * Parser::kParameterEntrySize, Heap::kOld));
|
|
for (intptr_t i = 0; i < param_count; ++i) {
|
|
const intptr_t entry_start = i * Parser::kParameterEntrySize;
|
|
|
|
if (i == positional_count) {
|
|
intptr_t named_parameter_count_check =
|
|
ReadListLength(); // read list length.
|
|
ASSERT(named_parameter_count_check == named_parameter_count);
|
|
}
|
|
|
|
// Read ith variable declaration.
|
|
VariableDeclarationHelper helper(this);
|
|
helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer);
|
|
param_descriptor.SetAt(entry_start + Parser::kParameterIsFinalOffset,
|
|
helper.IsFinal() ? Bool::True() : Bool::False());
|
|
|
|
Tag tag = ReadTag(); // read (first part of) initializer.
|
|
if (tag == kSomething) {
|
|
// this will (potentially) read the initializer, but reset the position.
|
|
Instance& constant =
|
|
constant_evaluator_.EvaluateExpression(ReaderOffset());
|
|
SkipExpression(); // read (actual) initializer.
|
|
param_descriptor.SetAt(entry_start + Parser::kParameterDefaultValueOffset,
|
|
constant);
|
|
} else {
|
|
param_descriptor.SetAt(entry_start + Parser::kParameterDefaultValueOffset,
|
|
Object::null_instance());
|
|
}
|
|
|
|
param_descriptor.SetAt(entry_start + Parser::kParameterMetadataOffset,
|
|
/* Issue(28434): Missing parameter metadata. */
|
|
Object::null_instance());
|
|
}
|
|
return param_descriptor.raw();
|
|
}
|
|
|
|
RawObject* StreamingFlowGraphBuilder::EvaluateMetadata(intptr_t kernel_offset) {
|
|
SetOffset(kernel_offset);
|
|
const Tag tag = PeekTag();
|
|
|
|
if (tag == kClass) {
|
|
ClassHelper class_helper(this);
|
|
class_helper.ReadUntilExcluding(ClassHelper::kAnnotations);
|
|
} else if (tag == kProcedure) {
|
|
ProcedureHelper procedure_helper(this);
|
|
procedure_helper.ReadUntilExcluding(ProcedureHelper::kAnnotations);
|
|
} else if (tag == kField) {
|
|
FieldHelper field_helper(this);
|
|
field_helper.ReadUntilExcluding(FieldHelper::kAnnotations);
|
|
} else if (tag == kConstructor) {
|
|
ConstructorHelper constructor_helper(this);
|
|
constructor_helper.ReadUntilExcluding(ConstructorHelper::kAnnotations);
|
|
} else {
|
|
FATAL("No support for metadata on this type of kernel node\n");
|
|
}
|
|
|
|
intptr_t list_length = ReadListLength(); // read list length.
|
|
const Array& metadata_values = Array::Handle(Z, Array::New(list_length));
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
// this will (potentially) read the expression, but reset the position.
|
|
Instance& value = constant_evaluator_.EvaluateExpression(ReaderOffset());
|
|
SkipExpression(); // read (actual) initializer.
|
|
metadata_values.SetAt(i, value);
|
|
}
|
|
|
|
return metadata_values.raw();
|
|
}
|
|
|
|
void StreamingFlowGraphBuilder::CollectTokenPositionsFor(
|
|
intptr_t script_index,
|
|
intptr_t initial_script_index,
|
|
GrowableArray<intptr_t>* record_token_positions_in,
|
|
GrowableArray<intptr_t>* record_yield_positions_in) {
|
|
record_token_positions_into_ = record_token_positions_in;
|
|
record_yield_positions_into_ = record_yield_positions_in;
|
|
record_for_script_id_ = script_index;
|
|
current_script_id_ = initial_script_index;
|
|
|
|
const Tag tag = PeekTag();
|
|
if (tag == kProcedure) {
|
|
ProcedureHelper procedure_helper(this);
|
|
procedure_helper.ReadUntilExcluding(ProcedureHelper::kEnd);
|
|
} else if (tag == kConstructor) {
|
|
ConstructorHelper constructor_helper(this);
|
|
constructor_helper.ReadUntilExcluding(ConstructorHelper::kEnd);
|
|
} else if (tag == kFunctionNode) {
|
|
FunctionNodeHelper function_node_helper(this);
|
|
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd);
|
|
} else if (tag == kField) {
|
|
FieldHelper field_helper(this);
|
|
field_helper.ReadUntilExcluding(FieldHelper::kEnd);
|
|
} else {
|
|
UNREACHABLE();
|
|
}
|
|
|
|
record_token_positions_into_ = NULL;
|
|
record_yield_positions_into_ = NULL;
|
|
record_for_script_id_ = -1;
|
|
}
|
|
|
|
intptr_t StreamingFlowGraphBuilder::SourceTableSize() {
|
|
AlternativeReadingScope alt(reader_);
|
|
SetOffset(reader_->size() - 4);
|
|
intptr_t library_count = reader_->ReadUInt32();
|
|
SetOffset(reader_->size() - 4 - 4 * library_count - 3 * 4);
|
|
SetOffset(reader_->ReadUInt32()); // read source table offset.
|
|
return ReadUInt(); // read source table size.
|
|
}
|
|
|
|
String& StreamingFlowGraphBuilder::SourceTableUriFor(intptr_t index) {
|
|
AlternativeReadingScope alt(reader_);
|
|
SetOffset(reader_->size() - 4);
|
|
intptr_t library_count = reader_->ReadUInt32();
|
|
SetOffset(reader_->size() - 4 - 4 * library_count - 3 * 4);
|
|
SetOffset(reader_->ReadUInt32()); // read source table offset.
|
|
intptr_t size = ReadUInt(); // read source table size.
|
|
intptr_t start = 0;
|
|
intptr_t end = -1;
|
|
for (intptr_t i = 0; i < size; ++i) {
|
|
intptr_t offset = ReadUInt();
|
|
if (i == index - 1) {
|
|
start = offset;
|
|
} else if (i == index) {
|
|
end = offset;
|
|
}
|
|
}
|
|
intptr_t end_offset = ReaderOffset();
|
|
return H.DartString(
|
|
reader_->CopyDataIntoZone(Z, end_offset + start, end - start),
|
|
end - start, Heap::kOld);
|
|
}
|
|
|
|
String& StreamingFlowGraphBuilder::GetSourceFor(intptr_t index) {
|
|
AlternativeReadingScope alt(reader_);
|
|
SetOffset(reader_->size() - 4);
|
|
intptr_t library_count = reader_->ReadUInt32();
|
|
SetOffset(reader_->size() - 4 - 4 * library_count - 3 * 4);
|
|
SetOffset(reader_->ReadUInt32()); // read source table offset.
|
|
intptr_t size = ReadUInt(); // read source table size.
|
|
intptr_t uris_size = 0;
|
|
for (intptr_t i = 0; i < size; ++i) {
|
|
uris_size = ReadUInt();
|
|
}
|
|
SkipBytes(uris_size);
|
|
|
|
// Read the source code strings and line starts.
|
|
for (intptr_t i = 0; i < size; ++i) {
|
|
intptr_t length = ReadUInt();
|
|
if (index == i) {
|
|
return H.DartString(reader_->CopyDataIntoZone(Z, ReaderOffset(), length),
|
|
length, Heap::kOld);
|
|
}
|
|
SkipBytes(length);
|
|
intptr_t line_count = ReadUInt();
|
|
for (intptr_t j = 0; j < line_count; ++j) {
|
|
ReadUInt();
|
|
}
|
|
}
|
|
|
|
return String::Handle(String::null());
|
|
}
|
|
|
|
Array& StreamingFlowGraphBuilder::GetLineStartsFor(intptr_t index) {
|
|
AlternativeReadingScope alt(reader_);
|
|
SetOffset(reader_->size() - 4);
|
|
intptr_t library_count = reader_->ReadUInt32();
|
|
SetOffset(reader_->size() - 4 - 4 * library_count - 3 * 4);
|
|
SetOffset(reader_->ReadUInt32()); // read source table offset.
|
|
intptr_t size = ReadUInt(); // read source table size.
|
|
intptr_t uris_size = 0;
|
|
for (intptr_t i = 0; i < size; ++i) {
|
|
uris_size = ReadUInt();
|
|
}
|
|
SkipBytes(uris_size);
|
|
|
|
// Read the source code strings and line starts.
|
|
for (intptr_t i = 0; i < size; ++i) {
|
|
intptr_t length = ReadUInt();
|
|
SkipBytes(length);
|
|
intptr_t line_count = ReadUInt();
|
|
if (i == index) {
|
|
Array& array_object =
|
|
Array::Handle(Z, Array::New(line_count, Heap::kOld));
|
|
Smi& value = Smi::Handle(Z);
|
|
intptr_t previous_line_start = 0;
|
|
for (intptr_t j = 0; j < line_count; ++j) {
|
|
intptr_t line_start = ReadUInt() + previous_line_start;
|
|
value = Smi::New(line_start);
|
|
array_object.SetAt(j, value);
|
|
previous_line_start = line_start;
|
|
}
|
|
return array_object;
|
|
} else {
|
|
for (intptr_t j = 0; j < line_count; ++j) {
|
|
ReadUInt();
|
|
}
|
|
}
|
|
}
|
|
|
|
return Array::Handle(Array::null());
|
|
}
|
|
|
|
} // namespace kernel
|
|
} // namespace dart
|
|
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|