OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 <cstring> | 5 #include <cstring> |
6 | 6 |
7 #include "platform/assert.h" | 7 #include "platform/assert.h" |
8 | 8 |
9 #include "vm/dart_api_impl.h" | 9 #include "vm/dart_api_impl.h" |
10 #include "vm/dart_api_state.h" | 10 #include "vm/dart_api_state.h" |
(...skipping 12 matching lines...) Expand all Loading... |
23 : recorder_(Timeline::recorder()) { | 23 : recorder_(Timeline::recorder()) { |
24 Timeline::recorder_ = new_recorder; | 24 Timeline::recorder_ = new_recorder; |
25 } | 25 } |
26 | 26 |
27 ~TimelineRecorderOverride() { Timeline::recorder_ = recorder_; } | 27 ~TimelineRecorderOverride() { Timeline::recorder_ = recorder_; } |
28 | 28 |
29 private: | 29 private: |
30 TimelineEventRecorder* recorder_; | 30 TimelineEventRecorder* recorder_; |
31 }; | 31 }; |
32 | 32 |
33 | |
34 class TimelineTestHelper : public AllStatic { | 33 class TimelineTestHelper : public AllStatic { |
35 public: | 34 public: |
36 static void SetStream(TimelineEvent* event, TimelineStream* stream) { | 35 static void SetStream(TimelineEvent* event, TimelineStream* stream) { |
37 event->StreamInit(stream); | 36 event->StreamInit(stream); |
38 } | 37 } |
39 | 38 |
40 static void FakeThreadEvent(TimelineEventBlock* block, | 39 static void FakeThreadEvent(TimelineEventBlock* block, |
41 intptr_t ftid, | 40 intptr_t ftid, |
42 const char* label = "fake", | 41 const char* label = "fake", |
43 TimelineStream* stream = NULL) { | 42 TimelineStream* stream = NULL) { |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
92 } | 91 } |
93 | 92 |
94 static void Clear(TimelineEventRecorder* recorder) { | 93 static void Clear(TimelineEventRecorder* recorder) { |
95 ASSERT(recorder != NULL); | 94 ASSERT(recorder != NULL); |
96 recorder->Clear(); | 95 recorder->Clear(); |
97 } | 96 } |
98 | 97 |
99 static void FinishBlock(TimelineEventBlock* block) { block->Finish(); } | 98 static void FinishBlock(TimelineEventBlock* block) { block->Finish(); } |
100 }; | 99 }; |
101 | 100 |
102 | |
103 TEST_CASE(TimelineEventIsValid) { | 101 TEST_CASE(TimelineEventIsValid) { |
104 // Create a test stream. | 102 // Create a test stream. |
105 TimelineStream stream; | 103 TimelineStream stream; |
106 stream.Init("testStream", true); | 104 stream.Init("testStream", true); |
107 | 105 |
108 TimelineEvent event; | 106 TimelineEvent event; |
109 TimelineTestHelper::SetStream(&event, &stream); | 107 TimelineTestHelper::SetStream(&event, &stream); |
110 | 108 |
111 // Starts invalid. | 109 // Starts invalid. |
112 EXPECT(!event.IsValid()); | 110 EXPECT(!event.IsValid()); |
113 | 111 |
114 // Becomes valid. | 112 // Becomes valid. |
115 event.Instant("hello"); | 113 event.Instant("hello"); |
116 EXPECT(event.IsValid()); | 114 EXPECT(event.IsValid()); |
117 | 115 |
118 // Becomes invalid. | 116 // Becomes invalid. |
119 event.Reset(); | 117 event.Reset(); |
120 EXPECT(!event.IsValid()); | 118 EXPECT(!event.IsValid()); |
121 } | 119 } |
122 | 120 |
123 | |
124 TEST_CASE(TimelineEventDuration) { | 121 TEST_CASE(TimelineEventDuration) { |
125 // Create a test stream. | 122 // Create a test stream. |
126 TimelineStream stream; | 123 TimelineStream stream; |
127 stream.Init("testStream", true); | 124 stream.Init("testStream", true); |
128 | 125 |
129 // Create a test event. | 126 // Create a test event. |
130 TimelineEvent event; | 127 TimelineEvent event; |
131 TimelineTestHelper::SetStream(&event, &stream); | 128 TimelineTestHelper::SetStream(&event, &stream); |
132 event.DurationBegin("apple"); | 129 event.DurationBegin("apple"); |
133 // Measure the duration. | 130 // Measure the duration. |
134 int64_t current_duration = event.TimeDuration(); | 131 int64_t current_duration = event.TimeDuration(); |
135 event.DurationEnd(); | 132 event.DurationEnd(); |
136 // Verify that duration is larger. | 133 // Verify that duration is larger. |
137 EXPECT_GE(event.TimeDuration(), current_duration); | 134 EXPECT_GE(event.TimeDuration(), current_duration); |
138 } | 135 } |
139 | 136 |
140 | |
141 TEST_CASE(TimelineEventDurationPrintJSON) { | 137 TEST_CASE(TimelineEventDurationPrintJSON) { |
142 // Create a test stream. | 138 // Create a test stream. |
143 TimelineStream stream; | 139 TimelineStream stream; |
144 stream.Init("testStream", true); | 140 stream.Init("testStream", true); |
145 | 141 |
146 // Create a test event. | 142 // Create a test event. |
147 TimelineEvent event; | 143 TimelineEvent event; |
148 TimelineTestHelper::SetStream(&event, &stream); | 144 TimelineTestHelper::SetStream(&event, &stream); |
149 event.DurationBegin("apple"); | 145 event.DurationBegin("apple"); |
150 { | 146 { |
151 // Test printing to JSON. | 147 // Test printing to JSON. |
152 JSONStream js; | 148 JSONStream js; |
153 event.PrintJSON(&js); | 149 event.PrintJSON(&js); |
154 // Check category | 150 // Check category |
155 EXPECT_SUBSTRING("\"cat\":\"testStream\"", js.ToCString()); | 151 EXPECT_SUBSTRING("\"cat\":\"testStream\"", js.ToCString()); |
156 // Check name. | 152 // Check name. |
157 EXPECT_SUBSTRING("\"name\":\"apple\"", js.ToCString()); | 153 EXPECT_SUBSTRING("\"name\":\"apple\"", js.ToCString()); |
158 // Check phase. | 154 // Check phase. |
159 EXPECT_SUBSTRING("\"ph\":\"X\"", js.ToCString()); | 155 EXPECT_SUBSTRING("\"ph\":\"X\"", js.ToCString()); |
160 // Check that ts key is present. | 156 // Check that ts key is present. |
161 EXPECT_SUBSTRING("\"ts\":", js.ToCString()); | 157 EXPECT_SUBSTRING("\"ts\":", js.ToCString()); |
162 // Check that dur key is present. | 158 // Check that dur key is present. |
163 EXPECT_SUBSTRING("\"dur\":", js.ToCString()); | 159 EXPECT_SUBSTRING("\"dur\":", js.ToCString()); |
164 } | 160 } |
165 event.DurationEnd(); | 161 event.DurationEnd(); |
166 } | 162 } |
167 | 163 |
168 | |
169 TEST_CASE(TimelineEventPrintSystrace) { | 164 TEST_CASE(TimelineEventPrintSystrace) { |
170 const intptr_t kBufferLength = 1024; | 165 const intptr_t kBufferLength = 1024; |
171 char buffer[kBufferLength]; | 166 char buffer[kBufferLength]; |
172 | 167 |
173 // Create a test stream. | 168 // Create a test stream. |
174 TimelineStream stream; | 169 TimelineStream stream; |
175 stream.Init("testStream", true); | 170 stream.Init("testStream", true); |
176 | 171 |
177 // Create a test event. | 172 // Create a test event. |
178 TimelineEvent event; | 173 TimelineEvent event; |
(...skipping 23 matching lines...) Expand all Loading... |
202 EXPECT_SUBSTRING("C|", buffer); | 197 EXPECT_SUBSTRING("C|", buffer); |
203 EXPECT_SUBSTRING("|CTR|4", buffer); | 198 EXPECT_SUBSTRING("|CTR|4", buffer); |
204 | 199 |
205 // Test a duration event. This event kind is not supported so we should | 200 // Test a duration event. This event kind is not supported so we should |
206 // serialize it to an empty string. | 201 // serialize it to an empty string. |
207 event.Duration("DUR", 0, 1, 2, 3); | 202 event.Duration("DUR", 0, 1, 2, 3); |
208 event.PrintSystrace(&buffer[0], kBufferLength); | 203 event.PrintSystrace(&buffer[0], kBufferLength); |
209 EXPECT_STREQ("", buffer); | 204 EXPECT_STREQ("", buffer); |
210 } | 205 } |
211 | 206 |
212 | |
213 TEST_CASE(TimelineEventArguments) { | 207 TEST_CASE(TimelineEventArguments) { |
214 // Create a test stream. | 208 // Create a test stream. |
215 TimelineStream stream; | 209 TimelineStream stream; |
216 stream.Init("testStream", true); | 210 stream.Init("testStream", true); |
217 | 211 |
218 // Create a test event. | 212 // Create a test event. |
219 TimelineEvent event; | 213 TimelineEvent event; |
220 TimelineTestHelper::SetStream(&event, &stream); | 214 TimelineTestHelper::SetStream(&event, &stream); |
221 | 215 |
222 // Allocate room for four arguments. | 216 // Allocate room for four arguments. |
223 event.SetNumArguments(4); | 217 event.SetNumArguments(4); |
224 // Reset. | 218 // Reset. |
225 event.Reset(); | 219 event.Reset(); |
226 | 220 |
227 event.DurationBegin("apple"); | 221 event.DurationBegin("apple"); |
228 event.SetNumArguments(2); | 222 event.SetNumArguments(2); |
229 event.CopyArgument(0, "arg1", "value1"); | 223 event.CopyArgument(0, "arg1", "value1"); |
230 event.CopyArgument(1, "arg2", "value2"); | 224 event.CopyArgument(1, "arg2", "value2"); |
231 event.DurationEnd(); | 225 event.DurationEnd(); |
232 } | 226 } |
233 | 227 |
234 | |
235 TEST_CASE(TimelineEventArgumentsPrintJSON) { | 228 TEST_CASE(TimelineEventArgumentsPrintJSON) { |
236 // Create a test stream. | 229 // Create a test stream. |
237 TimelineStream stream; | 230 TimelineStream stream; |
238 stream.Init("testStream", true); | 231 stream.Init("testStream", true); |
239 | 232 |
240 // Create a test event. | 233 // Create a test event. |
241 TimelineEvent event; | 234 TimelineEvent event; |
242 TimelineTestHelper::SetStream(&event, &stream); | 235 TimelineTestHelper::SetStream(&event, &stream); |
243 | 236 |
244 event.DurationBegin("apple"); | 237 event.DurationBegin("apple"); |
245 event.SetNumArguments(2); | 238 event.SetNumArguments(2); |
246 event.CopyArgument(0, "arg1", "value1"); | 239 event.CopyArgument(0, "arg1", "value1"); |
247 event.CopyArgument(1, "arg2", "value2"); | 240 event.CopyArgument(1, "arg2", "value2"); |
248 event.DurationEnd(); | 241 event.DurationEnd(); |
249 | 242 |
250 { | 243 { |
251 // Test printing to JSON. | 244 // Test printing to JSON. |
252 JSONStream js; | 245 JSONStream js; |
253 event.PrintJSON(&js); | 246 event.PrintJSON(&js); |
254 | 247 |
255 // Check both arguments. | 248 // Check both arguments. |
256 EXPECT_SUBSTRING("\"arg1\":\"value1\"", js.ToCString()); | 249 EXPECT_SUBSTRING("\"arg1\":\"value1\"", js.ToCString()); |
257 EXPECT_SUBSTRING("\"arg2\":\"value2\"", js.ToCString()); | 250 EXPECT_SUBSTRING("\"arg2\":\"value2\"", js.ToCString()); |
258 } | 251 } |
259 } | 252 } |
260 | 253 |
261 | |
262 TEST_CASE(TimelineEventBufferPrintJSON) { | 254 TEST_CASE(TimelineEventBufferPrintJSON) { |
263 TimelineEventRecorder* recorder = Timeline::recorder(); | 255 TimelineEventRecorder* recorder = Timeline::recorder(); |
264 JSONStream js; | 256 JSONStream js; |
265 TimelineEventFilter filter; | 257 TimelineEventFilter filter; |
266 recorder->PrintJSON(&js, &filter); | 258 recorder->PrintJSON(&js, &filter); |
267 // Check the type. This test will fail if we ever make Timeline public. | 259 // Check the type. This test will fail if we ever make Timeline public. |
268 EXPECT_SUBSTRING("\"type\":\"_Timeline\"", js.ToCString()); | 260 EXPECT_SUBSTRING("\"type\":\"_Timeline\"", js.ToCString()); |
269 // Check that there is a traceEvents array. | 261 // Check that there is a traceEvents array. |
270 EXPECT_SUBSTRING("\"traceEvents\":[", js.ToCString()); | 262 EXPECT_SUBSTRING("\"traceEvents\":[", js.ToCString()); |
271 } | 263 } |
272 | 264 |
273 | |
274 // Count the number of each event type seen. | 265 // Count the number of each event type seen. |
275 class EventCounterRecorder : public TimelineEventCallbackRecorder { | 266 class EventCounterRecorder : public TimelineEventCallbackRecorder { |
276 public: | 267 public: |
277 EventCounterRecorder() { | 268 EventCounterRecorder() { |
278 for (intptr_t i = 0; i < TimelineEvent::kNumEventTypes; i++) { | 269 for (intptr_t i = 0; i < TimelineEvent::kNumEventTypes; i++) { |
279 counts_[i] = 0; | 270 counts_[i] = 0; |
280 } | 271 } |
281 } | 272 } |
282 | 273 |
283 void OnEvent(TimelineEvent* event) { counts_[event->event_type()]++; } | 274 void OnEvent(TimelineEvent* event) { counts_[event->event_type()]++; } |
284 | 275 |
285 intptr_t CountFor(TimelineEvent::EventType type) { return counts_[type]; } | 276 intptr_t CountFor(TimelineEvent::EventType type) { return counts_[type]; } |
286 | 277 |
287 private: | 278 private: |
288 intptr_t counts_[TimelineEvent::kNumEventTypes]; | 279 intptr_t counts_[TimelineEvent::kNumEventTypes]; |
289 }; | 280 }; |
290 | 281 |
291 | |
292 TEST_CASE(TimelineEventCallbackRecorderBasic) { | 282 TEST_CASE(TimelineEventCallbackRecorderBasic) { |
293 EventCounterRecorder* recorder = new EventCounterRecorder(); | 283 EventCounterRecorder* recorder = new EventCounterRecorder(); |
294 TimelineRecorderOverride override(recorder); | 284 TimelineRecorderOverride override(recorder); |
295 | 285 |
296 // Initial counts are all zero. | 286 // Initial counts are all zero. |
297 for (intptr_t i = TimelineEvent::kNone + 1; i < TimelineEvent::kNumEventTypes; | 287 for (intptr_t i = TimelineEvent::kNone + 1; i < TimelineEvent::kNumEventTypes; |
298 i++) { | 288 i++) { |
299 EXPECT_EQ(0, recorder->CountFor(static_cast<TimelineEvent::EventType>(i))); | 289 EXPECT_EQ(0, recorder->CountFor(static_cast<TimelineEvent::EventType>(i))); |
300 } | 290 } |
301 | 291 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
340 event = stream.StartEvent(); | 330 event = stream.StartEvent(); |
341 EXPECT_EQ(0, recorder->CountFor(TimelineEvent::kAsyncEnd)); | 331 EXPECT_EQ(0, recorder->CountFor(TimelineEvent::kAsyncEnd)); |
342 event->AsyncEnd("asyncEndCabbage", async_id); | 332 event->AsyncEnd("asyncEndCabbage", async_id); |
343 EXPECT_EQ(0, recorder->CountFor(TimelineEvent::kAsyncEnd)); | 333 EXPECT_EQ(0, recorder->CountFor(TimelineEvent::kAsyncEnd)); |
344 event->Complete(); | 334 event->Complete(); |
345 EXPECT_EQ(1, recorder->CountFor(TimelineEvent::kAsyncEnd)); | 335 EXPECT_EQ(1, recorder->CountFor(TimelineEvent::kAsyncEnd)); |
346 | 336 |
347 delete recorder; | 337 delete recorder; |
348 } | 338 } |
349 | 339 |
350 | |
351 static bool LabelMatch(TimelineEvent* event, const char* label) { | 340 static bool LabelMatch(TimelineEvent* event, const char* label) { |
352 ASSERT(event != NULL); | 341 ASSERT(event != NULL); |
353 return strcmp(event->label(), label) == 0; | 342 return strcmp(event->label(), label) == 0; |
354 } | 343 } |
355 | 344 |
356 | |
357 TEST_CASE(TimelineAnalysis_ThreadBlockCount) { | 345 TEST_CASE(TimelineAnalysis_ThreadBlockCount) { |
358 TimelineEventEndlessRecorder* recorder = new TimelineEventEndlessRecorder(); | 346 TimelineEventEndlessRecorder* recorder = new TimelineEventEndlessRecorder(); |
359 ASSERT(recorder != NULL); | 347 ASSERT(recorder != NULL); |
360 // Blocks owned by thread "1". | 348 // Blocks owned by thread "1". |
361 TimelineEventBlock* block_1_0 = recorder->GetNewBlock(); | 349 TimelineEventBlock* block_1_0 = recorder->GetNewBlock(); |
362 TimelineTestHelper::SetBlockThread(block_1_0, 1); | 350 TimelineTestHelper::SetBlockThread(block_1_0, 1); |
363 TimelineEventBlock* block_1_1 = recorder->GetNewBlock(); | 351 TimelineEventBlock* block_1_1 = recorder->GetNewBlock(); |
364 TimelineTestHelper::SetBlockThread(block_1_1, 1); | 352 TimelineTestHelper::SetBlockThread(block_1_1, 1); |
365 TimelineEventBlock* block_1_2 = recorder->GetNewBlock(); | 353 TimelineEventBlock* block_1_2 = recorder->GetNewBlock(); |
366 TimelineTestHelper::SetBlockThread(block_1_2, 1); | 354 TimelineTestHelper::SetBlockThread(block_1_2, 1); |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
466 EXPECT(LabelMatch(it.Next(), "E")); | 454 EXPECT(LabelMatch(it.Next(), "E")); |
467 EXPECT(it.HasNext()); | 455 EXPECT(it.HasNext()); |
468 EXPECT(LabelMatch(it.Next(), "F")); | 456 EXPECT(LabelMatch(it.Next(), "F")); |
469 EXPECT(!it.HasNext()); | 457 EXPECT(!it.HasNext()); |
470 } | 458 } |
471 | 459 |
472 TimelineTestHelper::Clear(recorder); | 460 TimelineTestHelper::Clear(recorder); |
473 delete recorder; | 461 delete recorder; |
474 } | 462 } |
475 | 463 |
476 | |
477 TEST_CASE(TimelineRingRecorderJSONOrder) { | 464 TEST_CASE(TimelineRingRecorderJSONOrder) { |
478 TimelineStream stream; | 465 TimelineStream stream; |
479 stream.Init("testStream", true); | 466 stream.Init("testStream", true); |
480 | 467 |
481 TimelineEventRingRecorder* recorder = | 468 TimelineEventRingRecorder* recorder = |
482 new TimelineEventRingRecorder(TimelineEventBlock::kBlockSize * 2); | 469 new TimelineEventRingRecorder(TimelineEventBlock::kBlockSize * 2); |
483 | 470 |
484 TimelineEventBlock* block_0 = recorder->GetNewBlock(); | 471 TimelineEventBlock* block_0 = recorder->GetNewBlock(); |
485 EXPECT(block_0 != NULL); | 472 EXPECT(block_0 != NULL); |
486 TimelineEventBlock* block_1 = recorder->GetNewBlock(); | 473 TimelineEventBlock* block_1 = recorder->GetNewBlock(); |
(...skipping 18 matching lines...) Expand all Loading... |
505 // Verify that "Alpha" comes before "Beta" even though "Beta" is in the first | 492 // Verify that "Alpha" comes before "Beta" even though "Beta" is in the first |
506 // block. | 493 // block. |
507 const char* alpha = strstr(js.ToCString(), "Alpha"); | 494 const char* alpha = strstr(js.ToCString(), "Alpha"); |
508 const char* beta = strstr(js.ToCString(), "Beta"); | 495 const char* beta = strstr(js.ToCString(), "Beta"); |
509 EXPECT(alpha < beta); | 496 EXPECT(alpha < beta); |
510 | 497 |
511 TimelineTestHelper::Clear(recorder); | 498 TimelineTestHelper::Clear(recorder); |
512 delete recorder; | 499 delete recorder; |
513 } | 500 } |
514 | 501 |
515 | |
516 TEST_CASE(TimelinePauses_Basic) { | 502 TEST_CASE(TimelinePauses_Basic) { |
517 TimelineEventEndlessRecorder* recorder = new TimelineEventEndlessRecorder(); | 503 TimelineEventEndlessRecorder* recorder = new TimelineEventEndlessRecorder(); |
518 ASSERT(recorder != NULL); | 504 ASSERT(recorder != NULL); |
519 Zone* zone = thread->zone(); | 505 Zone* zone = thread->zone(); |
520 Isolate* isolate = thread->isolate(); | 506 Isolate* isolate = thread->isolate(); |
521 OSThread* os_thread = thread->os_thread(); | 507 OSThread* os_thread = thread->os_thread(); |
522 ASSERT(os_thread != NULL); | 508 ASSERT(os_thread != NULL); |
523 ThreadId tid = os_thread->trace_id(); | 509 ThreadId tid = os_thread->trace_id(); |
524 | 510 |
525 // Test case. | 511 // Test case. |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
679 EXPECT_EQ(10, pauses.InclusiveTime("a")); | 665 EXPECT_EQ(10, pauses.InclusiveTime("a")); |
680 EXPECT_EQ(10, pauses.ExclusiveTime("a")); | 666 EXPECT_EQ(10, pauses.ExclusiveTime("a")); |
681 EXPECT_EQ(10, pauses.MaxInclusiveTime("a")); | 667 EXPECT_EQ(10, pauses.MaxInclusiveTime("a")); |
682 EXPECT_EQ(8, pauses.MaxExclusiveTime("a")); | 668 EXPECT_EQ(8, pauses.MaxExclusiveTime("a")); |
683 } | 669 } |
684 TimelineTestHelper::Clear(recorder); | 670 TimelineTestHelper::Clear(recorder); |
685 | 671 |
686 delete recorder; | 672 delete recorder; |
687 } | 673 } |
688 | 674 |
689 | |
690 TEST_CASE(TimelinePauses_BeginEnd) { | 675 TEST_CASE(TimelinePauses_BeginEnd) { |
691 TimelineEventEndlessRecorder* recorder = new TimelineEventEndlessRecorder(); | 676 TimelineEventEndlessRecorder* recorder = new TimelineEventEndlessRecorder(); |
692 ASSERT(recorder != NULL); | 677 ASSERT(recorder != NULL); |
693 Zone* zone = thread->zone(); | 678 Zone* zone = thread->zone(); |
694 Isolate* isolate = thread->isolate(); | 679 Isolate* isolate = thread->isolate(); |
695 OSThread* os_thread = thread->os_thread(); | 680 OSThread* os_thread = thread->os_thread(); |
696 ASSERT(os_thread != NULL); | 681 ASSERT(os_thread != NULL); |
697 ThreadId tid = os_thread->trace_id(); | 682 ThreadId tid = os_thread->trace_id(); |
698 | 683 |
699 // Test case. | 684 // Test case. |
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
894 EXPECT(pauses.has_error()); | 879 EXPECT(pauses.has_error()); |
895 } | 880 } |
896 TimelineTestHelper::Clear(recorder); | 881 TimelineTestHelper::Clear(recorder); |
897 | 882 |
898 delete recorder; | 883 delete recorder; |
899 } | 884 } |
900 | 885 |
901 #endif // !PRODUCT | 886 #endif // !PRODUCT |
902 | 887 |
903 } // namespace dart | 888 } // namespace dart |
OLD | NEW |