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