• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(CcTest::i_isolate()),
1114              result->IsUndefined());
1115     CHECK_EQ(results[i]->IsNumber(), result->IsNumber());
1116     if (result->IsNumber()) {
1117       int32_t value = 0;
1118       CHECK(results[i]->ToInt32(&value));
1119       CHECK_EQ(value, result->ToInt32(context).ToLocalChecked()->Value());
1120     }
1121   }
1122 }
1123 
1124 
TEST(SliceFromCons)1125 TEST(SliceFromCons) {
1126   FLAG_string_slices = true;
1127   CcTest::InitializeVM();
1128   Factory* factory = CcTest::i_isolate()->factory();
1129   v8::HandleScope scope(CcTest::isolate());
1130   Handle<String> string =
1131       factory->NewStringFromStaticChars("parentparentparent");
1132   Handle<String> parent =
1133       factory->NewConsString(string, string).ToHandleChecked();
1134   CHECK(parent->IsConsString());
1135   CHECK(!parent->IsFlat());
1136   Handle<String> slice = factory->NewSubString(parent, 1, 25);
1137   // After slicing, the original string becomes a flat cons.
1138   CHECK(parent->IsFlat());
1139   CHECK(slice->IsSlicedString());
1140   CHECK_EQ(SlicedString::cast(*slice)->parent(),
1141            // Parent could have been short-circuited.
1142            parent->IsConsString() ? ConsString::cast(*parent)->first()
1143                                   : *parent);
1144   CHECK(SlicedString::cast(*slice)->parent()->IsSeqString());
1145   CHECK(slice->IsFlat());
1146 }
1147 
1148 
1149 class OneByteVectorResource : public v8::String::ExternalOneByteStringResource {
1150  public:
OneByteVectorResource(i::Vector<const char> vector)1151   explicit OneByteVectorResource(i::Vector<const char> vector)
1152       : data_(vector) {}
~OneByteVectorResource()1153   virtual ~OneByteVectorResource() {}
length() const1154   virtual size_t length() const { return data_.length(); }
data() const1155   virtual const char* data() const { return data_.start(); }
1156  private:
1157   i::Vector<const char> data_;
1158 };
1159 
1160 
TEST(SliceFromExternal)1161 TEST(SliceFromExternal) {
1162   FLAG_string_slices = true;
1163   CcTest::InitializeVM();
1164   Factory* factory = CcTest::i_isolate()->factory();
1165   v8::HandleScope scope(CcTest::isolate());
1166   OneByteVectorResource resource(
1167       i::Vector<const char>("abcdefghijklmnopqrstuvwxyz", 26));
1168   Handle<String> string =
1169       factory->NewExternalStringFromOneByte(&resource).ToHandleChecked();
1170   CHECK(string->IsExternalString());
1171   Handle<String> slice = factory->NewSubString(string, 1, 25);
1172   CHECK(slice->IsSlicedString());
1173   CHECK(string->IsExternalString());
1174   CHECK_EQ(SlicedString::cast(*slice)->parent(), *string);
1175   CHECK(SlicedString::cast(*slice)->parent()->IsExternalString());
1176   CHECK(slice->IsFlat());
1177 }
1178 
1179 
TEST(TrivialSlice)1180 TEST(TrivialSlice) {
1181   // This tests whether a slice that contains the entire parent string
1182   // actually creates a new string (it should not).
1183   FLAG_string_slices = true;
1184   CcTest::InitializeVM();
1185   Factory* factory = CcTest::i_isolate()->factory();
1186   v8::HandleScope scope(CcTest::isolate());
1187   v8::Local<v8::Value> result;
1188   Handle<String> string;
1189   const char* init = "var str = 'abcdefghijklmnopqrstuvwxyz';";
1190   const char* check = "str.slice(0,26)";
1191   const char* crosscheck = "str.slice(1,25)";
1192 
1193   CompileRun(init);
1194 
1195   result = CompileRun(check);
1196   CHECK(result->IsString());
1197   string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1198   CHECK(!string->IsSlicedString());
1199 
1200   string = factory->NewSubString(string, 0, 26);
1201   CHECK(!string->IsSlicedString());
1202   result = CompileRun(crosscheck);
1203   CHECK(result->IsString());
1204   string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1205   CHECK(string->IsSlicedString());
1206   CHECK_EQ(0, strcmp("bcdefghijklmnopqrstuvwxy", string->ToCString().get()));
1207 }
1208 
1209 
TEST(SliceFromSlice)1210 TEST(SliceFromSlice) {
1211   // This tests whether a slice that contains the entire parent string
1212   // actually creates a new string (it should not).
1213   FLAG_string_slices = true;
1214   CcTest::InitializeVM();
1215   v8::HandleScope scope(CcTest::isolate());
1216   v8::Local<v8::Value> result;
1217   Handle<String> string;
1218   const char* init = "var str = 'abcdefghijklmnopqrstuvwxyz';";
1219   const char* slice = "var slice = ''; slice = str.slice(1,-1); slice";
1220   const char* slice_from_slice = "slice.slice(1,-1);";
1221 
1222   CompileRun(init);
1223   result = CompileRun(slice);
1224   CHECK(result->IsString());
1225   string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1226   CHECK(string->IsSlicedString());
1227   CHECK(SlicedString::cast(*string)->parent()->IsSeqString());
1228   CHECK_EQ(0, strcmp("bcdefghijklmnopqrstuvwxy", string->ToCString().get()));
1229 
1230   result = CompileRun(slice_from_slice);
1231   CHECK(result->IsString());
1232   string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1233   CHECK(string->IsSlicedString());
1234   CHECK(SlicedString::cast(*string)->parent()->IsSeqString());
1235   CHECK_EQ(0, strcmp("cdefghijklmnopqrstuvwx", string->ToCString().get()));
1236 }
1237 
1238 
UNINITIALIZED_TEST(OneByteArrayJoin)1239 UNINITIALIZED_TEST(OneByteArrayJoin) {
1240   v8::Isolate::CreateParams create_params;
1241   // Set heap limits.
1242   create_params.constraints.set_max_semi_space_size(1 * Page::kPageSize / MB);
1243   create_params.constraints.set_max_old_space_size(6 * Page::kPageSize / MB);
1244   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
1245   v8::Isolate* isolate = v8::Isolate::New(create_params);
1246   isolate->Enter();
1247 
1248   {
1249     // String s is made of 2^17 = 131072 'c' characters and a is an array
1250     // starting with 'bad', followed by 2^14 times the string s. That means the
1251     // total length of the concatenated strings is 2^31 + 3. So on 32bit systems
1252     // summing the lengths of the strings (as Smis) overflows and wraps.
1253     LocalContext context(isolate);
1254     v8::HandleScope scope(isolate);
1255     v8::TryCatch try_catch(isolate);
1256     CHECK(CompileRun(
1257               "var two_14 = Math.pow(2, 14);"
1258               "var two_17 = Math.pow(2, 17);"
1259               "var s = Array(two_17 + 1).join('c');"
1260               "var a = ['bad'];"
1261               "for (var i = 1; i <= two_14; i++) a.push(s);"
1262               "a.join("
1263               ");").IsEmpty());
1264     CHECK(try_catch.HasCaught());
1265   }
1266   isolate->Exit();
1267   isolate->Dispose();
1268 }
1269 
1270 
CheckException(const char * source)1271 static void CheckException(const char* source) {
1272   // An empty handle is returned upon exception.
1273   CHECK(CompileRun(source).IsEmpty());
1274 }
1275 
1276 
TEST(RobustSubStringStub)1277 TEST(RobustSubStringStub) {
1278   // This tests whether the SubStringStub can handle unsafe arguments.
1279   // If not recognized, those unsafe arguments lead to out-of-bounds reads.
1280   FLAG_allow_natives_syntax = true;
1281   CcTest::InitializeVM();
1282   v8::HandleScope scope(CcTest::isolate());
1283   v8::Local<v8::Value> result;
1284   Handle<String> string;
1285   CompileRun("var short = 'abcdef';");
1286 
1287   // Invalid indices.
1288   CheckException("%_SubString(short,     0,    10000);");
1289   CheckException("%_SubString(short, -1234,        5);");
1290   CheckException("%_SubString(short,     5,        2);");
1291   // Special HeapNumbers.
1292   CheckException("%_SubString(short,     1, Infinity);");
1293   CheckException("%_SubString(short,   NaN,        5);");
1294   // String arguments.
1295   CheckException("%_SubString(short,    '2',     '5');");
1296   // Ordinary HeapNumbers can be handled (in runtime).
1297   result = CompileRun("%_SubString(short, Math.sqrt(4), 5.1);");
1298   string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1299   CHECK_EQ(0, strcmp("cde", string->ToCString().get()));
1300 
1301   CompileRun("var long = 'abcdefghijklmnopqrstuvwxyz';");
1302   // Invalid indices.
1303   CheckException("%_SubString(long,     0,    10000);");
1304   CheckException("%_SubString(long, -1234,       17);");
1305   CheckException("%_SubString(long,    17,        2);");
1306   // Special HeapNumbers.
1307   CheckException("%_SubString(long,     1, Infinity);");
1308   CheckException("%_SubString(long,   NaN,       17);");
1309   // String arguments.
1310   CheckException("%_SubString(long,    '2',    '17');");
1311   // Ordinary HeapNumbers within bounds can be handled (in runtime).
1312   result = CompileRun("%_SubString(long, Math.sqrt(4), 17.1);");
1313   string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1314   CHECK_EQ(0, strcmp("cdefghijklmnopq", string->ToCString().get()));
1315 
1316   // Test that out-of-bounds substring of a slice fails when the indices
1317   // would have been valid for the underlying string.
1318   CompileRun("var slice = long.slice(1, 15);");
1319   CheckException("%_SubString(slice, 0, 17);");
1320 }
1321 
1322 
1323 namespace {
1324 
1325 int* global_use_counts = NULL;
1326 
MockUseCounterCallback(v8::Isolate * isolate,v8::Isolate::UseCounterFeature feature)1327 void MockUseCounterCallback(v8::Isolate* isolate,
1328                             v8::Isolate::UseCounterFeature feature) {
1329   ++global_use_counts[feature];
1330 }
1331 }
1332 
1333 
TEST(CountBreakIterator)1334 TEST(CountBreakIterator) {
1335   CcTest::InitializeVM();
1336   v8::HandleScope scope(CcTest::isolate());
1337   LocalContext context;
1338   int use_counts[v8::Isolate::kUseCounterFeatureCount] = {};
1339   global_use_counts = use_counts;
1340   CcTest::isolate()->SetUseCounterCallback(MockUseCounterCallback);
1341   CHECK_EQ(0, use_counts[v8::Isolate::kBreakIterator]);
1342   v8::Local<v8::Value> result = CompileRun(
1343       "(function() {"
1344       "  if (!this.Intl) return 0;"
1345       "  var iterator = Intl.v8BreakIterator(['en']);"
1346       "  iterator.adoptText('Now is the time');"
1347       "  iterator.next();"
1348       "  return iterator.next();"
1349       "})();");
1350   CHECK(result->IsNumber());
1351   int uses =
1352       result->ToInt32(context.local()).ToLocalChecked()->Value() == 0 ? 0 : 1;
1353   CHECK_EQ(uses, use_counts[v8::Isolate::kBreakIterator]);
1354   // Make sure GC cleans up the break iterator, so we don't get a memory leak
1355   // reported by ASAN.
1356   CcTest::isolate()->LowMemoryNotification();
1357 }
1358 
1359 
TEST(StringReplaceAtomTwoByteResult)1360 TEST(StringReplaceAtomTwoByteResult) {
1361   CcTest::InitializeVM();
1362   v8::HandleScope scope(CcTest::isolate());
1363   LocalContext context;
1364   v8::Local<v8::Value> result = CompileRun(
1365       "var subject = 'one_byte~only~string~'; "
1366       "var replace = '\x80';            "
1367       "subject.replace(/~/g, replace);  ");
1368   CHECK(result->IsString());
1369   Handle<String> string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1370   CHECK(string->IsSeqTwoByteString());
1371 
1372   v8::Local<v8::String> expected = v8_str("one_byte\x80only\x80string\x80");
1373   CHECK(expected->Equals(context.local(), result).FromJust());
1374 }
1375 
1376 
TEST(IsAscii)1377 TEST(IsAscii) {
1378   CHECK(String::IsAscii(static_cast<char*>(NULL), 0));
1379   CHECK(String::IsOneByte(static_cast<uc16*>(NULL), 0));
1380 }
1381 
1382 
1383 
1384 template<typename Op, bool return_first>
ConvertLatin1(uint16_t c)1385 static uint16_t ConvertLatin1(uint16_t c) {
1386   uint32_t result[Op::kMaxWidth];
1387   int chars;
1388   chars = Op::Convert(c, 0, result, NULL);
1389   if (chars == 0) return 0;
1390   CHECK_LE(chars, static_cast<int>(sizeof(result)));
1391   if (!return_first && chars > 1) {
1392     return 0;
1393   }
1394   return result[0];
1395 }
1396 
1397 
CheckCanonicalEquivalence(uint16_t c,uint16_t test)1398 static void CheckCanonicalEquivalence(uint16_t c, uint16_t test) {
1399   uint16_t expect = ConvertLatin1<unibrow::Ecma262UnCanonicalize, true>(c);
1400   if (expect > unibrow::Latin1::kMaxChar) expect = 0;
1401   CHECK_EQ(expect, test);
1402 }
1403 
1404 
TEST(Latin1IgnoreCase)1405 TEST(Latin1IgnoreCase) {
1406   using namespace unibrow;
1407   for (uint16_t c = Latin1::kMaxChar + 1; c != 0; c++) {
1408     uint16_t lower = ConvertLatin1<ToLowercase, false>(c);
1409     uint16_t upper = ConvertLatin1<ToUppercase, false>(c);
1410     uint16_t test = Latin1::ConvertNonLatin1ToLatin1(c);
1411     // Filter out all character whose upper is not their lower or vice versa.
1412     if (lower == 0 && upper == 0) {
1413       CheckCanonicalEquivalence(c, test);
1414       continue;
1415     }
1416     if (lower > Latin1::kMaxChar && upper > Latin1::kMaxChar) {
1417       CheckCanonicalEquivalence(c, test);
1418       continue;
1419     }
1420     if (lower == 0 && upper != 0) {
1421       lower = ConvertLatin1<ToLowercase, false>(upper);
1422     }
1423     if (upper == 0 && lower != c) {
1424       upper = ConvertLatin1<ToUppercase, false>(lower);
1425     }
1426     if (lower > Latin1::kMaxChar && upper > Latin1::kMaxChar) {
1427       CheckCanonicalEquivalence(c, test);
1428       continue;
1429     }
1430     if (upper != c && lower != c) {
1431       CheckCanonicalEquivalence(c, test);
1432       continue;
1433     }
1434     CHECK_EQ(Min(upper, lower), test);
1435   }
1436 }
1437 
1438 
1439 class DummyResource: public v8::String::ExternalStringResource {
1440  public:
data() const1441   virtual const uint16_t* data() const { return NULL; }
length() const1442   virtual size_t length() const { return 1 << 30; }
1443 };
1444 
1445 
1446 class DummyOneByteResource: public v8::String::ExternalOneByteStringResource {
1447  public:
data() const1448   virtual const char* data() const { return NULL; }
length() const1449   virtual size_t length() const { return 1 << 30; }
1450 };
1451 
1452 
TEST(InvalidExternalString)1453 TEST(InvalidExternalString) {
1454   CcTest::InitializeVM();
1455   LocalContext context;
1456   Isolate* isolate = CcTest::i_isolate();
1457   { HandleScope scope(isolate);
1458     DummyOneByteResource r;
1459     CHECK(isolate->factory()->NewExternalStringFromOneByte(&r).is_null());
1460     CHECK(isolate->has_pending_exception());
1461     isolate->clear_pending_exception();
1462   }
1463 
1464   { HandleScope scope(isolate);
1465     DummyResource r;
1466     CHECK(isolate->factory()->NewExternalStringFromTwoByte(&r).is_null());
1467     CHECK(isolate->has_pending_exception());
1468     isolate->clear_pending_exception();
1469   }
1470 }
1471 
1472 
1473 #define INVALID_STRING_TEST(FUN, TYPE)                                         \
1474   TEST(StringOOM##FUN) {                                                       \
1475     CcTest::InitializeVM();                                                    \
1476     LocalContext context;                                                      \
1477     Isolate* isolate = CcTest::i_isolate();                                    \
1478     STATIC_ASSERT(String::kMaxLength < kMaxInt);                               \
1479     static const int invalid = String::kMaxLength + 1;                         \
1480     HandleScope scope(isolate);                                                \
1481     Vector<TYPE> dummy = Vector<TYPE>::New(invalid);                           \
1482     memset(dummy.start(), 0x0, dummy.length() * sizeof(TYPE));                 \
1483     CHECK(isolate->factory()->FUN(Vector<const TYPE>::cast(dummy)).is_null()); \
1484     memset(dummy.start(), 0x20, dummy.length() * sizeof(TYPE));                \
1485     CHECK(isolate->has_pending_exception());                                   \
1486     isolate->clear_pending_exception();                                        \
1487     dummy.Dispose();                                                           \
1488   }
1489 
INVALID_STRING_TEST(NewStringFromAscii,char)1490 INVALID_STRING_TEST(NewStringFromAscii, char)
1491 INVALID_STRING_TEST(NewStringFromUtf8, char)
1492 INVALID_STRING_TEST(NewStringFromOneByte, uint8_t)
1493 
1494 #undef INVALID_STRING_TEST
1495 
1496 
1497 TEST(FormatMessage) {
1498   CcTest::InitializeVM();
1499   LocalContext context;
1500   Isolate* isolate = CcTest::i_isolate();
1501   HandleScope scope(isolate);
1502   Handle<String> arg0 = isolate->factory()->NewStringFromAsciiChecked("arg0");
1503   Handle<String> arg1 = isolate->factory()->NewStringFromAsciiChecked("arg1");
1504   Handle<String> arg2 = isolate->factory()->NewStringFromAsciiChecked("arg2");
1505   Handle<String> result =
1506       MessageTemplate::FormatMessage(MessageTemplate::kPropertyNotFunction,
1507                                      arg0, arg1, arg2).ToHandleChecked();
1508   Handle<String> expected = isolate->factory()->NewStringFromAsciiChecked(
1509       "'arg0' returned for property 'arg1' of object 'arg2' is not a function");
1510   CHECK(String::Equals(result, expected));
1511 }
1512 
TEST(Regress609831)1513 TEST(Regress609831) {
1514   CcTest::InitializeVM();
1515   LocalContext context;
1516   Isolate* isolate = CcTest::i_isolate();
1517   {
1518     HandleScope scope(isolate);
1519     v8::Local<v8::Value> result = CompileRun(
1520         "String.fromCharCode(32, 32, 32, 32, 32, "
1521         "32, 32, 32, 32, 32, 32, 32, 32, 32, 32, "
1522         "32, 32, 32, 32, 32, 32, 32, 32, 32, 32)");
1523     CHECK(v8::Utils::OpenHandle(*result)->IsSeqOneByteString());
1524   }
1525   {
1526     HandleScope scope(isolate);
1527     v8::Local<v8::Value> result = CompileRun(
1528         "String.fromCharCode(432, 432, 432, 432, 432, "
1529         "432, 432, 432, 432, 432, 432, 432, 432, 432, "
1530         "432, 432, 432, 432, 432, 432, 432, 432, 432)");
1531     CHECK(v8::Utils::OpenHandle(*result)->IsSeqTwoByteString());
1532   }
1533 }
1534