| OLD | NEW |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/object.h" | 5 #include "vm/object.h" |
| 6 | 6 |
| 7 #include "vm/hash_table.h" |
| 7 #include "vm/isolate_reload.h" | 8 #include "vm/isolate_reload.h" |
| 8 #include "vm/log.h" | 9 #include "vm/log.h" |
| 9 #include "vm/resolver.h" | 10 #include "vm/resolver.h" |
| 10 #include "vm/symbols.h" | 11 #include "vm/symbols.h" |
| 11 | 12 |
| 12 namespace dart { | 13 namespace dart { |
| 13 | 14 |
| 14 #ifndef PRODUCT | 15 #ifndef PRODUCT |
| 15 | 16 |
| 16 DECLARE_FLAG(bool, trace_reload); | 17 DECLARE_FLAG(bool, trace_reload); |
| 18 DECLARE_FLAG(bool, trace_reload_verbose); |
| 17 DECLARE_FLAG(bool, two_args_smi_icd); | 19 DECLARE_FLAG(bool, two_args_smi_icd); |
| 18 | 20 |
| 19 #define IRC (Isolate::Current()->reload_context()) | 21 #define IRC (Isolate::Current()->reload_context()) |
| 20 | 22 |
| 21 class ObjectReloadUtils : public AllStatic { | 23 class ObjectReloadUtils : public AllStatic { |
| 22 static void DumpLibraryDictionary(const Library& lib) { | 24 static void DumpLibraryDictionary(const Library& lib) { |
| 23 DictionaryIterator it(lib); | 25 DictionaryIterator it(lib); |
| 24 Object& entry = Object::Handle(); | 26 Object& entry = Object::Handle(); |
| 25 String& name = String::Handle(); | 27 String& name = String::Handle(); |
| 26 TIR_Print("Dumping dictionary for %s\n", lib.ToCString()); | 28 TIR_Print("Dumping dictionary for %s\n", lib.ToCString()); |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 134 reload_context->AddStaticFieldMapping(old_field, field); | 136 reload_context->AddStaticFieldMapping(old_field, field); |
| 135 } | 137 } |
| 136 } | 138 } |
| 137 } | 139 } |
| 138 } | 140 } |
| 139 } | 141 } |
| 140 | 142 |
| 141 | 143 |
| 142 void Class::CopyCanonicalConstants(const Class& old_cls) const { | 144 void Class::CopyCanonicalConstants(const Class& old_cls) const { |
| 143 if (is_enum_class()) { | 145 if (is_enum_class()) { |
| 146 // We do not copy enum classes's canonical constants because we explicitly |
| 147 // become the old enum values to the new enum values. |
| 144 return; | 148 return; |
| 145 } | 149 } |
| 146 #if defined(DEBUG) | 150 #if defined(DEBUG) |
| 147 { | 151 { |
| 148 // Class has no canonical constants allocated. | 152 // Class has no canonical constants allocated. |
| 149 const Array& my_constants = Array::Handle(constants()); | 153 const Array& my_constants = Array::Handle(constants()); |
| 150 ASSERT(my_constants.Length() == 0); | 154 ASSERT(my_constants.Length() == 0); |
| 151 } | 155 } |
| 152 #endif // defined(DEBUG). | 156 #endif // defined(DEBUG). |
| 153 // Copy old constants into new class. | 157 // Copy old constants into new class. |
| (...skipping 10 matching lines...) Expand all Loading... |
| 164 | 168 |
| 165 void Class::CopyCanonicalType(const Class& old_cls) const { | 169 void Class::CopyCanonicalType(const Class& old_cls) const { |
| 166 const Type& old_canonical_type = Type::Handle(old_cls.canonical_type()); | 170 const Type& old_canonical_type = Type::Handle(old_cls.canonical_type()); |
| 167 if (old_canonical_type.IsNull()) { | 171 if (old_canonical_type.IsNull()) { |
| 168 return; | 172 return; |
| 169 } | 173 } |
| 170 set_canonical_type(old_canonical_type); | 174 set_canonical_type(old_canonical_type); |
| 171 } | 175 } |
| 172 | 176 |
| 173 | 177 |
| 174 static intptr_t IndexOfEnum(const Array& enum_names, const String& name) { | 178 class EnumMapTraits { |
| 175 ASSERT(!enum_names.IsNull()); | 179 public: |
| 176 ASSERT(!name.IsNull()); | 180 static bool ReportStats() { return false; } |
| 177 String& enum_name = String::Handle(); | 181 static const char* Name() { return "EnumMapTraits"; } |
| 178 for (intptr_t i = 0; i < enum_names.Length(); i++) { | 182 |
| 179 enum_name = String::RawCast(enum_names.At(i)); | 183 static bool IsMatch(const Object& a, const Object& b) { |
| 180 ASSERT(!enum_name.IsNull()); | 184 return a.raw() == b.raw(); |
| 181 if (enum_name.Equals(name)) { | |
| 182 return i; | |
| 183 } | |
| 184 } | 185 } |
| 185 | 186 |
| 186 return -1; | 187 static uword Hash(const Object& obj) { |
| 187 } | 188 ASSERT(obj.IsString()); |
| 189 return String::Cast(obj).Hash(); |
| 190 } |
| 191 }; |
| 188 | 192 |
| 189 | 193 |
| 190 static void UpdateEnumIndex(const Instance& enum_value, | 194 // Given an old enum class, add become mappings from old values to new values. |
| 191 const Field& enum_index_field, | 195 // Some notes about how we reload enums below: |
| 192 const intptr_t index) { | 196 // |
| 193 enum_value.SetField(enum_index_field, Smi::Handle(Smi::New(index))); | 197 // When an enum is reloaded the following three things can happen, possibly |
| 194 } | 198 // simultaneously. |
| 195 | 199 // |
| 196 | 200 // 1) A new enum value is added. |
| 197 // TODO(johnmccutchan): The code in the class finalizer canonicalizes all | 201 // This case is handled automatically. |
| 198 // instances and the values array. We probably should do the same thing. | 202 // 2) Enum values are reordered. |
| 203 // We pair old and new enums and the old enums 'become' the new ones so |
| 204 // the ordering is always correct (i.e. enum indicies match slots in values |
| 205 // array) |
| 206 // 3) An existing enum value is removed. |
| 207 // We leave old enum values that have no mapping to the reloaded class |
| 208 // in the heap. This means that if a programmer does the following: |
| 209 // enum Foo { A, B }; var x = Foo.A; |
| 210 // *reload* |
| 211 // enum Foo { B }; |
| 212 // *reload* |
| 213 // enum Foo { A, B }; expect(identical(x, Foo.A)); |
| 214 // The program will fail because we were not able to pair Foo.A on the second |
| 215 // reload. |
| 216 // |
| 217 // Deleted enum values still in the heap continue to function but their |
| 218 // index field will not be valid. |
| 199 void Class::ReplaceEnum(const Class& old_enum) const { | 219 void Class::ReplaceEnum(const Class& old_enum) const { |
| 200 // We only do this for finalized enum classes. | 220 // We only do this for finalized enum classes. |
| 201 ASSERT(is_enum_class()); | 221 ASSERT(is_enum_class()); |
| 202 ASSERT(old_enum.is_enum_class()); | 222 ASSERT(old_enum.is_enum_class()); |
| 203 ASSERT(is_finalized()); | 223 ASSERT(is_finalized()); |
| 204 ASSERT(old_enum.is_finalized()); | 224 ASSERT(old_enum.is_finalized()); |
| 205 | 225 |
| 206 Thread* thread = Thread::Current(); | 226 Thread* thread = Thread::Current(); |
| 227 Zone* zone = thread->zone(); |
| 207 IsolateReloadContext* reload_context = Isolate::Current()->reload_context(); | 228 IsolateReloadContext* reload_context = Isolate::Current()->reload_context(); |
| 208 ASSERT(reload_context != NULL); | 229 ASSERT(reload_context != NULL); |
| 209 | 230 |
| 210 TIR_Print("ReplaceEnum `%s` (%" Pd " and %" Pd ")\n", | 231 Array& enum_fields = Array::Handle(zone); |
| 211 ToCString(), id(), old_enum.id()); | 232 Field& field = Field::Handle(zone); |
| 233 String& enum_ident = String::Handle(); |
| 234 Instance& old_enum_value = Instance::Handle(zone); |
| 235 Instance& enum_value = Instance::Handle(zone); |
| 212 | 236 |
| 213 // Grab '_enum_names' from |old_enum|. | 237 Array& enum_map_storage = Array::Handle(zone, |
| 214 const Field& old_enum_names_field = Field::Handle( | 238 HashTables::New<UnorderedHashMap<EnumMapTraits> >(4)); |
| 215 old_enum.LookupStaticFieldAllowPrivate(Symbols::_EnumNames())); | 239 ASSERT(!enum_map_storage.IsNull()); |
| 216 ASSERT(!old_enum_names_field.IsNull()); | |
| 217 const Array& old_enum_names = | |
| 218 Array::Handle(Array::RawCast(old_enum_names_field.StaticValue())); | |
| 219 ASSERT(!old_enum_names.IsNull()); | |
| 220 | 240 |
| 221 // Grab 'values' from |old_enum|. | 241 TIR_Print("Replacing enum `%s`\n", String::Handle(Name()).ToCString()); |
| 222 const Field& old_enum_values_field = Field::Handle( | |
| 223 old_enum.LookupStaticFieldAllowPrivate(Symbols::Values())); | |
| 224 ASSERT(!old_enum_values_field.IsNull()); | |
| 225 const Array& old_enum_values = | |
| 226 Array::Handle(Array::RawCast(old_enum_values_field.StaticValue())); | |
| 227 ASSERT(!old_enum_values.IsNull()); | |
| 228 | 242 |
| 229 // Grab _enum_names from |this|. | 243 { |
| 230 const Field& enum_names_field = Field::Handle( | 244 UnorderedHashMap<EnumMapTraits> enum_map(enum_map_storage.raw()); |
| 231 LookupStaticFieldAllowPrivate(Symbols::_EnumNames())); | 245 // Build a map of all enum name -> old enum instance. |
| 232 ASSERT(!enum_names_field.IsNull()); | 246 enum_fields = old_enum.fields(); |
| 233 Array& enum_names = | 247 for (intptr_t i = 0; i < enum_fields.Length(); i++) { |
| 234 Array::Handle(Array::RawCast(enum_names_field.StaticValue())); | 248 field = Field::RawCast(enum_fields.At(i)); |
| 235 ASSERT(!enum_names.IsNull()); | 249 enum_ident = field.name(); |
| 236 | 250 if (!field.is_static()) { |
| 237 // Grab values from |this|. | 251 // Enum instances are only held in static fields. |
| 238 const Field& enum_values_field = Field::Handle( | 252 continue; |
| 239 LookupStaticFieldAllowPrivate(Symbols::Values())); | 253 } |
| 240 ASSERT(!enum_values_field.IsNull()); | 254 if (enum_ident.Equals(Symbols::Values())) { |
| 241 Array& enum_values = | 255 // Non-enum instance. |
| 242 Array::Handle(Array::RawCast(enum_values_field.StaticValue())); | 256 continue; |
| 243 ASSERT(!enum_values.IsNull()); | 257 } |
| 244 | 258 old_enum_value = field.StaticValue(); |
| 245 // Grab the |index| field. | 259 ASSERT(!old_enum_value.IsNull()); |
| 246 const Field& index_field = | 260 VTIR_Print("Element %s being added to mapping\n", enum_ident.ToCString()); |
| 247 Field::Handle(old_enum.LookupInstanceField(Symbols::Index())); | 261 bool update = enum_map.UpdateOrInsert(enum_ident, old_enum_value); |
| 248 ASSERT(!index_field.IsNull()); | 262 VTIR_Print("Element %s added to mapping\n", enum_ident.ToCString()); |
| 249 | 263 ASSERT(!update); |
| 250 // Build list of enum from |old_enum| that aren't present in |this|. | |
| 251 // This array holds pairs: (name, value). | |
| 252 const GrowableObjectArray& to_add = | |
| 253 GrowableObjectArray::Handle(GrowableObjectArray::New(Heap::kOld)); | |
| 254 const String& enum_class_name = String::Handle(UserVisibleName()); | |
| 255 String& enum_name = String::Handle(); | |
| 256 String& enum_field_name = String::Handle(); | |
| 257 Object& enum_value = Object::Handle(); | |
| 258 Field& enum_field = Field::Handle(); | |
| 259 | |
| 260 TIR_Print("New version of enum has %" Pd " elements\n", | |
| 261 enum_values.Length()); | |
| 262 TIR_Print("Old version of enum had %" Pd " elements\n", | |
| 263 old_enum_values.Length()); | |
| 264 | |
| 265 for (intptr_t i = 0; i < old_enum_names.Length(); i++) { | |
| 266 enum_name = String::RawCast(old_enum_names.At(i)); | |
| 267 const intptr_t index_in_new_cls = IndexOfEnum(enum_names, enum_name); | |
| 268 if (index_in_new_cls < 0) { | |
| 269 // Doesn't exist in new enum, add. | |
| 270 TIR_Print("Adding enum value `%s` to %s\n", | |
| 271 enum_name.ToCString(), | |
| 272 this->ToCString()); | |
| 273 enum_value = old_enum_values.At(i); | |
| 274 ASSERT(!enum_value.IsNull()); | |
| 275 to_add.Add(enum_name); | |
| 276 to_add.Add(enum_value); | |
| 277 } else { | |
| 278 // Exists in both the new and the old. | |
| 279 TIR_Print("Moving enum value `%s` to %" Pd "\n", | |
| 280 enum_name.ToCString(), | |
| 281 index_in_new_cls); | |
| 282 // Grab old value. | |
| 283 enum_value = old_enum_values.At(i); | |
| 284 // Update index to the be new index. | |
| 285 UpdateEnumIndex(Instance::Cast(enum_value), | |
| 286 index_field, | |
| 287 index_in_new_cls); | |
| 288 // Chop off the 'EnumClass.' | |
| 289 enum_field_name = String::SubString(enum_name, | |
| 290 enum_class_name.Length() + 1); | |
| 291 ASSERT(!enum_field_name.IsNull()); | |
| 292 // Grab the static field. | |
| 293 enum_field = LookupStaticFieldAllowPrivate(enum_field_name); | |
| 294 ASSERT(!enum_field.IsNull()); | |
| 295 // Use old value with updated index. | |
| 296 enum_field.SetStaticValue(Instance::Cast(enum_value), true); | |
| 297 enum_values.SetAt(index_in_new_cls, enum_value); | |
| 298 enum_names.SetAt(index_in_new_cls, enum_name); | |
| 299 } | 264 } |
| 265 // The storage given to the map may have been reallocated, remember the new |
| 266 // address. |
| 267 enum_map_storage = enum_map.Release().raw(); |
| 300 } | 268 } |
| 301 | 269 |
| 302 if (to_add.Length() == 0) { | 270 bool enums_deleted = false; |
| 303 // Nothing to do. | 271 { |
| 304 TIR_Print("Found no missing enums in %s\n", ToCString()); | 272 UnorderedHashMap<EnumMapTraits> enum_map(enum_map_storage.raw()); |
| 305 return; | 273 // Add a become mapping from the old instances to the new instances. |
| 274 enum_fields = fields(); |
| 275 for (intptr_t i = 0; i < enum_fields.Length(); i++) { |
| 276 field = Field::RawCast(enum_fields.At(i)); |
| 277 enum_ident = field.name(); |
| 278 if (!field.is_static()) { |
| 279 // Enum instances are only held in static fields. |
| 280 continue; |
| 281 } |
| 282 if (enum_ident.Equals(Symbols::Values())) { |
| 283 // Non-enum instance. |
| 284 continue; |
| 285 } |
| 286 enum_value = field.StaticValue(); |
| 287 ASSERT(!enum_value.IsNull()); |
| 288 old_enum_value ^= enum_map.GetOrNull(enum_ident); |
| 289 if (old_enum_value.IsNull()) { |
| 290 VTIR_Print("New element %s was not found in mapping\n", |
| 291 enum_ident.ToCString()); |
| 292 } else { |
| 293 VTIR_Print("Adding element `%s` to become mapping\n", |
| 294 enum_ident.ToCString()); |
| 295 bool removed = enum_map.Remove(enum_ident); |
| 296 ASSERT(removed); |
| 297 reload_context->AddEnumBecomeMapping(old_enum_value, enum_value); |
| 298 } |
| 299 } |
| 300 enums_deleted = enum_map.NumOccupied() > 0; |
| 301 // The storage given to the map may have been reallocated, remember the new |
| 302 // address. |
| 303 enum_map_storage = enum_map.Release().raw(); |
| 306 } | 304 } |
| 307 | 305 |
| 308 // Grow the values and enum_names arrays. | 306 if (enums_deleted && FLAG_trace_reload_verbose) { |
| 309 const intptr_t offset = enum_names.Length(); | 307 // TODO(johnmccutchan): Add this to the reload 'notices' list. |
| 310 const intptr_t num_to_add = to_add.Length() / 2; | 308 VTIR_Print("The following enum values were deleted and are forever lost in " |
| 311 ASSERT(offset == enum_values.Length()); | 309 "the heap:\n"); |
| 312 enum_names = Array::Grow(enum_names, | 310 UnorderedHashMap<EnumMapTraits> enum_map(enum_map_storage.raw()); |
| 313 enum_names.Length() + num_to_add, | 311 UnorderedHashMap<EnumMapTraits>::Iterator it(&enum_map); |
| 314 Heap::kOld); | 312 while (it.MoveNext()) { |
| 315 enum_values = Array::Grow(enum_values, | 313 const intptr_t entry = it.Current(); |
| 316 enum_values.Length() + num_to_add, | 314 enum_ident = String::RawCast(enum_map.GetKey(entry)); |
| 317 Heap::kOld); | 315 ASSERT(!enum_ident.IsNull()); |
| 318 | 316 } |
| 319 // Install new names and values into the grown arrays. Also, update | 317 enum_map.Release(); |
| 320 // the index of the new enum values and add static fields for the new | |
| 321 // enum values. | |
| 322 Field& enum_value_field = Field::Handle(); | |
| 323 for (intptr_t i = 0; i < num_to_add; i++) { | |
| 324 const intptr_t target_index = offset + i; | |
| 325 enum_name = String::RawCast(to_add.At(i * 2)); | |
| 326 enum_value = to_add.At(i * 2 + 1); | |
| 327 | |
| 328 // Update the enum value's index into the new arrays. | |
| 329 TIR_Print("Updating index of %s in %s to %" Pd "\n", | |
| 330 enum_name.ToCString(), | |
| 331 ToCString(), | |
| 332 target_index); | |
| 333 UpdateEnumIndex(Instance::Cast(enum_value), index_field, target_index); | |
| 334 | |
| 335 enum_names.SetAt(target_index, enum_name); | |
| 336 enum_values.SetAt(target_index, enum_value); | |
| 337 | |
| 338 // Install new static field into class. | |
| 339 // Chop off the 'EnumClass.' | |
| 340 enum_field_name = String::SubString(enum_name, | |
| 341 enum_class_name.Length() + 1); | |
| 342 ASSERT(!enum_field_name.IsNull()); | |
| 343 enum_field_name = Symbols::New(thread, enum_field_name); | |
| 344 enum_value_field = Field::New(enum_field_name, | |
| 345 /* is_static = */ true, | |
| 346 /* is_final = */ true, | |
| 347 /* is_const = */ true, | |
| 348 /* is_reflectable = */ true, | |
| 349 *this, | |
| 350 Object::dynamic_type(), | |
| 351 token_pos()); | |
| 352 enum_value_field.set_has_initializer(false); | |
| 353 enum_value_field.SetStaticValue(Instance::Cast(enum_value), true); | |
| 354 enum_value_field.RecordStore(Instance::Cast(enum_value)); | |
| 355 AddField(enum_value_field); | |
| 356 } | 318 } |
| 357 | |
| 358 // Replace the arrays stored in the static fields. | |
| 359 enum_names_field.SetStaticValue(enum_names, true); | |
| 360 enum_values_field.SetStaticValue(enum_values, true); | |
| 361 } | 319 } |
| 362 | 320 |
| 363 | 321 |
| 364 void Class::PatchFieldsAndFunctions() const { | 322 void Class::PatchFieldsAndFunctions() const { |
| 365 // Move all old functions and fields to a patch class so that they | 323 // Move all old functions and fields to a patch class so that they |
| 366 // still refer to their original script. | 324 // still refer to their original script. |
| 367 const PatchClass& patch = | 325 const PatchClass& patch = |
| 368 PatchClass::Handle(PatchClass::New(*this, Script::Handle(script()))); | 326 PatchClass::Handle(PatchClass::New(*this, Script::Handle(script()))); |
| 369 ASSERT(!patch.IsNull()); | 327 ASSERT(!patch.IsNull()); |
| 370 | 328 |
| (...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 602 // This can be incorrect if the call site was an unqualified invocation. | 560 // This can be incorrect if the call site was an unqualified invocation. |
| 603 const Class& cls = Class::Handle(old_target.Owner()); | 561 const Class& cls = Class::Handle(old_target.Owner()); |
| 604 new_target = cls.LookupStaticFunction(selector); | 562 new_target = cls.LookupStaticFunction(selector); |
| 605 } | 563 } |
| 606 | 564 |
| 607 const Array& args_desc_array = Array::Handle(arguments_descriptor()); | 565 const Array& args_desc_array = Array::Handle(arguments_descriptor()); |
| 608 ArgumentsDescriptor args_desc(args_desc_array); | 566 ArgumentsDescriptor args_desc(args_desc_array); |
| 609 if (new_target.IsNull() || | 567 if (new_target.IsNull() || |
| 610 !new_target.AreValidArguments(args_desc, NULL)) { | 568 !new_target.AreValidArguments(args_desc, NULL)) { |
| 611 // TODO(rmacnak): Patch to a NSME stub. | 569 // TODO(rmacnak): Patch to a NSME stub. |
| 612 TIR_Print("Cannot rebind static call to %s from %s\n", | 570 VTIR_Print("Cannot rebind static call to %s from %s\n", |
| 613 old_target.ToCString(), | 571 old_target.ToCString(), |
| 614 Object::Handle(Owner()).ToCString()); | 572 Object::Handle(Owner()).ToCString()); |
| 615 return; | 573 return; |
| 616 } | 574 } |
| 617 ClearAndSetStaticTarget(new_target); | 575 ClearAndSetStaticTarget(new_target); |
| 618 } else { | 576 } else { |
| 619 ClearWithSentinel(); | 577 ClearWithSentinel(); |
| 620 } | 578 } |
| 621 } | 579 } |
| 622 | 580 |
| 623 #endif // !PRODUCT | 581 #endif // !PRODUCT |
| 624 | 582 |
| 625 } // namespace dart. | 583 } // namespace dart. |
| OLD | NEW |