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