Index: runtime/vm/object_reload.cc |
diff --git a/runtime/vm/object_reload.cc b/runtime/vm/object_reload.cc |
index 838e9b8375b3d4d6850ced7752f6c8bfc5bfc4c9..9786e41600afc3ff27b69edc6f8064de7b49c6ae 100644 |
--- a/runtime/vm/object_reload.cc |
+++ b/runtime/vm/object_reload.cc |
@@ -18,7 +18,6 @@ DECLARE_FLAG(bool, trace_reload); |
DECLARE_FLAG(bool, trace_reload_verbose); |
DECLARE_FLAG(bool, two_args_smi_icd); |
-#define IRC (Isolate::Current()->reload_context()) |
class ObjectReloadUtils : public AllStatic { |
static void DumpLibraryDictionary(const Library& lib) { |
@@ -364,127 +363,237 @@ void Class::PatchFieldsAndFunctions() const { |
} |
-bool Class::CanReload(const Class& replacement) const { |
- ASSERT(IsolateReloadContext::IsSameClass(*this, replacement)); |
+class EnumClassConflict : public ClassReasonForCancelling { |
+ public: |
+ EnumClassConflict(const Class& from, const Class& to) |
+ : ClassReasonForCancelling(from, to) { } |
+ |
+ RawString* ToString() { |
+ return String::NewFormatted( |
+ from_.is_enum_class() |
+ ? "Enum class cannot be redefined to be a non-enum class: %s" |
+ : "Class cannot be redefined to be a enum class: %s", |
+ from_.ToCString()); |
+ } |
+}; |
- if (is_enum_class() && !replacement.is_enum_class()) { |
- IRC->ReportError(String::Handle(String::NewFormatted( |
- "Enum class cannot be redefined to be a non-enum class: %s", |
- ToCString()))); |
- return false; |
+ |
+class EnsureFinalizedError : public ClassReasonForCancelling { |
+ public: |
+ EnsureFinalizedError(const Class& from, const Class& to, const Error& error) |
+ : ClassReasonForCancelling(from, to), error_(error) { } |
+ |
+ private: |
+ const Error& error_; |
+ |
+ RawError* ToError() { return error_.raw(); } |
+}; |
+ |
+ |
+class NativeFieldsConflict : public ClassReasonForCancelling { |
+ public: |
+ NativeFieldsConflict(const Class& from, const Class& to) |
+ : ClassReasonForCancelling(from, to) { } |
+ |
+ private: |
+ RawString* ToString() { |
+ return String::NewFormatted( |
+ "Number of native fields changed in %s", from_.ToCString()); |
} |
+}; |
- if (!is_enum_class() && replacement.is_enum_class()) { |
- IRC->ReportError(String::Handle(String::NewFormatted( |
- "Class cannot be redefined to be a enum class: %s", |
- ToCString()))); |
- return false; |
+ |
+class TypeParametersChanged : public ClassReasonForCancelling { |
+ public: |
+ TypeParametersChanged(const Class& from, const Class& to) |
+ : ClassReasonForCancelling(from, to) {} |
+ |
+ RawString* ToString() { |
+ return String::NewFormatted( |
+ "Limitation: type parameters have changed for %s", from_.ToCString()); |
+ } |
+}; |
+ |
+ |
+class PreFinalizedConflict : public ClassReasonForCancelling { |
+ public: |
+ PreFinalizedConflict(const Class& from, const Class& to) |
+ : ClassReasonForCancelling(from, to) {} |
+ |
+ private: |
+ RawString* ToString() { |
+ return String::NewFormatted( |
+ "Original class ('%s') is prefinalized and replacement class " |
+ "('%s') is not ", |
+ from_.ToCString(), to_.ToCString()); |
+ } |
+}; |
+ |
+ |
+class InstanceSizeConflict : public ClassReasonForCancelling { |
+ public: |
+ InstanceSizeConflict(const Class& from, const Class& to) |
+ : ClassReasonForCancelling(from, to) {} |
+ |
+ private: |
+ RawString* ToString() { |
+ return String::NewFormatted( |
+ "Instance size mismatch between '%s' (%" Pd ") and replacement " |
+ "'%s' ( %" Pd ")", |
+ from_.ToCString(), |
+ from_.instance_size(), |
+ to_.ToCString(), |
+ to_.instance_size()); |
+ } |
+}; |
+ |
+ |
+class UnimplementedDeferredLibrary : public ReasonForCancelling { |
+ public: |
+ UnimplementedDeferredLibrary(const Library& from, |
+ const Library& to, |
+ const String& name) |
+ : ReasonForCancelling(), from_(from), to_(to), name_(name) {} |
+ |
+ private: |
+ const Library& from_; |
+ const Library& to_; |
+ const String& name_; |
+ |
+ RawString* ToString() { |
+ const String& lib_url = String::Handle(to_.url()); |
+ return String::NewFormatted( |
+ "Reloading support for deferred loading has not yet been implemented:" |
+ " library '%s' has deferred import '%s'", |
+ lib_url.ToCString(), name_.ToCString()); |
+ } |
+}; |
+ |
+ |
+// This is executed before interating over the instances. |
+void Class::CheckReload(const Class& replacement, |
+ IsolateReloadContext* context) const { |
+ ASSERT(IsolateReloadContext::IsSameClass(*this, replacement)); |
+ |
+ // Class cannot change enum property. |
+ if (is_enum_class() != replacement.is_enum_class()) { |
+ context->AddReasonForCancelling( |
+ new EnumClassConflict(*this, replacement)); |
+ return; |
} |
if (is_finalized()) { |
+ // Ensure the replacement class is also finalized. |
const Error& error = |
Error::Handle(replacement.EnsureIsFinalized(Thread::Current())); |
if (!error.IsNull()) { |
- IRC->ReportError(error); |
- return false; |
+ context->AddReasonForCancelling( |
+ new EnsureFinalizedError(*this, replacement, error)); |
+ return; // No reason to check other properties. |
} |
TIR_Print("Finalized replacement class for %s\n", ToCString()); |
} |
- // At this point the original and replacement must be in the same state. |
- ASSERT(is_finalized() == replacement.is_finalized()); |
+ // Native field count cannot change. |
+ if (num_native_fields() != replacement.num_native_fields()) { |
+ context->AddReasonForCancelling( |
+ new NativeFieldsConflict(*this, replacement)); |
+ return; |
+ } |
+ |
+ // Just checking. |
+ ASSERT(is_enum_class() == replacement.is_enum_class()); |
+ ASSERT(num_native_fields() == replacement.num_native_fields()); |
if (is_finalized()) { |
- // Get the field maps for both classes. These field maps walk the class |
- // hierarchy. |
- const Array& fields = |
- Array::Handle(OffsetToFieldMap()); |
- const Array& replacement_fields = |
- Array::Handle(replacement.OffsetToFieldMap()); |
- |
- // Check that the size of the instance is the same. |
- if (fields.Length() != replacement_fields.Length()) { |
- IRC->ReportError(String::Handle(String::NewFormatted( |
- "Number of instance fields changed in %s", ToCString()))); |
- return false; |
- } |
+ if (!CanReloadFinalized(replacement, context)) return; |
+ } |
+ if (is_prefinalized()) { |
+ if (!CanReloadPreFinalized(replacement, context)) return; |
+ } |
+ ASSERT(is_finalized() == replacement.is_finalized()); |
+ TIR_Print("Class `%s` can be reloaded (%" Pd " and %" Pd ")\n", |
+ ToCString(), id(), replacement.id()); |
+} |
- // Check that we have the same next field offset. This check is not |
- // redundant with the one above because the instance OffsetToFieldMap |
- // array length is based on the instance size (which may be aligned up). |
- if (next_field_offset() != replacement.next_field_offset()) { |
- IRC->ReportError(String::Handle(String::NewFormatted( |
- "Number of instance fields changed in %s", ToCString()))); |
- return false; |
- } |
- if (NumTypeArguments() != replacement.NumTypeArguments()) { |
- IRC->ReportError(String::Handle(String::NewFormatted( |
- "Number of type arguments changed in %s", ToCString()))); |
- return false; |
- } |
- // Verify that field names / offsets match across the entire hierarchy. |
- Field& field = Field::Handle(); |
- String& field_name = String::Handle(); |
- Field& replacement_field = Field::Handle(); |
- String& replacement_field_name = String::Handle(); |
- for (intptr_t i = 0; i < fields.Length(); i++) { |
- if (fields.At(i) == Field::null()) { |
- ASSERT(replacement_fields.At(i) == Field::null()); |
- continue; |
- } |
- field = Field::RawCast(fields.At(i)); |
- replacement_field = Field::RawCast(replacement_fields.At(i)); |
- field_name = field.name(); |
- replacement_field_name = replacement_field.name(); |
- if (!field_name.Equals(replacement_field_name)) { |
- IRC->ReportError(String::Handle(String::NewFormatted( |
- "Name of instance field changed ('%s' vs '%s') in '%s'", |
- field_name.ToCString(), |
- replacement_field_name.ToCString(), |
- ToCString()))); |
- return false; |
- } |
- } |
- } else if (is_prefinalized()) { |
- if (!replacement.is_prefinalized()) { |
- IRC->ReportError(String::Handle(String::NewFormatted( |
- "Original class ('%s') is prefinalized and replacement class " |
- "('%s') is not ", |
- ToCString(), replacement.ToCString()))); |
- return false; |
- } |
- if (instance_size() != replacement.instance_size()) { |
- IRC->ReportError(String::Handle(String::NewFormatted( |
- "Instance size mismatch between '%s' (%" Pd ") and replacement " |
- "'%s' ( %" Pd ")", |
- ToCString(), |
- instance_size(), |
- replacement.ToCString(), |
- replacement.instance_size()))); |
- return false; |
+bool Class::RequiresInstanceMorphing(const Class& replacement) const { |
+ // Get the field maps for both classes. These field maps walk the class |
+ // hierarchy. |
+ const Array& fields = Array::Handle(OffsetToFieldMap()); |
+ const Array& replacement_fields |
+ = Array::Handle(replacement.OffsetToFieldMap()); |
+ |
+ // Check that the size of the instance is the same. |
+ if (fields.Length() != replacement_fields.Length()) return true; |
+ |
+ // Check that we have the same next field offset. This check is not |
+ // redundant with the one above because the instance OffsetToFieldMap |
+ // array length is based on the instance size (which may be aligned up). |
+ if (next_field_offset() != replacement.next_field_offset()) return true; |
+ |
+ // Verify that field names / offsets match across the entire hierarchy. |
+ Field& field = Field::Handle(); |
+ String& field_name = String::Handle(); |
+ Field& replacement_field = Field::Handle(); |
+ String& replacement_field_name = String::Handle(); |
+ |
+ for (intptr_t i = 0; i < fields.Length(); i++) { |
+ if (fields.At(i) == Field::null()) { |
+ ASSERT(replacement_fields.At(i) == Field::null()); |
+ continue; |
} |
+ field = Field::RawCast(fields.At(i)); |
+ replacement_field = Field::RawCast(replacement_fields.At(i)); |
+ field_name = field.name(); |
+ replacement_field_name = replacement_field.name(); |
+ if (!field_name.Equals(replacement_field_name)) return true; |
} |
+ return false; |
+} |
- // native field count check. |
- if (num_native_fields() != replacement.num_native_fields()) { |
- IRC->ReportError(String::Handle(String::NewFormatted( |
- "Number of native fields changed in %s", ToCString()))); |
+ |
+bool Class::CanReloadFinalized(const Class& replacement, |
+ IsolateReloadContext* context) const { |
+ // Make sure the declaration types matches for the two classes. |
+ // ex. class A<int,B> {} cannot be replace with class A<B> {}. |
+ |
+ const AbstractType& dt = AbstractType::Handle(DeclarationType()); |
+ const AbstractType& replacement_dt = |
+ AbstractType::Handle(replacement.DeclarationType()); |
+ if (!dt.Equals(replacement_dt)) { |
+ context->AddReasonForCancelling( |
+ new TypeParametersChanged(*this, replacement)); |
return false; |
} |
+ if (RequiresInstanceMorphing(replacement)) { |
+ context->AddInstanceMorpher(new InstanceMorpher(*this, replacement)); |
+ } |
+ return true; |
+} |
- // TODO(johnmccutchan) type parameter count check. |
- TIR_Print("Class `%s` can be reloaded (%" Pd " and %" Pd ")\n", |
- ToCString(), |
- id(), |
- replacement.id()); |
+bool Class::CanReloadPreFinalized(const Class& replacement, |
+ IsolateReloadContext* context) const { |
+ // The replacement class must also prefinalized. |
+ if (!replacement.is_prefinalized()) { |
+ context->AddReasonForCancelling( |
+ new PreFinalizedConflict(*this, replacement)); |
+ return false; |
+ } |
+ // Check the instance sizes are equal. |
+ if (instance_size() != replacement.instance_size()) { |
+ context->AddReasonForCancelling( |
+ new InstanceSizeConflict(*this, replacement)); |
+ return false; |
+ } |
return true; |
} |
-bool Library::CanReload(const Library& replacement) const { |
+void Library::CheckReload(const Library& replacement, |
+ IsolateReloadContext* context) const { |
// TODO(26878): If the replacement library uses deferred loading, |
// reject it. We do not yet support reloading deferred libraries. |
LibraryPrefix& prefix = LibraryPrefix::Handle(); |
@@ -492,21 +601,18 @@ bool Library::CanReload(const Library& replacement) const { |
while (it.HasNext()) { |
prefix = it.GetNext(); |
if (prefix.is_deferred_load()) { |
- const String& lib_url = String::Handle(replacement.url()); |
const String& prefix_name = String::Handle(prefix.name()); |
- IRC->ReportError(String::Handle(String::NewFormatted( |
- "Reloading support for deferred loading has not yet been implemented:" |
- " library '%s' has deferred import '%s'", |
- lib_url.ToCString(), prefix_name.ToCString()))); |
- return false; |
+ context->AddReasonForCancelling( |
+ new UnimplementedDeferredLibrary(*this, replacement, prefix_name)); |
+ return; |
} |
} |
- return true; |
} |
static const Function* static_call_target = NULL; |
+ |
void ICData::Reset() const { |
if (is_static_call()) { |
const Function& old_target = Function::Handle(GetTargetAt(0)); |