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