blob: e6a41fc2fa13ce4630d1f768ca24e542a5ca4134 [file] [log] [blame]
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/kernel_binary_flowgraph.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()
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 dart::AbstractType::ZoneHandle(Z, result_.raw());
}
void StreamingDartTypeTranslator::BuildTypeInternal() {
Tag tag = builder_->ReadTag();
switch (tag) {
case kInvalidType:
result_ = ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(Z), // No previous error.
dart::Script::Handle(Z, dart::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 kBottomType:
result_ = dart::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.
dart::Object& klass =
dart::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;
intptr_t* type_parameters = NULL;
if (!simple) {
list_length =
builder_->ReadListLength(); // read type_parameters list length
type_parameters = new intptr_t[list_length];
for (int i = 0; i < list_length; ++i) {
type_parameters[i] = builder_->ReaderOffset();
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, type_parameters, list_length);
Function& signature_function = Function::ZoneHandle(
Z, Function::NewSignatureFunction(*active_class_->klass,
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).
dart::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);
}
}
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();
}
static intptr_t FindTypeParameterIndex(intptr_t* parameters,
intptr_t parameters_count,
intptr_t look_for) {
for (intptr_t i = 0; i < parameters_count; ++i) {
if (look_for == parameters[i]) {
return i;
}
}
return -1;
}
static intptr_t FindTypeParameterIndex(List<TypeParameter>* parameters,
intptr_t look_for) {
for (intptr_t i = 0; i < parameters->length(); ++i) {
if (look_for == (*parameters)[i]->kernel_offset()) {
return i;
}
}
return -1;
}
void StreamingDartTypeTranslator::BuildTypeParameterType() {
builder_->ReadUInt(); // read parameter index.
intptr_t binary_offset = builder_->ReadUInt(); // read binary offset.
builder_->SkipOptionalDartType(); // read bound.
if (binary_offset == 0) {
// TODO(jensj): This doesn't appear to actually happen.
UNIMPLEMENTED();
return;
}
for (TypeParameterScope* scope = type_parameter_scope_; scope != NULL;
scope = scope->outer()) {
const intptr_t index = FindTypeParameterIndex(
scope->parameters(), scope->parameters_count(), binary_offset);
if (index >= 0) {
result_ ^= dart::Type::DynamicType();
return;
}
}
if ((active_class_->member != NULL) && active_class_->member->IsProcedure()) {
Procedure* procedure = Procedure::Cast(active_class_->member);
if ((procedure->function() != NULL) &&
(procedure->function()->type_parameters().length() > 0)) {
//
// 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'>(); }
// }
//
const intptr_t index = FindTypeParameterIndex(
&procedure->function()->type_parameters(), binary_offset);
if (index >= 0) {
if (procedure->kind() == Procedure::kFactory) {
// The index of the type parameter in [parameters] is
// the same index into the `klass->type_parameters()` array.
result_ ^= dart::TypeArguments::Handle(
Z, active_class_->klass->type_parameters())
.TypeAt(index);
} else {
result_ ^= dart::Type::DynamicType();
}
return;
}
}
}
ASSERT(active_class_->kernel_class != NULL);
List<TypeParameter>* parameters =
&active_class_->kernel_class->type_parameters();
const intptr_t index = FindTypeParameterIndex(parameters, binary_offset);
if (index >= 0) {
// The index of the type parameter in [parameters] is
// the same index into the `klass->type_parameters()` array.
result_ ^=
dart::TypeArguments::Handle(Z, active_class_->klass->type_parameters())
.TypeAt(index);
return;
}
UNREACHABLE();
}
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 ith type's 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_.IsDynamicType()) {
only_dynamic = false;
}
if (result_.IsMalformed()) {
type_arguments = TypeArguments::null();
return type_arguments;
}
type_arguments.SetTypeAt(i, result_);
}
if (finalize_) {
type_arguments = type_arguments.Canonicalize();
}
}
return type_arguments;
}
const TypeArguments&
StreamingDartTypeTranslator::BuildInstantiatedTypeArguments(
const dart::Class& receiver_class,
intptr_t length) {
const TypeArguments& type_arguments = BuildTypeArguments(length);
if (type_arguments.IsNull()) 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(*builder_->active_class()->klass, type);
}
const TypeArguments& instantiated_type_arguments =
TypeArguments::ZoneHandle(Z, type.arguments());
return instantiated_type_arguments;
}
const Type& StreamingDartTypeTranslator::ReceiverType(
const dart::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_,
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.
// Read unused "interface_target_reference".
builder_->SkipCanonicalNameReference();
if (H.StringEquals(name, "length")) {
EvaluateExpression(expression_offset);
if (result_.IsString()) {
const dart::String& str =
dart::String::Handle(Z, dart::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 dart::Field& field =
dart::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());
closure_function.set_kernel_function(function.kernel_function());
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 dart::Instance& receiver =
EvaluateExpression(builder_->ReaderOffset(), false); // read receiver.
dart::Class& klass = dart::Class::Handle(
Z, isolate_->class_table()->At(receiver.GetClassId()));
ASSERT(!klass.IsNull());
// Search the superclass chain for the selector.
dart::Function& function = dart::Function::Handle(Z);
const dart::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));
dart::Class& klass = dart::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));
dart::Class& klass = dart::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.
LogicalExpression::Operator op = static_cast<LogicalExpression::Operator>(
builder_->ReadByte()); // read operator.
if (op == LogicalExpression::kAnd) {
if (left) {
EvaluateBooleanExpressionHere(); // read right.
} else {
builder_->SkipExpression(); // read right.
}
} else {
ASSERT(op == LogicalExpression::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_ = dart::String::ConcatAll(strings, Heap::kOld);
result_ = H.Canonicalize(result_);
} else {
// Get string interpolation function.
const dart::Class& cls = dart::Class::Handle(
Z, dart::Library::LookupCoreClass(Symbols::StringBase()));
ASSERT(!cls.IsNull());
const Function& func = Function::Handle(
Z, cls.LookupStaticFunction(
dart::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(dart::String::Cast(result));
}
}
void StreamingConstantEvaluator::EvaluateSymbolLiteral() {
const dart::String& symbol_value = H.DartSymbol(
builder_->ReadStringReference()); // read index into string table.
const dart::Class& symbol_class =
dart::Class::ZoneHandle(Z, I->object_store()->symbol_class());
ASSERT(!symbol_class.IsNull());
const dart::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 dart::Class& map_class = dart::Class::Handle(
Z, dart::Library::LookupCoreClass(Symbols::ImmutableMap()));
ASSERT(!map_class.IsNull());
ASSERT(map_class.NumTypeArguments() == 2);
const dart::Field& field = dart::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();
LocalVariable* local = builder_->LookupVariable(kernel_position);
// read variable declaration.
builder_->ReadPosition(); // read position.
builder_->ReadPosition(); // read equals position.
builder_->ReadFlags(); // read flags.
builder_->SkipStringReference(); // read name index.
builder_->SkipDartType(); // read type.
Tag tag = builder_->ReadTag(); // read (first part of) initializer.
if (tag == kNothing) {
local->SetConstValue(Instance::ZoneHandle(Z, dart::Instance::null()));
} else {
local->SetConstValue(EvaluateExpression(
builder_->ReaderOffset(), false)); // read rest of initializer.
}
EvaluateExpression(builder_->ReaderOffset(), false); // read body
}
void StreamingConstantEvaluator::EvaluateBigIntLiteral() {
const dart::String& value =
H.DartString(builder_->ReadStringReference()); // read string reference.
result_ = Integer::New(value, Heap::kOld);
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_ = dart::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_ = dart::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_ = dart::Bool::Get(value).raw();
}
void StreamingConstantEvaluator::EvaluateNullLiteral() {
result_ = dart::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) {
dart::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 dart::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 num_arguments = kNumArgs + kNumExtraArgs;
const Array& arg_values =
Array::Handle(Z, Array::New(num_arguments, 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, num_arguments,
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,
dart::Class* target_klass) {
intptr_t types_count = builder_->ReadListLength(); // read types count.
const TypeArguments* type_arguments = NULL;
if (types_count > 0) {
type_arguments = &T.BuildInstantiatedTypeArguments(
*target_klass, types_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);
AssertBoolInCheckedMode();
return result_.raw() == Bool::True().raw();
}
bool StreamingConstantEvaluator::GetCachedConstant(intptr_t kernel_offset,
Instance* value) {
if (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, &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) 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, value);
script_.set_compile_time_constants(constants.Release());
}
Fragment StreamingFlowGraphBuilder::BuildExpressionAt(intptr_t kernel_offset) {
SetOffset(kernel_offset);
return BuildExpression(); // read expression.
}
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:
// TODO(jensj)
UNIMPLEMENTED();
return Fragment();
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);
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(true);
case kFunctionDeclaration:
// TODO(jensj)
UNIMPLEMENTED();
return Fragment();
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() {
intptr_t offset = ReaderOffset();
uint32_t result = reader_->ReadUInt();
SetOffset(offset);
return result;
}
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 dart::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 dart::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 dart::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);
}
}
void StreamingFlowGraphBuilder::SkipStringReference() {
ReadUInt();
}
void StreamingFlowGraphBuilder::SkipCanonicalNameReference() {
ReadUInt();
}
void StreamingFlowGraphBuilder::SkipDartType() {
Tag tag = ReadTag();
switch (tag) {
case kInvalidType:
case kDynamicType:
case kVoidType:
case kBottomType:
// 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.
ReadUInt(); // read binary offset.
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) {
intptr_t length = ReadListLength(); // read number of types.
for (intptr_t i = 0; i < length; ++i) {
SkipDartType(); // skip the ith type.
}
}
}
void StreamingFlowGraphBuilder::SkipFunctionType(bool simple) {
if (!simple) {
intptr_t list_length =
ReadListLength(); // read type_parameters list length.
for (int i = 0; i < list_length; ++i) {
SkipStringReference(); // read string index (name).
SkipDartType(); // read dart type.
}
ReadUInt(); // read required parameter count.
ReadUInt(); // read total parameter count.
}
const intptr_t positional_count =
ReadListLength(); // read positional_parameters list length.
for (intptr_t i = 0; i < positional_count; ++i) {
SkipDartType(); // read ith positional parameter.
}
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.
}
}
SkipDartType(); // read return type.
}
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.
// Read unused "interface_target_reference".
SkipCanonicalNameReference();
return;
case kPropertySet:
ReadPosition(); // read position.
SkipExpression(); // read receiver.
SkipName(); // read name.
SkipExpression(); // read value.
// read unused "interface_target_reference".
SkipCanonicalNameReference();
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.
// read unused "interface_target_reference".
SkipCanonicalNameReference();
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.
intptr_t list_length = ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
SkipExpression(); // read ith expression.
}
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.
intptr_t list_length = ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
SkipExpression(); // read ith expression.
}
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:
// TODO(jensj)
UNIMPLEMENTED();
return;
case kLet:
SkipVariableDeclaration(); // read variable declaration.
SkipExpression(); // read expression.
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.
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:
SkipExpression(); // read condition.
SkipStatement(); // read body.
return;
case kDoStatement:
SkipStatement(); // read body.
SkipExpression(); // read condition.
return;
case kForStatement: {
intptr_t list_length = ReadListLength(); // read number of variables.
for (intptr_t i = 0; i < list_length; ++i) {
SkipVariableDeclaration(); // read ith variable.
}
Tag tag = ReadTag(); // Read first part of condition.
if (tag == kSomething) {
SkipExpression(); // read rest of condition.
}
list_length = ReadListLength(); // read number of updates.
for (intptr_t i = 0; i < list_length; ++i) {
SkipExpression(); // read ith update.
}
SkipStatement(); // read body.
return;
}
case kForInStatement:
case kAsyncForInStatement:
ReadPosition(); // read position.
SkipVariableDeclaration(); // read variable.
SkipExpression(); // read iterable.
SkipStatement(); // read body.
return;
case kSwitchStatement: {
SkipExpression(); // read condition.
int num_cases = ReadListLength(); // read number of cases.
for (intptr_t i = 0; i < num_cases; ++i) {
int num_expressions = ReadListLength(); // read number of expressions.
for (intptr_t j = 0; j < num_expressions; ++j) {
ReadPosition(); // read jth position.
SkipExpression(); // read jth expression.
}
ReadBool(); // read is_default.
SkipStatement(); // read body.
}
return;
}
case kContinueSwitchStatement:
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 num_matches = ReadListLength(); // read number of catches.
for (intptr_t i = 0; i < num_matches; ++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:
ReadPosition(); // read position.
ReadByte(); // read flags.
SkipExpression(); // read expression.
return;
case kVariableDeclaration:
SkipVariableDeclaration();
return;
case kFunctionDeclaration:
// TODO(jensj)
UNIMPLEMENTED();
return;
default:
UNREACHABLE();
}
}
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.
// List of types.
intptr_t list_length = ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
SkipDartType(); // read ith type.
}
// List of positional.
list_length = ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
SkipExpression(); // read ith expression.
}
// List of named.
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() {
ReadPosition(); // read position.
ReadPosition(); // read equals position.
ReadFlags(); // read flags.
SkipStringReference(); // read name index.
SkipDartType(); // read type.
Tag tag = ReadTag(); // read (first part of) initializer.
if (tag == kSomething) {
SkipExpression(); // read (actual) initializer.
}
}
TokenPosition StreamingFlowGraphBuilder::ReadPosition(bool record) {
return reader_->ReadPosition(record);
}
Tag StreamingFlowGraphBuilder::ReadTag(uint8_t* payload) {
return reader_->ReadTag(payload);
}
Tag StreamingFlowGraphBuilder::PeekTag(uint8_t* payload) {
return reader_->PeekTag(payload);
}
word StreamingFlowGraphBuilder::ReadFlags() {
return reader_->ReadFlags();
}
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_;
}
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_;
}
Value* StreamingFlowGraphBuilder::Pop() {
return flow_graph_builder_->Pop();
}
Tag StreamingFlowGraphBuilder::PeekArgumentsFirstPositionalTag() {
// read parts of arguments, then go back to before doing so.
intptr_t offset = ReaderOffset();
ReadUInt(); // read number of arguments.
// List of types.
intptr_t list_length = ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
SkipDartType(); // read ith type.
}
// List of positional.
list_length = ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
Tag tag = ReadTag(); // read first tag.
SetOffset(offset); // reset offset.
return tag;
}
UNREACHABLE();
return kNothing;
}
const TypeArguments& StreamingFlowGraphBuilder::PeekArgumentsInstantiatedType(
const dart::Class& klass) {
// read parts of arguments, then go back to before doing so.
intptr_t offset = ReaderOffset();
ReadUInt(); // read argument count.
intptr_t list_length = ReadListLength(); // read types list length.
const TypeArguments& type_arguments =
T.BuildInstantiatedTypeArguments(klass, list_length); // read types.
SetOffset(offset);
return type_arguments;
}
intptr_t StreamingFlowGraphBuilder::PeekArgumentsCount() {
return PeekUInt();
}
intptr_t StreamingFlowGraphBuilder::PeekArgumentsTypeCount() {
intptr_t offset = ReaderOffset();
ReadUInt(); // read arguments count.
intptr_t types_count = ReadListLength(); // read length of types list.
SetOffset(offset);
return types_count;
}
void StreamingFlowGraphBuilder::SkipArgumentsBeforeActualArguments() {
ReadUInt(); // read arguments count.
intptr_t types_count = ReadListLength();
for (intptr_t i = 0; i < types_count; ++i) {
SkipDartType(); // read ith type.
}
}
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 dart::String& name) {
return flow_graph_builder_->MethodKind(name);
}
dart::RawFunction* StreamingFlowGraphBuilder::LookupMethodByMember(
NameIndex target,
const dart::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) {
return flow_graph_builder_->StaticCall(position, target, argument_count,
argument_names);
}
Fragment StreamingFlowGraphBuilder::InstanceCall(TokenPosition position,
const dart::String& name,
Token::Kind kind,
intptr_t argument_count,
intptr_t num_args_checked) {
return flow_graph_builder_->InstanceCall(position, name, kind, argument_count,
num_args_checked);
}
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(const dart::Class& klass,
intptr_t argument_count) {
return flow_graph_builder_->AllocateObject(klass, argument_count);
}
Fragment StreamingFlowGraphBuilder::InstanceCall(TokenPosition position,
const dart::String& name,
Token::Kind kind,
intptr_t argument_count,
const Array& argument_names,
intptr_t num_args_checked) {
return flow_graph_builder_->InstanceCall(position, name, kind, argument_count,
argument_names, num_args_checked);
}
Fragment StreamingFlowGraphBuilder::StoreLocal(TokenPosition position,
LocalVariable* variable) {
return flow_graph_builder_->StoreLocal(position, variable);
}
Fragment StreamingFlowGraphBuilder::StoreStaticField(TokenPosition position,
const dart::Field& field) {
return flow_graph_builder_->StoreStaticField(position, field);
}
Fragment StreamingFlowGraphBuilder::StringInterpolate(TokenPosition position) {
return flow_graph_builder_->StringInterpolate(position);
}
Fragment StreamingFlowGraphBuilder::StringInterpolateSingle(
TokenPosition position) {
return flow_graph_builder_->StringInterpolateSingle(position);
}
Fragment StreamingFlowGraphBuilder::ThrowTypeError() {
return flow_graph_builder_->ThrowTypeError();
}
Fragment StreamingFlowGraphBuilder::LoadInstantiatorTypeArguments() {
return flow_graph_builder_->LoadInstantiatorTypeArguments();
}
Fragment StreamingFlowGraphBuilder::LoadFunctionTypeArguments() {
return flow_graph_builder_->LoadFunctionTypeArguments();
}
Fragment StreamingFlowGraphBuilder::InstantiateType(const AbstractType& type) {
return flow_graph_builder_->InstantiateType(type);
}
Fragment StreamingFlowGraphBuilder::CreateArray() {
return flow_graph_builder_->CreateArray();
}
Fragment StreamingFlowGraphBuilder::StoreIndexed(intptr_t class_id) {
return flow_graph_builder_->StoreIndexed(class_id);
}
Fragment StreamingFlowGraphBuilder::CheckStackOverflow() {
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.
intptr_t offset = ReaderOffset();
Fragment result = flow_graph_builder_->TranslateFinallyFinalizers(
outer_finally, target_context_depth);
SetOffset(offset);
return result;
}
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 dart::AbstractType& dst_type,
const dart::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 dart::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 types_count = ReadListLength(); // read type count.
return T.BuildTypeArguments(types_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.
intptr_t list_length = ReadListLength(); // read type count.
for (intptr_t i = 0; i < list_length; ++i) {
SkipDartType(); // read ith type.
}
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) {
dart::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 dart::String& getter_name = ReadNameAsGetterName(); // read name.
SkipCanonicalNameReference(); // Read unused "interface_target_reference".
return instructions + InstanceCall(position, getter_name, Token::kGET, 1);
}
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 dart::String& setter_name = ReadNameAsSetterName(); // read name.
instructions += BuildExpression(); // read value.
instructions += StoreLocal(TokenPosition::kNoSource, variable);
instructions += PushArgument();
SkipCanonicalNameReference(); // read unused "interface_target_reference".
instructions += InstanceCall(position, setter_name, Token::kSET, 2);
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 dart::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 dart::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 dart::Field& field =
dart::Field::ZoneHandle(Z, H.LookupFieldByKernelField(target));
if (field.is_const()) {
return Constant(constant_evaluator_.EvaluateExpression(offset));
} else {
const dart::Class& owner = dart::Class::Handle(Z, field.Owner());
const dart::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 dart::Field& field =
dart::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, dart::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 dart::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,
// read unused "interface_target_reference".
SkipCanonicalNameReference();
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 unused "interface_target_reference".
SkipCanonicalNameReference();
return Constant(result);
}
}
SetOffset(before_branch_offset);
}
Fragment instructions = BuildExpression(); // read receiver.
const dart::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 unused "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.
Array& argument_names = Array::ZoneHandle(Z);
intptr_t argument_count;
instructions +=
BuildArguments(&argument_names, &argument_count); // read arguments.
++argument_count;
intptr_t num_args_checked = 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);
num_args_checked = argument_count;
}
instructions += InstanceCall(position, name, token_kind, argument_count,
argument_names, num_args_checked);
// 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();
}
SkipCanonicalNameReference(); // read unused "interface_target_reference".
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 dart::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 dart::Class& klass = dart::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(klass, 1);
} else {
instructions += AllocateObject(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() == dart::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.
dart::Class& klass = dart::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) {
const TypeArguments& type_arguments = PeekArgumentsInstantiatedType(klass);
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 {
instructions += TranslateInstantiatedTypeArguments(type_arguments);
}
instructions += PushArgument();
instructions += AllocateObject(klass, 1);
} else {
instructions += AllocateObject(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;
LogicalExpression::Operator op =
static_cast<LogicalExpression::Operator>(ReadByte());
if (op == LogicalExpression::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 == LogicalExpression::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, dart::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, dart::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, dart::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(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 += PushArgument(); // The array.
const dart::Class& factory_class =
dart::Class::Handle(Z, dart::Library::LookupCoreClass(Symbols::List()));
const Function& factory_method = Function::ZoneHandle(
Z, factory_class.LookupFactory(
dart::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 dart::Class& map_class =
dart::Class::Handle(Z, dart::Library::LookupCoreClass(Symbols::Map()));
const Function& factory_method = Function::ZoneHandle(
Z, map_class.LookupFactory(
dart::Library::PrivateCoreLibName(Symbols::MapLiteralFactory())));
return instructions + StaticCall(position, factory_method, 2);
}
Fragment StreamingFlowGraphBuilder::BuildLet(TokenPosition* position) {
if (position != NULL) *position = TokenPosition::kNoSource;
Fragment instructions = BuildVariableDeclaration(false); // read variable.
instructions += BuildExpression(); // read body.
return instructions;
}
Fragment StreamingFlowGraphBuilder::BuildBigIntLiteral(
TokenPosition* position) {
if (position != NULL) *position = TokenPosition::kNoSource;
const dart::String& value =
H.DartString(ReadStringReference()); // read index into string table.
return Constant(Integer::ZoneHandle(Z, Integer::New(value, Heap::kOld)));
}
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::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);
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);
return instructions;
}
Fragment StreamingFlowGraphBuilder::BuildEmptyStatement() {
return Fragment();
}
Fragment StreamingFlowGraphBuilder::BuildAssertStatement() {
if (!I->asserts()) {
intptr_t offset = ReaderOffset() - 1; // Include the tag.
SetOffset(offset);
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);
const dart::Class& klass = dart::Class::ZoneHandle(
Z, dart::Library::LookupCoreClass(Symbols::AssertionError()));
ASSERT(!klass.IsNull());
const dart::Function& constructor = dart::Function::ZoneHandle(
Z, klass.LookupConstructorAllowPrivate(
H.DartSymbol("_AssertionError._create")));
ASSERT(!constructor.IsNull());
const dart::String& url = H.DartString(
parsed_function()->function().ToLibNamePrefixedQualifiedCString(),
Heap::kOld);
// Create instance of _AssertionError
Fragment otherwise_fragment(otherwise);
otherwise_fragment += AllocateObject(klass, 0);
LocalVariable* instance = MakeTemporary();
// Call _AssertionError._create constructor.
otherwise_fragment += LoadLocal(instance);
otherwise_fragment += PushArgument(); // this
otherwise_fragment += Constant(H.DartString("<no message>", Heap::kOld));
otherwise_fragment += PushArgument(); // failedAssertion
otherwise_fragment += Constant(url);
otherwise_fragment += PushArgument(); // url
otherwise_fragment += IntConstant(0);
otherwise_fragment += PushArgument(); // line
otherwise_fragment += IntConstant(0);
otherwise_fragment += PushArgument(); // column
Tag tag = ReadTag(); // read (first part of) message.
if (tag == kSomething) {
otherwise_fragment += BuildExpression(); // read (rest of) message.
} else {
otherwise_fragment += Constant(H.DartString("<no message>", Heap::kOld));
}
otherwise_fragment += PushArgument(); // message
otherwise_fragment += StaticCall(TokenPosition::kNoSource, constructor, 6);
otherwise_fragment += Drop();
// Throw _AssertionError exception.
otherwise_fragment += PushArgument();
otherwise_fragment += ThrowException(TokenPosition::kNoSource);
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();
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);
} else {
entry = condition.entry;
}
loop_depth_dec();
return Fragment(entry, loop_exit);
}
Fragment StreamingFlowGraphBuilder::BuildDoStatement() {
loop_depth_inc();
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), loop_exit);
}
Fragment StreamingFlowGraphBuilder::BuildForStatement() {
intptr_t offset = ReaderOffset() - 1; // Include the tag.
Fragment declarations;
bool new_context = false;
declarations += EnterScope(offset, &new_context);
intptr_t list_length = ReadListLength(); // read number of variables.
for (intptr_t i = 0; i < list_length; ++i) {
declarations += BuildVariableDeclaration(false); // 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);
return loop;
}
Fragment StreamingFlowGraphBuilder::BuildForInStatement(bool async) {
intptr_t offset = ReaderOffset() - 1; // Include the tag.
TokenPosition position = ReadPosition(); // read position.
intptr_t variable_kernel_position = ReaderOffset();
SkipVariableDeclaration(); // read variable.
TokenPosition iterable_position = TokenPosition::kNoSource;
Fragment instructions =
BuildExpression(&iterable_position); // read iterable.
instructions += PushArgument();
const dart::String& iterator_getter = dart::String::ZoneHandle(
Z, dart::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);
body += LoadLocal(iterator);
body += PushArgument();
const dart::String& current_getter = dart::String::ZoneHandle(
Z, dart::Field::GetterSymbol(Symbols::Current()));
body += InstanceCall(position, current_getter, Token::kGET, 1);
body += StoreLocal(TokenPosition::kNoSource,
LookupVariable(variable_kernel_position));
body += Drop();
body += BuildStatement(); // read body.
body += ExitScope(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() {
// We need the number of cases. So start by getting that, then go back.
intptr_t offset = ReaderOffset();
SkipExpression(); // temporarily skip condition
int num_cases = ReadListLength(); // read number of cases.
SetOffset(offset);
SwitchBlock block(flow_graph_builder_, num_cases);
// 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();
num_cases = 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[num_cases];
intptr_t* case_expression_offsets = new intptr_t[num_cases];
bool* case_is_default = new bool[num_cases];
for (intptr_t i = 0; i < num_cases; ++i) {
case_expression_offsets[i] = ReaderOffset();
int num_expressions = ReadListLength(); // read number of expressions.
for (intptr_t j = 0; j < num_expressions; ++j) {
ReadPosition(); // read jth position.
SkipExpression(); // read jth expression.
}
bool is_default = ReadBool(); // read is_default.
case_is_default[i] = is_default;
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 < (num_cases - 1))) {
const dart::Class& klass = dart::Class::ZoneHandle(
Z, dart::Library::LookupCoreClass(Symbols::FallThroughError()));
ASSERT(!klass.IsNull());
const dart::Function& constructor = dart::Function::ZoneHandle(
Z, klass.LookupConstructorAllowPrivate(
H.DartSymbol("FallThroughError._create")));
ASSERT(!constructor.IsNull());
const dart::String& url = H.DartString(
parsed_function()->function().ToLibNamePrefixedQualifiedCString(),
Heap::kOld);
// Create instance of _FallThroughError
body_fragment += AllocateObject(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 (num_expressions > 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 < num_cases; ++i) {
SetOffset(case_expression_offsets[i]);
int num_expressions = ReadListLength(); // read length of expressions.
if (case_is_default[i]) {
ASSERT(i == (num_cases - 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 < num_expressions; ++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 < num_expressions; ++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,
/*num_args_checked=*/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);
}
}
}
bool has_no_default = num_cases > 0 && !case_is_default[num_cases - 1];
if (has_no_default) {
// 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[num_cases - 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;
SetOffset(end_offset);
return Fragment(head_instructions.entry, current_instructions.current);
}
Fragment StreamingFlowGraphBuilder::BuildContinueSwitchStatement() {
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()) {
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 num_matches = ReadListLength(); // read number of catches.
const Array& handler_types =
Array::ZoneHandle(Z, Array::New(num_matches, 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 < num_matches; ++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);
tag = ReadTag(); // read first part of exception.
if (tag == kSomething) {
catch_handler_body += LoadLocal(CurrentException());
catch_handler_body +=
StoreLocal(TokenPosition::kNoSource, LookupVariable(ReaderOffset()));
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()));
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);
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
catch_body += NullConstant();
catch_body += PushArgument(); // instantiator type arguments
catch_body += NullConstant();
catch_body += PushArgument(); // function type arguments
catch_body += Constant(*type_guard);
catch_body += PushArgument(); // guard type
catch_body += InstanceCall(
TokenPosition::kNoSource,
dart::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_, NULL, 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 & YieldStatement::kFlagNative) ==
YieldStatement::kFlagNative); // 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(bool has_tag) {
intptr_t kernel_position = ReaderOffset() - (has_tag ? 1 : 0);
LocalVariable* variable = LookupVariable(kernel_position);
TokenPosition position = ReadPosition(); // read position.
TokenPosition equals_position = ReadPosition(); // read equals position.
word flags = ReadFlags(); // read flags.
dart::String& name = H.DartSymbol(ReadStringReference()); // read 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 ((flags & VariableDeclaration::kFlagConst) ==
VariableDeclaration::kFlagConst) {
// Const!
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(position, equals_position);
if (NeedsDebugStepCheck(stack(), debug_position)) {
instructions = DebugStepCheck(debug_position) + instructions;
}
instructions += StoreLocal(position, variable);
instructions += Drop();
return instructions;
}
} // namespace kernel
} // namespace dart
#endif // !defined(DART_PRECOMPILED_RUNTIME)