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/hash_table.h" |
8 #include "vm/isolate_reload.h" | 8 #include "vm/isolate_reload.h" |
9 #include "vm/log.h" | 9 #include "vm/log.h" |
10 #include "vm/resolver.h" | 10 #include "vm/resolver.h" |
11 #include "vm/symbols.h" | 11 #include "vm/symbols.h" |
12 | 12 |
13 namespace dart { | 13 namespace dart { |
14 | 14 |
15 #ifndef PRODUCT | 15 #ifndef PRODUCT |
16 | 16 |
17 DECLARE_FLAG(bool, trace_reload); | 17 DECLARE_FLAG(bool, trace_reload); |
18 DECLARE_FLAG(bool, trace_reload_verbose); | 18 DECLARE_FLAG(bool, trace_reload_verbose); |
19 DECLARE_FLAG(bool, two_args_smi_icd); | 19 DECLARE_FLAG(bool, two_args_smi_icd); |
20 | 20 |
21 #define IRC (Isolate::Current()->reload_context()) | |
22 | 21 |
23 class ObjectReloadUtils : public AllStatic { | 22 class ObjectReloadUtils : public AllStatic { |
24 static void DumpLibraryDictionary(const Library& lib) { | 23 static void DumpLibraryDictionary(const Library& lib) { |
25 DictionaryIterator it(lib); | 24 DictionaryIterator it(lib); |
26 Object& entry = Object::Handle(); | 25 Object& entry = Object::Handle(); |
27 String& name = String::Handle(); | 26 String& name = String::Handle(); |
28 TIR_Print("Dumping dictionary for %s\n", lib.ToCString()); | 27 TIR_Print("Dumping dictionary for %s\n", lib.ToCString()); |
29 while (it.HasNext()) { | 28 while (it.HasNext()) { |
30 entry = it.GetNext(); | 29 entry = it.GetNext(); |
31 name = entry.DictionaryName(); | 30 name = entry.DictionaryName(); |
(...skipping 325 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
357 ASSERT(!owner.IsNull()); | 356 ASSERT(!owner.IsNull()); |
358 if (!owner.IsPatchClass()) { | 357 if (!owner.IsPatchClass()) { |
359 ASSERT(owner.raw() == this->raw()); | 358 ASSERT(owner.raw() == this->raw()); |
360 field.set_owner(patch); | 359 field.set_owner(patch); |
361 } | 360 } |
362 field.ForceDynamicGuardedCidAndLength(); | 361 field.ForceDynamicGuardedCidAndLength(); |
363 } | 362 } |
364 } | 363 } |
365 | 364 |
366 | 365 |
367 bool Class::CanReload(const Class& replacement) const { | 366 class EnumClassConflict : public ClassReasonForCancelling { |
367 public: | |
368 EnumClassConflict(const Class& from, const Class& to) | |
369 : ClassReasonForCancelling(from, to) { } | |
370 | |
371 RawString* ToString() { | |
372 return String::NewFormatted( | |
373 from_.is_enum_class() | |
374 ? "Enum class cannot be redefined to be a non-enum class: %s" | |
375 : "Class cannot be redefined to be a enum class: %s", | |
376 from_.ToCString()); | |
377 } | |
378 }; | |
379 | |
380 | |
381 class EnsureFinalizedError : public ClassReasonForCancelling { | |
382 public: | |
383 EnsureFinalizedError(const Class& from, const Class& to, const Error& error) | |
384 : ClassReasonForCancelling(from, to), error_(error) { } | |
385 | |
386 private: | |
387 const Error& error_; | |
388 | |
389 RawError* ToError() { return error_.raw(); } | |
390 }; | |
391 | |
392 | |
393 class NativeFieldsConflict : public ClassReasonForCancelling { | |
394 public: | |
395 NativeFieldsConflict(const Class& from, const Class& to) | |
396 : ClassReasonForCancelling(from, to) { } | |
397 | |
398 private: | |
399 RawString* ToString() { | |
400 return String::NewFormatted( | |
401 "Number of native fields changed in %s", from_.ToCString()); | |
402 } | |
403 }; | |
404 | |
405 | |
406 class TypeParametersChanged : public ClassReasonForCancelling { | |
407 public: | |
408 TypeParametersChanged(const Class& from, const Class& to) | |
409 : ClassReasonForCancelling(from, to) {} | |
410 | |
411 RawString* ToString() { | |
412 return String::NewFormatted( | |
413 "Limitation: type parameters has changes for %s", from_.ToCString()); | |
414 } | |
415 }; | |
416 | |
417 | |
418 class PreFinalizedConflict : public ClassReasonForCancelling { | |
419 public: | |
420 PreFinalizedConflict(const Class& from, const Class& to) | |
421 : ClassReasonForCancelling(from, to) {} | |
422 | |
423 private: | |
424 RawString* ToString() { | |
425 return String::NewFormatted( | |
426 "Original class ('%s') is prefinalized and replacement class " | |
427 "('%s') is not ", | |
428 from_.ToCString(), to_.ToCString()); | |
429 } | |
430 }; | |
431 | |
432 | |
433 class InstanceSizeConflict : public ClassReasonForCancelling { | |
434 public: | |
435 InstanceSizeConflict(const Class& from, const Class& to) | |
436 : ClassReasonForCancelling(from, to) {} | |
437 | |
438 private: | |
439 RawString* ToString() { | |
440 return String::NewFormatted( | |
441 "Instance size mismatch between '%s' (%" Pd ") and replacement " | |
442 "'%s' ( %" Pd ")", | |
443 from_.ToCString(), | |
444 from_.instance_size(), | |
445 to_.ToCString(), | |
446 to_.instance_size()); | |
447 } | |
448 }; | |
449 | |
450 | |
451 class UnimplementedDeferredLibrary : public ReasonForCancelling { | |
452 public: | |
453 UnimplementedDeferredLibrary(const Library& from, | |
454 const Library& to, | |
455 const String& name) | |
456 : ReasonForCancelling(), from_(from), to_(to), name_(name) {} | |
457 | |
458 private: | |
459 const Library& from_; | |
460 const Library& to_; | |
461 const String& name_; | |
462 | |
463 RawString* ToString() { | |
464 const String& lib_url = String::Handle(to_.url()); | |
465 return String::NewFormatted( | |
466 "Reloading support for deferred loading has not yet been implemented:" | |
467 " library '%s' has deferred import '%s'", | |
468 lib_url.ToCString(), name_.ToCString()); | |
469 } | |
470 }; | |
471 | |
472 | |
473 // This is executed before interating over the instances. | |
474 void Class::CheckReload(const Class& replacement, | |
475 IsolateReloadContext* context) const { | |
368 ASSERT(IsolateReloadContext::IsSameClass(*this, replacement)); | 476 ASSERT(IsolateReloadContext::IsSameClass(*this, replacement)); |
369 | 477 |
370 if (is_enum_class() && !replacement.is_enum_class()) { | 478 // Class cannot change enum property. |
371 IRC->ReportError(String::Handle(String::NewFormatted( | 479 if (is_enum_class() != replacement.is_enum_class()) { |
372 "Enum class cannot be redefined to be a non-enum class: %s", | 480 context->AddReasonForCancelling( |
373 ToCString()))); | 481 new EnumClassConflict(*this, replacement)); |
374 return false; | 482 return; |
375 } | |
376 | |
377 if (!is_enum_class() && replacement.is_enum_class()) { | |
378 IRC->ReportError(String::Handle(String::NewFormatted( | |
379 "Class cannot be redefined to be a enum class: %s", | |
380 ToCString()))); | |
381 return false; | |
382 } | 483 } |
383 | 484 |
384 if (is_finalized()) { | 485 if (is_finalized()) { |
486 // Ensure the replacement class is also finalized. | |
385 const Error& error = | 487 const Error& error = |
386 Error::Handle(replacement.EnsureIsFinalized(Thread::Current())); | 488 Error::Handle(replacement.EnsureIsFinalized(Thread::Current())); |
387 if (!error.IsNull()) { | 489 if (!error.IsNull()) { |
388 IRC->ReportError(error); | 490 context->AddReasonForCancelling( |
Cutch
2016/07/26 14:42:46
This way of reporting errors is much nicer than wh
bakster
2016/07/26 16:54:51
Thanks
| |
389 return false; | 491 new EnsureFinalizedError(*this, replacement, error)); |
492 return; // No reason to check other properties. | |
390 } | 493 } |
391 TIR_Print("Finalized replacement class for %s\n", ToCString()); | 494 TIR_Print("Finalized replacement class for %s\n", ToCString()); |
392 } | 495 } |
393 | 496 |
394 // At this point the original and replacement must be in the same state. | 497 // Native field count cannot change. |
498 if (num_native_fields() != replacement.num_native_fields()) { | |
499 context->AddReasonForCancelling( | |
500 new NativeFieldsConflict(*this, replacement)); | |
501 return; | |
502 } | |
503 | |
504 // Just checking. | |
505 ASSERT(is_enum_class() == replacement.is_enum_class()); | |
506 ASSERT(num_native_fields() == replacement.num_native_fields()); | |
507 | |
508 if (is_finalized()) { | |
509 if (!CanReloadFinalized(replacement, context)) return; | |
510 } | |
511 if (is_prefinalized()) { | |
512 if (!CanReloadPreFinalized(replacement, context)) return; | |
513 } | |
395 ASSERT(is_finalized() == replacement.is_finalized()); | 514 ASSERT(is_finalized() == replacement.is_finalized()); |
396 | 515 TIR_Print("Class `%s` can be reloaded (%" Pd " and %" Pd ")\n", |
397 if (is_finalized()) { | 516 ToCString(), id(), replacement.id()); |
398 // Get the field maps for both classes. These field maps walk the class | 517 } |
399 // hierarchy. | 518 |
400 const Array& fields = | 519 |
401 Array::Handle(OffsetToFieldMap()); | 520 |
402 const Array& replacement_fields = | 521 bool Class::RequiresInstanceMorphing(const Class& replacement) const { |
403 Array::Handle(replacement.OffsetToFieldMap()); | 522 // Get the field maps for both classes. These field maps walk the class |
404 | 523 // hierarchy. |
405 // Check that the size of the instance is the same. | 524 const Array& fields = Array::Handle(OffsetToFieldMap()); |
406 if (fields.Length() != replacement_fields.Length()) { | 525 const Array& replacement_fields |
407 IRC->ReportError(String::Handle(String::NewFormatted( | 526 = Array::Handle(replacement.OffsetToFieldMap()); |
408 "Number of instance fields changed in %s", ToCString()))); | 527 |
528 // Check that the size of the instance is the same. | |
529 if (fields.Length() != replacement_fields.Length()) return true; | |
530 | |
531 // Check that we have the same next field offset. This check is not | |
532 // redundant with the one above because the instance OffsetToFieldMap | |
533 // array length is based on the instance size (which may be aligned up). | |
534 if (next_field_offset() != replacement.next_field_offset()) return true; | |
535 | |
536 // Verify that field names / offsets match across the entire hierarchy. | |
537 Field& field = Field::Handle(); | |
538 String& field_name = String::Handle(); | |
539 Field& replacement_field = Field::Handle(); | |
540 String& replacement_field_name = String::Handle(); | |
541 | |
542 for (intptr_t i = 0; i < fields.Length(); i++) { | |
543 if (fields.At(i) == Field::null()) { | |
544 ASSERT(replacement_fields.At(i) == Field::null()); | |
545 continue; | |
546 } | |
547 field = Field::RawCast(fields.At(i)); | |
548 replacement_field = Field::RawCast(replacement_fields.At(i)); | |
549 field_name = field.name(); | |
550 replacement_field_name = replacement_field.name(); | |
551 if (!field_name.Equals(replacement_field_name)) return true; | |
552 } | |
553 return false; | |
554 } | |
555 | |
556 | |
557 bool Class::CanReloadFinalized(const Class& replacement, | |
558 IsolateReloadContext* context) const { | |
559 // Make sure the declaration types matches for the two classes. | |
560 // ex. class A<int,B> {} cannot be replace with class A<B> {}. | |
561 const AbstractType& dt = AbstractType::Handle(DeclarationType()); | |
562 const AbstractType& replacement_dt = | |
563 AbstractType::Handle(replacement.DeclarationType()); | |
564 if (!dt.Equals(replacement_dt)) { | |
565 context->AddReasonForCancelling( | |
566 new TypeParametersChanged(*this, replacement)); | |
567 return false; | |
568 } | |
569 if (RequiresInstanceMorphing(replacement)) { | |
570 context->AddInstanceMorpher(new InstanceMorpher(*this, replacement)); | |
571 } | |
572 return true; | |
573 } | |
574 | |
575 | |
576 bool Class::CanReloadPreFinalized(const Class& replacement, | |
577 IsolateReloadContext* context) const { | |
578 // The replacement class must also prefinalized. | |
579 if (!replacement.is_prefinalized()) { | |
580 context->AddReasonForCancelling( | |
581 new PreFinalizedConflict(*this, replacement)); | |
409 return false; | 582 return false; |
410 } | 583 } |
411 | 584 // Check the instance sizes are equal. |
412 // Check that we have the same next field offset. This check is not | 585 if (instance_size() != replacement.instance_size()) { |
413 // redundant with the one above because the instance OffsetToFieldMap | 586 context->AddReasonForCancelling( |
414 // array length is based on the instance size (which may be aligned up). | 587 new InstanceSizeConflict(*this, replacement)); |
415 if (next_field_offset() != replacement.next_field_offset()) { | |
416 IRC->ReportError(String::Handle(String::NewFormatted( | |
417 "Number of instance fields changed in %s", ToCString()))); | |
418 return false; | 588 return false; |
419 } | 589 } |
420 | |
421 if (NumTypeArguments() != replacement.NumTypeArguments()) { | |
422 IRC->ReportError(String::Handle(String::NewFormatted( | |
423 "Number of type arguments changed in %s", ToCString()))); | |
424 return false; | |
425 } | |
426 | |
427 // Verify that field names / offsets match across the entire hierarchy. | |
428 Field& field = Field::Handle(); | |
429 String& field_name = String::Handle(); | |
430 Field& replacement_field = Field::Handle(); | |
431 String& replacement_field_name = String::Handle(); | |
432 for (intptr_t i = 0; i < fields.Length(); i++) { | |
433 if (fields.At(i) == Field::null()) { | |
434 ASSERT(replacement_fields.At(i) == Field::null()); | |
435 continue; | |
436 } | |
437 field = Field::RawCast(fields.At(i)); | |
438 replacement_field = Field::RawCast(replacement_fields.At(i)); | |
439 field_name = field.name(); | |
440 replacement_field_name = replacement_field.name(); | |
441 if (!field_name.Equals(replacement_field_name)) { | |
442 IRC->ReportError(String::Handle(String::NewFormatted( | |
443 "Name of instance field changed ('%s' vs '%s') in '%s'", | |
444 field_name.ToCString(), | |
445 replacement_field_name.ToCString(), | |
446 ToCString()))); | |
447 return false; | |
448 } | |
449 } | |
450 } else if (is_prefinalized()) { | |
451 if (!replacement.is_prefinalized()) { | |
452 IRC->ReportError(String::Handle(String::NewFormatted( | |
453 "Original class ('%s') is prefinalized and replacement class " | |
454 "('%s') is not ", | |
455 ToCString(), replacement.ToCString()))); | |
456 return false; | |
457 } | |
458 if (instance_size() != replacement.instance_size()) { | |
459 IRC->ReportError(String::Handle(String::NewFormatted( | |
460 "Instance size mismatch between '%s' (%" Pd ") and replacement " | |
461 "'%s' ( %" Pd ")", | |
462 ToCString(), | |
463 instance_size(), | |
464 replacement.ToCString(), | |
465 replacement.instance_size()))); | |
466 return false; | |
467 } | |
468 } | |
469 | |
470 // native field count check. | |
471 if (num_native_fields() != replacement.num_native_fields()) { | |
472 IRC->ReportError(String::Handle(String::NewFormatted( | |
473 "Number of native fields changed in %s", ToCString()))); | |
474 return false; | |
475 } | |
476 | |
477 // TODO(johnmccutchan) type parameter count check. | |
478 | |
479 TIR_Print("Class `%s` can be reloaded (%" Pd " and %" Pd ")\n", | |
480 ToCString(), | |
481 id(), | |
482 replacement.id()); | |
483 return true; | 590 return true; |
484 } | 591 } |
485 | 592 |
486 | 593 |
487 bool Library::CanReload(const Library& replacement) const { | 594 void Library::CheckReload(const Library& replacement, |
595 IsolateReloadContext* context) const { | |
488 // TODO(26878): If the replacement library uses deferred loading, | 596 // TODO(26878): If the replacement library uses deferred loading, |
489 // reject it. We do not yet support reloading deferred libraries. | 597 // reject it. We do not yet support reloading deferred libraries. |
490 LibraryPrefix& prefix = LibraryPrefix::Handle(); | 598 LibraryPrefix& prefix = LibraryPrefix::Handle(); |
491 LibraryPrefixIterator it(replacement); | 599 LibraryPrefixIterator it(replacement); |
492 while (it.HasNext()) { | 600 while (it.HasNext()) { |
493 prefix = it.GetNext(); | 601 prefix = it.GetNext(); |
494 if (prefix.is_deferred_load()) { | 602 if (prefix.is_deferred_load()) { |
495 const String& lib_url = String::Handle(replacement.url()); | |
496 const String& prefix_name = String::Handle(prefix.name()); | 603 const String& prefix_name = String::Handle(prefix.name()); |
497 IRC->ReportError(String::Handle(String::NewFormatted( | 604 context->AddReasonForCancelling( |
498 "Reloading support for deferred loading has not yet been implemented:" | 605 new UnimplementedDeferredLibrary(*this, replacement, prefix_name)); |
499 " library '%s' has deferred import '%s'", | 606 return; |
500 lib_url.ToCString(), prefix_name.ToCString()))); | |
501 return false; | |
502 } | 607 } |
503 } | 608 } |
504 return true; | |
505 } | 609 } |
506 | 610 |
507 | 611 |
508 static const Function* static_call_target = NULL; | 612 static const Function* static_call_target = NULL; |
509 | 613 |
614 | |
510 void ICData::Reset() const { | 615 void ICData::Reset() const { |
511 if (is_static_call()) { | 616 if (is_static_call()) { |
512 const Function& old_target = Function::Handle(GetTargetAt(0)); | 617 const Function& old_target = Function::Handle(GetTargetAt(0)); |
513 if (old_target.IsNull()) { | 618 if (old_target.IsNull()) { |
514 FATAL("old_target is NULL.\n"); | 619 FATAL("old_target is NULL.\n"); |
515 } | 620 } |
516 static_call_target = &old_target; | 621 static_call_target = &old_target; |
517 | 622 |
518 const String& selector = String::Handle(old_target.name()); | 623 const String& selector = String::Handle(old_target.name()); |
519 Function& new_target = Function::Handle(); | 624 Function& new_target = Function::Handle(); |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
574 } | 679 } |
575 ClearAndSetStaticTarget(new_target); | 680 ClearAndSetStaticTarget(new_target); |
576 } else { | 681 } else { |
577 ClearWithSentinel(); | 682 ClearWithSentinel(); |
578 } | 683 } |
579 } | 684 } |
580 | 685 |
581 #endif // !PRODUCT | 686 #endif // !PRODUCT |
582 | 687 |
583 } // namespace dart. | 688 } // namespace dart. |
OLD | NEW |