OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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/snapshot.h" | 5 #include "vm/snapshot.h" |
6 | 6 |
7 #include "platform/assert.h" | 7 #include "platform/assert.h" |
8 #include "vm/bootstrap.h" | 8 #include "vm/bootstrap.h" |
9 #include "vm/class_finalizer.h" | 9 #include "vm/class_finalizer.h" |
10 #include "vm/dart.h" | 10 #include "vm/dart.h" |
(...skipping 11 matching lines...) Expand all Loading... |
22 #include "vm/verified_memory.h" | 22 #include "vm/verified_memory.h" |
23 #include "vm/version.h" | 23 #include "vm/version.h" |
24 | 24 |
25 // We currently only expect the Dart mutator to read snapshots. | 25 // We currently only expect the Dart mutator to read snapshots. |
26 #define ASSERT_NO_SAFEPOINT_SCOPE() \ | 26 #define ASSERT_NO_SAFEPOINT_SCOPE() \ |
27 isolate()->AssertCurrentThreadIsMutator(); \ | 27 isolate()->AssertCurrentThreadIsMutator(); \ |
28 ASSERT(thread()->no_safepoint_scope_depth() != 0) | 28 ASSERT(thread()->no_safepoint_scope_depth() != 0) |
29 | 29 |
30 namespace dart { | 30 namespace dart { |
31 | 31 |
32 static const int kNumVmIsolateSnapshotReferences = 32 * KB; | |
33 static const int kNumInitialReferencesInFullSnapshot = 160 * KB; | |
34 static const int kNumInitialReferences = 64; | 32 static const int kNumInitialReferences = 64; |
35 | 33 |
36 | 34 |
37 static bool IsSingletonClassId(intptr_t class_id) { | 35 static bool IsSingletonClassId(intptr_t class_id) { |
38 // Check if this is a singleton object class which is shared by all isolates. | 36 // Check if this is a singleton object class which is shared by all isolates. |
39 return ((class_id >= kClassCid && class_id <= kUnwindErrorCid) || | 37 return ((class_id >= kClassCid && class_id <= kUnwindErrorCid) || |
40 (class_id >= kNullCid && class_id <= kVoidCid)); | 38 (class_id >= kNullCid && class_id <= kVoidCid)); |
41 } | 39 } |
42 | 40 |
43 | 41 |
(...skipping 535 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
579 } | 577 } |
580 ~HeapLocker() { | 578 ~HeapLocker() { |
581 page_space_->ReleaseDataLock(); | 579 page_space_->ReleaseDataLock(); |
582 } | 580 } |
583 | 581 |
584 private: | 582 private: |
585 PageSpace* page_space_; | 583 PageSpace* page_space_; |
586 }; | 584 }; |
587 | 585 |
588 | 586 |
589 RawApiError* SnapshotReader::ReadFullSnapshot() { | |
590 ASSERT(Snapshot::IsFull(kind_)); | |
591 Thread* thread = Thread::Current(); | |
592 Isolate* isolate = thread->isolate(); | |
593 ASSERT(isolate != NULL); | |
594 ObjectStore* object_store = isolate->object_store(); | |
595 ASSERT(object_store != NULL); | |
596 | |
597 // First read the version string, and check that it matches. | |
598 RawApiError* error = VerifyVersionAndFeatures(); | |
599 if (error != ApiError::null()) { | |
600 return error; | |
601 } | |
602 | |
603 // The version string matches. Read the rest of the snapshot. | |
604 | |
605 // TODO(asiva): Add a check here to ensure we have the right heap | |
606 // size for the full snapshot being read. | |
607 { | |
608 NoSafepointScope no_safepoint; | |
609 HeapLocker hl(thread, old_space()); | |
610 | |
611 // Read in all the objects stored in the object store. | |
612 intptr_t num_flds = | |
613 (object_store->to_snapshot(kind_) - object_store->from()); | |
614 for (intptr_t i = 0; i <= num_flds; i++) { | |
615 *(object_store->from() + i) = ReadObjectImpl(kAsInlinedObject); | |
616 } | |
617 for (intptr_t i = 0; i < backward_references_->length(); i++) { | |
618 if (!(*backward_references_)[i].is_deserialized()) { | |
619 ReadObjectImpl(kAsInlinedObject); | |
620 (*backward_references_)[i].set_state(kIsDeserialized); | |
621 } | |
622 } | |
623 | |
624 if (kind_ == Snapshot::kAppNoJIT) { | |
625 ICData& ic = ICData::Handle(thread->zone()); | |
626 Object& funcOrCode = Object::Handle(thread->zone()); | |
627 Code& code = Code::Handle(thread->zone()); | |
628 Smi& entry_point = Smi::Handle(thread->zone()); | |
629 for (intptr_t i = 0; i < backward_references_->length(); i++) { | |
630 if ((*backward_references_)[i].reference()->IsICData()) { | |
631 ic ^= (*backward_references_)[i].reference()->raw(); | |
632 for (intptr_t j = 0; j < ic.NumberOfChecks(); j++) { | |
633 funcOrCode = ic.GetTargetOrCodeAt(j); | |
634 if (funcOrCode.IsCode()) { | |
635 code ^= funcOrCode.raw(); | |
636 entry_point = Smi::FromAlignedAddress(code.EntryPoint()); | |
637 ic.SetEntryPointAt(j, entry_point); | |
638 } | |
639 } | |
640 } | |
641 } | |
642 } | |
643 | |
644 // Validate the class table. | |
645 #if defined(DEBUG) | |
646 isolate->ValidateClassTable(); | |
647 #endif | |
648 | |
649 // Setup native resolver for bootstrap impl. | |
650 Bootstrap::SetupNativeResolver(); | |
651 } | |
652 | |
653 Class& cls = Class::Handle(thread->zone()); | |
654 for (intptr_t i = 0; i < backward_references_->length(); i++) { | |
655 if ((*backward_references_)[i].reference()->IsClass()) { | |
656 cls ^= (*backward_references_)[i].reference()->raw(); | |
657 cls.RehashConstants(thread->zone()); | |
658 } | |
659 } | |
660 | |
661 return ApiError::null(); | |
662 } | |
663 | |
664 | |
665 RawObject* SnapshotReader::ReadScriptSnapshot() { | 587 RawObject* SnapshotReader::ReadScriptSnapshot() { |
666 ASSERT(kind_ == Snapshot::kScript); | 588 ASSERT(kind_ == Snapshot::kScript); |
667 | 589 |
668 // First read the version string, and check that it matches. | 590 // First read the version string, and check that it matches. |
669 RawApiError* error = VerifyVersionAndFeatures(); | 591 RawApiError* error = VerifyVersionAndFeatures(); |
670 if (error != ApiError::null()) { | 592 if (error != ApiError::null()) { |
671 return error; | 593 return error; |
672 } | 594 } |
673 | 595 |
674 // The version string matches. Read the rest of the snapshot. | 596 // The version string matches. Read the rest of the snapshot. |
(...skipping 1020 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1695 reinterpret_cast<RawObject**>(result.raw()->ptr()); | 1617 reinterpret_cast<RawObject**>(result.raw()->ptr()); |
1696 for (intptr_t i = 0; i < len; i++) { | 1618 for (intptr_t i = 0; i < len; i++) { |
1697 *PassiveObjectHandle() = ReadObjectImpl(as_reference, | 1619 *PassiveObjectHandle() = ReadObjectImpl(as_reference, |
1698 object_id, | 1620 object_id, |
1699 (i + offset)); | 1621 (i + offset)); |
1700 result.SetAt(i, *PassiveObjectHandle()); | 1622 result.SetAt(i, *PassiveObjectHandle()); |
1701 } | 1623 } |
1702 } | 1624 } |
1703 | 1625 |
1704 | 1626 |
1705 VmIsolateSnapshotReader::VmIsolateSnapshotReader( | |
1706 Snapshot::Kind kind, | |
1707 const uint8_t* buffer, | |
1708 intptr_t size, | |
1709 const uint8_t* instructions_buffer, | |
1710 const uint8_t* data_buffer, | |
1711 Thread* thread) | |
1712 : SnapshotReader(buffer, | |
1713 size, | |
1714 instructions_buffer, | |
1715 data_buffer, | |
1716 kind, | |
1717 new ZoneGrowableArray<BackRefNode>( | |
1718 kNumVmIsolateSnapshotReferences), | |
1719 thread) { | |
1720 ASSERT(Snapshot::IsFull(kind)); | |
1721 } | |
1722 | |
1723 | |
1724 VmIsolateSnapshotReader::~VmIsolateSnapshotReader() { | |
1725 intptr_t len = GetBackwardReferenceTable()->length(); | |
1726 Object::InitVmIsolateSnapshotObjectTable(len); | |
1727 ZoneGrowableArray<BackRefNode>* backrefs = GetBackwardReferenceTable(); | |
1728 for (intptr_t i = 0; i < len; i++) { | |
1729 Object::vm_isolate_snapshot_object_table().SetAt( | |
1730 i, *(backrefs->At(i).reference())); | |
1731 } | |
1732 ResetBackwardReferenceTable(); | |
1733 Dart::set_instructions_snapshot_buffer(instructions_buffer_); | |
1734 Dart::set_data_snapshot_buffer(data_buffer_); | |
1735 } | |
1736 | |
1737 | |
1738 RawApiError* VmIsolateSnapshotReader::ReadVmIsolateSnapshot() { | |
1739 ASSERT(Snapshot::IsFull(kind())); | |
1740 Thread* thread = Thread::Current(); | |
1741 Isolate* isolate = thread->isolate(); | |
1742 ASSERT(isolate != NULL); | |
1743 ASSERT(isolate == Dart::vm_isolate()); | |
1744 ObjectStore* object_store = isolate->object_store(); | |
1745 ASSERT(object_store != NULL); | |
1746 | |
1747 // First read the version string, and check that it matches. | |
1748 RawApiError* error = VerifyVersionAndFeatures(); | |
1749 if (error != ApiError::null()) { | |
1750 return error; | |
1751 } | |
1752 | |
1753 // The version string matches. Read the rest of the snapshot. | |
1754 | |
1755 { | |
1756 NoSafepointScope no_safepoint; | |
1757 HeapLocker hl(thread, old_space()); | |
1758 | |
1759 // Read in the symbol table. | |
1760 object_store->symbol_table_ = reinterpret_cast<RawArray*>(ReadObject()); | |
1761 | |
1762 Symbols::InitOnceFromSnapshot(isolate); | |
1763 | |
1764 // Read in all the script objects and the accompanying token streams | |
1765 // for bootstrap libraries so that they are in the VM isolate's read | |
1766 // only memory. | |
1767 *(ArrayHandle()) ^= ReadObject(); | |
1768 | |
1769 if (Snapshot::IncludesCode(kind())) { | |
1770 StubCode::ReadFrom(this); | |
1771 } | |
1772 | |
1773 // Validate the class table. | |
1774 #if defined(DEBUG) | |
1775 isolate->ValidateClassTable(); | |
1776 #endif | |
1777 | |
1778 return ApiError::null(); | |
1779 } | |
1780 } | |
1781 | |
1782 | |
1783 IsolateSnapshotReader::IsolateSnapshotReader(Snapshot::Kind kind, | |
1784 const uint8_t* buffer, | |
1785 intptr_t size, | |
1786 const uint8_t* instructions_buffer, | |
1787 const uint8_t* data_buffer, | |
1788 Thread* thread) | |
1789 : SnapshotReader(buffer, | |
1790 size, | |
1791 instructions_buffer, | |
1792 data_buffer, | |
1793 kind, | |
1794 new ZoneGrowableArray<BackRefNode>( | |
1795 kNumInitialReferencesInFullSnapshot), | |
1796 thread) { | |
1797 isolate()->set_compilation_allowed(kind != Snapshot::kAppNoJIT); | |
1798 ASSERT(Snapshot::IsFull(kind)); | |
1799 } | |
1800 | |
1801 | |
1802 IsolateSnapshotReader::~IsolateSnapshotReader() { | |
1803 ResetBackwardReferenceTable(); | |
1804 } | |
1805 | |
1806 | |
1807 ScriptSnapshotReader::ScriptSnapshotReader(const uint8_t* buffer, | 1627 ScriptSnapshotReader::ScriptSnapshotReader(const uint8_t* buffer, |
1808 intptr_t size, | 1628 intptr_t size, |
1809 Thread* thread) | 1629 Thread* thread) |
1810 : SnapshotReader(buffer, | 1630 : SnapshotReader(buffer, |
1811 size, | 1631 size, |
1812 NULL, /* instructions_buffer */ | 1632 NULL, /* instructions_buffer */ |
1813 NULL, /* data_buffer */ | 1633 NULL, /* data_buffer */ |
1814 Snapshot::kScript, | 1634 Snapshot::kScript, |
1815 new ZoneGrowableArray<BackRefNode>(kNumInitialReferences), | 1635 new ZoneGrowableArray<BackRefNode>(kNumInitialReferences), |
1816 thread) { | 1636 thread) { |
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2032 | 1852 |
2033 intptr_t count() const { return count_; } | 1853 intptr_t count() const { return count_; } |
2034 | 1854 |
2035 private: | 1855 private: |
2036 Object& objHandle_; | 1856 Object& objHandle_; |
2037 intptr_t count_; | 1857 intptr_t count_; |
2038 const Array* scripts_; | 1858 const Array* scripts_; |
2039 }; | 1859 }; |
2040 | 1860 |
2041 | 1861 |
2042 FullSnapshotWriter::FullSnapshotWriter(Snapshot::Kind kind, | |
2043 uint8_t** vm_isolate_snapshot_buffer, | |
2044 uint8_t** isolate_snapshot_buffer, | |
2045 ReAlloc alloc, | |
2046 InstructionsWriter* instructions_writer) | |
2047 : thread_(Thread::Current()), | |
2048 kind_(kind), | |
2049 vm_isolate_snapshot_buffer_(vm_isolate_snapshot_buffer), | |
2050 isolate_snapshot_buffer_(isolate_snapshot_buffer), | |
2051 alloc_(alloc), | |
2052 vm_isolate_snapshot_size_(0), | |
2053 isolate_snapshot_size_(0), | |
2054 forward_list_(NULL), | |
2055 instructions_writer_(instructions_writer), | |
2056 scripts_(Array::Handle(zone())), | |
2057 saved_symbol_table_(Array::Handle(zone())), | |
2058 new_vm_symbol_table_(Array::Handle(zone())) { | |
2059 ASSERT(isolate_snapshot_buffer_ != NULL); | |
2060 ASSERT(alloc_ != NULL); | |
2061 ASSERT(isolate() != NULL); | |
2062 ASSERT(ClassFinalizer::AllClassesFinalized()); | |
2063 ASSERT(isolate() != NULL); | |
2064 ASSERT(heap() != NULL); | |
2065 ObjectStore* object_store = isolate()->object_store(); | |
2066 ASSERT(object_store != NULL); | |
2067 | |
2068 #if defined(DEBUG) | |
2069 // Ensure the class table is valid. | |
2070 isolate()->ValidateClassTable(); | |
2071 #endif | |
2072 // Can't have any mutation happening while we're serializing. | |
2073 ASSERT(isolate()->background_compiler() == NULL); | |
2074 | |
2075 intptr_t first_object_id = -1; | |
2076 if (vm_isolate_snapshot_buffer != NULL) { | |
2077 NOT_IN_PRODUCT(TimelineDurationScope tds(thread(), | |
2078 Timeline::GetIsolateStream(), "PrepareNewVMIsolate")); | |
2079 | |
2080 // Collect all the script objects and their accompanying token stream | |
2081 // objects into an array so that we can write it out as part of the VM | |
2082 // isolate snapshot. We first count the number of script objects, allocate | |
2083 // an array and then fill it up with the script objects. | |
2084 ScriptVisitor scripts_counter(thread()); | |
2085 heap()->IterateOldObjects(&scripts_counter); | |
2086 Dart::vm_isolate()->heap()->IterateOldObjects(&scripts_counter); | |
2087 intptr_t count = scripts_counter.count(); | |
2088 scripts_ = Array::New(count, Heap::kOld); | |
2089 ScriptVisitor script_visitor(thread(), &scripts_); | |
2090 heap()->IterateOldObjects(&script_visitor); | |
2091 Dart::vm_isolate()->heap()->IterateOldObjects(&script_visitor); | |
2092 ASSERT(script_visitor.count() == count); | |
2093 | |
2094 // Tuck away the current symbol table. | |
2095 saved_symbol_table_ = object_store->symbol_table(); | |
2096 | |
2097 // Create a unified symbol table that will be written as the vm isolate's | |
2098 // symbol table. | |
2099 new_vm_symbol_table_ = Symbols::UnifiedSymbolTable(); | |
2100 | |
2101 // Create an empty symbol table that will be written as the isolate's symbol | |
2102 // table. | |
2103 Symbols::SetupSymbolTable(isolate()); | |
2104 | |
2105 first_object_id = kMaxPredefinedObjectIds; | |
2106 } else { | |
2107 intptr_t max_vm_isolate_object_id = | |
2108 Object::vm_isolate_snapshot_object_table().Length(); | |
2109 first_object_id = kMaxPredefinedObjectIds + max_vm_isolate_object_id; | |
2110 } | |
2111 | |
2112 forward_list_ = new ForwardList(thread(), first_object_id); | |
2113 ASSERT(forward_list_ != NULL); | |
2114 } | |
2115 | |
2116 | |
2117 FullSnapshotWriter::~FullSnapshotWriter() { | |
2118 delete forward_list_; | |
2119 // We may run Dart code afterwards, restore the symbol table if needed. | |
2120 if (!saved_symbol_table_.IsNull()) { | |
2121 isolate()->object_store()->set_symbol_table(saved_symbol_table_); | |
2122 saved_symbol_table_ = Array::null(); | |
2123 } | |
2124 new_vm_symbol_table_ = Array::null(); | |
2125 scripts_ = Array::null(); | |
2126 } | |
2127 | |
2128 | |
2129 void FullSnapshotWriter::WriteVmIsolateSnapshot() { | |
2130 NOT_IN_PRODUCT(TimelineDurationScope tds(thread(), | |
2131 Timeline::GetIsolateStream(), "WriteVmIsolateSnapshot")); | |
2132 | |
2133 ASSERT(vm_isolate_snapshot_buffer_ != NULL); | |
2134 SnapshotWriter writer(thread(), | |
2135 kind_, | |
2136 vm_isolate_snapshot_buffer_, | |
2137 alloc_, | |
2138 kInitialSize, | |
2139 forward_list_, | |
2140 instructions_writer_, | |
2141 true, /* can_send_any_object */ | |
2142 true /* writing_vm_isolate */); | |
2143 // Write full snapshot for the VM isolate. | |
2144 // Setup for long jump in case there is an exception while writing | |
2145 // the snapshot. | |
2146 LongJumpScope jump; | |
2147 if (setjmp(*jump.Set()) == 0) { | |
2148 // Reserve space in the output buffer for a snapshot header. | |
2149 writer.ReserveHeader(); | |
2150 | |
2151 // Write out the version string. | |
2152 writer.WriteVersionAndFeatures(); | |
2153 | |
2154 /* | |
2155 * Now Write out the following | |
2156 * - the symbol table | |
2157 * - all the scripts and token streams for these scripts | |
2158 * - the stub code (precompiled snapshots only) | |
2159 **/ | |
2160 // Write out the symbol table. | |
2161 writer.WriteObject(new_vm_symbol_table_.raw()); | |
2162 | |
2163 // Write out all the script objects and the accompanying token streams | |
2164 // for the bootstrap libraries so that they are in the VM isolate | |
2165 // read only memory. | |
2166 writer.WriteObject(scripts_.raw()); | |
2167 | |
2168 if (Snapshot::IncludesCode(kind_)) { | |
2169 StubCode::WriteTo(&writer); | |
2170 } | |
2171 | |
2172 writer.FillHeader(writer.kind()); | |
2173 | |
2174 vm_isolate_snapshot_size_ = writer.BytesWritten(); | |
2175 } else { | |
2176 writer.ThrowException(writer.exception_type(), writer.exception_msg()); | |
2177 } | |
2178 } | |
2179 | |
2180 | |
2181 void FullSnapshotWriter::WriteIsolateFullSnapshot() { | |
2182 NOT_IN_PRODUCT(TimelineDurationScope tds(thread(), | |
2183 Timeline::GetIsolateStream(), "WriteIsolateFullSnapshot")); | |
2184 | |
2185 SnapshotWriter writer(thread(), | |
2186 kind_, | |
2187 isolate_snapshot_buffer_, | |
2188 alloc_, | |
2189 kInitialSize, | |
2190 forward_list_, | |
2191 instructions_writer_, | |
2192 true, /* can_send_any_object */ | |
2193 false /* writing_vm_isolate */); | |
2194 ObjectStore* object_store = isolate()->object_store(); | |
2195 ASSERT(object_store != NULL); | |
2196 | |
2197 // Write full snapshot for a regular isolate. | |
2198 // Setup for long jump in case there is an exception while writing | |
2199 // the snapshot. | |
2200 LongJumpScope jump; | |
2201 if (setjmp(*jump.Set()) == 0) { | |
2202 // Reserve space in the output buffer for a snapshot header. | |
2203 writer.ReserveHeader(); | |
2204 | |
2205 // Write out the version string. | |
2206 writer.WriteVersionAndFeatures(); | |
2207 | |
2208 // Write out the full snapshot. | |
2209 | |
2210 // Write out all the objects in the object store of the isolate which | |
2211 // is the root set for all dart allocated objects at this point. | |
2212 SnapshotWriterVisitor visitor(&writer, false); | |
2213 visitor.VisitPointers(object_store->from(), | |
2214 object_store->to_snapshot(kind_)); | |
2215 | |
2216 // Write out all forwarded objects. | |
2217 writer.WriteForwardedObjects(); | |
2218 | |
2219 writer.FillHeader(writer.kind()); | |
2220 | |
2221 isolate_snapshot_size_ = writer.BytesWritten(); | |
2222 } else { | |
2223 writer.ThrowException(writer.exception_type(), writer.exception_msg()); | |
2224 } | |
2225 } | |
2226 | |
2227 | |
2228 void FullSnapshotWriter::WriteFullSnapshot() { | |
2229 if (vm_isolate_snapshot_buffer() != NULL) { | |
2230 WriteVmIsolateSnapshot(); | |
2231 } | |
2232 WriteIsolateFullSnapshot(); | |
2233 if (Snapshot::IncludesCode(kind_)) { | |
2234 instructions_writer_->Write(); | |
2235 | |
2236 OS::Print("VMIsolate(CodeSize): %" Pd "\n", VmIsolateSnapshotSize()); | |
2237 OS::Print("Isolate(CodeSize): %" Pd "\n", IsolateSnapshotSize()); | |
2238 OS::Print("Instructions(CodeSize): %" Pd "\n", | |
2239 instructions_writer_->binary_size()); | |
2240 intptr_t total = VmIsolateSnapshotSize() + | |
2241 IsolateSnapshotSize() + | |
2242 instructions_writer_->binary_size(); | |
2243 OS::Print("Total(CodeSize): %" Pd "\n", total); | |
2244 } | |
2245 } | |
2246 | |
2247 | |
2248 ForwardList::ForwardList(Thread* thread, intptr_t first_object_id) | 1862 ForwardList::ForwardList(Thread* thread, intptr_t first_object_id) |
2249 : thread_(thread), | 1863 : thread_(thread), |
2250 first_object_id_(first_object_id), | 1864 first_object_id_(first_object_id), |
2251 nodes_(), | 1865 nodes_(), |
2252 first_unprocessed_object_id_(first_object_id) { | 1866 first_unprocessed_object_id_(first_object_id) { |
2253 ASSERT(first_object_id > 0); | 1867 ASSERT(first_object_id > 0); |
2254 } | 1868 } |
2255 | 1869 |
2256 | 1870 |
2257 ForwardList::~ForwardList() { | 1871 ForwardList::~ForwardList() { |
(...skipping 567 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2825 if (setjmp(*jump.Set()) == 0) { | 2439 if (setjmp(*jump.Set()) == 0) { |
2826 NoSafepointScope no_safepoint; | 2440 NoSafepointScope no_safepoint; |
2827 WriteObject(obj.raw()); | 2441 WriteObject(obj.raw()); |
2828 } else { | 2442 } else { |
2829 ThrowException(exception_type(), exception_msg()); | 2443 ThrowException(exception_type(), exception_msg()); |
2830 } | 2444 } |
2831 } | 2445 } |
2832 | 2446 |
2833 | 2447 |
2834 } // namespace dart | 2448 } // namespace dart |
OLD | NEW |