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 |