Chromium Code Reviews| 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 |