901fed103b
Also ensure the number of parent type arguments is appropriately adjusted in the returned FunctionType when partially instantiating a FunctionType, and that free parent type arguments are _not_ included in types built while reading constants. Since the number of parent type arguments is now cached there, we no longer need to also cache them in ClosureData. We can also remove the parent walking in Function::NumParentTypeArguments(). Previously, a FunctionType where the component types did not use parent type parameters was considered instantiated. This CL changes it so that FunctionType with free parent type parameters are never considered instantiated. This is necessary because otherwise, when instantiating the parent type parameters, a FunctionType that does not use its parent type parameters will be used unchanged instead of creating a copy with fewer parent type parameters. Because of this, places where IsInstantiated was used to simply check for generic parent functions (namely, for implicit closure creation) has been appropriately weakened to check !HasGenericParent() instead. TEST=Existing tests on CI. Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try Change-Id: Ifb4a0a1273d8d01908cdf4ffc3c4c28a1c33ffa0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/190021 Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Régis Crelier <regis@google.com> Commit-Queue: Tess Strickland <sstrickl@google.com>
351 lines
15 KiB
C++
351 lines
15 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"
|
|
|
|
namespace dart {
|
|
namespace kernel {
|
|
|
|
#define Z (zone_)
|
|
#define H (translation_helper_)
|
|
|
|
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_(Instance::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 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 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_offset) {
|
|
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());
|
|
KernelConstantsMap constant_map(H.info().constants());
|
|
result_ ^= constant_map.GetOrNull(constant_offset);
|
|
ASSERT(constant_map.Release().ptr() == H.info().constants());
|
|
}
|
|
|
|
// On miss, evaluate, and insert value.
|
|
if (result_.IsNull()) {
|
|
LeaveCompilerScope cs(H.thread());
|
|
result_ = ReadConstantInternal(constant_offset);
|
|
SafepointMutexLocker ml(
|
|
H.thread()->isolate_group()->kernel_constants_mutex());
|
|
KernelConstantsMap constant_map(H.info().constants());
|
|
auto insert = constant_map.InsertNewOrGetValue(constant_offset, result_);
|
|
ASSERT(insert == result_.ptr());
|
|
H.info().set_constants(constant_map.Release()); // update!
|
|
}
|
|
return result_.ptr();
|
|
}
|
|
|
|
bool ConstantReader::IsInstanceConstant(intptr_t constant_offset,
|
|
const Class& clazz) {
|
|
// Get reader directly into raw bytes of constant table.
|
|
KernelReaderHelper reader(Z, &H, script_, H.constants_table(), 0);
|
|
reader.ReadUInt(); // skip variable-sized int for adjusted constant offset
|
|
reader.SetOffset(reader.ReaderOffset() + constant_offset);
|
|
// 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;
|
|
}
|
|
|
|
InstancePtr ConstantReader::ReadConstantInternal(intptr_t constant_offset) {
|
|
// Get reader directly into raw bytes of constant table.
|
|
bool null_safety = H.thread()->isolate_group()->null_safety();
|
|
KernelReaderHelper reader(Z, &H, script_, H.constants_table(), 0);
|
|
reader.ReadUInt(); // skip variable-sized int for adjusted constant offset
|
|
reader.SetOffset(reader.ReaderOffset() + constant_offset);
|
|
// 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& corelib = Library::Handle(Z, Library::CoreLibrary());
|
|
const auto& list_class =
|
|
Class::Handle(Z, corelib.LookupClassAllowPrivate(Symbols::_List()));
|
|
// Build type from the raw bytes (needs temporary translator).
|
|
TypeTranslator type_translator(
|
|
&reader, this, active_class_, true,
|
|
active_class_->RequireConstCanonicalTypeErasure(null_safety));
|
|
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_offset = reader.ReadUInt();
|
|
ASSERT(entry_offset < constant_offset); // DAG!
|
|
constant = ReadConstant(entry_offset);
|
|
array.SetAt(j, constant);
|
|
}
|
|
instance = array.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_, true,
|
|
active_class_->RequireConstCanonicalTypeErasure(null_safety));
|
|
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.LookupFieldByKernelGetterOrSetter(
|
|
reader.ReadCanonicalNameReference());
|
|
// Recurse into lazily evaluating all "sub" constants
|
|
// needed to evaluate the current constant.
|
|
const intptr_t entry_offset = reader.ReadUInt();
|
|
ASSERT(entry_offset < constant_offset); // DAG!
|
|
constant = ReadConstant(entry_offset);
|
|
instance.SetField(field, constant);
|
|
}
|
|
break;
|
|
}
|
|
case kPartialInstantiationConstant: {
|
|
// Recurse into lazily evaluating the "sub" constant
|
|
// needed to evaluate the current constant.
|
|
const intptr_t entry_offset = reader.ReadUInt();
|
|
ASSERT(entry_offset < constant_offset); // DAG!
|
|
const auto& constant = Instance::Handle(Z, ReadConstant(entry_offset));
|
|
ASSERT(!constant.IsNull());
|
|
|
|
// Build type from the raw bytes (needs temporary translator).
|
|
TypeTranslator type_translator(
|
|
&reader, this, active_class_, true,
|
|
active_class_->RequireConstCanonicalTypeErasure(null_safety));
|
|
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 kTearOffConstant: {
|
|
const NameIndex index = reader.ReadCanonicalNameReference();
|
|
Function& function =
|
|
Function::Handle(Z, 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_, true);
|
|
instance = type_translator.BuildType().ptr();
|
|
break;
|
|
}
|
|
default:
|
|
// Set literals (kSetConstant) are currently desugared in the frontend
|
|
// and will not reach the VM. See http://dartbug.com/35124 for some
|
|
// discussion. Map constants (kMapConstant ) are already lowered to
|
|
// InstanceConstant or ListConstant. 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
|