Files
sdk/runtime/vm/class_finalizer.cc
T
Alexander Markov 7b50b7a8e6 [vm/bytecode] Bootstrapping VM from bytecode
Previously, core snapshot was generated from AST
(because --enable-interpreter/--use-bytecode-compiler was not specified
when building core snapshot).

As the result, CL
https://dart.googlesource.com/sdk/+/da8cb470cc94830a98d49532e8d5d1a5b3d80f8b
which declared libraries in bytecode also removed bytecode entirely from
core snapshot in Dart SDK.

This CL enables bytecode by default if --bytecode argument is
specified for gn.py. This enables JIT compiler from bytecode
(interpreter is still disabled by default but can be enabled
using --enable-interpreter). Core snapshot and other
snapshots now have bytecode.

This change revealed a bunch of bugs which are fixed in this CL:

* _Closure fields were treated as unboxing candidates which triggered
  assertion in LoadFieldTOS in interpreter.

* Several places should load class declarations if they are not loaded yet.

* Canonicalization of TypeRef objects which are not fully initialized
  may cause duplicate entries in the hash table of canonical
  TypeArguments. This triggers assertions when hash table is rehashed.
  The solution is to avoid canonicalization of non-root recursive types
  and recursive type arguments. Also, TypeRef::Canonicalize and
  TypeRef::Hash are reverted to assert and work only if type was set.

* Native wrapper classes are eagerly stamped as type-finalized
  which caused assertion failures when reading their class declarations
  from bytecode.

* When building flow graph for FFI trampolines kernel offset of library
  (which is now declared in bytecode) was queried. Added special case
  to Function::KernelDataProgramOffset().

* In interpreter-only mode with simulator (e.g. SIMARM64) if simulator
  is not called before code is interrupted with stack overflow check,
  simulator returns get_sp() = 0, which was treated as stack overflow.

* test standalone_2/io/platform_resolved_executable_test.dart
  spawns sub-process but it didn't pass VM options.

Change-Id: I81bc4f1a4c6725cfa246a435ebe5d8abe43abc67
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/107199
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
2019-06-26 18:25:26 +00:00

1731 lines
66 KiB
C++

// Copyright (c) 2013, 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/class_finalizer.h"
#include "vm/compiler/jit/compiler.h"
#include "vm/flags.h"
#include "vm/hash_table.h"
#include "vm/heap/heap.h"
#include "vm/isolate.h"
#include "vm/kernel_loader.h"
#include "vm/log.h"
#include "vm/longjump.h"
#include "vm/object_store.h"
#include "vm/program_visitor.h"
#include "vm/runtime_entry.h"
#include "vm/symbols.h"
#include "vm/timeline.h"
#include "vm/type_table.h"
#include "vm/type_testing_stubs.h"
namespace dart {
DEFINE_FLAG(bool, print_classes, false, "Prints details about loaded classes.");
DEFINE_FLAG(bool, trace_class_finalization, false, "Trace class finalization.");
DEFINE_FLAG(bool, trace_type_finalization, false, "Trace type finalization.");
bool ClassFinalizer::AllClassesFinalized() {
ObjectStore* object_store = Isolate::Current()->object_store();
const GrowableObjectArray& classes =
GrowableObjectArray::Handle(object_store->pending_classes());
return classes.Length() == 0;
}
// Removes optimized code once we load more classes, since CHA based
// optimizations may have become invalid.
// Only methods which owner classes where subclasses can be invalid.
// TODO(srdjan): Be even more precise by recording the exact CHA optimization.
static void RemoveCHAOptimizedCode(
const Class& subclass,
const GrowableArray<intptr_t>& added_subclass_to_cids) {
ASSERT(FLAG_use_cha_deopt);
if (added_subclass_to_cids.is_empty()) {
return;
}
// Switch all functions' code to unoptimized.
const ClassTable& class_table = *Isolate::Current()->class_table();
Class& cls = Class::Handle();
for (intptr_t i = 0; i < added_subclass_to_cids.length(); i++) {
intptr_t cid = added_subclass_to_cids[i];
cls = class_table.At(cid);
ASSERT(!cls.IsNull());
cls.DisableCHAOptimizedCode(subclass);
}
}
void AddSuperType(const AbstractType& type,
GrowableArray<intptr_t>* finalized_super_classes) {
ASSERT(type.HasTypeClass());
ASSERT(!type.IsDynamicType());
if (type.IsObjectType()) {
return;
}
const Class& cls = Class::Handle(type.type_class());
ASSERT(cls.is_finalized());
const intptr_t cid = cls.id();
for (intptr_t i = 0; i < finalized_super_classes->length(); i++) {
if ((*finalized_super_classes)[i] == cid) {
// Already added.
return;
}
}
finalized_super_classes->Add(cid);
const AbstractType& super_type = AbstractType::Handle(cls.super_type());
AddSuperType(super_type, finalized_super_classes);
}
// Use array instead of set since we expect very few subclassed classes
// to occur.
static void CollectFinalizedSuperClasses(
const Class& cls_,
GrowableArray<intptr_t>* finalized_super_classes) {
Class& cls = Class::Handle(cls_.raw());
AbstractType& super_type = Type::Handle();
super_type = cls.super_type();
if (!super_type.IsNull()) {
if (super_type.HasTypeClass()) {
cls = super_type.type_class();
if (cls.is_finalized()) {
AddSuperType(super_type, finalized_super_classes);
}
}
}
}
class InterfaceFinder {
public:
InterfaceFinder(Zone* zone,
ClassTable* class_table,
GrowableArray<intptr_t>* cids)
: class_table_(class_table),
array_handles_(zone),
class_handles_(zone),
type_handles_(zone),
cids_(cids) {}
void FindAllInterfaces(const Class& klass) {
// The class is implementing it's own interface.
cids_->Add(klass.id());
ScopedHandle<Array> array(&array_handles_);
ScopedHandle<Class> interface_class(&class_handles_);
ScopedHandle<Class> current_class(&class_handles_);
ScopedHandle<AbstractType> type(&type_handles_);
*current_class = klass.raw();
while (true) {
// We don't care about top types.
const intptr_t cid = current_class->id();
if (cid == kObjectCid || cid == kDynamicCid || cid == kVoidCid) {
break;
}
// The class is implementing it's directly declared implemented
// interfaces.
*array = klass.interfaces();
if (!array->IsNull()) {
for (intptr_t i = 0; i < array->Length(); ++i) {
*type ^= array->At(i);
*interface_class = class_table_->At(type->type_class_id());
FindAllInterfaces(*interface_class);
}
}
// The class is implementing it's super type's interfaces.
*type = current_class->super_type();
if (type->IsNull()) break;
*current_class = class_table_->At(type->type_class_id());
}
}
private:
ClassTable* class_table_;
ReusableHandleStack<Array> array_handles_;
ReusableHandleStack<Class> class_handles_;
ReusableHandleStack<AbstractType> type_handles_;
GrowableArray<intptr_t>* cids_;
};
static void CollectImmediateSuperInterfaces(const Class& cls,
GrowableArray<intptr_t>* cids) {
const Array& interfaces = Array::Handle(cls.interfaces());
Class& ifc = Class::Handle();
AbstractType& type = AbstractType::Handle();
for (intptr_t i = 0; i < interfaces.Length(); ++i) {
type ^= interfaces.At(i);
if (!type.HasTypeClass()) continue;
ifc = type.type_class();
for (intptr_t j = 0; j < cids->length(); ++j) {
if ((*cids)[j] == ifc.id()) {
// Already added.
return;
}
}
cids->Add(ifc.id());
}
}
// Processing ObjectStore::pending_classes_ occurs:
// a) when bootstrap process completes (VerifyBootstrapClasses).
// b) after the user classes are loaded (dart_api).
bool ClassFinalizer::ProcessPendingClasses() {
Thread* thread = Thread::Current();
TIMELINE_DURATION(thread, Isolate, "ProcessPendingClasses");
Isolate* isolate = thread->isolate();
ASSERT(isolate != NULL);
HANDLESCOPE(thread);
ObjectStore* object_store = isolate->object_store();
const Error& error = Error::Handle(thread->zone(), thread->sticky_error());
if (!error.IsNull()) {
return false;
}
if (AllClassesFinalized()) {
return true;
}
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
GrowableObjectArray& class_array = GrowableObjectArray::Handle();
class_array = object_store->pending_classes();
ASSERT(!class_array.IsNull());
Class& cls = Class::Handle();
#if defined(DEBUG)
for (intptr_t i = 0; i < class_array.Length(); i++) {
cls ^= class_array.At(i);
ASSERT(cls.is_declared_in_bytecode() || cls.is_declaration_loaded());
}
#endif
// Finalize types in all classes.
for (intptr_t i = 0; i < class_array.Length(); i++) {
cls ^= class_array.At(i);
if (cls.is_declared_in_bytecode()) {
cls.EnsureDeclarationLoaded();
ASSERT(cls.is_type_finalized());
} else {
FinalizeTypesInClass(cls);
}
}
if (FLAG_print_classes) {
for (intptr_t i = 0; i < class_array.Length(); i++) {
cls ^= class_array.At(i);
PrintClassInformation(cls);
}
}
// Clear pending classes array.
class_array = GrowableObjectArray::New();
object_store->set_pending_classes(class_array);
VerifyImplicitFieldOffsets(); // Verification after an error may fail.
return true;
} else {
return false;
}
UNREACHABLE();
return true;
}
#if !defined(DART_PRECOMPILED_RUNTIME)
void ClassFinalizer::VerifyBootstrapClasses() {
if (FLAG_trace_class_finalization) {
OS::PrintErr("VerifyBootstrapClasses START.\n");
}
ObjectStore* object_store = Isolate::Current()->object_store();
Class& cls = Class::Handle();
#if defined(DEBUG)
// Basic checking.
cls = object_store->object_class();
ASSERT(Instance::InstanceSize() == cls.instance_size());
cls = object_store->integer_implementation_class();
ASSERT(Integer::InstanceSize() == cls.instance_size());
cls = object_store->smi_class();
ASSERT(Smi::InstanceSize() == cls.instance_size());
cls = object_store->mint_class();
ASSERT(Mint::InstanceSize() == cls.instance_size());
cls = object_store->one_byte_string_class();
ASSERT(OneByteString::InstanceSize() == cls.instance_size());
cls = object_store->two_byte_string_class();
ASSERT(TwoByteString::InstanceSize() == cls.instance_size());
cls = object_store->external_one_byte_string_class();
ASSERT(ExternalOneByteString::InstanceSize() == cls.instance_size());
cls = object_store->external_two_byte_string_class();
ASSERT(ExternalTwoByteString::InstanceSize() == cls.instance_size());
cls = object_store->double_class();
ASSERT(Double::InstanceSize() == cls.instance_size());
cls = object_store->bool_class();
ASSERT(Bool::InstanceSize() == cls.instance_size());
cls = object_store->array_class();
ASSERT(Array::InstanceSize() == cls.instance_size());
cls = object_store->immutable_array_class();
ASSERT(ImmutableArray::InstanceSize() == cls.instance_size());
cls = object_store->weak_property_class();
ASSERT(WeakProperty::InstanceSize() == cls.instance_size());
cls = object_store->linked_hash_map_class();
ASSERT(LinkedHashMap::InstanceSize() == cls.instance_size());
#endif // defined(DEBUG)
// Remember the currently pending classes.
const GrowableObjectArray& class_array =
GrowableObjectArray::Handle(object_store->pending_classes());
for (intptr_t i = 0; i < class_array.Length(); i++) {
// TODO(iposva): Add real checks.
cls ^= class_array.At(i);
if (cls.is_finalized() || cls.is_prefinalized()) {
// Pre-finalized bootstrap classes must not define any fields.
ASSERT(!cls.HasInstanceFields());
}
}
// Finalize type hierarchy for types that aren't pre-finalized
// by Object::Init().
if (!ProcessPendingClasses()) {
// TODO(srdjan): Exit like a real VM instead.
const Error& err = Error::Handle(Thread::Current()->sticky_error());
OS::PrintErr("Could not verify bootstrap classes : %s\n",
err.ToErrorCString());
OS::Exit(255);
}
if (FLAG_trace_class_finalization) {
OS::PrintErr("VerifyBootstrapClasses END.\n");
}
Isolate::Current()->heap()->Verify();
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
void ClassFinalizer::FinalizeTypeParameters(const Class& cls,
PendingTypes* pending_types) {
if (FLAG_trace_type_finalization) {
THR_Print("Finalizing type parameters of '%s'\n",
String::Handle(cls.Name()).ToCString());
}
// The type parameter bounds are not finalized here.
const TypeArguments& type_parameters =
TypeArguments::Handle(cls.type_parameters());
if (!type_parameters.IsNull()) {
TypeParameter& type_parameter = TypeParameter::Handle();
const intptr_t num_types = type_parameters.Length();
for (intptr_t i = 0; i < num_types; i++) {
type_parameter ^= type_parameters.TypeAt(i);
type_parameter ^=
FinalizeType(cls, type_parameter, kFinalize, pending_types);
type_parameters.SetTypeAt(i, type_parameter);
}
}
}
// This function reports a compilation error if the recursive 'type' T being
// finalized is a non-contractive type, i.e. if the induced type set S of P is
// not finite, where P is the instantiation of T with its own type parameters.
// The induced type set S consists of the super types of any type in S as well
// as the type arguments of any parameterized type in S.
// The Dart Language Specification does not disallow the declaration and use of
// non-contractive types (this may change). They are nevertheless disallowed
// as an implementation restriction in the VM since they cause divergence.
// A non-contractive type can be detected by looking at the queue of types
// pending finalization that are mutually recursive with the checked type.
void ClassFinalizer::CheckRecursiveType(const Class& cls,
const AbstractType& type,
PendingTypes* pending_types) {
ASSERT(pending_types != NULL);
Zone* zone = Thread::Current()->zone();
if (FLAG_trace_type_finalization) {
THR_Print("Checking recursive type '%s': %s\n",
String::Handle(type.Name()).ToCString(), type.ToCString());
}
const Class& type_cls = Class::Handle(zone, type.type_class());
const TypeArguments& arguments =
TypeArguments::Handle(zone, type.arguments());
// A type can only be recursive via its type arguments.
if (arguments.IsNull()) {
// However, Kernel does not keep the relation between a function type and
// its declaring typedef. Therefore, a typedef-declared function type may
// refer to the still unfinalized typedef via a type in its signature.
ASSERT(type.IsFunctionType());
return;
}
const intptr_t num_type_args = arguments.Length();
ASSERT(num_type_args > 0);
ASSERT(num_type_args == type_cls.NumTypeArguments());
const intptr_t num_type_params = type_cls.NumTypeParameters();
const intptr_t first_type_param = num_type_args - num_type_params;
// If the type is not generic (num_type_params == 0) or if its type parameters
// are instantiated, no divergence can occur. Note that if the type parameters
// are null, i.e. if the generic type is raw, they are considered
// instantiated and no divergence can occur.
if ((num_type_params == 0) ||
arguments.IsSubvectorInstantiated(first_type_param, num_type_params)) {
return;
}
// Consider mutually recursive and uninstantiated types pending finalization
// with the same type class and report an error if they are not equal in their
// raw form, i.e. where each class type parameter is substituted with dynamic.
// This test eliminates divergent types without restricting recursive types
// typically found in the wild.
TypeArguments& pending_arguments = TypeArguments::Handle(zone);
const intptr_t num_pending_types = pending_types->length();
for (intptr_t i = num_pending_types - 1; i >= 0; i--) {
const AbstractType& pending_type = pending_types->At(i);
if (FLAG_trace_type_finalization) {
THR_Print(" Comparing with pending type '%s': %s\n",
String::Handle(pending_type.Name()).ToCString(),
pending_type.ToCString());
}
if ((pending_type.raw() != type.raw()) && pending_type.IsType() &&
(pending_type.type_class() == type_cls.raw())) {
pending_arguments = pending_type.arguments();
if (!pending_arguments.IsSubvectorEquivalent(arguments, first_type_param,
num_type_params) &&
!pending_arguments.IsSubvectorInstantiated(first_type_param,
num_type_params)) {
const TypeArguments& instantiated_arguments = TypeArguments::Handle(
zone, arguments.InstantiateFrom(Object::null_type_arguments(),
Object::null_type_arguments(),
kNoneFree, NULL, Heap::kNew));
const TypeArguments& instantiated_pending_arguments =
TypeArguments::Handle(zone, pending_arguments.InstantiateFrom(
Object::null_type_arguments(),
Object::null_type_arguments(),
kNoneFree, NULL, Heap::kNew));
if (!instantiated_pending_arguments.IsSubvectorEquivalent(
instantiated_arguments, first_type_param, num_type_params)) {
const String& type_name = String::Handle(zone, type.Name());
ReportError(cls, type.token_pos(), "illegal recursive type '%s'",
type_name.ToCString());
}
}
}
}
}
// Expand the type arguments of the given type and finalize its full type
// argument vector. Return the number of type arguments (0 for a raw type).
intptr_t ClassFinalizer::ExpandAndFinalizeTypeArguments(
const Class& cls,
const AbstractType& type,
PendingTypes* pending_types) {
Zone* zone = Thread::Current()->zone();
// The type class does not need to be finalized in order to finalize the type.
// Also, the type parameters of the type class must be finalized.
Class& type_class = Class::Handle(zone, type.type_class());
type_class.EnsureDeclarationLoaded();
if (!type_class.is_type_finalized()) {
FinalizeTypeParameters(type_class, pending_types);
}
// The finalized type argument vector needs num_type_arguments types.
const intptr_t num_type_arguments = type_class.NumTypeArguments();
// The class has num_type_parameters type parameters.
const intptr_t num_type_parameters = type_class.NumTypeParameters();
// Initialize the type argument vector.
// Check the number of parsed type arguments, if any.
// Specifying no type arguments indicates a raw type, which is not an error.
// However, type parameter bounds are checked below, even for a raw type.
TypeArguments& arguments = TypeArguments::Handle(zone, type.arguments());
if (!arguments.IsNull() && (arguments.Length() != num_type_parameters)) {
// Make the type raw and continue without reporting any error.
// A static warning should have been reported.
// TODO(regis): Check if this is dead code.
arguments = TypeArguments::null();
type.set_arguments(arguments);
}
// Mark the type as being finalized in order to detect self reference and
// postpone bound checking (if required) until after all types in the graph of
// mutually recursive types are finalized.
type.SetIsBeingFinalized();
ASSERT(pending_types != NULL);
pending_types->Add(type);
// The full type argument vector consists of the type arguments of the
// super types of type_class, which are initialized from the parsed
// type arguments, followed by the parsed type arguments.
TypeArguments& full_arguments = TypeArguments::Handle(zone);
if (num_type_arguments > 0) {
// If no type arguments were parsed and if the super types do not prepend
// type arguments to the vector, we can leave the vector as null.
if (!arguments.IsNull() || (num_type_arguments > num_type_parameters)) {
full_arguments = TypeArguments::New(num_type_arguments);
// Copy the parsed type arguments at the correct offset in the full type
// argument vector.
const intptr_t offset = num_type_arguments - num_type_parameters;
AbstractType& type_arg = AbstractType::Handle(zone, Type::DynamicType());
// Leave the temporary type arguments at indices [0..offset[ as null.
for (intptr_t i = 0; i < num_type_parameters; i++) {
// If no type parameters were provided, a raw type is desired, so we
// create a vector of dynamic.
if (!arguments.IsNull()) {
type_arg = arguments.TypeAt(i);
// The parsed type_arg may or may not be finalized.
}
full_arguments.SetTypeAt(offset + i, type_arg);
}
// Replace the compile-time argument vector (of length zero or
// num_type_parameters) of this type being finalized with the still
// unfinalized run-time argument vector (of length num_type_arguments).
// This type being finalized may be recursively reached via bounds
// checking or type arguments of its super type.
type.set_arguments(full_arguments);
// Finalize the current type arguments of the type, which are still the
// parsed type arguments.
if (!arguments.IsNull()) {
for (intptr_t i = 0; i < num_type_parameters; i++) {
type_arg = full_arguments.TypeAt(offset + i);
ASSERT(!type_arg.IsBeingFinalized());
type_arg = FinalizeType(cls, type_arg, kFinalize, pending_types);
if (type_arg.IsFunctionType()) {
const Function& signature_function =
Function::Handle(zone, Type::Cast(type_arg).signature());
if (signature_function.IsGeneric()) {
const String& type_arg_name =
String::Handle(zone, type_arg.UserVisibleName());
const String& type_name =
String::Handle(zone, type.UserVisibleName());
ReportError(cls, type_arg.token_pos(),
"generic function type '%s' not allowed as type "
"argument of type '%s'",
type_arg_name.ToCString(), type_name.ToCString());
}
}
full_arguments.SetTypeAt(offset + i, type_arg);
}
}
if (offset > 0) {
TrailPtr instantiation_trail = new Trail(zone, 4);
FinalizeTypeArguments(type_class, full_arguments, offset, pending_types,
instantiation_trail);
}
if (full_arguments.IsRaw(0, num_type_arguments)) {
// The parameterized_type is raw. Set its argument vector to null, which
// is more efficient in type tests.
full_arguments = TypeArguments::null();
}
type.set_arguments(full_arguments);
} else {
ASSERT(full_arguments.IsNull()); // Use null vector for raw type.
}
}
ASSERT(full_arguments.IsNull() ||
!full_arguments.IsRaw(0, num_type_arguments));
return full_arguments.IsNull() ? 0 : full_arguments.Length();
}
// Finalize the type argument vector 'arguments' of the type defined by the
// class 'cls' parameterized with the type arguments 'cls_args'.
// The vector 'cls_args' is already initialized as a subvector at the correct
// position in the passed in 'arguments' vector.
// The subvector 'cls_args' has length cls.NumTypeParameters() and starts at
// offset cls.NumTypeArguments() - cls.NumTypeParameters() of the 'arguments'
// vector.
// The type argument vector of cls may overlap the type argument vector of its
// super class. In case of an overlap, the overlapped type arguments of the
// super class are already initialized. The still uninitialized ones have an
// offset smaller than 'num_uninitialized_arguments'.
// Example 1 (without overlap):
// Declared: class C<K, V> extends B<V> { ... }
// class B<T> extends A<int> { ... }
// Input: C<String, double> expressed as
// cls = C, arguments = [dynamic, dynamic, String, double],
// num_uninitialized_arguments = 2,
// i.e. cls_args = [String, double], offset = 2, length = 2.
// Output: arguments = [int, double, String, double]
// Example 2 (with overlap):
// Declared: class C<K, V> extends B<K> { ... }
// class B<T> extends A<int> { ... }
// Input: C<String, double> expressed as
// cls = C, arguments = [dynamic, String, double],
// num_uninitialized_arguments = 1,
// i.e. cls_args = [String, double], offset = 1, length = 2.
// Output: arguments = [int, String, double]
//
// It is too early to canonicalize the type arguments of the vector, because
// several type argument vectors may be mutually recursive and finalized at the
// same time. Canonicalization happens when pending types are processed.
// The trail is required to correctly instantiate a recursive type argument
// of the super type.
void ClassFinalizer::FinalizeTypeArguments(const Class& cls,
const TypeArguments& arguments,
intptr_t num_uninitialized_arguments,
PendingTypes* pending_types,
TrailPtr instantiation_trail) {
ASSERT(arguments.Length() >= cls.NumTypeArguments());
if (!cls.is_type_finalized()) {
FinalizeTypeParameters(cls, pending_types);
}
AbstractType& super_type = AbstractType::Handle(cls.super_type());
if (!super_type.IsNull()) {
const Class& super_class = Class::Handle(super_type.type_class());
const intptr_t num_super_type_params = super_class.NumTypeParameters();
const intptr_t num_super_type_args = super_class.NumTypeArguments();
if (!super_type.IsFinalized() && !super_type.IsBeingFinalized()) {
super_type = FinalizeType(cls, super_type, kFinalize, pending_types);
cls.set_super_type(super_type);
}
TypeArguments& super_type_args =
TypeArguments::Handle(super_type.arguments());
// Offset of super type's type parameters in cls' type argument vector.
const intptr_t super_offset = num_super_type_args - num_super_type_params;
// If the super type is raw (i.e. super_type_args is null), set to dynamic.
AbstractType& super_type_arg = AbstractType::Handle(Type::DynamicType());
for (intptr_t i = super_offset; i < num_uninitialized_arguments; i++) {
if (!super_type_args.IsNull()) {
super_type_arg = super_type_args.TypeAt(i);
if (!super_type_arg.IsTypeRef()) {
if (super_type_arg.IsBeingFinalized()) {
ASSERT(super_type_arg.IsType());
CheckRecursiveType(cls, super_type_arg, pending_types);
if (FLAG_trace_type_finalization) {
THR_Print("Creating TypeRef '%s': '%s'\n",
String::Handle(super_type_arg.Name()).ToCString(),
super_type_arg.ToCString());
}
super_type_arg = TypeRef::New(super_type_arg);
super_type_args.SetTypeAt(i, super_type_arg);
} else {
if (!super_type_arg.IsFinalized()) {
super_type_arg =
FinalizeType(cls, super_type_arg, kFinalize, pending_types);
super_type_args.SetTypeAt(i, super_type_arg);
// Note that super_type_arg may still not be finalized here, in
// which case it is a TypeRef to a legal recursive type.
}
}
}
// Instantiate super_type_arg with the current argument vector.
if (!super_type_arg.IsInstantiated()) {
if (FLAG_trace_type_finalization && super_type_arg.IsTypeRef()) {
AbstractType& ref_type =
AbstractType::Handle(TypeRef::Cast(super_type_arg).type());
THR_Print(
"Instantiating TypeRef '%s': '%s'\n"
" instantiator: '%s'\n",
String::Handle(super_type_arg.Name()).ToCString(),
ref_type.ToCString(), arguments.ToCString());
}
// In the typical case of an F-bounded type, the instantiation of the
// super_type_arg from arguments is a fixpoint. Take the shortcut.
// Example: class B<T>; class D<T> extends B<D<T>>;
// While finalizing D<T>, the super type arg D<T> (a typeref) gets
// instantiated from vector [T], yielding itself.
if (super_type_arg.IsTypeRef() &&
(super_type_arg.arguments() == arguments.raw())) {
ASSERT(super_type_arg.IsBeingFinalized());
arguments.SetTypeAt(i, super_type_arg);
continue;
}
super_type_arg = super_type_arg.InstantiateFrom(
arguments, Object::null_type_arguments(), kNoneFree,
instantiation_trail, Heap::kOld);
if (super_type_arg.IsBeingFinalized()) {
// The super_type_arg was instantiated from a type being finalized.
// We need to finish finalizing its type arguments.
ASSERT(super_type_arg.IsTypeRef());
AbstractType& ref_super_type_arg =
AbstractType::Handle(TypeRef::Cast(super_type_arg).type());
if (FLAG_trace_type_finalization) {
THR_Print("Instantiated TypeRef '%s': '%s'\n",
String::Handle(super_type_arg.Name()).ToCString(),
ref_super_type_arg.ToCString());
}
CheckRecursiveType(cls, ref_super_type_arg, pending_types);
pending_types->Add(ref_super_type_arg);
const Class& super_cls =
Class::Handle(ref_super_type_arg.type_class());
const TypeArguments& super_args =
TypeArguments::Handle(ref_super_type_arg.arguments());
// Mark as finalized before finalizing to avoid cycles.
ref_super_type_arg.SetIsFinalized();
// Although the instantiator is different between cls and super_cls,
// we still need to pass the current instantiation trail as to avoid
// divergence. Finalizing the type arguments of super_cls may indeed
// recursively require instantiating the same type_refs already
// present in the trail (see issue #29949).
FinalizeTypeArguments(
super_cls, super_args,
super_cls.NumTypeArguments() - super_cls.NumTypeParameters(),
pending_types, instantiation_trail);
if (FLAG_trace_type_finalization) {
THR_Print("Finalized instantiated TypeRef '%s': '%s'\n",
String::Handle(super_type_arg.Name()).ToCString(),
ref_super_type_arg.ToCString());
}
}
}
}
arguments.SetTypeAt(i, super_type_arg);
}
FinalizeTypeArguments(super_class, arguments, super_offset, pending_types,
instantiation_trail);
}
}
RawAbstractType* ClassFinalizer::FinalizeType(const Class& cls,
const AbstractType& type,
FinalizationKind finalization,
PendingTypes* pending_types) {
// Only the 'root' type of the graph can be canonicalized, after all depending
// types have been bound checked.
ASSERT((pending_types == NULL) || (finalization < kCanonicalize));
if (type.IsFinalized()) {
// Ensure type is canonical if canonicalization is requested.
if ((finalization >= kCanonicalize) && !type.IsCanonical() &&
type.IsType()) {
return type.Canonicalize();
}
return type.raw();
}
ASSERT(finalization >= kFinalize);
if (type.IsTypeRef()) {
// The referenced type will be finalized later by the code that set the
// is_being_finalized mark bit.
return type.raw();
}
// Recursive types must be processed in FinalizeTypeArguments() and cannot be
// encountered here.
ASSERT(!type.IsBeingFinalized());
Zone* zone = Thread::Current()->zone();
if (FLAG_trace_type_finalization) {
THR_Print("Finalizing type '%s' for class '%s'\n",
String::Handle(zone, type.Name()).ToCString(),
String::Handle(zone, cls.Name()).ToCString());
}
if (type.IsTypeParameter()) {
const TypeParameter& type_parameter = TypeParameter::Cast(type);
const Class& parameterized_class =
Class::Handle(zone, type_parameter.parameterized_class());
intptr_t offset;
if (!parameterized_class.IsNull()) {
// The index must reflect the position of this type parameter in the type
// arguments vector of its parameterized class. The offset to add is the
// number of type arguments in the super type, which is equal to the
// difference in number of type arguments and type parameters of the
// parameterized class.
offset = parameterized_class.NumTypeArguments() -
parameterized_class.NumTypeParameters();
} else {
const Function& function =
Function::Handle(zone, type_parameter.parameterized_function());
ASSERT(!function.IsNull());
offset = function.NumParentTypeParameters();
}
type_parameter.set_index(type_parameter.index() + offset);
type_parameter.SetIsFinalized();
if (FLAG_trace_type_finalization) {
THR_Print("Done finalizing type parameter '%s' with index %" Pd "\n",
String::Handle(zone, type_parameter.name()).ToCString(),
type_parameter.index());
}
// We do not canonicalize type parameters.
return type_parameter.raw();
}
// At this point, we can only have a Type.
ASSERT(type.IsType());
// This type is the root type of the type graph if no pending types queue is
// allocated yet.
const bool is_root_type = pending_types == NULL;
if (is_root_type) {
pending_types = new PendingTypes(zone, 4);
}
const intptr_t num_expanded_type_arguments =
ExpandAndFinalizeTypeArguments(cls, type, pending_types);
// Self referencing types may get finalized indirectly.
if (!type.IsFinalized()) {
// If the type is a function type, we also need to finalize the types in its
// signature, i.e. finalize the result type and parameter types of the
// signature function of this function type.
// We do this after marking this type as finalized in order to allow a
// typedef function type to refer to itself via its parameter types and
// result type.
if (type.IsFunctionType()) {
const Type& fun_type = Type::Cast(type);
const Class& scope_class = Class::Handle(zone, fun_type.type_class());
if (scope_class.IsTypedefClass()) {
Function& signature =
Function::Handle(zone, scope_class.signature_function());
if (!scope_class.is_type_finalized()) {
FinalizeSignature(scope_class, signature, finalization);
}
// If the function type is a generic typedef, instantiate its signature
// from its type arguments.
// Example: typedef F<T> = S Function<S>(T x) has uninstantiated
// signature (T x) => S.
// The instantiated signature of F(int) becomes (int x) => S.
// Note that after this step, the signature of the function type is not
// identical to the canonical signature of the typedef class anymore.
if (scope_class.IsGeneric() && !signature.HasInstantiatedSignature()) {
if (FLAG_trace_type_finalization) {
THR_Print("Instantiating signature '%s' of typedef '%s'\n",
String::Handle(zone, signature.Signature()).ToCString(),
String::Handle(zone, fun_type.Name()).ToCString());
}
const TypeArguments& instantiator_type_arguments =
TypeArguments::Handle(zone, fun_type.arguments());
signature = signature.InstantiateSignatureFrom(
instantiator_type_arguments, Object::null_type_arguments(),
kNoneFree, Heap::kOld);
// Note that if instantiator_type_arguments contains type parameters,
// as in F<K>, the signature is still uninstantiated (the typedef type
// parameters were substituted in the signature with typedef type
// arguments). Note also that the function type parameters were not
// modified.
FinalizeSignature(scope_class, signature, finalization);
}
fun_type.set_signature(signature);
} else {
FinalizeSignature(cls, Function::Handle(zone, fun_type.signature()),
finalization);
}
}
if (FLAG_trace_type_finalization) {
THR_Print("Marking type '%s' as finalized for class '%s'\n",
String::Handle(zone, type.Name()).ToCString(),
String::Handle(zone, cls.Name()).ToCString());
}
// Mark the type as finalized.
type.SetIsFinalized();
}
if (FLAG_trace_type_finalization) {
THR_Print("Done finalizing type '%s' with %" Pd " type args: %s\n",
String::Handle(zone, type.Name()).ToCString(),
num_expanded_type_arguments, type.ToCString());
}
if (finalization >= kCanonicalize) {
if (FLAG_trace_type_finalization) {
THR_Print("Canonicalizing type '%s'\n",
String::Handle(zone, type.Name()).ToCString());
AbstractType& canonical_type =
AbstractType::Handle(zone, type.Canonicalize());
THR_Print("Done canonicalizing type '%s'\n",
String::Handle(zone, canonical_type.Name()).ToCString());
return canonical_type.raw();
}
return type.Canonicalize();
} else {
return type.raw();
}
}
void ClassFinalizer::FinalizeSignature(const Class& cls,
const Function& function,
FinalizationKind finalization) {
AbstractType& type = AbstractType::Handle();
AbstractType& finalized_type = AbstractType::Handle();
// Finalize function type parameters and their upper bounds.
const intptr_t num_parent_type_params = function.NumParentTypeParameters();
const intptr_t num_type_params = function.NumTypeParameters();
if (num_type_params > 0) {
TypeParameter& type_param = TypeParameter::Handle();
const TypeArguments& type_params =
TypeArguments::Handle(function.type_parameters());
for (intptr_t i = 0; i < num_type_params; i++) {
type_param ^= type_params.TypeAt(i);
if (!type_param.IsFinalized()) {
type_param.set_index(num_parent_type_params + i);
type_param.SetIsFinalized();
}
type = type_param.bound();
finalized_type = FinalizeType(cls, type, finalization);
if (finalized_type.raw() != type.raw()) {
type_param.set_bound(finalized_type);
}
}
}
// Finalize result type.
type = function.result_type();
finalized_type = FinalizeType(cls, type, finalization);
// The result type may be malformed or malbounded.
if (finalized_type.raw() != type.raw()) {
function.set_result_type(finalized_type);
}
// Finalize formal parameter types.
const intptr_t num_parameters = function.NumParameters();
for (intptr_t i = 0; i < num_parameters; i++) {
type = function.ParameterTypeAt(i);
finalized_type = FinalizeType(cls, type, finalization);
// The parameter type may be malformed or malbounded.
if (type.raw() != finalized_type.raw()) {
function.SetParameterTypeAt(i, finalized_type);
}
}
}
// Finalize the upper bounds of the type parameters of class cls.
void ClassFinalizer::FinalizeUpperBounds(const Class& cls,
FinalizationKind finalization) {
const intptr_t num_type_params = cls.NumTypeParameters();
TypeParameter& type_param = TypeParameter::Handle();
AbstractType& bound = AbstractType::Handle();
const TypeArguments& type_params =
TypeArguments::Handle(cls.type_parameters());
ASSERT((type_params.IsNull() && (num_type_params == 0)) ||
(type_params.Length() == num_type_params));
for (intptr_t i = 0; i < num_type_params; i++) {
type_param ^= type_params.TypeAt(i);
bound = type_param.bound();
if (bound.IsFunctionType()) {
const Function& signature_function =
Function::Handle(Type::Cast(bound).signature());
if (signature_function.IsGeneric()) {
const String& bound_name = String::Handle(bound.UserVisibleName());
const String& type_param_name = String::Handle(type_param.name());
ReportError(cls, bound.token_pos(),
"generic function type '%s' not allowed as bound of "
"class type parameter '%s'",
bound_name.ToCString(), type_param_name.ToCString());
}
}
// Bound may be finalized, but not canonical yet.
if (bound.IsCanonical() || bound.IsBeingFinalized()) {
// A bound involved in F-bounded quantification may form a cycle.
continue;
}
bound = FinalizeType(cls, bound, finalization);
type_param.set_bound(bound);
}
}
#if defined(TARGET_ARCH_X64)
static bool IsPotentialExactGeneric(const AbstractType& type) {
// TODO(dartbug.com/34170) Investigate supporting this for fields with types
// that depend on type parameters of the enclosing class.
if (type.IsType() && !type.IsFunctionType() && !type.IsDartFunctionType() &&
type.IsInstantiated()) {
const Class& cls = Class::Handle(type.type_class());
return cls.IsGeneric() && !cls.IsFutureOrClass();
}
return false;
}
#else
// TODO(dartbug.com/34170) Support other architectures.
static bool IsPotentialExactGeneric(const AbstractType& type) {
return false;
}
#endif
void ClassFinalizer::FinalizeMemberTypes(const Class& cls) {
// Note that getters and setters are explicitly listed as such in the list of
// functions of a class, so we do not need to consider fields as implicitly
// generating getters and setters.
// Most overriding conflicts are only static warnings, i.e. they are not
// reported as compile-time errors by the vm.
// Static warning examples are:
// - a static getter 'v' conflicting with an inherited instance setter 'v='.
// - a static setter 'v=' conflicting with an inherited instance member 'v'.
// - an instance member 'v' conflicting with an accessible static member 'v'
// or 'v=' of a super class (except that an instance method 'v' does not
// conflict with an accessible static setter 'v=' of a super class).
// The compile-time errors we report are:
// - a static member 'v' conflicting with an inherited instance member 'v'.
// - a static setter 'v=' conflicting with an inherited instance setter 'v='.
// - an instance method conflicting with an inherited instance field or
// instance getter.
// - an instance field or instance getter conflicting with an inherited
// instance method.
// Finalize type of fields and check for conflicts in super classes.
Isolate* isolate = Isolate::Current();
Zone* zone = Thread::Current()->zone();
Array& array = Array::Handle(zone, cls.fields());
Field& field = Field::Handle(zone);
AbstractType& type = AbstractType::Handle(zone);
const intptr_t num_fields = array.Length();
const bool track_exactness = isolate->use_field_guards();
for (intptr_t i = 0; i < num_fields; i++) {
field ^= array.At(i);
type = field.type();
type = FinalizeType(cls, type);
field.SetFieldType(type);
if (track_exactness && IsPotentialExactGeneric(type)) {
field.set_static_type_exactness_state(
StaticTypeExactnessState::Uninitialized());
}
}
// Finalize function signatures and check for conflicts in super classes and
// interfaces.
array = cls.functions();
Function& function = Function::Handle(zone);
const intptr_t num_functions = array.Length();
for (intptr_t i = 0; i < num_functions; i++) {
function ^= array.At(i);
FinalizeSignature(cls, function);
if (function.IsSetterFunction() || function.IsImplicitSetterFunction()) {
continue;
}
if (function.is_static()) {
if (function.IsRedirectingFactory()) {
Type& type = Type::Handle(zone, function.RedirectionType());
type ^= FinalizeType(cls, type);
function.SetRedirectionType(type);
}
}
}
}
// For a class used as an interface marks this class and all its superclasses
// implemented.
//
// Does not mark its interfaces implemented because those would already be
// marked as such.
static void MarkImplemented(Zone* zone, const Class& iface) {
if (iface.is_implemented()) {
return;
}
Class& cls = Class::Handle(zone, iface.raw());
AbstractType& type = AbstractType::Handle(zone);
while (!cls.is_implemented()) {
cls.set_is_implemented();
type = cls.super_type();
if (type.IsNull() || type.IsObjectType()) {
break;
}
cls = type.type_class();
}
}
void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
Thread* thread = Thread::Current();
HANDLESCOPE(thread);
cls.EnsureDeclarationLoaded();
if (cls.is_type_finalized()) {
return;
}
if (FLAG_trace_class_finalization) {
THR_Print("Finalize types in %s\n", cls.ToCString());
}
// Finalize super class.
Class& super_class = Class::Handle(cls.SuperClass());
if (!super_class.IsNull()) {
FinalizeTypesInClass(super_class);
}
// Finalize type parameters before finalizing the super type.
FinalizeTypeParameters(cls); // May change super type while applying mixin.
super_class = cls.SuperClass(); // Get again possibly changed super class.
ASSERT(super_class.IsNull() || super_class.is_type_finalized());
FinalizeUpperBounds(cls);
// Finalize super type.
AbstractType& super_type = AbstractType::Handle(cls.super_type());
if (!super_type.IsNull()) {
super_type = FinalizeType(cls, super_type);
cls.set_super_type(super_type);
}
if (cls.IsTypedefClass()) {
Function& signature = Function::Handle(cls.signature_function());
Type& type = Type::Handle(signature.SignatureType());
ASSERT(type.signature() == signature.raw());
ASSERT(type.type_class() == cls.raw());
cls.set_is_type_finalized();
// Finalize the result and parameter types of the signature
// function of this typedef class.
FinalizeSignature(cls, signature); // Does not modify signature type.
ASSERT(signature.SignatureType() == type.raw());
// Finalize the signature type of this typedef.
type ^= FinalizeType(cls, type);
ASSERT(type.type_class() == cls.raw());
// If a different canonical signature type is returned, update the signature
// function of the typedef.
signature = type.signature();
signature.SetSignatureType(type);
cls.set_signature_function(signature);
// Closure instances do not refer to this typedef as their class, so there
// is no need to add this typedef class to the subclasses of _Closure.
ASSERT(super_type.IsNull() || super_type.IsObjectType());
return;
}
// Finalize interface types (but not necessarily interface classes).
Array& interface_types = Array::Handle(cls.interfaces());
AbstractType& interface_type = AbstractType::Handle();
for (intptr_t i = 0; i < interface_types.Length(); i++) {
interface_type ^= interface_types.At(i);
interface_type = FinalizeType(cls, interface_type);
interface_types.SetAt(i, interface_type);
}
cls.set_is_type_finalized();
RegisterClassInHierarchy(thread->zone(), cls);
}
void ClassFinalizer::RegisterClassInHierarchy(Zone* zone, const Class& cls) {
auto& type = AbstractType::Handle(zone, cls.super_type());
auto& other_cls = Class::Handle(zone);
// Add this class to the direct subclasses of the superclass, unless the
// superclass is Object.
if (!type.IsNull() && !type.IsObjectType()) {
other_cls = cls.SuperClass();
ASSERT(!other_cls.IsNull());
other_cls.AddDirectSubclass(cls);
}
// Add this class as an implementor to the implemented interface's type
// classes.
const auto& interfaces = Array::Handle(zone, cls.interfaces());
const intptr_t mixin_index =
cls.is_transformed_mixin_application() ? interfaces.Length() - 1 : -1;
for (intptr_t i = 0; i < interfaces.Length(); ++i) {
type ^= interfaces.At(i);
other_cls = type.type_class();
MarkImplemented(zone, other_cls);
other_cls.AddDirectImplementor(cls, /* is_mixin = */ i == mixin_index);
}
}
void ClassFinalizer::FinalizeClass(const Class& cls) {
ASSERT(cls.is_type_finalized());
if (cls.is_finalized()) {
return;
}
Thread* thread = Thread::Current();
HANDLESCOPE(thread);
if (FLAG_trace_class_finalization) {
THR_Print("Finalize %s\n", cls.ToCString());
}
#if defined(SUPPORT_TIMELINE)
TimelineDurationScope tds(thread, Timeline::GetCompilerStream(),
"FinalizeClass");
if (tds.enabled()) {
tds.SetNumArguments(1);
tds.CopyArgument(0, "class", cls.ToCString());
}
#endif // defined(SUPPORT_TIMELINE)
#if !defined(DART_PRECOMPILED_RUNTIME)
// If loading from a kernel, make sure that the class is fully loaded.
ASSERT(cls.IsTopLevel() || cls.is_declared_in_bytecode() ||
(cls.kernel_offset() > 0));
if (!cls.is_loaded()) {
if (cls.is_declared_in_bytecode()) {
kernel::BytecodeReader::FinishClassLoading(cls);
} else {
kernel::KernelLoader::FinishLoading(cls);
}
if (cls.is_finalized()) {
return;
}
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
if (cls.is_patch()) {
// The fields and functions of a patch class are copied to the
// patched class after parsing. There is nothing to finalize.
ASSERT(Array::Handle(cls.functions()).Length() == 0);
ASSERT(Array::Handle(cls.fields()).Length() == 0);
cls.set_is_finalized();
return;
}
// Ensure super class is finalized.
const Class& super = Class::Handle(cls.SuperClass());
if (!super.IsNull()) {
FinalizeClass(super);
if (cls.is_finalized()) {
return;
}
}
// Mark as loaded and finalized.
cls.Finalize();
// Every class should have at least a constructor, unless it is a top level
// class or a typedef class. The Kernel frontend does not create an implicit
// constructor for abstract classes.
// Moreover, Dart 2 precompiler (TFA) can tree shake all members if unused.
ASSERT(FLAG_precompiled_mode || cls.IsTopLevel() || cls.IsTypedefClass() ||
cls.is_abstract() || (Array::Handle(cls.functions()).Length() > 0));
FinalizeMemberTypes(cls);
// Run additional checks after all types are finalized.
if (FLAG_use_cha_deopt) {
GrowableArray<intptr_t> cids;
CollectFinalizedSuperClasses(cls, &cids);
CollectImmediateSuperInterfaces(cls, &cids);
RemoveCHAOptimizedCode(cls, cids);
}
if (FLAG_use_cha_deopt) {
Zone* zone = thread->zone();
ClassTable* class_table = thread->isolate()->class_table();
auto& interface_class = Class::Handle(zone);
// We scan every interface this [cls] implements and invalidate all CHA code
// which depends on knowing the implementors of that interface.
GrowableArray<intptr_t> cids;
InterfaceFinder finder(zone, class_table, &cids);
finder.FindAllInterfaces(cls);
for (intptr_t j = 0; j < cids.length(); ++j) {
interface_class = class_table->At(cids[j]);
interface_class.DisableCHAImplementorUsers();
}
}
if (cls.is_enum_class()) {
AllocateEnumValues(cls);
}
}
RawError* ClassFinalizer::LoadClassMembers(const Class& cls) {
ASSERT(Thread::Current()->IsMutatorThread());
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
#if !defined(DART_PRECOMPILED_RUNTIME)
cls.EnsureDeclarationLoaded();
#endif
ASSERT(cls.is_type_finalized());
ClassFinalizer::FinalizeClass(cls);
return Error::null();
} else {
return Thread::Current()->StealStickyError();
}
}
// Allocate instances for each enumeration value, and populate the
// static field 'values'.
// By allocating the instances programmatically, we save an implicit final
// getter function object for each enumeration value and for the
// values field. We also don't have to generate the code for these getters
// from thin air (no source code is available).
void ClassFinalizer::AllocateEnumValues(const Class& enum_cls) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
const Field& index_field =
Field::Handle(zone, enum_cls.LookupInstanceField(Symbols::Index()));
ASSERT(!index_field.IsNull());
const Field& name_field = Field::Handle(
zone, enum_cls.LookupInstanceFieldAllowPrivate(Symbols::_name()));
ASSERT(!name_field.IsNull());
const String& enum_name = String::Handle(zone, enum_cls.ScrubbedName());
const Array& fields = Array::Handle(zone, enum_cls.fields());
Field& field = Field::Handle(zone);
Instance& enum_value = Instance::Handle(zone);
String& enum_ident = String::Handle(zone);
enum_ident =
Symbols::FromConcat(thread, Symbols::_DeletedEnumPrefix(), enum_name);
enum_value = Instance::New(enum_cls, Heap::kOld);
enum_value.SetField(index_field, Smi::Handle(zone, Smi::New(-1)));
enum_value.SetField(name_field, enum_ident);
const char* error_msg = NULL;
enum_value = enum_value.CheckAndCanonicalize(thread, &error_msg);
ASSERT(!enum_value.IsNull());
ASSERT(enum_value.IsCanonical());
const Field& sentinel = Field::Handle(
zone, enum_cls.LookupStaticField(Symbols::_DeletedEnumSentinel()));
ASSERT(!sentinel.IsNull());
sentinel.SetStaticValue(enum_value, true);
sentinel.RecordStore(enum_value);
const GrowableObjectArray& pending_unevaluated_const_fields =
GrowableObjectArray::Handle(zone,
thread->isolate()
->object_store()
->pending_unevaluated_const_fields());
ASSERT(enum_cls.is_declared_in_bytecode() || enum_cls.kernel_offset() > 0);
Error& error = Error::Handle(zone);
for (intptr_t i = 0; i < fields.Length(); i++) {
field = Field::RawCast(fields.At(i));
if (!field.is_static() || !field.is_const() ||
(sentinel.raw() == field.raw())) {
continue;
}
// The eager evaluation of the enum values is required for hot-reload (see
// commit e3ecc87). However, while busy loading the constant table, we
// need to postpone this evaluation until table is done.
if (!FLAG_precompiled_mode) {
if (field.IsUninitialized()) {
if (pending_unevaluated_const_fields.IsNull()) {
// Evaluate right away.
error = field.Initialize();
if (!error.IsNull()) {
ReportError(error);
}
} else {
// Postpone evaluation until we have a constant table.
pending_unevaluated_const_fields.Add(field);
}
}
}
}
}
void ClassFinalizer::PrintClassInformation(const Class& cls) {
Thread* thread = Thread::Current();
HANDLESCOPE(thread);
const String& class_name = String::Handle(cls.Name());
THR_Print("class '%s'", class_name.ToCString());
const Library& library = Library::Handle(cls.library());
if (!library.IsNull()) {
THR_Print(" library '%s%s':\n", String::Handle(library.url()).ToCString(),
String::Handle(library.private_key()).ToCString());
} else {
THR_Print(" (null library):\n");
}
const AbstractType& super_type = AbstractType::Handle(cls.super_type());
if (super_type.IsNull()) {
THR_Print(" Super: NULL");
} else {
const String& super_name = String::Handle(super_type.Name());
THR_Print(" Super: %s", super_name.ToCString());
}
const Array& interfaces_array = Array::Handle(cls.interfaces());
if (interfaces_array.Length() > 0) {
THR_Print("; interfaces: ");
AbstractType& interface = AbstractType::Handle();
intptr_t len = interfaces_array.Length();
for (intptr_t i = 0; i < len; i++) {
interface ^= interfaces_array.At(i);
THR_Print(" %s ", interface.ToCString());
}
}
THR_Print("\n");
const Array& functions_array = Array::Handle(cls.functions());
Function& function = Function::Handle();
intptr_t len = functions_array.Length();
for (intptr_t i = 0; i < len; i++) {
function ^= functions_array.At(i);
THR_Print(" %s\n", function.ToCString());
}
const Array& fields_array = Array::Handle(cls.fields());
Field& field = Field::Handle();
len = fields_array.Length();
for (intptr_t i = 0; i < len; i++) {
field ^= fields_array.At(i);
THR_Print(" %s\n", field.ToCString());
}
}
void ClassFinalizer::ReportError(const Error& error) {
Report::LongJump(error);
UNREACHABLE();
}
void ClassFinalizer::ReportErrors(const Error& prev_error,
const Class& cls,
TokenPosition token_pos,
const char* format,
...) {
va_list args;
va_start(args, format);
const Script& script = Script::Handle(cls.script());
Report::LongJumpV(prev_error, script, token_pos, format, args);
va_end(args);
UNREACHABLE();
}
void ClassFinalizer::ReportError(const Class& cls,
TokenPosition token_pos,
const char* format,
...) {
va_list args;
va_start(args, format);
const Script& script = Script::Handle(cls.script());
Report::MessageV(Report::kError, script, token_pos, Report::AtLocation,
format, args);
va_end(args);
UNREACHABLE();
}
void ClassFinalizer::VerifyImplicitFieldOffsets() {
#ifdef DEBUG
Thread* thread = Thread::Current();
Isolate* isolate = thread->isolate();
if (isolate->obfuscate()) {
// Field names are obfuscated.
return;
}
Zone* zone = thread->zone();
const ClassTable& class_table = *(isolate->class_table());
Class& cls = Class::Handle(zone);
Array& fields_array = Array::Handle(zone);
Field& field = Field::Handle(zone);
String& name = String::Handle(zone);
String& expected_name = String::Handle(zone);
Error& error = Error::Handle(zone);
TypeParameter& type_param = TypeParameter::Handle(zone);
// Now verify field offsets of '_ByteBuffer' class.
cls = class_table.At(kByteBufferCid);
error = cls.EnsureIsFinalized(thread);
ASSERT(error.IsNull());
fields_array ^= cls.fields();
ASSERT(fields_array.Length() == ByteBuffer::NumberOfFields());
field ^= fields_array.At(0);
ASSERT(field.Offset() == ByteBuffer::data_offset());
name ^= field.name();
expected_name ^= String::New("_data");
ASSERT(String::EqualsIgnoringPrivateKey(name, expected_name));
// Now verify field offsets of 'Pointer' class.
cls = class_table.At(kFfiPointerCid);
error = cls.EnsureIsFinalized(thread);
ASSERT(error.IsNull());
ASSERT(cls.NumTypeParameters() == 1);
type_param ^= TypeParameter::RawCast(
TypeArguments::Handle(cls.type_parameters()).TypeAt(0));
ASSERT(Pointer::kNativeTypeArgPos == type_param.index());
#endif
}
void ClassFinalizer::SortClasses() {
Thread* T = Thread::Current();
Zone* Z = T->zone();
Isolate* I = T->isolate();
// Prevent background compiler from adding deferred classes or canonicalizing
// new types while classes are being sorted and type hashes are modified.
BackgroundCompiler::Stop(I);
ClassTable* table = I->class_table();
intptr_t num_cids = table->NumCids();
intptr_t* old_to_new_cid = new intptr_t[num_cids];
for (intptr_t cid = 0; cid < kNumPredefinedCids; cid++) {
old_to_new_cid[cid] = cid; // The predefined classes cannot change cids.
}
for (intptr_t cid = kNumPredefinedCids; cid < num_cids; cid++) {
old_to_new_cid[cid] = -1;
}
intptr_t next_new_cid = kNumPredefinedCids;
GrowableArray<intptr_t> dfs_stack;
Class& cls = Class::Handle(Z);
GrowableObjectArray& subclasses = GrowableObjectArray::Handle(Z);
// Object doesn't use its subclasses list.
for (intptr_t cid = kNumPredefinedCids; cid < num_cids; cid++) {
if (!table->HasValidClassAt(cid)) {
continue;
}
cls = table->At(cid);
if (cls.is_patch() || !cls.is_declaration_loaded()) {
continue;
}
if (cls.SuperClass() == I->object_store()->object_class()) {
dfs_stack.Add(cid);
}
}
while (dfs_stack.length() > 0) {
intptr_t cid = dfs_stack.RemoveLast();
ASSERT(table->HasValidClassAt(cid));
cls = table->At(cid);
ASSERT(!cls.IsNull());
if (old_to_new_cid[cid] == -1) {
old_to_new_cid[cid] = next_new_cid++;
if (FLAG_trace_class_finalization) {
THR_Print("%" Pd ": %s, was %" Pd "\n", old_to_new_cid[cid],
cls.ToCString(), cid);
}
}
subclasses = cls.direct_subclasses();
if (!subclasses.IsNull()) {
for (intptr_t i = 0; i < subclasses.Length(); i++) {
cls ^= subclasses.At(i);
ASSERT(!cls.IsNull());
dfs_stack.Add(cls.id());
}
}
}
// Top-level classes, typedefs, patch classes, etc.
for (intptr_t cid = kNumPredefinedCids; cid < num_cids; cid++) {
if (old_to_new_cid[cid] == -1) {
old_to_new_cid[cid] = next_new_cid++;
if (FLAG_trace_class_finalization && table->HasValidClassAt(cid)) {
cls = table->At(cid);
THR_Print("%" Pd ": %s, was %" Pd "\n", old_to_new_cid[cid],
cls.ToCString(), cid);
}
}
}
ASSERT(next_new_cid == num_cids);
RemapClassIds(old_to_new_cid);
delete[] old_to_new_cid;
RehashTypes(); // Types use cid's as part of their hashes.
I->RehashConstants(); // Const objects use cid's as part of their hashes.
}
class CidRewriteVisitor : public ObjectVisitor {
public:
explicit CidRewriteVisitor(intptr_t* old_to_new_cids)
: old_to_new_cids_(old_to_new_cids) {}
intptr_t Map(intptr_t cid) {
ASSERT(cid != -1);
return old_to_new_cids_[cid];
}
void VisitObject(RawObject* obj) {
if (obj->IsClass()) {
RawClass* cls = Class::RawCast(obj);
cls->ptr()->id_ = Map(cls->ptr()->id_);
} else if (obj->IsField()) {
RawField* field = Field::RawCast(obj);
field->ptr()->guarded_cid_ = Map(field->ptr()->guarded_cid_);
field->ptr()->is_nullable_ = Map(field->ptr()->is_nullable_);
} else if (obj->IsTypeParameter()) {
RawTypeParameter* param = TypeParameter::RawCast(obj);
param->ptr()->parameterized_class_id_ =
Map(param->ptr()->parameterized_class_id_);
} else if (obj->IsType()) {
RawType* type = Type::RawCast(obj);
RawObject* id = type->ptr()->type_class_id_;
if (!id->IsHeapObject()) {
type->ptr()->type_class_id_ =
Smi::New(Map(Smi::Value(Smi::RawCast(id))));
}
} else {
intptr_t old_cid = obj->GetClassId();
intptr_t new_cid = Map(old_cid);
if (old_cid != new_cid) {
// Don't touch objects that are unchanged. In particular, Instructions,
// which are write-protected.
obj->SetClassId(new_cid);
}
}
}
private:
intptr_t* old_to_new_cids_;
};
void ClassFinalizer::RemapClassIds(intptr_t* old_to_new_cid) {
Thread* T = Thread::Current();
Isolate* I = T->isolate();
// Code, ICData, allocation stubs have now-invalid cids.
ClearAllCode();
{
HeapIterationScope his(T);
I->set_remapping_cids(true);
// Update the class table. Do it before rewriting cids in headers, as the
// heap walkers load an object's size *after* calling the visitor.
I->class_table()->Remap(old_to_new_cid);
// Rewrite cids in headers and cids in Classes, Fields, Types and
// TypeParameters.
{
CidRewriteVisitor visitor(old_to_new_cid);
I->heap()->VisitObjects(&visitor);
}
I->set_remapping_cids(false);
}
#if defined(DEBUG)
I->class_table()->Validate();
I->heap()->Verify();
#endif
}
// Clears the cached canonicalized hash codes for all instances which directly
// (or indirectly) depend on class ids.
//
// In the Dart VM heap the following instances directly use cids for the
// computation of canonical hash codes:
//
// * RawType (due to RawType::type_class_id_)
// * RawTypeParameter (due to RawTypeParameter::parameterized_class_id_)
//
// The following instances use cids for the computation of canonical hash codes
// indirectly:
//
// * RawTypeRef (due to RawTypeRef::type_->type_class_id)
// * RawType (due to RawType::signature_'s result/parameter types)
// * RawTypeArguments (due to type references)
// * RawInstance (due to instance fields)
// * RawArray (due to type arguments & array entries)
//
// Caching of the canonical hash codes happens for:
//
// * RawType::hash_
// * RawTypeParameter::hash_
// * RawTypeArguments::hash_
//
// No caching of canonical hash codes (i.e. it gets re-computed every time)
// happens for:
//
// * RawTypeRef (computed via RawTypeRef::type_->type_class_id)
// * RawInstance (computed via size & fields)
// * RawArray (computed via type arguments & array entries)
//
// Usages of canonical hash codes are:
//
// * ObjectStore::canonical_types()
// * ObjectStore::canonical_type_arguments()
// * Class::constants()
//
class ClearTypeHashVisitor : public ObjectVisitor {
public:
explicit ClearTypeHashVisitor(Zone* zone)
: type_param_(TypeParameter::Handle(zone)),
type_(Type::Handle(zone)),
type_args_(TypeArguments::Handle(zone)) {}
void VisitObject(RawObject* obj) {
if (obj->IsTypeParameter()) {
type_param_ ^= obj;
type_param_.SetHash(0);
} else if (obj->IsType()) {
type_ ^= obj;
type_.SetHash(0);
} else if (obj->IsTypeArguments()) {
type_args_ ^= obj;
type_args_.SetHash(0);
}
}
private:
TypeParameter& type_param_;
Type& type_;
TypeArguments& type_args_;
};
void ClassFinalizer::RehashTypes() {
Thread* T = Thread::Current();
Zone* Z = T->zone();
Isolate* I = T->isolate();
// Clear all cached hash values.
{
HeapIterationScope his(T);
ClearTypeHashVisitor visitor(Z);
I->heap()->VisitObjects(&visitor);
}
// Rehash the canonical Types table.
ObjectStore* object_store = I->object_store();
GrowableObjectArray& types =
GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
Array& types_array = Array::Handle(Z);
Type& type = Type::Handle(Z);
{
CanonicalTypeSet types_table(Z, object_store->canonical_types());
types_array = HashTables::ToArray(types_table, false);
for (intptr_t i = 0; i < types_array.Length(); i++) {
type ^= types_array.At(i);
types.Add(type);
}
types_table.Release();
}
intptr_t dict_size = Utils::RoundUpToPowerOfTwo(types.Length() * 4 / 3);
types_array = HashTables::New<CanonicalTypeSet>(dict_size, Heap::kOld);
CanonicalTypeSet types_table(Z, types_array.raw());
for (intptr_t i = 0; i < types.Length(); i++) {
type ^= types.At(i);
bool present = types_table.Insert(type);
ASSERT(!present || type.IsRecursive());
}
object_store->set_canonical_types(types_table.Release());
// Rehash the canonical TypeArguments table.
Array& typeargs_array = Array::Handle(Z);
GrowableObjectArray& typeargs =
GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
TypeArguments& typearg = TypeArguments::Handle(Z);
{
CanonicalTypeArgumentsSet typeargs_table(
Z, object_store->canonical_type_arguments());
typeargs_array = HashTables::ToArray(typeargs_table, false);
for (intptr_t i = 0; i < typeargs_array.Length(); i++) {
typearg ^= typeargs_array.At(i);
typeargs.Add(typearg);
}
typeargs_table.Release();
}
// The canonical constant tables use canonical hashcodes which can change
// due to cid-renumbering.
I->RehashConstants();
dict_size = Utils::RoundUpToPowerOfTwo(typeargs.Length() * 4 / 3);
typeargs_array =
HashTables::New<CanonicalTypeArgumentsSet>(dict_size, Heap::kOld);
CanonicalTypeArgumentsSet typeargs_table(Z, typeargs_array.raw());
for (intptr_t i = 0; i < typeargs.Length(); i++) {
typearg ^= typeargs.At(i);
bool present = typeargs_table.Insert(typearg);
ASSERT(!present || typearg.IsRecursive());
}
object_store->set_canonical_type_arguments(typeargs_table.Release());
}
void ClassFinalizer::ClearAllCode(bool including_nonchanging_cids) {
class ClearCodeFunctionVisitor : public FunctionVisitor {
void Visit(const Function& function) {
function.ClearCode();
function.ClearICDataArray();
}
};
ClearCodeFunctionVisitor function_visitor;
ProgramVisitor::VisitFunctions(&function_visitor);
class ClearCodeClassVisitor : public ClassVisitor {
public:
explicit ClearCodeClassVisitor(bool force) : force_(force) {}
void Visit(const Class& cls) {
if (force_ || cls.id() >= kNumPredefinedCids) {
cls.DisableAllocationStub();
}
}
private:
bool force_;
};
ClearCodeClassVisitor class_visitor(including_nonchanging_cids);
ProgramVisitor::VisitClasses(&class_visitor);
// Apart from normal function code and allocation stubs we have two global
// code objects to clear.
if (including_nonchanging_cids) {
auto thread = Thread::Current();
auto object_store = thread->isolate()->object_store();
auto& null_code = Code::Handle(thread->zone());
object_store->set_build_method_extractor_code(null_code);
auto& miss_function = Function::Handle(
thread->zone(), object_store->megamorphic_miss_function());
miss_function.ClearCode();
object_store->SetMegamorphicMissHandler(null_code, miss_function);
}
}
} // namespace dart