1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 // Check that we can traverse very deep stacks of ConsStrings using
29 // StringCharacterStram. Check that Get(int) works on very deep stacks
30 // of ConsStrings. These operations may not be very fast, but they
31 // should be possible without getting errors due to too deep recursion.
32
33 #include <stdlib.h>
34
35 #include "v8.h"
36
37 #include "api.h"
38 #include "factory.h"
39 #include "objects.h"
40 #include "cctest.h"
41 #include "zone-inl.h"
42
43 // Adapted from http://en.wikipedia.org/wiki/Multiply-with-carry
44 class MyRandomNumberGenerator {
45 public:
MyRandomNumberGenerator()46 MyRandomNumberGenerator() {
47 init();
48 }
49
init(uint32_t seed=0x5688c73e)50 void init(uint32_t seed = 0x5688c73e) {
51 static const uint32_t phi = 0x9e3779b9;
52 c = 362436;
53 i = kQSize-1;
54 Q[0] = seed;
55 Q[1] = seed + phi;
56 Q[2] = seed + phi + phi;
57 for (unsigned j = 3; j < kQSize; j++) {
58 Q[j] = Q[j - 3] ^ Q[j - 2] ^ phi ^ j;
59 }
60 }
61
next()62 uint32_t next() {
63 uint64_t a = 18782;
64 uint32_t r = 0xfffffffe;
65 i = (i + 1) & (kQSize-1);
66 uint64_t t = a * Q[i] + c;
67 c = (t >> 32);
68 uint32_t x = static_cast<uint32_t>(t + c);
69 if (x < c) {
70 x++;
71 c++;
72 }
73 return (Q[i] = r - x);
74 }
75
next(int max)76 uint32_t next(int max) {
77 return next() % max;
78 }
79
next(double threshold)80 bool next(double threshold) {
81 ASSERT(threshold >= 0.0 && threshold <= 1.0);
82 if (threshold == 1.0) return true;
83 if (threshold == 0.0) return false;
84 uint32_t value = next() % 100000;
85 return threshold > static_cast<double>(value)/100000.0;
86 }
87
88 private:
89 static const uint32_t kQSize = 4096;
90 uint32_t Q[kQSize];
91 uint32_t c;
92 uint32_t i;
93 };
94
95
96 using namespace v8::internal;
97
98
99 static const int DEEP_DEPTH = 8 * 1024;
100 static const int SUPER_DEEP_DEPTH = 80 * 1024;
101
102
103 class Resource: public v8::String::ExternalStringResource,
104 public ZoneObject {
105 public:
Resource(Vector<const uc16> string)106 explicit Resource(Vector<const uc16> string): data_(string.start()) {
107 length_ = string.length();
108 }
data() const109 virtual const uint16_t* data() const { return data_; }
length() const110 virtual size_t length() const { return length_; }
111
112 private:
113 const uc16* data_;
114 size_t length_;
115 };
116
117
118 class AsciiResource: public v8::String::ExternalAsciiStringResource,
119 public ZoneObject {
120 public:
AsciiResource(Vector<const char> string)121 explicit AsciiResource(Vector<const char> string): data_(string.start()) {
122 length_ = string.length();
123 }
data() const124 virtual const char* data() const { return data_; }
length() const125 virtual size_t length() const { return length_; }
126
127 private:
128 const char* data_;
129 size_t length_;
130 };
131
132
InitializeBuildingBlocks(Handle<String> * building_blocks,int bb_length,bool long_blocks,MyRandomNumberGenerator * rng,Zone * zone)133 static void InitializeBuildingBlocks(Handle<String>* building_blocks,
134 int bb_length,
135 bool long_blocks,
136 MyRandomNumberGenerator* rng,
137 Zone* zone) {
138 // A list of pointers that we don't have any interest in cleaning up.
139 // If they are reachable from a root then leak detection won't complain.
140 Isolate* isolate = CcTest::i_isolate();
141 Factory* factory = isolate->factory();
142 for (int i = 0; i < bb_length; i++) {
143 int len = rng->next(16);
144 int slice_head_chars = 0;
145 int slice_tail_chars = 0;
146 int slice_depth = 0;
147 for (int j = 0; j < 3; j++) {
148 if (rng->next(0.35)) slice_depth++;
149 }
150 // Must truncate something for a slice string. Loop until
151 // at least one end will be sliced.
152 while (slice_head_chars == 0 && slice_tail_chars == 0) {
153 slice_head_chars = rng->next(15);
154 slice_tail_chars = rng->next(12);
155 }
156 if (long_blocks) {
157 // Generate building blocks which will never be merged
158 len += ConsString::kMinLength + 1;
159 } else if (len > 14) {
160 len += 1234;
161 }
162 // Don't slice 0 length strings.
163 if (len == 0) slice_depth = 0;
164 int slice_length = slice_depth*(slice_head_chars + slice_tail_chars);
165 len += slice_length;
166 switch (rng->next(4)) {
167 case 0: {
168 uc16 buf[2000];
169 for (int j = 0; j < len; j++) {
170 buf[j] = rng->next(0x10000);
171 }
172 building_blocks[i] =
173 factory->NewStringFromTwoByte(Vector<const uc16>(buf, len));
174 for (int j = 0; j < len; j++) {
175 CHECK_EQ(buf[j], building_blocks[i]->Get(j));
176 }
177 break;
178 }
179 case 1: {
180 char buf[2000];
181 for (int j = 0; j < len; j++) {
182 buf[j] = rng->next(0x80);
183 }
184 building_blocks[i] =
185 factory->NewStringFromAscii(Vector<const char>(buf, len));
186 for (int j = 0; j < len; j++) {
187 CHECK_EQ(buf[j], building_blocks[i]->Get(j));
188 }
189 break;
190 }
191 case 2: {
192 uc16* buf = zone->NewArray<uc16>(len);
193 for (int j = 0; j < len; j++) {
194 buf[j] = rng->next(0x10000);
195 }
196 Resource* resource = new(zone) Resource(Vector<const uc16>(buf, len));
197 building_blocks[i] = factory->NewExternalStringFromTwoByte(resource);
198 for (int j = 0; j < len; j++) {
199 CHECK_EQ(buf[j], building_blocks[i]->Get(j));
200 }
201 break;
202 }
203 case 3: {
204 char* buf = zone->NewArray<char>(len);
205 for (int j = 0; j < len; j++) {
206 buf[j] = rng->next(0x80);
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 }
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);
224 }
225 }
226
227
228 class ConsStringStats {
229 public:
ConsStringStats()230 ConsStringStats() {
231 Reset();
232 }
233 void Reset();
234 void VerifyEqual(const ConsStringStats& that) const;
235 unsigned leaves_;
236 unsigned empty_leaves_;
237 unsigned chars_;
238 unsigned left_traversals_;
239 unsigned right_traversals_;
240 private:
241 DISALLOW_COPY_AND_ASSIGN(ConsStringStats);
242 };
243
244
Reset()245 void ConsStringStats::Reset() {
246 leaves_ = 0;
247 empty_leaves_ = 0;
248 chars_ = 0;
249 left_traversals_ = 0;
250 right_traversals_ = 0;
251 }
252
253
VerifyEqual(const ConsStringStats & that) const254 void ConsStringStats::VerifyEqual(const ConsStringStats& that) const {
255 CHECK(this->leaves_ == that.leaves_);
256 CHECK(this->empty_leaves_ == that.empty_leaves_);
257 CHECK(this->chars_ == that.chars_);
258 CHECK(this->left_traversals_ == that.left_traversals_);
259 CHECK(this->right_traversals_ == that.right_traversals_);
260 }
261
262
263 class ConsStringGenerationData {
264 public:
265 static const int kNumberOfBuildingBlocks = 256;
266 ConsStringGenerationData(bool long_blocks, Zone* zone);
267 void Reset();
268 inline Handle<String> block(int offset);
269 inline Handle<String> block(uint32_t offset);
270 // Input variables.
271 double early_termination_threshold_;
272 double leftness_;
273 double rightness_;
274 double empty_leaf_threshold_;
275 unsigned max_leaves_;
276 // Cached data.
277 Handle<String> building_blocks_[kNumberOfBuildingBlocks];
278 String* empty_string_;
279 MyRandomNumberGenerator rng_;
280 // Stats.
281 ConsStringStats stats_;
282 unsigned early_terminations_;
283 private:
284 DISALLOW_COPY_AND_ASSIGN(ConsStringGenerationData);
285 };
286
287
ConsStringGenerationData(bool long_blocks,Zone * zone)288 ConsStringGenerationData::ConsStringGenerationData(bool long_blocks,
289 Zone* zone) {
290 rng_.init();
291 InitializeBuildingBlocks(
292 building_blocks_, kNumberOfBuildingBlocks, long_blocks, &rng_, zone);
293 empty_string_ = CcTest::heap()->empty_string();
294 Reset();
295 }
296
297
block(uint32_t offset)298 Handle<String> ConsStringGenerationData::block(uint32_t offset) {
299 return building_blocks_[offset % kNumberOfBuildingBlocks ];
300 }
301
302
block(int offset)303 Handle<String> ConsStringGenerationData::block(int offset) {
304 CHECK_GE(offset, 0);
305 return building_blocks_[offset % kNumberOfBuildingBlocks];
306 }
307
308
Reset()309 void ConsStringGenerationData::Reset() {
310 early_termination_threshold_ = 0.01;
311 leftness_ = 0.75;
312 rightness_ = 0.75;
313 empty_leaf_threshold_ = 0.02;
314 max_leaves_ = 1000;
315 stats_.Reset();
316 early_terminations_ = 0;
317 rng_.init();
318 }
319
320
AccumulateStats(ConsString * cons_string,ConsStringStats * stats)321 void AccumulateStats(ConsString* cons_string, ConsStringStats* stats) {
322 int left_length = cons_string->first()->length();
323 int right_length = cons_string->second()->length();
324 CHECK(cons_string->length() == left_length + right_length);
325 // Check left side.
326 bool left_is_cons = cons_string->first()->IsConsString();
327 if (left_is_cons) {
328 stats->left_traversals_++;
329 AccumulateStats(ConsString::cast(cons_string->first()), stats);
330 } else {
331 CHECK_NE(left_length, 0);
332 stats->leaves_++;
333 stats->chars_ += left_length;
334 }
335 // Check right side.
336 if (cons_string->second()->IsConsString()) {
337 stats->right_traversals_++;
338 AccumulateStats(ConsString::cast(cons_string->second()), stats);
339 } else {
340 if (right_length == 0) {
341 stats->empty_leaves_++;
342 CHECK(!left_is_cons);
343 }
344 stats->leaves_++;
345 stats->chars_ += right_length;
346 }
347 }
348
349
AccumulateStats(Handle<String> cons_string,ConsStringStats * stats)350 void AccumulateStats(Handle<String> cons_string, ConsStringStats* stats) {
351 DisallowHeapAllocation no_allocation;
352 if (cons_string->IsConsString()) {
353 return AccumulateStats(ConsString::cast(*cons_string), stats);
354 }
355 // This string got flattened by gc.
356 stats->chars_ += cons_string->length();
357 }
358
359
AccumulateStatsWithOperator(ConsString * cons_string,ConsStringStats * stats)360 void AccumulateStatsWithOperator(
361 ConsString* cons_string, ConsStringStats* stats) {
362 unsigned offset = 0;
363 int32_t type = cons_string->map()->instance_type();
364 unsigned length = static_cast<unsigned>(cons_string->length());
365 ConsStringIteratorOp op;
366 String* string = op.Operate(cons_string, &offset, &type, &length);
367 CHECK(string != NULL);
368 while (true) {
369 ASSERT(!string->IsConsString());
370 // Accumulate stats.
371 stats->leaves_++;
372 stats->chars_ += string->length();
373 // Check for completion.
374 bool keep_going_fast_check = op.HasMore();
375 string = op.ContinueOperation(&type, &length);
376 if (string == NULL) return;
377 // Verify no false positives for fast check.
378 CHECK(keep_going_fast_check);
379 }
380 }
381
382
VerifyConsString(Handle<String> root,ConsStringGenerationData * data)383 void VerifyConsString(Handle<String> root, ConsStringGenerationData* data) {
384 // Verify basic data.
385 CHECK(root->IsConsString());
386 CHECK(static_cast<unsigned>(root->length()) == data->stats_.chars_);
387 // Recursive verify.
388 ConsStringStats stats;
389 AccumulateStats(ConsString::cast(*root), &stats);
390 stats.VerifyEqual(data->stats_);
391 // Iteratively verify.
392 stats.Reset();
393 AccumulateStatsWithOperator(ConsString::cast(*root), &stats);
394 // Don't see these. Must copy over.
395 stats.empty_leaves_ = data->stats_.empty_leaves_;
396 stats.left_traversals_ = data->stats_.left_traversals_;
397 stats.right_traversals_ = data->stats_.right_traversals_;
398 // Adjust total leaves to compensate.
399 stats.leaves_ += stats.empty_leaves_;
400 stats.VerifyEqual(data->stats_);
401 }
402
403
ConstructRandomString(ConsStringGenerationData * data,unsigned max_recursion)404 static Handle<String> ConstructRandomString(ConsStringGenerationData* data,
405 unsigned max_recursion) {
406 Factory* factory = CcTest::i_isolate()->factory();
407 // Compute termination characteristics.
408 bool terminate = false;
409 bool flat = data->rng_.next(data->empty_leaf_threshold_);
410 bool terminate_early = data->rng_.next(data->early_termination_threshold_);
411 if (terminate_early) data->early_terminations_++;
412 // The obvious condition.
413 terminate |= max_recursion == 0;
414 // Flat cons string terminate by definition.
415 terminate |= flat;
416 // Cap for max leaves.
417 terminate |= data->stats_.leaves_ >= data->max_leaves_;
418 // Roll the dice.
419 terminate |= terminate_early;
420 // Compute termination characteristics for each side.
421 bool terminate_left = terminate || !data->rng_.next(data->leftness_);
422 bool terminate_right = terminate || !data->rng_.next(data->rightness_);
423 // Generate left string.
424 Handle<String> left;
425 if (terminate_left) {
426 left = data->block(data->rng_.next());
427 data->stats_.leaves_++;
428 data->stats_.chars_ += left->length();
429 } else {
430 data->stats_.left_traversals_++;
431 }
432 // Generate right string.
433 Handle<String> right;
434 if (terminate_right) {
435 right = data->block(data->rng_.next());
436 data->stats_.leaves_++;
437 data->stats_.chars_ += right->length();
438 } else {
439 data->stats_.right_traversals_++;
440 }
441 // Generate the necessary sub-nodes recursively.
442 if (!terminate_right) {
443 // Need to balance generation fairly.
444 if (!terminate_left && data->rng_.next(0.5)) {
445 left = ConstructRandomString(data, max_recursion - 1);
446 }
447 right = ConstructRandomString(data, max_recursion - 1);
448 }
449 if (!terminate_left && left.is_null()) {
450 left = ConstructRandomString(data, max_recursion - 1);
451 }
452 // Build the cons string.
453 Handle<String> root = factory->NewConsString(left, right);
454 CHECK(root->IsConsString() && !root->IsFlat());
455 // Special work needed for flat string.
456 if (flat) {
457 data->stats_.empty_leaves_++;
458 FlattenString(root);
459 CHECK(root->IsConsString() && root->IsFlat());
460 }
461 return root;
462 }
463
464
ConstructLeft(ConsStringGenerationData * data,int depth)465 static Handle<String> ConstructLeft(
466 ConsStringGenerationData* data,
467 int depth) {
468 Factory* factory = CcTest::i_isolate()->factory();
469 Handle<String> answer = factory->NewStringFromAscii(CStrVector(""));
470 data->stats_.leaves_++;
471 for (int i = 0; i < depth; i++) {
472 Handle<String> block = data->block(i);
473 Handle<String> next = factory->NewConsString(answer, block);
474 if (next->IsConsString()) data->stats_.leaves_++;
475 data->stats_.chars_ += block->length();
476 answer = next;
477 }
478 data->stats_.left_traversals_ = data->stats_.leaves_ - 2;
479 return answer;
480 }
481
482
ConstructRight(ConsStringGenerationData * data,int depth)483 static Handle<String> ConstructRight(
484 ConsStringGenerationData* data,
485 int depth) {
486 Factory* factory = CcTest::i_isolate()->factory();
487 Handle<String> answer = factory->NewStringFromAscii(CStrVector(""));
488 data->stats_.leaves_++;
489 for (int i = depth - 1; i >= 0; i--) {
490 Handle<String> block = data->block(i);
491 Handle<String> next = factory->NewConsString(block, answer);
492 if (next->IsConsString()) data->stats_.leaves_++;
493 data->stats_.chars_ += block->length();
494 answer = next;
495 }
496 data->stats_.right_traversals_ = data->stats_.leaves_ - 2;
497 return answer;
498 }
499
500
ConstructBalancedHelper(ConsStringGenerationData * data,int from,int to)501 static Handle<String> ConstructBalancedHelper(
502 ConsStringGenerationData* data,
503 int from,
504 int to) {
505 Factory* factory = CcTest::i_isolate()->factory();
506 CHECK(to > from);
507 if (to - from == 1) {
508 data->stats_.chars_ += data->block(from)->length();
509 return data->block(from);
510 }
511 if (to - from == 2) {
512 data->stats_.chars_ += data->block(from)->length();
513 data->stats_.chars_ += data->block(from+1)->length();
514 return factory->NewConsString(data->block(from), data->block(from+1));
515 }
516 Handle<String> part1 =
517 ConstructBalancedHelper(data, from, from + ((to - from) / 2));
518 Handle<String> part2 =
519 ConstructBalancedHelper(data, from + ((to - from) / 2), to);
520 if (part1->IsConsString()) data->stats_.left_traversals_++;
521 if (part2->IsConsString()) data->stats_.right_traversals_++;
522 return factory->NewConsString(part1, part2);
523 }
524
525
ConstructBalanced(ConsStringGenerationData * data,int depth=DEEP_DEPTH)526 static Handle<String> ConstructBalanced(
527 ConsStringGenerationData* data, int depth = DEEP_DEPTH) {
528 Handle<String> string = ConstructBalancedHelper(data, 0, depth);
529 data->stats_.leaves_ =
530 data->stats_.left_traversals_ + data->stats_.right_traversals_ + 2;
531 return string;
532 }
533
534
535 static ConsStringIteratorOp cons_string_iterator_op_1;
536 static ConsStringIteratorOp cons_string_iterator_op_2;
537
Traverse(Handle<String> s1,Handle<String> s2)538 static void Traverse(Handle<String> s1, Handle<String> s2) {
539 int i = 0;
540 StringCharacterStream character_stream_1(*s1, &cons_string_iterator_op_1);
541 StringCharacterStream character_stream_2(*s2, &cons_string_iterator_op_2);
542 while (character_stream_1.HasMore()) {
543 CHECK(character_stream_2.HasMore());
544 uint16_t c = character_stream_1.GetNext();
545 CHECK_EQ(c, character_stream_2.GetNext());
546 i++;
547 }
548 CHECK(!character_stream_1.HasMore());
549 CHECK(!character_stream_2.HasMore());
550 CHECK_EQ(s1->length(), i);
551 CHECK_EQ(s2->length(), i);
552 }
553
554
TraverseFirst(Handle<String> s1,Handle<String> s2,int chars)555 static void TraverseFirst(Handle<String> s1, Handle<String> s2, int chars) {
556 int i = 0;
557 StringCharacterStream character_stream_1(*s1, &cons_string_iterator_op_1);
558 StringCharacterStream character_stream_2(*s2, &cons_string_iterator_op_2);
559 while (character_stream_1.HasMore() && i < chars) {
560 CHECK(character_stream_2.HasMore());
561 uint16_t c = character_stream_1.GetNext();
562 CHECK_EQ(c, character_stream_2.GetNext());
563 i++;
564 }
565 s1->Get(s1->length() - 1);
566 s2->Get(s2->length() - 1);
567 }
568
569
TEST(Traverse)570 TEST(Traverse) {
571 printf("TestTraverse\n");
572 CcTest::InitializeVM();
573 v8::HandleScope scope(CcTest::isolate());
574 Zone zone(CcTest::i_isolate());
575 ConsStringGenerationData data(false, &zone);
576 Handle<String> flat = ConstructBalanced(&data);
577 FlattenString(flat);
578 Handle<String> left_asymmetric = ConstructLeft(&data, DEEP_DEPTH);
579 Handle<String> right_asymmetric = ConstructRight(&data, DEEP_DEPTH);
580 Handle<String> symmetric = ConstructBalanced(&data);
581 printf("1\n");
582 Traverse(flat, symmetric);
583 printf("2\n");
584 Traverse(flat, left_asymmetric);
585 printf("3\n");
586 Traverse(flat, right_asymmetric);
587 printf("4\n");
588 Handle<String> left_deep_asymmetric =
589 ConstructLeft(&data, SUPER_DEEP_DEPTH);
590 Handle<String> right_deep_asymmetric =
591 ConstructRight(&data, SUPER_DEEP_DEPTH);
592 printf("5\n");
593 TraverseFirst(left_asymmetric, left_deep_asymmetric, 1050);
594 printf("6\n");
595 TraverseFirst(left_asymmetric, right_deep_asymmetric, 65536);
596 printf("7\n");
597 FlattenString(left_asymmetric);
598 printf("10\n");
599 Traverse(flat, left_asymmetric);
600 printf("11\n");
601 FlattenString(right_asymmetric);
602 printf("12\n");
603 Traverse(flat, right_asymmetric);
604 printf("14\n");
605 FlattenString(symmetric);
606 printf("15\n");
607 Traverse(flat, symmetric);
608 printf("16\n");
609 FlattenString(left_deep_asymmetric);
610 printf("18\n");
611 }
612
613
VerifyCharacterStream(String * flat_string,String * cons_string)614 static void VerifyCharacterStream(
615 String* flat_string, String* cons_string) {
616 // Do not want to test ConString traversal on flat string.
617 CHECK(flat_string->IsFlat() && !flat_string->IsConsString());
618 CHECK(cons_string->IsConsString());
619 // TODO(dcarney) Test stream reset as well.
620 int length = flat_string->length();
621 // Iterate start search in multiple places in the string.
622 int outer_iterations = length > 20 ? 20 : length;
623 for (int j = 0; j <= outer_iterations; j++) {
624 int offset = length * j / outer_iterations;
625 if (offset < 0) offset = 0;
626 // Want to test the offset == length case.
627 if (offset > length) offset = length;
628 StringCharacterStream flat_stream(
629 flat_string, &cons_string_iterator_op_1, static_cast<unsigned>(offset));
630 StringCharacterStream cons_stream(
631 cons_string, &cons_string_iterator_op_2, static_cast<unsigned>(offset));
632 for (int i = offset; i < length; i++) {
633 uint16_t c = flat_string->Get(i);
634 CHECK(flat_stream.HasMore());
635 CHECK(cons_stream.HasMore());
636 CHECK_EQ(c, flat_stream.GetNext());
637 CHECK_EQ(c, cons_stream.GetNext());
638 }
639 CHECK(!flat_stream.HasMore());
640 CHECK(!cons_stream.HasMore());
641 }
642 }
643
644
PrintStats(const ConsStringGenerationData & data)645 static inline void PrintStats(const ConsStringGenerationData& data) {
646 #ifdef DEBUG
647 printf(
648 "%s: [%d], %s: [%d], %s: [%d], %s: [%d], %s: [%d], %s: [%d]\n",
649 "leaves", data.stats_.leaves_,
650 "empty", data.stats_.empty_leaves_,
651 "chars", data.stats_.chars_,
652 "lefts", data.stats_.left_traversals_,
653 "rights", data.stats_.right_traversals_,
654 "early_terminations", data.early_terminations_);
655 #endif
656 }
657
658
659 template<typename BuildString>
TestStringCharacterStream(BuildString build,int test_cases)660 void TestStringCharacterStream(BuildString build, int test_cases) {
661 CcTest::InitializeVM();
662 Isolate* isolate = CcTest::i_isolate();
663 HandleScope outer_scope(isolate);
664 Zone zone(isolate);
665 ConsStringGenerationData data(true, &zone);
666 for (int i = 0; i < test_cases; i++) {
667 printf("%d\n", i);
668 HandleScope inner_scope(isolate);
669 AlwaysAllocateScope always_allocate;
670 // Build flat version of cons string.
671 Handle<String> flat_string = build(i, &data);
672 ConsStringStats flat_string_stats;
673 AccumulateStats(flat_string, &flat_string_stats);
674 // Flatten string.
675 FlattenString(flat_string);
676 // Build unflattened version of cons string to test.
677 Handle<String> cons_string = build(i, &data);
678 ConsStringStats cons_string_stats;
679 AccumulateStats(cons_string, &cons_string_stats);
680 DisallowHeapAllocation no_allocation;
681 PrintStats(data);
682 // Full verify of cons string.
683 cons_string_stats.VerifyEqual(flat_string_stats);
684 cons_string_stats.VerifyEqual(data.stats_);
685 VerifyConsString(cons_string, &data);
686 String* flat_string_ptr =
687 flat_string->IsConsString() ?
688 ConsString::cast(*flat_string)->first() :
689 *flat_string;
690 VerifyCharacterStream(flat_string_ptr, *cons_string);
691 }
692 }
693
694
695 static const int kCharacterStreamNonRandomCases = 8;
696
697
BuildEdgeCaseConsString(int test_case,ConsStringGenerationData * data)698 static Handle<String> BuildEdgeCaseConsString(
699 int test_case, ConsStringGenerationData* data) {
700 Factory* factory = CcTest::i_isolate()->factory();
701 data->Reset();
702 switch (test_case) {
703 case 0:
704 return ConstructBalanced(data, 71);
705 case 1:
706 return ConstructLeft(data, 71);
707 case 2:
708 return ConstructRight(data, 71);
709 case 3:
710 return ConstructLeft(data, 10);
711 case 4:
712 return ConstructRight(data, 10);
713 case 5:
714 // 2 element balanced tree.
715 data->stats_.chars_ += data->block(0)->length();
716 data->stats_.chars_ += data->block(1)->length();
717 data->stats_.leaves_ += 2;
718 return factory->NewConsString(data->block(0), data->block(1));
719 case 6:
720 // Simple flattened tree.
721 data->stats_.chars_ += data->block(0)->length();
722 data->stats_.chars_ += data->block(1)->length();
723 data->stats_.leaves_ += 2;
724 data->stats_.empty_leaves_ += 1;
725 {
726 Handle<String> string =
727 factory->NewConsString(data->block(0), data->block(1));
728 FlattenString(string);
729 return string;
730 }
731 case 7:
732 // Left node flattened.
733 data->stats_.chars_ += data->block(0)->length();
734 data->stats_.chars_ += data->block(1)->length();
735 data->stats_.chars_ += data->block(2)->length();
736 data->stats_.leaves_ += 3;
737 data->stats_.empty_leaves_ += 1;
738 data->stats_.left_traversals_ += 1;
739 {
740 Handle<String> left =
741 factory->NewConsString(data->block(0), data->block(1));
742 FlattenString(left);
743 return factory->NewConsString(left, data->block(2));
744 }
745 case 8:
746 // Left node and right node flattened.
747 data->stats_.chars_ += data->block(0)->length();
748 data->stats_.chars_ += data->block(1)->length();
749 data->stats_.chars_ += data->block(2)->length();
750 data->stats_.chars_ += data->block(3)->length();
751 data->stats_.leaves_ += 4;
752 data->stats_.empty_leaves_ += 2;
753 data->stats_.left_traversals_ += 1;
754 data->stats_.right_traversals_ += 1;
755 {
756 Handle<String> left =
757 factory->NewConsString(data->block(0), data->block(1));
758 FlattenString(left);
759 Handle<String> right =
760 factory->NewConsString(data->block(2), data->block(2));
761 FlattenString(right);
762 return factory->NewConsString(left, right);
763 }
764 }
765 UNREACHABLE();
766 return Handle<String>();
767 }
768
769
TEST(StringCharacterStreamEdgeCases)770 TEST(StringCharacterStreamEdgeCases) {
771 printf("TestStringCharacterStreamEdgeCases\n");
772 TestStringCharacterStream(
773 BuildEdgeCaseConsString, kCharacterStreamNonRandomCases);
774 }
775
776
777 static const int kBalances = 3;
778 static const int kTreeLengths = 4;
779 static const int kEmptyLeaves = 4;
780 static const int kUniqueRandomParameters =
781 kBalances*kTreeLengths*kEmptyLeaves;
782
783
InitializeGenerationData(int test_case,ConsStringGenerationData * data)784 static void InitializeGenerationData(
785 int test_case, ConsStringGenerationData* data) {
786 // Clear the settings and reinit the rng.
787 data->Reset();
788 // Spin up the rng to a known location that is unique per test.
789 static const int kPerTestJump = 501;
790 for (int j = 0; j < test_case*kPerTestJump; j++) {
791 data->rng_.next();
792 }
793 // Choose balanced, left or right heavy trees.
794 switch (test_case % kBalances) {
795 case 0:
796 // Nothing to do. Already balanced.
797 break;
798 case 1:
799 // Left balanced.
800 data->leftness_ = 0.90;
801 data->rightness_ = 0.15;
802 break;
803 case 2:
804 // Right balanced.
805 data->leftness_ = 0.15;
806 data->rightness_ = 0.90;
807 break;
808 default:
809 UNREACHABLE();
810 break;
811 }
812 // Must remove the influence of the above decision.
813 test_case /= kBalances;
814 // Choose tree length.
815 switch (test_case % kTreeLengths) {
816 case 0:
817 data->max_leaves_ = 16;
818 data->early_termination_threshold_ = 0.2;
819 break;
820 case 1:
821 data->max_leaves_ = 50;
822 data->early_termination_threshold_ = 0.05;
823 break;
824 case 2:
825 data->max_leaves_ = 500;
826 data->early_termination_threshold_ = 0.03;
827 break;
828 case 3:
829 data->max_leaves_ = 5000;
830 data->early_termination_threshold_ = 0.001;
831 break;
832 default:
833 UNREACHABLE();
834 break;
835 }
836 // Must remove the influence of the above decision.
837 test_case /= kTreeLengths;
838 // Choose how much we allow empty nodes, including not at all.
839 data->empty_leaf_threshold_ =
840 0.03 * static_cast<double>(test_case % kEmptyLeaves);
841 }
842
843
BuildRandomConsString(int test_case,ConsStringGenerationData * data)844 static Handle<String> BuildRandomConsString(
845 int test_case, ConsStringGenerationData* data) {
846 InitializeGenerationData(test_case, data);
847 return ConstructRandomString(data, 200);
848 }
849
850
TEST(StringCharacterStreamRandom)851 TEST(StringCharacterStreamRandom) {
852 printf("StringCharacterStreamRandom\n");
853 TestStringCharacterStream(BuildRandomConsString, kUniqueRandomParameters*7);
854 }
855
856
857 static const int DEEP_ASCII_DEPTH = 100000;
858
859
TEST(DeepAscii)860 TEST(DeepAscii) {
861 printf("TestDeepAscii\n");
862 CcTest::InitializeVM();
863 Factory* factory = CcTest::i_isolate()->factory();
864 v8::HandleScope scope(CcTest::isolate());
865
866 char* foo = NewArray<char>(DEEP_ASCII_DEPTH);
867 for (int i = 0; i < DEEP_ASCII_DEPTH; i++) {
868 foo[i] = "foo "[i % 4];
869 }
870 Handle<String> string =
871 factory->NewStringFromAscii(Vector<const char>(foo, DEEP_ASCII_DEPTH));
872 Handle<String> foo_string = factory->NewStringFromAscii(CStrVector("foo"));
873 for (int i = 0; i < DEEP_ASCII_DEPTH; i += 10) {
874 string = factory->NewConsString(string, foo_string);
875 }
876 Handle<String> flat_string = factory->NewConsString(string, foo_string);
877 FlattenString(flat_string);
878
879 for (int i = 0; i < 500; i++) {
880 TraverseFirst(flat_string, string, DEEP_ASCII_DEPTH);
881 }
882 DeleteArray<char>(foo);
883 }
884
885
TEST(Utf8Conversion)886 TEST(Utf8Conversion) {
887 // Smoke test for converting strings to utf-8.
888 CcTest::InitializeVM();
889 v8::HandleScope handle_scope(CcTest::isolate());
890 // A simple ascii string
891 const char* ascii_string = "abcdef12345";
892 int len = v8::String::NewFromUtf8(CcTest::isolate(), ascii_string,
893 v8::String::kNormalString,
894 StrLength(ascii_string))->Utf8Length();
895 CHECK_EQ(StrLength(ascii_string), len);
896 // A mixed ascii and non-ascii string
897 // U+02E4 -> CB A4
898 // U+0064 -> 64
899 // U+12E4 -> E1 8B A4
900 // U+0030 -> 30
901 // U+3045 -> E3 81 85
902 const uint16_t mixed_string[] = {0x02E4, 0x0064, 0x12E4, 0x0030, 0x3045};
903 // The characters we expect to be output
904 const unsigned char as_utf8[11] = {0xCB, 0xA4, 0x64, 0xE1, 0x8B, 0xA4, 0x30,
905 0xE3, 0x81, 0x85, 0x00};
906 // The number of bytes expected to be written for each length
907 const int lengths[12] = {0, 0, 2, 3, 3, 3, 6, 7, 7, 7, 10, 11};
908 const int char_lengths[12] = {0, 0, 1, 2, 2, 2, 3, 4, 4, 4, 5, 5};
909 v8::Handle<v8::String> mixed = v8::String::NewFromTwoByte(
910 CcTest::isolate(), mixed_string, v8::String::kNormalString, 5);
911 CHECK_EQ(10, mixed->Utf8Length());
912 // Try encoding the string with all capacities
913 char buffer[11];
914 const char kNoChar = static_cast<char>(-1);
915 for (int i = 0; i <= 11; i++) {
916 // Clear the buffer before reusing it
917 for (int j = 0; j < 11; j++)
918 buffer[j] = kNoChar;
919 int chars_written;
920 int written = mixed->WriteUtf8(buffer, i, &chars_written);
921 CHECK_EQ(lengths[i], written);
922 CHECK_EQ(char_lengths[i], chars_written);
923 // Check that the contents are correct
924 for (int j = 0; j < lengths[i]; j++)
925 CHECK_EQ(as_utf8[j], static_cast<unsigned char>(buffer[j]));
926 // Check that the rest of the buffer hasn't been touched
927 for (int j = lengths[i]; j < 11; j++)
928 CHECK_EQ(kNoChar, buffer[j]);
929 }
930 }
931
932
TEST(ExternalShortStringAdd)933 TEST(ExternalShortStringAdd) {
934 Isolate* isolate = CcTest::i_isolate();
935 Zone zone(isolate);
936
937 LocalContext context;
938 v8::HandleScope handle_scope(CcTest::isolate());
939
940 // Make sure we cover all always-flat lengths and at least one above.
941 static const int kMaxLength = 20;
942 CHECK_GT(kMaxLength, i::ConsString::kMinLength);
943
944 // Allocate two JavaScript arrays for holding short strings.
945 v8::Handle<v8::Array> ascii_external_strings =
946 v8::Array::New(CcTest::isolate(), kMaxLength + 1);
947 v8::Handle<v8::Array> non_ascii_external_strings =
948 v8::Array::New(CcTest::isolate(), kMaxLength + 1);
949
950 // Generate short ascii and non-ascii external strings.
951 for (int i = 0; i <= kMaxLength; i++) {
952 char* ascii = zone.NewArray<char>(i + 1);
953 for (int j = 0; j < i; j++) {
954 ascii[j] = 'a';
955 }
956 // Terminating '\0' is left out on purpose. It is not required for external
957 // string data.
958 AsciiResource* ascii_resource =
959 new(&zone) AsciiResource(Vector<const char>(ascii, i));
960 v8::Local<v8::String> ascii_external_string =
961 v8::String::NewExternal(CcTest::isolate(), ascii_resource);
962
963 ascii_external_strings->Set(v8::Integer::New(i), ascii_external_string);
964 uc16* non_ascii = zone.NewArray<uc16>(i + 1);
965 for (int j = 0; j < i; j++) {
966 non_ascii[j] = 0x1234;
967 }
968 // Terminating '\0' is left out on purpose. It is not required for external
969 // string data.
970 Resource* resource = new(&zone) Resource(Vector<const uc16>(non_ascii, i));
971 v8::Local<v8::String> non_ascii_external_string =
972 v8::String::NewExternal(CcTest::isolate(), resource);
973 non_ascii_external_strings->Set(v8::Integer::New(i),
974 non_ascii_external_string);
975 }
976
977 // Add the arrays with the short external strings in the global object.
978 v8::Handle<v8::Object> global = context->Global();
979 global->Set(v8_str("external_ascii"), ascii_external_strings);
980 global->Set(v8_str("external_non_ascii"), non_ascii_external_strings);
981 global->Set(v8_str("max_length"), v8::Integer::New(kMaxLength));
982
983 // Add short external ascii and non-ascii strings checking the result.
984 static const char* source =
985 "function test() {"
986 " var ascii_chars = 'aaaaaaaaaaaaaaaaaaaa';"
987 " var non_ascii_chars = '\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234';" //NOLINT
988 " if (ascii_chars.length != max_length) return 1;"
989 " if (non_ascii_chars.length != max_length) return 2;"
990 " var ascii = Array(max_length + 1);"
991 " var non_ascii = Array(max_length + 1);"
992 " for (var i = 0; i <= max_length; i++) {"
993 " ascii[i] = ascii_chars.substring(0, i);"
994 " non_ascii[i] = non_ascii_chars.substring(0, i);"
995 " };"
996 " for (var i = 0; i <= max_length; i++) {"
997 " if (ascii[i] != external_ascii[i]) return 3;"
998 " if (non_ascii[i] != external_non_ascii[i]) return 4;"
999 " for (var j = 0; j < i; j++) {"
1000 " if (external_ascii[i] !="
1001 " (external_ascii[j] + external_ascii[i - j])) return 5;"
1002 " if (external_non_ascii[i] !="
1003 " (external_non_ascii[j] + external_non_ascii[i - j])) return 6;"
1004 " if (non_ascii[i] != (non_ascii[j] + non_ascii[i - j])) return 7;"
1005 " if (ascii[i] != (ascii[j] + ascii[i - j])) return 8;"
1006 " if (ascii[i] != (external_ascii[j] + ascii[i - j])) return 9;"
1007 " if (ascii[i] != (ascii[j] + external_ascii[i - j])) return 10;"
1008 " if (non_ascii[i] !="
1009 " (external_non_ascii[j] + non_ascii[i - j])) return 11;"
1010 " if (non_ascii[i] !="
1011 " (non_ascii[j] + external_non_ascii[i - j])) return 12;"
1012 " }"
1013 " }"
1014 " return 0;"
1015 "};"
1016 "test()";
1017 CHECK_EQ(0, CompileRun(source)->Int32Value());
1018 }
1019
1020
TEST(JSONStringifySliceMadeExternal)1021 TEST(JSONStringifySliceMadeExternal) {
1022 CcTest::InitializeVM();
1023 Isolate* isolate = CcTest::i_isolate();
1024 Zone zone(isolate);
1025 // Create a sliced string from a one-byte string. The latter is turned
1026 // into a two-byte external string. Check that JSON.stringify works.
1027 v8::HandleScope handle_scope(CcTest::isolate());
1028 v8::Handle<v8::String> underlying =
1029 CompileRun("var underlying = 'abcdefghijklmnopqrstuvwxyz';"
1030 "underlying")->ToString();
1031 v8::Handle<v8::String> slice =
1032 CompileRun("var slice = underlying.slice(1);"
1033 "slice")->ToString();
1034 CHECK(v8::Utils::OpenHandle(*slice)->IsSlicedString());
1035 CHECK(v8::Utils::OpenHandle(*underlying)->IsSeqOneByteString());
1036
1037 int length = underlying->Length();
1038 uc16* two_byte = zone.NewArray<uc16>(length + 1);
1039 underlying->Write(two_byte);
1040 Resource* resource =
1041 new(&zone) Resource(Vector<const uc16>(two_byte, length));
1042 CHECK(underlying->MakeExternal(resource));
1043 CHECK(v8::Utils::OpenHandle(*slice)->IsSlicedString());
1044 CHECK(v8::Utils::OpenHandle(*underlying)->IsExternalTwoByteString());
1045
1046 CHECK_EQ("\"bcdefghijklmnopqrstuvwxyz\"",
1047 *v8::String::Utf8Value(CompileRun("JSON.stringify(slice)")));
1048 }
1049
1050
TEST(CachedHashOverflow)1051 TEST(CachedHashOverflow) {
1052 CcTest::InitializeVM();
1053 // We incorrectly allowed strings to be tagged as array indices even if their
1054 // values didn't fit in the hash field.
1055 // See http://code.google.com/p/v8/issues/detail?id=728
1056 Isolate* isolate = CcTest::i_isolate();
1057 Zone zone(isolate);
1058
1059 v8::HandleScope handle_scope(CcTest::isolate());
1060 // Lines must be executed sequentially. Combining them into one script
1061 // makes the bug go away.
1062 const char* lines[] = {
1063 "var x = [];",
1064 "x[4] = 42;",
1065 "var s = \"1073741828\";",
1066 "x[s];",
1067 "x[s] = 37;",
1068 "x[4];",
1069 "x[s];",
1070 NULL
1071 };
1072
1073 Handle<Smi> fortytwo(Smi::FromInt(42), isolate);
1074 Handle<Smi> thirtyseven(Smi::FromInt(37), isolate);
1075 Handle<Object> results[] = { isolate->factory()->undefined_value(),
1076 fortytwo,
1077 isolate->factory()->undefined_value(),
1078 isolate->factory()->undefined_value(),
1079 thirtyseven,
1080 fortytwo,
1081 thirtyseven // Bug yielded 42 here.
1082 };
1083
1084 const char* line;
1085 for (int i = 0; (line = lines[i]); i++) {
1086 printf("%s\n", line);
1087 v8::Local<v8::Value> result = v8::Script::Compile(
1088 v8::String::NewFromUtf8(CcTest::isolate(), line))->Run();
1089 CHECK_EQ(results[i]->IsUndefined(), result->IsUndefined());
1090 CHECK_EQ(results[i]->IsNumber(), result->IsNumber());
1091 if (result->IsNumber()) {
1092 CHECK_EQ(Smi::cast(results[i]->ToSmi()->ToObjectChecked())->value(),
1093 result->ToInt32()->Value());
1094 }
1095 }
1096 }
1097
1098
TEST(SliceFromCons)1099 TEST(SliceFromCons) {
1100 FLAG_string_slices = true;
1101 CcTest::InitializeVM();
1102 Factory* factory = CcTest::i_isolate()->factory();
1103 v8::HandleScope scope(CcTest::isolate());
1104 Handle<String> string =
1105 factory->NewStringFromAscii(CStrVector("parentparentparent"));
1106 Handle<String> parent = factory->NewConsString(string, string);
1107 CHECK(parent->IsConsString());
1108 CHECK(!parent->IsFlat());
1109 Handle<String> slice = factory->NewSubString(parent, 1, 25);
1110 // After slicing, the original string becomes a flat cons.
1111 CHECK(parent->IsFlat());
1112 CHECK(slice->IsSlicedString());
1113 CHECK_EQ(SlicedString::cast(*slice)->parent(),
1114 // Parent could have been short-circuited.
1115 parent->IsConsString() ? ConsString::cast(*parent)->first()
1116 : *parent);
1117 CHECK(SlicedString::cast(*slice)->parent()->IsSeqString());
1118 CHECK(slice->IsFlat());
1119 }
1120
1121
1122 class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
1123 public:
AsciiVectorResource(i::Vector<const char> vector)1124 explicit AsciiVectorResource(i::Vector<const char> vector)
1125 : data_(vector) {}
~AsciiVectorResource()1126 virtual ~AsciiVectorResource() {}
length() const1127 virtual size_t length() const { return data_.length(); }
data() const1128 virtual const char* data() const { return data_.start(); }
1129 private:
1130 i::Vector<const char> data_;
1131 };
1132
1133
TEST(SliceFromExternal)1134 TEST(SliceFromExternal) {
1135 FLAG_string_slices = true;
1136 CcTest::InitializeVM();
1137 Factory* factory = CcTest::i_isolate()->factory();
1138 v8::HandleScope scope(CcTest::isolate());
1139 AsciiVectorResource resource(
1140 i::Vector<const char>("abcdefghijklmnopqrstuvwxyz", 26));
1141 Handle<String> string = factory->NewExternalStringFromAscii(&resource);
1142 CHECK(string->IsExternalString());
1143 Handle<String> slice = factory->NewSubString(string, 1, 25);
1144 CHECK(slice->IsSlicedString());
1145 CHECK(string->IsExternalString());
1146 CHECK_EQ(SlicedString::cast(*slice)->parent(), *string);
1147 CHECK(SlicedString::cast(*slice)->parent()->IsExternalString());
1148 CHECK(slice->IsFlat());
1149 }
1150
1151
TEST(TrivialSlice)1152 TEST(TrivialSlice) {
1153 // This tests whether a slice that contains the entire parent string
1154 // actually creates a new string (it should not).
1155 FLAG_string_slices = true;
1156 CcTest::InitializeVM();
1157 Factory* factory = CcTest::i_isolate()->factory();
1158 v8::HandleScope scope(CcTest::isolate());
1159 v8::Local<v8::Value> result;
1160 Handle<String> string;
1161 const char* init = "var str = 'abcdefghijklmnopqrstuvwxyz';";
1162 const char* check = "str.slice(0,26)";
1163 const char* crosscheck = "str.slice(1,25)";
1164
1165 CompileRun(init);
1166
1167 result = CompileRun(check);
1168 CHECK(result->IsString());
1169 string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1170 CHECK(!string->IsSlicedString());
1171
1172 string = factory->NewSubString(string, 0, 26);
1173 CHECK(!string->IsSlicedString());
1174 result = CompileRun(crosscheck);
1175 CHECK(result->IsString());
1176 string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1177 CHECK(string->IsSlicedString());
1178 CHECK_EQ("bcdefghijklmnopqrstuvwxy", *(string->ToCString()));
1179 }
1180
1181
TEST(SliceFromSlice)1182 TEST(SliceFromSlice) {
1183 // This tests whether a slice that contains the entire parent string
1184 // actually creates a new string (it should not).
1185 FLAG_string_slices = true;
1186 CcTest::InitializeVM();
1187 v8::HandleScope scope(CcTest::isolate());
1188 v8::Local<v8::Value> result;
1189 Handle<String> string;
1190 const char* init = "var str = 'abcdefghijklmnopqrstuvwxyz';";
1191 const char* slice = "var slice = str.slice(1,-1); slice";
1192 const char* slice_from_slice = "slice.slice(1,-1);";
1193
1194 CompileRun(init);
1195 result = CompileRun(slice);
1196 CHECK(result->IsString());
1197 string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1198 CHECK(string->IsSlicedString());
1199 CHECK(SlicedString::cast(*string)->parent()->IsSeqString());
1200 CHECK_EQ("bcdefghijklmnopqrstuvwxy", *(string->ToCString()));
1201
1202 result = CompileRun(slice_from_slice);
1203 CHECK(result->IsString());
1204 string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1205 CHECK(string->IsSlicedString());
1206 CHECK(SlicedString::cast(*string)->parent()->IsSeqString());
1207 CHECK_EQ("cdefghijklmnopqrstuvwx", *(string->ToCString()));
1208 }
1209
1210
TEST(AsciiArrayJoin)1211 TEST(AsciiArrayJoin) {
1212 // Set heap limits.
1213 static const int K = 1024;
1214 v8::ResourceConstraints constraints;
1215 constraints.set_max_young_space_size(256 * K);
1216 constraints.set_max_old_space_size(4 * K * K);
1217 v8::SetResourceConstraints(CcTest::isolate(), &constraints);
1218
1219 // String s is made of 2^17 = 131072 'c' characters and a is an array
1220 // starting with 'bad', followed by 2^14 times the string s. That means the
1221 // total length of the concatenated strings is 2^31 + 3. So on 32bit systems
1222 // summing the lengths of the strings (as Smis) overflows and wraps.
1223 static const char* join_causing_out_of_memory =
1224 "var two_14 = Math.pow(2, 14);"
1225 "var two_17 = Math.pow(2, 17);"
1226 "var s = Array(two_17 + 1).join('c');"
1227 "var a = ['bad'];"
1228 "for (var i = 1; i <= two_14; i++) a.push(s);"
1229 "a.join("");";
1230
1231 v8::HandleScope scope(CcTest::isolate());
1232 LocalContext context;
1233 v8::V8::IgnoreOutOfMemoryException();
1234 v8::Local<v8::Script> script = v8::Script::Compile(
1235 v8::String::NewFromUtf8(CcTest::isolate(), join_causing_out_of_memory));
1236 v8::Local<v8::Value> result = script->Run();
1237
1238 // Check for out of memory state.
1239 CHECK(result.IsEmpty());
1240 CHECK(context->HasOutOfMemoryException());
1241 }
1242
1243
CheckException(const char * source)1244 static void CheckException(const char* source) {
1245 // An empty handle is returned upon exception.
1246 CHECK(CompileRun(source).IsEmpty());
1247 }
1248
1249
TEST(RobustSubStringStub)1250 TEST(RobustSubStringStub) {
1251 // This tests whether the SubStringStub can handle unsafe arguments.
1252 // If not recognized, those unsafe arguments lead to out-of-bounds reads.
1253 FLAG_allow_natives_syntax = true;
1254 CcTest::InitializeVM();
1255 v8::HandleScope scope(CcTest::isolate());
1256 v8::Local<v8::Value> result;
1257 Handle<String> string;
1258 CompileRun("var short = 'abcdef';");
1259
1260 // Invalid indices.
1261 CheckException("%_SubString(short, 0, 10000);");
1262 CheckException("%_SubString(short, -1234, 5);");
1263 CheckException("%_SubString(short, 5, 2);");
1264 // Special HeapNumbers.
1265 CheckException("%_SubString(short, 1, Infinity);");
1266 CheckException("%_SubString(short, NaN, 5);");
1267 // String arguments.
1268 CheckException("%_SubString(short, '2', '5');");
1269 // Ordinary HeapNumbers can be handled (in runtime).
1270 result = CompileRun("%_SubString(short, Math.sqrt(4), 5.1);");
1271 string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1272 CHECK_EQ("cde", *(string->ToCString()));
1273
1274 CompileRun("var long = 'abcdefghijklmnopqrstuvwxyz';");
1275 // Invalid indices.
1276 CheckException("%_SubString(long, 0, 10000);");
1277 CheckException("%_SubString(long, -1234, 17);");
1278 CheckException("%_SubString(long, 17, 2);");
1279 // Special HeapNumbers.
1280 CheckException("%_SubString(long, 1, Infinity);");
1281 CheckException("%_SubString(long, NaN, 17);");
1282 // String arguments.
1283 CheckException("%_SubString(long, '2', '17');");
1284 // Ordinary HeapNumbers within bounds can be handled (in runtime).
1285 result = CompileRun("%_SubString(long, Math.sqrt(4), 17.1);");
1286 string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1287 CHECK_EQ("cdefghijklmnopq", *(string->ToCString()));
1288
1289 // Test that out-of-bounds substring of a slice fails when the indices
1290 // would have been valid for the underlying string.
1291 CompileRun("var slice = long.slice(1, 15);");
1292 CheckException("%_SubString(slice, 0, 17);");
1293 }
1294
1295
TEST(RegExpOverflow)1296 TEST(RegExpOverflow) {
1297 // Result string has the length 2^32, causing a 32-bit integer overflow.
1298 CcTest::InitializeVM();
1299 v8::HandleScope scope(CcTest::isolate());
1300 LocalContext context;
1301 v8::V8::IgnoreOutOfMemoryException();
1302 v8::Local<v8::Value> result = CompileRun(
1303 "var a = 'a'; "
1304 "for (var i = 0; i < 16; i++) { "
1305 " a += a; "
1306 "} "
1307 "a.replace(/a/g, a); ");
1308 CHECK(result.IsEmpty());
1309 CHECK(context->HasOutOfMemoryException());
1310 }
1311
1312
TEST(StringReplaceAtomTwoByteResult)1313 TEST(StringReplaceAtomTwoByteResult) {
1314 CcTest::InitializeVM();
1315 v8::HandleScope scope(CcTest::isolate());
1316 LocalContext context;
1317 v8::Local<v8::Value> result = CompileRun(
1318 "var subject = 'ascii~only~string~'; "
1319 "var replace = '\x80'; "
1320 "subject.replace(/~/g, replace); ");
1321 CHECK(result->IsString());
1322 Handle<String> string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1323 CHECK(string->IsSeqTwoByteString());
1324
1325 v8::Local<v8::String> expected = v8_str("ascii\x80only\x80string\x80");
1326 CHECK(expected->Equals(result));
1327 }
1328
1329
TEST(IsAscii)1330 TEST(IsAscii) {
1331 CHECK(String::IsAscii(static_cast<char*>(NULL), 0));
1332 CHECK(String::IsOneByte(static_cast<uc16*>(NULL), 0));
1333 }
1334
1335
1336
1337 template<typename Op, bool return_first>
ConvertLatin1(uint16_t c)1338 static uint16_t ConvertLatin1(uint16_t c) {
1339 uint32_t result[Op::kMaxWidth];
1340 int chars;
1341 chars = Op::Convert(c, 0, result, NULL);
1342 if (chars == 0) return 0;
1343 CHECK_LE(chars, static_cast<int>(sizeof(result)));
1344 if (!return_first && chars > 1) {
1345 return 0;
1346 }
1347 return result[0];
1348 }
1349
1350
CheckCanonicalEquivalence(uint16_t c,uint16_t test)1351 static void CheckCanonicalEquivalence(uint16_t c, uint16_t test) {
1352 uint16_t expect = ConvertLatin1<unibrow::Ecma262UnCanonicalize, true>(c);
1353 if (expect > unibrow::Latin1::kMaxChar) expect = 0;
1354 CHECK_EQ(expect, test);
1355 }
1356
1357
TEST(Latin1IgnoreCase)1358 TEST(Latin1IgnoreCase) {
1359 using namespace unibrow;
1360 for (uint16_t c = Latin1::kMaxChar + 1; c != 0; c++) {
1361 uint16_t lower = ConvertLatin1<ToLowercase, false>(c);
1362 uint16_t upper = ConvertLatin1<ToUppercase, false>(c);
1363 uint16_t test = Latin1::ConvertNonLatin1ToLatin1(c);
1364 // Filter out all character whose upper is not their lower or vice versa.
1365 if (lower == 0 && upper == 0) {
1366 CheckCanonicalEquivalence(c, test);
1367 continue;
1368 }
1369 if (lower > Latin1::kMaxChar && upper > Latin1::kMaxChar) {
1370 CheckCanonicalEquivalence(c, test);
1371 continue;
1372 }
1373 if (lower == 0 && upper != 0) {
1374 lower = ConvertLatin1<ToLowercase, false>(upper);
1375 }
1376 if (upper == 0 && lower != c) {
1377 upper = ConvertLatin1<ToUppercase, false>(lower);
1378 }
1379 if (lower > Latin1::kMaxChar && upper > Latin1::kMaxChar) {
1380 CheckCanonicalEquivalence(c, test);
1381 continue;
1382 }
1383 if (upper != c && lower != c) {
1384 CheckCanonicalEquivalence(c, test);
1385 continue;
1386 }
1387 CHECK_EQ(Min(upper, lower), test);
1388 }
1389 }
1390