c3b5939965
Closes https://github.com/dart-lang/sdk/issues/33306 Change-Id: I7088d8b7143edbe24f5cefe4be037ad2006e0625 Reviewed-on: https://dart-review.googlesource.com/58101 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Régis Crelier <regis@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
3050 lines
109 KiB
C++
3050 lines
109 KiB
C++
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include <set>
|
|
|
|
#include "vm/compiler/aot/precompiler.h"
|
|
#include "vm/compiler/frontend/kernel_to_il.h"
|
|
|
|
#include "vm/compiler/backend/il.h"
|
|
#include "vm/compiler/backend/il_printer.h"
|
|
#include "vm/compiler/frontend/kernel_binary_flowgraph.h"
|
|
#include "vm/compiler/frontend/prologue_builder.h"
|
|
#include "vm/compiler/jit/compiler.h"
|
|
#include "vm/kernel_loader.h"
|
|
#include "vm/longjump.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/report.h"
|
|
#include "vm/resolver.h"
|
|
#include "vm/stack_frame.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()
|
|
|
|
ActiveTypeParametersScope::ActiveTypeParametersScope(ActiveClass* active_class,
|
|
const Function& innermost,
|
|
Zone* Z)
|
|
: active_class_(active_class), saved_(*active_class) {
|
|
active_class_->enclosing = &innermost;
|
|
|
|
intptr_t num_params = 0;
|
|
|
|
Function& f = Function::Handle(Z);
|
|
TypeArguments& f_params = TypeArguments::Handle(Z);
|
|
for (f = innermost.raw(); f.parent_function() != Object::null();
|
|
f = f.parent_function()) {
|
|
f_params = f.type_parameters();
|
|
num_params += f_params.Length();
|
|
}
|
|
if (num_params == 0) return;
|
|
|
|
TypeArguments& params =
|
|
TypeArguments::Handle(Z, TypeArguments::New(num_params));
|
|
|
|
intptr_t index = num_params;
|
|
for (f = innermost.raw(); f.parent_function() != Object::null();
|
|
f = f.parent_function()) {
|
|
f_params = f.type_parameters();
|
|
for (intptr_t j = f_params.Length() - 1; j >= 0; --j) {
|
|
params.SetTypeAt(--index, AbstractType::Handle(Z, f_params.TypeAt(j)));
|
|
}
|
|
}
|
|
|
|
active_class_->local_type_parameters = ¶ms;
|
|
}
|
|
|
|
ActiveTypeParametersScope::ActiveTypeParametersScope(
|
|
ActiveClass* active_class,
|
|
const Function* function,
|
|
const TypeArguments& new_params,
|
|
Zone* Z)
|
|
: active_class_(active_class), saved_(*active_class) {
|
|
active_class_->enclosing = function;
|
|
|
|
if (new_params.IsNull()) return;
|
|
|
|
const TypeArguments* old_params = active_class->local_type_parameters;
|
|
const intptr_t old_param_count =
|
|
old_params == NULL ? 0 : old_params->Length();
|
|
const TypeArguments& extended_params = TypeArguments::Handle(
|
|
Z, TypeArguments::New(old_param_count + new_params.Length()));
|
|
|
|
intptr_t index = 0;
|
|
for (intptr_t i = 0; i < old_param_count; ++i) {
|
|
extended_params.SetTypeAt(
|
|
index++, AbstractType::ZoneHandle(Z, old_params->TypeAt(i)));
|
|
}
|
|
for (intptr_t i = 0; i < new_params.Length(); ++i) {
|
|
extended_params.SetTypeAt(
|
|
index++, AbstractType::ZoneHandle(Z, new_params.TypeAt(i)));
|
|
}
|
|
|
|
active_class_->local_type_parameters = &extended_params;
|
|
}
|
|
|
|
Fragment& Fragment::operator+=(const Fragment& other) {
|
|
if (entry == NULL) {
|
|
entry = other.entry;
|
|
current = other.current;
|
|
} else if (current != NULL && other.entry != NULL) {
|
|
current->LinkTo(other.entry);
|
|
current = other.current;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Fragment& Fragment::operator<<=(Instruction* next) {
|
|
if (entry == NULL) {
|
|
entry = current = next;
|
|
} else if (current != NULL) {
|
|
current->LinkTo(next);
|
|
current = next;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void Fragment::Prepend(Instruction* start) {
|
|
if (entry == NULL) {
|
|
entry = current = start;
|
|
} else {
|
|
start->LinkTo(entry);
|
|
entry = start;
|
|
}
|
|
}
|
|
|
|
Fragment Fragment::closed() {
|
|
ASSERT(entry != NULL);
|
|
return Fragment(entry, NULL);
|
|
}
|
|
|
|
Fragment operator+(const Fragment& first, const Fragment& second) {
|
|
Fragment result = first;
|
|
result += second;
|
|
return result;
|
|
}
|
|
|
|
Fragment operator<<(const Fragment& fragment, Instruction* next) {
|
|
Fragment result = fragment;
|
|
result <<= next;
|
|
return result;
|
|
}
|
|
|
|
intptr_t ActiveClass::MemberTypeParameterCount(Zone* zone) {
|
|
ASSERT(member != NULL);
|
|
if (member->IsFactory()) {
|
|
TypeArguments& class_types =
|
|
TypeArguments::Handle(zone, klass->type_parameters());
|
|
return class_types.Length();
|
|
} else if (member->IsMethodExtractor()) {
|
|
Function& extracted =
|
|
Function::Handle(zone, member->extracted_method_closure());
|
|
TypeArguments& function_types =
|
|
TypeArguments::Handle(zone, extracted.type_parameters());
|
|
return function_types.Length();
|
|
} else {
|
|
TypeArguments& function_types =
|
|
TypeArguments::Handle(zone, member->type_parameters());
|
|
return function_types.Length();
|
|
}
|
|
}
|
|
|
|
TranslationHelper::TranslationHelper(Thread* thread)
|
|
: thread_(thread),
|
|
zone_(thread->zone()),
|
|
isolate_(thread->isolate()),
|
|
allocation_space_(thread->IsMutatorThread() ? Heap::kNew : Heap::kOld),
|
|
string_offsets_(TypedData::Handle(Z)),
|
|
string_data_(TypedData::Handle(Z)),
|
|
canonical_names_(TypedData::Handle(Z)),
|
|
metadata_payloads_(TypedData::Handle(Z)),
|
|
metadata_mappings_(TypedData::Handle(Z)),
|
|
constants_(Array::Handle(Z)) {}
|
|
|
|
void TranslationHelper::Reset() {
|
|
string_offsets_ = TypedData::null();
|
|
string_data_ = TypedData::null();
|
|
canonical_names_ = TypedData::null();
|
|
metadata_payloads_ = TypedData::null();
|
|
metadata_mappings_ = TypedData::null();
|
|
constants_ = Array::null();
|
|
}
|
|
|
|
void TranslationHelper::InitFromScript(const Script& script) {
|
|
const KernelProgramInfo& info =
|
|
KernelProgramInfo::Handle(Z, script.kernel_program_info());
|
|
if (info.IsNull()) {
|
|
// If there is no kernel data associated with the script, then
|
|
// do not bother initializing!.
|
|
// This can happen with few special functions like
|
|
// NoSuchMethodDispatcher and InvokeFieldDispatcher.
|
|
return;
|
|
}
|
|
InitFromKernelProgramInfo(info);
|
|
}
|
|
|
|
void TranslationHelper::InitFromKernelProgramInfo(
|
|
const KernelProgramInfo& info) {
|
|
SetStringOffsets(TypedData::Handle(Z, info.string_offsets()));
|
|
SetStringData(TypedData::Handle(Z, info.string_data()));
|
|
SetCanonicalNames(TypedData::Handle(Z, info.canonical_names()));
|
|
SetMetadataPayloads(TypedData::Handle(Z, info.metadata_payloads()));
|
|
SetMetadataMappings(TypedData::Handle(Z, info.metadata_mappings()));
|
|
SetConstants(Array::Handle(Z, info.constants()));
|
|
}
|
|
|
|
void TranslationHelper::SetStringOffsets(const TypedData& string_offsets) {
|
|
ASSERT(string_offsets_.IsNull());
|
|
string_offsets_ = string_offsets.raw();
|
|
}
|
|
|
|
void TranslationHelper::SetStringData(const TypedData& string_data) {
|
|
ASSERT(string_data_.IsNull());
|
|
string_data_ = string_data.raw();
|
|
}
|
|
|
|
void TranslationHelper::SetCanonicalNames(const TypedData& canonical_names) {
|
|
ASSERT(canonical_names_.IsNull());
|
|
canonical_names_ = canonical_names.raw();
|
|
}
|
|
|
|
void TranslationHelper::SetMetadataPayloads(
|
|
const TypedData& metadata_payloads) {
|
|
ASSERT(metadata_payloads_.IsNull());
|
|
metadata_payloads_ = metadata_payloads.raw();
|
|
}
|
|
|
|
void TranslationHelper::SetMetadataMappings(
|
|
const TypedData& metadata_mappings) {
|
|
ASSERT(metadata_mappings_.IsNull());
|
|
metadata_mappings_ = metadata_mappings.raw();
|
|
}
|
|
|
|
void TranslationHelper::SetConstants(const Array& constants) {
|
|
ASSERT(constants_.IsNull());
|
|
constants_ = constants.raw();
|
|
}
|
|
|
|
intptr_t TranslationHelper::StringOffset(StringIndex index) const {
|
|
return string_offsets_.GetUint32(index << 2);
|
|
}
|
|
|
|
intptr_t TranslationHelper::StringSize(StringIndex index) const {
|
|
return StringOffset(StringIndex(index + 1)) - StringOffset(index);
|
|
}
|
|
|
|
uint8_t TranslationHelper::CharacterAt(StringIndex string_index,
|
|
intptr_t index) {
|
|
ASSERT(index < StringSize(string_index));
|
|
return string_data_.GetUint8(StringOffset(string_index) + index);
|
|
}
|
|
|
|
uint8_t* TranslationHelper::StringBuffer(StringIndex string_index) const {
|
|
// Though this implementation appears like it could be replaced by
|
|
// string_data_.DataAddr(StringOffset(string_index)), it can't quite. If the
|
|
// last string in the string table is a zero length string, then the latter
|
|
// expression will try to return the address that is one past the backing
|
|
// store of the string_data_ table. Though this is safe in C++ as long as the
|
|
// address is not dereferenced, it will trigger the assert in
|
|
// TypedData::DataAddr.
|
|
ASSERT(Thread::Current()->no_safepoint_scope_depth() > 0);
|
|
return reinterpret_cast<uint8_t*>(string_data_.DataAddr(0)) +
|
|
StringOffset(string_index);
|
|
}
|
|
|
|
bool TranslationHelper::StringEquals(StringIndex string_index,
|
|
const char* other) {
|
|
intptr_t length = strlen(other);
|
|
if (length != StringSize(string_index)) return false;
|
|
|
|
NoSafepointScope no_safepoint;
|
|
return memcmp(StringBuffer(string_index), other, length) == 0;
|
|
}
|
|
|
|
NameIndex TranslationHelper::CanonicalNameParent(NameIndex name) {
|
|
// Canonical names are pairs of 4-byte parent and string indexes, so the size
|
|
// of an entry is 8 bytes. The parent is biased: 0 represents the root name
|
|
// and N+1 represents the name with index N.
|
|
return NameIndex(static_cast<intptr_t>(canonical_names_.GetUint32(8 * name)) -
|
|
1);
|
|
}
|
|
|
|
StringIndex TranslationHelper::CanonicalNameString(NameIndex name) {
|
|
return StringIndex(canonical_names_.GetUint32((8 * name) + 4));
|
|
}
|
|
|
|
bool TranslationHelper::IsAdministrative(NameIndex name) {
|
|
// Administrative names start with '@'.
|
|
StringIndex name_string = CanonicalNameString(name);
|
|
return (StringSize(name_string) > 0) && (CharacterAt(name_string, 0) == '@');
|
|
}
|
|
|
|
bool TranslationHelper::IsPrivate(NameIndex name) {
|
|
// Private names start with '_'.
|
|
StringIndex name_string = CanonicalNameString(name);
|
|
return (StringSize(name_string) > 0) && (CharacterAt(name_string, 0) == '_');
|
|
}
|
|
|
|
bool TranslationHelper::IsRoot(NameIndex name) {
|
|
return name == -1;
|
|
}
|
|
|
|
bool TranslationHelper::IsLibrary(NameIndex name) {
|
|
// Libraries are the only canonical names with the root as their parent.
|
|
return !IsRoot(name) && IsRoot(CanonicalNameParent(name));
|
|
}
|
|
|
|
bool TranslationHelper::IsClass(NameIndex name) {
|
|
// Classes have the library as their parent and are not an administrative
|
|
// name starting with @.
|
|
return !IsAdministrative(name) && !IsRoot(name) &&
|
|
IsLibrary(CanonicalNameParent(name));
|
|
}
|
|
|
|
bool TranslationHelper::IsMember(NameIndex name) {
|
|
return IsConstructor(name) || IsField(name) || IsProcedure(name);
|
|
}
|
|
|
|
bool TranslationHelper::IsField(NameIndex name) {
|
|
// Fields with private names have the import URI of the library where they are
|
|
// visible as the parent and the string "@fields" as the parent's parent.
|
|
// Fields with non-private names have the string "@fields' as the parent.
|
|
if (IsRoot(name)) {
|
|
return false;
|
|
}
|
|
NameIndex kind = CanonicalNameParent(name);
|
|
if (IsPrivate(name)) {
|
|
kind = CanonicalNameParent(kind);
|
|
}
|
|
return StringEquals(CanonicalNameString(kind), "@fields");
|
|
}
|
|
|
|
bool TranslationHelper::IsConstructor(NameIndex name) {
|
|
// Constructors with private names have the import URI of the library where
|
|
// they are visible as the parent and the string "@constructors" as the
|
|
// parent's parent. Constructors with non-private names have the string
|
|
// "@constructors" as the parent.
|
|
if (IsRoot(name)) {
|
|
return false;
|
|
}
|
|
NameIndex kind = CanonicalNameParent(name);
|
|
if (IsPrivate(name)) {
|
|
kind = CanonicalNameParent(kind);
|
|
}
|
|
return StringEquals(CanonicalNameString(kind), "@constructors");
|
|
}
|
|
|
|
bool TranslationHelper::IsProcedure(NameIndex name) {
|
|
return IsMethod(name) || IsGetter(name) || IsSetter(name) || IsFactory(name);
|
|
}
|
|
|
|
bool TranslationHelper::IsMethod(NameIndex name) {
|
|
// Methods with private names have the import URI of the library where they
|
|
// are visible as the parent and the string "@methods" as the parent's parent.
|
|
// Methods with non-private names have the string "@methods" as the parent.
|
|
if (IsRoot(name)) {
|
|
return false;
|
|
}
|
|
NameIndex kind = CanonicalNameParent(name);
|
|
if (IsPrivate(name)) {
|
|
kind = CanonicalNameParent(kind);
|
|
}
|
|
return StringEquals(CanonicalNameString(kind), "@methods");
|
|
}
|
|
|
|
bool TranslationHelper::IsGetter(NameIndex name) {
|
|
// Getters with private names have the import URI of the library where they
|
|
// are visible as the parent and the string "@getters" as the parent's parent.
|
|
// Getters with non-private names have the string "@getters" as the parent.
|
|
if (IsRoot(name)) {
|
|
return false;
|
|
}
|
|
NameIndex kind = CanonicalNameParent(name);
|
|
if (IsPrivate(name)) {
|
|
kind = CanonicalNameParent(kind);
|
|
}
|
|
return StringEquals(CanonicalNameString(kind), "@getters");
|
|
}
|
|
|
|
bool TranslationHelper::IsSetter(NameIndex name) {
|
|
// Setters with private names have the import URI of the library where they
|
|
// are visible as the parent and the string "@setters" as the parent's parent.
|
|
// Setters with non-private names have the string "@setters" as the parent.
|
|
if (IsRoot(name)) {
|
|
return false;
|
|
}
|
|
NameIndex kind = CanonicalNameParent(name);
|
|
if (IsPrivate(name)) {
|
|
kind = CanonicalNameParent(kind);
|
|
}
|
|
return StringEquals(CanonicalNameString(kind), "@setters");
|
|
}
|
|
|
|
bool TranslationHelper::IsFactory(NameIndex name) {
|
|
// Factories with private names have the import URI of the library where they
|
|
// are visible as the parent and the string "@factories" as the parent's
|
|
// parent. Factories with non-private names have the string "@factories" as
|
|
// the parent.
|
|
if (IsRoot(name)) {
|
|
return false;
|
|
}
|
|
NameIndex kind = CanonicalNameParent(name);
|
|
if (IsPrivate(name)) {
|
|
kind = CanonicalNameParent(kind);
|
|
}
|
|
return StringEquals(CanonicalNameString(kind), "@factories");
|
|
}
|
|
|
|
NameIndex TranslationHelper::EnclosingName(NameIndex name) {
|
|
ASSERT(IsField(name) || IsConstructor(name) || IsProcedure(name));
|
|
NameIndex enclosing = CanonicalNameParent(CanonicalNameParent(name));
|
|
if (IsPrivate(name)) {
|
|
enclosing = CanonicalNameParent(enclosing);
|
|
}
|
|
ASSERT(IsLibrary(enclosing) || IsClass(enclosing));
|
|
return enclosing;
|
|
}
|
|
|
|
RawInstance* TranslationHelper::Canonicalize(const Instance& instance) {
|
|
if (instance.IsNull()) return instance.raw();
|
|
|
|
const char* error_str = NULL;
|
|
RawInstance* result = instance.CheckAndCanonicalize(thread(), &error_str);
|
|
if (result == Object::null()) {
|
|
ReportError("Invalid const object %s", error_str);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
const String& TranslationHelper::DartString(const char* content,
|
|
Heap::Space space) {
|
|
return String::ZoneHandle(Z, String::New(content, space));
|
|
}
|
|
|
|
String& TranslationHelper::DartString(StringIndex string_index,
|
|
Heap::Space space) {
|
|
intptr_t length = StringSize(string_index);
|
|
uint8_t* buffer = Z->Alloc<uint8_t>(length);
|
|
{
|
|
NoSafepointScope no_safepoint;
|
|
memmove(buffer, StringBuffer(string_index), length);
|
|
}
|
|
return String::ZoneHandle(Z, String::FromUTF8(buffer, length, space));
|
|
}
|
|
|
|
String& TranslationHelper::DartString(const uint8_t* utf8_array,
|
|
intptr_t len,
|
|
Heap::Space space) {
|
|
return String::ZoneHandle(Z, String::FromUTF8(utf8_array, len, space));
|
|
}
|
|
|
|
const String& TranslationHelper::DartSymbolPlain(const char* content) const {
|
|
return String::ZoneHandle(Z, Symbols::New(thread_, content));
|
|
}
|
|
|
|
String& TranslationHelper::DartSymbolPlain(StringIndex string_index) const {
|
|
intptr_t length = StringSize(string_index);
|
|
uint8_t* buffer = Z->Alloc<uint8_t>(length);
|
|
{
|
|
NoSafepointScope no_safepoint;
|
|
memmove(buffer, StringBuffer(string_index), length);
|
|
}
|
|
String& result =
|
|
String::ZoneHandle(Z, Symbols::FromUTF8(thread_, buffer, length));
|
|
return result;
|
|
}
|
|
|
|
const String& TranslationHelper::DartSymbolObfuscate(
|
|
const char* content) const {
|
|
String& result = String::ZoneHandle(Z, Symbols::New(thread_, content));
|
|
if (I->obfuscate()) {
|
|
Obfuscator obfuscator(thread_, String::Handle(Z));
|
|
result = obfuscator.Rename(result, true);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
String& TranslationHelper::DartSymbolObfuscate(StringIndex string_index) const {
|
|
intptr_t length = StringSize(string_index);
|
|
uint8_t* buffer = Z->Alloc<uint8_t>(length);
|
|
{
|
|
NoSafepointScope no_safepoint;
|
|
memmove(buffer, StringBuffer(string_index), length);
|
|
}
|
|
String& result =
|
|
String::ZoneHandle(Z, Symbols::FromUTF8(thread_, buffer, length));
|
|
if (I->obfuscate()) {
|
|
Obfuscator obfuscator(thread_, String::Handle(Z));
|
|
result = obfuscator.Rename(result, true);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
const String& TranslationHelper::DartClassName(NameIndex kernel_class) {
|
|
ASSERT(IsClass(kernel_class));
|
|
String& name = DartString(CanonicalNameString(kernel_class));
|
|
return ManglePrivateName(CanonicalNameParent(kernel_class), &name);
|
|
}
|
|
|
|
const String& TranslationHelper::DartConstructorName(NameIndex constructor) {
|
|
ASSERT(IsConstructor(constructor));
|
|
return DartFactoryName(constructor);
|
|
}
|
|
|
|
const String& TranslationHelper::DartProcedureName(NameIndex procedure) {
|
|
ASSERT(IsProcedure(procedure));
|
|
if (IsSetter(procedure)) {
|
|
return DartSetterName(procedure);
|
|
} else if (IsGetter(procedure)) {
|
|
return DartGetterName(procedure);
|
|
} else if (IsFactory(procedure)) {
|
|
return DartFactoryName(procedure);
|
|
} else {
|
|
return DartMethodName(procedure);
|
|
}
|
|
}
|
|
|
|
const String& TranslationHelper::DartSetterName(NameIndex setter) {
|
|
return DartSetterName(CanonicalNameParent(setter),
|
|
CanonicalNameString(setter));
|
|
}
|
|
|
|
const String& TranslationHelper::DartSetterName(NameIndex parent,
|
|
StringIndex setter) {
|
|
// The names flowing into [setter] are coming from the Kernel file:
|
|
// * user-defined setters: `fieldname=`
|
|
// * property-set expressions: `fieldname`
|
|
//
|
|
// The VM uses `get:fieldname` and `set:fieldname`.
|
|
//
|
|
// => In order to be consistent, we remove the `=` always and adopt the VM
|
|
// conventions.
|
|
intptr_t size = StringSize(setter);
|
|
ASSERT(size > 0);
|
|
if (CharacterAt(setter, size - 1) == '=') {
|
|
--size;
|
|
}
|
|
uint8_t* buffer = Z->Alloc<uint8_t>(size);
|
|
{
|
|
NoSafepointScope no_safepoint;
|
|
memmove(buffer, StringBuffer(setter), size);
|
|
}
|
|
String& name =
|
|
String::ZoneHandle(Z, String::FromUTF8(buffer, size, allocation_space_));
|
|
ManglePrivateName(parent, &name);
|
|
name = Field::SetterSymbol(name);
|
|
return name;
|
|
}
|
|
|
|
const String& TranslationHelper::DartGetterName(NameIndex getter) {
|
|
return DartGetterName(CanonicalNameParent(getter),
|
|
CanonicalNameString(getter));
|
|
}
|
|
|
|
const String& TranslationHelper::DartGetterName(NameIndex parent,
|
|
StringIndex getter) {
|
|
String& name = DartString(getter);
|
|
ManglePrivateName(parent, &name);
|
|
name = Field::GetterSymbol(name);
|
|
return name;
|
|
}
|
|
|
|
const String& TranslationHelper::DartFieldName(NameIndex field) {
|
|
return DartFieldName(CanonicalNameParent(field), CanonicalNameString(field));
|
|
}
|
|
|
|
const String& TranslationHelper::DartFieldName(NameIndex parent,
|
|
StringIndex field) {
|
|
String& name = DartString(field);
|
|
return ManglePrivateName(parent, &name);
|
|
}
|
|
|
|
const String& TranslationHelper::DartMethodName(NameIndex method) {
|
|
return DartMethodName(CanonicalNameParent(method),
|
|
CanonicalNameString(method));
|
|
}
|
|
|
|
const String& TranslationHelper::DartMethodName(NameIndex parent,
|
|
StringIndex method) {
|
|
String& name = DartString(method);
|
|
return ManglePrivateName(parent, &name);
|
|
}
|
|
|
|
const String& TranslationHelper::DartFactoryName(NameIndex factory) {
|
|
ASSERT(IsConstructor(factory) || IsFactory(factory));
|
|
GrowableHandlePtrArray<const String> pieces(Z, 3);
|
|
pieces.Add(DartClassName(EnclosingName(factory)));
|
|
pieces.Add(Symbols::Dot());
|
|
// [DartMethodName] will mangle the name.
|
|
pieces.Add(DartMethodName(factory));
|
|
return String::ZoneHandle(Z, Symbols::FromConcatAll(thread_, pieces));
|
|
}
|
|
|
|
RawLibrary* TranslationHelper::LookupLibraryByKernelLibrary(
|
|
NameIndex kernel_library) {
|
|
// We only use the string and don't rely on having any particular parent.
|
|
// This ASSERT is just a sanity check.
|
|
ASSERT(IsLibrary(kernel_library) ||
|
|
IsAdministrative(CanonicalNameParent(kernel_library)));
|
|
const String& library_name =
|
|
DartSymbolPlain(CanonicalNameString(kernel_library));
|
|
ASSERT(!library_name.IsNull());
|
|
RawLibrary* library = Library::LookupLibrary(thread_, library_name);
|
|
ASSERT(library != Object::null());
|
|
return library;
|
|
}
|
|
|
|
RawClass* TranslationHelper::LookupClassByKernelClass(NameIndex kernel_class) {
|
|
ASSERT(IsClass(kernel_class));
|
|
const String& class_name = DartClassName(kernel_class);
|
|
NameIndex kernel_library = CanonicalNameParent(kernel_class);
|
|
Library& library =
|
|
Library::Handle(Z, LookupLibraryByKernelLibrary(kernel_library));
|
|
RawClass* klass = library.LookupClassAllowPrivate(class_name);
|
|
|
|
ASSERT(klass != Object::null());
|
|
return klass;
|
|
}
|
|
|
|
RawField* TranslationHelper::LookupFieldByKernelField(NameIndex kernel_field) {
|
|
ASSERT(IsField(kernel_field));
|
|
NameIndex enclosing = EnclosingName(kernel_field);
|
|
|
|
Class& klass = Class::Handle(Z);
|
|
if (IsLibrary(enclosing)) {
|
|
Library& library =
|
|
Library::Handle(Z, LookupLibraryByKernelLibrary(enclosing));
|
|
klass = library.toplevel_class();
|
|
} else {
|
|
ASSERT(IsClass(enclosing));
|
|
klass = LookupClassByKernelClass(enclosing);
|
|
}
|
|
RawField* field = klass.LookupFieldAllowPrivate(
|
|
DartSymbolObfuscate(CanonicalNameString(kernel_field)));
|
|
ASSERT(field != Object::null());
|
|
return field;
|
|
}
|
|
|
|
RawFunction* TranslationHelper::LookupStaticMethodByKernelProcedure(
|
|
NameIndex procedure) {
|
|
const String& procedure_name = DartProcedureName(procedure);
|
|
|
|
// The parent is either a library or a class (in which case the procedure is a
|
|
// static method).
|
|
NameIndex enclosing = EnclosingName(procedure);
|
|
if (IsLibrary(enclosing)) {
|
|
Library& library =
|
|
Library::Handle(Z, LookupLibraryByKernelLibrary(enclosing));
|
|
RawFunction* function = library.LookupFunctionAllowPrivate(procedure_name);
|
|
ASSERT(function != Object::null());
|
|
return function;
|
|
} else {
|
|
ASSERT(IsClass(enclosing));
|
|
Class& klass = Class::Handle(Z, LookupClassByKernelClass(enclosing));
|
|
Function& function = Function::ZoneHandle(
|
|
Z, klass.LookupFunctionAllowPrivate(procedure_name));
|
|
ASSERT(!function.IsNull());
|
|
|
|
// TODO(27590): We can probably get rid of this after no longer using
|
|
// core libraries from the source.
|
|
if (function.IsRedirectingFactory()) {
|
|
ClassFinalizer::ResolveRedirectingFactory(klass, function);
|
|
function = function.RedirectionTarget();
|
|
}
|
|
return function.raw();
|
|
}
|
|
}
|
|
|
|
RawFunction* TranslationHelper::LookupConstructorByKernelConstructor(
|
|
NameIndex constructor) {
|
|
ASSERT(IsConstructor(constructor));
|
|
Class& klass =
|
|
Class::Handle(Z, LookupClassByKernelClass(EnclosingName(constructor)));
|
|
return LookupConstructorByKernelConstructor(klass, constructor);
|
|
}
|
|
|
|
RawFunction* TranslationHelper::LookupConstructorByKernelConstructor(
|
|
const Class& owner,
|
|
NameIndex constructor) {
|
|
ASSERT(IsConstructor(constructor));
|
|
RawFunction* function =
|
|
owner.LookupConstructorAllowPrivate(DartConstructorName(constructor));
|
|
ASSERT(function != Object::null());
|
|
return function;
|
|
}
|
|
|
|
RawFunction* TranslationHelper::LookupConstructorByKernelConstructor(
|
|
const Class& owner,
|
|
StringIndex constructor_name) {
|
|
GrowableHandlePtrArray<const String> pieces(Z, 3);
|
|
pieces.Add(DartString(String::Handle(owner.Name()).ToCString(), Heap::kOld));
|
|
pieces.Add(Symbols::Dot());
|
|
String& name = DartString(constructor_name);
|
|
pieces.Add(ManglePrivateName(Library::Handle(owner.library()), &name));
|
|
|
|
String& new_name =
|
|
String::ZoneHandle(Z, Symbols::FromConcatAll(thread_, pieces));
|
|
RawFunction* function = owner.LookupConstructorAllowPrivate(new_name);
|
|
ASSERT(function != Object::null());
|
|
return function;
|
|
}
|
|
|
|
Type& TranslationHelper::GetCanonicalType(const Class& klass) {
|
|
ASSERT(!klass.IsNull());
|
|
// Note that if cls 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);
|
|
// Note that the receiver type may now be a malbounded type.
|
|
klass.SetCanonicalType(type);
|
|
}
|
|
return type;
|
|
}
|
|
|
|
void TranslationHelper::ReportError(const char* format, ...) {
|
|
const Script& null_script = Script::Handle(Z);
|
|
|
|
va_list args;
|
|
va_start(args, format);
|
|
Report::MessageV(Report::kError, null_script, TokenPosition::kNoSource,
|
|
Report::AtLocation, format, args);
|
|
va_end(args);
|
|
UNREACHABLE();
|
|
}
|
|
|
|
void TranslationHelper::ReportError(const Script& script,
|
|
const TokenPosition position,
|
|
const char* format,
|
|
...) {
|
|
va_list args;
|
|
va_start(args, format);
|
|
Report::MessageV(Report::kError, script, position, Report::AtLocation, format,
|
|
args);
|
|
va_end(args);
|
|
UNREACHABLE();
|
|
}
|
|
|
|
void TranslationHelper::ReportError(const Error& prev_error,
|
|
const char* format,
|
|
...) {
|
|
const Script& null_script = Script::Handle(Z);
|
|
|
|
va_list args;
|
|
va_start(args, format);
|
|
Report::LongJumpV(prev_error, null_script, TokenPosition::kNoSource, format,
|
|
args);
|
|
va_end(args);
|
|
UNREACHABLE();
|
|
}
|
|
|
|
void TranslationHelper::ReportError(const Error& prev_error,
|
|
const Script& script,
|
|
const TokenPosition position,
|
|
const char* format,
|
|
...) {
|
|
va_list args;
|
|
va_start(args, format);
|
|
Report::LongJumpV(prev_error, script, position, format, args);
|
|
va_end(args);
|
|
UNREACHABLE();
|
|
}
|
|
|
|
String& TranslationHelper::ManglePrivateName(NameIndex parent,
|
|
String* name_to_modify,
|
|
bool symbolize,
|
|
bool obfuscate) {
|
|
if (name_to_modify->Length() >= 1 && name_to_modify->CharAt(0) == '_') {
|
|
const Library& library =
|
|
Library::Handle(Z, LookupLibraryByKernelLibrary(parent));
|
|
*name_to_modify = library.PrivateName(*name_to_modify);
|
|
if (obfuscate && I->obfuscate()) {
|
|
const String& library_key = String::Handle(library.private_key());
|
|
Obfuscator obfuscator(thread_, library_key);
|
|
*name_to_modify = obfuscator.Rename(*name_to_modify);
|
|
}
|
|
} else if (symbolize) {
|
|
*name_to_modify = Symbols::New(thread_, *name_to_modify);
|
|
if (obfuscate && I->obfuscate()) {
|
|
const String& library_key = String::Handle();
|
|
Obfuscator obfuscator(thread_, library_key);
|
|
*name_to_modify = obfuscator.Rename(*name_to_modify);
|
|
}
|
|
}
|
|
return *name_to_modify;
|
|
}
|
|
|
|
String& TranslationHelper::ManglePrivateName(const Library& library,
|
|
String* name_to_modify,
|
|
bool symbolize,
|
|
bool obfuscate) {
|
|
if (name_to_modify->Length() >= 1 && name_to_modify->CharAt(0) == '_') {
|
|
*name_to_modify = library.PrivateName(*name_to_modify);
|
|
if (obfuscate && I->obfuscate()) {
|
|
const String& library_key = String::Handle(library.private_key());
|
|
Obfuscator obfuscator(thread_, library_key);
|
|
*name_to_modify = obfuscator.Rename(*name_to_modify);
|
|
}
|
|
} else if (symbolize) {
|
|
*name_to_modify = Symbols::New(thread_, *name_to_modify);
|
|
if (obfuscate && I->obfuscate()) {
|
|
const String& library_key = String::Handle();
|
|
Obfuscator obfuscator(thread_, library_key);
|
|
*name_to_modify = obfuscator.Rename(*name_to_modify);
|
|
}
|
|
}
|
|
return *name_to_modify;
|
|
}
|
|
|
|
FlowGraphBuilder::FlowGraphBuilder(
|
|
intptr_t kernel_offset,
|
|
ParsedFunction* parsed_function,
|
|
const ZoneGrowableArray<const ICData*>& ic_data_array,
|
|
ZoneGrowableArray<intptr_t>* context_level_array,
|
|
InlineExitCollector* exit_collector,
|
|
bool optimizing,
|
|
intptr_t osr_id,
|
|
intptr_t first_block_id)
|
|
: BaseFlowGraphBuilder(parsed_function,
|
|
first_block_id - 1,
|
|
context_level_array),
|
|
translation_helper_(Thread::Current()),
|
|
thread_(translation_helper_.thread()),
|
|
zone_(translation_helper_.zone()),
|
|
kernel_offset_(kernel_offset),
|
|
parsed_function_(parsed_function),
|
|
optimizing_(optimizing),
|
|
osr_id_(osr_id),
|
|
ic_data_array_(ic_data_array),
|
|
exit_collector_(exit_collector),
|
|
next_function_id_(0),
|
|
loop_depth_(0),
|
|
try_depth_(0),
|
|
catch_depth_(0),
|
|
for_in_depth_(0),
|
|
graph_entry_(NULL),
|
|
scopes_(NULL),
|
|
breakable_block_(NULL),
|
|
switch_block_(NULL),
|
|
try_finally_block_(NULL),
|
|
catch_block_(NULL),
|
|
streaming_flow_graph_builder_(NULL) {
|
|
const Script& script =
|
|
Script::Handle(Z, parsed_function->function().script());
|
|
H.InitFromScript(script);
|
|
}
|
|
|
|
FlowGraphBuilder::~FlowGraphBuilder() {}
|
|
|
|
Fragment FlowGraphBuilder::TranslateFinallyFinalizers(
|
|
TryFinallyBlock* outer_finally,
|
|
intptr_t target_context_depth) {
|
|
TryFinallyBlock* const saved_block = try_finally_block_;
|
|
TryCatchBlock* const saved_try_catch_block = try_catch_block_;
|
|
const intptr_t saved_depth = context_depth_;
|
|
const intptr_t saved_try_depth = try_depth_;
|
|
|
|
Fragment instructions;
|
|
|
|
// While translating the body of a finalizer we need to set the try-finally
|
|
// block which is active when translating the body.
|
|
while (try_finally_block_ != outer_finally) {
|
|
// Set correct try depth (in case there are nested try statements).
|
|
try_depth_ = try_finally_block_->try_depth();
|
|
|
|
// Potentially restore the context to what is expected for the finally
|
|
// block.
|
|
instructions += AdjustContextTo(try_finally_block_->context_depth());
|
|
|
|
// The to-be-translated finalizer has to have the correct try-index (namely
|
|
// the one outside the try-finally block).
|
|
bool changed_try_index = false;
|
|
intptr_t target_try_index = try_finally_block_->try_index();
|
|
while (CurrentTryIndex() != target_try_index) {
|
|
try_catch_block_ = try_catch_block_->outer();
|
|
changed_try_index = true;
|
|
}
|
|
if (changed_try_index) {
|
|
JoinEntryInstr* entry = BuildJoinEntry();
|
|
instructions += Goto(entry);
|
|
instructions = Fragment(instructions.entry, entry);
|
|
}
|
|
|
|
intptr_t finalizer_kernel_offset =
|
|
try_finally_block_->finalizer_kernel_offset();
|
|
try_finally_block_ = try_finally_block_->outer();
|
|
instructions += streaming_flow_graph_builder_->BuildStatementAt(
|
|
finalizer_kernel_offset);
|
|
|
|
// We only need to make sure that if the finalizer ended normally, we
|
|
// continue towards the next outer try-finally.
|
|
if (!instructions.is_open()) break;
|
|
}
|
|
|
|
if (instructions.is_open() && target_context_depth != -1) {
|
|
// A target context depth of -1 indicates that the code after this
|
|
// will not care about the context chain so we can leave it any way we
|
|
// want after the last finalizer. That is used when returning.
|
|
instructions += AdjustContextTo(target_context_depth);
|
|
}
|
|
|
|
try_finally_block_ = saved_block;
|
|
try_catch_block_ = saved_try_catch_block;
|
|
context_depth_ = saved_depth;
|
|
try_depth_ = saved_try_depth;
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::EnterScope(intptr_t kernel_offset,
|
|
intptr_t* num_context_variables) {
|
|
Fragment instructions;
|
|
const intptr_t context_size =
|
|
scopes_->scopes.Lookup(kernel_offset)->num_context_variables();
|
|
if (context_size > 0) {
|
|
instructions += PushContext(context_size);
|
|
instructions += Drop();
|
|
}
|
|
if (num_context_variables != NULL) {
|
|
*num_context_variables = context_size;
|
|
}
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::ExitScope(intptr_t kernel_offset) {
|
|
Fragment instructions;
|
|
const intptr_t context_size =
|
|
scopes_->scopes.Lookup(kernel_offset)->num_context_variables();
|
|
if (context_size > 0) {
|
|
instructions += PopContext();
|
|
}
|
|
return instructions;
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::LoadContextAt(int depth) {
|
|
intptr_t delta = context_depth_ - depth;
|
|
ASSERT(delta >= 0);
|
|
Fragment instructions = LoadLocal(parsed_function_->current_context_var());
|
|
while (delta-- > 0) {
|
|
instructions += LoadField(Context::parent_offset());
|
|
}
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::AdjustContextTo(int depth) {
|
|
ASSERT(depth <= context_depth_ && depth >= 0);
|
|
Fragment instructions;
|
|
if (depth < context_depth_) {
|
|
instructions += LoadContextAt(depth);
|
|
instructions += StoreLocal(TokenPosition::kNoSource,
|
|
parsed_function_->current_context_var());
|
|
instructions += Drop();
|
|
context_depth_ = depth;
|
|
}
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::PushContext(int size) {
|
|
ASSERT(size > 0);
|
|
Fragment instructions = AllocateContext(size);
|
|
LocalVariable* context = MakeTemporary();
|
|
instructions += LoadLocal(context);
|
|
instructions += LoadLocal(parsed_function_->current_context_var());
|
|
instructions +=
|
|
StoreInstanceField(TokenPosition::kNoSource, Context::parent_offset());
|
|
instructions += StoreLocal(TokenPosition::kNoSource,
|
|
parsed_function_->current_context_var());
|
|
++context_depth_;
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::PopContext() {
|
|
return AdjustContextTo(context_depth_ - 1);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::LoadInstantiatorTypeArguments() {
|
|
// TODO(27590): We could use `active_class_->IsGeneric()`.
|
|
Fragment instructions;
|
|
if (scopes_->type_arguments_variable != NULL) {
|
|
#ifdef DEBUG
|
|
Function& function =
|
|
Function::Handle(Z, parsed_function_->function().raw());
|
|
while (function.IsClosureFunction()) {
|
|
function = function.parent_function();
|
|
}
|
|
ASSERT(function.IsFactory());
|
|
#endif
|
|
instructions += LoadLocal(scopes_->type_arguments_variable);
|
|
} else if (scopes_->this_variable != NULL &&
|
|
active_class_.ClassNumTypeArguments() > 0) {
|
|
ASSERT(!parsed_function_->function().IsFactory());
|
|
intptr_t type_arguments_field_offset =
|
|
active_class_.klass->type_arguments_field_offset();
|
|
ASSERT(type_arguments_field_offset != Class::kNoTypeArguments);
|
|
|
|
instructions += LoadLocal(scopes_->this_variable);
|
|
instructions += LoadField(type_arguments_field_offset);
|
|
} else {
|
|
instructions += NullConstant();
|
|
}
|
|
return instructions;
|
|
}
|
|
|
|
// This function is responsible for pushing a type arguments vector which
|
|
// contains all type arguments of enclosing functions prepended to the type
|
|
// arguments of the current function.
|
|
Fragment FlowGraphBuilder::LoadFunctionTypeArguments() {
|
|
Fragment instructions;
|
|
if (!Isolate::Current()->reify_generic_functions()) {
|
|
instructions += NullConstant();
|
|
return instructions;
|
|
}
|
|
|
|
const Function& function = parsed_function_->function();
|
|
|
|
if (function.IsGeneric() || function.HasGenericParent()) {
|
|
ASSERT(parsed_function_->function_type_arguments() != NULL);
|
|
instructions += LoadLocal(parsed_function_->function_type_arguments());
|
|
} else {
|
|
instructions += NullConstant();
|
|
}
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::InstantiateType(const AbstractType& type) {
|
|
Value* function_type_args = Pop();
|
|
Value* instantiator_type_args = Pop();
|
|
InstantiateTypeInstr* instr = new (Z) InstantiateTypeInstr(
|
|
TokenPosition::kNoSource, type, instantiator_type_args,
|
|
function_type_args, GetNextDeoptId());
|
|
Push(instr);
|
|
return Fragment(instr);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::InstantiateTypeArguments(
|
|
const TypeArguments& type_arguments) {
|
|
Value* function_type_args = Pop();
|
|
Value* instantiator_type_args = Pop();
|
|
InstantiateTypeArgumentsInstr* instr = new (Z) InstantiateTypeArgumentsInstr(
|
|
TokenPosition::kNoSource, type_arguments, *active_class_.klass,
|
|
instantiator_type_args, function_type_args, GetNextDeoptId());
|
|
Push(instr);
|
|
return Fragment(instr);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::TranslateInstantiatedTypeArguments(
|
|
const TypeArguments& type_arguments) {
|
|
Fragment instructions;
|
|
|
|
if (type_arguments.IsNull() || type_arguments.IsInstantiated()) {
|
|
// There are no type references to type parameters so we can just take it.
|
|
instructions += Constant(type_arguments);
|
|
} else {
|
|
// The [type_arguments] vector contains a type reference to a type
|
|
// parameter we need to resolve it.
|
|
const bool use_instantiator =
|
|
type_arguments.IsUninstantiatedIdentity() ||
|
|
type_arguments.CanShareInstantiatorTypeArguments(*active_class_.klass);
|
|
if (use_instantiator) {
|
|
// If the instantiator type arguments are just passed on, we don't need to
|
|
// resolve the type parameters.
|
|
//
|
|
// This is for example the case here:
|
|
// class Foo<T> {
|
|
// newList() => new List<T>();
|
|
// }
|
|
// We just use the type argument vector from the [Foo] object and pass it
|
|
// directly to the `new List<T>()` factory constructor.
|
|
instructions += LoadInstantiatorTypeArguments();
|
|
} else {
|
|
// Otherwise we need to resolve [TypeParameterType]s in the type
|
|
// expression based on the current instantiator type argument vector.
|
|
if (!type_arguments.IsInstantiated(kCurrentClass)) {
|
|
instructions += LoadInstantiatorTypeArguments();
|
|
} else {
|
|
instructions += NullConstant();
|
|
}
|
|
if (!type_arguments.IsInstantiated(kFunctions)) {
|
|
instructions += LoadFunctionTypeArguments();
|
|
} else {
|
|
instructions += NullConstant();
|
|
}
|
|
instructions += InstantiateTypeArguments(type_arguments);
|
|
}
|
|
}
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::AllocateContext(intptr_t size) {
|
|
AllocateContextInstr* allocate =
|
|
new (Z) AllocateContextInstr(TokenPosition::kNoSource, size);
|
|
Push(allocate);
|
|
return Fragment(allocate);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::AllocateObject(TokenPosition position,
|
|
const Class& klass,
|
|
intptr_t argument_count) {
|
|
ArgumentArray arguments = GetArguments(argument_count);
|
|
AllocateObjectInstr* allocate =
|
|
new (Z) AllocateObjectInstr(position, klass, arguments);
|
|
Push(allocate);
|
|
return Fragment(allocate);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::AllocateObject(const Class& klass,
|
|
const Function& closure_function) {
|
|
ArgumentArray arguments = new (Z) ZoneGrowableArray<PushArgumentInstr*>(Z, 0);
|
|
AllocateObjectInstr* allocate =
|
|
new (Z) AllocateObjectInstr(TokenPosition::kNoSource, klass, arguments);
|
|
allocate->set_closure_function(closure_function);
|
|
Push(allocate);
|
|
return Fragment(allocate);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::BooleanNegate() {
|
|
BooleanNegateInstr* negate = new (Z) BooleanNegateInstr(Pop());
|
|
Push(negate);
|
|
return Fragment(negate);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::StrictCompare(Token::Kind kind,
|
|
bool number_check /* = false */) {
|
|
Value* right = Pop();
|
|
Value* left = Pop();
|
|
StrictCompareInstr* compare =
|
|
new (Z) StrictCompareInstr(TokenPosition::kNoSource, kind, left, right,
|
|
number_check, GetNextDeoptId());
|
|
Push(compare);
|
|
return Fragment(compare);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::BranchIfTrue(TargetEntryInstr** then_entry,
|
|
TargetEntryInstr** otherwise_entry,
|
|
bool negate) {
|
|
Fragment instructions = Constant(Bool::True());
|
|
return instructions + BranchIfEqual(then_entry, otherwise_entry, negate);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::BranchIfNull(TargetEntryInstr** then_entry,
|
|
TargetEntryInstr** otherwise_entry,
|
|
bool negate) {
|
|
Fragment instructions = NullConstant();
|
|
return instructions + BranchIfEqual(then_entry, otherwise_entry, negate);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::BranchIfEqual(TargetEntryInstr** then_entry,
|
|
TargetEntryInstr** otherwise_entry,
|
|
bool negate) {
|
|
Value* right_value = Pop();
|
|
Value* left_value = Pop();
|
|
StrictCompareInstr* compare = new (Z) StrictCompareInstr(
|
|
TokenPosition::kNoSource, negate ? Token::kNE_STRICT : Token::kEQ_STRICT,
|
|
left_value, right_value, false, GetNextDeoptId());
|
|
BranchInstr* branch = new (Z) BranchInstr(compare, GetNextDeoptId());
|
|
*then_entry = *branch->true_successor_address() = BuildTargetEntry();
|
|
*otherwise_entry = *branch->false_successor_address() = BuildTargetEntry();
|
|
return Fragment(branch).closed();
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::BranchIfStrictEqual(
|
|
TargetEntryInstr** then_entry,
|
|
TargetEntryInstr** otherwise_entry) {
|
|
Value* rhs = Pop();
|
|
Value* lhs = Pop();
|
|
StrictCompareInstr* compare =
|
|
new (Z) StrictCompareInstr(TokenPosition::kNoSource, Token::kEQ_STRICT,
|
|
lhs, rhs, false, GetNextDeoptId());
|
|
BranchInstr* branch = new (Z) BranchInstr(compare, GetNextDeoptId());
|
|
*then_entry = *branch->true_successor_address() = BuildTargetEntry();
|
|
*otherwise_entry = *branch->false_successor_address() = BuildTargetEntry();
|
|
return Fragment(branch).closed();
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::CatchBlockEntry(const Array& handler_types,
|
|
intptr_t handler_index,
|
|
bool needs_stacktrace,
|
|
bool is_synthesized) {
|
|
LocalVariable* exception_var = CurrentException();
|
|
LocalVariable* stacktrace_var = CurrentStackTrace();
|
|
LocalVariable* raw_exception_var = CurrentRawException();
|
|
LocalVariable* raw_stacktrace_var = CurrentRawStackTrace();
|
|
|
|
CatchBlockEntryInstr* entry = new (Z) CatchBlockEntryInstr(
|
|
TokenPosition::kNoSource, // Token position of catch block.
|
|
is_synthesized, // whether catch block was synthesized by FE compiler
|
|
AllocateBlockId(), CurrentTryIndex(), graph_entry_, handler_types,
|
|
handler_index, *exception_var, *stacktrace_var, needs_stacktrace,
|
|
GetNextDeoptId(), raw_exception_var, raw_stacktrace_var);
|
|
graph_entry_->AddCatchEntry(entry);
|
|
|
|
Fragment instructions(entry);
|
|
|
|
// Auxiliary variables introduced by the try catch can be captured if we are
|
|
// inside a function with yield/resume points. In this case we first need
|
|
// to restore the context to match the context at entry into the closure.
|
|
const bool should_restore_closure_context =
|
|
CurrentException()->is_captured() || CurrentCatchContext()->is_captured();
|
|
LocalVariable* context_variable = parsed_function_->current_context_var();
|
|
if (should_restore_closure_context) {
|
|
ASSERT(parsed_function_->function().IsClosureFunction());
|
|
LocalScope* scope = parsed_function_->node_sequence()->scope();
|
|
|
|
LocalVariable* closure_parameter = scope->VariableAt(0);
|
|
ASSERT(!closure_parameter->is_captured());
|
|
instructions += LoadLocal(closure_parameter);
|
|
instructions += LoadField(Closure::context_offset());
|
|
instructions += StoreLocal(TokenPosition::kNoSource, context_variable);
|
|
instructions += Drop();
|
|
}
|
|
|
|
if (exception_var->is_captured()) {
|
|
instructions += LoadLocal(context_variable);
|
|
instructions += LoadLocal(raw_exception_var);
|
|
instructions +=
|
|
StoreInstanceField(TokenPosition::kNoSource,
|
|
Context::variable_offset(exception_var->index()));
|
|
}
|
|
if (stacktrace_var->is_captured()) {
|
|
instructions += LoadLocal(context_variable);
|
|
instructions += LoadLocal(raw_stacktrace_var);
|
|
instructions +=
|
|
StoreInstanceField(TokenPosition::kNoSource,
|
|
Context::variable_offset(stacktrace_var->index()));
|
|
}
|
|
|
|
// :saved_try_context_var can be captured in the context of
|
|
// of the closure, in this case CatchBlockEntryInstr restores
|
|
// :current_context_var to point to closure context in the
|
|
// same way as normal function prologue does.
|
|
// Update current context depth to reflect that.
|
|
const intptr_t saved_context_depth = context_depth_;
|
|
ASSERT(!CurrentCatchContext()->is_captured() ||
|
|
CurrentCatchContext()->owner()->context_level() == 0);
|
|
context_depth_ = 0;
|
|
instructions += LoadLocal(CurrentCatchContext());
|
|
instructions += StoreLocal(TokenPosition::kNoSource,
|
|
parsed_function_->current_context_var());
|
|
instructions += Drop();
|
|
context_depth_ = saved_context_depth;
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::TryCatch(int try_handler_index) {
|
|
// The body of the try needs to have it's own block in order to get a new try
|
|
// index.
|
|
//
|
|
// => We therefore create a block for the body (fresh try index) and another
|
|
// join block (with current try index).
|
|
Fragment body;
|
|
JoinEntryInstr* entry = new (Z)
|
|
JoinEntryInstr(AllocateBlockId(), try_handler_index, GetNextDeoptId());
|
|
body += LoadLocal(parsed_function_->current_context_var());
|
|
body += StoreLocal(TokenPosition::kNoSource, CurrentCatchContext());
|
|
body += Drop();
|
|
body += Goto(entry);
|
|
return Fragment(body.entry, entry);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::CheckStackOverflowInPrologue(
|
|
TokenPosition position) {
|
|
if (IsInlining()) {
|
|
// If we are inlining don't actually attach the stack check. We must still
|
|
// create the stack check in order to allocate a deopt id.
|
|
CheckStackOverflow(position);
|
|
return Fragment();
|
|
}
|
|
return CheckStackOverflow(position);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::CheckStackOverflow(TokenPosition position) {
|
|
return Fragment(
|
|
new (Z) CheckStackOverflowInstr(position, loop_depth_, GetNextDeoptId()));
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::CloneContext(intptr_t num_context_variables) {
|
|
LocalVariable* context_variable = parsed_function_->current_context_var();
|
|
|
|
Fragment instructions = LoadLocal(context_variable);
|
|
|
|
CloneContextInstr* clone_instruction = new (Z) CloneContextInstr(
|
|
TokenPosition::kNoSource, Pop(), num_context_variables, GetNextDeoptId());
|
|
instructions <<= clone_instruction;
|
|
Push(clone_instruction);
|
|
|
|
instructions += StoreLocal(TokenPosition::kNoSource, context_variable);
|
|
instructions += Drop();
|
|
return instructions;
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::Constant(const Object& value) {
|
|
ASSERT(value.IsNotTemporaryScopedHandle());
|
|
ConstantInstr* constant = new (Z) ConstantInstr(value);
|
|
Push(constant);
|
|
return Fragment(constant);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::CreateArray() {
|
|
Value* element_count = Pop();
|
|
CreateArrayInstr* array =
|
|
new (Z) CreateArrayInstr(TokenPosition::kNoSource,
|
|
Pop(), // Element type.
|
|
element_count, GetNextDeoptId());
|
|
Push(array);
|
|
return Fragment(array);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::Goto(JoinEntryInstr* destination) {
|
|
return Fragment(new (Z) GotoInstr(destination, GetNextDeoptId())).closed();
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::IntConstant(int64_t value) {
|
|
return Fragment(
|
|
Constant(Integer::ZoneHandle(Z, Integer::New(value, Heap::kOld))));
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::InstanceCall(
|
|
TokenPosition position,
|
|
const String& name,
|
|
Token::Kind kind,
|
|
intptr_t type_args_len,
|
|
intptr_t argument_count,
|
|
const Array& argument_names,
|
|
intptr_t checked_argument_count,
|
|
const Function& interface_target,
|
|
const InferredTypeMetadata* result_type) {
|
|
const intptr_t total_count = argument_count + (type_args_len > 0 ? 1 : 0);
|
|
ArgumentArray arguments = GetArguments(total_count);
|
|
InstanceCallInstr* call = new (Z) InstanceCallInstr(
|
|
position, name, kind, arguments, type_args_len, argument_names,
|
|
checked_argument_count, ic_data_array_, GetNextDeoptId(),
|
|
interface_target);
|
|
if ((result_type != NULL) && !result_type->IsTrivial()) {
|
|
call->SetResultType(Z, CompileType::CreateNullable(result_type->nullable,
|
|
result_type->cid));
|
|
}
|
|
Push(call);
|
|
return Fragment(call);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::ClosureCall(intptr_t type_args_len,
|
|
intptr_t argument_count,
|
|
const Array& argument_names) {
|
|
Value* function = Pop();
|
|
const intptr_t total_count = argument_count + (type_args_len > 0 ? 1 : 0);
|
|
ArgumentArray arguments = GetArguments(total_count);
|
|
ClosureCallInstr* call = new (Z)
|
|
ClosureCallInstr(function, arguments, type_args_len, argument_names,
|
|
TokenPosition::kNoSource, GetNextDeoptId());
|
|
Push(call);
|
|
return Fragment(call);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::ThrowException(TokenPosition position) {
|
|
Fragment instructions;
|
|
instructions += Drop();
|
|
instructions +=
|
|
Fragment(new (Z) ThrowInstr(position, GetNextDeoptId())).closed();
|
|
// Use it's side effect of leaving a constant on the stack (does not change
|
|
// the graph).
|
|
NullConstant();
|
|
|
|
pending_argument_count_ -= 1;
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::TailCall(const Code& code) {
|
|
Fragment instructions;
|
|
Value* arg_desc = Pop();
|
|
return Fragment(new (Z) TailCallInstr(code, arg_desc));
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::TestTypeArgsLen(Fragment eq_branch,
|
|
Fragment neq_branch,
|
|
intptr_t num_type_args) {
|
|
Fragment test;
|
|
|
|
TargetEntryInstr* eq_entry;
|
|
TargetEntryInstr* neq_entry;
|
|
|
|
test += LoadArgDescriptor();
|
|
test += LoadField(ArgumentsDescriptor::type_args_len_offset());
|
|
test += IntConstant(num_type_args);
|
|
test += BranchIfEqual(&eq_entry, &neq_entry);
|
|
|
|
eq_branch.Prepend(eq_entry);
|
|
neq_branch.Prepend(neq_entry);
|
|
|
|
JoinEntryInstr* join = BuildJoinEntry();
|
|
eq_branch += Goto(join);
|
|
neq_branch += Goto(join);
|
|
|
|
return Fragment(test.entry, join);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::TestDelayedTypeArgs(LocalVariable* closure,
|
|
Fragment present,
|
|
Fragment absent) {
|
|
Fragment test;
|
|
|
|
TargetEntryInstr* absent_entry;
|
|
TargetEntryInstr* present_entry;
|
|
|
|
test += LoadLocal(closure);
|
|
test += LoadField(Closure::delayed_type_arguments_offset());
|
|
test += Constant(Object::empty_type_arguments());
|
|
test += BranchIfEqual(&absent_entry, &present_entry);
|
|
|
|
present.Prepend(present_entry);
|
|
absent.Prepend(absent_entry);
|
|
|
|
JoinEntryInstr* join = BuildJoinEntry();
|
|
absent += Goto(join);
|
|
present += Goto(join);
|
|
|
|
return Fragment(test.entry, join);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::TestAnyTypeArgs(Fragment present,
|
|
Fragment absent) {
|
|
if (parsed_function_->function().IsClosureFunction()) {
|
|
LocalVariable* closure =
|
|
parsed_function_->node_sequence()->scope()->VariableAt(0);
|
|
|
|
JoinEntryInstr* complete = BuildJoinEntry();
|
|
JoinEntryInstr* present_entry = BuildJoinEntry();
|
|
|
|
Fragment test = TestTypeArgsLen(
|
|
TestDelayedTypeArgs(closure, Goto(present_entry), absent),
|
|
Goto(present_entry), 0);
|
|
test += Goto(complete);
|
|
|
|
Fragment(present_entry) + present + Goto(complete);
|
|
|
|
return Fragment(test.entry, complete);
|
|
} else {
|
|
return TestTypeArgsLen(absent, present, 0);
|
|
}
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::RethrowException(TokenPosition position,
|
|
int catch_try_index) {
|
|
Fragment instructions;
|
|
instructions += Drop();
|
|
instructions += Drop();
|
|
instructions += Fragment(new (Z) ReThrowInstr(position, catch_try_index,
|
|
GetNextDeoptId()))
|
|
.closed();
|
|
// Use it's side effect of leaving a constant on the stack (does not change
|
|
// the graph).
|
|
NullConstant();
|
|
|
|
pending_argument_count_ -= 2;
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::LoadClassId() {
|
|
LoadClassIdInstr* load = new (Z) LoadClassIdInstr(Pop());
|
|
Push(load);
|
|
return Fragment(load);
|
|
}
|
|
|
|
const Field& MayCloneField(Zone* zone, const Field& field) {
|
|
if ((Compiler::IsBackgroundCompilation() ||
|
|
FLAG_force_clone_compiler_objects) &&
|
|
field.IsOriginal()) {
|
|
return Field::ZoneHandle(zone, field.CloneFromOriginal());
|
|
} else {
|
|
ASSERT(field.IsZoneHandle());
|
|
return field;
|
|
}
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::LoadField(const Field& field) {
|
|
LoadFieldInstr* load =
|
|
new (Z) LoadFieldInstr(Pop(), &MayCloneField(Z, field),
|
|
AbstractType::ZoneHandle(Z, field.type()),
|
|
TokenPosition::kNoSource, parsed_function_);
|
|
Push(load);
|
|
return Fragment(load);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::LoadField(intptr_t offset, intptr_t class_id) {
|
|
return BaseFlowGraphBuilder::LoadField(offset, class_id);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::LoadField(intptr_t offset, intptr_t class_id) {
|
|
LoadFieldInstr* load = new (Z) LoadFieldInstr(
|
|
Pop(), offset, AbstractType::ZoneHandle(Z), TokenPosition::kNoSource);
|
|
load->set_result_cid(class_id);
|
|
Push(load);
|
|
return Fragment(load);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::LoadIndexed(intptr_t index_scale) {
|
|
Value* index = Pop();
|
|
Value* array = Pop();
|
|
LoadIndexedInstr* instr = new (Z)
|
|
LoadIndexedInstr(array, index, index_scale, kArrayCid, kAlignedAccess,
|
|
Thread::kNoDeoptId, TokenPosition::kNoSource);
|
|
Push(instr);
|
|
return Fragment(instr);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::LoadNativeField(MethodRecognizer::Kind kind,
|
|
intptr_t offset,
|
|
const Type& type,
|
|
intptr_t class_id,
|
|
bool is_immutable) {
|
|
LoadFieldInstr* load =
|
|
new (Z) LoadFieldInstr(Pop(), offset, type, TokenPosition::kNoSource);
|
|
load->set_recognized_kind(kind);
|
|
load->set_result_cid(class_id);
|
|
load->set_is_immutable(is_immutable);
|
|
Push(load);
|
|
return Fragment(load);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::LoadLocal(LocalVariable* variable) {
|
|
LoadLocalInstr* load =
|
|
new (Z) LoadLocalInstr(*variable, TokenPosition::kNoSource);
|
|
Push(load);
|
|
return Fragment(load);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::LoadLocal(LocalVariable* variable) {
|
|
if (variable->is_captured()) {
|
|
Fragment instructions;
|
|
instructions += LoadContextAt(variable->owner()->context_level());
|
|
instructions += LoadField(Context::variable_offset(variable->index()));
|
|
return instructions;
|
|
} else {
|
|
return BaseFlowGraphBuilder::LoadLocal(variable);
|
|
}
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::InitStaticField(const Field& field) {
|
|
InitStaticFieldInstr* init = new (Z)
|
|
InitStaticFieldInstr(Pop(), MayCloneField(Z, field), GetNextDeoptId());
|
|
return Fragment(init);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::LoadStaticField() {
|
|
LoadStaticFieldInstr* load =
|
|
new (Z) LoadStaticFieldInstr(Pop(), TokenPosition::kNoSource);
|
|
Push(load);
|
|
return Fragment(load);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::NullConstant() {
|
|
return Constant(Instance::ZoneHandle(Z, Instance::null()));
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::NativeCall(const String* name,
|
|
const Function* function) {
|
|
InlineBailout("kernel::FlowGraphBuilder::NativeCall");
|
|
const intptr_t num_args =
|
|
function->NumParameters() +
|
|
((function->IsGeneric() && Isolate::Current()->reify_generic_functions())
|
|
? 1
|
|
: 0);
|
|
ArgumentArray arguments = GetArguments(num_args);
|
|
NativeCallInstr* call =
|
|
new (Z) NativeCallInstr(name, function, FLAG_link_natives_lazily,
|
|
function->end_token_pos(), arguments);
|
|
Push(call);
|
|
return Fragment(call);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::PushArgument() {
|
|
PushArgumentInstr* argument = new (Z) PushArgumentInstr(Pop());
|
|
Push(argument);
|
|
|
|
argument->set_temp_index(argument->temp_index() - 1);
|
|
++pending_argument_count_;
|
|
|
|
return Fragment(argument);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::Return(TokenPosition position) {
|
|
Fragment instructions;
|
|
const Function& function = parsed_function_->function();
|
|
|
|
// Emit a type check of the return type in checked mode for all functions
|
|
// and in strong mode for native functions.
|
|
if (I->type_checks() || (function.is_native() && I->strong())) {
|
|
const AbstractType& return_type =
|
|
AbstractType::Handle(Z, function.result_type());
|
|
instructions += CheckAssignable(return_type, Symbols::FunctionResult());
|
|
}
|
|
|
|
Value* value = Pop();
|
|
ASSERT(stack_ == NULL);
|
|
|
|
if (NeedsDebugStepCheck(function, position)) {
|
|
instructions += DebugStepCheck(position);
|
|
}
|
|
|
|
if (FLAG_causal_async_stacks &&
|
|
(function.IsAsyncClosure() || function.IsAsyncGenClosure())) {
|
|
// We are returning from an asynchronous closure. Before we do that, be
|
|
// sure to clear the thread's asynchronous stack trace.
|
|
const Function& target = Function::ZoneHandle(
|
|
Z, I->object_store()->async_clear_thread_stack_trace());
|
|
ASSERT(!target.IsNull());
|
|
instructions += StaticCall(TokenPosition::kNoSource, target,
|
|
/* argument_count = */ 0, ICData::kStatic);
|
|
instructions += Drop();
|
|
}
|
|
|
|
ReturnInstr* return_instr =
|
|
new (Z) ReturnInstr(position, value, GetNextDeoptId());
|
|
if (exit_collector_ != NULL) exit_collector_->AddExit(return_instr);
|
|
|
|
instructions <<= return_instr;
|
|
|
|
return instructions.closed();
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::CheckNull(TokenPosition position,
|
|
LocalVariable* receiver,
|
|
const String& function_name) {
|
|
Fragment instructions = LoadLocal(receiver);
|
|
|
|
CheckNullInstr* check_null =
|
|
new (Z) CheckNullInstr(Pop(), function_name, GetNextDeoptId(), position);
|
|
|
|
instructions <<= check_null;
|
|
|
|
// Null out receiver to make sure it is not saved into the frame before
|
|
// doing the call.
|
|
instructions += NullConstant();
|
|
instructions += StoreLocal(TokenPosition::kNoSource, receiver);
|
|
instructions += Drop();
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::StaticCall(TokenPosition position,
|
|
const Function& target,
|
|
intptr_t argument_count,
|
|
ICData::RebindRule rebind_rule) {
|
|
return StaticCall(position, target, argument_count, Array::null_array(),
|
|
rebind_rule);
|
|
}
|
|
|
|
static intptr_t GetResultCidOfListFactory(Zone* zone,
|
|
const Function& function,
|
|
intptr_t argument_count) {
|
|
if (!function.IsFactory()) {
|
|
return kDynamicCid;
|
|
}
|
|
|
|
const Class& owner = Class::Handle(zone, function.Owner());
|
|
if ((owner.library() != Library::CoreLibrary()) &&
|
|
(owner.library() != Library::TypedDataLibrary())) {
|
|
return kDynamicCid;
|
|
}
|
|
|
|
if ((owner.Name() == Symbols::List().raw()) &&
|
|
(function.name() == Symbols::ListFactory().raw())) {
|
|
ASSERT(argument_count == 1 || argument_count == 2);
|
|
return (argument_count == 1) ? kGrowableObjectArrayCid : kArrayCid;
|
|
}
|
|
return FactoryRecognizer::ResultCid(function);
|
|
}
|
|
|
|
void FlowGraphBuilder::SetResultTypeForStaticCall(
|
|
StaticCallInstr* call,
|
|
const Function& target,
|
|
intptr_t argument_count,
|
|
const InferredTypeMetadata* result_type) {
|
|
const intptr_t list_cid =
|
|
GetResultCidOfListFactory(Z, target, argument_count);
|
|
if (list_cid != kDynamicCid) {
|
|
ASSERT((result_type == NULL) || (result_type->cid == kDynamicCid) ||
|
|
(result_type->cid == list_cid));
|
|
call->SetResultType(Z, CompileType::FromCid(list_cid));
|
|
call->set_is_known_list_constructor(true);
|
|
return;
|
|
}
|
|
if (target.recognized_kind() != MethodRecognizer::kUnknown) {
|
|
intptr_t recognized_cid = MethodRecognizer::ResultCid(target);
|
|
if (recognized_cid != kDynamicCid) {
|
|
ASSERT((result_type == NULL) || (result_type->cid == kDynamicCid) ||
|
|
(result_type->cid == recognized_cid));
|
|
call->SetResultType(Z, CompileType::FromCid(recognized_cid));
|
|
return;
|
|
}
|
|
}
|
|
if ((result_type != NULL) && !result_type->IsTrivial()) {
|
|
call->SetResultType(Z, CompileType::CreateNullable(result_type->nullable,
|
|
result_type->cid));
|
|
}
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::StaticCall(TokenPosition position,
|
|
const Function& target,
|
|
intptr_t argument_count,
|
|
const Array& argument_names,
|
|
ICData::RebindRule rebind_rule,
|
|
const InferredTypeMetadata* result_type,
|
|
intptr_t type_args_count) {
|
|
const intptr_t total_count = argument_count + (type_args_count > 0 ? 1 : 0);
|
|
ArgumentArray arguments = GetArguments(total_count);
|
|
StaticCallInstr* call = new (Z)
|
|
StaticCallInstr(position, target, type_args_count, argument_names,
|
|
arguments, ic_data_array_, GetNextDeoptId(), rebind_rule);
|
|
SetResultTypeForStaticCall(call, target, argument_count, result_type);
|
|
Push(call);
|
|
return Fragment(call);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::StoreIndexed(intptr_t class_id) {
|
|
Value* value = Pop();
|
|
Value* index = Pop();
|
|
const StoreBarrierType emit_store_barrier =
|
|
value->BindsToConstant() ? kNoStoreBarrier : kEmitStoreBarrier;
|
|
StoreIndexedInstr* store = new (Z) StoreIndexedInstr(
|
|
Pop(), // Array.
|
|
index, value, emit_store_barrier, Instance::ElementSizeFor(class_id),
|
|
class_id, kAlignedAccess, Thread::kNoDeoptId, TokenPosition::kNoSource);
|
|
Push(store);
|
|
return Fragment(store);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::StoreInstanceField(
|
|
const Field& field,
|
|
bool is_initialization_store,
|
|
StoreBarrierType emit_store_barrier) {
|
|
Fragment instructions;
|
|
|
|
const AbstractType& dst_type = AbstractType::ZoneHandle(Z, field.type());
|
|
if (I->type_checks()) {
|
|
instructions +=
|
|
CheckAssignable(dst_type, String::ZoneHandle(Z, field.name()));
|
|
}
|
|
|
|
Value* value = Pop();
|
|
if (value->BindsToConstant()) {
|
|
emit_store_barrier = kNoStoreBarrier;
|
|
}
|
|
|
|
StoreInstanceFieldInstr* store = new (Z)
|
|
StoreInstanceFieldInstr(MayCloneField(Z, field), Pop(), value,
|
|
emit_store_barrier, TokenPosition::kNoSource);
|
|
store->set_is_initialization(is_initialization_store);
|
|
instructions <<= store;
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::StoreInstanceFieldGuarded(
|
|
const Field& field,
|
|
bool is_initialization_store) {
|
|
Fragment instructions;
|
|
const Field& field_clone = MayCloneField(Z, field);
|
|
if (I->use_field_guards()) {
|
|
LocalVariable* store_expression = MakeTemporary();
|
|
instructions += LoadLocal(store_expression);
|
|
instructions += GuardFieldClass(field_clone, GetNextDeoptId());
|
|
instructions += LoadLocal(store_expression);
|
|
instructions += GuardFieldLength(field_clone, GetNextDeoptId());
|
|
}
|
|
instructions += StoreInstanceField(field_clone, is_initialization_store);
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::StoreInstanceField(
|
|
TokenPosition position,
|
|
intptr_t offset,
|
|
StoreBarrierType emit_store_barrier) {
|
|
return BaseFlowGraphBuilder::StoreInstanceField(position, offset,
|
|
emit_store_barrier);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::StoreInstanceField(
|
|
TokenPosition position,
|
|
intptr_t offset,
|
|
StoreBarrierType emit_store_barrier) {
|
|
Value* value = Pop();
|
|
if (value->BindsToConstant()) {
|
|
emit_store_barrier = kNoStoreBarrier;
|
|
}
|
|
StoreInstanceFieldInstr* store = new (Z) StoreInstanceFieldInstr(
|
|
offset, Pop(), value, emit_store_barrier, position);
|
|
return Fragment(store);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::StoreLocal(TokenPosition position,
|
|
LocalVariable* variable) {
|
|
if (variable->is_captured()) {
|
|
Fragment instructions;
|
|
LocalVariable* value = MakeTemporary();
|
|
instructions += LoadContextAt(variable->owner()->context_level());
|
|
instructions += LoadLocal(value);
|
|
instructions += StoreInstanceField(
|
|
position, Context::variable_offset(variable->index()));
|
|
return instructions;
|
|
}
|
|
return StoreLocalRaw(position, variable);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::StoreLocalRaw(TokenPosition position,
|
|
LocalVariable* variable) {
|
|
Value* value = Pop();
|
|
StoreLocalInstr* store = new (Z) StoreLocalInstr(*variable, value, position);
|
|
Fragment instructions(store);
|
|
Push(store);
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::StoreStaticField(TokenPosition position,
|
|
const Field& field) {
|
|
return Fragment(
|
|
new (Z) StoreStaticFieldInstr(MayCloneField(Z, field), Pop(), position));
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::StringInterpolate(TokenPosition position) {
|
|
Value* array = Pop();
|
|
StringInterpolateInstr* interpolate =
|
|
new (Z) StringInterpolateInstr(array, position, GetNextDeoptId());
|
|
Push(interpolate);
|
|
return Fragment(interpolate);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::StringInterpolateSingle(TokenPosition position) {
|
|
const int kTypeArgsLen = 0;
|
|
const int kNumberOfArguments = 1;
|
|
const Array& kNoArgumentNames = Object::null_array();
|
|
const Class& cls =
|
|
Class::Handle(Library::LookupCoreClass(Symbols::StringBase()));
|
|
ASSERT(!cls.IsNull());
|
|
const Function& function = Function::ZoneHandle(
|
|
Z, Resolver::ResolveStatic(
|
|
cls, Library::PrivateCoreLibName(Symbols::InterpolateSingle()),
|
|
kTypeArgsLen, kNumberOfArguments, kNoArgumentNames));
|
|
Fragment instructions;
|
|
instructions += PushArgument();
|
|
instructions +=
|
|
StaticCall(position, function, /* argument_count = */ 1, ICData::kStatic);
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::ThrowTypeError() {
|
|
const Class& klass =
|
|
Class::ZoneHandle(Z, Library::LookupCoreClass(Symbols::TypeError()));
|
|
ASSERT(!klass.IsNull());
|
|
GrowableHandlePtrArray<const String> pieces(Z, 3);
|
|
pieces.Add(Symbols::TypeError());
|
|
pieces.Add(Symbols::Dot());
|
|
pieces.Add(H.DartSymbolObfuscate("_create"));
|
|
|
|
const Function& constructor = Function::ZoneHandle(
|
|
Z, klass.LookupConstructorAllowPrivate(
|
|
String::ZoneHandle(Z, Symbols::FromConcatAll(thread_, pieces))));
|
|
ASSERT(!constructor.IsNull());
|
|
|
|
const String& url = H.DartString(
|
|
parsed_function_->function().ToLibNamePrefixedQualifiedCString(),
|
|
Heap::kOld);
|
|
|
|
Fragment instructions;
|
|
|
|
// Create instance of _FallThroughError
|
|
instructions += AllocateObject(TokenPosition::kNoSource, klass, 0);
|
|
LocalVariable* instance = MakeTemporary();
|
|
|
|
// Call _TypeError._create constructor.
|
|
instructions += LoadLocal(instance);
|
|
instructions += PushArgument(); // this
|
|
|
|
instructions += Constant(url);
|
|
instructions += PushArgument(); // url
|
|
|
|
instructions += NullConstant();
|
|
instructions += PushArgument(); // line
|
|
|
|
instructions += IntConstant(0);
|
|
instructions += PushArgument(); // column
|
|
|
|
instructions += Constant(H.DartSymbolPlain("Malformed type."));
|
|
instructions += PushArgument(); // message
|
|
|
|
instructions += StaticCall(TokenPosition::kNoSource, constructor,
|
|
/* argument_count = */ 5, ICData::kStatic);
|
|
instructions += Drop();
|
|
|
|
// Throw the exception
|
|
instructions += PushArgument();
|
|
instructions += ThrowException(TokenPosition::kNoSource);
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::ThrowNoSuchMethodError() {
|
|
const Class& klass = Class::ZoneHandle(
|
|
Z, Library::LookupCoreClass(Symbols::NoSuchMethodError()));
|
|
ASSERT(!klass.IsNull());
|
|
const Function& throw_function = Function::ZoneHandle(
|
|
Z, klass.LookupStaticFunctionAllowPrivate(Symbols::ThrowNew()));
|
|
ASSERT(!throw_function.IsNull());
|
|
|
|
Fragment instructions;
|
|
|
|
// Call NoSuchMethodError._throwNew static function.
|
|
instructions += NullConstant();
|
|
instructions += PushArgument(); // receiver
|
|
|
|
instructions += Constant(H.DartString("<unknown>", Heap::kOld));
|
|
instructions += PushArgument(); // memberName
|
|
|
|
instructions += IntConstant(-1);
|
|
instructions += PushArgument(); // invocation_type
|
|
|
|
instructions += NullConstant();
|
|
instructions += PushArgument(); // type arguments
|
|
|
|
instructions += NullConstant();
|
|
instructions += PushArgument(); // arguments
|
|
|
|
instructions += NullConstant();
|
|
instructions += PushArgument(); // argumentNames
|
|
|
|
instructions += StaticCall(TokenPosition::kNoSource, throw_function,
|
|
/* argument_count = */ 6, ICData::kStatic);
|
|
// Leave "result" on the stack since callers expect it to be there (even
|
|
// though the function will result in an exception).
|
|
|
|
return instructions;
|
|
}
|
|
|
|
RawFunction* FlowGraphBuilder::LookupMethodByMember(NameIndex target,
|
|
const String& method_name) {
|
|
NameIndex kernel_class = H.EnclosingName(target);
|
|
Class& klass = Class::Handle(Z, H.LookupClassByKernelClass(kernel_class));
|
|
|
|
RawFunction* function = klass.LookupFunctionAllowPrivate(method_name);
|
|
#ifdef DEBUG
|
|
if (function == Object::null()) {
|
|
THR_Print("Unable to find \'%s\' in %s\n", method_name.ToCString(),
|
|
klass.ToCString());
|
|
}
|
|
#endif
|
|
ASSERT(function != Object::null());
|
|
return function;
|
|
}
|
|
|
|
LocalVariable* BaseFlowGraphBuilder::MakeTemporary() {
|
|
char name[64];
|
|
intptr_t index = stack_->definition()->temp_index();
|
|
Utils::SNPrint(name, 64, ":temp%" Pd, index);
|
|
const String& symbol_name =
|
|
String::ZoneHandle(Z, Symbols::New(thread_, name));
|
|
LocalVariable* variable =
|
|
new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
|
|
symbol_name, Object::dynamic_type());
|
|
// Set the index relative to the base of the expression stack including
|
|
// outgoing arguments.
|
|
variable->set_index(kFirstLocalSlotFromFp -
|
|
parsed_function_->num_stack_locals() -
|
|
pending_argument_count_ - index);
|
|
|
|
// The value has uses as if it were a local variable. Mark the definition
|
|
// as used so that its temp index will not be cleared (causing it to never
|
|
// be materialized in the expression stack).
|
|
stack_->definition()->set_ssa_temp_index(0);
|
|
|
|
return variable;
|
|
}
|
|
|
|
intptr_t BaseFlowGraphBuilder::CurrentTryIndex() {
|
|
if (try_catch_block_ == NULL) {
|
|
return CatchClauseNode::kInvalidTryIndex;
|
|
} else {
|
|
return try_catch_block_->try_index();
|
|
}
|
|
}
|
|
|
|
LocalVariable* FlowGraphBuilder::LookupVariable(intptr_t kernel_offset) {
|
|
LocalVariable* local = scopes_->locals.Lookup(kernel_offset);
|
|
ASSERT(local != NULL);
|
|
return local;
|
|
}
|
|
|
|
void BaseFlowGraphBuilder::SetTempIndex(Definition* definition) {
|
|
definition->set_temp_index(
|
|
stack_ == NULL ? 0 : stack_->definition()->temp_index() + 1);
|
|
}
|
|
|
|
void BaseFlowGraphBuilder::Push(Definition* definition) {
|
|
SetTempIndex(definition);
|
|
Value::AddToList(new (Z) Value(definition), &stack_);
|
|
}
|
|
|
|
Value* BaseFlowGraphBuilder::Pop() {
|
|
ASSERT(stack_ != NULL);
|
|
Value* value = stack_;
|
|
stack_ = value->next_use();
|
|
if (stack_ != NULL) stack_->set_previous_use(NULL);
|
|
|
|
value->set_next_use(NULL);
|
|
value->set_previous_use(NULL);
|
|
value->definition()->ClearSSATempIndex();
|
|
return value;
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::Drop() {
|
|
ASSERT(stack_ != NULL);
|
|
Fragment instructions;
|
|
Definition* definition = stack_->definition();
|
|
// The SSA renaming implementation doesn't like [LoadLocal]s without a
|
|
// tempindex.
|
|
if (definition->HasSSATemp() || definition->IsLoadLocal()) {
|
|
instructions <<= new (Z) DropTempsInstr(1, NULL);
|
|
} else {
|
|
definition->ClearTempIndex();
|
|
}
|
|
|
|
Pop();
|
|
return instructions;
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::DropTempsPreserveTop(
|
|
intptr_t num_temps_to_drop) {
|
|
Value* top = Pop();
|
|
|
|
for (intptr_t i = 0; i < num_temps_to_drop; ++i) {
|
|
Pop();
|
|
}
|
|
|
|
DropTempsInstr* drop_temps = new (Z) DropTempsInstr(num_temps_to_drop, top);
|
|
Push(drop_temps);
|
|
|
|
return Fragment(drop_temps);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::MakeTemp() {
|
|
MakeTempInstr* make_temp = new (Z) MakeTempInstr(Z);
|
|
Push(make_temp);
|
|
return Fragment(make_temp);
|
|
}
|
|
|
|
void FlowGraphBuilder::InlineBailout(const char* reason) {
|
|
bool is_inlining = exit_collector_ != NULL;
|
|
if (is_inlining) {
|
|
parsed_function_->function().set_is_inlinable(false);
|
|
parsed_function_->Bailout("kernel::FlowGraphBuilder", reason);
|
|
}
|
|
}
|
|
|
|
FlowGraph* FlowGraphBuilder::BuildGraph() {
|
|
const Function& function = parsed_function_->function();
|
|
|
|
#ifdef DEBUG
|
|
// If we attached the native name to the function after it's creation (namely
|
|
// after reading the constant table from the kernel blob), we must have done
|
|
// so before building flow graph for the functions (since FGB depends needs
|
|
// the native name to be there).
|
|
const Script& script = Script::Handle(Z, function.script());
|
|
const KernelProgramInfo& info =
|
|
KernelProgramInfo::Handle(script.kernel_program_info());
|
|
ASSERT(info.IsNull() ||
|
|
info.potential_natives() == GrowableObjectArray::null());
|
|
#endif
|
|
|
|
StreamingFlowGraphBuilder streaming_flow_graph_builder(
|
|
this, TypedData::Handle(Z, function.KernelData()),
|
|
function.KernelDataProgramOffset());
|
|
streaming_flow_graph_builder_ = &streaming_flow_graph_builder;
|
|
FlowGraph* result =
|
|
streaming_flow_graph_builder_->BuildGraph(function.kernel_offset());
|
|
streaming_flow_graph_builder_ = NULL;
|
|
return result;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::NativeFunctionBody(intptr_t first_positional_offset,
|
|
const Function& function) {
|
|
ASSERT(function.is_native());
|
|
// We explicitly build the graph for native functions in the same way that the
|
|
// from-source backend does. We should find a way to have a single component
|
|
// to build these graphs so that this code is not duplicated.
|
|
|
|
Fragment body;
|
|
MethodRecognizer::Kind kind = MethodRecognizer::RecognizeKind(function);
|
|
switch (kind) {
|
|
case MethodRecognizer::kObjectEquals:
|
|
body += LoadLocal(scopes_->this_variable);
|
|
body += LoadLocal(LookupVariable(first_positional_offset));
|
|
body += StrictCompare(Token::kEQ_STRICT);
|
|
break;
|
|
case MethodRecognizer::kStringBaseLength:
|
|
case MethodRecognizer::kStringBaseIsEmpty:
|
|
body += LoadLocal(scopes_->this_variable);
|
|
body += LoadNativeField(MethodRecognizer::kStringBaseLength,
|
|
String::length_offset(),
|
|
Type::ZoneHandle(Z, Type::SmiType()), kSmiCid,
|
|
/* is_immutable = */ true);
|
|
if (kind == MethodRecognizer::kStringBaseIsEmpty) {
|
|
body += IntConstant(0);
|
|
body += StrictCompare(Token::kEQ_STRICT);
|
|
}
|
|
break;
|
|
case MethodRecognizer::kGrowableArrayLength:
|
|
body += LoadLocal(scopes_->this_variable);
|
|
body += LoadNativeField(kind, GrowableObjectArray::length_offset(),
|
|
Type::ZoneHandle(Z, Type::SmiType()), kSmiCid);
|
|
break;
|
|
case MethodRecognizer::kObjectArrayLength:
|
|
case MethodRecognizer::kImmutableArrayLength:
|
|
body += LoadLocal(scopes_->this_variable);
|
|
body +=
|
|
LoadNativeField(kind, Array::length_offset(),
|
|
Type::ZoneHandle(Z, Type::SmiType()), kSmiCid, true);
|
|
break;
|
|
case MethodRecognizer::kTypedDataLength:
|
|
body += LoadLocal(scopes_->this_variable);
|
|
body +=
|
|
LoadNativeField(kind, TypedData::length_offset(),
|
|
Type::ZoneHandle(Z, Type::SmiType()), kSmiCid, true);
|
|
break;
|
|
case MethodRecognizer::kClassIDgetID:
|
|
body += LoadLocal(LookupVariable(first_positional_offset));
|
|
body += LoadClassId();
|
|
break;
|
|
case MethodRecognizer::kGrowableArrayCapacity:
|
|
body += LoadLocal(scopes_->this_variable);
|
|
body += LoadField(GrowableObjectArray::data_offset(), kArrayCid);
|
|
body += LoadNativeField(MethodRecognizer::kObjectArrayLength,
|
|
Array::length_offset(),
|
|
Type::ZoneHandle(Z, Type::SmiType()), kSmiCid);
|
|
break;
|
|
case MethodRecognizer::kListFactory: {
|
|
// factory List<E>([int length]) {
|
|
// return (:arg_desc.positional_count == 2) ? new _List<E>(length)
|
|
// : new _GrowableList<E>(0);
|
|
// }
|
|
const Library& core_lib = Library::Handle(Z, Library::CoreLibrary());
|
|
|
|
TargetEntryInstr *allocate_non_growable, *allocate_growable;
|
|
|
|
body += LoadArgDescriptor();
|
|
body +=
|
|
LoadField(ArgumentsDescriptor::positional_count_offset(), kSmiCid);
|
|
body += IntConstant(2);
|
|
body += BranchIfStrictEqual(&allocate_non_growable, &allocate_growable);
|
|
|
|
JoinEntryInstr* join = BuildJoinEntry();
|
|
|
|
{
|
|
const Class& cls = Class::Handle(
|
|
Z, core_lib.LookupClass(
|
|
Library::PrivateCoreLibName(Symbols::_List())));
|
|
ASSERT(!cls.IsNull());
|
|
const Function& func = Function::ZoneHandle(
|
|
Z, cls.LookupFactoryAllowPrivate(Symbols::_ListFactory()));
|
|
ASSERT(!func.IsNull());
|
|
|
|
Fragment allocate(allocate_non_growable);
|
|
allocate += LoadLocal(scopes_->type_arguments_variable);
|
|
allocate += PushArgument();
|
|
allocate += LoadLocal(LookupVariable(first_positional_offset));
|
|
allocate += PushArgument();
|
|
allocate +=
|
|
StaticCall(TokenPosition::kNoSource, func, 2, ICData::kStatic);
|
|
allocate += StoreLocal(TokenPosition::kNoSource,
|
|
parsed_function_->expression_temp_var());
|
|
allocate += Drop();
|
|
allocate += Goto(join);
|
|
}
|
|
|
|
{
|
|
const Class& cls = Class::Handle(
|
|
Z, core_lib.LookupClass(
|
|
Library::PrivateCoreLibName(Symbols::_GrowableList())));
|
|
ASSERT(!cls.IsNull());
|
|
const Function& func = Function::ZoneHandle(
|
|
Z, cls.LookupFactoryAllowPrivate(Symbols::_GrowableListFactory()));
|
|
ASSERT(!func.IsNull());
|
|
|
|
Fragment allocate(allocate_growable);
|
|
allocate += LoadLocal(scopes_->type_arguments_variable);
|
|
allocate += PushArgument();
|
|
allocate += IntConstant(0);
|
|
allocate += PushArgument();
|
|
allocate +=
|
|
StaticCall(TokenPosition::kNoSource, func, 2, ICData::kStatic);
|
|
allocate += StoreLocal(TokenPosition::kNoSource,
|
|
parsed_function_->expression_temp_var());
|
|
allocate += Drop();
|
|
allocate += Goto(join);
|
|
}
|
|
|
|
body = Fragment(body.entry, join);
|
|
body += LoadLocal(parsed_function_->expression_temp_var());
|
|
break;
|
|
}
|
|
case MethodRecognizer::kObjectArrayAllocate:
|
|
body += LoadLocal(scopes_->type_arguments_variable);
|
|
body += LoadLocal(LookupVariable(first_positional_offset));
|
|
body += CreateArray();
|
|
break;
|
|
case MethodRecognizer::kLinkedHashMap_getIndex:
|
|
body += LoadLocal(scopes_->this_variable);
|
|
body += LoadNativeField(kind, LinkedHashMap::index_offset(),
|
|
Object::dynamic_type(), kTypedDataUint32ArrayCid);
|
|
break;
|
|
case MethodRecognizer::kLinkedHashMap_setIndex:
|
|
body += LoadLocal(scopes_->this_variable);
|
|
body += LoadLocal(LookupVariable(first_positional_offset));
|
|
body += StoreInstanceField(TokenPosition::kNoSource,
|
|
LinkedHashMap::index_offset());
|
|
body += NullConstant();
|
|
break;
|
|
case MethodRecognizer::kLinkedHashMap_getData:
|
|
body += LoadLocal(scopes_->this_variable);
|
|
body += LoadNativeField(kind, LinkedHashMap::data_offset(),
|
|
Object::dynamic_type(), kArrayCid);
|
|
break;
|
|
case MethodRecognizer::kLinkedHashMap_setData:
|
|
body += LoadLocal(scopes_->this_variable);
|
|
body += LoadLocal(LookupVariable(first_positional_offset));
|
|
body += StoreInstanceField(TokenPosition::kNoSource,
|
|
LinkedHashMap::data_offset());
|
|
body += NullConstant();
|
|
break;
|
|
case MethodRecognizer::kLinkedHashMap_getHashMask:
|
|
body += LoadLocal(scopes_->this_variable);
|
|
body += LoadNativeField(kind, LinkedHashMap::hash_mask_offset(),
|
|
Type::ZoneHandle(Z, Type::SmiType()), kSmiCid);
|
|
break;
|
|
case MethodRecognizer::kLinkedHashMap_setHashMask:
|
|
body += LoadLocal(scopes_->this_variable);
|
|
body += LoadLocal(LookupVariable(first_positional_offset));
|
|
body += StoreInstanceField(TokenPosition::kNoSource,
|
|
LinkedHashMap::hash_mask_offset(),
|
|
kNoStoreBarrier);
|
|
body += NullConstant();
|
|
break;
|
|
case MethodRecognizer::kLinkedHashMap_getUsedData:
|
|
body += LoadLocal(scopes_->this_variable);
|
|
body += LoadNativeField(kind, LinkedHashMap::used_data_offset(),
|
|
Type::ZoneHandle(Z, Type::SmiType()), kSmiCid);
|
|
break;
|
|
case MethodRecognizer::kLinkedHashMap_setUsedData:
|
|
body += LoadLocal(scopes_->this_variable);
|
|
body += LoadLocal(LookupVariable(first_positional_offset));
|
|
body += StoreInstanceField(TokenPosition::kNoSource,
|
|
LinkedHashMap::used_data_offset(),
|
|
kNoStoreBarrier);
|
|
body += NullConstant();
|
|
break;
|
|
case MethodRecognizer::kLinkedHashMap_getDeletedKeys:
|
|
body += LoadLocal(scopes_->this_variable);
|
|
body += LoadNativeField(kind, LinkedHashMap::deleted_keys_offset(),
|
|
Type::ZoneHandle(Z, Type::SmiType()), kSmiCid);
|
|
break;
|
|
case MethodRecognizer::kLinkedHashMap_setDeletedKeys:
|
|
body += LoadLocal(scopes_->this_variable);
|
|
body += LoadLocal(LookupVariable(first_positional_offset));
|
|
body += StoreInstanceField(TokenPosition::kNoSource,
|
|
LinkedHashMap::deleted_keys_offset(),
|
|
kNoStoreBarrier);
|
|
body += NullConstant();
|
|
break;
|
|
default: {
|
|
String& name = String::ZoneHandle(Z, function.native_name());
|
|
if (function.IsGeneric() &&
|
|
Isolate::Current()->reify_generic_functions()) {
|
|
body += LoadLocal(parsed_function_->RawTypeArgumentsVariable());
|
|
body += PushArgument();
|
|
}
|
|
for (intptr_t i = 0; i < function.NumParameters(); ++i) {
|
|
body += LoadLocal(parsed_function_->RawParameterVariable(i));
|
|
body += PushArgument();
|
|
}
|
|
body += NativeCall(&name, &function);
|
|
break;
|
|
}
|
|
}
|
|
return body + Return(TokenPosition::kNoSource);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::BuildImplicitClosureCreation(
|
|
const Function& target) {
|
|
Fragment fragment;
|
|
const Class& closure_class =
|
|
Class::ZoneHandle(Z, I->object_store()->closure_class());
|
|
fragment += AllocateObject(closure_class, target);
|
|
LocalVariable* closure = MakeTemporary();
|
|
|
|
// The function signature can have uninstantiated class type parameters.
|
|
if (!target.HasInstantiatedSignature(kCurrentClass)) {
|
|
fragment += LoadLocal(closure);
|
|
fragment += LoadInstantiatorTypeArguments();
|
|
fragment +=
|
|
StoreInstanceField(TokenPosition::kNoSource,
|
|
Closure::instantiator_type_arguments_offset());
|
|
}
|
|
|
|
// The function signature cannot have uninstantiated function type parameters,
|
|
// because the function cannot be local and have parent generic functions.
|
|
ASSERT(target.HasInstantiatedSignature(kFunctions));
|
|
|
|
// Allocate a context that closes over `this`.
|
|
fragment += AllocateContext(1);
|
|
LocalVariable* context = MakeTemporary();
|
|
|
|
// Store the function and the context in the closure.
|
|
fragment += LoadLocal(closure);
|
|
fragment += Constant(target);
|
|
fragment +=
|
|
StoreInstanceField(TokenPosition::kNoSource, Closure::function_offset());
|
|
|
|
fragment += LoadLocal(closure);
|
|
fragment += LoadLocal(context);
|
|
fragment +=
|
|
StoreInstanceField(TokenPosition::kNoSource, Closure::context_offset());
|
|
|
|
fragment += LoadLocal(closure);
|
|
fragment += Constant(Object::empty_type_arguments());
|
|
fragment += StoreInstanceField(TokenPosition::kNoSource,
|
|
Closure::delayed_type_arguments_offset());
|
|
|
|
// The context is on top of the operand stack. Store `this`. The context
|
|
// doesn't need a parent pointer because it doesn't close over anything
|
|
// else.
|
|
fragment += LoadLocal(scopes_->this_variable);
|
|
fragment +=
|
|
StoreInstanceField(TokenPosition::kNoSource, Context::variable_offset(0));
|
|
|
|
return fragment;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::GuardFieldLength(const Field& field,
|
|
intptr_t deopt_id) {
|
|
return Fragment(new (Z) GuardFieldLengthInstr(Pop(), field, deopt_id));
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::GuardFieldClass(const Field& field,
|
|
intptr_t deopt_id) {
|
|
return Fragment(new (Z) GuardFieldClassInstr(Pop(), field, deopt_id));
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::CheckVariableTypeInCheckedMode(
|
|
const AbstractType& dst_type,
|
|
const String& name_symbol) {
|
|
if (I->type_checks()) {
|
|
return CheckAssignable(dst_type, name_symbol);
|
|
}
|
|
return Fragment();
|
|
}
|
|
|
|
bool FlowGraphBuilder::NeedsDebugStepCheck(const Function& function,
|
|
TokenPosition position) {
|
|
return position.IsDebugPause() && !function.is_native() &&
|
|
function.is_debuggable();
|
|
}
|
|
|
|
bool FlowGraphBuilder::NeedsDebugStepCheck(Value* value,
|
|
TokenPosition position) {
|
|
if (!position.IsDebugPause()) {
|
|
return false;
|
|
}
|
|
Definition* definition = value->definition();
|
|
if (definition->IsConstant() || definition->IsLoadStaticField()) {
|
|
return true;
|
|
}
|
|
if (definition->IsAllocateObject()) {
|
|
return !definition->AsAllocateObject()->closure_function().IsNull();
|
|
}
|
|
return definition->IsLoadLocal() &&
|
|
!definition->AsLoadLocal()->local().IsInternal();
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::DebugStepCheck(TokenPosition position) {
|
|
return Fragment(new (Z) DebugStepCheckInstr(
|
|
position, RawPcDescriptors::kRuntimeCall, GetNextDeoptId()));
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::EvaluateAssertion() {
|
|
const Class& klass =
|
|
Class::ZoneHandle(Z, Library::LookupCoreClass(Symbols::AssertionError()));
|
|
ASSERT(!klass.IsNull());
|
|
const Function& target = Function::ZoneHandle(
|
|
Z, klass.LookupStaticFunctionAllowPrivate(Symbols::EvaluateAssertion()));
|
|
ASSERT(!target.IsNull());
|
|
return StaticCall(TokenPosition::kNoSource, target, /* argument_count = */ 1,
|
|
ICData::kStatic);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::CheckBoolean() {
|
|
Fragment instructions;
|
|
if (I->strong() || I->type_checks() || I->asserts()) {
|
|
LocalVariable* top_of_stack = MakeTemporary();
|
|
instructions += LoadLocal(top_of_stack);
|
|
instructions += AssertBool();
|
|
instructions += Drop();
|
|
}
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::CheckAssignable(const AbstractType& dst_type,
|
|
const String& dst_name,
|
|
AssertAssignableInstr::Kind kind) {
|
|
Fragment instructions;
|
|
if (dst_type.IsMalformed()) {
|
|
return ThrowTypeError();
|
|
}
|
|
if (!dst_type.IsDynamicType() && !dst_type.IsObjectType() &&
|
|
!dst_type.IsVoidType()) {
|
|
LocalVariable* top_of_stack = MakeTemporary();
|
|
instructions += LoadLocal(top_of_stack);
|
|
instructions +=
|
|
AssertAssignable(TokenPosition::kNoSource, dst_type, dst_name, kind);
|
|
instructions += Drop();
|
|
}
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::AssertBool() {
|
|
Value* value = Pop();
|
|
AssertBooleanInstr* instr = new (Z)
|
|
AssertBooleanInstr(TokenPosition::kNoSource, value, GetNextDeoptId());
|
|
Push(instr);
|
|
return Fragment(instr);
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::AssertAssignable(TokenPosition position,
|
|
const AbstractType& dst_type,
|
|
const String& dst_name,
|
|
AssertAssignableInstr::Kind kind) {
|
|
Fragment instructions;
|
|
Value* value = Pop();
|
|
|
|
if (!dst_type.IsInstantiated(kCurrentClass)) {
|
|
instructions += LoadInstantiatorTypeArguments();
|
|
} else {
|
|
instructions += NullConstant();
|
|
}
|
|
Value* instantiator_type_args = Pop();
|
|
|
|
if (!dst_type.IsInstantiated(kFunctions)) {
|
|
instructions += LoadFunctionTypeArguments();
|
|
} else {
|
|
instructions += NullConstant();
|
|
}
|
|
Value* function_type_args = Pop();
|
|
|
|
AssertAssignableInstr* instr = new (Z) AssertAssignableInstr(
|
|
position, value, instantiator_type_args, function_type_args, dst_type,
|
|
dst_name, GetNextDeoptId(), kind);
|
|
Push(instr);
|
|
|
|
instructions += Fragment(instr);
|
|
|
|
return instructions;
|
|
}
|
|
|
|
Fragment FlowGraphBuilder::AssertSubtype(TokenPosition position,
|
|
const AbstractType& sub_type,
|
|
const AbstractType& super_type,
|
|
const String& dst_name) {
|
|
Fragment instructions;
|
|
|
|
instructions += LoadInstantiatorTypeArguments();
|
|
Value* instantiator_type_args = Pop();
|
|
instructions += LoadFunctionTypeArguments();
|
|
Value* function_type_args = Pop();
|
|
|
|
AssertSubtypeInstr* instr = new (Z)
|
|
AssertSubtypeInstr(position, instantiator_type_args, function_type_args,
|
|
sub_type, super_type, dst_name, GetNextDeoptId());
|
|
instructions += Fragment(instr);
|
|
|
|
return instructions;
|
|
}
|
|
|
|
BlockEntryInstr* FlowGraphBuilder::BuildPrologue(TargetEntryInstr* normal_entry,
|
|
PrologueInfo* prologue_info) {
|
|
const bool compiling_for_osr = IsCompiledForOsr();
|
|
|
|
kernel::PrologueBuilder prologue_builder(
|
|
parsed_function_, last_used_block_id_, compiling_for_osr, IsInlining());
|
|
BlockEntryInstr* instruction_cursor =
|
|
prologue_builder.BuildPrologue(normal_entry, prologue_info);
|
|
|
|
last_used_block_id_ = prologue_builder.last_used_block_id();
|
|
|
|
return instruction_cursor;
|
|
}
|
|
|
|
FlowGraph* FlowGraphBuilder::BuildGraphOfMethodExtractor(
|
|
const Function& method) {
|
|
// A method extractor is the implicit getter for a method.
|
|
const Function& function =
|
|
Function::ZoneHandle(Z, method.extracted_method_closure());
|
|
|
|
TargetEntryInstr* normal_entry = BuildTargetEntry();
|
|
graph_entry_ = new (Z)
|
|
GraphEntryInstr(*parsed_function_, normal_entry, Compiler::kNoOSRDeoptId);
|
|
Fragment body(normal_entry);
|
|
body += CheckStackOverflowInPrologue(method.token_pos());
|
|
body += BuildImplicitClosureCreation(function);
|
|
body += Return(TokenPosition::kNoSource);
|
|
|
|
// There is no prologue code for a method extractor.
|
|
PrologueInfo prologue_info(-1, -1);
|
|
return new (Z) FlowGraph(*parsed_function_, graph_entry_, last_used_block_id_,
|
|
prologue_info);
|
|
}
|
|
|
|
FlowGraph* FlowGraphBuilder::BuildGraphOfNoSuchMethodDispatcher(
|
|
const Function& function) {
|
|
// This function is specialized for a receiver class, a method name, and
|
|
// the arguments descriptor at a call site.
|
|
|
|
TargetEntryInstr* normal_entry = BuildTargetEntry();
|
|
PrologueInfo prologue_info(-1, -1);
|
|
BlockEntryInstr* instruction_cursor =
|
|
BuildPrologue(normal_entry, &prologue_info);
|
|
graph_entry_ = new (Z)
|
|
GraphEntryInstr(*parsed_function_, normal_entry, Compiler::kNoOSRDeoptId);
|
|
|
|
// The backend will expect an array of default values for all the named
|
|
// parameters, even if they are all known to be passed at the call site
|
|
// because the call site matches the arguments descriptor. Use null for
|
|
// the default values.
|
|
const Array& descriptor_array =
|
|
Array::ZoneHandle(Z, function.saved_args_desc());
|
|
ArgumentsDescriptor descriptor(descriptor_array);
|
|
ZoneGrowableArray<const Instance*>* default_values =
|
|
new ZoneGrowableArray<const Instance*>(Z, descriptor.NamedCount());
|
|
for (intptr_t i = 0; i < descriptor.NamedCount(); ++i) {
|
|
default_values->Add(&Object::null_instance());
|
|
}
|
|
parsed_function_->set_default_parameter_values(default_values);
|
|
|
|
Fragment body(instruction_cursor);
|
|
body += CheckStackOverflowInPrologue(function.token_pos());
|
|
|
|
// The receiver is the first argument to noSuchMethod, and it is the first
|
|
// argument passed to the dispatcher function.
|
|
LocalScope* scope = parsed_function_->node_sequence()->scope();
|
|
body += LoadLocal(scope->VariableAt(0));
|
|
body += PushArgument();
|
|
|
|
// The second argument to noSuchMethod is an invocation mirror. Push the
|
|
// arguments for allocating the invocation mirror. First, the name.
|
|
body += Constant(String::ZoneHandle(Z, function.name()));
|
|
body += PushArgument();
|
|
|
|
// Second, the arguments descriptor.
|
|
body += Constant(descriptor_array);
|
|
body += PushArgument();
|
|
|
|
// Third, an array containing the original arguments. Create it and fill
|
|
// it in.
|
|
const intptr_t receiver_index = descriptor.TypeArgsLen() > 0 ? 1 : 0;
|
|
body += Constant(TypeArguments::ZoneHandle(Z, TypeArguments::null()));
|
|
body += IntConstant(receiver_index + descriptor.Count());
|
|
body += CreateArray();
|
|
LocalVariable* array = MakeTemporary();
|
|
if (receiver_index > 0) {
|
|
LocalVariable* type_args = parsed_function_->function_type_arguments();
|
|
ASSERT(type_args != NULL);
|
|
body += LoadLocal(array);
|
|
body += IntConstant(0);
|
|
body += LoadLocal(type_args);
|
|
body += StoreIndexed(kArrayCid);
|
|
body += Drop();
|
|
}
|
|
for (intptr_t i = 0; i < descriptor.PositionalCount(); ++i) {
|
|
body += LoadLocal(array);
|
|
body += IntConstant(receiver_index + i);
|
|
body += LoadLocal(scope->VariableAt(i));
|
|
body += StoreIndexed(kArrayCid);
|
|
body += Drop();
|
|
}
|
|
String& name = String::Handle(Z);
|
|
for (intptr_t i = 0; i < descriptor.NamedCount(); ++i) {
|
|
intptr_t parameter_index = descriptor.PositionalCount() + i;
|
|
name = descriptor.NameAt(i);
|
|
name = Symbols::New(H.thread(), name);
|
|
body += LoadLocal(array);
|
|
body += IntConstant(receiver_index + descriptor.PositionAt(i));
|
|
body += LoadLocal(scope->VariableAt(parameter_index));
|
|
body += StoreIndexed(kArrayCid);
|
|
body += Drop();
|
|
}
|
|
body += PushArgument();
|
|
|
|
// Fourth, false indicating this is not a super NoSuchMethod.
|
|
body += Constant(Bool::False());
|
|
body += PushArgument();
|
|
|
|
const Class& mirror_class =
|
|
Class::Handle(Z, Library::LookupCoreClass(Symbols::InvocationMirror()));
|
|
ASSERT(!mirror_class.IsNull());
|
|
const Function& allocation_function = Function::ZoneHandle(
|
|
Z, mirror_class.LookupStaticFunction(
|
|
Library::PrivateCoreLibName(Symbols::AllocateInvocationMirror())));
|
|
ASSERT(!allocation_function.IsNull());
|
|
body += StaticCall(TokenPosition::kMinSource, allocation_function,
|
|
/* argument_count = */ 4, ICData::kStatic);
|
|
body += PushArgument(); // For the call to noSuchMethod.
|
|
|
|
const int kTypeArgsLen = 0;
|
|
ArgumentsDescriptor two_arguments(
|
|
Array::Handle(Z, ArgumentsDescriptor::New(kTypeArgsLen, 2)));
|
|
Function& no_such_method =
|
|
Function::ZoneHandle(Z, Resolver::ResolveDynamicForReceiverClass(
|
|
Class::Handle(Z, function.Owner()),
|
|
Symbols::NoSuchMethod(), two_arguments));
|
|
if (no_such_method.IsNull()) {
|
|
// If noSuchMethod is not found on the receiver class, call
|
|
// Object.noSuchMethod.
|
|
no_such_method = Resolver::ResolveDynamicForReceiverClass(
|
|
Class::Handle(Z, I->object_store()->object_class()),
|
|
Symbols::NoSuchMethod(), two_arguments);
|
|
}
|
|
body += StaticCall(TokenPosition::kMinSource, no_such_method,
|
|
/* argument_count = */ 2, ICData::kNSMDispatch);
|
|
body += Return(TokenPosition::kNoSource);
|
|
|
|
return new (Z) FlowGraph(*parsed_function_, graph_entry_, last_used_block_id_,
|
|
prologue_info);
|
|
}
|
|
|
|
FlowGraph* FlowGraphBuilder::BuildGraphOfInvokeFieldDispatcher(
|
|
const Function& function) {
|
|
// Find the name of the field we should dispatch to.
|
|
const Class& owner = Class::Handle(Z, function.Owner());
|
|
ASSERT(!owner.IsNull());
|
|
const String& field_name = String::Handle(Z, function.name());
|
|
const String& getter_name = String::ZoneHandle(
|
|
Z, Symbols::New(H.thread(),
|
|
String::Handle(Z, Field::GetterSymbol(field_name))));
|
|
|
|
// Determine if this is `class Closure { get call => this; }`
|
|
const Class& closure_class =
|
|
Class::Handle(Z, I->object_store()->closure_class());
|
|
const bool is_closure_call = (owner.raw() == closure_class.raw()) &&
|
|
field_name.Equals(Symbols::Call());
|
|
|
|
// Set default parameters & construct argument names array.
|
|
//
|
|
// The backend will expect an array of default values for all the named
|
|
// parameters, even if they are all known to be passed at the call site
|
|
// because the call site matches the arguments descriptor. Use null for
|
|
// the default values.
|
|
const Array& descriptor_array =
|
|
Array::ZoneHandle(Z, function.saved_args_desc());
|
|
ArgumentsDescriptor descriptor(descriptor_array);
|
|
const Array& argument_names =
|
|
Array::ZoneHandle(Z, Array::New(descriptor.NamedCount(), Heap::kOld));
|
|
ZoneGrowableArray<const Instance*>* default_values =
|
|
new ZoneGrowableArray<const Instance*>(Z, descriptor.NamedCount());
|
|
String& string_handle = String::Handle(Z);
|
|
for (intptr_t i = 0; i < descriptor.NamedCount(); ++i) {
|
|
default_values->Add(&Object::null_instance());
|
|
string_handle = descriptor.NameAt(i);
|
|
argument_names.SetAt(i, string_handle);
|
|
}
|
|
parsed_function_->set_default_parameter_values(default_values);
|
|
|
|
TargetEntryInstr* normal_entry = BuildTargetEntry();
|
|
PrologueInfo prologue_info(-1, -1);
|
|
BlockEntryInstr* instruction_cursor =
|
|
BuildPrologue(normal_entry, &prologue_info);
|
|
graph_entry_ = new (Z)
|
|
GraphEntryInstr(*parsed_function_, normal_entry, Compiler::kNoOSRDeoptId);
|
|
|
|
Fragment body(instruction_cursor);
|
|
body += CheckStackOverflowInPrologue(function.token_pos());
|
|
|
|
LocalScope* scope = parsed_function_->node_sequence()->scope();
|
|
|
|
if (descriptor.TypeArgsLen() > 0) {
|
|
LocalVariable* type_args = parsed_function_->function_type_arguments();
|
|
ASSERT(type_args != NULL);
|
|
body += LoadLocal(type_args);
|
|
body += PushArgument();
|
|
}
|
|
|
|
LocalVariable* closure = NULL;
|
|
if (is_closure_call) {
|
|
closure = scope->VariableAt(0);
|
|
|
|
// The closure itself is the first argument.
|
|
body += LoadLocal(closure);
|
|
} else {
|
|
// Invoke the getter to get the field value.
|
|
body += LoadLocal(scope->VariableAt(0));
|
|
body += PushArgument();
|
|
const intptr_t kTypeArgsLen = 0;
|
|
const intptr_t kNumArgsChecked = 1;
|
|
body += InstanceCall(TokenPosition::kMinSource, getter_name, Token::kGET,
|
|
kTypeArgsLen, 1, Array::null_array(), kNumArgsChecked,
|
|
Function::null_function());
|
|
}
|
|
|
|
body += PushArgument();
|
|
|
|
// Push all arguments onto the stack.
|
|
intptr_t pos = 1;
|
|
for (; pos < descriptor.Count(); pos++) {
|
|
body += LoadLocal(scope->VariableAt(pos));
|
|
body += PushArgument();
|
|
}
|
|
|
|
if (is_closure_call) {
|
|
// Lookup the function in the closure.
|
|
body += LoadLocal(closure);
|
|
body += LoadField(Closure::function_offset());
|
|
|
|
body += ClosureCall(descriptor.TypeArgsLen(), descriptor.Count(),
|
|
argument_names);
|
|
} else {
|
|
const intptr_t kNumArgsChecked = 1;
|
|
body += InstanceCall(TokenPosition::kMinSource, Symbols::Call(),
|
|
Token::kILLEGAL, descriptor.TypeArgsLen(),
|
|
descriptor.Count(), argument_names, kNumArgsChecked,
|
|
Function::null_function());
|
|
}
|
|
|
|
body += Return(TokenPosition::kNoSource);
|
|
|
|
return new (Z) FlowGraph(*parsed_function_, graph_entry_, last_used_block_id_,
|
|
prologue_info);
|
|
}
|
|
|
|
TargetEntryInstr* BaseFlowGraphBuilder::BuildTargetEntry() {
|
|
return new (Z)
|
|
TargetEntryInstr(AllocateBlockId(), CurrentTryIndex(), GetNextDeoptId());
|
|
}
|
|
|
|
JoinEntryInstr* BaseFlowGraphBuilder::BuildJoinEntry(intptr_t try_index) {
|
|
return new (Z) JoinEntryInstr(AllocateBlockId(), try_index, GetNextDeoptId());
|
|
}
|
|
|
|
JoinEntryInstr* BaseFlowGraphBuilder::BuildJoinEntry() {
|
|
return new (Z)
|
|
JoinEntryInstr(AllocateBlockId(), CurrentTryIndex(), GetNextDeoptId());
|
|
}
|
|
|
|
ArgumentArray BaseFlowGraphBuilder::GetArguments(int count) {
|
|
ArgumentArray arguments =
|
|
new (Z) ZoneGrowableArray<PushArgumentInstr*>(Z, count);
|
|
arguments->SetLength(count);
|
|
for (intptr_t i = count - 1; i >= 0; --i) {
|
|
ASSERT(stack_->definition()->IsPushArgument());
|
|
ASSERT(!stack_->definition()->HasSSATemp());
|
|
arguments->data()[i] = stack_->definition()->AsPushArgument();
|
|
Drop();
|
|
}
|
|
pending_argument_count_ -= count;
|
|
ASSERT(pending_argument_count_ >= 0);
|
|
return arguments;
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::SmiRelationalOp(Token::Kind kind) {
|
|
Value* right = Pop();
|
|
Value* left = Pop();
|
|
RelationalOpInstr* instr = new (Z) RelationalOpInstr(
|
|
TokenPosition::kNoSource, kind, left, right, kSmiCid, GetNextDeoptId());
|
|
Push(instr);
|
|
return Fragment(instr);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::SmiBinaryOp(Token::Kind kind,
|
|
bool is_truncating) {
|
|
Value* right = Pop();
|
|
Value* left = Pop();
|
|
BinarySmiOpInstr* instr =
|
|
new (Z) BinarySmiOpInstr(kind, left, right, GetNextDeoptId());
|
|
if (is_truncating) {
|
|
instr->mark_truncating();
|
|
}
|
|
Push(instr);
|
|
return Fragment(instr);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::LoadFpRelativeSlot(intptr_t offset) {
|
|
LoadIndexedUnsafeInstr* instr = new (Z) LoadIndexedUnsafeInstr(Pop(), offset);
|
|
Push(instr);
|
|
return Fragment(instr);
|
|
}
|
|
|
|
Fragment BaseFlowGraphBuilder::StoreFpRelativeSlot(intptr_t offset) {
|
|
Value* value = Pop();
|
|
Value* index = Pop();
|
|
StoreIndexedUnsafeInstr* instr =
|
|
new (Z) StoreIndexedUnsafeInstr(index, value, offset);
|
|
Push(instr);
|
|
return Fragment(instr);
|
|
}
|
|
|
|
JoinEntryInstr* BaseFlowGraphBuilder::BuildThrowNoSuchMethod() {
|
|
JoinEntryInstr* nsm = BuildJoinEntry();
|
|
|
|
Fragment failing(nsm);
|
|
const Code& nsm_handler =
|
|
Code::ZoneHandle(StubCode::CallClosureNoSuchMethod_entry()->code());
|
|
failing += LoadArgDescriptor();
|
|
failing += TailCall(nsm_handler);
|
|
|
|
return nsm;
|
|
}
|
|
|
|
RawObject* EvaluateMetadata(const Field& metadata_field) {
|
|
LongJumpScope jump;
|
|
if (setjmp(*jump.Set()) == 0) {
|
|
Thread* thread = Thread::Current();
|
|
Zone* zone_ = thread->zone();
|
|
TranslationHelper helper(thread);
|
|
Script& script = Script::Handle(Z, metadata_field.Script());
|
|
helper.InitFromScript(script);
|
|
|
|
const Class& owner_class = Class::Handle(Z, metadata_field.Owner());
|
|
ActiveClass active_class;
|
|
ActiveClassScope active_class_scope(&active_class, &owner_class);
|
|
|
|
StreamingFlowGraphBuilder streaming_flow_graph_builder(
|
|
&helper, Script::Handle(Z, metadata_field.Script()), Z,
|
|
TypedData::Handle(Z, metadata_field.KernelData()),
|
|
metadata_field.KernelDataProgramOffset(), &active_class);
|
|
return streaming_flow_graph_builder.EvaluateMetadata(
|
|
metadata_field.kernel_offset());
|
|
} else {
|
|
Thread* thread = Thread::Current();
|
|
Error& error = Error::Handle();
|
|
error = thread->sticky_error();
|
|
thread->clear_sticky_error();
|
|
return error.raw();
|
|
}
|
|
}
|
|
|
|
RawObject* BuildParameterDescriptor(const Function& function) {
|
|
LongJumpScope jump;
|
|
if (setjmp(*jump.Set()) == 0) {
|
|
Thread* thread = Thread::Current();
|
|
Zone* zone_ = thread->zone();
|
|
TranslationHelper helper(thread);
|
|
Script& script = Script::Handle(Z, function.script());
|
|
helper.InitFromScript(script);
|
|
|
|
StreamingFlowGraphBuilder streaming_flow_graph_builder(
|
|
&helper, Script::Handle(Z, function.script()), Z,
|
|
TypedData::Handle(Z, function.KernelData()),
|
|
function.KernelDataProgramOffset(), /* active_class = */ NULL);
|
|
return streaming_flow_graph_builder.BuildParameterDescriptor(
|
|
function.kernel_offset());
|
|
} else {
|
|
Thread* thread = Thread::Current();
|
|
Error& error = Error::Handle();
|
|
error = thread->sticky_error();
|
|
thread->clear_sticky_error();
|
|
return error.raw();
|
|
}
|
|
}
|
|
|
|
static int LowestFirst(const intptr_t* a, const intptr_t* b) {
|
|
return *a - *b;
|
|
}
|
|
|
|
/**
|
|
* If index exists as sublist in list, sort the sublist from lowest to highest,
|
|
* then copy it, as Smis and without duplicates,
|
|
* to a new Array in Heap::kOld which is returned.
|
|
* Note that the source list is both sorted and de-duplicated as well, but will
|
|
* possibly contain duplicate and unsorted data at the end.
|
|
* Otherwise (when sublist doesn't exist in list) return new empty array.
|
|
*/
|
|
static RawArray* AsSortedDuplicateFreeArray(GrowableArray<intptr_t>* source) {
|
|
intptr_t size = source->length();
|
|
if (size == 0) {
|
|
return Object::empty_array().raw();
|
|
}
|
|
|
|
source->Sort(LowestFirst);
|
|
|
|
intptr_t last = 0;
|
|
for (intptr_t current = 1; current < size; ++current) {
|
|
if (source->At(last) != source->At(current)) {
|
|
(*source)[++last] = source->At(current);
|
|
}
|
|
}
|
|
Array& array_object = Array::Handle();
|
|
array_object = Array::New(last + 1, Heap::kOld);
|
|
Smi& smi_value = Smi::Handle();
|
|
for (intptr_t i = 0; i <= last; ++i) {
|
|
smi_value = Smi::New(source->At(i));
|
|
array_object.SetAt(i, smi_value);
|
|
}
|
|
return array_object.raw();
|
|
}
|
|
|
|
static void ProcessTokenPositionsEntry(
|
|
const TypedData& kernel_data,
|
|
const Script& script,
|
|
const Script& entry_script,
|
|
intptr_t kernel_offset,
|
|
intptr_t data_kernel_offset,
|
|
Zone* zone_,
|
|
TranslationHelper* helper,
|
|
GrowableArray<intptr_t>* token_positions,
|
|
GrowableArray<intptr_t>* yield_positions) {
|
|
if (kernel_data.IsNull()) {
|
|
return;
|
|
}
|
|
|
|
StreamingFlowGraphBuilder streaming_flow_graph_builder(
|
|
helper, script, zone_, kernel_data, data_kernel_offset,
|
|
/* active_class = */ NULL);
|
|
streaming_flow_graph_builder.CollectTokenPositionsFor(
|
|
script.kernel_script_index(), entry_script.kernel_script_index(),
|
|
kernel_offset, token_positions, yield_positions);
|
|
}
|
|
|
|
void CollectTokenPositionsFor(const Script& interesting_script) {
|
|
Thread* thread = Thread::Current();
|
|
Zone* zone_ = thread->zone();
|
|
TranslationHelper helper(thread);
|
|
helper.InitFromScript(interesting_script);
|
|
|
|
GrowableArray<intptr_t> token_positions(10);
|
|
GrowableArray<intptr_t> yield_positions(1);
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
const GrowableObjectArray& libs =
|
|
GrowableObjectArray::Handle(Z, isolate->object_store()->libraries());
|
|
Library& lib = Library::Handle(Z);
|
|
Object& entry = Object::Handle(Z);
|
|
Script& entry_script = Script::Handle(Z);
|
|
TypedData& data = TypedData::Handle(Z);
|
|
|
|
auto& temp_array = Array::Handle(Z);
|
|
auto& temp_field = Field::Handle(Z);
|
|
auto& temp_function = Function::Handle(Z);
|
|
for (intptr_t i = 0; i < libs.Length(); i++) {
|
|
lib ^= libs.At(i);
|
|
DictionaryIterator it(lib);
|
|
while (it.HasNext()) {
|
|
entry = it.GetNext();
|
|
data = TypedData::null();
|
|
if (entry.IsClass()) {
|
|
const Class& klass = Class::Cast(entry);
|
|
if (klass.script() == interesting_script.raw()) {
|
|
token_positions.Add(klass.token_pos().value());
|
|
}
|
|
if (klass.is_finalized()) {
|
|
temp_array = klass.fields();
|
|
for (intptr_t i = 0; i < temp_array.Length(); ++i) {
|
|
temp_field ^= temp_array.At(i);
|
|
if (temp_field.kernel_offset() <= 0) {
|
|
// Skip artificially injected fields.
|
|
continue;
|
|
}
|
|
entry_script = temp_field.Script();
|
|
if (entry_script.raw() != interesting_script.raw()) {
|
|
continue;
|
|
}
|
|
data = temp_field.KernelData();
|
|
ProcessTokenPositionsEntry(data, interesting_script, entry_script,
|
|
temp_field.kernel_offset(),
|
|
temp_field.KernelDataProgramOffset(), Z,
|
|
&helper, &token_positions,
|
|
&yield_positions);
|
|
}
|
|
temp_array = klass.functions();
|
|
for (intptr_t i = 0; i < temp_array.Length(); ++i) {
|
|
temp_function ^= temp_array.At(i);
|
|
entry_script = temp_function.script();
|
|
if (entry_script.raw() != interesting_script.raw()) {
|
|
continue;
|
|
}
|
|
data = temp_function.KernelData();
|
|
ProcessTokenPositionsEntry(data, interesting_script, entry_script,
|
|
temp_function.kernel_offset(),
|
|
temp_function.KernelDataProgramOffset(),
|
|
Z, &helper, &token_positions,
|
|
&yield_positions);
|
|
}
|
|
} else {
|
|
// Class isn't finalized yet: read the data attached to it.
|
|
ASSERT(klass.kernel_offset() > 0);
|
|
data = lib.kernel_data();
|
|
ASSERT(!data.IsNull());
|
|
const intptr_t library_kernel_offset = lib.kernel_offset();
|
|
ASSERT(library_kernel_offset > 0);
|
|
const intptr_t class_offset = klass.kernel_offset();
|
|
|
|
entry_script = klass.script();
|
|
if (entry_script.raw() != interesting_script.raw()) {
|
|
continue;
|
|
}
|
|
ProcessTokenPositionsEntry(data, interesting_script, entry_script,
|
|
class_offset, library_kernel_offset, Z,
|
|
&helper, &token_positions,
|
|
&yield_positions);
|
|
}
|
|
} else if (entry.IsFunction()) {
|
|
temp_function ^= entry.raw();
|
|
entry_script = temp_function.script();
|
|
if (entry_script.raw() != interesting_script.raw()) {
|
|
continue;
|
|
}
|
|
data = temp_function.KernelData();
|
|
ProcessTokenPositionsEntry(data, interesting_script, entry_script,
|
|
temp_function.kernel_offset(),
|
|
temp_function.KernelDataProgramOffset(), Z,
|
|
&helper, &token_positions, &yield_positions);
|
|
} else if (entry.IsField()) {
|
|
const Field& field = Field::Cast(entry);
|
|
if (field.kernel_offset() <= 0) {
|
|
// Skip artificially injected fields.
|
|
continue;
|
|
}
|
|
entry_script = field.Script();
|
|
if (entry_script.raw() != interesting_script.raw()) {
|
|
continue;
|
|
}
|
|
data = field.KernelData();
|
|
ProcessTokenPositionsEntry(data, interesting_script, entry_script,
|
|
field.kernel_offset(),
|
|
field.KernelDataProgramOffset(), Z, &helper,
|
|
&token_positions, &yield_positions);
|
|
}
|
|
}
|
|
}
|
|
|
|
Script& script = Script::Handle(Z, interesting_script.raw());
|
|
Array& array_object = Array::Handle(Z);
|
|
array_object = AsSortedDuplicateFreeArray(&token_positions);
|
|
script.set_debug_positions(array_object);
|
|
array_object = AsSortedDuplicateFreeArray(&yield_positions);
|
|
script.set_yield_positions(array_object);
|
|
}
|
|
|
|
} // namespace kernel
|
|
} // namespace dart
|
|
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|