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

Side by Side Diff: runtime/vm/object_reload.cc

Issue 2153143002: Rework how enums are implemented and reloaded (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 5 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 unified diff | Download patch
« no previous file with comments | « runtime/vm/object.cc ('k') | runtime/vm/parser.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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.
OLDNEW
« no previous file with comments | « runtime/vm/object.cc ('k') | runtime/vm/parser.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698