mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 00:20:56 +00:00
f98993384f
Review URL: http://codereview.chromium.org//8773026 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@2133 260f80e4-7a28-3924-810f-c04153c831b5
1304 lines
53 KiB
C++
1304 lines
53 KiB
C++
// Copyright (c) 2011, 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/flags.h"
|
|
#include "vm/heap.h"
|
|
#include "vm/isolate.h"
|
|
#include "vm/longjump.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/parser.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.");
|
|
DEFINE_FLAG(bool, verify_implements, false,
|
|
"Verify that all classes implement their interface.");
|
|
DECLARE_FLAG(bool, enable_type_checks);
|
|
DECLARE_FLAG(bool, silent_warnings);
|
|
DECLARE_FLAG(bool, warning_as_error);
|
|
|
|
void ClassFinalizer::AddPendingClasses(
|
|
const GrowableArray<const Class*>& classes) {
|
|
if (!classes.is_empty()) {
|
|
ObjectStore* object_store = Isolate::Current()->object_store();
|
|
const Array& old_array = Array::Handle(object_store->pending_classes());
|
|
const intptr_t old_length = old_array.Length();
|
|
const int new_length = old_length + classes.length();
|
|
const Array& new_array = Array::Handle(Array::Grow(old_array, new_length));
|
|
// Add new classes.
|
|
for (int i = 0; i < classes.length(); i++) {
|
|
new_array.SetAt(i + old_length, *classes[i]);
|
|
}
|
|
object_store->set_pending_classes(new_array);
|
|
}
|
|
}
|
|
|
|
|
|
bool ClassFinalizer::AllClassesFinalized() {
|
|
ObjectStore* object_store = Isolate::Current()->object_store();
|
|
const Array& classes = Array::Handle(object_store->pending_classes());
|
|
return classes.Length() == 0;
|
|
}
|
|
|
|
|
|
// Class finalization occurs:
|
|
// a) when bootstrap process completes (VerifyBootstrapClasses).
|
|
// b) after the user classes are loaded (dart_api).
|
|
bool ClassFinalizer::FinalizePendingClasses(bool generating_snapshot) {
|
|
bool retval = true;
|
|
Isolate* isolate = Isolate::Current();
|
|
ASSERT(isolate != NULL);
|
|
ObjectStore* object_store = isolate->object_store();
|
|
const String& error = String::Handle(object_store->sticky_error());
|
|
if (!error.IsNull()) {
|
|
return false;
|
|
}
|
|
LongJump* base = isolate->long_jump_base();
|
|
LongJump jump;
|
|
isolate->set_long_jump_base(&jump);
|
|
if (setjmp(*jump.Set()) == 0) {
|
|
const Array& class_array = Array::Handle(object_store->pending_classes());
|
|
ASSERT(!class_array.IsNull());
|
|
Class& cls = Class::Handle();
|
|
// First resolve all superclasses.
|
|
for (intptr_t i = 0; i < class_array.Length(); i++) {
|
|
cls ^= class_array.At(i);
|
|
if (FLAG_trace_class_finalization) {
|
|
OS::Print("Resolving super and default: %s\n", cls.ToCString());
|
|
}
|
|
ResolveSuperType(cls);
|
|
if (cls.is_interface()) {
|
|
ResolveFactoryClass(cls);
|
|
}
|
|
}
|
|
// Finalize all classes.
|
|
for (intptr_t i = 0; i < class_array.Length(); i++) {
|
|
cls ^= class_array.At(i);
|
|
FinalizeClass(cls, generating_snapshot);
|
|
}
|
|
if (FLAG_print_classes) {
|
|
for (intptr_t i = 0; i < class_array.Length(); i++) {
|
|
cls ^= class_array.At(i);
|
|
PrintClassInformation(cls);
|
|
}
|
|
}
|
|
if (FLAG_verify_implements) {
|
|
for (intptr_t i = 0; i < class_array.Length(); i++) {
|
|
cls ^= class_array.At(i);
|
|
if (!cls.is_interface()) {
|
|
VerifyClassImplements(cls);
|
|
}
|
|
}
|
|
}
|
|
// Clear pending classes array.
|
|
object_store->set_pending_classes(Array::Handle(Array::Empty()));
|
|
|
|
// Check to ensure there are no duplicate definitions in the library
|
|
// hierarchy.
|
|
const String& str = String::Handle(Library::CheckForDuplicateDefinition());
|
|
if (!str.IsNull()) {
|
|
ReportError("Duplicate definition : %s\n", str.ToCString());
|
|
}
|
|
} else {
|
|
retval = false;
|
|
}
|
|
isolate->set_long_jump_base(base);
|
|
return retval;
|
|
}
|
|
|
|
|
|
#if defined (DEBUG)
|
|
// Adds all interfaces of cls into 'collected'. Duplicate entries may occur.
|
|
// No cycles are allowed.
|
|
void ClassFinalizer::CollectInterfaces(const Class& cls,
|
|
GrowableArray<const Class*>* collected) {
|
|
const Array& interface_array = Array::ZoneHandle(cls.interfaces());
|
|
for (intptr_t i = 0; i < interface_array.Length(); i++) {
|
|
AbstractType& interface = AbstractType::Handle();
|
|
interface ^= interface_array.At(i);
|
|
const Class& interface_class = Class::ZoneHandle(interface.type_class());
|
|
collected->Add(&interface_class);
|
|
CollectInterfaces(interface_class, collected);
|
|
}
|
|
}
|
|
|
|
|
|
// Collect all interfaces of the class 'cls' and check that every function
|
|
// defined in each interface can be found in the class.
|
|
// No need to check instance fields since they have been turned into
|
|
// getters/setters.
|
|
void ClassFinalizer::VerifyClassImplements(const Class& cls) {
|
|
ASSERT(!cls.is_interface());
|
|
GrowableArray<const Class*> interfaces;
|
|
CollectInterfaces(cls, &interfaces);
|
|
const String& class_name = String::Handle(cls.Name());
|
|
for (int i = 0; i < interfaces.length(); i++) {
|
|
const String& interface_name = String::Handle(interfaces[i]->Name());
|
|
const Array& interface_functions =
|
|
Array::Handle(interfaces[i]->functions());
|
|
for (intptr_t f = 0; f < interface_functions.Length(); f++) {
|
|
Function& interface_function = Function::Handle();
|
|
interface_function ^= interface_functions.At(f);
|
|
const String& function_name = String::Handle(interface_function.name());
|
|
// Check for constructor/factory.
|
|
if (function_name.StartsWith(interface_name)) {
|
|
// TODO(srdjan): convert 'InterfaceName.' to 'ClassName.' and check.
|
|
continue;
|
|
}
|
|
if (interface_function.kind() == RawFunction::kConstImplicitGetter) {
|
|
// This interface constants are not overridable.
|
|
continue;
|
|
}
|
|
// Lookup function in 'cls' and all its super classes.
|
|
Class& test_class = Class::Handle(cls.raw());
|
|
Function& class_function =
|
|
Function::Handle(test_class.LookupDynamicFunction(function_name));
|
|
while (class_function.IsNull()) {
|
|
test_class = test_class.SuperClass();
|
|
if (test_class.IsNull()) break;
|
|
class_function = test_class.LookupDynamicFunction(function_name);
|
|
}
|
|
if (class_function.IsNull()) {
|
|
OS::Print("%s implements '%s' missing: '%s'\n",
|
|
class_name.ToCString(),
|
|
interface_name.ToCString(),
|
|
function_name.ToCString());
|
|
} else if (class_function.IsSubtypeOf(TypeArguments::Handle(),
|
|
interface_function,
|
|
TypeArguments::Handle())) {
|
|
OS::Print("The type of instance method '%s' in class '%s' is not a "
|
|
"subtype of the type of '%s' in interface '%s'\n",
|
|
function_name.ToCString(),
|
|
class_name.ToCString(),
|
|
function_name.ToCString(),
|
|
interface_name.ToCString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
|
|
void ClassFinalizer::VerifyClassImplements(const Class& cls) {}
|
|
|
|
#endif
|
|
|
|
|
|
void ClassFinalizer::VerifyBootstrapClasses() {
|
|
if (FLAG_trace_class_finalization) {
|
|
OS::Print("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->smi_class();
|
|
ASSERT(Smi::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->four_byte_string_class();
|
|
ASSERT(FourByteString::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->external_four_byte_string_class();
|
|
ASSERT(ExternalFourByteString::InstanceSize() == cls.instance_size());
|
|
cls = object_store->double_class();
|
|
ASSERT(Double::InstanceSize() == cls.instance_size());
|
|
cls = object_store->mint_class();
|
|
ASSERT(Mint::InstanceSize() == cls.instance_size());
|
|
cls = object_store->bigint_class();
|
|
ASSERT(Bigint::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(Array::InstanceSize() == cls.instance_size());
|
|
cls = object_store->byte_buffer_class();
|
|
ASSERT(ByteBuffer::InstanceSize() == cls.instance_size());
|
|
#endif // defined(DEBUG)
|
|
|
|
// Remember the currently pending classes.
|
|
const Array& class_array = Array::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(Array::Handle(cls.fields()).Length() == 0);
|
|
}
|
|
}
|
|
|
|
// Finalize classes that aren't pre-finalized by Object::Init().
|
|
if (!FinalizePendingClasses()) {
|
|
// TODO(srdjan): Exit like a real VM instead.
|
|
const String& err = String::Handle(object_store->sticky_error());
|
|
OS::PrintErr("Could not verify bootstrap classes : %s\n", err.ToCString());
|
|
OS::Exit(255);
|
|
}
|
|
if (FLAG_trace_class_finalization) {
|
|
OS::Print("VerifyBootstrapClasses END.\n");
|
|
}
|
|
Isolate::Current()->heap()->Verify();
|
|
}
|
|
|
|
|
|
// Resolve unresolved_class in the library of cls.
|
|
RawClass* ClassFinalizer::ResolveClass(
|
|
const Class& cls, const UnresolvedClass& unresolved_class) {
|
|
Library& lib = Library::Handle();
|
|
if (unresolved_class.qualifier() == String::null()) {
|
|
lib = cls.library();
|
|
} else {
|
|
const String& qualifier = String::Handle(unresolved_class.qualifier());
|
|
LibraryPrefix& lib_prefix = LibraryPrefix::Handle();
|
|
lib_prefix = cls.LookupLibraryPrefix(qualifier);
|
|
if (lib_prefix.IsNull()) {
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, unresolved_class.token_index(),
|
|
"cannot resolve library prefix '%s' from '%s'.\n",
|
|
String::Handle(unresolved_class.Name()).ToCString(),
|
|
String::Handle(cls.Name()).ToCString());
|
|
}
|
|
lib = lib_prefix.library();
|
|
}
|
|
ASSERT(!lib.IsNull());
|
|
const String& class_name = String::Handle(unresolved_class.ident());
|
|
const Class& resolved_class = Class::Handle(lib.LookupClass(class_name));
|
|
if (resolved_class.IsNull()) {
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, unresolved_class.token_index(),
|
|
"cannot resolve class name '%s' from '%s'.\n",
|
|
String::Handle(unresolved_class.Name()).ToCString(),
|
|
String::Handle(cls.Name()).ToCString());
|
|
}
|
|
return resolved_class.raw();
|
|
}
|
|
|
|
|
|
// Resolve unresolved supertype (String -> Class).
|
|
void ClassFinalizer::ResolveSuperType(const Class& cls) {
|
|
if (cls.is_finalized()) {
|
|
return;
|
|
}
|
|
Type& super_type = Type::Handle(cls.super_type());
|
|
if (super_type.IsNull()) {
|
|
return;
|
|
}
|
|
// Resolve failures lead to a longjmp.
|
|
ResolveType(cls, super_type);
|
|
const Class& super_class = Class::Handle(super_type.type_class());
|
|
if (cls.is_interface() != super_class.is_interface()) {
|
|
String& class_name = String::Handle(cls.Name());
|
|
String& super_class_name = String::Handle(super_class.Name());
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, -1,
|
|
"class '%s' and superclass '%s' are not "
|
|
"both classes or both interfaces.\n",
|
|
class_name.ToCString(),
|
|
super_class_name.ToCString());
|
|
}
|
|
// If cls belongs to core lib or to core lib's implementation, restrictions
|
|
// about allowed interfaces are lifted.
|
|
if ((cls.library() != Library::CoreLibrary()) &&
|
|
(cls.library() != Library::CoreImplLibrary())) {
|
|
// Prevent extending core implementation classes Bool, Double, ObjectArray,
|
|
// ImmutableArray, GrowableObjectArray, IntegerImplementation, Smi, Mint,
|
|
// BigInt, OneByteString, TwoByteString, FourByteString.
|
|
ObjectStore* object_store = Isolate::Current()->object_store();
|
|
const Library& core_impl_lib = Library::Handle(Library::CoreImplLibrary());
|
|
const String& integer_implementation_name =
|
|
String::Handle(String::NewSymbol("IntegerImplementation"));
|
|
const Class& integer_implementation_class =
|
|
Class::Handle(core_impl_lib.LookupClass(integer_implementation_name));
|
|
const String& growable_object_array_name =
|
|
String::Handle(String::NewSymbol("GrowableObjectArray"));
|
|
const Class& growable_object_array_class =
|
|
Class::Handle(core_impl_lib.LookupClass(growable_object_array_name));
|
|
if ((super_class.raw() == object_store->bool_class()) ||
|
|
(super_class.raw() == object_store->double_class()) ||
|
|
(super_class.raw() == object_store->array_class()) ||
|
|
(super_class.raw() == object_store->immutable_array_class()) ||
|
|
(super_class.raw() == growable_object_array_class.raw()) ||
|
|
(super_class.raw() == object_store->byte_buffer_class()) ||
|
|
(super_class.raw() == integer_implementation_class.raw()) ||
|
|
(super_class.raw() == object_store->smi_class()) ||
|
|
(super_class.raw() == object_store->mint_class()) ||
|
|
(super_class.raw() == object_store->bigint_class()) ||
|
|
(super_class.raw() == object_store->one_byte_string_class()) ||
|
|
(super_class.raw() == object_store->two_byte_string_class()) ||
|
|
(super_class.raw() == object_store->four_byte_string_class())) {
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, -1,
|
|
"'%s' is not allowed to extend '%s'\n",
|
|
String::Handle(cls.Name()).ToCString(),
|
|
String::Handle(super_class.Name()).ToCString());
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
void ClassFinalizer::ResolveFactoryClass(const Class& interface) {
|
|
ASSERT(interface.is_interface());
|
|
if (interface.is_finalized() ||
|
|
!interface.HasFactoryClass() ||
|
|
interface.HasResolvedFactoryClass()) {
|
|
return;
|
|
}
|
|
const UnresolvedClass& unresolved_factory_class =
|
|
UnresolvedClass::Handle(interface.UnresolvedFactoryClass());
|
|
|
|
// Lookup the factory class.
|
|
const Class& factory_class =
|
|
Class::Handle(ResolveClass(interface, unresolved_factory_class));
|
|
ASSERT(!factory_class.IsNull());
|
|
if (factory_class.is_interface()) {
|
|
const String& interface_name = String::Handle(interface.Name());
|
|
const String& factory_name = String::Handle(factory_class.Name());
|
|
const Script& script = Script::Handle(interface.script());
|
|
ReportError(script, unresolved_factory_class.token_index(),
|
|
"factory clause of interface '%s' names non-class '%s'.\n",
|
|
interface_name.ToCString(),
|
|
factory_name.ToCString());
|
|
}
|
|
interface.set_factory_class(factory_class);
|
|
// Check that the type parameter lists are identical.
|
|
const Class& factory_signature_class = Class::Handle(
|
|
unresolved_factory_class.factory_signature_class());
|
|
ASSERT(!factory_signature_class.IsNull());
|
|
ResolveAndFinalizeUpperBounds(factory_class);
|
|
ResolveAndFinalizeUpperBounds(factory_signature_class);
|
|
const intptr_t num_type_params = factory_signature_class.NumTypeParameters();
|
|
bool mismatch = factory_class.NumTypeParameters() != num_type_params;
|
|
if (mismatch && (num_type_params == 0)) {
|
|
// TODO(regis): For now, and until the core lib is fixed, we accept a
|
|
// factory clause with a class missing its list of type parameters.
|
|
// See bug 5408808.
|
|
const String& interface_name = String::Handle(interface.Name());
|
|
const String& factory_name = String::Handle(factory_class.Name());
|
|
const Script& script = Script::Handle(interface.script());
|
|
ReportWarning(script, unresolved_factory_class.token_index(),
|
|
"class '%s' in factory clause of interface '%s' is "
|
|
"missing its type parameter list.\n",
|
|
factory_name.ToCString(),
|
|
interface_name.ToCString());
|
|
return;
|
|
}
|
|
String& expected_type_name = String::Handle();
|
|
String& actual_type_name = String::Handle();
|
|
AbstractType& expected_type_extends = AbstractType::Handle();
|
|
AbstractType& actual_type_extends = AbstractType::Handle();
|
|
const Array& expected_type_names =
|
|
Array::Handle(factory_signature_class.type_parameters());
|
|
const Array& actual_type_names =
|
|
Array::Handle(factory_class.type_parameters());
|
|
const TypeArguments& expected_extends_array =
|
|
TypeArguments::Handle(factory_signature_class.type_parameter_extends());
|
|
const TypeArguments& actual_extends_array =
|
|
TypeArguments::Handle(factory_class.type_parameter_extends());
|
|
for (intptr_t i = 0; !mismatch && (i < num_type_params); i++) {
|
|
expected_type_name ^= expected_type_names.At(i);
|
|
actual_type_name ^= actual_type_names.At(i);
|
|
expected_type_extends = expected_extends_array.TypeAt(i);
|
|
actual_type_extends = actual_extends_array.TypeAt(i);
|
|
if (!expected_type_name.Equals(actual_type_name) ||
|
|
!expected_type_extends.Equals(actual_type_extends)) {
|
|
mismatch = true;
|
|
}
|
|
}
|
|
if (mismatch) {
|
|
const String& interface_name = String::Handle(interface.Name());
|
|
const String& factory_name = String::Handle(factory_class.Name());
|
|
// TODO(regis): Report the filename and position as well.
|
|
const Script& script = Script::Handle(interface.script());
|
|
ReportError(script, unresolved_factory_class.token_index(),
|
|
"mismatch in number or names of type parameters between "
|
|
"factory clause of interface '%s' and actual factory "
|
|
"class '%s'.\n",
|
|
interface_name.ToCString(),
|
|
factory_name.ToCString());
|
|
}
|
|
}
|
|
|
|
|
|
void ClassFinalizer::ResolveType(const Class& cls, const AbstractType& type) {
|
|
if (type.IsResolved()) {
|
|
return;
|
|
}
|
|
if (FLAG_trace_type_finalization) {
|
|
OS::Print("Resolve type '%s'\n", String::Handle(type.Name()).ToCString());
|
|
}
|
|
|
|
// Resolve the type class.
|
|
if (!type.HasResolvedTypeClass()) {
|
|
// Type parameters are always resolved in the parser in the correct
|
|
// non-static scope or factory scope. That resolution scope is unknown here.
|
|
// Being able to resolve a type parameter from class cls here would indicate
|
|
// that the type parameter appeared in a static scope. Leaving the type as
|
|
// unresolved is the correct thing to do.
|
|
|
|
// Lookup the type class.
|
|
const UnresolvedClass& unresolved_class =
|
|
UnresolvedClass::Handle(type.unresolved_class());
|
|
const Class& type_class =
|
|
Class::Handle(ResolveClass(cls, unresolved_class));
|
|
|
|
// Replace unresolved class with resolved type class.
|
|
ASSERT(type.IsType());
|
|
Type& parameterized_type = Type::Handle();
|
|
parameterized_type ^= type.raw();
|
|
parameterized_type.set_type_class(Object::Handle(type_class.raw()));
|
|
}
|
|
|
|
// Resolve type arguments, if any.
|
|
const AbstractTypeArguments& arguments =
|
|
AbstractTypeArguments::Handle(type.arguments());
|
|
if (!arguments.IsNull()) {
|
|
intptr_t num_arguments = arguments.Length();
|
|
AbstractType& type_argument = AbstractType::Handle();
|
|
for (intptr_t i = 0; i < num_arguments; i++) {
|
|
type_argument = arguments.TypeAt(i);
|
|
ResolveType(cls, type_argument);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// 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.
|
|
// Example:
|
|
// Declared: class C<K, V> extends B<V> { ... }
|
|
// class B<T> extends Array<int> { ... }
|
|
// Input: C<String, double> expressed as
|
|
// cls = C, arguments = [null, null, String, double],
|
|
// i.e. cls_args = [String, double], offset = 2, length = 2.
|
|
// Output: arguments = [int, double, String, double]
|
|
void ClassFinalizer::FinalizeTypeArguments(
|
|
const Class& cls, const AbstractTypeArguments& arguments) {
|
|
ASSERT(arguments.Length() >= cls.NumTypeArguments());
|
|
Type& super_type = Type::Handle(cls.super_type());
|
|
if (!super_type.IsNull()) {
|
|
super_type ^= FinalizeType(super_type);
|
|
cls.set_super_type(super_type);
|
|
const Class& super_class = Class::Handle(super_type.type_class());
|
|
const AbstractTypeArguments& super_type_args =
|
|
AbstractTypeArguments::Handle(super_type.arguments());
|
|
const intptr_t num_super_type_params = super_class.NumTypeParameters();
|
|
const intptr_t offset = super_class.NumTypeArguments();
|
|
const intptr_t super_offset = offset - num_super_type_params;
|
|
ASSERT(offset == (cls.NumTypeArguments() - cls.NumTypeParameters()));
|
|
AbstractType& super_type_arg = AbstractType::Handle();
|
|
for (intptr_t i = 0; i < num_super_type_params; i++) {
|
|
super_type_arg = super_type_args.TypeAt(super_offset + i);
|
|
if (!super_type_arg.IsInstantiated()) {
|
|
super_type_arg = super_type_arg.InstantiateFrom(arguments, offset);
|
|
}
|
|
super_type_arg = super_type_arg.Canonicalize();
|
|
arguments.SetTypeAt(super_offset + i, super_type_arg);
|
|
}
|
|
FinalizeTypeArguments(super_class, arguments);
|
|
}
|
|
}
|
|
|
|
|
|
// Verify the upper bounds of the type arguments of class cls.
|
|
void ClassFinalizer::VerifyUpperBounds(const Class& cls,
|
|
const AbstractTypeArguments& arguments) {
|
|
ASSERT(FLAG_enable_type_checks);
|
|
ASSERT(arguments.Length() >= cls.NumTypeArguments());
|
|
const intptr_t num_type_params = cls.NumTypeParameters();
|
|
const intptr_t offset = cls.NumTypeArguments() - num_type_params;
|
|
AbstractType& type = AbstractType::Handle();
|
|
AbstractType& type_extends = AbstractType::Handle();
|
|
const AbstractTypeArguments& extends_array =
|
|
AbstractTypeArguments::Handle(cls.type_parameter_extends());
|
|
ASSERT((extends_array.IsNull() && (num_type_params == 0)) ||
|
|
(extends_array.Length() == num_type_params));
|
|
for (intptr_t i = 0; i < num_type_params; i++) {
|
|
type_extends = extends_array.TypeAt(i);
|
|
if (!type_extends.IsDynamicType()) {
|
|
type = arguments.TypeAt(offset + i);
|
|
if (type.IsInstantiated()) {
|
|
if (!type_extends.IsInstantiated()) {
|
|
type_extends = type_extends.InstantiateFrom(arguments, offset);
|
|
}
|
|
// TODO(regis): Where do we check the constraints when the type is
|
|
// generic?
|
|
if (!type.IsSubtypeOf(type_extends)) {
|
|
const String& type_argument_name = String::Handle(type.Name());
|
|
const String& class_name = String::Handle(cls.Name());
|
|
const String& extends_name = String::Handle(type_extends.Name());
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, -1,
|
|
"type argument '%s' of class '%s' "
|
|
"does not extend type '%s'\n",
|
|
type_argument_name.ToCString(),
|
|
class_name.ToCString(),
|
|
extends_name.ToCString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const Type& super_type = Type::Handle(cls.super_type());
|
|
if (!super_type.IsNull()) {
|
|
ASSERT(super_type.IsFinalized());
|
|
const Class& super_class = Class::Handle(super_type.type_class());
|
|
VerifyUpperBounds(super_class, arguments);
|
|
}
|
|
}
|
|
|
|
|
|
RawAbstractType* ClassFinalizer::FinalizeType(const AbstractType& type) {
|
|
ASSERT(type.IsResolved());
|
|
if (type.IsFinalized()) {
|
|
return type.raw();
|
|
}
|
|
if (FLAG_trace_type_finalization) {
|
|
OS::Print("Finalize type '%s'\n", String::Handle(type.Name()).ToCString());
|
|
}
|
|
|
|
// At this point, we can only have a parameterized_type.
|
|
Type& parameterized_type = Type::Handle();
|
|
parameterized_type ^= type.raw();
|
|
|
|
if (parameterized_type.IsBeingFinalized()) {
|
|
ReportError("type '%s' illegally refers to itself\n",
|
|
String::Handle(parameterized_type.Name()).ToCString());
|
|
}
|
|
|
|
// Mark type as being finalized in order to detect illegal self reference.
|
|
parameterized_type.set_is_being_finalized();
|
|
|
|
// Finalize the current type arguments of the type, which are still the
|
|
// parsed type arguments.
|
|
AbstractTypeArguments& arguments =
|
|
AbstractTypeArguments::Handle(parameterized_type.arguments());
|
|
if (!arguments.IsNull()) {
|
|
intptr_t num_arguments = arguments.Length();
|
|
for (intptr_t i = 0; i < num_arguments; i++) {
|
|
AbstractType& type_argument = AbstractType::Handle(arguments.TypeAt(i));
|
|
type_argument = FinalizeType(type_argument);
|
|
arguments.SetTypeAt(i, type_argument);
|
|
}
|
|
}
|
|
|
|
// The type class does not need to be finalized in order to finalize the type,
|
|
// however, it must at least be resolved (this was done as part of resolving
|
|
// the type itself, a precondition to calling FinalizeType) and the upper
|
|
// bounds of its type parameters must be finalized (done here).
|
|
Class& type_class = Class::Handle(parameterized_type.type_class());
|
|
|
|
// If the type class is a signature class, we are finalizing its signature
|
|
// type, thereby finalizing the result type and parameter types of its
|
|
// signature function.
|
|
// Do this before marking this type as finalized in order to detect cycles.
|
|
if (type_class.IsSignatureClass()) {
|
|
// Signature classes are finalized upon creation.
|
|
ASSERT(type_class.is_finalized());
|
|
// Resolve and finalize the result and parameter types of the signature
|
|
// function of this signature class.
|
|
ResolveAndFinalizeSignature(
|
|
type_class, Function::Handle(type_class.signature_function()));
|
|
}
|
|
|
|
// The finalized type argument vector needs num_type_arguments types.
|
|
const intptr_t num_type_arguments = type_class.NumTypeArguments();
|
|
// The type 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, subtyping constraints are checked below, even for a raw type.
|
|
if (!arguments.IsNull() && (arguments.Length() != num_type_parameters)) {
|
|
// TODO(regis): We need to store the token_index in each type.
|
|
ReportError("wrong number of type arguments in type '%s'\n",
|
|
String::Handle(type.Name()).ToCString());
|
|
}
|
|
// The full type argument vector consists of the type arguments of the
|
|
// super types of type_class, which may be initialized from the parsed
|
|
// type arguments, followed by the parsed type arguments.
|
|
if (num_type_arguments > 0) {
|
|
TypeArguments& full_arguments = TypeArguments::Handle(
|
|
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 = AbstractType::Handle(Type::DynamicType());
|
|
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 DynamicType.
|
|
if (!arguments.IsNull()) {
|
|
type = arguments.TypeAt(i);
|
|
}
|
|
full_arguments.SetTypeAt(offset + i, type);
|
|
}
|
|
if (type_class.IsSignatureClass()) {
|
|
const Function& signature_fun =
|
|
Function::Handle(type_class.signature_function());
|
|
ASSERT(!signature_fun.is_static());
|
|
const Class& signature_fun_owner = Class::Handle(signature_fun.owner());
|
|
FinalizeTypeArguments(signature_fun_owner, full_arguments);
|
|
} else {
|
|
FinalizeTypeArguments(type_class, full_arguments);
|
|
}
|
|
// FinalizeTypeArguments can modify 'full_arguments',
|
|
// canonicalize afterwards.
|
|
full_arguments ^= full_arguments.Canonicalize();
|
|
parameterized_type.set_arguments(full_arguments);
|
|
|
|
// Mark the type as finalized before finalizing the upper bounds, because
|
|
// cycles via upper bounds are legal at compile time.
|
|
parameterized_type.set_is_finalized();
|
|
|
|
ResolveAndFinalizeUpperBounds(type_class);
|
|
if (FLAG_enable_type_checks) {
|
|
VerifyUpperBounds(type_class, full_arguments);
|
|
}
|
|
} else {
|
|
parameterized_type.set_is_finalized();
|
|
}
|
|
return parameterized_type.Canonicalize();
|
|
}
|
|
|
|
|
|
RawType* ClassFinalizer::FinalizeAndCanonicalizeType(const Type& type,
|
|
String* errmsg) {
|
|
Isolate* isolate = Isolate::Current();
|
|
ASSERT(isolate != NULL);
|
|
LongJump* base = isolate->long_jump_base();
|
|
LongJump jump;
|
|
isolate->set_long_jump_base(&jump);
|
|
if (setjmp(*jump.Set()) == 0) {
|
|
Type& canonical_type = Type::Handle();
|
|
canonical_type ^= FinalizeType(type);
|
|
isolate->set_long_jump_base(base);
|
|
*errmsg = String::null();
|
|
return canonical_type.raw();
|
|
} else {
|
|
// Error occured: Get the error message.
|
|
isolate->set_long_jump_base(base);
|
|
*errmsg = isolate->object_store()->sticky_error();
|
|
return type.raw();
|
|
}
|
|
UNREACHABLE();
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void ClassFinalizer::ResolveAndFinalizeSignature(const Class& cls,
|
|
const Function& function) {
|
|
// Resolve result type.
|
|
AbstractType& type = AbstractType::Handle(function.result_type());
|
|
if (!type.IsResolved()) {
|
|
if (function.IsFactory()) {
|
|
// The signature class of the factory for a generic class holds the type
|
|
// parameters and their upper bounds. Copy the signature class from the
|
|
// result before it gets resolved.
|
|
const UnresolvedClass& unresolved_type_class =
|
|
UnresolvedClass::Handle(type.unresolved_class());
|
|
const Class& factory_signature_class =
|
|
Class::Handle(unresolved_type_class.factory_signature_class());
|
|
ASSERT(!factory_signature_class.IsNull());
|
|
function.set_signature_class(factory_signature_class);
|
|
ResolveType(cls, type);
|
|
const Class& type_class = Class::Handle(type.type_class());
|
|
// Verify that the factory signature declares the same number of type
|
|
// parameters as the return type class or interface.
|
|
ResolveAndFinalizeUpperBounds(factory_signature_class);
|
|
if (factory_signature_class.NumTypeParameters() !=
|
|
type_class.NumTypeParameters()) {
|
|
const String& function_name = String::Handle(function.name());
|
|
if (factory_signature_class.NumTypeParameters() == 0) {
|
|
// TODO(regis): For now, and until the core lib is fixed, we accept a
|
|
// factory method with missing list of type parameters and use the
|
|
// list of the enclosing class.
|
|
// See bug 5408808.
|
|
const Class& enclosing_class = Class::Handle(function.owner());
|
|
function.set_signature_class(enclosing_class);
|
|
const Script& script = Script::Handle(enclosing_class.script());
|
|
ReportWarning(script, unresolved_type_class.token_index(),
|
|
"factory method '%s' should declare a list of "
|
|
"%d type parameter%s.\n",
|
|
function_name.ToCString(),
|
|
type_class.NumTypeParameters(),
|
|
type_class.NumTypeParameters() > 1 ? "s" : "");
|
|
} else {
|
|
const Class& enclosing_class = Class::Handle(function.owner());
|
|
const Script& script = Script::Handle(enclosing_class.script());
|
|
ReportError(script, unresolved_type_class.token_index(),
|
|
"factory method '%s' must declare %d type parameter%s.\n",
|
|
function_name.ToCString(),
|
|
type_class.NumTypeParameters(),
|
|
type_class.NumTypeParameters() > 1 ? "s" : "");
|
|
}
|
|
}
|
|
} else {
|
|
ResolveType(cls, type);
|
|
}
|
|
}
|
|
type = FinalizeType(type);
|
|
function.set_result_type(type);
|
|
// Resolve formal parameter types.
|
|
const intptr_t num_parameters = function.NumberOfParameters();
|
|
for (intptr_t i = 0; i < num_parameters; i++) {
|
|
type = function.ParameterTypeAt(i);
|
|
ResolveType(cls, type);
|
|
type = FinalizeType(type);
|
|
function.SetParameterTypeAt(i, type);
|
|
}
|
|
}
|
|
|
|
|
|
static RawClass* FindSuperOwnerOfInstanceMember(const Class& cls,
|
|
const String& name) {
|
|
Class& super_class = Class::Handle();
|
|
Function& function = Function::Handle();
|
|
Field& field = Field::Handle();
|
|
super_class = cls.SuperClass();
|
|
while (!super_class.IsNull()) {
|
|
// Check if an instance member of same name exists in any super class.
|
|
function = super_class.LookupFunction(name);
|
|
if (!function.IsNull() && !function.is_static()) {
|
|
return super_class.raw();
|
|
}
|
|
field = super_class.LookupField(name);
|
|
if (!field.IsNull() && !field.is_static()) {
|
|
return super_class.raw();
|
|
}
|
|
super_class = super_class.SuperClass();
|
|
}
|
|
return Class::null();
|
|
}
|
|
|
|
|
|
static RawClass* FindSuperOwnerOfFunction(const Class& cls,
|
|
const String& name) {
|
|
Class& super_class = Class::Handle();
|
|
Function& function = Function::Handle();
|
|
super_class = cls.SuperClass();
|
|
while (!super_class.IsNull()) {
|
|
// Check if a function of same name exists in any super class.
|
|
function = super_class.LookupFunction(name);
|
|
if (!function.IsNull()) {
|
|
return super_class.raw();
|
|
}
|
|
super_class = super_class.SuperClass();
|
|
}
|
|
return Class::null();
|
|
}
|
|
|
|
|
|
// Resolve and finalize the upper bounds of the type parameters of class cls.
|
|
void ClassFinalizer::ResolveAndFinalizeUpperBounds(const Class& cls) {
|
|
const intptr_t num_type_params = cls.NumTypeParameters();
|
|
AbstractType& type_extends = AbstractType::Handle();
|
|
const AbstractTypeArguments& extends_array =
|
|
AbstractTypeArguments::Handle(cls.type_parameter_extends());
|
|
ASSERT((extends_array.IsNull() && (num_type_params == 0)) ||
|
|
(extends_array.Length() == num_type_params));
|
|
for (intptr_t i = 0; i < num_type_params; i++) {
|
|
type_extends = extends_array.TypeAt(i);
|
|
ResolveType(cls, type_extends);
|
|
type_extends = FinalizeType(type_extends);
|
|
extends_array.SetTypeAt(i, type_extends);
|
|
}
|
|
}
|
|
|
|
|
|
void ClassFinalizer::ResolveAndFinalizeMemberTypes(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.
|
|
// The only compile errors we report are therefore:
|
|
// - a getter having the same name as a method (but not a getter) in a super
|
|
// class or in a subclass.
|
|
// - a setter having the same name as a method (but not a setter) in a super
|
|
// class or in a subclass.
|
|
// - a static field, instance field, or static method (but not an instance
|
|
// method) having the same name as an instance member in a super class.
|
|
|
|
// Resolve type of fields and check for conflicts in super classes.
|
|
Array& array = Array::Handle(cls.fields());
|
|
Field& field = Field::Handle();
|
|
AbstractType& type = AbstractType::Handle();
|
|
String& name = String::Handle();
|
|
Class& super_class = Class::Handle();
|
|
intptr_t num_fields = array.Length();
|
|
for (intptr_t i = 0; i < num_fields; i++) {
|
|
field ^= array.At(i);
|
|
type = field.type();
|
|
ResolveType(cls, type);
|
|
type = FinalizeType(type);
|
|
field.set_type(type);
|
|
name = field.name();
|
|
super_class = FindSuperOwnerOfInstanceMember(cls, name);
|
|
if (!super_class.IsNull()) {
|
|
const String& class_name = String::Handle(cls.Name());
|
|
const String& super_class_name = String::Handle(super_class.Name());
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, field.token_index(),
|
|
"field '%s' of class '%s' conflicts with instance "
|
|
"member '%s' of super class '%s'.\n",
|
|
name.ToCString(),
|
|
class_name.ToCString(),
|
|
name.ToCString(),
|
|
super_class_name.ToCString());
|
|
}
|
|
}
|
|
// Resolve function signatures and check for conflicts in super classes.
|
|
array = cls.functions();
|
|
Function& function = Function::Handle();
|
|
Function& overridden_function = Function::Handle();
|
|
intptr_t num_functions = array.Length();
|
|
String& function_name = String::Handle();
|
|
for (intptr_t i = 0; i < num_functions; i++) {
|
|
function ^= array.At(i);
|
|
ResolveAndFinalizeSignature(cls, function);
|
|
function_name = function.name();
|
|
if (function.is_static()) {
|
|
super_class = FindSuperOwnerOfInstanceMember(cls, function_name);
|
|
if (!super_class.IsNull()) {
|
|
const String& class_name = String::Handle(cls.Name());
|
|
const String& super_class_name = String::Handle(super_class.Name());
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, function.token_index(),
|
|
"static function '%s' of class '%s' conflicts with "
|
|
"instance member '%s' of super class '%s'.\n",
|
|
function_name.ToCString(),
|
|
class_name.ToCString(),
|
|
function_name.ToCString(),
|
|
super_class_name.ToCString());
|
|
}
|
|
} else {
|
|
// TODO(regis): This arity check is still being debated. Revisit.
|
|
super_class = cls.SuperClass();
|
|
while (!super_class.IsNull()) {
|
|
overridden_function = super_class.LookupDynamicFunction(function_name);
|
|
if (!overridden_function.IsNull() &&
|
|
!function.HasCompatibleParametersWith(overridden_function)) {
|
|
// Function types are purposely not checked for subtyping.
|
|
const String& class_name = String::Handle(cls.Name());
|
|
const String& super_class_name = String::Handle(super_class.Name());
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, function.token_index(),
|
|
"class '%s' overrides function '%s' of super class '%s' "
|
|
"with incompatible parameters.\n",
|
|
class_name.ToCString(),
|
|
function_name.ToCString(),
|
|
super_class_name.ToCString());
|
|
}
|
|
super_class = super_class.SuperClass();
|
|
}
|
|
}
|
|
if (function.kind() == RawFunction::kGetterFunction) {
|
|
name = Field::NameFromGetter(function_name);
|
|
super_class = FindSuperOwnerOfFunction(cls, name);
|
|
if (!super_class.IsNull()) {
|
|
const String& class_name = String::Handle(cls.Name());
|
|
const String& super_class_name = String::Handle(super_class.Name());
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, function.token_index(),
|
|
"getter '%s' of class '%s' conflicts with "
|
|
"function '%s' of super class '%s'.\n",
|
|
name.ToCString(),
|
|
class_name.ToCString(),
|
|
name.ToCString(),
|
|
super_class_name.ToCString());
|
|
}
|
|
} else if (function.kind() == RawFunction::kSetterFunction) {
|
|
name = Field::NameFromSetter(function_name);
|
|
super_class = FindSuperOwnerOfFunction(cls, name);
|
|
if (!super_class.IsNull()) {
|
|
const String& class_name = String::Handle(cls.Name());
|
|
const String& super_class_name = String::Handle(super_class.Name());
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, function.token_index(),
|
|
"setter '%s' of class '%s' conflicts with "
|
|
"function '%s' of super class '%s'.\n",
|
|
name.ToCString(),
|
|
class_name.ToCString(),
|
|
name.ToCString(),
|
|
super_class_name.ToCString());
|
|
}
|
|
} else {
|
|
name = Field::GetterName(function_name);
|
|
super_class = FindSuperOwnerOfFunction(cls, name);
|
|
if (!super_class.IsNull()) {
|
|
const String& class_name = String::Handle(cls.Name());
|
|
const String& super_class_name = String::Handle(super_class.Name());
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, function.token_index(),
|
|
"function '%s' of class '%s' conflicts with "
|
|
"getter '%s' of super class '%s'.\n",
|
|
function_name.ToCString(),
|
|
class_name.ToCString(),
|
|
function_name.ToCString(),
|
|
super_class_name.ToCString());
|
|
}
|
|
name = Field::SetterName(function_name);
|
|
super_class = FindSuperOwnerOfFunction(cls, name);
|
|
if (!super_class.IsNull()) {
|
|
const String& class_name = String::Handle(cls.Name());
|
|
const String& super_class_name = String::Handle(super_class.Name());
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, function.token_index(),
|
|
"function '%s' of class '%s' conflicts with "
|
|
"setter '%s' of super class '%s'.\n",
|
|
function_name.ToCString(),
|
|
class_name.ToCString(),
|
|
function_name.ToCString(),
|
|
super_class_name.ToCString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ClassFinalizer::FinalizeClass(const Class& cls, bool generating_snapshot) {
|
|
if (cls.is_finalized()) {
|
|
return;
|
|
}
|
|
if (FLAG_trace_class_finalization) {
|
|
OS::Print("Finalize %s\n", cls.ToCString());
|
|
}
|
|
// Signature classes are finalized upon creation.
|
|
ASSERT(!cls.IsSignatureClass());
|
|
if (!IsSuperCycleFree(cls)) {
|
|
const String& name = String::Handle(cls.Name());
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, -1,
|
|
"class '%s' has a cycle in its superclass relationship.\n",
|
|
name.ToCString());
|
|
}
|
|
GrowableArray<const Class*> visited;
|
|
ResolveInterfaces(cls, &visited);
|
|
Type& super_type = Type::Handle(cls.super_type());
|
|
if (!super_type.IsNull()) {
|
|
const Class& super_class = Class::Handle(super_type.type_class());
|
|
// Finalize super class and super type.
|
|
FinalizeClass(super_class, generating_snapshot);
|
|
super_type ^= FinalizeType(super_type);
|
|
cls.set_super_type(super_type);
|
|
}
|
|
if (cls.is_interface()) {
|
|
if (cls.HasFactoryClass()) {
|
|
const Class& factory_class = Class::Handle(cls.FactoryClass());
|
|
// Finalize factory class.
|
|
if (!factory_class.is_finalized()) {
|
|
FinalizeClass(factory_class, generating_snapshot);
|
|
// Finalizing the factory class may indirectly finalize this interface.
|
|
if (cls.is_finalized()) {
|
|
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(interface_type);
|
|
interface_types.SetAt(i, interface_type);
|
|
}
|
|
// Mark as finalized before resolving type parameter upper bounds and member
|
|
// types in order to break cycles.
|
|
cls.Finalize();
|
|
ResolveAndFinalizeUpperBounds(cls);
|
|
ResolveAndFinalizeMemberTypes(cls);
|
|
// Run additional checks after all types are finalized.
|
|
if (cls.is_const()) {
|
|
CheckForLegalConstClass(cls);
|
|
}
|
|
// Check to ensure we don't have classes with native fields in libraries
|
|
// which do not have a native resolver.
|
|
if (!generating_snapshot && cls.num_native_fields() != 0) {
|
|
const Library& lib = Library::Handle(cls.library());
|
|
if (lib.native_entry_resolver() == NULL) {
|
|
const String& cls_name = String::Handle(cls.Name());
|
|
const String& lib_name = String::Handle(lib.url());
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, -1,
|
|
"class '%s' is trying to extend a native fields class, "
|
|
"but library '%s' has no native resolvers",
|
|
cls_name.ToCString(), lib_name.ToCString());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool ClassFinalizer::IsSuperCycleFree(const Class& cls) {
|
|
Class& test1 = Class::Handle(cls.raw());
|
|
Class& test2 = Class::Handle(cls.SuperClass());
|
|
// A finalized class has been checked for cycles.
|
|
// Using the hare and tortoise algorithm for locating cycles.
|
|
while (!test1.is_finalized() &&
|
|
!test2.IsNull() && !test2.is_finalized()) {
|
|
if (test1.raw() == test2.raw()) {
|
|
// Found a cycle.
|
|
return false;
|
|
}
|
|
test1 = test1.SuperClass();
|
|
test2 = test2.SuperClass();
|
|
if (!test2.IsNull()) {
|
|
test2 = test2.SuperClass();
|
|
}
|
|
}
|
|
// No cycles.
|
|
return true;
|
|
}
|
|
|
|
|
|
bool ClassFinalizer::AddInterfaceIfUnique(
|
|
GrowableArray<AbstractType*>* interface_list,
|
|
AbstractType* interface,
|
|
AbstractType* conflicting) {
|
|
String& interface_class_name = String::Handle(interface->ClassName());
|
|
String& existing_interface_class_name = String::Handle();
|
|
for (intptr_t i = 0; i < interface_list->length(); i++) {
|
|
existing_interface_class_name = (*interface_list)[i]->ClassName();
|
|
if (interface_class_name.Equals(existing_interface_class_name)) {
|
|
// Same interface class name, now check names of type arguments.
|
|
const String& interface_name = String::Handle(interface->Name());
|
|
const String& existing_interface_name =
|
|
String::Handle((*interface_list)[i]->Name());
|
|
// TODO(regis): Revisit depending on the outcome of issue 4905685.
|
|
if (!interface_name.Equals(existing_interface_name)) {
|
|
*conflicting = (*interface_list)[i]->raw();
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
interface_list->Add(interface);
|
|
return true;
|
|
}
|
|
|
|
|
|
template<typename T>
|
|
static RawArray* NewArray(const GrowableArray<T*>& objs) {
|
|
Array& a = Array::Handle(Array::New(objs.length()));
|
|
for (int i = 0; i < objs.length(); i++) {
|
|
a.SetAt(i, *objs[i]);
|
|
}
|
|
return a.raw();
|
|
}
|
|
|
|
|
|
// Walks the graph of explicitly declared interfaces of classes and
|
|
// interfaces recursively. Resolves unresolved interfaces.
|
|
// Returns false if there is an interface reference that cannot be
|
|
// resolved, or if there is a cycle in the graph. We detect cycles by
|
|
// remembering interfaces we've visited in each path through the
|
|
// graph. If we visit an interface a second time on a given path,
|
|
// we found a loop.
|
|
void ClassFinalizer::ResolveInterfaces(const Class& cls,
|
|
GrowableArray<const Class*>* visited) {
|
|
ASSERT(visited != NULL);
|
|
for (int i = 0; i < visited->length(); i++) {
|
|
if ((*visited)[i]->raw() == cls.raw()) {
|
|
// We have already visited interface class 'cls'. We found a cycle.
|
|
const String& interface_name = String::Handle(cls.Name());
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, -1,
|
|
"Cyclic reference found for interface '%s'\n",
|
|
interface_name.ToCString());
|
|
}
|
|
}
|
|
|
|
// If the class/interface has no explicit interfaces, we are done.
|
|
Array& super_interfaces = Array::Handle(cls.interfaces());
|
|
if (super_interfaces.Length() == 0) {
|
|
return;
|
|
}
|
|
|
|
// If cls belongs to core lib or to core lib's implementation, restrictions
|
|
// about allowed interfaces are lifted.
|
|
const bool cls_belongs_to_core_lib =
|
|
(cls.library() == Library::CoreLibrary()) ||
|
|
(cls.library() == Library::CoreImplLibrary());
|
|
|
|
// Resolve and check the interfaces of cls.
|
|
visited->Add(&cls);
|
|
AbstractType& interface = AbstractType::Handle();
|
|
for (intptr_t i = 0; i < super_interfaces.Length(); i++) {
|
|
interface ^= super_interfaces.At(i);
|
|
ResolveType(cls, interface);
|
|
if (interface.IsTypeParameter()) {
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, -1,
|
|
"Type parameter '%s' cannot be used as interface\n",
|
|
String::Handle(interface.Name()).ToCString());
|
|
}
|
|
const Class& interface_class = Class::Handle(interface.type_class());
|
|
if (!interface_class.is_interface()) {
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, -1,
|
|
"Class '%s' is used where an interface is expected\n",
|
|
String::Handle(interface_class.Name()).ToCString());
|
|
}
|
|
// Verify that unless cls belongs to core lib, it cannot extend or implement
|
|
// any of bool, num, int, double, String, Function, Dynamic.
|
|
// The exception is signature classes, which are compiler generated and
|
|
// represent a function type, therefore implementing the Function interface.
|
|
if (!cls_belongs_to_core_lib) {
|
|
if (interface.IsBoolInterface() ||
|
|
interface.IsNumberInterface() ||
|
|
interface.IsIntInterface() ||
|
|
interface.IsDoubleInterface() ||
|
|
interface.IsStringInterface() ||
|
|
(interface.IsFunctionInterface() && !cls.IsSignatureClass()) ||
|
|
interface.IsDynamicType()) {
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, -1,
|
|
"'%s' is not allowed to extend or implement '%s'\n",
|
|
String::Handle(cls.Name()).ToCString(),
|
|
String::Handle(interface_class.Name()).ToCString());
|
|
}
|
|
}
|
|
// Now resolve the super interfaces.
|
|
ResolveInterfaces(interface_class, visited);
|
|
}
|
|
visited->RemoveLast();
|
|
}
|
|
|
|
|
|
// A class is marked as constant if it has one constant constructor.
|
|
// A constant class:
|
|
// - may extend only const classes.
|
|
// - has only const instance fields.
|
|
// Note: we must check for cycles before checking for const properties.
|
|
void ClassFinalizer::CheckForLegalConstClass(const Class& cls) {
|
|
ASSERT(cls.is_const());
|
|
const Class& super = Class::Handle(cls.SuperClass());
|
|
if (!super.IsNull() && !super.is_const()) {
|
|
String& name = String::Handle(super.Name());
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, -1,
|
|
"superclass '%s' must be const.\n", name.ToCString());
|
|
}
|
|
const Array& fields_array = Array::Handle(cls.fields());
|
|
intptr_t len = fields_array.Length();
|
|
Field& field = Field::Handle();
|
|
for (intptr_t i = 0; i < len; i++) {
|
|
field ^= fields_array.At(i);
|
|
if (!field.is_static() && !field.is_final()) {
|
|
const String& class_name = String::Handle(cls.Name());
|
|
const String& field_name = String::Handle(field.name());
|
|
const Script& script = Script::Handle(cls.script());
|
|
ReportError(script, field.token_index(),
|
|
"const class '%s' has non-final field '%s'\n",
|
|
class_name.ToCString(), field_name.ToCString());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ClassFinalizer::PrintClassInformation(const Class& cls) {
|
|
HANDLESCOPE(Isolate::Current());
|
|
const String& class_name = String::Handle(cls.Name());
|
|
OS::Print("%s '%s'",
|
|
cls.is_interface() ? "interface" : "class",
|
|
class_name.ToCString());
|
|
const Library& library = Library::Handle(cls.library());
|
|
if (!library.IsNull()) {
|
|
OS::Print(" library '%s%s':\n",
|
|
String::Handle(library.url()).ToCString(),
|
|
String::Handle(library.private_key()).ToCString());
|
|
} else {
|
|
OS::Print(" (null library):\n");
|
|
}
|
|
const Array& interfaces_array = Array::Handle(cls.interfaces());
|
|
AbstractType& interface = AbstractType::Handle();
|
|
intptr_t len = interfaces_array.Length();
|
|
for (intptr_t i = 0; i < len; i++) {
|
|
interface ^= interfaces_array.At(i);
|
|
OS::Print(" %s\n", interface.ToCString());
|
|
}
|
|
const Array& functions_array = Array::Handle(cls.functions());
|
|
Function& function = Function::Handle();
|
|
len = functions_array.Length();
|
|
for (intptr_t i = 0; i < len; i++) {
|
|
function ^= functions_array.At(i);
|
|
OS::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);
|
|
OS::Print(" %s\n", field.ToCString());
|
|
}
|
|
}
|
|
|
|
|
|
void ClassFinalizer::ReportError(const Script& script,
|
|
intptr_t token_index,
|
|
const char* format, ...) {
|
|
const intptr_t kMessageBufferSize = 512;
|
|
char message_buffer[kMessageBufferSize];
|
|
va_list args;
|
|
va_start(args, format);
|
|
Parser::FormatMessage(script, token_index, "Error",
|
|
message_buffer, kMessageBufferSize,
|
|
format, args);
|
|
Isolate::Current()->long_jump_base()->Jump(1, message_buffer);
|
|
UNREACHABLE();
|
|
}
|
|
|
|
|
|
void ClassFinalizer::ReportError(const char* format, ...) {
|
|
const intptr_t kMessageBufferSize = 512;
|
|
char message_buffer[kMessageBufferSize];
|
|
va_list args;
|
|
va_start(args, format);
|
|
Parser::FormatMessage(Script::Handle(), -1, "Error",
|
|
message_buffer, kMessageBufferSize,
|
|
format, args);
|
|
va_end(args);
|
|
Isolate::Current()->long_jump_base()->Jump(1, message_buffer);
|
|
UNREACHABLE();
|
|
}
|
|
|
|
|
|
void ClassFinalizer::ReportWarning(const Script& script,
|
|
intptr_t token_index,
|
|
const char* format, ...) {
|
|
if (FLAG_silent_warnings) return;
|
|
const intptr_t kMessageBufferSize = 512;
|
|
char message_buffer[kMessageBufferSize];
|
|
va_list args;
|
|
va_start(args, format);
|
|
Parser::FormatMessage(script, token_index, "Warning",
|
|
message_buffer, kMessageBufferSize,
|
|
format, args);
|
|
va_end(args);
|
|
if (FLAG_warning_as_error) {
|
|
Isolate::Current()->long_jump_base()->Jump(1, message_buffer);
|
|
UNREACHABLE();
|
|
} else {
|
|
OS::Print(message_buffer);
|
|
}
|
|
}
|
|
|
|
} // namespace dart
|