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 "platform/unaligned.h"
8#include "vm/code_patcher.h"
9#include "vm/hash_table.h"
10#include "vm/isolate_reload.h"
11#include "vm/log.h"
12#include "vm/object_store.h"
13#include "vm/resolver.h"
14#include "vm/stub_code.h"
15#include "vm/symbols.h"
16
17namespace dart {
18
19#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
20
21DECLARE_FLAG(bool, trace_reload);
22DECLARE_FLAG(bool, trace_reload_verbose);
23DECLARE_FLAG(bool, two_args_smi_icd);
24
25void CallSiteResetter::ZeroEdgeCounters(const Function& function) {
26 ic_data_array_ = function.ic_data_array();
27 if (ic_data_array_.IsNull()) {
28 return;
29 }
30 ASSERT(ic_data_array_.Length() > 0);
31 edge_counters_ ^= ic_data_array_.At(0);
32 if (edge_counters_.IsNull()) {
33 return;
34 }
35 // Fill edge counters array with zeros.
36 for (intptr_t i = 0; i < edge_counters_.Length(); i++) {
37 edge_counters_.SetAt(i, Object::smi_zero());
38 }
39}
40
41CallSiteResetter::CallSiteResetter(Zone* zone)
42 : zone_(zone),
43 instrs_(Instructions::Handle(zone)),
44 pool_(ObjectPool::Handle(zone)),
45 object_(Object::Handle(zone)),
46 name_(String::Handle(zone)),
47 new_cls_(Class::Handle(zone)),
48 new_lib_(Library::Handle(zone)),
49 new_function_(Function::Handle(zone)),
50 new_field_(Field::Handle(zone)),
51 entries_(Array::Handle(zone)),
52 old_target_(Function::Handle(zone)),
53 new_target_(Function::Handle(zone)),
54 caller_(Function::Handle(zone)),
55 args_desc_array_(Array::Handle(zone)),
56 ic_data_array_(Array::Handle(zone)),
57 edge_counters_(Array::Handle(zone)),
58 descriptors_(PcDescriptors::Handle(zone)),
59 ic_data_(ICData::Handle(zone)) {}
60
61void CallSiteResetter::ResetCaches(const Code& code) {
62 // Iterate over the Code's object pool and reset all ICDatas and
63 // SubtypeTestCaches.
64#ifdef TARGET_ARCH_IA32
65 // IA32 does not have an object pool, but, we can iterate over all
66 // embedded objects by using the variable length data section.
67 if (!code.is_alive()) {
68 return;
69 }
70 instrs_ = code.instructions();
71 ASSERT(!instrs_.IsNull());
72 uword base_address = instrs_.PayloadStart();
73 intptr_t offsets_length = code.pointer_offsets_length();
74 const int32_t* offsets = code.raw_ptr()->data();
75 for (intptr_t i = 0; i < offsets_length; i++) {
76 int32_t offset = offsets[i];
77 ObjectPtr* object_ptr = reinterpret_cast<ObjectPtr*>(base_address + offset);
78 ObjectPtr raw_object = LoadUnaligned(object_ptr);
79 if (!raw_object->IsHeapObject()) {
80 continue;
81 }
82 object_ = raw_object;
83 if (object_.IsICData()) {
84 Reset(ICData::Cast(object_));
85 } else if (object_.IsSubtypeTestCache()) {
86 SubtypeTestCache::Cast(object_).Reset();
87 }
88 }
89#else
90 pool_ = code.object_pool();
91 ASSERT(!pool_.IsNull());
92 ResetCaches(pool_);
93#endif
94}
95
96static void FindICData(const Array& ic_data_array,
97 intptr_t deopt_id,
98 ICData* ic_data) {
99 // ic_data_array is sorted because of how it is constructed in
100 // Function::SaveICDataMap.
101 intptr_t lo = 1;
102 intptr_t hi = ic_data_array.Length() - 1;
103 while (lo <= hi) {
104 intptr_t mid = (hi - lo + 1) / 2 + lo;
105 ASSERT(mid >= lo);
106 ASSERT(mid <= hi);
107 *ic_data ^= ic_data_array.At(mid);
108 if (ic_data->deopt_id() == deopt_id) {
109 return;
110 } else if (ic_data->deopt_id() > deopt_id) {
111 hi = mid - 1;
112 } else {
113 lo = mid + 1;
114 }
115 }
116 FATAL1("Missing deopt id %" Pd "\n", deopt_id);
117}
118
119void CallSiteResetter::ResetSwitchableCalls(const Code& code) {
120 if (code.is_optimized()) {
121 return; // No switchable calls in optimized code.
122 }
123
124 object_ = code.owner();
125 if (!object_.IsFunction()) {
126 return; // No switchable calls in stub code.
127 }
128 const Function& function = Function::Cast(object_);
129
130 if (function.kind() == FunctionLayout::kIrregexpFunction) {
131 // Regex matchers do not support breakpoints or stepping, and they only call
132 // core library functions that cannot change due to reload. As a performance
133 // optimization, avoid this matching of ICData to PCs for these functions'
134 // large number of instance calls.
135 ASSERT(!function.is_debuggable());
136 return;
137 }
138
139 ic_data_array_ = function.ic_data_array();
140 if (ic_data_array_.IsNull()) {
141 // The megamorphic miss stub and some recognized function doesn't populate
142 // their ic_data_array. Check this only happens for functions without IC
143 // calls.
144#if defined(DEBUG)
145 descriptors_ = code.pc_descriptors();
146 PcDescriptors::Iterator iter(descriptors_, PcDescriptorsLayout::kIcCall);
147 while (iter.MoveNext()) {
148 FATAL1("%s has IC calls but no ic_data_array\n", object_.ToCString());
149 }
150#endif
151 return;
152 }
153
154 descriptors_ = code.pc_descriptors();
155 PcDescriptors::Iterator iter(descriptors_, PcDescriptorsLayout::kIcCall);
156 while (iter.MoveNext()) {
157 uword pc = code.PayloadStart() + iter.PcOffset();
158 CodePatcher::GetInstanceCallAt(pc, code, &object_);
159 // This check both avoids unnecessary patching to reduce log spam and
160 // prevents patching over breakpoint stubs.
161 if (!object_.IsICData()) {
162 FindICData(ic_data_array_, iter.DeoptId(), &ic_data_);
163 ASSERT(ic_data_.rebind_rule() == ICData::kInstance);
164 ASSERT(ic_data_.NumArgsTested() == 1);
165 const Code& stub =
166 ic_data_.is_tracking_exactness()
167 ? StubCode::OneArgCheckInlineCacheWithExactnessCheck()
168 : StubCode::OneArgCheckInlineCache();
169 CodePatcher::PatchInstanceCallAt(pc, code, ic_data_, stub);
170 if (FLAG_trace_ic) {
171 OS::PrintErr("Instance call at %" Px
172 " resetting to polymorphic dispatch, %s\n",
173 pc, ic_data_.ToCString());
174 }
175 }
176 }
177}
178
179void CallSiteResetter::RebindStaticTargets(const Bytecode& bytecode) {
180 // Iterate over the Bytecode's object pool and reset all ICDatas.
181 pool_ = bytecode.object_pool();
182 ASSERT(!pool_.IsNull());
183
184 for (intptr_t i = 0; i < pool_.Length(); i++) {
185 ObjectPool::EntryType entry_type = pool_.TypeAt(i);
186 if (entry_type != ObjectPool::EntryType::kTaggedObject) {
187 continue;
188 }
189 object_ = pool_.ObjectAt(i);
190 if (object_.IsFunction()) {
191 const Function& old_function = Function::Cast(object_);
192 if (old_function.IsClosureFunction()) {
193 continue;
194 }
195 name_ = old_function.name();
196 new_cls_ = old_function.Owner();
197 if (new_cls_.IsTopLevel()) {
198 new_lib_ = new_cls_.library();
199 new_function_ = new_lib_.LookupLocalFunction(name_);
200 } else {
201 new_function_ = new_cls_.LookupFunction(name_);
202 }
203 if (!new_function_.IsNull() &&
204 (new_function_.is_static() == old_function.is_static()) &&
205 (new_function_.kind() == old_function.kind())) {
206 pool_.SetObjectAt(i, new_function_);
207 } else {
208 VTIR_Print("Cannot rebind function %s\n", old_function.ToCString());
209 }
210 } else if (object_.IsField()) {
211 const Field& old_field = Field::Cast(object_);
212 name_ = old_field.name();
213 new_cls_ = old_field.Owner();
214 if (new_cls_.IsTopLevel()) {
215 new_lib_ = new_cls_.library();
216 new_field_ = new_lib_.LookupLocalField(name_);
217 } else {
218 new_field_ = new_cls_.LookupField(name_);
219 }
220 if (!new_field_.IsNull() &&
221 (new_field_.is_static() == old_field.is_static())) {
222 pool_.SetObjectAt(i, new_field_);
223 } else {
224 VTIR_Print("Cannot rebind field %s\n", old_field.ToCString());
225 }
226 }
227 }
228}
229
230void CallSiteResetter::ResetCaches(const ObjectPool& pool) {
231 for (intptr_t i = 0; i < pool.Length(); i++) {
232 ObjectPool::EntryType entry_type = pool.TypeAt(i);
233 if (entry_type != ObjectPool::EntryType::kTaggedObject) {
234 continue;
235 }
236 object_ = pool.ObjectAt(i);
237 if (object_.IsICData()) {
238 Reset(ICData::Cast(object_));
239 } else if (object_.IsSubtypeTestCache()) {
240 SubtypeTestCache::Cast(object_).Reset();
241 }
242 }
243}
244
245void Class::CopyStaticFieldValues(IsolateReloadContext* reload_context,
246 const Class& old_cls) const {
247 // We only update values for non-enum classes.
248 const bool update_values = !is_enum_class();
249
250 const Array& old_field_list = Array::Handle(old_cls.fields());
251 Field& old_field = Field::Handle();
252 String& old_name = String::Handle();
253
254 const Array& field_list = Array::Handle(fields());
255 Field& field = Field::Handle();
256 String& name = String::Handle();
257
258 for (intptr_t i = 0; i < field_list.Length(); i++) {
259 field = Field::RawCast(field_list.At(i));
260 name = field.name();
261 // Find the corresponding old field, if it exists, and migrate
262 // over the field value.
263 for (intptr_t j = 0; j < old_field_list.Length(); j++) {
264 old_field = Field::RawCast(old_field_list.At(j));
265 old_name = old_field.name();
266 if (name.Equals(old_name)) {
267 if (field.is_static()) {
268 // We only copy values if requested and if the field is not a const
269 // field. We let const fields be updated with a reload.
270 if (update_values && !field.is_const()) {
271 // Make new field point to the old field value so that both
272 // old and new code see and update same value.
273 reload_context->isolate()->field_table()->Free(field.field_id());
274 field.set_field_id(old_field.field_id());
275 }
276 reload_context->AddStaticFieldMapping(old_field, field);
277 } else {
278 if (old_field.needs_load_guard()) {
279 ASSERT(!old_field.is_unboxing_candidate());
280 field.set_needs_load_guard(true);
281 field.set_is_unboxing_candidate(false);
282 }
283 }
284 }
285 }
286 }
287}
288
289void Class::CopyCanonicalConstants(const Class& old_cls) const {
290 if (is_enum_class()) {
291 // We do not copy enum classes's canonical constants because we explicitly
292 // become the old enum values to the new enum values.
293 return;
294 }
295#if defined(DEBUG)
296 {
297 // Class has no canonical constants allocated.
298 const Array& my_constants = Array::Handle(constants());
299 ASSERT(my_constants.Length() == 0);
300 }
301#endif // defined(DEBUG).
302 // Copy old constants into new class.
303 const Array& old_constants = Array::Handle(old_cls.constants());
304 if (old_constants.IsNull() || old_constants.Length() == 0) {
305 return;
306 }
307 TIR_Print("Copied %" Pd " canonical constants for class `%s`\n",
308 old_constants.Length(), ToCString());
309 set_constants(old_constants);
310}
311
312void Class::CopyDeclarationType(const Class& old_cls) const {
313 const Type& old_declaration_type = Type::Handle(old_cls.declaration_type());
314 if (old_declaration_type.IsNull()) {
315 return;
316 }
317 set_declaration_type(old_declaration_type);
318}
319
320class EnumMapTraits {
321 public:
322 static bool ReportStats() { return false; }
323 static const char* Name() { return "EnumMapTraits"; }
324
325 static bool IsMatch(const Object& a, const Object& b) {
326 return a.raw() == b.raw();
327 }
328
329 static uword Hash(const Object& obj) {
330 ASSERT(obj.IsString());
331 return String::Cast(obj).Hash();
332 }
333};
334
335// Given an old enum class, add become mappings from old values to new values.
336// Some notes about how we reload enums below:
337//
338// When an enum is reloaded the following three things can happen, possibly
339// simultaneously.
340//
341// 1) A new enum value is added.
342// This case is handled automatically.
343// 2) Enum values are reordered.
344// We pair old and new enums and the old enums 'become' the new ones so
345// the ordering is always correct (i.e. enum indices match slots in values
346// array)
347// 3) An existing enum value is removed.
348// Each enum class has a canonical 'deleted' enum sentinel instance.
349// When an enum value is deleted, we 'become' all references to the 'deleted'
350// sentinel value. The index value is -1.
351//
352void Class::ReplaceEnum(IsolateReloadContext* reload_context,
353 const Class& old_enum) const {
354 // We only do this for finalized enum classes.
355 ASSERT(is_enum_class());
356 ASSERT(old_enum.is_enum_class());
357 ASSERT(is_finalized());
358 ASSERT(old_enum.is_finalized());
359
360 Zone* zone = Thread::Current()->zone();
361
362 Array& enum_fields = Array::Handle(zone);
363 Field& field = Field::Handle(zone);
364 String& enum_ident = String::Handle();
365 Instance& old_enum_value = Instance::Handle(zone);
366 Instance& enum_value = Instance::Handle(zone);
367 // The E.values array.
368 Instance& old_enum_values = Instance::Handle(zone);
369 // The E.values array.
370 Instance& enum_values = Instance::Handle(zone);
371 // The E._deleted_enum_sentinel instance.
372 Instance& old_deleted_enum_sentinel = Instance::Handle(zone);
373 // The E._deleted_enum_sentinel instance.
374 Instance& deleted_enum_sentinel = Instance::Handle(zone);
375 Array& enum_map_storage =
376 Array::Handle(zone, HashTables::New<UnorderedHashMap<EnumMapTraits> >(4));
377 ASSERT(!enum_map_storage.IsNull());
378
379 TIR_Print("Replacing enum `%s`\n", String::Handle(Name()).ToCString());
380
381 {
382 UnorderedHashMap<EnumMapTraits> enum_map(enum_map_storage.raw());
383 // Build a map of all enum name -> old enum instance.
384 enum_fields = old_enum.fields();
385 for (intptr_t i = 0; i < enum_fields.Length(); i++) {
386 field = Field::RawCast(enum_fields.At(i));
387 enum_ident = field.name();
388 if (!field.is_static()) {
389 // Enum instances are only held in static fields.
390 continue;
391 }
392 if (enum_ident.Equals(Symbols::Values())) {
393 old_enum_values = field.StaticValue();
394 // Non-enum instance.
395 continue;
396 }
397 if (enum_ident.Equals(Symbols::_DeletedEnumSentinel())) {
398 old_deleted_enum_sentinel = field.StaticValue();
399 // Non-enum instance.
400 continue;
401 }
402 old_enum_value = field.StaticValue();
403 ASSERT(!old_enum_value.IsNull());
404 VTIR_Print("Element %s being added to mapping\n", enum_ident.ToCString());
405 bool update = enum_map.UpdateOrInsert(enum_ident, old_enum_value);
406 VTIR_Print("Element %s added to mapping\n", enum_ident.ToCString());
407 ASSERT(!update);
408 }
409 // The storage given to the map may have been reallocated, remember the new
410 // address.
411 enum_map_storage = enum_map.Release().raw();
412 }
413
414 bool enums_deleted = false;
415 {
416 UnorderedHashMap<EnumMapTraits> enum_map(enum_map_storage.raw());
417 // Add a become mapping from the old instances to the new instances.
418 enum_fields = fields();
419 for (intptr_t i = 0; i < enum_fields.Length(); i++) {
420 field = Field::RawCast(enum_fields.At(i));
421 enum_ident = field.name();
422 if (!field.is_static()) {
423 // Enum instances are only held in static fields.
424 continue;
425 }
426 if (enum_ident.Equals(Symbols::Values())) {
427 enum_values = field.StaticValue();
428 // Non-enum instance.
429 continue;
430 }
431 if (enum_ident.Equals(Symbols::_DeletedEnumSentinel())) {
432 deleted_enum_sentinel = field.StaticValue();
433 // Non-enum instance.
434 continue;
435 }
436 enum_value = field.StaticValue();
437 ASSERT(!enum_value.IsNull());
438 old_enum_value ^= enum_map.GetOrNull(enum_ident);
439 if (old_enum_value.IsNull()) {
440 VTIR_Print("New element %s was not found in mapping\n",
441 enum_ident.ToCString());
442 } else {
443 VTIR_Print("Adding element `%s` to become mapping\n",
444 enum_ident.ToCString());
445 bool removed = enum_map.Remove(enum_ident);
446 ASSERT(removed);
447 reload_context->AddEnumBecomeMapping(old_enum_value, enum_value);
448 }
449 }
450 enums_deleted = enum_map.NumOccupied() > 0;
451 // The storage given to the map may have been reallocated, remember the new
452 // address.
453 enum_map_storage = enum_map.Release().raw();
454 }
455
456 // Map the old E.values array to the new E.values array.
457 ASSERT(!old_enum_values.IsNull());
458 ASSERT(!enum_values.IsNull());
459 reload_context->AddEnumBecomeMapping(old_enum_values, enum_values);
460
461 // Map the old E._deleted_enum_sentinel to the new E._deleted_enum_sentinel.
462 ASSERT(!old_deleted_enum_sentinel.IsNull());
463 ASSERT(!deleted_enum_sentinel.IsNull());
464 reload_context->AddEnumBecomeMapping(old_deleted_enum_sentinel,
465 deleted_enum_sentinel);
466
467 if (enums_deleted) {
468 // Map all deleted enums to the deleted enum sentinel value.
469 // TODO(johnmccutchan): Add this to the reload 'notices' list.
470 VTIR_Print(
471 "The following enum values were deleted from %s and will become the "
472 "deleted enum sentinel:\n",
473 old_enum.ToCString());
474 UnorderedHashMap<EnumMapTraits> enum_map(enum_map_storage.raw());
475 UnorderedHashMap<EnumMapTraits>::Iterator it(&enum_map);
476 while (it.MoveNext()) {
477 const intptr_t entry = it.Current();
478 enum_ident = String::RawCast(enum_map.GetKey(entry));
479 ASSERT(!enum_ident.IsNull());
480 old_enum_value ^= enum_map.GetOrNull(enum_ident);
481 VTIR_Print("Element `%s` was deleted\n", enum_ident.ToCString());
482 reload_context->AddEnumBecomeMapping(old_enum_value,
483 deleted_enum_sentinel);
484 }
485 enum_map.Release();
486 }
487}
488
489void Class::PatchFieldsAndFunctions() const {
490 // Move all old functions and fields to a patch class so that they
491 // still refer to their original script.
492 const PatchClass& patch =
493 PatchClass::Handle(PatchClass::New(*this, Script::Handle(script())));
494 ASSERT(!patch.IsNull());
495 const Library& lib = Library::Handle(library());
496 if (!lib.is_declared_in_bytecode()) {
497 patch.set_library_kernel_data(ExternalTypedData::Handle(lib.kernel_data()));
498 patch.set_library_kernel_offset(lib.kernel_offset());
499 }
500
501 const Array& funcs = Array::Handle(functions());
502 Function& func = Function::Handle();
503 Object& owner = Object::Handle();
504 for (intptr_t i = 0; i < funcs.Length(); i++) {
505 func = Function::RawCast(funcs.At(i));
506 if ((func.token_pos() == TokenPosition::kMinSource) ||
507 func.IsClosureFunction()) {
508 // Eval functions do not need to have their script updated.
509 //
510 // Closure functions refer to the parent's script which we can
511 // rely on being updated for us, if necessary.
512 continue;
513 }
514
515 // If the source for this function is already patched, leave it alone.
516 owner = func.RawOwner();
517 ASSERT(!owner.IsNull());
518 if (!owner.IsPatchClass()) {
519 ASSERT(owner.raw() == this->raw());
520 func.set_owner(patch);
521 }
522 }
523
524 const Array& field_list = Array::Handle(fields());
525 Field& field = Field::Handle();
526 for (intptr_t i = 0; i < field_list.Length(); i++) {
527 field = Field::RawCast(field_list.At(i));
528 owner = field.RawOwner();
529 ASSERT(!owner.IsNull());
530 if (!owner.IsPatchClass()) {
531 ASSERT(owner.raw() == this->raw());
532 field.set_owner(patch);
533 }
534 field.ForceDynamicGuardedCidAndLength();
535 }
536}
537
538void Class::MigrateImplicitStaticClosures(IsolateReloadContext* irc,
539 const Class& new_cls) const {
540 const Array& funcs = Array::Handle(functions());
541 Function& old_func = Function::Handle();
542 String& selector = String::Handle();
543 Function& new_func = Function::Handle();
544 Instance& old_closure = Instance::Handle();
545 Instance& new_closure = Instance::Handle();
546 for (intptr_t i = 0; i < funcs.Length(); i++) {
547 old_func ^= funcs.At(i);
548 if (old_func.is_static() && old_func.HasImplicitClosureFunction()) {
549 selector = old_func.name();
550 new_func = new_cls.LookupFunction(selector);
551 if (!new_func.IsNull() && new_func.is_static()) {
552 old_func = old_func.ImplicitClosureFunction();
553 old_closure = old_func.ImplicitStaticClosure();
554 new_func = new_func.ImplicitClosureFunction();
555 new_closure = new_func.ImplicitStaticClosure();
556 if (old_closure.IsCanonical()) {
557 new_closure.SetCanonical();
558 }
559 irc->AddBecomeMapping(old_closure, new_closure);
560 }
561 }
562 }
563}
564
565class EnumClassConflict : public ClassReasonForCancelling {
566 public:
567 EnumClassConflict(Zone* zone, const Class& from, const Class& to)
568 : ClassReasonForCancelling(zone, from, to) {}
569
570 StringPtr ToString() {
571 return String::NewFormatted(
572 from_.is_enum_class()
573 ? "Enum class cannot be redefined to be a non-enum class: %s"
574 : "Class cannot be redefined to be a enum class: %s",
575 from_.ToCString());
576 }
577};
578
579class TypedefClassConflict : public ClassReasonForCancelling {
580 public:
581 TypedefClassConflict(Zone* zone, const Class& from, const Class& to)
582 : ClassReasonForCancelling(zone, from, to) {}
583
584 StringPtr ToString() {
585 return String::NewFormatted(
586 from_.IsTypedefClass()
587 ? "Typedef class cannot be redefined to be a non-typedef class: %s"
588 : "Class cannot be redefined to be a typedef class: %s",
589 from_.ToCString());
590 }
591};
592
593class EnsureFinalizedError : public ClassReasonForCancelling {
594 public:
595 EnsureFinalizedError(Zone* zone,
596 const Class& from,
597 const Class& to,
598 const Error& error)
599 : ClassReasonForCancelling(zone, from, to), error_(error) {}
600
601 private:
602 const Error& error_;
603
604 ErrorPtr ToError() { return error_.raw(); }
605
606 StringPtr ToString() { return String::New(error_.ToErrorCString()); }
607};
608
609class ConstToNonConstClass : public ClassReasonForCancelling {
610 public:
611 ConstToNonConstClass(Zone* zone, const Class& from, const Class& to)
612 : ClassReasonForCancelling(zone, from, to) {}
613
614 private:
615 StringPtr ToString() {
616 return String::NewFormatted("Const class cannot become non-const: %s",
617 from_.ToCString());
618 }
619};
620
621class ConstClassFieldRemoved : public ClassReasonForCancelling {
622 public:
623 ConstClassFieldRemoved(Zone* zone, const Class& from, const Class& to)
624 : ClassReasonForCancelling(zone, from, to) {}
625
626 private:
627 StringPtr ToString() {
628 return String::NewFormatted("Const class cannot remove fields: %s",
629 from_.ToCString());
630 }
631};
632
633class NativeFieldsConflict : public ClassReasonForCancelling {
634 public:
635 NativeFieldsConflict(Zone* zone, const Class& from, const Class& to)
636 : ClassReasonForCancelling(zone, from, to) {}
637
638 private:
639 StringPtr ToString() {
640 return String::NewFormatted("Number of native fields changed in %s",
641 from_.ToCString());
642 }
643};
644
645class TypeParametersChanged : public ClassReasonForCancelling {
646 public:
647 TypeParametersChanged(Zone* zone, const Class& from, const Class& to)
648 : ClassReasonForCancelling(zone, from, to) {}
649
650 StringPtr ToString() {
651 return String::NewFormatted(
652 "Limitation: type parameters have changed for %s", from_.ToCString());
653 }
654
655 void AppendTo(JSONArray* array) {
656 JSONObject jsobj(array);
657 jsobj.AddProperty("type", "ReasonForCancellingReload");
658 jsobj.AddProperty("kind", "TypeParametersChanged");
659 jsobj.AddProperty("class", to_);
660 jsobj.AddProperty("message",
661 "Limitation: changing type parameters "
662 "does not work with hot reload.");
663 }
664};
665
666class PreFinalizedConflict : public ClassReasonForCancelling {
667 public:
668 PreFinalizedConflict(Zone* zone, const Class& from, const Class& to)
669 : ClassReasonForCancelling(zone, from, to) {}
670
671 private:
672 StringPtr ToString() {
673 return String::NewFormatted(
674 "Original class ('%s') is prefinalized and replacement class "
675 "('%s') is not ",
676 from_.ToCString(), to_.ToCString());
677 }
678};
679
680class InstanceSizeConflict : public ClassReasonForCancelling {
681 public:
682 InstanceSizeConflict(Zone* zone, const Class& from, const Class& to)
683 : ClassReasonForCancelling(zone, from, to) {}
684
685 private:
686 StringPtr ToString() {
687 return String::NewFormatted("Instance size mismatch between '%s' (%" Pd
688 ") and replacement "
689 "'%s' ( %" Pd ")",
690 from_.ToCString(), from_.host_instance_size(),
691 to_.ToCString(), to_.host_instance_size());
692 }
693};
694
695class UnimplementedDeferredLibrary : public ReasonForCancelling {
696 public:
697 UnimplementedDeferredLibrary(Zone* zone,
698 const Library& from,
699 const Library& to,
700 const String& name)
701 : ReasonForCancelling(zone), from_(from), to_(to), name_(name) {}
702
703 private:
704 const Library& from_;
705 const Library& to_;
706 const String& name_;
707
708 StringPtr ToString() {
709 const String& lib_url = String::Handle(to_.url());
710 from_.ToCString();
711 return String::NewFormatted(
712 "Reloading support for deferred loading has not yet been implemented:"
713 " library '%s' has deferred import '%s'",
714 lib_url.ToCString(), name_.ToCString());
715 }
716};
717
718// This is executed before iterating over the instances.
719void Class::CheckReload(const Class& replacement,
720 IsolateReloadContext* context) const {
721 ASSERT(IsolateReloadContext::IsSameClass(*this, replacement));
722
723 if (!is_declaration_loaded()) {
724 // The old class hasn't been used in any meanfully way, so the VM is okay
725 // with any change.
726 return;
727 }
728
729 // Ensure is_enum_class etc have been set.
730 replacement.EnsureDeclarationLoaded();
731
732 // Class cannot change enum property.
733 if (is_enum_class() != replacement.is_enum_class()) {
734 context->group_reload_context()->AddReasonForCancelling(
735 new (context->zone())
736 EnumClassConflict(context->zone(), *this, replacement));
737 return;
738 }
739
740 // Class cannot change typedef property.
741 if (IsTypedefClass() != replacement.IsTypedefClass()) {
742 context->group_reload_context()->AddReasonForCancelling(
743 new (context->zone())
744 TypedefClassConflict(context->zone(), *this, replacement));
745 return;
746 }
747
748 if (is_finalized()) {
749 // Ensure the replacement class is also finalized.
750 const Error& error =
751 Error::Handle(replacement.EnsureIsFinalized(Thread::Current()));
752 if (!error.IsNull()) {
753 context->group_reload_context()->AddReasonForCancelling(
754 new (context->zone())
755 EnsureFinalizedError(context->zone(), *this, replacement, error));
756 return; // No reason to check other properties.
757 }
758 ASSERT(replacement.is_finalized());
759 TIR_Print("Finalized replacement class for %s\n", ToCString());
760 }
761
762 if (is_finalized() && is_const() && (constants() != Array::null()) &&
763 (Array::LengthOf(constants()) > 0)) {
764 // Consts can't become non-consts.
765 if (!replacement.is_const()) {
766 context->group_reload_context()->AddReasonForCancelling(
767 new (context->zone())
768 ConstToNonConstClass(context->zone(), *this, replacement));
769 return;
770 }
771
772 // Consts can't lose fields.
773 bool field_removed = false;
774 const Array& old_fields =
775 Array::Handle(OffsetToFieldMap(true /* original classes */));
776 const Array& new_fields = Array::Handle(replacement.OffsetToFieldMap());
777 if (new_fields.Length() < old_fields.Length()) {
778 field_removed = true;
779 } else {
780 Field& old_field = Field::Handle();
781 Field& new_field = Field::Handle();
782 String& old_name = String::Handle();
783 String& new_name = String::Handle();
784 for (intptr_t i = 0, n = old_fields.Length(); i < n; i++) {
785 old_field ^= old_fields.At(i);
786 new_field ^= new_fields.At(i);
787 if (old_field.IsNull() != new_field.IsNull()) {
788 field_removed = true;
789 break;
790 }
791 if (!old_field.IsNull()) {
792 old_name = old_field.name();
793 new_name = new_field.name();
794 if (!old_name.Equals(new_name)) {
795 field_removed = true;
796 break;
797 }
798 }
799 }
800 }
801 if (field_removed) {
802 context->group_reload_context()->AddReasonForCancelling(
803 new (context->zone())
804 ConstClassFieldRemoved(context->zone(), *this, replacement));
805 return;
806 }
807 }
808
809 // Native field count cannot change.
810 if (num_native_fields() != replacement.num_native_fields()) {
811 context->group_reload_context()->AddReasonForCancelling(
812 new (context->zone())
813 NativeFieldsConflict(context->zone(), *this, replacement));
814 return;
815 }
816
817 // Just checking.
818 ASSERT(is_enum_class() == replacement.is_enum_class());
819 ASSERT(num_native_fields() == replacement.num_native_fields());
820
821 if (is_finalized()) {
822 if (!CanReloadFinalized(replacement, context)) return;
823 }
824 if (is_prefinalized()) {
825 if (!CanReloadPreFinalized(replacement, context)) return;
826 }
827 TIR_Print("Class `%s` can be reloaded (%" Pd " and %" Pd ")\n", ToCString(),
828 id(), replacement.id());
829}
830
831bool Class::RequiresInstanceMorphing(const Class& replacement) const {
832 // Get the field maps for both classes. These field maps walk the class
833 // hierarchy.
834 const Array& fields =
835 Array::Handle(OffsetToFieldMap(true /* original classes */));
836 const Array& replacement_fields =
837 Array::Handle(replacement.OffsetToFieldMap());
838
839 // Check that the size of the instance is the same.
840 if (fields.Length() != replacement_fields.Length()) return true;
841
842 // Check that we have the same next field offset. This check is not
843 // redundant with the one above because the instance OffsetToFieldMap
844 // array length is based on the instance size (which may be aligned up).
845 if (host_next_field_offset() != replacement.host_next_field_offset()) {
846 return true;
847 }
848
849 // Verify that field names / offsets match across the entire hierarchy.
850 Field& field = Field::Handle();
851 String& field_name = String::Handle();
852 Field& replacement_field = Field::Handle();
853 String& replacement_field_name = String::Handle();
854
855 for (intptr_t i = 0; i < fields.Length(); i++) {
856 if (fields.At(i) == Field::null()) {
857 ASSERT(replacement_fields.At(i) == Field::null());
858 continue;
859 }
860 field = Field::RawCast(fields.At(i));
861 replacement_field = Field::RawCast(replacement_fields.At(i));
862 field_name = field.name();
863 replacement_field_name = replacement_field.name();
864 if (!field_name.Equals(replacement_field_name)) return true;
865 }
866 return false;
867}
868
869bool Class::CanReloadFinalized(const Class& replacement,
870 IsolateReloadContext* context) const {
871 // Make sure the declaration types argument count matches for the two classes.
872 // ex. class A<int,B> {} cannot be replace with class A<B> {}.
873 auto group_context = context->group_reload_context();
874 auto shared_class_table =
875 group_context->isolate_group()->shared_class_table();
876 if (NumTypeArguments() != replacement.NumTypeArguments()) {
877 group_context->AddReasonForCancelling(
878 new (context->zone())
879 TypeParametersChanged(context->zone(), *this, replacement));
880 return false;
881 }
882 if (RequiresInstanceMorphing(replacement)) {
883 ASSERT(id() == replacement.id());
884 const classid_t cid = id();
885 // We unconditionally create an instance morpher. As a side effect of
886 // building the morpher, we will mark all new fields as late.
887 auto instance_morpher = InstanceMorpher::CreateFromClassDescriptors(
888 context->zone(), shared_class_table, *this, replacement);
889 group_context->EnsureHasInstanceMorpherFor(cid, instance_morpher);
890 }
891 return true;
892}
893
894bool Class::CanReloadPreFinalized(const Class& replacement,
895 IsolateReloadContext* context) const {
896 // The replacement class must also prefinalized.
897 if (!replacement.is_prefinalized()) {
898 context->group_reload_context()->AddReasonForCancelling(
899 new (context->zone())
900 PreFinalizedConflict(context->zone(), *this, replacement));
901 return false;
902 }
903 // Check the instance sizes are equal.
904 if (host_instance_size() != replacement.host_instance_size()) {
905 context->group_reload_context()->AddReasonForCancelling(
906 new (context->zone())
907 InstanceSizeConflict(context->zone(), *this, replacement));
908 return false;
909 }
910 return true;
911}
912
913void Library::CheckReload(const Library& replacement,
914 IsolateReloadContext* context) const {
915 // TODO(26878): If the replacement library uses deferred loading,
916 // reject it. We do not yet support reloading deferred libraries.
917 Object& object = Object::Handle();
918 LibraryPrefix& prefix = LibraryPrefix::Handle();
919 DictionaryIterator it(replacement);
920 while (it.HasNext()) {
921 object = it.GetNext();
922 if (!object.IsLibraryPrefix()) continue;
923 prefix ^= object.raw();
924 if (prefix.is_deferred_load()) {
925 const String& prefix_name = String::Handle(prefix.name());
926 context->group_reload_context()->AddReasonForCancelling(
927 new (context->zone()) UnimplementedDeferredLibrary(
928 context->zone(), *this, replacement, prefix_name));
929 return;
930 }
931 }
932}
933
934void CallSiteResetter::Reset(const ICData& ic) {
935 ICData::RebindRule rule = ic.rebind_rule();
936 if (rule == ICData::kInstance) {
937 const intptr_t num_args = ic.NumArgsTested();
938 const bool tracking_exactness = ic.is_tracking_exactness();
939 const intptr_t len = ic.Length();
940 // We need at least one non-sentinel entry to require a check
941 // for the smi fast path case.
942 if (num_args == 2 && len >= 2) {
943 if (ic.IsImmutable()) {
944 return;
945 }
946 name_ = ic.target_name();
947 const Class& smi_class = Class::Handle(zone_, Smi::Class());
948 const Function& smi_op_target = Function::Handle(
949 zone_, Resolver::ResolveDynamicAnyArgs(zone_, smi_class, name_));
950 GrowableArray<intptr_t> class_ids(2);
951 Function& target = Function::Handle(zone_);
952 ic.GetCheckAt(0, &class_ids, &target);
953 if ((target.raw() == smi_op_target.raw()) && (class_ids[0] == kSmiCid) &&
954 (class_ids[1] == kSmiCid)) {
955 // The smi fast path case, preserve the initial entry but reset the
956 // count.
957 ic.ClearCountAt(0);
958 ic.WriteSentinelAt(1);
959 entries_ = ic.entries();
960 entries_.Truncate(2 * ic.TestEntryLength());
961 return;
962 }
963 // Fall back to the normal behavior with cached empty ICData arrays.
964 }
965 entries_ = ICData::CachedEmptyICDataArray(num_args, tracking_exactness);
966 ic.set_entries(entries_);
967 ic.set_is_megamorphic(false);
968 return;
969 } else if (rule == ICData::kNoRebind || rule == ICData::kNSMDispatch) {
970 // TODO(30877) we should account for addition/removal of NSM.
971 // Don't rebind dispatchers.
972 return;
973 } else if (rule == ICData::kStatic || rule == ICData::kSuper) {
974 old_target_ = ic.GetTargetAt(0);
975 if (old_target_.IsNull()) {
976 FATAL("old_target is NULL.\n");
977 }
978 name_ = old_target_.name();
979
980 if (rule == ICData::kStatic) {
981 ASSERT(old_target_.is_static() ||
982 old_target_.kind() == FunctionLayout::kConstructor);
983 // This can be incorrect if the call site was an unqualified invocation.
984 new_cls_ = old_target_.Owner();
985 new_target_ = new_cls_.LookupFunction(name_);
986 if (new_target_.kind() != old_target_.kind()) {
987 new_target_ = Function::null();
988 }
989 } else {
990 // Super call.
991 caller_ = ic.Owner();
992 ASSERT(!caller_.is_static());
993 new_cls_ = caller_.Owner();
994 new_cls_ = new_cls_.SuperClass();
995 new_target_ = Function::null();
996 while (!new_cls_.IsNull()) {
997 // TODO(rmacnak): Should use Resolver::ResolveDynamicAnyArgs to handle
998 // method-extractors and call-through-getters, but we're in a no
999 // safepoint scope here.
1000 new_target_ = new_cls_.LookupDynamicFunction(name_);
1001 if (!new_target_.IsNull()) {
1002 break;
1003 }
1004 new_cls_ = new_cls_.SuperClass();
1005 }
1006 }
1007 args_desc_array_ = ic.arguments_descriptor();
1008 ArgumentsDescriptor args_desc(args_desc_array_);
1009 if (new_target_.IsNull() ||
1010 !new_target_.AreValidArguments(args_desc, NULL)) {
1011 // TODO(rmacnak): Patch to a NSME stub.
1012 VTIR_Print("Cannot rebind static call to %s from %s\n",
1013 old_target_.ToCString(),
1014 Object::Handle(zone_, ic.Owner()).ToCString());
1015 return;
1016 }
1017 ic.ClearAndSetStaticTarget(new_target_);
1018 } else {
1019 FATAL("Unexpected rebind rule.");
1020 }
1021}
1022
1023#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
1024
1025} // namespace dart
1026