Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(389)

Unified Diff: runtime/vm/class_finalizer.cc

Issue 12473002: Complete implementation of bounds checking in the vm, by introducing a vm object (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/class_finalizer.h ('k') | runtime/vm/code_generator.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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();
}
« no previous file with comments | « runtime/vm/class_finalizer.h ('k') | runtime/vm/code_generator.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698