| Index: runtime/vm/class_finalizer.cc
|
| ===================================================================
|
| --- runtime/vm/class_finalizer.cc (revision 19513)
|
| +++ runtime/vm/class_finalizer.cc (working copy)
|
| @@ -65,7 +65,7 @@
|
| }
|
|
|
|
|
| -void AddSuperType(const Type& type,
|
| +void AddSuperType(const AbstractType& type,
|
| GrowableArray<intptr_t>* finalized_super_classes) {
|
| ASSERT(type.HasResolvedTypeClass());
|
| if (type.IsObjectType()) {
|
| @@ -81,7 +81,7 @@
|
| }
|
| }
|
| finalized_super_classes->Add(cid);
|
| - const Type& super_type = Type::Handle(cls.super_type());
|
| + const AbstractType& super_type = AbstractType::Handle(cls.super_type());
|
| AddSuperType(super_type, finalized_super_classes);
|
| }
|
|
|
| @@ -92,11 +92,11 @@
|
| const GrowableObjectArray& pending_classes,
|
| GrowableArray<intptr_t>* finalized_super_classes) {
|
| Class& cls = Class::Handle();
|
| - Type& super_type = Type::Handle();
|
| + AbstractType& super_type = Type::Handle();
|
| for (intptr_t i = 0; i < pending_classes.Length(); i++) {
|
| cls ^= pending_classes.At(i);
|
| ASSERT(!cls.is_finalized());
|
| - super_type ^= cls.super_type();
|
| + super_type = cls.super_type();
|
| if (!super_type.IsNull()) {
|
| if (!super_type.IsMalformed() &&
|
| super_type.HasResolvedTypeClass() &&
|
| @@ -434,10 +434,14 @@
|
| if (!target_type.IsInstantiated()) {
|
| const AbstractTypeArguments& type_args = AbstractTypeArguments::Handle(
|
| type.arguments());
|
| - target_type ^= target_type.InstantiateFrom(type_args);
|
| - // TODO(regis): Check bounds in checked mode.
|
| - target_type ^= FinalizeType(cls, target_type, kCanonicalize);
|
| - if (target_type.IsMalformed()) {
|
| + Error& malformed_error = Error::Handle();
|
| + target_type ^= target_type.InstantiateFrom(type_args, &malformed_error);
|
| + if (malformed_error.IsNull()) {
|
| + target_type ^= FinalizeType(cls, target_type, kCanonicalize);
|
| + } else {
|
| + FinalizeMalformedType(malformed_error,
|
| + cls, target_type, kFinalize,
|
| + "cannot resolve redirecting factory");
|
| target_target = Function::null();
|
| }
|
| }
|
| @@ -504,6 +508,7 @@
|
|
|
|
|
| void ClassFinalizer::FinalizeTypeParameters(const Class& cls) {
|
| + // The type parameter bounds are not finalized here.
|
| const TypeArguments& type_parameters =
|
| TypeArguments::Handle(cls.type_parameters());
|
| if (!type_parameters.IsNull()) {
|
| @@ -537,12 +542,13 @@
|
| void ClassFinalizer::FinalizeTypeArguments(
|
| const Class& cls,
|
| const AbstractTypeArguments& arguments,
|
| - FinalizationKind finalization) {
|
| + FinalizationKind finalization,
|
| + Error* bound_error) {
|
| ASSERT(arguments.Length() >= cls.NumTypeArguments());
|
| if (!cls.is_finalized()) {
|
| FinalizeTypeParameters(cls);
|
| }
|
| - Type& super_type = Type::Handle(cls.super_type());
|
| + AbstractType& super_type = AbstractType::Handle(cls.super_type());
|
| if (!super_type.IsNull()) {
|
| const Class& super_class = Class::Handle(super_type.type_class());
|
| AbstractTypeArguments& super_type_args = AbstractTypeArguments::Handle();
|
| @@ -576,7 +582,16 @@
|
| if (!super_type_args.IsNull()) {
|
| super_type_arg = super_type_args.TypeAt(super_offset + i);
|
| if (!super_type_arg.IsInstantiated()) {
|
| - super_type_arg = super_type_arg.InstantiateFrom(arguments);
|
| + Error& malformed_error = Error::Handle();
|
| + super_type_arg = super_type_arg.InstantiateFrom(arguments,
|
| + &malformed_error);
|
| + if (!malformed_error.IsNull()) {
|
| + if (!super_type_arg.IsInstantiated()) {
|
| + // CheckTypeArgumentBounds will insert a BoundedType.
|
| + } else if (bound_error->IsNull()) {
|
| + *bound_error = malformed_error.raw();
|
| + }
|
| + }
|
| }
|
| if (finalization >= kCanonicalize) {
|
| super_type_arg = super_type_arg.Canonicalize();
|
| @@ -584,11 +599,89 @@
|
| }
|
| arguments.SetTypeAt(super_offset + i, super_type_arg);
|
| }
|
| - FinalizeTypeArguments(super_class, arguments, finalization);
|
| + FinalizeTypeArguments(super_class, arguments, finalization, bound_error);
|
| }
|
| }
|
|
|
|
|
| +// Check the type argument vector 'arguments' against the corresponding bounds
|
| +// of the type parameters of class 'cls' and, recursively, of its superclasses.
|
| +// Replace a type argument that cannot be checked at compile time by a
|
| +// BoundedType, thereby postponing the bound check to run time.
|
| +// Return a bound error if a type argument is not within bound at compile time.
|
| +void ClassFinalizer::CheckTypeArgumentBounds(
|
| + const Class& cls,
|
| + const AbstractTypeArguments& arguments,
|
| + Error* bound_error) {
|
| + if (!cls.is_finalized()) {
|
| + ResolveAndFinalizeUpperBounds(cls);
|
| + }
|
| + // Note that when finalizing a type, we need to verify the bounds in both
|
| + // production mode and checked mode, because the finalized type may be written
|
| + // to a snapshot. It would be wrong to ignore bounds when generating the
|
| + // snapshot in production mode and then use the unchecked type in checked mode
|
| + // after reading it from the snapshot.
|
| + // However, we do not immediately report a bound error, which would be wrong
|
| + // in production mode, but simply postpone the bound checking to runtime.
|
| + const intptr_t num_type_params = cls.NumTypeParameters();
|
| + const intptr_t offset = cls.NumTypeArguments() - num_type_params;
|
| + AbstractType& type_arg = AbstractType::Handle();
|
| + AbstractType& cls_type_param = AbstractType::Handle();
|
| + AbstractType& bound = AbstractType::Handle();
|
| + const TypeArguments& cls_type_params =
|
| + TypeArguments::Handle(cls.type_parameters());
|
| + ASSERT((cls_type_params.IsNull() && (num_type_params == 0)) ||
|
| + (cls_type_params.Length() == num_type_params));
|
| + for (intptr_t i = 0; i < num_type_params; i++) {
|
| + type_arg = arguments.TypeAt(offset + i);
|
| + if (type_arg.IsDynamicType()) {
|
| + continue;
|
| + }
|
| + cls_type_param = cls_type_params.TypeAt(i);
|
| + const TypeParameter& type_param = TypeParameter::Cast(cls_type_param);
|
| + ASSERT(type_param.IsFinalized());
|
| + bound = type_param.bound();
|
| + if (!bound.IsObjectType() && !bound.IsDynamicType()) {
|
| + Error& malformed_error = Error::Handle();
|
| + // Note that the bound may be malformed, in which case the bound check
|
| + // will return an error and the bound check will be postponed to run time.
|
| + // Note also that the bound may still be unfinalized.
|
| + if (!bound.IsFinalized()) {
|
| + ASSERT(bound.IsBeingFinalized());
|
| + // The bound refers to type parameters, creating a cycle; postpone
|
| + // bound check to run time, when the bound will be finalized.
|
| + // TODO(regis): Do we need to instantiate an uninstantiated bound here?
|
| + type_arg = BoundedType::New(type_arg, bound, type_param);
|
| + arguments.SetTypeAt(offset + i, type_arg);
|
| + continue;
|
| + }
|
| + if (!bound.IsInstantiated()) {
|
| + bound = bound.InstantiateFrom(arguments, &malformed_error);
|
| + }
|
| + // TODO(regis): We could simplify this code if we could differentiate
|
| + // between a failed bound check and a bound check that is undecidable at
|
| + // compile time.
|
| + if (malformed_error.IsNull()) {
|
| + type_param.CheckBound(type_arg, bound, &malformed_error);
|
| + }
|
| + if (!malformed_error.IsNull()) {
|
| + if (!type_arg.IsInstantiated() || !bound.IsInstantiated()) {
|
| + type_arg = BoundedType::New(type_arg, bound, type_param);
|
| + arguments.SetTypeAt(offset + i, type_arg);
|
| + } else if (bound_error->IsNull()) {
|
| + *bound_error = malformed_error.raw();
|
| + }
|
| + }
|
| + }
|
| + }
|
| + AbstractType& super_type = AbstractType::Handle(cls.super_type());
|
| + if (!super_type.IsNull()) {
|
| + const Class& super_class = Class::Handle(super_type.type_class());
|
| + CheckTypeArgumentBounds(super_class, arguments, bound_error);
|
| + }
|
| +}
|
| +
|
| +
|
| RawAbstractType* ClassFinalizer::FinalizeType(const Class& cls,
|
| const AbstractType& type,
|
| FinalizationKind finalization) {
|
| @@ -627,8 +720,6 @@
|
| parameterized_class.NumTypeParameters();
|
| type_parameter.set_index(type_parameter.index() + offset);
|
| type_parameter.set_is_finalized();
|
| - // TODO(regis): We are not able to finalize the bound here without getting
|
| - // into cycles. Revisit.
|
| // We do not canonicalize type parameters.
|
| return type_parameter.raw();
|
| }
|
| @@ -718,6 +809,7 @@
|
| // super types of type_class, which may be initialized from the parsed
|
| // type arguments, followed by the parsed type arguments.
|
| TypeArguments& full_arguments = TypeArguments::Handle();
|
| + Error& bound_error = Error::Handle();
|
| 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.
|
| @@ -749,9 +841,13 @@
|
| Function::Handle(type_class.signature_function());
|
| ASSERT(!signature_fun.is_static());
|
| const Class& sig_fun_owner = Class::Handle(signature_fun.Owner());
|
| - FinalizeTypeArguments(sig_fun_owner, full_arguments, finalization);
|
| + FinalizeTypeArguments(
|
| + sig_fun_owner, full_arguments, finalization, &bound_error);
|
| + CheckTypeArgumentBounds(sig_fun_owner, full_arguments, &bound_error);
|
| } else {
|
| - FinalizeTypeArguments(type_class, full_arguments, finalization);
|
| + FinalizeTypeArguments(
|
| + type_class, full_arguments, finalization, &bound_error);
|
| + CheckTypeArgumentBounds(type_class, full_arguments, &bound_error);
|
| }
|
| if (full_arguments.IsRaw(num_type_arguments)) {
|
| // The parameterized_type is raw. Set its argument vector to null, which
|
| @@ -774,33 +870,6 @@
|
| parameterized_type.SetIsFinalized();
|
| }
|
|
|
| - // Upper bounds of the finalized type arguments are only verified in checked
|
| - // mode, since bound errors are never reported by the vm in production mode.
|
| - if (FLAG_enable_type_checks &&
|
| - !full_arguments.IsNull() &&
|
| - full_arguments.IsInstantiated()) {
|
| - ResolveAndFinalizeUpperBounds(type_class);
|
| - Error& malformed_error = Error::Handle();
|
| - // Pass the full type argument vector as the bounds instantiator.
|
| - if (!full_arguments.IsWithinBoundsOf(type_class,
|
| - full_arguments,
|
| - &malformed_error)) {
|
| - ASSERT(!malformed_error.IsNull());
|
| - // The type argument vector of the type is not within bounds. The type
|
| - // is malformed. Prepend malformed_error to new malformed type error in
|
| - // order to report both locations.
|
| - // Note that malformed bounds never result in a compile time error, even
|
| - // in checked mode. Therefore, overwrite finalization with kFinalize
|
| - // when finalizing the malformed type.
|
| - FinalizeMalformedType(
|
| - malformed_error,
|
| - cls, parameterized_type, kFinalize,
|
| - "type arguments of type '%s' are not within bounds",
|
| - String::Handle(parameterized_type.UserVisibleName()).ToCString());
|
| - return parameterized_type.raw();
|
| - }
|
| - }
|
| -
|
| // If the type class is a signature class, we are currently finalizing a
|
| // signature type, i.e. finalizing the result type and parameter types of the
|
| // signature function of this signature type.
|
| @@ -812,6 +881,27 @@
|
| FinalizeClass(type_class);
|
| }
|
|
|
| + // If a bound error occurred, return a BoundedType with a malformed bound.
|
| + // The malformed bound will be ignored in production mode.
|
| + if (!bound_error.IsNull()) {
|
| + FinalizationKind bound_finalization = kTryResolve; // No compile error.
|
| + if (FLAG_enable_type_checks || FLAG_error_on_malformed_type) {
|
| + bound_finalization = finalization;
|
| + }
|
| + const String& parameterized_type_name = String::Handle(
|
| + parameterized_type.UserVisibleName());
|
| + const Type& malformed_bound = Type::Handle(
|
| + NewFinalizedMalformedType(bound_error,
|
| + cls,
|
| + parameterized_type.token_pos(),
|
| + bound_finalization,
|
| + "type '%s' has an out of bound type argument",
|
| + parameterized_type_name.ToCString()));
|
| + return BoundedType::New(parameterized_type,
|
| + malformed_bound,
|
| + TypeParameter::Handle());
|
| + }
|
| +
|
| if (finalization >= kCanonicalize) {
|
| return parameterized_type.Canonicalize();
|
| } else {
|
| @@ -1108,7 +1198,8 @@
|
|
|
| // Check that the super class of the mixin class is extending
|
| // class Object.
|
| - const Type& mixin_super_type = Type::Handle(mixin_cls.super_type());
|
| + const AbstractType& mixin_super_type =
|
| + AbstractType::Handle(mixin_cls.super_type());
|
| if (!mixin_super_type.IsObjectType()) {
|
| const Script& script = Script::Handle(cls.script());
|
| const String& class_name = String::Handle(mixin_cls.Name());
|
| @@ -1224,9 +1315,15 @@
|
| // Finalize type parameters before finalizing the super type.
|
| FinalizeTypeParameters(cls);
|
| // Finalize super type.
|
| - Type& super_type = Type::Handle(cls.super_type());
|
| + AbstractType& super_type = AbstractType::Handle(cls.super_type());
|
| if (!super_type.IsNull()) {
|
| - super_type ^= FinalizeType(cls, super_type, kCanonicalizeWellFormed);
|
| + // In case of a bound error in the super type in production mode, the
|
| + // finalized super type will be a BoundedType with a malformed bound.
|
| + // It should not be a problem if the class is written to a snapshot and
|
| + // later executed in checked mode. Note that the finalized type argument
|
| + // vector of any type of the base class will contain a BoundedType for the
|
| + // out of bound type argument.
|
| + super_type = FinalizeType(cls, super_type, kCanonicalizeWellFormed);
|
| cls.set_super_type(super_type);
|
| }
|
| if (cls.IsSignatureClass()) {
|
| @@ -1244,6 +1341,9 @@
|
| // subclasses of Object.
|
| ASSERT(super_type.IsNull() || super_type.IsObjectType());
|
|
|
| + // The type parameters of signature classes may have bounds.
|
| + ResolveAndFinalizeUpperBounds(cls);
|
| +
|
| // Resolve and finalize the result and parameter types of the signature
|
| // function of this signature class.
|
| const Function& sig_function = Function::Handle(cls.signature_function());
|
| @@ -1265,6 +1365,8 @@
|
| // Mark as finalized before resolving type parameter upper bounds and member
|
| // types in order to break cycles.
|
| cls.Finalize();
|
| + // Finalize bounds even if running in production mode, so that a snapshot
|
| + // contains them.
|
| ResolveAndFinalizeUpperBounds(cls);
|
| ResolveAndFinalizeMemberTypes(cls);
|
| // Run additional checks after all types are finalized.
|
| @@ -1373,7 +1475,7 @@
|
|
|
| // If the class/interface has no explicit super class/interfaces
|
| // and is not a mixin application, we are done.
|
| - Type& super_type = Type::Handle(cls.super_type());
|
| + AbstractType& super_type = AbstractType::Handle(cls.super_type());
|
| Type& mixin_type = Type::Handle(cls.mixin());
|
| Array& super_interfaces = Array::Handle(cls.interfaces());
|
| if ((super_type.IsNull() || super_type.IsObjectType()) &&
|
| @@ -1553,7 +1655,7 @@
|
| } else {
|
| OS::Print(" (null library):\n");
|
| }
|
| - const Type& super_type = Type::Handle(cls.super_type());
|
| + const AbstractType& super_type = AbstractType::Handle(cls.super_type());
|
| if (super_type.IsNull()) {
|
| OS::Print(" Super: NULL");
|
| } else {
|
| @@ -1612,21 +1714,15 @@
|
| ReportError(error);
|
| }
|
| }
|
| + // In checked mode, always mark the type as malformed.
|
| + // In production mode, mark the type as malformed only if its type class is
|
| + // not resolved.
|
| + // In both mode, make the type raw, since it may not be possible to
|
| + // properly finalize its type arguments.
|
| if (FLAG_enable_type_checks || !type.HasResolvedTypeClass()) {
|
| - // In check mode, always mark the type as malformed.
|
| - // In production mode, mark the type as malformed only if its type class is
|
| - // not resolved.
|
| type.set_malformed_error(error);
|
| - if (!type.HasResolvedTypeClass()) {
|
| - // We do not want an unresolved class to end up in a snapshot.
|
| - type.set_type_class(Object::Handle(Object::null_class()));
|
| - }
|
| - } else {
|
| - // In production mode, do not mark the type with a resolved type class as
|
| - // malformed, but make it raw.
|
| - type.set_arguments(AbstractTypeArguments::Handle());
|
| }
|
| - ASSERT(type.HasResolvedTypeClass());
|
| + type.set_arguments(AbstractTypeArguments::Handle());
|
| if (!type.IsFinalized()) {
|
| type.SetIsFinalized();
|
| // Do not canonicalize malformed types, since they may not be resolved.
|
| @@ -1655,6 +1751,7 @@
|
| ReportMalformedType(prev_error, cls, type, finalization, format, args);
|
| va_end(args);
|
| ASSERT(type.IsMalformed());
|
| + ASSERT(type.IsFinalized());
|
| return type.raw();
|
| }
|
|
|
|
|