OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 #include "vm/object.h" | |
6 | |
7 #include "vm/isolate_reload.h" | |
8 #include "vm/log.h" | |
9 #include "vm/resolver.h" | |
10 #include "vm/symbols.h" | |
11 | |
12 namespace dart { | |
13 | |
14 #ifndef PRODUCT | |
15 | |
16 DECLARE_FLAG(bool, trace_reload); | |
17 DECLARE_FLAG(bool, two_args_smi_icd); | |
18 | |
19 #define IRC (Isolate::Current()->reload_context()) | |
20 | |
21 class ObjectReloadUtils : public AllStatic { | |
22 static void DumpLibraryDictionary(const Library& lib) { | |
23 DictionaryIterator it(lib); | |
24 Object& entry = Object::Handle(); | |
25 String& name = String::Handle(); | |
26 TIR_Print("Dumping dictionary for %s\n", lib.ToCString()); | |
27 while (it.HasNext()) { | |
28 entry = it.GetNext(); | |
29 name = entry.DictionaryName(); | |
30 TIR_Print("%s -> %s\n", name.ToCString(), entry.ToCString()); | |
31 } | |
32 } | |
33 }; | |
34 | |
35 | |
36 void Function::Reparent(const Class& new_cls) const { | |
37 set_owner(new_cls); | |
38 } | |
39 | |
40 | |
41 void Function::ZeroEdgeCounters() const { | |
42 const Array& saved_ic_data = Array::Handle(ic_data_array()); | |
43 if (saved_ic_data.IsNull()) { | |
44 return; | |
45 } | |
46 const intptr_t saved_ic_datalength = saved_ic_data.Length(); | |
47 ASSERT(saved_ic_datalength > 0); | |
48 const Array& edge_counters_array = | |
49 Array::Handle(Array::RawCast(saved_ic_data.At(0))); | |
50 ASSERT(!edge_counters_array.IsNull()); | |
51 // Fill edge counters array with zeros. | |
52 const Smi& zero = Smi::Handle(Smi::New(0)); | |
53 for (intptr_t i = 0; i < edge_counters_array.Length(); i++) { | |
54 edge_counters_array.SetAt(i, zero); | |
55 } | |
56 } | |
57 | |
58 | |
59 static void ClearICs(const Function& function, const Code& code) { | |
60 if (function.ic_data_array() == Array::null()) { | |
61 return; // Already reset in an earlier round. | |
62 } | |
63 | |
64 Thread* thread = Thread::Current(); | |
65 Zone* zone = thread->zone(); | |
66 | |
67 ZoneGrowableArray<const ICData*>* ic_data_array = | |
68 new(zone) ZoneGrowableArray<const ICData*>(); | |
69 function.RestoreICDataMap(ic_data_array, false /* clone ic-data */); | |
70 if (ic_data_array->length() == 0) { | |
71 return; | |
72 } | |
73 const PcDescriptors& descriptors = | |
74 PcDescriptors::Handle(code.pc_descriptors()); | |
75 PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kIcCall | | |
76 RawPcDescriptors::kUnoptStaticCall); | |
77 while (iter.MoveNext()) { | |
78 const ICData* ic_data = (*ic_data_array)[iter.DeoptId()]; | |
79 if (ic_data == NULL) { | |
80 continue; | |
81 } | |
82 bool is_static_call = iter.Kind() == RawPcDescriptors::kUnoptStaticCall; | |
83 ic_data->Reset(is_static_call); | |
84 } | |
85 } | |
86 | |
87 | |
88 void Function::FillICDataWithSentinels(const Code& code) const { | |
89 ASSERT(code.raw() == CurrentCode()); | |
90 ClearICs(*this, code); | |
91 } | |
92 | |
93 | |
94 void Class::CopyStaticFieldValues(const Class& old_cls) const { | |
95 // We only update values for non-enum classes. | |
96 const bool update_values = !is_enum_class(); | |
97 | |
98 IsolateReloadContext* reload_context = Isolate::Current()->reload_context(); | |
99 ASSERT(reload_context != NULL); | |
100 | |
101 const Array& old_field_list = Array::Handle(old_cls.fields()); | |
102 Field& old_field = Field::Handle(); | |
103 String& old_name = String::Handle(); | |
104 | |
105 const Array& field_list = Array::Handle(fields()); | |
106 Field& field = Field::Handle(); | |
107 String& name = String::Handle(); | |
108 | |
109 Instance& value = Instance::Handle(); | |
110 for (intptr_t i = 0; i < field_list.Length(); i++) { | |
111 field = Field::RawCast(field_list.At(i)); | |
112 name = field.name(); | |
113 if (field.is_static()) { | |
114 // Find the corresponding old field, if it exists, and migrate | |
115 // over the field value. | |
116 for (intptr_t j = 0; j < old_field_list.Length(); j++) { | |
117 old_field = Field::RawCast(old_field_list.At(j)); | |
118 old_name = old_field.name(); | |
119 if (name.Equals(old_name)) { | |
120 if (update_values) { | |
121 value = old_field.StaticValue(); | |
122 field.SetStaticValue(value); | |
123 } | |
124 reload_context->AddStaticFieldMapping(old_field, field); | |
125 } | |
126 } | |
127 } | |
128 } | |
129 } | |
130 | |
131 | |
132 void Class::CopyCanonicalConstants(const Class& old_cls) const { | |
133 if (is_enum_class()) { | |
134 return; | |
135 } | |
136 #if defined(DEBUG) | |
137 { | |
138 // Class has no canonical constants allocated. | |
139 const Array& my_constants = Array::Handle(constants()); | |
140 ASSERT(my_constants.Length() == 0); | |
141 } | |
142 #endif // defined(DEBUG). | |
143 // Copy old constants into new class. | |
144 const Array& old_constants = Array::Handle(old_cls.constants()); | |
145 if (old_constants.IsNull() || old_constants.Length() == 0) { | |
146 return; | |
147 } | |
148 TIR_Print("Copied %" Pd " canonical constants for class `%s`\n", | |
149 old_constants.Length(), | |
150 ToCString()); | |
151 set_constants(old_constants); | |
152 } | |
153 | |
154 | |
155 void Class::CopyCanonicalTypes(const Class& old_cls) const { | |
156 const Object& old_canonical_types = Object::Handle(old_cls.canonical_types()); | |
157 if (old_canonical_types.IsNull()) { | |
158 return; | |
159 } | |
160 set_canonical_types(old_canonical_types); | |
161 } | |
162 | |
163 | |
164 static intptr_t IndexOfEnum(const Array& enum_names, const String& name) { | |
165 ASSERT(!enum_names.IsNull()); | |
166 ASSERT(!name.IsNull()); | |
167 String& enum_name = String::Handle(); | |
168 for (intptr_t i = 0; i < enum_names.Length(); i++) { | |
169 enum_name = String::RawCast(enum_names.At(i)); | |
170 ASSERT(!enum_name.IsNull()); | |
171 if (enum_name.Equals(name)) { | |
172 return i; | |
173 } | |
174 } | |
175 | |
176 return -1; | |
177 } | |
178 | |
179 | |
180 static void UpdateEnumIndex(const Instance& enum_value, | |
181 const Field& enum_index_field, | |
182 const intptr_t index) { | |
183 enum_value.SetField(enum_index_field, Smi::Handle(Smi::New(index))); | |
184 } | |
185 | |
186 | |
187 // TODO(johnmccutchan): The code in the class finalizer canonicalizes all | |
188 // instances and the values array. We probably should do the same thing. | |
189 void Class::ReplaceEnum(const Class& old_enum) const { | |
190 // We only do this for finalized enum classes. | |
191 ASSERT(is_enum_class()); | |
192 ASSERT(old_enum.is_enum_class()); | |
193 ASSERT(is_finalized()); | |
194 ASSERT(old_enum.is_finalized()); | |
195 | |
196 Thread* thread = Thread::Current(); | |
197 IsolateReloadContext* reload_context = Isolate::Current()->reload_context(); | |
198 ASSERT(reload_context != NULL); | |
199 | |
200 TIR_Print("ReplaceEnum `%s` (%" Pd " and %" Pd ")\n", | |
201 ToCString(), id(), old_enum.id()); | |
202 | |
203 // Grab '_enum_names' from |old_enum|. | |
204 const Field& old_enum_names_field = Field::Handle( | |
205 old_enum.LookupStaticFieldAllowPrivate(Symbols::_EnumNames())); | |
206 ASSERT(!old_enum_names_field.IsNull()); | |
207 const Array& old_enum_names = | |
208 Array::Handle(Array::RawCast(old_enum_names_field.StaticValue())); | |
209 ASSERT(!old_enum_names.IsNull()); | |
210 | |
211 // Grab 'values' from |old_enum|. | |
212 const Field& old_enum_values_field = Field::Handle( | |
213 old_enum.LookupStaticField(Symbols::Values())); | |
214 ASSERT(!old_enum_values_field.IsNull()); | |
215 const Array& old_enum_values = | |
216 Array::Handle(Array::RawCast(old_enum_values_field.StaticValue())); | |
217 ASSERT(!old_enum_values.IsNull()); | |
218 | |
219 // Grab _enum_names from |this|. | |
220 const Field& enum_names_field = Field::Handle( | |
221 LookupStaticFieldAllowPrivate(Symbols::_EnumNames())); | |
222 ASSERT(!enum_names_field.IsNull()); | |
223 Array& enum_names = | |
224 Array::Handle(Array::RawCast(enum_names_field.StaticValue())); | |
225 ASSERT(!enum_names.IsNull()); | |
226 | |
227 // Grab values from |this|. | |
228 const Field& enum_values_field = Field::Handle( | |
229 LookupStaticField(Symbols::Values())); | |
230 ASSERT(!enum_values_field.IsNull()); | |
231 Array& enum_values = | |
232 Array::Handle(Array::RawCast(enum_values_field.StaticValue())); | |
233 ASSERT(!enum_values.IsNull()); | |
234 | |
235 // Grab the |index| field. | |
236 const Field& index_field = | |
237 Field::Handle(old_enum.LookupInstanceField(Symbols::Index())); | |
238 ASSERT(!index_field.IsNull()); | |
239 | |
240 // Build list of enum from |old_enum| that aren't present in |this|. | |
241 // This array holds pairs: (name, value). | |
242 const GrowableObjectArray& to_add = | |
243 GrowableObjectArray::Handle(GrowableObjectArray::New(Heap::kOld)); | |
244 const String& enum_class_name = String::Handle(UserVisibleName()); | |
245 String& enum_name = String::Handle(); | |
246 String& enum_field_name = String::Handle(); | |
247 Object& enum_value = Object::Handle(); | |
248 Field& enum_field = Field::Handle(); | |
249 | |
250 TIR_Print("New version of enum has %" Pd " elements\n", | |
251 enum_values.Length()); | |
252 TIR_Print("Old version of enum had %" Pd " elements\n", | |
253 old_enum_values.Length()); | |
254 | |
255 for (intptr_t i = 0; i < old_enum_names.Length(); i++) { | |
256 enum_name = String::RawCast(old_enum_names.At(i)); | |
257 const intptr_t index_in_new_cls = IndexOfEnum(enum_names, enum_name); | |
258 if (index_in_new_cls < 0) { | |
259 // Doesn't exist in new enum, add. | |
260 TIR_Print("Adding enum value `%s` to %s\n", | |
261 enum_name.ToCString(), | |
262 this->ToCString()); | |
263 enum_value = old_enum_values.At(i); | |
264 ASSERT(!enum_value.IsNull()); | |
265 to_add.Add(enum_name); | |
266 to_add.Add(enum_value); | |
267 } else { | |
268 // Exists in both the new and the old. | |
269 TIR_Print("Moving enum value `%s` to %" Pd "\n", | |
270 enum_name.ToCString(), | |
271 index_in_new_cls); | |
272 // Grab old value. | |
273 enum_value = old_enum_values.At(i); | |
274 // Update index to the be new index. | |
275 UpdateEnumIndex(Instance::Cast(enum_value), | |
276 index_field, | |
277 index_in_new_cls); | |
278 // Chop off the 'EnumClass.' | |
279 enum_field_name = String::SubString(enum_name, | |
280 enum_class_name.Length() + 1); | |
281 ASSERT(!enum_field_name.IsNull()); | |
282 // Grab the static field. | |
283 enum_field = LookupStaticField(enum_field_name); | |
284 ASSERT(!enum_field.IsNull()); | |
285 // Use old value with updated index. | |
286 enum_field.SetStaticValue(Instance::Cast(enum_value), true); | |
287 enum_values.SetAt(index_in_new_cls, enum_value); | |
288 enum_names.SetAt(index_in_new_cls, enum_name); | |
289 } | |
290 } | |
291 | |
292 if (to_add.Length() == 0) { | |
293 // Nothing to do. | |
294 TIR_Print("Found no missing enums in %s\n", ToCString()); | |
295 return; | |
296 } | |
297 | |
298 // Grow the values and enum_names arrays. | |
299 const intptr_t offset = enum_names.Length(); | |
300 const intptr_t num_to_add = to_add.Length() / 2; | |
301 ASSERT(offset == enum_values.Length()); | |
302 enum_names = Array::Grow(enum_names, | |
303 enum_names.Length() + num_to_add, | |
304 Heap::kOld); | |
305 enum_values = Array::Grow(enum_values, | |
306 enum_values.Length() + num_to_add, | |
307 Heap::kOld); | |
308 | |
309 // Install new names and values into the grown arrays. Also, update | |
310 // the index of the new enum values and add static fields for the new | |
311 // enum values. | |
312 Field& enum_value_field = Field::Handle(); | |
313 for (intptr_t i = 0; i < num_to_add; i++) { | |
314 const intptr_t target_index = offset + i; | |
315 enum_name = String::RawCast(to_add.At(i * 2)); | |
316 enum_value = to_add.At(i * 2 + 1); | |
317 | |
318 // Update the enum value's index into the new arrays. | |
319 TIR_Print("Updating index of %s in %s to %" Pd "\n", | |
320 enum_name.ToCString(), | |
321 ToCString(), | |
322 target_index); | |
323 UpdateEnumIndex(Instance::Cast(enum_value), index_field, target_index); | |
324 | |
325 enum_names.SetAt(target_index, enum_name); | |
326 enum_values.SetAt(target_index, enum_value); | |
327 | |
328 // Install new static field into class. | |
329 // Chop off the 'EnumClass.' | |
330 enum_field_name = String::SubString(enum_name, | |
331 enum_class_name.Length() + 1); | |
332 ASSERT(!enum_field_name.IsNull()); | |
333 enum_field_name = Symbols::New(thread, enum_field_name); | |
334 enum_value_field = Field::New(enum_field_name, | |
335 /* is_static = */ true, | |
336 /* is_final = */ true, | |
337 /* is_const = */ true, | |
338 /* is_reflectable = */ true, | |
339 *this, | |
340 Object::dynamic_type(), | |
341 token_pos()); | |
342 enum_value_field.set_has_initializer(false); | |
343 enum_value_field.SetStaticValue(Instance::Cast(enum_value), true); | |
344 enum_value_field.RecordStore(Instance::Cast(enum_value)); | |
345 AddField(enum_value_field); | |
346 } | |
347 | |
348 // Replace the arrays stored in the static fields. | |
349 enum_names_field.SetStaticValue(enum_names, true); | |
350 enum_values_field.SetStaticValue(enum_values, true); | |
351 } | |
352 | |
353 | |
354 void Class::PatchFieldsAndFunctions() const { | |
355 // Move all old functions and fields to a patch class so that they | |
356 // still refer to their original script. | |
357 const PatchClass& patch = | |
358 PatchClass::Handle(PatchClass::New(*this, Script::Handle(script()))); | |
359 ASSERT(!patch.IsNull()); | |
360 | |
361 const Array& funcs = Array::Handle(functions()); | |
362 Function& func = Function::Handle(); | |
363 Object& owner = Object::Handle(); | |
364 for (intptr_t i = 0; i < funcs.Length(); i++) { | |
365 func = Function::RawCast(funcs.At(i)); | |
366 if ((func.token_pos() == TokenPosition::kMinSource) || | |
367 func.IsClosureFunction()) { | |
368 // Eval functions do not need to have their script updated. | |
369 // | |
370 // Closure functions refer to the parent's script which we can | |
371 // rely on being updated for us, if necessary. | |
372 continue; | |
373 } | |
374 | |
375 // If the source for this function is already patched, leave it alone. | |
376 owner = func.RawOwner(); | |
377 ASSERT(!owner.IsNull()); | |
378 if (!owner.IsPatchClass()) { | |
379 ASSERT(owner.raw() == this->raw()); | |
380 func.set_owner(patch); | |
381 } | |
382 } | |
383 | |
384 const Array& field_list = Array::Handle(fields()); | |
385 Field& field = Field::Handle(); | |
386 for (intptr_t i = 0; i < field_list.Length(); i++) { | |
387 field = Field::RawCast(field_list.At(i)); | |
388 owner = field.RawOwner(); | |
389 ASSERT(!owner.IsNull()); | |
390 if (!owner.IsPatchClass()) { | |
391 ASSERT(owner.raw() == this->raw()); | |
392 field.set_owner(patch); | |
393 } | |
394 field.ForceDynamicGuardedCidAndLength(); | |
395 } | |
396 } | |
397 | |
398 | |
399 bool Class::CanReload(const Class& replacement) const { | |
400 #if defined(DEBUG) | |
401 { | |
402 ASSERT(IsolateReloadContext::IsSameClass(*this, replacement)); | |
403 } | |
404 #endif | |
405 | |
406 if (is_enum_class() && !replacement.is_enum_class()) { | |
407 IRC->ReportError(String::Handle(String::NewFormatted( | |
408 "Enum class cannot be redefined to be a non-enum class: %s", | |
409 ToCString()))); | |
410 return false; | |
411 } | |
412 | |
413 if (!is_enum_class() && replacement.is_enum_class()) { | |
414 IRC->ReportError(String::Handle(String::NewFormatted( | |
415 "Class cannot be redefined to be a enum class: %s", | |
416 ToCString()))); | |
417 return false; | |
418 } | |
419 | |
420 if (is_finalized()) { | |
421 const Error& error = | |
422 Error::Handle(replacement.EnsureIsFinalized(Thread::Current())); | |
423 if (!error.IsNull()) { | |
424 IRC->ReportError(error); | |
425 return false; | |
426 } | |
427 TIR_Print("Finalized replacement class for %s\n", ToCString()); | |
428 } | |
429 | |
430 if (is_finalized()) { | |
431 // Get the field maps for both classes. These field maps walk the class | |
432 // hierarchy. | |
433 const Array& fields = | |
434 Array::Handle(OffsetToFieldMap()); | |
435 const Array& replacement_fields = | |
436 Array::Handle(replacement.OffsetToFieldMap()); | |
437 | |
438 // Check that we have the same number of fields. | |
439 if (fields.Length() != replacement_fields.Length()) { | |
440 IRC->ReportError(String::Handle(String::NewFormatted( | |
441 "Number of instance fields changed in %s", ToCString()))); | |
442 return false; | |
443 } | |
444 | |
445 // Verify that field names / offsets match across the entire hierarchy. | |
446 Field& field = Field::Handle(); | |
447 String& field_name = String::Handle(); | |
448 Field& replacement_field = Field::Handle(); | |
449 String& replacement_field_name = String::Handle(); | |
450 for (intptr_t i = 0; i < fields.Length(); i++) { | |
451 if (fields.At(i) == Field::null()) { | |
452 ASSERT(replacement_fields.At(i) == Field::null()); | |
453 continue; | |
454 } | |
455 field = Field::RawCast(fields.At(i)); | |
456 replacement_field = Field::RawCast(replacement_fields.At(i)); | |
457 field_name = field.name(); | |
458 replacement_field_name = replacement_field.name(); | |
459 if (!field_name.Equals(replacement_field_name)) { | |
460 IRC->ReportError(String::Handle(String::NewFormatted( | |
461 "Name of instance field changed ('%s' vs '%s') in '%s'", | |
462 field_name.ToCString(), | |
463 replacement_field_name.ToCString(), | |
464 ToCString()))); | |
465 return false; | |
466 } | |
467 } | |
468 } else if (is_prefinalized()) { | |
469 if (!replacement.is_prefinalized()) { | |
470 IRC->ReportError(String::Handle(String::NewFormatted( | |
471 "Original class ('%s') is prefinalized and replacement class ('%s')", | |
472 ToCString(), replacement.ToCString()))); | |
473 return false; | |
474 } | |
475 if (instance_size() != replacement.instance_size()) { | |
476 IRC->ReportError(String::Handle(String::NewFormatted( | |
477 "Instance size mismatch between '%s' (%" Pd ") and replacement " | |
478 "'%s' ( %" Pd ")", | |
479 ToCString(), | |
480 instance_size(), | |
481 replacement.ToCString(), | |
482 replacement.instance_size()))); | |
483 return false; | |
484 } | |
485 } | |
486 | |
487 // native field count check. | |
488 if (num_native_fields() != replacement.num_native_fields()) { | |
489 IRC->ReportError(String::Handle(String::NewFormatted( | |
490 "Number of native fields changed in %s", ToCString()))); | |
491 return false; | |
492 } | |
493 | |
494 // TODO(johnmccutchan) type parameter count check. | |
495 | |
496 TIR_Print("Class `%s` can be reloaded (%" Pd " and %" Pd ")\n", | |
497 ToCString(), | |
498 id(), | |
499 replacement.id()); | |
500 return true; | |
501 } | |
502 | |
503 | |
504 bool Library::CanReload(const Library& replacement) const { | |
505 return true; | |
506 } | |
507 | |
508 | |
509 static const Function* static_call_target = NULL; | |
510 | |
511 void ICData::Reset(bool is_static_call) const { | |
rmacnak
2016/05/11 19:56:18
ICData should have an 'is_static_call' bit.
Cutch
2016/05/12 15:50:14
Added TODO
| |
512 if (is_static_call) { | |
513 const Function& old_target = Function::Handle(GetTargetAt(0)); | |
514 if (old_target.IsNull()) { | |
515 FATAL("old_target is NULL.\n"); | |
516 } | |
517 static_call_target = &old_target; | |
518 if (!old_target.is_static()) { | |
519 // TODO(johnmccutchan): Improve this. | |
520 TIR_Print("Cannot rebind super-call to %s from %s\n", | |
521 old_target.ToCString(), | |
522 Object::Handle(Owner()).ToCString()); | |
523 return; | |
524 } | |
525 const String& selector = String::Handle(old_target.name()); | |
526 const Class& cls = Class::Handle(old_target.Owner()); | |
527 const Function& new_target = | |
528 Function::Handle(cls.LookupStaticFunction(selector)); | |
529 if (new_target.IsNull()) { | |
530 // TODO(johnmccutchan): Improve this. | |
531 TIR_Print("Cannot rebind static call to %s from %s\n", | |
532 old_target.ToCString(), | |
533 Object::Handle(Owner()).ToCString()); | |
534 return; | |
535 } | |
536 ClearAndSetStaticTarget(new_target); | |
537 } else { | |
538 ClearWithSentinel(); | |
539 } | |
540 } | |
541 | |
542 #endif // !PRODUCT | |
543 | |
544 } // namespace dart. | |
OLD | NEW |