5b7968346d
This adds a third reference to Field declaration, so that we know have three references, one for each use case: 1) fieldReference used for initialization; in FieldInitializer and as the key in the InstanceConstant.fieldValues map. 2) getterReference used for reading; in InstanceGet, StaticGet and SuperPropertyGet 3) setterReference used for writing; in InstanceSet, StaticSet and SuperPropertySet TEST=existing Change-Id: I223f130e808e7f19a831c1fe5e3a4725d1bcdc3b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/203770 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Jens Johansen <jensj@google.com>
493 lines
20 KiB
C++
493 lines
20 KiB
C++
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "vm/compiler/frontend/constant_reader.h"
|
|
|
|
#include "vm/object_store.h"
|
|
|
|
namespace dart {
|
|
namespace kernel {
|
|
|
|
#define Z (zone_)
|
|
#define H (translation_helper_)
|
|
|
|
// Note: If changing how the constants are saved in the binary (and thus how
|
|
// they are read here) be aware that there's also some reading going on in
|
|
// KernelLoader::ReadVMAnnotations which then also has to be updated!
|
|
|
|
ConstantReader::ConstantReader(KernelReaderHelper* helper,
|
|
ActiveClass* active_class)
|
|
: helper_(helper),
|
|
zone_(helper->zone_),
|
|
translation_helper_(helper->translation_helper_),
|
|
active_class_(active_class),
|
|
script_(helper->script()),
|
|
result_(Object::Handle(zone_)) {}
|
|
|
|
InstancePtr ConstantReader::ReadConstantInitializer() {
|
|
Tag tag = helper_->ReadTag(); // read tag.
|
|
switch (tag) {
|
|
case kSomething:
|
|
return ReadConstantExpression();
|
|
default:
|
|
H.ReportError(script_, TokenPosition::kNoSource,
|
|
"Not a constant expression: unexpected kernel tag %s (%d)",
|
|
Reader::TagName(tag), tag);
|
|
}
|
|
return Instance::RawCast(result_.ptr());
|
|
}
|
|
|
|
InstancePtr ConstantReader::ReadConstantExpression() {
|
|
Tag tag = helper_->ReadTag(); // read tag.
|
|
switch (tag) {
|
|
case kConstantExpression:
|
|
helper_->ReadPosition();
|
|
helper_->SkipDartType();
|
|
result_ = ReadConstant(helper_->ReadUInt());
|
|
break;
|
|
case kInvalidExpression: {
|
|
helper_->ReadPosition(); // Skip position.
|
|
const String& message = H.DartString(helper_->ReadStringReference());
|
|
// Invalid expression message has pointer to the source code, no need to
|
|
// report it twice.
|
|
H.ReportError(helper_->script(), TokenPosition::kNoSource, "%s",
|
|
message.ToCString());
|
|
break;
|
|
}
|
|
default:
|
|
H.ReportError(script_, TokenPosition::kNoSource,
|
|
"Not a constant expression: unexpected kernel tag %s (%d)",
|
|
Reader::TagName(tag), tag);
|
|
}
|
|
return Instance::RawCast(result_.ptr());
|
|
}
|
|
|
|
ObjectPtr ConstantReader::ReadAnnotations() {
|
|
intptr_t list_length = helper_->ReadListLength(); // read list length.
|
|
const auto& metadata_values =
|
|
Array::Handle(Z, ImmutableArray::New(list_length, H.allocation_space()));
|
|
Instance& value = Instance::Handle(Z);
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
// This will read the expression.
|
|
value = ReadConstantExpression();
|
|
metadata_values.SetAt(i, value);
|
|
}
|
|
return H.Canonicalize(metadata_values);
|
|
}
|
|
|
|
InstancePtr ConstantReader::ReadConstant(intptr_t constant_index) {
|
|
ASSERT(!H.constants().IsNull());
|
|
ASSERT(!H.constants_table().IsNull()); // raw bytes
|
|
|
|
// For kernel-level cache (in contrast with script-level caching),
|
|
// we need to access the raw constants array inside the shared
|
|
// KernelProgramInfo directly, so that all scripts will see the
|
|
// results after new insertions. These accesses at kernel-level
|
|
// must be locked since mutator and background compiler can
|
|
// access the array at the same time.
|
|
{
|
|
SafepointMutexLocker ml(
|
|
H.thread()->isolate_group()->kernel_constants_mutex());
|
|
const auto& constants_array = Array::Handle(Z, H.info().constants());
|
|
ASSERT(constant_index < constants_array.Length());
|
|
result_ = constants_array.At(constant_index);
|
|
}
|
|
|
|
// On miss, evaluate, and insert value.
|
|
if (result_.ptr() == Object::sentinel().ptr()) {
|
|
LeaveCompilerScope cs(H.thread());
|
|
result_ = ReadConstantInternal(constant_index);
|
|
SafepointMutexLocker ml(
|
|
H.thread()->isolate_group()->kernel_constants_mutex());
|
|
const auto& constants_array = Array::Handle(Z, H.info().constants());
|
|
ASSERT(constant_index < constants_array.Length());
|
|
constants_array.SetAt(constant_index, result_);
|
|
}
|
|
return Instance::RawCast(result_.ptr());
|
|
}
|
|
|
|
bool ConstantReader::IsInstanceConstant(intptr_t constant_index,
|
|
const Class& clazz) {
|
|
// Get reader directly into raw bytes of constant table/constant mapping.
|
|
KernelReaderHelper reader(Z, &H, script_, H.constants_table(), 0);
|
|
NavigateToIndex(&reader, constant_index);
|
|
|
|
// Peek for an instance of the given clazz.
|
|
if (reader.ReadByte() == kInstanceConstant) {
|
|
const NameIndex index = reader.ReadCanonicalNameReference();
|
|
return H.LookupClassByKernelClass(index) == clazz.ptr();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
intptr_t ConstantReader::NumConstants() {
|
|
ASSERT(!H.constants_table().IsNull());
|
|
KernelReaderHelper reader(Z, &H, script_, H.constants_table(), 0);
|
|
return NumConstants(&reader);
|
|
}
|
|
|
|
intptr_t ConstantReader::NumConstants(KernelReaderHelper* reader) {
|
|
// Get reader directly into raw bytes of constant table/constant mapping.
|
|
// Get the length of the constants (at the end of the mapping).
|
|
reader->SetOffset(reader->ReaderSize() - 4);
|
|
return reader->ReadUInt32();
|
|
}
|
|
|
|
intptr_t ConstantReader::NavigateToIndex(KernelReaderHelper* reader,
|
|
intptr_t constant_index) {
|
|
const intptr_t num_constants = NumConstants(reader);
|
|
|
|
// Get the binary offset of the constant at the wanted index.
|
|
reader->SetOffset(reader->ReaderSize() - 4 - (num_constants * 4) +
|
|
(constant_index * 4));
|
|
const intptr_t constant_offset = reader->ReadUInt32();
|
|
|
|
reader->SetOffset(constant_offset);
|
|
|
|
return constant_offset;
|
|
}
|
|
|
|
InstancePtr ConstantReader::ReadConstantInternal(intptr_t constant_index) {
|
|
// Get reader directly into raw bytes of constant table/constant mapping.
|
|
bool null_safety = H.thread()->isolate_group()->null_safety();
|
|
KernelReaderHelper reader(Z, &H, script_, H.constants_table(), 0);
|
|
const intptr_t constant_offset = NavigateToIndex(&reader, constant_index);
|
|
|
|
// No function types returned as part of any types built should reference
|
|
// free parent type args, ensured by clearing the enclosing function type.
|
|
ActiveEnclosingFunctionScope scope(active_class_, nullptr);
|
|
// Construct constant from raw bytes.
|
|
Instance& instance = Instance::Handle(Z);
|
|
const intptr_t constant_tag = reader.ReadByte();
|
|
switch (constant_tag) {
|
|
case kNullConstant:
|
|
instance = Instance::null();
|
|
break;
|
|
case kBoolConstant:
|
|
instance = reader.ReadByte() == 1 ? Object::bool_true().ptr()
|
|
: Object::bool_false().ptr();
|
|
break;
|
|
case kIntConstant: {
|
|
uint8_t payload = 0;
|
|
Tag integer_tag = reader.ReadTag(&payload); // read tag.
|
|
switch (integer_tag) {
|
|
case kBigIntLiteral: {
|
|
const String& value = H.DartString(reader.ReadStringReference());
|
|
instance = Integer::New(value, Heap::kOld);
|
|
break;
|
|
}
|
|
case kSpecializedIntLiteral: {
|
|
const int64_t value =
|
|
static_cast<int32_t>(payload) - SpecializedIntLiteralBias;
|
|
instance = Integer::New(value, Heap::kOld);
|
|
break;
|
|
}
|
|
case kNegativeIntLiteral: {
|
|
const int64_t value = -static_cast<int64_t>(reader.ReadUInt());
|
|
instance = Integer::New(value, Heap::kOld);
|
|
break;
|
|
}
|
|
case kPositiveIntLiteral: {
|
|
const int64_t value = reader.ReadUInt();
|
|
instance = Integer::New(value, Heap::kOld);
|
|
break;
|
|
}
|
|
default:
|
|
H.ReportError(
|
|
script_, TokenPosition::kNoSource,
|
|
"Cannot lazily read integer: unexpected kernel tag %s (%d)",
|
|
Reader::TagName(integer_tag), integer_tag);
|
|
}
|
|
break;
|
|
}
|
|
case kDoubleConstant:
|
|
instance = Double::New(reader.ReadDouble(), Heap::kOld);
|
|
break;
|
|
case kStringConstant:
|
|
instance = H.DartSymbolPlain(reader.ReadStringReference()).ptr();
|
|
break;
|
|
case kSymbolConstant: {
|
|
Library& library = Library::Handle(Z);
|
|
library = Library::InternalLibrary();
|
|
const auto& symbol_class =
|
|
Class::Handle(Z, library.LookupClass(Symbols::Symbol()));
|
|
const auto& symbol_name_field = Field::Handle(
|
|
Z, symbol_class.LookupInstanceFieldAllowPrivate(Symbols::_name()));
|
|
ASSERT(!symbol_name_field.IsNull());
|
|
const NameIndex index = reader.ReadCanonicalNameReference();
|
|
if (index == -1) {
|
|
library = Library::null();
|
|
} else {
|
|
library = H.LookupLibraryByKernelLibrary(index);
|
|
}
|
|
const String& symbol =
|
|
H.DartIdentifier(library, reader.ReadStringReference());
|
|
instance = Instance::New(symbol_class, Heap::kOld);
|
|
instance.SetField(symbol_name_field, symbol);
|
|
break;
|
|
}
|
|
case kListConstant: {
|
|
const auto& list_class = Class::Handle(
|
|
Z, H.isolate_group()->object_store()->immutable_array_class());
|
|
ASSERT(!list_class.IsNull());
|
|
// Build type from the raw bytes (needs temporary translator).
|
|
TypeTranslator type_translator(
|
|
&reader, this, active_class_, /* finalize = */ true,
|
|
active_class_->RequireConstCanonicalTypeErasure(null_safety),
|
|
/* in_constant_context = */ true);
|
|
auto& type_arguments =
|
|
TypeArguments::Handle(Z, TypeArguments::New(1, Heap::kOld));
|
|
AbstractType& type = type_translator.BuildType();
|
|
type_arguments.SetTypeAt(0, type);
|
|
// Instantiate class.
|
|
type = Type::New(list_class, type_arguments);
|
|
type = ClassFinalizer::FinalizeType(type, ClassFinalizer::kCanonicalize);
|
|
type_arguments = type.arguments();
|
|
// Fill array with constant elements.
|
|
const intptr_t length = reader.ReadUInt();
|
|
const Array& array =
|
|
Array::Handle(Z, ImmutableArray::New(length, Heap::kOld));
|
|
array.SetTypeArguments(type_arguments);
|
|
Instance& constant = Instance::Handle(Z);
|
|
for (intptr_t j = 0; j < length; ++j) {
|
|
// Recurse into lazily evaluating all "sub" constants
|
|
// needed to evaluate the current constant.
|
|
const intptr_t entry_index = reader.ReadUInt();
|
|
ASSERT(entry_index < constant_offset); // DAG!
|
|
constant = ReadConstant(entry_index);
|
|
array.SetAt(j, constant);
|
|
}
|
|
instance = array.ptr();
|
|
break;
|
|
}
|
|
case kMapConstant: {
|
|
const auto& map_class = Class::Handle(
|
|
Z,
|
|
H.isolate_group()->object_store()->immutable_linked_hash_map_class());
|
|
ASSERT(!map_class.IsNull());
|
|
|
|
// Build types from the raw bytes (needs temporary translator).
|
|
TypeTranslator type_translator(
|
|
&reader, this, active_class_, /* finalize = */ true,
|
|
active_class_->RequireConstCanonicalTypeErasure(null_safety),
|
|
/* in_constant_context = */ true);
|
|
auto& type_arguments =
|
|
TypeArguments::Handle(Z, TypeArguments::New(2, Heap::kOld));
|
|
AbstractType& type = type_translator.BuildType();
|
|
type_arguments.SetTypeAt(0, type);
|
|
type = type_translator.BuildType().ptr();
|
|
type_arguments.SetTypeAt(1, type);
|
|
|
|
// Instantiate class.
|
|
type = Type::New(map_class, type_arguments);
|
|
type = ClassFinalizer::FinalizeType(type, ClassFinalizer::kCanonicalize);
|
|
type_arguments = type.arguments();
|
|
|
|
// Fill map with constant elements.
|
|
const auto& map = LinkedHashMap::Handle(
|
|
Z, ImmutableLinkedHashMap::NewUninitialized(Heap::kOld));
|
|
ASSERT_EQUAL(map.GetClassId(), kImmutableLinkedHashMapCid);
|
|
map.SetTypeArguments(type_arguments);
|
|
const intptr_t length = reader.ReadUInt();
|
|
const intptr_t used_data = (length << 1);
|
|
map.set_used_data(used_data);
|
|
|
|
const intptr_t data_size = Utils::RoundUpToPowerOfTwo(used_data);
|
|
const auto& data = Array::Handle(Z, Array::New(data_size));
|
|
map.set_data(data);
|
|
|
|
map.set_deleted_keys(0);
|
|
map.ComputeAndSetHashMask();
|
|
|
|
Instance& constant = Instance::Handle(Z);
|
|
for (intptr_t j = 0; j < used_data; ++j) {
|
|
// Recurse into lazily evaluating all "sub" constants
|
|
// needed to evaluate the current constant.
|
|
const intptr_t entry_index = reader.ReadUInt();
|
|
ASSERT(entry_index < constant_offset); // DAG!
|
|
constant = ReadConstant(entry_index);
|
|
data.SetAt(j, constant);
|
|
}
|
|
|
|
instance = map.ptr();
|
|
break;
|
|
}
|
|
case kSetConstant: {
|
|
const auto& set_class = Class::Handle(
|
|
Z,
|
|
H.isolate_group()->object_store()->immutable_linked_hash_set_class());
|
|
ASSERT(!set_class.IsNull());
|
|
|
|
// Build types from the raw bytes (needs temporary translator).
|
|
TypeTranslator type_translator(
|
|
&reader, this, active_class_, /* finalize = */ true,
|
|
active_class_->RequireConstCanonicalTypeErasure(null_safety),
|
|
/* in_constant_context = */ true);
|
|
auto& type_arguments =
|
|
TypeArguments::Handle(Z, TypeArguments::New(1, Heap::kOld));
|
|
AbstractType& type = type_translator.BuildType();
|
|
type_arguments.SetTypeAt(0, type);
|
|
|
|
// Instantiate class.
|
|
type = Type::New(set_class, type_arguments);
|
|
type = ClassFinalizer::FinalizeType(type, ClassFinalizer::kCanonicalize);
|
|
type_arguments = type.arguments();
|
|
|
|
// Fill set with constant elements.
|
|
const auto& set = LinkedHashSet::Handle(
|
|
Z, ImmutableLinkedHashSet::NewUninitialized(Heap::kOld));
|
|
ASSERT_EQUAL(set.GetClassId(), kImmutableLinkedHashSetCid);
|
|
set.SetTypeArguments(type_arguments);
|
|
const intptr_t length = reader.ReadUInt();
|
|
const intptr_t used_data = length;
|
|
set.set_used_data(used_data);
|
|
|
|
const intptr_t data_size = Utils::RoundUpToPowerOfTwo(used_data);
|
|
const auto& data = Array::Handle(Z, Array::New(data_size));
|
|
set.set_data(data);
|
|
|
|
set.set_deleted_keys(0);
|
|
set.ComputeAndSetHashMask();
|
|
|
|
Instance& constant = Instance::Handle(Z);
|
|
for (intptr_t j = 0; j < used_data; ++j) {
|
|
// Recurse into lazily evaluating all "sub" constants
|
|
// needed to evaluate the current constant.
|
|
const intptr_t entry_index = reader.ReadUInt();
|
|
ASSERT(entry_index < constant_offset); // DAG!
|
|
constant = ReadConstant(entry_index);
|
|
data.SetAt(j, constant);
|
|
}
|
|
|
|
instance = set.ptr();
|
|
break;
|
|
}
|
|
case kInstanceConstant: {
|
|
const NameIndex index = reader.ReadCanonicalNameReference();
|
|
const auto& klass = Class::Handle(Z, H.LookupClassByKernelClass(index));
|
|
if (!klass.is_declaration_loaded()) {
|
|
FATAL1(
|
|
"Trying to evaluate an instance constant whose references class "
|
|
"%s is not loaded yet.",
|
|
klass.ToCString());
|
|
}
|
|
const auto& obj =
|
|
Object::Handle(Z, klass.EnsureIsAllocateFinalized(H.thread()));
|
|
ASSERT(obj.IsNull());
|
|
ASSERT(klass.is_enum_class() || klass.is_const());
|
|
instance = Instance::New(klass, Heap::kOld);
|
|
// Build type from the raw bytes (needs temporary translator).
|
|
TypeTranslator type_translator(
|
|
&reader, this, active_class_, /* finalize = */ true,
|
|
active_class_->RequireConstCanonicalTypeErasure(null_safety),
|
|
/* in_constant_context = */ true);
|
|
const intptr_t number_of_type_arguments = reader.ReadUInt();
|
|
if (klass.NumTypeArguments() > 0) {
|
|
auto& type_arguments = TypeArguments::Handle(
|
|
Z, TypeArguments::New(number_of_type_arguments, Heap::kOld));
|
|
for (intptr_t j = 0; j < number_of_type_arguments; ++j) {
|
|
type_arguments.SetTypeAt(j, type_translator.BuildType());
|
|
}
|
|
// Instantiate class.
|
|
auto& type = AbstractType::Handle(Z, Type::New(klass, type_arguments));
|
|
type =
|
|
ClassFinalizer::FinalizeType(type, ClassFinalizer::kCanonicalize);
|
|
type_arguments = type.arguments();
|
|
instance.SetTypeArguments(type_arguments);
|
|
} else {
|
|
ASSERT(number_of_type_arguments == 0);
|
|
}
|
|
// Set the fields.
|
|
const intptr_t number_of_fields = reader.ReadUInt();
|
|
Field& field = Field::Handle(Z);
|
|
Instance& constant = Instance::Handle(Z);
|
|
for (intptr_t j = 0; j < number_of_fields; ++j) {
|
|
field = H.LookupFieldByKernelField(reader.ReadCanonicalNameReference());
|
|
// Recurse into lazily evaluating all "sub" constants
|
|
// needed to evaluate the current constant.
|
|
const intptr_t entry_index = reader.ReadUInt();
|
|
ASSERT(entry_index < constant_offset); // DAG!
|
|
constant = ReadConstant(entry_index);
|
|
instance.SetField(field, constant);
|
|
}
|
|
break;
|
|
}
|
|
case kInstantiationConstant: {
|
|
// Recurse into lazily evaluating the "sub" constant
|
|
// needed to evaluate the current constant.
|
|
const intptr_t entry_index = reader.ReadUInt();
|
|
ASSERT(entry_index < constant_offset); // DAG!
|
|
const auto& constant = Instance::Handle(Z, ReadConstant(entry_index));
|
|
ASSERT(!constant.IsNull());
|
|
|
|
// Build type from the raw bytes (needs temporary translator).
|
|
TypeTranslator type_translator(
|
|
&reader, this, active_class_, /* finalize = */ true,
|
|
active_class_->RequireConstCanonicalTypeErasure(null_safety),
|
|
/* in_constant_context = */ true);
|
|
const intptr_t number_of_type_arguments = reader.ReadUInt();
|
|
ASSERT(number_of_type_arguments > 0);
|
|
auto& type_arguments = TypeArguments::Handle(
|
|
Z, TypeArguments::New(number_of_type_arguments, Heap::kOld));
|
|
for (intptr_t j = 0; j < number_of_type_arguments; ++j) {
|
|
type_arguments.SetTypeAt(j, type_translator.BuildType());
|
|
}
|
|
type_arguments = type_arguments.Canonicalize(Thread::Current(), nullptr);
|
|
// Make a copy of the old closure, and set delayed type arguments.
|
|
Closure& closure = Closure::Handle(Z, Closure::RawCast(constant.ptr()));
|
|
Function& function = Function::Handle(Z, closure.function());
|
|
const auto& type_arguments2 =
|
|
TypeArguments::Handle(Z, closure.instantiator_type_arguments());
|
|
// The function type arguments are used for type parameters from enclosing
|
|
// closures. Though inner closures cannot be constants. We should
|
|
// therefore see `null here.
|
|
ASSERT(closure.function_type_arguments() == TypeArguments::null());
|
|
Context& context = Context::Handle(Z, closure.context());
|
|
instance = Closure::New(type_arguments2, Object::null_type_arguments(),
|
|
type_arguments, function, context, Heap::kOld);
|
|
break;
|
|
}
|
|
case kStaticTearOffConstant:
|
|
case kConstructorTearOffConstant:
|
|
case kRedirectingFactoryTearOffConstant: {
|
|
const NameIndex index = reader.ReadCanonicalNameReference();
|
|
Function& function = Function::Handle(Z);
|
|
if (H.IsConstructor(index)) {
|
|
function = H.LookupConstructorByKernelConstructor(index);
|
|
} else {
|
|
function = H.LookupStaticMethodByKernelProcedure(index);
|
|
}
|
|
function = function.ImplicitClosureFunction();
|
|
instance = function.ImplicitStaticClosure();
|
|
break;
|
|
}
|
|
case kTypeLiteralConstant: {
|
|
// Build type from the raw bytes (needs temporary translator).
|
|
// Const canonical type erasure is not applied to constant type literals.
|
|
// However, CFE must ensure that constant type literals can be
|
|
// canonicalized to an identical representant independently of the null
|
|
// safety mode currently in use (sound or unsound) or migration state of
|
|
// the declaring library (legacy or opted-in).
|
|
TypeTranslator type_translator(&reader, this, active_class_,
|
|
/* finalize = */ true,
|
|
/* apply_canonical_type_erasure = */ false,
|
|
/* in_constant_context = */ true);
|
|
instance = type_translator.BuildType().ptr();
|
|
break;
|
|
}
|
|
default:
|
|
// We should never see unevaluated constants (kUnevaluatedConstant) in
|
|
// the constant table, they should have been fully evaluated before we
|
|
// get them.
|
|
H.ReportError(script_, TokenPosition::kNoSource,
|
|
"Cannot lazily read constant: unexpected kernel tag (%" Pd
|
|
")",
|
|
constant_tag);
|
|
}
|
|
return H.Canonicalize(instance);
|
|
}
|
|
|
|
} // namespace kernel
|
|
} // namespace dart
|