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" |
11 | 11 |
12 #include "api.h" | 12 #include "api.h" |
13 #include "factory.h" | 13 #include "factory.h" |
14 #include "objects.h" | 14 #include "objects.h" |
15 #include "cctest.h" | 15 #include "cctest.h" |
16 #include "zone-inl.h" | 16 #include "zone-inl.h" |
17 | 17 |
18 unsigned int seed = 123; | 18 // Adapted from http://en.wikipedia.org/wiki/Multiply-with-carry |
| 19 class RandomNumberGenerator { |
| 20 public: |
| 21 RandomNumberGenerator() { |
| 22 init(); |
| 23 } |
19 | 24 |
20 static uint32_t gen() { | 25 void init(uint32_t seed = 0x5688c73e) { |
21 uint64_t z; | 26 static const uint32_t phi = 0x9e3779b9; |
22 z = seed; | 27 c = 362436; |
23 z *= 279470273; | 28 i = kQSize-1; |
24 z %= 4294967291U; | 29 Q[0] = seed; |
25 seed = static_cast<unsigned int>(z); | 30 Q[1] = seed + phi; |
26 return static_cast<uint32_t>(seed >> 16); | 31 Q[2] = seed + phi + phi; |
27 } | 32 for (unsigned j = 3; j < kQSize; j++) { |
| 33 Q[j] = Q[j - 3] ^ Q[j - 2] ^ phi ^ j; |
| 34 } |
| 35 } |
| 36 |
| 37 uint32_t next() { |
| 38 uint64_t a = 18782; |
| 39 uint32_t r = 0xfffffffe; |
| 40 i = (i + 1) & (kQSize-1); |
| 41 uint64_t t = a * Q[i] + c; |
| 42 c = (t >> 32); |
| 43 uint32_t x = t + c; |
| 44 if (x < c) { |
| 45 x++; |
| 46 c++; |
| 47 } |
| 48 return (Q[i] = r - x); |
| 49 } |
| 50 |
| 51 uint32_t next(int max) { |
| 52 return next() % max; |
| 53 } |
| 54 |
| 55 bool next(double threshold) { |
| 56 ASSERT(threshold >= 0.0 && threshold <= 1.0); |
| 57 if (threshold == 1.0) return true; |
| 58 if (threshold == 0.0) return false; |
| 59 uint32_t value = next() % 100000; |
| 60 return threshold > static_cast<double>(value)/100000.0; |
| 61 } |
| 62 |
| 63 private: |
| 64 static const uint32_t kQSize = 4096; |
| 65 uint32_t Q[kQSize]; |
| 66 uint32_t c; |
| 67 uint32_t i; |
| 68 }; |
28 | 69 |
29 | 70 |
30 using namespace v8::internal; | 71 using namespace v8::internal; |
31 | 72 |
32 static v8::Persistent<v8::Context> env; | 73 static v8::Persistent<v8::Context> env; |
33 | 74 |
34 | 75 |
35 static void InitializeVM() { | 76 static void InitializeVM() { |
36 if (env.IsEmpty()) { | 77 if (env.IsEmpty()) { |
37 v8::HandleScope scope; | 78 v8::HandleScope scope; |
38 const char* extensions[] = { "v8/print" }; | 79 const char* extensions[] = { "v8/print" }; |
39 v8::ExtensionConfiguration config(1, extensions); | 80 v8::ExtensionConfiguration config(1, extensions); |
40 env = v8::Context::New(&config); | 81 env = v8::Context::New(&config); |
41 } | 82 } |
42 v8::HandleScope scope; | 83 v8::HandleScope scope; |
43 env->Enter(); | 84 env->Enter(); |
44 } | 85 } |
45 | 86 |
46 | 87 |
47 static const int NUMBER_OF_BUILDING_BLOCKS = 128; | 88 static const int NUMBER_OF_BUILDING_BLOCKS = 256; |
48 static const int DEEP_DEPTH = 8 * 1024; | 89 static const int DEEP_DEPTH = 8 * 1024; |
49 static const int SUPER_DEEP_DEPTH = 80 * 1024; | 90 static const int SUPER_DEEP_DEPTH = 80 * 1024; |
50 | 91 |
51 | 92 |
52 class Resource: public v8::String::ExternalStringResource, | 93 class Resource: public v8::String::ExternalStringResource, |
53 public ZoneObject { | 94 public ZoneObject { |
54 public: | 95 public: |
55 explicit Resource(Vector<const uc16> string): data_(string.start()) { | 96 explicit Resource(Vector<const uc16> string): data_(string.start()) { |
56 length_ = string.length(); | 97 length_ = string.length(); |
57 } | 98 } |
(...skipping 14 matching lines...) Expand all Loading... |
72 } | 113 } |
73 virtual const char* data() const { return data_; } | 114 virtual const char* data() const { return data_; } |
74 virtual size_t length() const { return length_; } | 115 virtual size_t length() const { return length_; } |
75 | 116 |
76 private: | 117 private: |
77 const char* data_; | 118 const char* data_; |
78 size_t length_; | 119 size_t length_; |
79 }; | 120 }; |
80 | 121 |
81 | 122 |
82 static void InitializeBuildingBlocks( | 123 static void InitializeBuildingBlocks(Handle<String>* building_blocks, |
83 Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS]) { | 124 int bb_length, |
| 125 bool long_blocks, |
| 126 RandomNumberGenerator* rng) { |
84 // A list of pointers that we don't have any interest in cleaning up. | 127 // A list of pointers that we don't have any interest in cleaning up. |
85 // If they are reachable from a root then leak detection won't complain. | 128 // If they are reachable from a root then leak detection won't complain. |
86 Zone* zone = Isolate::Current()->runtime_zone(); | 129 Zone* zone = Isolate::Current()->runtime_zone(); |
87 for (int i = 0; i < NUMBER_OF_BUILDING_BLOCKS; i++) { | 130 for (int i = 0; i < bb_length; i++) { |
88 int len = gen() % 16; | 131 int len = rng->next(16); |
89 if (len > 14) { | 132 int slice_head_chars = 0; |
| 133 int slice_tail_chars = 0; |
| 134 int slice_depth = 0; |
| 135 for (int j = 0; j < 3; j++) { |
| 136 if (rng->next(0.35)) slice_depth++; |
| 137 } |
| 138 // Must truncate something for a slice string. Loop until |
| 139 // at least one end will be sliced. |
| 140 while (slice_head_chars == 0 && slice_tail_chars == 0) { |
| 141 slice_head_chars = rng->next(15); |
| 142 slice_tail_chars = rng->next(12); |
| 143 } |
| 144 if (long_blocks) { |
| 145 // Generate building blocks which will never be merged |
| 146 len += ConsString::kMinLength + 1; |
| 147 } else if (len > 14) { |
90 len += 1234; | 148 len += 1234; |
91 } | 149 } |
92 switch (gen() % 4) { | 150 // Don't slice 0 length strings. |
| 151 if (len == 0) slice_depth = 0; |
| 152 int slice_length = slice_depth*(slice_head_chars + slice_tail_chars); |
| 153 len += slice_length; |
| 154 switch (rng->next(4)) { |
93 case 0: { | 155 case 0: { |
94 uc16 buf[2000]; | 156 uc16 buf[2000]; |
95 for (int j = 0; j < len; j++) { | 157 for (int j = 0; j < len; j++) { |
96 buf[j] = gen() % 65536; | 158 buf[j] = rng->next(0x10000); |
97 } | 159 } |
98 building_blocks[i] = | 160 building_blocks[i] = |
99 FACTORY->NewStringFromTwoByte(Vector<const uc16>(buf, len)); | 161 FACTORY->NewStringFromTwoByte(Vector<const uc16>(buf, len)); |
100 for (int j = 0; j < len; j++) { | 162 for (int j = 0; j < len; j++) { |
101 CHECK_EQ(buf[j], building_blocks[i]->Get(j)); | 163 CHECK_EQ(buf[j], building_blocks[i]->Get(j)); |
102 } | 164 } |
103 break; | 165 break; |
104 } | 166 } |
105 case 1: { | 167 case 1: { |
106 char buf[2000]; | 168 char buf[2000]; |
107 for (int j = 0; j < len; j++) { | 169 for (int j = 0; j < len; j++) { |
108 buf[j] = gen() % 128; | 170 buf[j] = rng->next(0x80); |
109 } | 171 } |
110 building_blocks[i] = | 172 building_blocks[i] = |
111 FACTORY->NewStringFromAscii(Vector<const char>(buf, len)); | 173 FACTORY->NewStringFromAscii(Vector<const char>(buf, len)); |
112 for (int j = 0; j < len; j++) { | 174 for (int j = 0; j < len; j++) { |
113 CHECK_EQ(buf[j], building_blocks[i]->Get(j)); | 175 CHECK_EQ(buf[j], building_blocks[i]->Get(j)); |
114 } | 176 } |
115 break; | 177 break; |
116 } | 178 } |
117 case 2: { | 179 case 2: { |
118 uc16* buf = zone->NewArray<uc16>(len); | 180 uc16* buf = zone->NewArray<uc16>(len); |
119 for (int j = 0; j < len; j++) { | 181 for (int j = 0; j < len; j++) { |
120 buf[j] = gen() % 65536; | 182 buf[j] = rng->next(0x10000); |
121 } | 183 } |
122 Resource* resource = new(zone) Resource(Vector<const uc16>(buf, len)); | 184 Resource* resource = new(zone) Resource(Vector<const uc16>(buf, len)); |
123 building_blocks[i] = FACTORY->NewExternalStringFromTwoByte(resource); | 185 building_blocks[i] = FACTORY->NewExternalStringFromTwoByte(resource); |
124 for (int j = 0; j < len; j++) { | 186 for (int j = 0; j < len; j++) { |
125 CHECK_EQ(buf[j], building_blocks[i]->Get(j)); | 187 CHECK_EQ(buf[j], building_blocks[i]->Get(j)); |
126 } | 188 } |
127 break; | 189 break; |
128 } | 190 } |
129 case 3: { | 191 case 3: { |
130 char* buf = NewArray<char>(len); | 192 char* buf = zone->NewArray<char>(len); |
131 for (int j = 0; j < len; j++) { | 193 for (int j = 0; j < len; j++) { |
132 buf[j] = gen() % 128; | 194 buf[j] = rng->next(128); |
133 } | 195 } |
134 building_blocks[i] = | 196 AsciiResource* resource = |
135 FACTORY->NewStringFromAscii(Vector<const char>(buf, len)); | 197 new(zone) AsciiResource(Vector<const char>(buf, len)); |
| 198 building_blocks[i] = FACTORY->NewExternalStringFromAscii(resource); |
136 for (int j = 0; j < len; j++) { | 199 for (int j = 0; j < len; j++) { |
137 CHECK_EQ(buf[j], building_blocks[i]->Get(j)); | 200 CHECK_EQ(buf[j], building_blocks[i]->Get(j)); |
138 } | 201 } |
139 DeleteArray<char>(buf); | |
140 break; | 202 break; |
141 } | 203 } |
142 } | 204 } |
| 205 for (int j = slice_depth; j > 0; j--) { |
| 206 building_blocks[i] = FACTORY->NewSubString( |
| 207 building_blocks[i], |
| 208 slice_head_chars, |
| 209 building_blocks[i]->length() - slice_tail_chars); |
| 210 } |
| 211 CHECK(len == building_blocks[i]->length() + slice_length); |
143 } | 212 } |
144 } | 213 } |
145 | 214 |
146 | 215 |
147 static Handle<String> ConstructLeft( | 216 static Handle<String> ConstructLeft( |
148 Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS], | 217 Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS], |
149 int depth) { | 218 int depth) { |
150 Handle<String> answer = FACTORY->NewStringFromAscii(CStrVector("")); | 219 Handle<String> answer = FACTORY->NewStringFromAscii(CStrVector("")); |
151 for (int i = 0; i < depth; i++) { | 220 for (int i = 0; i < depth; i++) { |
152 answer = FACTORY->NewConsString( | 221 answer = FACTORY->NewConsString( |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
191 } | 260 } |
192 | 261 |
193 | 262 |
194 static Handle<String> ConstructBalanced( | 263 static Handle<String> ConstructBalanced( |
195 Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS]) { | 264 Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS]) { |
196 return ConstructBalancedHelper(building_blocks, 0, DEEP_DEPTH); | 265 return ConstructBalancedHelper(building_blocks, 0, DEEP_DEPTH); |
197 } | 266 } |
198 | 267 |
199 | 268 |
200 static StringInputBuffer buffer; | 269 static StringInputBuffer buffer; |
201 | 270 static ConsStringIteratorOp cons_string_iterator_op_1; |
| 271 static ConsStringIteratorOp cons_string_iterator_op_2; |
202 | 272 |
203 static void Traverse(Handle<String> s1, Handle<String> s2) { | 273 static void Traverse(Handle<String> s1, Handle<String> s2) { |
204 int i = 0; | 274 int i = 0; |
205 buffer.Reset(*s1); | 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); |
206 StringInputBuffer buffer2(*s2); | 278 StringInputBuffer buffer2(*s2); |
207 while (buffer.has_more()) { | 279 while (buffer.has_more()) { |
208 CHECK(buffer2.has_more()); | 280 CHECK(buffer2.has_more()); |
| 281 CHECK(character_stream_1.HasMore()); |
| 282 CHECK(character_stream_2.HasMore()); |
209 uint16_t c = buffer.GetNext(); | 283 uint16_t c = buffer.GetNext(); |
210 CHECK_EQ(c, buffer2.GetNext()); | 284 CHECK_EQ(c, buffer2.GetNext()); |
| 285 CHECK_EQ(c, character_stream_1.GetNext()); |
| 286 CHECK_EQ(c, character_stream_2.GetNext()); |
211 i++; | 287 i++; |
212 } | 288 } |
| 289 CHECK(!character_stream_1.HasMore()); |
| 290 CHECK(!character_stream_2.HasMore()); |
213 CHECK_EQ(s1->length(), i); | 291 CHECK_EQ(s1->length(), i); |
214 CHECK_EQ(s2->length(), i); | 292 CHECK_EQ(s2->length(), i); |
215 } | 293 } |
216 | 294 |
217 | 295 |
218 static void TraverseFirst(Handle<String> s1, Handle<String> s2, int chars) { | 296 static void TraverseFirst(Handle<String> s1, Handle<String> s2, int chars) { |
219 int i = 0; | 297 int i = 0; |
220 buffer.Reset(*s1); | 298 buffer.Reset(*s1); |
221 StringInputBuffer buffer2(*s2); | 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); |
222 while (buffer.has_more() && i < chars) { | 302 while (buffer.has_more() && i < chars) { |
223 CHECK(buffer2.has_more()); | 303 CHECK(buffer2.has_more()); |
| 304 CHECK(character_stream_1.HasMore()); |
| 305 CHECK(character_stream_2.HasMore()); |
224 uint16_t c = buffer.GetNext(); | 306 uint16_t c = buffer.GetNext(); |
225 CHECK_EQ(c, buffer2.GetNext()); | 307 CHECK_EQ(c, buffer2.GetNext()); |
| 308 CHECK_EQ(c, character_stream_1.GetNext()); |
| 309 CHECK_EQ(c, character_stream_2.GetNext()); |
226 i++; | 310 i++; |
227 } | 311 } |
228 s1->Get(s1->length() - 1); | 312 s1->Get(s1->length() - 1); |
229 s2->Get(s2->length() - 1); | 313 s2->Get(s2->length() - 1); |
230 } | 314 } |
231 | 315 |
232 | 316 |
233 TEST(Traverse) { | 317 TEST(Traverse) { |
234 printf("TestTraverse\n"); | 318 printf("TestTraverse\n"); |
235 InitializeVM(); | 319 InitializeVM(); |
236 v8::HandleScope scope; | 320 v8::HandleScope scope; |
237 Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS]; | 321 Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS]; |
238 ZoneScope zone(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); | 322 ZoneScope zone(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); |
239 InitializeBuildingBlocks(building_blocks); | 323 RandomNumberGenerator rng; |
| 324 rng.init(); |
| 325 InitializeBuildingBlocks( |
| 326 building_blocks, NUMBER_OF_BUILDING_BLOCKS, false, &rng); |
240 Handle<String> flat = ConstructBalanced(building_blocks); | 327 Handle<String> flat = ConstructBalanced(building_blocks); |
241 FlattenString(flat); | 328 FlattenString(flat); |
242 Handle<String> left_asymmetric = ConstructLeft(building_blocks, DEEP_DEPTH); | 329 Handle<String> left_asymmetric = ConstructLeft(building_blocks, DEEP_DEPTH); |
243 Handle<String> right_asymmetric = ConstructRight(building_blocks, DEEP_DEPTH); | 330 Handle<String> right_asymmetric = ConstructRight(building_blocks, DEEP_DEPTH); |
244 Handle<String> symmetric = ConstructBalanced(building_blocks); | 331 Handle<String> symmetric = ConstructBalanced(building_blocks); |
245 printf("1\n"); | 332 printf("1\n"); |
246 Traverse(flat, symmetric); | 333 Traverse(flat, symmetric); |
247 printf("2\n"); | 334 printf("2\n"); |
248 Traverse(flat, left_asymmetric); | 335 Traverse(flat, left_asymmetric); |
249 printf("3\n"); | 336 printf("3\n"); |
(...skipping 18 matching lines...) Expand all Loading... |
268 printf("14\n"); | 355 printf("14\n"); |
269 FlattenString(symmetric); | 356 FlattenString(symmetric); |
270 printf("15\n"); | 357 printf("15\n"); |
271 Traverse(flat, symmetric); | 358 Traverse(flat, symmetric); |
272 printf("16\n"); | 359 printf("16\n"); |
273 FlattenString(left_deep_asymmetric); | 360 FlattenString(left_deep_asymmetric); |
274 printf("18\n"); | 361 printf("18\n"); |
275 } | 362 } |
276 | 363 |
277 | 364 |
| 365 class ConsStringStats { |
| 366 public: |
| 367 ConsStringStats() { |
| 368 Reset(); |
| 369 } |
| 370 void Reset(); |
| 371 void VerifyEqual(const ConsStringStats& that) const; |
| 372 unsigned leaves_; |
| 373 unsigned empty_leaves_; |
| 374 unsigned chars_; |
| 375 unsigned left_traversals_; |
| 376 unsigned right_traversals_; |
| 377 private: |
| 378 DISALLOW_COPY_AND_ASSIGN(ConsStringStats); |
| 379 }; |
| 380 |
| 381 |
| 382 void ConsStringStats::Reset() { |
| 383 leaves_ = 0; |
| 384 empty_leaves_ = 0; |
| 385 chars_ = 0; |
| 386 left_traversals_ = 0; |
| 387 right_traversals_ = 0; |
| 388 } |
| 389 |
| 390 |
| 391 void ConsStringStats::VerifyEqual(const ConsStringStats& that) const { |
| 392 CHECK(this->leaves_ == that.leaves_); |
| 393 CHECK(this->empty_leaves_ == that.empty_leaves_); |
| 394 CHECK(this->chars_ == that.chars_); |
| 395 CHECK(this->left_traversals_ == that.left_traversals_); |
| 396 CHECK(this->right_traversals_ == that.right_traversals_); |
| 397 } |
| 398 |
| 399 |
| 400 class ConsStringGenerationData { |
| 401 public: |
| 402 ConsStringGenerationData(); |
| 403 void Reset(); |
| 404 // Input variables. |
| 405 double early_termination_threshold_; |
| 406 double leftness_; |
| 407 double rightness_; |
| 408 double empty_leaf_threshold_; |
| 409 unsigned max_leaves_; |
| 410 // Cached data. |
| 411 Handle<String> building_blocks_[NUMBER_OF_BUILDING_BLOCKS]; |
| 412 String* empty_string_; |
| 413 RandomNumberGenerator rng_; |
| 414 // Stats. |
| 415 ConsStringStats stats_; |
| 416 unsigned early_terminations_; |
| 417 private: |
| 418 DISALLOW_COPY_AND_ASSIGN(ConsStringGenerationData); |
| 419 }; |
| 420 |
| 421 |
| 422 ConsStringGenerationData::ConsStringGenerationData() { |
| 423 rng_.init(); |
| 424 InitializeBuildingBlocks( |
| 425 building_blocks_, NUMBER_OF_BUILDING_BLOCKS, true, &rng_); |
| 426 empty_string_ = Isolate::Current()->heap()->empty_string(); |
| 427 Reset(); |
| 428 } |
| 429 |
| 430 |
| 431 void ConsStringGenerationData::Reset() { |
| 432 early_termination_threshold_ = 0.01; |
| 433 leftness_ = 0.75; |
| 434 rightness_ = 0.75; |
| 435 empty_leaf_threshold_ = 0.02; |
| 436 max_leaves_ = 1000; |
| 437 stats_.Reset(); |
| 438 early_terminations_ = 0; |
| 439 } |
| 440 |
| 441 |
| 442 void VerifyConsString(ConsString* cons_string, ConsStringStats* stats) { |
| 443 int left_length = cons_string->first()->length(); |
| 444 int right_length = cons_string->second()->length(); |
| 445 CHECK(cons_string->length() == left_length + right_length); |
| 446 // Check left side. |
| 447 if (cons_string->first()->IsConsString()) { |
| 448 stats->left_traversals_++; |
| 449 VerifyConsString(ConsString::cast(cons_string->first()), stats); |
| 450 } else { |
| 451 CHECK_NE(left_length, 0); |
| 452 stats->leaves_++; |
| 453 stats->chars_ += left_length; |
| 454 } |
| 455 // Check right side. |
| 456 if (cons_string->second()->IsConsString()) { |
| 457 stats->right_traversals_++; |
| 458 VerifyConsString(ConsString::cast(cons_string->second()), stats); |
| 459 } else { |
| 460 if (right_length == 0) stats->empty_leaves_++; |
| 461 stats->leaves_++; |
| 462 stats->chars_ += right_length; |
| 463 } |
| 464 } |
| 465 |
| 466 |
| 467 void VerifyConsStringWithOperator( |
| 468 ConsString* cons_string, ConsStringStats* stats) { |
| 469 // Init op. |
| 470 ConsStringIteratorOp op; |
| 471 op.Reset(); |
| 472 // Use response for initial search and on blown stack. |
| 473 ConsStringIteratorOp::ContinueResponse response; |
| 474 response.string_ = cons_string; |
| 475 response.offset_ = 0; |
| 476 response.type_ = cons_string->map()->instance_type(); |
| 477 response.length_ = (uint32_t) cons_string->length(); |
| 478 while (true) { |
| 479 String* string = op.Operate(ConsString::cast(response.string_), |
| 480 &response.offset_, |
| 481 &response.type_, |
| 482 &response.length_); |
| 483 CHECK(string != NULL); |
| 484 while (true) { |
| 485 // Accumulate stats. |
| 486 stats->leaves_++; |
| 487 stats->chars_ += string->length(); |
| 488 // Check for completion. |
| 489 bool keep_going_fast_check = op.HasMore(); |
| 490 bool keep_going = op.ContinueOperation(&response); |
| 491 if (!keep_going) return; |
| 492 // Verify no false positives for fast check. |
| 493 CHECK(keep_going_fast_check); |
| 494 CHECK(response.string_ != NULL); |
| 495 // Blew stack. Restart outer loop. |
| 496 if (response.string_->IsConsString()) break; |
| 497 string = response.string_; |
| 498 } |
| 499 }; |
| 500 } |
| 501 |
| 502 |
| 503 void VerifyConsString(Handle<String> root, ConsStringGenerationData* data) { |
| 504 // Verify basic data. |
| 505 CHECK(root->IsConsString()); |
| 506 CHECK((unsigned)root->length() == data->stats_.chars_); |
| 507 // Recursive verify. |
| 508 ConsStringStats stats; |
| 509 VerifyConsString(ConsString::cast(*root), &stats); |
| 510 stats.VerifyEqual(data->stats_); |
| 511 // Iteratively verify. |
| 512 stats.Reset(); |
| 513 VerifyConsStringWithOperator(ConsString::cast(*root), &stats); |
| 514 // Don't see these. Must copy over. |
| 515 stats.empty_leaves_ = data->stats_.empty_leaves_; |
| 516 stats.left_traversals_ = data->stats_.left_traversals_; |
| 517 stats.right_traversals_ = data->stats_.right_traversals_; |
| 518 // Adjust total leaves to compensate. |
| 519 stats.leaves_ += stats.empty_leaves_; |
| 520 stats.VerifyEqual(data->stats_); |
| 521 } |
| 522 |
| 523 |
| 524 static Handle<String> ConstructRandomString(ConsStringGenerationData* data, |
| 525 unsigned max_recursion) { |
| 526 // Compute termination characteristics. |
| 527 bool terminate = false; |
| 528 bool flat = data->rng_.next(data->empty_leaf_threshold_); |
| 529 bool terminate_early = data->rng_.next(data->early_termination_threshold_); |
| 530 if (terminate_early) data->early_terminations_++; |
| 531 // The obvious condition. |
| 532 terminate |= max_recursion == 0; |
| 533 // Flat cons string terminate by definition. |
| 534 terminate |= flat; |
| 535 // Cap for max leaves. |
| 536 terminate |= data->stats_.leaves_ >= data->max_leaves_; |
| 537 // Roll the dice. |
| 538 terminate |= terminate_early; |
| 539 // Compute termination characteristics for each side. |
| 540 bool terminate_left = terminate || !data->rng_.next(data->leftness_); |
| 541 bool terminate_right = terminate || !data->rng_.next(data->rightness_); |
| 542 // Generate left string. |
| 543 Handle<String> left; |
| 544 if (terminate_left) { |
| 545 left = data->building_blocks_[data->rng_.next(NUMBER_OF_BUILDING_BLOCKS)]; |
| 546 data->stats_.leaves_++; |
| 547 data->stats_.chars_ += left->length(); |
| 548 } else { |
| 549 left = ConstructRandomString(data, max_recursion - 1); |
| 550 data->stats_.left_traversals_++; |
| 551 } |
| 552 // Generate right string. |
| 553 Handle<String> right; |
| 554 if (terminate_right) { |
| 555 right = data->building_blocks_[data->rng_.next(NUMBER_OF_BUILDING_BLOCKS)]; |
| 556 data->stats_.leaves_++; |
| 557 data->stats_.chars_ += right->length(); |
| 558 } else { |
| 559 right = ConstructRandomString(data, max_recursion - 1); |
| 560 data->stats_.right_traversals_++; |
| 561 } |
| 562 // Build the cons string. |
| 563 Handle<String> root = FACTORY->NewConsString(left, right); |
| 564 CHECK(root->IsConsString() && !root->IsFlat()); |
| 565 // Special work needed for flat string. |
| 566 if (flat) { |
| 567 data->stats_.empty_leaves_++; |
| 568 FlattenString(root); |
| 569 CHECK(root->IsConsString() && root->IsFlat()); |
| 570 } |
| 571 return root; |
| 572 } |
| 573 |
| 574 |
| 575 static const int kCharacterStreamRandomCases = 150; |
| 576 static const int kCharacterStreamEdgeCases = |
| 577 kCharacterStreamRandomCases + 5; |
| 578 |
| 579 |
| 580 static Handle<String> BuildConsStrings(int testCase, |
| 581 ConsStringGenerationData* data) { |
| 582 // For random constructions, need to reset the generator. |
| 583 data->rng_.init(); |
| 584 for (int j = 0; j < testCase * 50; j++) { |
| 585 data->rng_.next(); |
| 586 } |
| 587 Handle<String> string; |
| 588 switch (testCase) { |
| 589 case 0: |
| 590 return ConstructBalanced(data->building_blocks_); |
| 591 case 1: |
| 592 return ConstructLeft(data->building_blocks_, DEEP_DEPTH); |
| 593 case 2: |
| 594 return ConstructRight(data->building_blocks_, DEEP_DEPTH); |
| 595 case 3: |
| 596 return ConstructLeft(data->building_blocks_, 10); |
| 597 case 4: |
| 598 return ConstructRight(data->building_blocks_, 10); |
| 599 case 5: |
| 600 return FACTORY->NewConsString( |
| 601 data->building_blocks_[0], data->building_blocks_[1]); |
| 602 default: |
| 603 if (testCase >= kCharacterStreamEdgeCases) { |
| 604 CHECK(false); |
| 605 return string; |
| 606 } |
| 607 // Random test case. |
| 608 data->Reset(); |
| 609 string = ConstructRandomString(data, 200); |
| 610 AssertNoAllocation no_alloc; |
| 611 VerifyConsString(string, data); |
| 612 #ifdef DEBUG |
| 613 printf( |
| 614 "%s: [%d], %s: [%d], %s: [%d], %s: [%d], %s: [%d], %s: [%d]\n", |
| 615 "leaves", data->stats_.leaves_, |
| 616 "empty", data->stats_.empty_leaves_, |
| 617 "chars", data->stats_.chars_, |
| 618 "lefts", data->stats_.left_traversals_, |
| 619 "rights", data->stats_.right_traversals_, |
| 620 "early_terminations", data->early_terminations_); |
| 621 #endif |
| 622 return string; |
| 623 } |
| 624 } |
| 625 |
| 626 |
| 627 static void VerifyCharacterStream( |
| 628 String* flat_string, String* cons_string) { |
| 629 // Do not want to test ConString traversal on flat string. |
| 630 CHECK(flat_string->IsFlat()); |
| 631 CHECK(!flat_string->IsConsString()); |
| 632 CHECK(cons_string->IsConsString()); |
| 633 // TODO(dcarney) Test stream reset as well. |
| 634 int length = flat_string->length(); |
| 635 // Iterate start search in multiple places in the string. |
| 636 int outer_iterations = length > 20 ? 20 : length; |
| 637 for (int j = 0; j <= outer_iterations; j++) { |
| 638 int offset = static_cast<double>(length)*j/outer_iterations; |
| 639 if (offset < 0) offset = 0; |
| 640 // Want to test the offset == length case. |
| 641 if (offset > length) offset = length; |
| 642 StringCharacterStream flat_stream( |
| 643 flat_string, (unsigned) offset, &cons_string_iterator_op_1); |
| 644 StringCharacterStream cons_stream( |
| 645 cons_string, (unsigned) offset, &cons_string_iterator_op_2); |
| 646 for (int i = offset; i < length; i++) { |
| 647 uint16_t c = flat_string->Get(i); |
| 648 CHECK(flat_stream.HasMore()); |
| 649 CHECK(cons_stream.HasMore()); |
| 650 CHECK_EQ(c, flat_stream.GetNext()); |
| 651 CHECK_EQ(c, cons_stream.GetNext()); |
| 652 } |
| 653 CHECK(!flat_stream.HasMore()); |
| 654 CHECK(!cons_stream.HasMore()); |
| 655 } |
| 656 } |
| 657 |
| 658 |
| 659 TEST(StringCharacterStreamEdgeCases) { |
| 660 printf("TestStringCharacterStreamEdgeCases\n"); |
| 661 InitializeVM(); |
| 662 Isolate* isolate = Isolate::Current(); |
| 663 HandleScope outer_scope(isolate); |
| 664 ZoneScope zone(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); |
| 665 ConsStringGenerationData data; |
| 666 for (int i = 0; i < kCharacterStreamEdgeCases; i++) { |
| 667 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); |
| 672 Handle<String> cons_string = BuildConsStrings(i, &data); |
| 673 Handle<String> flat_string = BuildConsStrings(i, &data); |
| 674 FlattenString(flat_string); |
| 675 AssertNoAllocation no_alloc; |
| 676 CHECK(flat_string->IsConsString() && flat_string->IsFlat()); |
| 677 VerifyCharacterStream(ConsString::cast(*flat_string)->first(), |
| 678 *cons_string); |
| 679 } |
| 680 } |
| 681 |
| 682 |
278 static const int DEEP_ASCII_DEPTH = 100000; | 683 static const int DEEP_ASCII_DEPTH = 100000; |
279 | 684 |
280 | 685 |
281 TEST(DeepAscii) { | 686 TEST(DeepAscii) { |
282 printf("TestDeepAscii\n"); | 687 printf("TestDeepAscii\n"); |
283 InitializeVM(); | 688 InitializeVM(); |
284 v8::HandleScope scope; | 689 v8::HandleScope scope; |
285 | 690 |
286 char* foo = NewArray<char>(DEEP_ASCII_DEPTH); | 691 char* foo = NewArray<char>(DEEP_ASCII_DEPTH); |
287 for (int i = 0; i < DEEP_ASCII_DEPTH; i++) { | 692 for (int i = 0; i < DEEP_ASCII_DEPTH; i++) { |
(...skipping 420 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
708 | 1113 |
709 v8::Local<v8::String> expected = v8_str("ascii\x80only\x80string\x80"); | 1114 v8::Local<v8::String> expected = v8_str("ascii\x80only\x80string\x80"); |
710 CHECK(expected->Equals(result)); | 1115 CHECK(expected->Equals(result)); |
711 } | 1116 } |
712 | 1117 |
713 | 1118 |
714 TEST(IsAscii) { | 1119 TEST(IsAscii) { |
715 CHECK(String::IsAscii(static_cast<char*>(NULL), 0)); | 1120 CHECK(String::IsAscii(static_cast<char*>(NULL), 0)); |
716 CHECK(String::IsAscii(static_cast<uc16*>(NULL), 0)); | 1121 CHECK(String::IsAscii(static_cast<uc16*>(NULL), 0)); |
717 } | 1122 } |
OLD | NEW |