The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / v8 / test / cctest / test-strings.cc @ f230a1cf

History | View | Annotate | Download (45.5 KB)

1
// Copyright 2012 the V8 project authors. All rights reserved.
2
// Redistribution and use in source and binary forms, with or without
3
// modification, are permitted provided that the following conditions are
4
// met:
5
//
6
//     * Redistributions of source code must retain the above copyright
7
//       notice, this list of conditions and the following disclaimer.
8
//     * Redistributions in binary form must reproduce the above
9
//       copyright notice, this list of conditions and the following
10
//       disclaimer in the documentation and/or other materials provided
11
//       with the distribution.
12
//     * Neither the name of Google Inc. nor the names of its
13
//       contributors may be used to endorse or promote products derived
14
//       from this software without specific prior written permission.
15
//
16
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27

    
28
// Check that we can traverse very deep stacks of ConsStrings using
29
// StringCharacterStram.  Check that Get(int) works on very deep stacks
30
// of ConsStrings.  These operations may not be very fast, but they
31
// should be possible without getting errors due to too deep recursion.
32

    
33
#include <stdlib.h>
34

    
35
#include "v8.h"
36

    
37
#include "api.h"
38
#include "factory.h"
39
#include "objects.h"
40
#include "cctest.h"
41
#include "zone-inl.h"
42

    
43
// Adapted from http://en.wikipedia.org/wiki/Multiply-with-carry
44
class MyRandomNumberGenerator {
45
 public:
46
  MyRandomNumberGenerator() {
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
                                     MyRandomNumberGenerator* rng,
137
                                     Zone* zone) {
138
  // A list of pointers that we don't have any interest in cleaning up.
139
  // If they are reachable from a root then leak detection won't complain.
140
  Isolate* isolate = CcTest::i_isolate();
141
  Factory* factory = isolate->factory();
142
  for (int i = 0; i < bb_length; i++) {
143
    int len = rng->next(16);
144
    int slice_head_chars = 0;
145
    int slice_tail_chars = 0;
146
    int slice_depth = 0;
147
    for (int j = 0; j < 3; j++) {
148
      if (rng->next(0.35)) slice_depth++;
149
    }
150
    // Must truncate something for a slice string. Loop until
151
    // at least one end will be sliced.
152
    while (slice_head_chars == 0 && slice_tail_chars == 0) {
153
      slice_head_chars = rng->next(15);
154
      slice_tail_chars = rng->next(12);
155
    }
156
    if (long_blocks) {
157
      // Generate building blocks which will never be merged
158
      len += ConsString::kMinLength + 1;
159
    } else if (len > 14) {
160
      len += 1234;
161
    }
162
    // Don't slice 0 length strings.
163
    if (len == 0) slice_depth = 0;
164
    int slice_length = slice_depth*(slice_head_chars + slice_tail_chars);
165
    len += slice_length;
166
    switch (rng->next(4)) {
167
      case 0: {
168
        uc16 buf[2000];
169
        for (int j = 0; j < len; j++) {
170
          buf[j] = rng->next(0x10000);
171
        }
172
        building_blocks[i] =
173
            factory->NewStringFromTwoByte(Vector<const uc16>(buf, len));
174
        for (int j = 0; j < len; j++) {
175
          CHECK_EQ(buf[j], building_blocks[i]->Get(j));
176
        }
177
        break;
178
      }
179
      case 1: {
180
        char buf[2000];
181
        for (int j = 0; j < len; j++) {
182
          buf[j] = rng->next(0x80);
183
        }
184
        building_blocks[i] =
185
            factory->NewStringFromAscii(Vector<const char>(buf, len));
186
        for (int j = 0; j < len; j++) {
187
          CHECK_EQ(buf[j], building_blocks[i]->Get(j));
188
        }
189
        break;
190
      }
191
      case 2: {
192
        uc16* buf = zone->NewArray<uc16>(len);
193
        for (int j = 0; j < len; j++) {
194
          buf[j] = rng->next(0x10000);
195
        }
196
        Resource* resource = new(zone) Resource(Vector<const uc16>(buf, len));
197
        building_blocks[i] = factory->NewExternalStringFromTwoByte(resource);
198
        for (int j = 0; j < len; j++) {
199
          CHECK_EQ(buf[j], building_blocks[i]->Get(j));
200
        }
201
        break;
202
      }
203
      case 3: {
204
        char* buf = zone->NewArray<char>(len);
205
        for (int j = 0; j < len; j++) {
206
          buf[j] = rng->next(0x80);
207
        }
208
        AsciiResource* resource =
209
            new(zone) AsciiResource(Vector<const char>(buf, len));
210
        building_blocks[i] = factory->NewExternalStringFromAscii(resource);
211
        for (int j = 0; j < len; j++) {
212
          CHECK_EQ(buf[j], building_blocks[i]->Get(j));
213
        }
214
        break;
215
      }
216
    }
217
    for (int j = slice_depth; j > 0; j--) {
218
      building_blocks[i] = factory->NewSubString(
219
          building_blocks[i],
220
          slice_head_chars,
221
          building_blocks[i]->length() - slice_tail_chars);
222
    }
223
    CHECK(len == building_blocks[i]->length() + slice_length);
224
  }
225
}
226

    
227

    
228
class ConsStringStats {
229
 public:
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
  MyRandomNumberGenerator 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_ = CcTest::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 = CcTest::i_isolate()->factory();
407
  // Compute termination characteristics.
408
  bool terminate = false;
409
  bool flat = data->rng_.next(data->empty_leaf_threshold_);
410
  bool terminate_early = data->rng_.next(data->early_termination_threshold_);
411
  if (terminate_early) data->early_terminations_++;
412
  // The obvious condition.
413
  terminate |= max_recursion == 0;
414
  // Flat cons string terminate by definition.
415
  terminate |= flat;
416
  // Cap for max leaves.
417
  terminate |= data->stats_.leaves_ >= data->max_leaves_;
418
  // Roll the dice.
419
  terminate |= terminate_early;
420
  // Compute termination characteristics for each side.
421
  bool terminate_left = terminate || !data->rng_.next(data->leftness_);
422
  bool terminate_right = terminate || !data->rng_.next(data->rightness_);
423
  // Generate left string.
424
  Handle<String> left;
425
  if (terminate_left) {
426
    left = data->block(data->rng_.next());
427
    data->stats_.leaves_++;
428
    data->stats_.chars_ += left->length();
429
  } else {
430
    data->stats_.left_traversals_++;
431
  }
432
  // Generate right string.
433
  Handle<String> right;
434
  if (terminate_right) {
435
    right = data->block(data->rng_.next());
436
    data->stats_.leaves_++;
437
    data->stats_.chars_ += right->length();
438
  } else {
439
    data->stats_.right_traversals_++;
440
  }
441
  // Generate the necessary sub-nodes recursively.
442
  if (!terminate_right) {
443
    // Need to balance generation fairly.
444
    if (!terminate_left && data->rng_.next(0.5)) {
445
      left = ConstructRandomString(data, max_recursion - 1);
446
    }
447
    right = ConstructRandomString(data, max_recursion - 1);
448
  }
449
  if (!terminate_left && left.is_null()) {
450
    left = ConstructRandomString(data, max_recursion - 1);
451
  }
452
  // Build the cons string.
453
  Handle<String> root = factory->NewConsString(left, right);
454
  CHECK(root->IsConsString() && !root->IsFlat());
455
  // Special work needed for flat string.
456
  if (flat) {
457
    data->stats_.empty_leaves_++;
458
    FlattenString(root);
459
    CHECK(root->IsConsString() && root->IsFlat());
460
  }
461
  return root;
462
}
463

    
464

    
465
static Handle<String> ConstructLeft(
466
    ConsStringGenerationData* data,
467
    int depth) {
468
  Factory* factory = CcTest::i_isolate()->factory();
469
  Handle<String> answer = factory->NewStringFromAscii(CStrVector(""));
470
  data->stats_.leaves_++;
471
  for (int i = 0; i < depth; i++) {
472
    Handle<String> block = data->block(i);
473
    Handle<String> next = factory->NewConsString(answer, block);
474
    if (next->IsConsString()) data->stats_.leaves_++;
475
    data->stats_.chars_ += block->length();
476
    answer = next;
477
  }
478
  data->stats_.left_traversals_ = data->stats_.leaves_ - 2;
479
  return answer;
480
}
481

    
482

    
483
static Handle<String> ConstructRight(
484
    ConsStringGenerationData* data,
485
    int depth) {
486
  Factory* factory = CcTest::i_isolate()->factory();
487
  Handle<String> answer = factory->NewStringFromAscii(CStrVector(""));
488
  data->stats_.leaves_++;
489
  for (int i = depth - 1; i >= 0; i--) {
490
    Handle<String> block = data->block(i);
491
    Handle<String> next = factory->NewConsString(block, answer);
492
    if (next->IsConsString()) data->stats_.leaves_++;
493
    data->stats_.chars_ += block->length();
494
    answer = next;
495
  }
496
  data->stats_.right_traversals_ = data->stats_.leaves_ - 2;
497
  return answer;
498
}
499

    
500

    
501
static Handle<String> ConstructBalancedHelper(
502
    ConsStringGenerationData* data,
503
    int from,
504
    int to) {
505
  Factory* factory = CcTest::i_isolate()->factory();
506
  CHECK(to > from);
507
  if (to - from == 1) {
508
    data->stats_.chars_ += data->block(from)->length();
509
    return data->block(from);
510
  }
511
  if (to - from == 2) {
512
    data->stats_.chars_ += data->block(from)->length();
513
    data->stats_.chars_ += data->block(from+1)->length();
514
    return factory->NewConsString(data->block(from), data->block(from+1));
515
  }
516
  Handle<String> part1 =
517
    ConstructBalancedHelper(data, from, from + ((to - from) / 2));
518
  Handle<String> part2 =
519
    ConstructBalancedHelper(data, from + ((to - from) / 2), to);
520
  if (part1->IsConsString()) data->stats_.left_traversals_++;
521
  if (part2->IsConsString()) data->stats_.right_traversals_++;
522
  return factory->NewConsString(part1, part2);
523
}
524

    
525

    
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(CcTest::i_isolate());
575
  ConsStringGenerationData data(false, &zone);
576
  Handle<String> flat = ConstructBalanced(&data);
577
  FlattenString(flat);
578
  Handle<String> left_asymmetric = ConstructLeft(&data, DEEP_DEPTH);
579
  Handle<String> right_asymmetric = ConstructRight(&data, DEEP_DEPTH);
580
  Handle<String> symmetric = ConstructBalanced(&data);
581
  printf("1\n");
582
  Traverse(flat, symmetric);
583
  printf("2\n");
584
  Traverse(flat, left_asymmetric);
585
  printf("3\n");
586
  Traverse(flat, right_asymmetric);
587
  printf("4\n");
588
  Handle<String> left_deep_asymmetric =
589
      ConstructLeft(&data, SUPER_DEEP_DEPTH);
590
  Handle<String> right_deep_asymmetric =
591
      ConstructRight(&data, SUPER_DEEP_DEPTH);
592
  printf("5\n");
593
  TraverseFirst(left_asymmetric, left_deep_asymmetric, 1050);
594
  printf("6\n");
595
  TraverseFirst(left_asymmetric, right_deep_asymmetric, 65536);
596
  printf("7\n");
597
  FlattenString(left_asymmetric);
598
  printf("10\n");
599
  Traverse(flat, left_asymmetric);
600
  printf("11\n");
601
  FlattenString(right_asymmetric);
602
  printf("12\n");
603
  Traverse(flat, right_asymmetric);
604
  printf("14\n");
605
  FlattenString(symmetric);
606
  printf("15\n");
607
  Traverse(flat, symmetric);
608
  printf("16\n");
609
  FlattenString(left_deep_asymmetric);
610
  printf("18\n");
611
}
612

    
613

    
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 = CcTest::i_isolate();
663
  HandleScope outer_scope(isolate);
664
  Zone zone(isolate);
665
  ConsStringGenerationData data(true, &zone);
666
  for (int i = 0; i < test_cases; i++) {
667
    printf("%d\n", i);
668
    HandleScope inner_scope(isolate);
669
    AlwaysAllocateScope always_allocate;
670
    // Build flat version of cons string.
671
    Handle<String> flat_string = build(i, &data);
672
    ConsStringStats flat_string_stats;
673
    AccumulateStats(flat_string, &flat_string_stats);
674
    // Flatten string.
675
    FlattenString(flat_string);
676
    // Build unflattened version of cons string to test.
677
    Handle<String> cons_string = build(i, &data);
678
    ConsStringStats cons_string_stats;
679
    AccumulateStats(cons_string, &cons_string_stats);
680
    DisallowHeapAllocation no_allocation;
681
    PrintStats(data);
682
    // Full verify of cons string.
683
    cons_string_stats.VerifyEqual(flat_string_stats);
684
    cons_string_stats.VerifyEqual(data.stats_);
685
    VerifyConsString(cons_string, &data);
686
    String* flat_string_ptr =
687
        flat_string->IsConsString() ?
688
        ConsString::cast(*flat_string)->first() :
689
        *flat_string;
690
    VerifyCharacterStream(flat_string_ptr, *cons_string);
691
  }
692
}
693

    
694

    
695
static const int kCharacterStreamNonRandomCases = 8;
696

    
697

    
698
static Handle<String> BuildEdgeCaseConsString(
699
    int test_case, ConsStringGenerationData* data) {
700
  Factory* factory = CcTest::i_isolate()->factory();
701
  data->Reset();
702
  switch (test_case) {
703
    case 0:
704
      return ConstructBalanced(data, 71);
705
    case 1:
706
      return ConstructLeft(data, 71);
707
    case 2:
708
      return ConstructRight(data, 71);
709
    case 3:
710
      return ConstructLeft(data, 10);
711
    case 4:
712
      return ConstructRight(data, 10);
713
    case 5:
714
      // 2 element balanced tree.
715
      data->stats_.chars_ += data->block(0)->length();
716
      data->stats_.chars_ += data->block(1)->length();
717
      data->stats_.leaves_ += 2;
718
      return factory->NewConsString(data->block(0), data->block(1));
719
    case 6:
720
      // Simple flattened tree.
721
      data->stats_.chars_ += data->block(0)->length();
722
      data->stats_.chars_ += data->block(1)->length();
723
      data->stats_.leaves_ += 2;
724
      data->stats_.empty_leaves_ += 1;
725
      {
726
        Handle<String> string =
727
            factory->NewConsString(data->block(0), data->block(1));
728
        FlattenString(string);
729
        return string;
730
      }
731
    case 7:
732
      // Left node flattened.
733
      data->stats_.chars_ += data->block(0)->length();
734
      data->stats_.chars_ += data->block(1)->length();
735
      data->stats_.chars_ += data->block(2)->length();
736
      data->stats_.leaves_ += 3;
737
      data->stats_.empty_leaves_ += 1;
738
      data->stats_.left_traversals_ += 1;
739
      {
740
        Handle<String> left =
741
            factory->NewConsString(data->block(0), data->block(1));
742
        FlattenString(left);
743
        return factory->NewConsString(left, data->block(2));
744
      }
745
    case 8:
746
      // Left node and right node flattened.
747
      data->stats_.chars_ += data->block(0)->length();
748
      data->stats_.chars_ += data->block(1)->length();
749
      data->stats_.chars_ += data->block(2)->length();
750
      data->stats_.chars_ += data->block(3)->length();
751
      data->stats_.leaves_ += 4;
752
      data->stats_.empty_leaves_ += 2;
753
      data->stats_.left_traversals_ += 1;
754
      data->stats_.right_traversals_ += 1;
755
      {
756
        Handle<String> left =
757
            factory->NewConsString(data->block(0), data->block(1));
758
        FlattenString(left);
759
        Handle<String> right =
760
            factory->NewConsString(data->block(2), data->block(2));
761
        FlattenString(right);
762
        return factory->NewConsString(left, right);
763
      }
764
  }
765
  UNREACHABLE();
766
  return Handle<String>();
767
}
768

    
769

    
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 = CcTest::i_isolate()->factory();
864
  v8::HandleScope scope(CcTest::isolate());
865

    
866
  char* foo = NewArray<char>(DEEP_ASCII_DEPTH);
867
  for (int i = 0; i < DEEP_ASCII_DEPTH; i++) {
868
    foo[i] = "foo "[i % 4];
869
  }
870
  Handle<String> string =
871
      factory->NewStringFromAscii(Vector<const char>(foo, DEEP_ASCII_DEPTH));
872
  Handle<String> foo_string = factory->NewStringFromAscii(CStrVector("foo"));
873
  for (int i = 0; i < DEEP_ASCII_DEPTH; i += 10) {
874
    string = factory->NewConsString(string, foo_string);
875
  }
876
  Handle<String> flat_string = factory->NewConsString(string, foo_string);
877
  FlattenString(flat_string);
878

    
879
  for (int i = 0; i < 500; i++) {
880
    TraverseFirst(flat_string, string, DEEP_ASCII_DEPTH);
881
  }
882
  DeleteArray<char>(foo);
883
}
884

    
885

    
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 = CcTest::i_isolate();
934
  Zone zone(isolate);
935

    
936
  LocalContext context;
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 = context->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
  CcTest::InitializeVM();
1022
  Isolate* isolate = CcTest::i_isolate();
1023
  Zone zone(isolate);
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
  CcTest::InitializeVM();
1052
  // We incorrectly allowed strings to be tagged as array indices even if their
1053
  // values didn't fit in the hash field.
1054
  // See http://code.google.com/p/v8/issues/detail?id=728
1055
  Isolate* isolate = CcTest::i_isolate();
1056
  Zone zone(isolate);
1057

    
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 = CcTest::i_isolate()->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 = CcTest::i_isolate()->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 = CcTest::i_isolate()->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(CcTest::isolate());
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
}