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 have changed 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( |
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 |
| 562 const AbstractType& dt = AbstractType::Handle(DeclarationType()); |
| 563 const AbstractType& replacement_dt = |
| 564 AbstractType::Handle(replacement.DeclarationType()); |
| 565 if (!dt.Equals(replacement_dt)) { |
| 566 context->AddReasonForCancelling( |
| 567 new TypeParametersChanged(*this, replacement)); |
| 568 return false; |
| 569 } |
| 570 if (RequiresInstanceMorphing(replacement)) { |
| 571 context->AddInstanceMorpher(new InstanceMorpher(*this, replacement)); |
| 572 } |
| 573 return true; |
| 574 } |
| 575 |
| 576 |
| 577 bool Class::CanReloadPreFinalized(const Class& replacement, |
| 578 IsolateReloadContext* context) const { |
| 579 // The replacement class must also prefinalized. |
| 580 if (!replacement.is_prefinalized()) { |
| 581 context->AddReasonForCancelling( |
| 582 new PreFinalizedConflict(*this, replacement)); |
409 return false; | 583 return false; |
410 } | 584 } |
411 | 585 // Check the instance sizes are equal. |
412 // Check that we have the same next field offset. This check is not | 586 if (instance_size() != replacement.instance_size()) { |
413 // redundant with the one above because the instance OffsetToFieldMap | 587 context->AddReasonForCancelling( |
414 // array length is based on the instance size (which may be aligned up). | 588 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; | 589 return false; |
419 } | 590 } |
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; | 591 return true; |
484 } | 592 } |
485 | 593 |
486 | 594 |
487 bool Library::CanReload(const Library& replacement) const { | 595 void Library::CheckReload(const Library& replacement, |
| 596 IsolateReloadContext* context) const { |
488 // TODO(26878): If the replacement library uses deferred loading, | 597 // TODO(26878): If the replacement library uses deferred loading, |
489 // reject it. We do not yet support reloading deferred libraries. | 598 // reject it. We do not yet support reloading deferred libraries. |
490 LibraryPrefix& prefix = LibraryPrefix::Handle(); | 599 LibraryPrefix& prefix = LibraryPrefix::Handle(); |
491 LibraryPrefixIterator it(replacement); | 600 LibraryPrefixIterator it(replacement); |
492 while (it.HasNext()) { | 601 while (it.HasNext()) { |
493 prefix = it.GetNext(); | 602 prefix = it.GetNext(); |
494 if (prefix.is_deferred_load()) { | 603 if (prefix.is_deferred_load()) { |
495 const String& lib_url = String::Handle(replacement.url()); | |
496 const String& prefix_name = String::Handle(prefix.name()); | 604 const String& prefix_name = String::Handle(prefix.name()); |
497 IRC->ReportError(String::Handle(String::NewFormatted( | 605 context->AddReasonForCancelling( |
498 "Reloading support for deferred loading has not yet been implemented:" | 606 new UnimplementedDeferredLibrary(*this, replacement, prefix_name)); |
499 " library '%s' has deferred import '%s'", | 607 return; |
500 lib_url.ToCString(), prefix_name.ToCString()))); | |
501 return false; | |
502 } | 608 } |
503 } | 609 } |
504 return true; | |
505 } | 610 } |
506 | 611 |
507 | 612 |
508 static const Function* static_call_target = NULL; | 613 static const Function* static_call_target = NULL; |
509 | 614 |
| 615 |
510 void ICData::Reset() const { | 616 void ICData::Reset() const { |
511 if (is_static_call()) { | 617 if (is_static_call()) { |
512 const Function& old_target = Function::Handle(GetTargetAt(0)); | 618 const Function& old_target = Function::Handle(GetTargetAt(0)); |
513 if (old_target.IsNull()) { | 619 if (old_target.IsNull()) { |
514 FATAL("old_target is NULL.\n"); | 620 FATAL("old_target is NULL.\n"); |
515 } | 621 } |
516 static_call_target = &old_target; | 622 static_call_target = &old_target; |
517 | 623 |
518 const String& selector = String::Handle(old_target.name()); | 624 const String& selector = String::Handle(old_target.name()); |
519 Function& new_target = Function::Handle(); | 625 Function& new_target = Function::Handle(); |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
574 } | 680 } |
575 ClearAndSetStaticTarget(new_target); | 681 ClearAndSetStaticTarget(new_target); |
576 } else { | 682 } else { |
577 ClearWithSentinel(); | 683 ClearWithSentinel(); |
578 } | 684 } |
579 } | 685 } |
580 | 686 |
581 #endif // !PRODUCT | 687 #endif // !PRODUCT |
582 | 688 |
583 } // namespace dart. | 689 } // namespace dart. |
OLD | NEW |