OLD | NEW |
---|---|
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 | 2 |
3 // Check that we can traverse very deep stacks of ConsStrings using | 3 // Check that we can traverse very deep stacks of ConsStrings using |
4 // StringInputBuffer. Check that Get(int) works on very deep stacks | 4 // StringInputBuffer. Check that Get(int) works on very deep stacks |
5 // of ConsStrings. These operations may not be very fast, but they | 5 // of ConsStrings. These operations may not be very fast, but they |
6 // should be possible without getting errors due to too deep recursion. | 6 // should be possible without getting errors due to too deep recursion. |
7 | 7 |
8 #include <stdlib.h> | 8 #include <stdlib.h> |
9 | 9 |
10 #include "v8.h" | 10 #include "v8.h" |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
78 v8::HandleScope scope; | 78 v8::HandleScope scope; |
79 const char* extensions[] = { "v8/print" }; | 79 const char* extensions[] = { "v8/print" }; |
80 v8::ExtensionConfiguration config(1, extensions); | 80 v8::ExtensionConfiguration config(1, extensions); |
81 env = v8::Context::New(&config); | 81 env = v8::Context::New(&config); |
82 } | 82 } |
83 v8::HandleScope scope; | 83 v8::HandleScope scope; |
84 env->Enter(); | 84 env->Enter(); |
85 } | 85 } |
86 | 86 |
87 | 87 |
88 static const int NUMBER_OF_BUILDING_BLOCKS = 256; | |
89 static const int DEEP_DEPTH = 8 * 1024; | 88 static const int DEEP_DEPTH = 8 * 1024; |
90 static const int SUPER_DEEP_DEPTH = 80 * 1024; | 89 static const int SUPER_DEEP_DEPTH = 80 * 1024; |
91 | 90 |
92 | 91 |
93 class Resource: public v8::String::ExternalStringResource, | 92 class Resource: public v8::String::ExternalStringResource, |
94 public ZoneObject { | 93 public ZoneObject { |
95 public: | 94 public: |
96 explicit Resource(Vector<const uc16> string): data_(string.start()) { | 95 explicit Resource(Vector<const uc16> string): data_(string.start()) { |
97 length_ = string.length(); | 96 length_ = string.length(); |
98 } | 97 } |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
184 Resource* resource = new(zone) Resource(Vector<const uc16>(buf, len)); | 183 Resource* resource = new(zone) Resource(Vector<const uc16>(buf, len)); |
185 building_blocks[i] = FACTORY->NewExternalStringFromTwoByte(resource); | 184 building_blocks[i] = FACTORY->NewExternalStringFromTwoByte(resource); |
186 for (int j = 0; j < len; j++) { | 185 for (int j = 0; j < len; j++) { |
187 CHECK_EQ(buf[j], building_blocks[i]->Get(j)); | 186 CHECK_EQ(buf[j], building_blocks[i]->Get(j)); |
188 } | 187 } |
189 break; | 188 break; |
190 } | 189 } |
191 case 3: { | 190 case 3: { |
192 char* buf = zone->NewArray<char>(len); | 191 char* buf = zone->NewArray<char>(len); |
193 for (int j = 0; j < len; j++) { | 192 for (int j = 0; j < len; j++) { |
194 buf[j] = rng->next(128); | 193 buf[j] = rng->next(0x80); |
195 } | 194 } |
196 AsciiResource* resource = | 195 AsciiResource* resource = |
197 new(zone) AsciiResource(Vector<const char>(buf, len)); | 196 new(zone) AsciiResource(Vector<const char>(buf, len)); |
198 building_blocks[i] = FACTORY->NewExternalStringFromAscii(resource); | 197 building_blocks[i] = FACTORY->NewExternalStringFromAscii(resource); |
199 for (int j = 0; j < len; j++) { | 198 for (int j = 0; j < len; j++) { |
200 CHECK_EQ(buf[j], building_blocks[i]->Get(j)); | 199 CHECK_EQ(buf[j], building_blocks[i]->Get(j)); |
201 } | 200 } |
202 break; | 201 break; |
203 } | 202 } |
204 } | 203 } |
205 for (int j = slice_depth; j > 0; j--) { | 204 for (int j = slice_depth; j > 0; j--) { |
206 building_blocks[i] = FACTORY->NewSubString( | 205 building_blocks[i] = FACTORY->NewSubString( |
207 building_blocks[i], | 206 building_blocks[i], |
208 slice_head_chars, | 207 slice_head_chars, |
209 building_blocks[i]->length() - slice_tail_chars); | 208 building_blocks[i]->length() - slice_tail_chars); |
210 } | 209 } |
211 CHECK(len == building_blocks[i]->length() + slice_length); | 210 CHECK(len == building_blocks[i]->length() + slice_length); |
212 } | 211 } |
213 } | 212 } |
214 | 213 |
215 | 214 |
216 static Handle<String> ConstructLeft( | |
217 Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS], | |
218 int depth) { | |
219 Handle<String> answer = FACTORY->NewStringFromAscii(CStrVector("")); | |
220 for (int i = 0; i < depth; i++) { | |
221 answer = FACTORY->NewConsString( | |
222 answer, | |
223 building_blocks[i % NUMBER_OF_BUILDING_BLOCKS]); | |
224 } | |
225 return answer; | |
226 } | |
227 | |
228 | |
229 static Handle<String> ConstructRight( | |
230 Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS], | |
231 int depth) { | |
232 Handle<String> answer = FACTORY->NewStringFromAscii(CStrVector("")); | |
233 for (int i = depth - 1; i >= 0; i--) { | |
234 answer = FACTORY->NewConsString( | |
235 building_blocks[i % NUMBER_OF_BUILDING_BLOCKS], | |
236 answer); | |
237 } | |
238 return answer; | |
239 } | |
240 | |
241 | |
242 static Handle<String> ConstructBalancedHelper( | |
243 Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS], | |
244 int from, | |
245 int to) { | |
246 CHECK(to > from); | |
247 if (to - from == 1) { | |
248 return building_blocks[from % NUMBER_OF_BUILDING_BLOCKS]; | |
249 } | |
250 if (to - from == 2) { | |
251 return FACTORY->NewConsString( | |
252 building_blocks[from % NUMBER_OF_BUILDING_BLOCKS], | |
253 building_blocks[(from+1) % NUMBER_OF_BUILDING_BLOCKS]); | |
254 } | |
255 Handle<String> part1 = | |
256 ConstructBalancedHelper(building_blocks, from, from + ((to - from) / 2)); | |
257 Handle<String> part2 = | |
258 ConstructBalancedHelper(building_blocks, from + ((to - from) / 2), to); | |
259 return FACTORY->NewConsString(part1, part2); | |
260 } | |
261 | |
262 | |
263 static Handle<String> ConstructBalanced( | |
264 Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS]) { | |
265 return ConstructBalancedHelper(building_blocks, 0, DEEP_DEPTH); | |
266 } | |
267 | |
268 | |
269 static StringInputBuffer buffer; | |
270 static ConsStringIteratorOp cons_string_iterator_op_1; | |
271 static ConsStringIteratorOp cons_string_iterator_op_2; | |
272 | |
273 static void Traverse(Handle<String> s1, Handle<String> s2) { | |
274 int i = 0; | |
275 buffer.Reset(*s1); | |
276 StringCharacterStream character_stream_1(*s1, 0, &cons_string_iterator_op_1); | |
277 StringCharacterStream character_stream_2(*s2, 0, &cons_string_iterator_op_2); | |
278 StringInputBuffer buffer2(*s2); | |
279 while (buffer.has_more()) { | |
280 CHECK(buffer2.has_more()); | |
281 CHECK(character_stream_1.HasMore()); | |
282 CHECK(character_stream_2.HasMore()); | |
283 uint16_t c = buffer.GetNext(); | |
284 CHECK_EQ(c, buffer2.GetNext()); | |
285 CHECK_EQ(c, character_stream_1.GetNext()); | |
286 CHECK_EQ(c, character_stream_2.GetNext()); | |
287 i++; | |
288 } | |
289 CHECK(!character_stream_1.HasMore()); | |
290 CHECK(!character_stream_2.HasMore()); | |
291 CHECK_EQ(s1->length(), i); | |
292 CHECK_EQ(s2->length(), i); | |
293 } | |
294 | |
295 | |
296 static void TraverseFirst(Handle<String> s1, Handle<String> s2, int chars) { | |
297 int i = 0; | |
298 buffer.Reset(*s1); | |
299 StringInputBuffer buffer2(*s2); | |
300 StringCharacterStream character_stream_1(*s1, 0, &cons_string_iterator_op_1); | |
301 StringCharacterStream character_stream_2(*s2, 0, &cons_string_iterator_op_2); | |
302 while (buffer.has_more() && i < chars) { | |
303 CHECK(buffer2.has_more()); | |
304 CHECK(character_stream_1.HasMore()); | |
305 CHECK(character_stream_2.HasMore()); | |
306 uint16_t c = buffer.GetNext(); | |
307 CHECK_EQ(c, buffer2.GetNext()); | |
308 CHECK_EQ(c, character_stream_1.GetNext()); | |
309 CHECK_EQ(c, character_stream_2.GetNext()); | |
310 i++; | |
311 } | |
312 s1->Get(s1->length() - 1); | |
313 s2->Get(s2->length() - 1); | |
314 } | |
315 | |
316 | |
317 TEST(Traverse) { | |
318 printf("TestTraverse\n"); | |
319 InitializeVM(); | |
320 v8::HandleScope scope; | |
321 Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS]; | |
322 ZoneScope zone(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); | |
323 RandomNumberGenerator rng; | |
324 rng.init(); | |
325 InitializeBuildingBlocks( | |
326 building_blocks, NUMBER_OF_BUILDING_BLOCKS, false, &rng); | |
327 Handle<String> flat = ConstructBalanced(building_blocks); | |
328 FlattenString(flat); | |
329 Handle<String> left_asymmetric = ConstructLeft(building_blocks, DEEP_DEPTH); | |
330 Handle<String> right_asymmetric = ConstructRight(building_blocks, DEEP_DEPTH); | |
331 Handle<String> symmetric = ConstructBalanced(building_blocks); | |
332 printf("1\n"); | |
333 Traverse(flat, symmetric); | |
334 printf("2\n"); | |
335 Traverse(flat, left_asymmetric); | |
336 printf("3\n"); | |
337 Traverse(flat, right_asymmetric); | |
338 printf("4\n"); | |
339 Handle<String> left_deep_asymmetric = | |
340 ConstructLeft(building_blocks, SUPER_DEEP_DEPTH); | |
341 Handle<String> right_deep_asymmetric = | |
342 ConstructRight(building_blocks, SUPER_DEEP_DEPTH); | |
343 printf("5\n"); | |
344 TraverseFirst(left_asymmetric, left_deep_asymmetric, 1050); | |
345 printf("6\n"); | |
346 TraverseFirst(left_asymmetric, right_deep_asymmetric, 65536); | |
347 printf("7\n"); | |
348 FlattenString(left_asymmetric); | |
349 printf("10\n"); | |
350 Traverse(flat, left_asymmetric); | |
351 printf("11\n"); | |
352 FlattenString(right_asymmetric); | |
353 printf("12\n"); | |
354 Traverse(flat, right_asymmetric); | |
355 printf("14\n"); | |
356 FlattenString(symmetric); | |
357 printf("15\n"); | |
358 Traverse(flat, symmetric); | |
359 printf("16\n"); | |
360 FlattenString(left_deep_asymmetric); | |
361 printf("18\n"); | |
362 } | |
363 | |
364 | |
365 class ConsStringStats { | 215 class ConsStringStats { |
366 public: | 216 public: |
367 ConsStringStats() { | 217 ConsStringStats() { |
368 Reset(); | 218 Reset(); |
369 } | 219 } |
370 void Reset(); | 220 void Reset(); |
371 void VerifyEqual(const ConsStringStats& that) const; | 221 void VerifyEqual(const ConsStringStats& that) const; |
372 unsigned leaves_; | 222 unsigned leaves_; |
373 unsigned empty_leaves_; | 223 unsigned empty_leaves_; |
374 unsigned chars_; | 224 unsigned chars_; |
(...skipping 17 matching lines...) Expand all Loading... | |
392 CHECK(this->leaves_ == that.leaves_); | 242 CHECK(this->leaves_ == that.leaves_); |
393 CHECK(this->empty_leaves_ == that.empty_leaves_); | 243 CHECK(this->empty_leaves_ == that.empty_leaves_); |
394 CHECK(this->chars_ == that.chars_); | 244 CHECK(this->chars_ == that.chars_); |
395 CHECK(this->left_traversals_ == that.left_traversals_); | 245 CHECK(this->left_traversals_ == that.left_traversals_); |
396 CHECK(this->right_traversals_ == that.right_traversals_); | 246 CHECK(this->right_traversals_ == that.right_traversals_); |
397 } | 247 } |
398 | 248 |
399 | 249 |
400 class ConsStringGenerationData { | 250 class ConsStringGenerationData { |
401 public: | 251 public: |
402 ConsStringGenerationData(); | 252 static const int kNumberOfBuildingBlocks = 256; |
253 explicit ConsStringGenerationData(bool long_blocks); | |
403 void Reset(); | 254 void Reset(); |
255 inline Handle<String> block(int offset); | |
256 inline Handle<String> block(uint32_t offset); | |
404 // Input variables. | 257 // Input variables. |
405 double early_termination_threshold_; | 258 double early_termination_threshold_; |
406 double leftness_; | 259 double leftness_; |
407 double rightness_; | 260 double rightness_; |
408 double empty_leaf_threshold_; | 261 double empty_leaf_threshold_; |
409 unsigned max_leaves_; | 262 unsigned max_leaves_; |
410 // Cached data. | 263 // Cached data. |
411 Handle<String> building_blocks_[NUMBER_OF_BUILDING_BLOCKS]; | 264 Handle<String> building_blocks_[kNumberOfBuildingBlocks]; |
412 String* empty_string_; | 265 String* empty_string_; |
413 RandomNumberGenerator rng_; | 266 RandomNumberGenerator rng_; |
414 // Stats. | 267 // Stats. |
415 ConsStringStats stats_; | 268 ConsStringStats stats_; |
416 unsigned early_terminations_; | 269 unsigned early_terminations_; |
417 private: | 270 private: |
418 DISALLOW_COPY_AND_ASSIGN(ConsStringGenerationData); | 271 DISALLOW_COPY_AND_ASSIGN(ConsStringGenerationData); |
419 }; | 272 }; |
420 | 273 |
421 | 274 |
422 ConsStringGenerationData::ConsStringGenerationData() { | 275 ConsStringGenerationData::ConsStringGenerationData(bool long_blocks) { |
423 rng_.init(); | 276 rng_.init(); |
424 InitializeBuildingBlocks( | 277 InitializeBuildingBlocks( |
425 building_blocks_, NUMBER_OF_BUILDING_BLOCKS, true, &rng_); | 278 building_blocks_, kNumberOfBuildingBlocks, long_blocks, &rng_); |
426 empty_string_ = Isolate::Current()->heap()->empty_string(); | 279 empty_string_ = Isolate::Current()->heap()->empty_string(); |
427 Reset(); | 280 Reset(); |
428 } | 281 } |
429 | 282 |
430 | 283 |
284 Handle<String> ConsStringGenerationData::block(uint32_t offset) { | |
285 return building_blocks_[offset % kNumberOfBuildingBlocks ]; | |
286 } | |
287 | |
288 | |
289 Handle<String> ConsStringGenerationData::block(int offset) { | |
290 CHECK_GE(offset, 0); | |
291 return building_blocks_[offset % kNumberOfBuildingBlocks]; | |
292 } | |
293 | |
294 | |
431 void ConsStringGenerationData::Reset() { | 295 void ConsStringGenerationData::Reset() { |
432 early_termination_threshold_ = 0.01; | 296 early_termination_threshold_ = 0.01; |
433 leftness_ = 0.75; | 297 leftness_ = 0.75; |
434 rightness_ = 0.75; | 298 rightness_ = 0.75; |
435 empty_leaf_threshold_ = 0.02; | 299 empty_leaf_threshold_ = 0.02; |
436 max_leaves_ = 1000; | 300 max_leaves_ = 1000; |
437 stats_.Reset(); | 301 stats_.Reset(); |
438 early_terminations_ = 0; | 302 early_terminations_ = 0; |
303 rng_.init(); | |
439 } | 304 } |
440 | 305 |
441 | 306 |
442 void VerifyConsString(ConsString* cons_string, ConsStringStats* stats) { | 307 void AccumulateStats(ConsString* cons_string, ConsStringStats* stats) { |
443 int left_length = cons_string->first()->length(); | 308 int left_length = cons_string->first()->length(); |
444 int right_length = cons_string->second()->length(); | 309 int right_length = cons_string->second()->length(); |
445 CHECK(cons_string->length() == left_length + right_length); | 310 CHECK(cons_string->length() == left_length + right_length); |
446 // Check left side. | 311 // Check left side. |
447 if (cons_string->first()->IsConsString()) { | 312 bool left_is_cons = cons_string->first()->IsConsString(); |
313 if (left_is_cons) { | |
448 stats->left_traversals_++; | 314 stats->left_traversals_++; |
449 VerifyConsString(ConsString::cast(cons_string->first()), stats); | 315 AccumulateStats(ConsString::cast(cons_string->first()), stats); |
450 } else { | 316 } else { |
451 CHECK_NE(left_length, 0); | 317 CHECK_NE(left_length, 0); |
452 stats->leaves_++; | 318 stats->leaves_++; |
453 stats->chars_ += left_length; | 319 stats->chars_ += left_length; |
454 } | 320 } |
455 // Check right side. | 321 // Check right side. |
456 if (cons_string->second()->IsConsString()) { | 322 if (cons_string->second()->IsConsString()) { |
457 stats->right_traversals_++; | 323 stats->right_traversals_++; |
458 VerifyConsString(ConsString::cast(cons_string->second()), stats); | 324 AccumulateStats(ConsString::cast(cons_string->second()), stats); |
459 } else { | 325 } else { |
460 if (right_length == 0) stats->empty_leaves_++; | 326 if (right_length == 0) { |
327 stats->empty_leaves_++; | |
328 CHECK(!left_is_cons); | |
329 } | |
461 stats->leaves_++; | 330 stats->leaves_++; |
462 stats->chars_ += right_length; | 331 stats->chars_ += right_length; |
463 } | 332 } |
464 } | 333 } |
465 | 334 |
466 | 335 |
467 void VerifyConsStringWithOperator( | 336 void AccumulateStats(Handle<String> cons_string, ConsStringStats* stats) { |
337 AssertNoAllocation no_alloc; | |
338 if (cons_string->IsConsString()) { | |
339 return AccumulateStats(ConsString::cast(*cons_string), stats); | |
340 } | |
341 // This string got flattened by gc. | |
342 stats->chars_ += cons_string->length(); | |
343 } | |
344 | |
345 | |
346 void AccumulateStatsWithOperator( | |
468 ConsString* cons_string, ConsStringStats* stats) { | 347 ConsString* cons_string, ConsStringStats* stats) { |
469 // Init op. | 348 // Init op. |
470 ConsStringIteratorOp op; | 349 ConsStringIteratorOp op; |
471 op.Reset(); | 350 op.Reset(); |
472 // Use response for initial search and on blown stack. | 351 // Use response for initial search and on blown stack. |
473 ConsStringIteratorOp::ContinueResponse response; | 352 ConsStringIteratorOp::ContinueResponse response; |
474 response.string_ = cons_string; | 353 response.string_ = cons_string; |
475 response.offset_ = 0; | 354 response.offset_ = 0; |
476 response.type_ = cons_string->map()->instance_type(); | 355 response.type_ = cons_string->map()->instance_type(); |
477 response.length_ = (uint32_t) cons_string->length(); | 356 response.length_ = (uint32_t) cons_string->length(); |
(...skipping 21 matching lines...) Expand all Loading... | |
499 }; | 378 }; |
500 } | 379 } |
501 | 380 |
502 | 381 |
503 void VerifyConsString(Handle<String> root, ConsStringGenerationData* data) { | 382 void VerifyConsString(Handle<String> root, ConsStringGenerationData* data) { |
504 // Verify basic data. | 383 // Verify basic data. |
505 CHECK(root->IsConsString()); | 384 CHECK(root->IsConsString()); |
506 CHECK((unsigned)root->length() == data->stats_.chars_); | 385 CHECK((unsigned)root->length() == data->stats_.chars_); |
507 // Recursive verify. | 386 // Recursive verify. |
508 ConsStringStats stats; | 387 ConsStringStats stats; |
509 VerifyConsString(ConsString::cast(*root), &stats); | 388 AccumulateStats(ConsString::cast(*root), &stats); |
510 stats.VerifyEqual(data->stats_); | 389 stats.VerifyEqual(data->stats_); |
511 // Iteratively verify. | 390 // Iteratively verify. |
512 stats.Reset(); | 391 stats.Reset(); |
513 VerifyConsStringWithOperator(ConsString::cast(*root), &stats); | 392 AccumulateStatsWithOperator(ConsString::cast(*root), &stats); |
514 // Don't see these. Must copy over. | 393 // Don't see these. Must copy over. |
515 stats.empty_leaves_ = data->stats_.empty_leaves_; | 394 stats.empty_leaves_ = data->stats_.empty_leaves_; |
516 stats.left_traversals_ = data->stats_.left_traversals_; | 395 stats.left_traversals_ = data->stats_.left_traversals_; |
517 stats.right_traversals_ = data->stats_.right_traversals_; | 396 stats.right_traversals_ = data->stats_.right_traversals_; |
518 // Adjust total leaves to compensate. | 397 // Adjust total leaves to compensate. |
519 stats.leaves_ += stats.empty_leaves_; | 398 stats.leaves_ += stats.empty_leaves_; |
520 stats.VerifyEqual(data->stats_); | 399 stats.VerifyEqual(data->stats_); |
521 } | 400 } |
522 | 401 |
523 | 402 |
(...skipping 11 matching lines...) Expand all Loading... | |
535 // Cap for max leaves. | 414 // Cap for max leaves. |
536 terminate |= data->stats_.leaves_ >= data->max_leaves_; | 415 terminate |= data->stats_.leaves_ >= data->max_leaves_; |
537 // Roll the dice. | 416 // Roll the dice. |
538 terminate |= terminate_early; | 417 terminate |= terminate_early; |
539 // Compute termination characteristics for each side. | 418 // Compute termination characteristics for each side. |
540 bool terminate_left = terminate || !data->rng_.next(data->leftness_); | 419 bool terminate_left = terminate || !data->rng_.next(data->leftness_); |
541 bool terminate_right = terminate || !data->rng_.next(data->rightness_); | 420 bool terminate_right = terminate || !data->rng_.next(data->rightness_); |
542 // Generate left string. | 421 // Generate left string. |
543 Handle<String> left; | 422 Handle<String> left; |
544 if (terminate_left) { | 423 if (terminate_left) { |
545 left = data->building_blocks_[data->rng_.next(NUMBER_OF_BUILDING_BLOCKS)]; | 424 left = data->block(data->rng_.next()); |
546 data->stats_.leaves_++; | 425 data->stats_.leaves_++; |
547 data->stats_.chars_ += left->length(); | 426 data->stats_.chars_ += left->length(); |
548 } else { | 427 } else { |
549 left = ConstructRandomString(data, max_recursion - 1); | |
550 data->stats_.left_traversals_++; | 428 data->stats_.left_traversals_++; |
551 } | 429 } |
552 // Generate right string. | 430 // Generate right string. |
553 Handle<String> right; | 431 Handle<String> right; |
554 if (terminate_right) { | 432 if (terminate_right) { |
555 right = data->building_blocks_[data->rng_.next(NUMBER_OF_BUILDING_BLOCKS)]; | 433 right = data->block(data->rng_.next()); |
556 data->stats_.leaves_++; | 434 data->stats_.leaves_++; |
557 data->stats_.chars_ += right->length(); | 435 data->stats_.chars_ += right->length(); |
558 } else { | 436 } else { |
437 data->stats_.right_traversals_++; | |
438 } | |
439 // Generate the necessary sub-nodes recursively. | |
440 if (!terminate_right) { | |
441 // Need to balance generation fairly. | |
442 if (!terminate_left && data->rng_.next(0.5)) { | |
443 left = ConstructRandomString(data, max_recursion - 1); | |
444 } | |
559 right = ConstructRandomString(data, max_recursion - 1); | 445 right = ConstructRandomString(data, max_recursion - 1); |
560 data->stats_.right_traversals_++; | 446 } |
447 if (!terminate_left && left.is_null()) { | |
448 left = ConstructRandomString(data, max_recursion - 1); | |
561 } | 449 } |
562 // Build the cons string. | 450 // Build the cons string. |
563 Handle<String> root = FACTORY->NewConsString(left, right); | 451 Handle<String> root = FACTORY->NewConsString(left, right); |
564 CHECK(root->IsConsString() && !root->IsFlat()); | 452 CHECK(root->IsConsString() && !root->IsFlat()); |
565 // Special work needed for flat string. | 453 // Special work needed for flat string. |
566 if (flat) { | 454 if (flat) { |
567 data->stats_.empty_leaves_++; | 455 data->stats_.empty_leaves_++; |
568 FlattenString(root); | 456 FlattenString(root); |
569 CHECK(root->IsConsString() && root->IsFlat()); | 457 CHECK(root->IsConsString() && root->IsFlat()); |
570 } | 458 } |
571 return root; | 459 return root; |
572 } | 460 } |
573 | 461 |
574 | 462 |
575 static const int kCharacterStreamRandomCases = 150; | 463 static Handle<String> ConstructLeft( |
576 static const int kCharacterStreamEdgeCases = | 464 ConsStringGenerationData* data, |
577 kCharacterStreamRandomCases + 5; | 465 int depth) { |
466 Handle<String> answer = FACTORY->NewStringFromAscii(CStrVector("")); | |
467 data->stats_.leaves_++; | |
468 for (int i = 0; i < depth; i++) { | |
469 Handle<String> block = data->block(i); | |
470 Handle<String> next = FACTORY->NewConsString(answer, block); | |
471 if (next->IsConsString()) data->stats_.leaves_++; | |
472 data->stats_.chars_ += block->length(); | |
473 answer = next; | |
474 } | |
475 data->stats_.left_traversals_ = data->stats_.leaves_ - 2; | |
476 return answer; | |
477 } | |
578 | 478 |
579 | 479 |
580 static Handle<String> BuildConsStrings(int testCase, | 480 static Handle<String> ConstructRight( |
581 ConsStringGenerationData* data) { | 481 ConsStringGenerationData* data, |
582 // For random constructions, need to reset the generator. | 482 int depth) { |
583 data->rng_.init(); | 483 Handle<String> answer = FACTORY->NewStringFromAscii(CStrVector("")); |
584 for (int j = 0; j < testCase * 50; j++) { | 484 data->stats_.leaves_++; |
585 data->rng_.next(); | 485 for (int i = depth - 1; i >= 0; i--) { |
486 Handle<String> block = data->block(i); | |
487 Handle<String> next = FACTORY->NewConsString(block, answer); | |
488 if (next->IsConsString()) data->stats_.leaves_++; | |
489 data->stats_.chars_ += block->length(); | |
490 answer = next; | |
586 } | 491 } |
587 Handle<String> string; | 492 data->stats_.right_traversals_ = data->stats_.leaves_ - 2; |
588 switch (testCase) { | 493 return answer; |
589 case 0: | 494 } |
590 return ConstructBalanced(data->building_blocks_); | 495 |
591 case 1: | 496 |
592 return ConstructLeft(data->building_blocks_, DEEP_DEPTH); | 497 static Handle<String> ConstructBalancedHelper( |
593 case 2: | 498 ConsStringGenerationData* data, |
594 return ConstructRight(data->building_blocks_, DEEP_DEPTH); | 499 int from, |
595 case 3: | 500 int to) { |
596 return ConstructLeft(data->building_blocks_, 10); | 501 CHECK(to > from); |
597 case 4: | 502 if (to - from == 1) { |
598 return ConstructRight(data->building_blocks_, 10); | 503 data->stats_.chars_ += data->block(from)->length(); |
599 case 5: | 504 return data->block(from); |
600 return FACTORY->NewConsString( | 505 } |
601 data->building_blocks_[0], data->building_blocks_[1]); | 506 if (to - from == 2) { |
602 default: | 507 data->stats_.chars_ += data->block(from)->length(); |
603 if (testCase >= kCharacterStreamEdgeCases) { | 508 data->stats_.chars_ += data->block(from+1)->length(); |
604 CHECK(false); | 509 return FACTORY->NewConsString(data->block(from), data->block(from+1)); |
605 return string; | 510 } |
606 } | 511 Handle<String> part1 = |
607 // Random test case. | 512 ConstructBalancedHelper(data, from, from + ((to - from) / 2)); |
608 data->Reset(); | 513 Handle<String> part2 = |
609 string = ConstructRandomString(data, 200); | 514 ConstructBalancedHelper(data, from + ((to - from) / 2), to); |
610 AssertNoAllocation no_alloc; | 515 if (part1->IsConsString()) data->stats_.left_traversals_++; |
611 VerifyConsString(string, data); | 516 if (part2->IsConsString()) data->stats_.right_traversals_++; |
612 #ifdef DEBUG | 517 return FACTORY->NewConsString(part1, part2); |
613 printf( | 518 } |
614 "%s: [%d], %s: [%d], %s: [%d], %s: [%d], %s: [%d], %s: [%d]\n", | 519 |
615 "leaves", data->stats_.leaves_, | 520 |
616 "empty", data->stats_.empty_leaves_, | 521 static Handle<String> ConstructBalanced( |
617 "chars", data->stats_.chars_, | 522 ConsStringGenerationData* data, int depth = DEEP_DEPTH) { |
618 "lefts", data->stats_.left_traversals_, | 523 Handle<String> string = ConstructBalancedHelper(data, 0, depth); |
619 "rights", data->stats_.right_traversals_, | 524 data->stats_.leaves_ = |
620 "early_terminations", data->early_terminations_); | 525 data->stats_.left_traversals_ + data->stats_.right_traversals_ + 2; |
621 #endif | 526 return string; |
622 return string; | 527 } |
623 } | 528 |
529 | |
530 static StringInputBuffer buffer; | |
531 static ConsStringIteratorOp cons_string_iterator_op_1; | |
532 static ConsStringIteratorOp cons_string_iterator_op_2; | |
533 | |
534 static void Traverse(Handle<String> s1, Handle<String> s2) { | |
535 int i = 0; | |
536 buffer.Reset(*s1); | |
537 StringCharacterStream character_stream_1(*s1, 0, &cons_string_iterator_op_1); | |
538 StringCharacterStream character_stream_2(*s2, 0, &cons_string_iterator_op_2); | |
539 StringInputBuffer buffer2(*s2); | |
540 while (buffer.has_more()) { | |
541 CHECK(buffer2.has_more()); | |
542 CHECK(character_stream_1.HasMore()); | |
543 CHECK(character_stream_2.HasMore()); | |
544 uint16_t c = buffer.GetNext(); | |
545 CHECK_EQ(c, buffer2.GetNext()); | |
546 CHECK_EQ(c, character_stream_1.GetNext()); | |
547 CHECK_EQ(c, character_stream_2.GetNext()); | |
548 i++; | |
549 } | |
550 CHECK(!character_stream_1.HasMore()); | |
551 CHECK(!character_stream_2.HasMore()); | |
552 CHECK_EQ(s1->length(), i); | |
553 CHECK_EQ(s2->length(), i); | |
554 } | |
555 | |
556 | |
557 static void TraverseFirst(Handle<String> s1, Handle<String> s2, int chars) { | |
558 int i = 0; | |
559 buffer.Reset(*s1); | |
560 StringInputBuffer buffer2(*s2); | |
561 StringCharacterStream character_stream_1(*s1, 0, &cons_string_iterator_op_1); | |
562 StringCharacterStream character_stream_2(*s2, 0, &cons_string_iterator_op_2); | |
563 while (buffer.has_more() && i < chars) { | |
564 CHECK(buffer2.has_more()); | |
565 CHECK(character_stream_1.HasMore()); | |
566 CHECK(character_stream_2.HasMore()); | |
567 uint16_t c = buffer.GetNext(); | |
568 CHECK_EQ(c, buffer2.GetNext()); | |
569 CHECK_EQ(c, character_stream_1.GetNext()); | |
570 CHECK_EQ(c, character_stream_2.GetNext()); | |
571 i++; | |
572 } | |
573 s1->Get(s1->length() - 1); | |
574 s2->Get(s2->length() - 1); | |
575 } | |
576 | |
577 | |
578 TEST(Traverse) { | |
579 printf("TestTraverse\n"); | |
580 InitializeVM(); | |
581 v8::HandleScope scope; | |
582 ZoneScope zone(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); | |
583 ConsStringGenerationData data(false); | |
584 Handle<String> flat = ConstructBalanced(&data); | |
585 FlattenString(flat); | |
586 Handle<String> left_asymmetric = ConstructLeft(&data, DEEP_DEPTH); | |
587 Handle<String> right_asymmetric = ConstructRight(&data, DEEP_DEPTH); | |
588 Handle<String> symmetric = ConstructBalanced(&data); | |
589 printf("1\n"); | |
590 Traverse(flat, symmetric); | |
591 printf("2\n"); | |
592 Traverse(flat, left_asymmetric); | |
593 printf("3\n"); | |
594 Traverse(flat, right_asymmetric); | |
595 printf("4\n"); | |
596 Handle<String> left_deep_asymmetric = | |
597 ConstructLeft(&data, SUPER_DEEP_DEPTH); | |
598 Handle<String> right_deep_asymmetric = | |
599 ConstructRight(&data, SUPER_DEEP_DEPTH); | |
600 printf("5\n"); | |
601 TraverseFirst(left_asymmetric, left_deep_asymmetric, 1050); | |
602 printf("6\n"); | |
603 TraverseFirst(left_asymmetric, right_deep_asymmetric, 65536); | |
604 printf("7\n"); | |
605 FlattenString(left_asymmetric); | |
606 printf("10\n"); | |
607 Traverse(flat, left_asymmetric); | |
608 printf("11\n"); | |
609 FlattenString(right_asymmetric); | |
610 printf("12\n"); | |
611 Traverse(flat, right_asymmetric); | |
612 printf("14\n"); | |
613 FlattenString(symmetric); | |
614 printf("15\n"); | |
615 Traverse(flat, symmetric); | |
616 printf("16\n"); | |
617 FlattenString(left_deep_asymmetric); | |
618 printf("18\n"); | |
624 } | 619 } |
625 | 620 |
626 | 621 |
627 static void VerifyCharacterStream( | 622 static void VerifyCharacterStream( |
628 String* flat_string, String* cons_string) { | 623 String* flat_string, String* cons_string) { |
629 // Do not want to test ConString traversal on flat string. | 624 // Do not want to test ConString traversal on flat string. |
630 CHECK(flat_string->IsFlat()); | 625 CHECK(flat_string->IsFlat() && !flat_string->IsConsString()); |
631 CHECK(!flat_string->IsConsString()); | |
632 CHECK(cons_string->IsConsString()); | 626 CHECK(cons_string->IsConsString()); |
633 // TODO(dcarney) Test stream reset as well. | 627 // TODO(dcarney) Test stream reset as well. |
634 int length = flat_string->length(); | 628 int length = flat_string->length(); |
635 // Iterate start search in multiple places in the string. | 629 // Iterate start search in multiple places in the string. |
636 int outer_iterations = length > 20 ? 20 : length; | 630 int outer_iterations = length > 20 ? 20 : length; |
637 for (int j = 0; j <= outer_iterations; j++) { | 631 for (int j = 0; j <= outer_iterations; j++) { |
638 int offset = length * j / outer_iterations; | 632 int offset = length * j / outer_iterations; |
639 if (offset < 0) offset = 0; | 633 if (offset < 0) offset = 0; |
640 // Want to test the offset == length case. | 634 // Want to test the offset == length case. |
641 if (offset > length) offset = length; | 635 if (offset > length) offset = length; |
642 StringCharacterStream flat_stream( | 636 StringCharacterStream flat_stream( |
643 flat_string, (unsigned) offset, &cons_string_iterator_op_1); | 637 flat_string, (unsigned) offset, &cons_string_iterator_op_1); |
644 StringCharacterStream cons_stream( | 638 StringCharacterStream cons_stream( |
645 cons_string, (unsigned) offset, &cons_string_iterator_op_2); | 639 cons_string, (unsigned) offset, &cons_string_iterator_op_2); |
646 for (int i = offset; i < length; i++) { | 640 for (int i = offset; i < length; i++) { |
647 uint16_t c = flat_string->Get(i); | 641 uint16_t c = flat_string->Get(i); |
648 CHECK(flat_stream.HasMore()); | 642 CHECK(flat_stream.HasMore()); |
649 CHECK(cons_stream.HasMore()); | 643 CHECK(cons_stream.HasMore()); |
650 CHECK_EQ(c, flat_stream.GetNext()); | 644 CHECK_EQ(c, flat_stream.GetNext()); |
651 CHECK_EQ(c, cons_stream.GetNext()); | 645 CHECK_EQ(c, cons_stream.GetNext()); |
652 } | 646 } |
653 CHECK(!flat_stream.HasMore()); | 647 CHECK(!flat_stream.HasMore()); |
654 CHECK(!cons_stream.HasMore()); | 648 CHECK(!cons_stream.HasMore()); |
655 } | 649 } |
656 } | 650 } |
657 | 651 |
658 | 652 |
659 TEST(StringCharacterStreamEdgeCases) { | 653 static inline void PrintStats(const ConsStringGenerationData& data) { |
660 printf("TestStringCharacterStreamEdgeCases\n"); | 654 #ifdef DEBUG |
655 printf( | |
656 "%s: [%d], %s: [%d], %s: [%d], %s: [%d], %s: [%d], %s: [%d]\n", | |
657 "leaves", data.stats_.leaves_, | |
658 "empty", data.stats_.empty_leaves_, | |
659 "chars", data.stats_.chars_, | |
660 "lefts", data.stats_.left_traversals_, | |
661 "rights", data.stats_.right_traversals_, | |
662 "early_terminations", data.early_terminations_); | |
663 #endif | |
664 } | |
665 | |
666 | |
667 template<typename BuildString> | |
668 void TestStringCharacterStream(BuildString build, int test_cases) { | |
661 InitializeVM(); | 669 InitializeVM(); |
662 Isolate* isolate = Isolate::Current(); | 670 Isolate* isolate = Isolate::Current(); |
663 HandleScope outer_scope(isolate); | 671 HandleScope outer_scope(isolate); |
664 ZoneScope zone(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); | 672 ZoneScope zone(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); |
665 ConsStringGenerationData data; | 673 ConsStringGenerationData data(true); |
666 for (int i = 0; i < kCharacterStreamEdgeCases; i++) { | 674 bool last_test_did_gc = false; |
675 for (int i = 0; i < test_cases; i++) { | |
667 printf("%d\n", i); | 676 printf("%d\n", i); |
668 isolate->heap()->CollectAllGarbage( | |
669 Heap::kNoGCFlags, "must not allocate in loop"); | |
670 AlwaysAllocateScope always_allocate; | |
671 HandleScope inner_scope(isolate); | 677 HandleScope inner_scope(isolate); |
672 Handle<String> cons_string = BuildConsStrings(i, &data); | 678 // Build flat version of cons string. |
673 Handle<String> flat_string = BuildConsStrings(i, &data); | 679 Handle<String> flat_string = build(i, &data); |
680 ConsStringStats flat_string_stats; | |
681 AccumulateStats(flat_string, &flat_string_stats); | |
682 // Flatten string. | |
674 FlattenString(flat_string); | 683 FlattenString(flat_string); |
684 // Build unflattened version of cons string to test. | |
685 Handle<String> cons_string = build(i, &data); | |
686 ConsStringStats cons_string_stats; | |
687 AccumulateStats(cons_string, &cons_string_stats); | |
688 // Check if gc changed our data structure. | |
689 bool broken_by_gc = | |
690 cons_string_stats.leaves_ != data.stats_.leaves_ || | |
691 cons_string_stats.leaves_ != flat_string_stats.leaves_; | |
692 // If gc altered the data structure, do a full collection and retry test. | |
693 if (broken_by_gc) { | |
694 // Bail if test runs twice. | |
695 if (last_test_did_gc) CHECK(false); | |
696 printf("forcing gc\n"); | |
697 isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags, "retry test"); | |
698 // Retry test. | |
699 last_test_did_gc = true; | |
700 i--; | |
701 continue; | |
702 } | |
703 last_test_did_gc = false; | |
675 AssertNoAllocation no_alloc; | 704 AssertNoAllocation no_alloc; |
676 CHECK(flat_string->IsConsString() && flat_string->IsFlat()); | 705 PrintStats(data); |
677 VerifyCharacterStream(ConsString::cast(*flat_string)->first(), | 706 // Full verify of cons string. |
678 *cons_string); | 707 cons_string_stats.VerifyEqual(flat_string_stats); |
679 } | 708 cons_string_stats.VerifyEqual(data.stats_); |
709 VerifyConsString(cons_string, &data); | |
710 String* flat_string_ptr = | |
711 flat_string->IsConsString() ? | |
712 ConsString::cast(*flat_string)->first() : | |
713 *flat_string; | |
714 VerifyCharacterStream(flat_string_ptr, *cons_string); | |
715 } | |
716 } | |
717 | |
718 | |
719 static const int kCharacterStreamNonRandomCases = 8; | |
720 | |
721 | |
722 static Handle<String> BuildEdgeCaseConsString( | |
723 int test_case, ConsStringGenerationData* data) { | |
724 data->Reset(); | |
725 switch (test_case) { | |
726 case 0: | |
727 return ConstructBalanced(data, 71); | |
728 case 1: | |
729 return ConstructLeft(data, 71); | |
730 case 2: | |
731 return ConstructRight(data, 71); | |
732 case 3: | |
733 return ConstructLeft(data, 10); | |
734 case 4: | |
735 return ConstructRight(data, 10); | |
736 case 5: | |
737 // 2 element balanced tree. | |
738 data->stats_.chars_ += data->block(0)->length(); | |
739 data->stats_.chars_ += data->block(1)->length(); | |
740 data->stats_.leaves_ += 2; | |
741 return FACTORY->NewConsString(data->block(0), data->block(1)); | |
742 case 6: | |
743 // Simple flattened tree. | |
744 data->stats_.chars_ += data->block(0)->length(); | |
745 data->stats_.chars_ += data->block(1)->length(); | |
746 data->stats_.leaves_ += 2; | |
747 data->stats_.empty_leaves_ += 1; | |
748 { | |
749 Handle<String> string = | |
750 FACTORY->NewConsString(data->block(0), data->block(1)); | |
751 FlattenString(string); | |
752 return string; | |
753 } | |
754 case 7: | |
755 // Left node flattened. | |
756 data->stats_.chars_ += data->block(0)->length(); | |
757 data->stats_.chars_ += data->block(1)->length(); | |
758 data->stats_.chars_ += data->block(2)->length(); | |
759 data->stats_.leaves_ += 3; | |
760 data->stats_.empty_leaves_ += 1; | |
761 data->stats_.left_traversals_ += 1; | |
762 { | |
763 Handle<String> left = | |
764 FACTORY->NewConsString(data->block(0), data->block(1)); | |
765 FlattenString(left); | |
766 return FACTORY->NewConsString(left, data->block(2)); | |
767 } | |
768 case 8: | |
769 // Left node and right node flattened. | |
770 data->stats_.chars_ += data->block(0)->length(); | |
771 data->stats_.chars_ += data->block(1)->length(); | |
772 data->stats_.chars_ += data->block(2)->length(); | |
773 data->stats_.chars_ += data->block(3)->length(); | |
774 data->stats_.leaves_ += 4; | |
775 data->stats_.empty_leaves_ += 2; | |
776 data->stats_.left_traversals_ += 1; | |
777 data->stats_.right_traversals_ += 1; | |
778 { | |
779 Handle<String> left = | |
780 FACTORY->NewConsString(data->block(0), data->block(1)); | |
781 FlattenString(left); | |
782 Handle<String> right = | |
783 FACTORY->NewConsString(data->block(2), data->block(2)); | |
784 FlattenString(right); | |
785 return FACTORY->NewConsString(left, right); | |
786 } | |
787 } | |
788 UNREACHABLE(); | |
789 return Handle<String>(); | |
790 } | |
791 | |
792 | |
793 TEST(StringCharacterStreamEdgeCases) { | |
794 printf("TestStringCharacterStreamEdgeCases\n"); | |
795 TestStringCharacterStream( | |
796 BuildEdgeCaseConsString, kCharacterStreamNonRandomCases); | |
797 } | |
798 | |
799 | |
800 static const int kBalances = 3; | |
801 static const int kTreeLengths = 4; | |
802 static const int kEmptyLeaves = 4; | |
803 static const int kUniqueRandomParameters = | |
804 kBalances*kTreeLengths*kEmptyLeaves; | |
805 | |
806 | |
807 static void InitializeGenerationData( | |
808 int test_case, ConsStringGenerationData* data) { | |
809 // Clear the settings and reinit the rng. | |
810 data->Reset(); | |
811 // Spin up the rng to a known location that is unique per test. | |
812 static const int kPerTestJump = 501; | |
813 for (int j = 0; j < test_case*kPerTestJump; j++) { | |
814 data->rng_.next(); | |
815 } | |
816 // Choose balanced, left or right heavy trees. | |
817 switch (test_case % kBalances) { | |
818 case 0: | |
819 // Nothing to do. Already balanced. | |
820 break; | |
821 case 1: | |
822 // Left balanced. | |
823 data->leftness_ = 0.90; | |
824 data->rightness_ = 0.15; | |
825 break; | |
826 case 2: | |
827 // Right balanced. | |
828 data->leftness_ = 0.15; | |
829 data->rightness_ = 0.90; | |
830 break; | |
831 default: | |
832 UNREACHABLE(); | |
833 break; | |
834 } | |
835 // Must remove the influence of the above decision. | |
836 test_case /= kBalances; | |
837 // Choose tree length. | |
838 switch (test_case % kTreeLengths) { | |
839 case 0: | |
840 data->max_leaves_ = 16; | |
841 data->early_termination_threshold_ = 0.2; | |
842 break; | |
843 case 1: | |
844 data->max_leaves_ = 50; | |
845 data->early_termination_threshold_ = 0.05; | |
846 break; | |
847 case 2: | |
848 data->max_leaves_ = 500; | |
849 data->early_termination_threshold_ = 0.03; | |
850 break; | |
851 case 3: | |
852 data->max_leaves_ = 5000; | |
853 data->early_termination_threshold_ = 0.001; | |
854 break; | |
855 default: | |
856 UNREACHABLE(); | |
857 break; | |
858 } | |
859 // Must remove the influence of the above decision. | |
860 test_case /= kTreeLengths; | |
861 // Choose how much we allow empty nodes, including not at all. | |
862 data->empty_leaf_threshold_ = | |
863 0.03 * static_cast<double>(test_case % kEmptyLeaves); | |
864 } | |
865 | |
866 | |
867 static Handle<String> BuildRandomConsString( | |
868 int test_case, ConsStringGenerationData* data) { | |
869 InitializeGenerationData(test_case, data); | |
870 return ConstructRandomString(data, 200); | |
871 } | |
872 | |
873 | |
874 TEST(StringCharacterStreamRandom) { | |
Michael Starzinger
2012/12/14 09:23:07
This test is failing on the GC Stress builder.
ht
| |
875 printf("StringCharacterStreamRandom\n"); | |
876 TestStringCharacterStream(BuildRandomConsString, kUniqueRandomParameters*7); | |
680 } | 877 } |
681 | 878 |
682 | 879 |
683 static const int DEEP_ASCII_DEPTH = 100000; | 880 static const int DEEP_ASCII_DEPTH = 100000; |
684 | 881 |
685 | 882 |
686 TEST(DeepAscii) { | 883 TEST(DeepAscii) { |
687 printf("TestDeepAscii\n"); | 884 printf("TestDeepAscii\n"); |
688 InitializeVM(); | 885 InitializeVM(); |
689 v8::HandleScope scope; | 886 v8::HandleScope scope; |
(...skipping 423 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1113 | 1310 |
1114 v8::Local<v8::String> expected = v8_str("ascii\x80only\x80string\x80"); | 1311 v8::Local<v8::String> expected = v8_str("ascii\x80only\x80string\x80"); |
1115 CHECK(expected->Equals(result)); | 1312 CHECK(expected->Equals(result)); |
1116 } | 1313 } |
1117 | 1314 |
1118 | 1315 |
1119 TEST(IsAscii) { | 1316 TEST(IsAscii) { |
1120 CHECK(String::IsAscii(static_cast<char*>(NULL), 0)); | 1317 CHECK(String::IsAscii(static_cast<char*>(NULL), 0)); |
1121 CHECK(String::IsAscii(static_cast<uc16*>(NULL), 0)); | 1318 CHECK(String::IsAscii(static_cast<uc16*>(NULL), 0)); |
1122 } | 1319 } |
OLD | NEW |