blob: 770042d8148510feba57502b0c6852f3dccf9860 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001// 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.
Steve Blocka7e24c12009-10-30 11:49:00 +000027
28// Check that we can traverse very deep stacks of ConsStrings using
Ben Murdochb8a8cc12014-11-26 15:28:44 +000029// StringCharacterStram. Check that Get(int) works on very deep stacks
Steve Blocka7e24c12009-10-30 11:49:00 +000030// 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
Ben Murdochb8a8cc12014-11-26 15:28:44 +000035#include "src/v8.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000036
Ben Murdochb8a8cc12014-11-26 15:28:44 +000037#include "src/api.h"
38#include "src/factory.h"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000039#include "src/messages.h"
Ben Murdochb8a8cc12014-11-26 15:28:44 +000040#include "src/objects.h"
Emily Bernierd0a1eb72015-03-24 16:35:39 -040041#include "src/unicode-decoder.h"
Ben Murdochb8a8cc12014-11-26 15:28:44 +000042#include "test/cctest/cctest.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000043
Ben Murdochb8a8cc12014-11-26 15:28:44 +000044// Adapted from http://en.wikipedia.org/wiki/Multiply-with-carry
45class MyRandomNumberGenerator {
46 public:
47 MyRandomNumberGenerator() {
48 init();
49 }
Steve Blocka7e24c12009-10-30 11:49:00 +000050
Ben Murdochb8a8cc12014-11-26 15:28:44 +000051 void init(uint32_t seed = 0x5688c73e) {
52 static const uint32_t phi = 0x9e3779b9;
53 c = 362436;
54 i = kQSize-1;
55 Q[0] = seed;
56 Q[1] = seed + phi;
57 Q[2] = seed + phi + phi;
58 for (unsigned j = 3; j < kQSize; j++) {
59 Q[j] = Q[j - 3] ^ Q[j - 2] ^ phi ^ j;
60 }
61 }
62
63 uint32_t next() {
64 uint64_t a = 18782;
65 uint32_t r = 0xfffffffe;
66 i = (i + 1) & (kQSize-1);
67 uint64_t t = a * Q[i] + c;
68 c = (t >> 32);
69 uint32_t x = static_cast<uint32_t>(t + c);
70 if (x < c) {
71 x++;
72 c++;
73 }
74 return (Q[i] = r - x);
75 }
76
77 uint32_t next(int max) {
78 return next() % max;
79 }
80
81 bool next(double threshold) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000082 CHECK(threshold >= 0.0 && threshold <= 1.0);
Ben Murdochb8a8cc12014-11-26 15:28:44 +000083 if (threshold == 1.0) return true;
84 if (threshold == 0.0) return false;
85 uint32_t value = next() % 100000;
86 return threshold > static_cast<double>(value)/100000.0;
87 }
88
89 private:
90 static const uint32_t kQSize = 4096;
91 uint32_t Q[kQSize];
92 uint32_t c;
93 uint32_t i;
94};
Steve Blocka7e24c12009-10-30 11:49:00 +000095
96
97using namespace v8::internal;
98
Steve Blocka7e24c12009-10-30 11:49:00 +000099
Steve Blocka7e24c12009-10-30 11:49:00 +0000100static const int DEEP_DEPTH = 8 * 1024;
101static const int SUPER_DEEP_DEPTH = 80 * 1024;
102
103
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000104class Resource: public v8::String::ExternalStringResource {
Steve Blocka7e24c12009-10-30 11:49:00 +0000105 public:
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000106 Resource(const uc16* data, size_t length): data_(data), length_(length) {}
107 ~Resource() { i::DeleteArray(data_); }
Steve Blocka7e24c12009-10-30 11:49:00 +0000108 virtual const uint16_t* data() const { return data_; }
109 virtual size_t length() const { return length_; }
110
111 private:
112 const uc16* data_;
113 size_t length_;
114};
115
116
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000117class OneByteResource : public v8::String::ExternalOneByteStringResource {
Steve Blockd0582a62009-12-15 09:54:21 +0000118 public:
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000119 OneByteResource(const char* data, size_t length)
120 : data_(data), length_(length) {}
121 ~OneByteResource() { i::DeleteArray(data_); }
Steve Blockd0582a62009-12-15 09:54:21 +0000122 virtual const char* data() const { return data_; }
123 virtual size_t length() const { return length_; }
124
125 private:
126 const char* data_;
127 size_t length_;
128};
129
130
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000131static void InitializeBuildingBlocks(Handle<String>* building_blocks,
132 int bb_length,
133 bool long_blocks,
134 MyRandomNumberGenerator* rng) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000135 // A list of pointers that we don't have any interest in cleaning up.
136 // If they are reachable from a root then leak detection won't complain.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000137 Isolate* isolate = CcTest::i_isolate();
138 Factory* factory = isolate->factory();
139 for (int i = 0; i < bb_length; i++) {
140 int len = rng->next(16);
141 int slice_head_chars = 0;
142 int slice_tail_chars = 0;
143 int slice_depth = 0;
144 for (int j = 0; j < 3; j++) {
145 if (rng->next(0.35)) slice_depth++;
146 }
147 // Must truncate something for a slice string. Loop until
148 // at least one end will be sliced.
149 while (slice_head_chars == 0 && slice_tail_chars == 0) {
150 slice_head_chars = rng->next(15);
151 slice_tail_chars = rng->next(12);
152 }
153 if (long_blocks) {
154 // Generate building blocks which will never be merged
155 len += ConsString::kMinLength + 1;
156 } else if (len > 14) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000157 len += 1234;
158 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000159 // Don't slice 0 length strings.
160 if (len == 0) slice_depth = 0;
161 int slice_length = slice_depth*(slice_head_chars + slice_tail_chars);
162 len += slice_length;
163 switch (rng->next(4)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000164 case 0: {
165 uc16 buf[2000];
166 for (int j = 0; j < len; j++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000167 buf[j] = rng->next(0x10000);
Steve Blocka7e24c12009-10-30 11:49:00 +0000168 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000169 building_blocks[i] = factory->NewStringFromTwoByte(
170 Vector<const uc16>(buf, len)).ToHandleChecked();
Steve Blocka7e24c12009-10-30 11:49:00 +0000171 for (int j = 0; j < len; j++) {
172 CHECK_EQ(buf[j], building_blocks[i]->Get(j));
173 }
174 break;
175 }
176 case 1: {
177 char buf[2000];
178 for (int j = 0; j < len; j++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000179 buf[j] = rng->next(0x80);
Steve Blocka7e24c12009-10-30 11:49:00 +0000180 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000181 building_blocks[i] = factory->NewStringFromAscii(
182 Vector<const char>(buf, len)).ToHandleChecked();
Steve Blocka7e24c12009-10-30 11:49:00 +0000183 for (int j = 0; j < len; j++) {
184 CHECK_EQ(buf[j], building_blocks[i]->Get(j));
185 }
186 break;
187 }
188 case 2: {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000189 uc16* buf = NewArray<uc16>(len);
Steve Blocka7e24c12009-10-30 11:49:00 +0000190 for (int j = 0; j < len; j++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000191 buf[j] = rng->next(0x10000);
Steve Blocka7e24c12009-10-30 11:49:00 +0000192 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000193 Resource* resource = new Resource(buf, len);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000194 building_blocks[i] = v8::Utils::OpenHandle(
195 *v8::String::NewExternalTwoByte(CcTest::isolate(), resource)
196 .ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000197 for (int j = 0; j < len; j++) {
198 CHECK_EQ(buf[j], building_blocks[i]->Get(j));
199 }
200 break;
201 }
202 case 3: {
203 char* buf = NewArray<char>(len);
204 for (int j = 0; j < len; j++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000205 buf[j] = rng->next(0x80);
Steve Blocka7e24c12009-10-30 11:49:00 +0000206 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000207 OneByteResource* resource = new OneByteResource(buf, len);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000208 building_blocks[i] = v8::Utils::OpenHandle(
209 *v8::String::NewExternalOneByte(CcTest::isolate(), resource)
210 .ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000211 for (int j = 0; j < len; j++) {
212 CHECK_EQ(buf[j], building_blocks[i]->Get(j));
213 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000214 break;
215 }
216 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000217 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);
Steve Blocka7e24c12009-10-30 11:49:00 +0000224 }
225}
226
227
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000228class ConsStringStats {
229 public:
230 ConsStringStats() {
231 Reset();
Steve Blocka7e24c12009-10-30 11:49:00 +0000232 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000233 void Reset();
234 void VerifyEqual(const ConsStringStats& that) const;
235 int leaves_;
236 int empty_leaves_;
237 int chars_;
238 int left_traversals_;
239 int right_traversals_;
240 private:
241 DISALLOW_COPY_AND_ASSIGN(ConsStringStats);
242};
243
244
245void ConsStringStats::Reset() {
246 leaves_ = 0;
247 empty_leaves_ = 0;
248 chars_ = 0;
249 left_traversals_ = 0;
250 right_traversals_ = 0;
251}
252
253
254void ConsStringStats::VerifyEqual(const ConsStringStats& that) const {
255 CHECK_EQ(this->leaves_, that.leaves_);
256 CHECK_EQ(this->empty_leaves_, that.empty_leaves_);
257 CHECK_EQ(this->chars_, that.chars_);
258 CHECK_EQ(this->left_traversals_, that.left_traversals_);
259 CHECK_EQ(this->right_traversals_, that.right_traversals_);
260}
261
262
263class ConsStringGenerationData {
264 public:
265 static const int kNumberOfBuildingBlocks = 256;
266 explicit ConsStringGenerationData(bool long_blocks);
267 void Reset();
268 inline Handle<String> block(int offset);
269 inline Handle<String> block(uint32_t offset);
270 // Input variables.
271 double early_termination_threshold_;
272 double leftness_;
273 double rightness_;
274 double empty_leaf_threshold_;
275 int max_leaves_;
276 // Cached data.
277 Handle<String> building_blocks_[kNumberOfBuildingBlocks];
278 String* empty_string_;
279 MyRandomNumberGenerator rng_;
280 // Stats.
281 ConsStringStats stats_;
282 int early_terminations_;
283 private:
284 DISALLOW_COPY_AND_ASSIGN(ConsStringGenerationData);
285};
286
287
288ConsStringGenerationData::ConsStringGenerationData(bool long_blocks) {
289 rng_.init();
290 InitializeBuildingBlocks(
291 building_blocks_, kNumberOfBuildingBlocks, long_blocks, &rng_);
292 empty_string_ = CcTest::heap()->empty_string();
293 Reset();
294}
295
296
297Handle<String> ConsStringGenerationData::block(uint32_t offset) {
298 return building_blocks_[offset % kNumberOfBuildingBlocks ];
299}
300
301
302Handle<String> ConsStringGenerationData::block(int offset) {
303 CHECK_GE(offset, 0);
304 return building_blocks_[offset % kNumberOfBuildingBlocks];
305}
306
307
308void ConsStringGenerationData::Reset() {
309 early_termination_threshold_ = 0.01;
310 leftness_ = 0.75;
311 rightness_ = 0.75;
312 empty_leaf_threshold_ = 0.02;
313 max_leaves_ = 1000;
314 stats_.Reset();
315 early_terminations_ = 0;
316 rng_.init();
317}
318
319
320void AccumulateStats(ConsString* cons_string, ConsStringStats* stats) {
321 int left_length = cons_string->first()->length();
322 int right_length = cons_string->second()->length();
323 CHECK(cons_string->length() == left_length + right_length);
324 // Check left side.
325 bool left_is_cons = cons_string->first()->IsConsString();
326 if (left_is_cons) {
327 stats->left_traversals_++;
328 AccumulateStats(ConsString::cast(cons_string->first()), stats);
329 } else {
330 CHECK_NE(left_length, 0);
331 stats->leaves_++;
332 stats->chars_ += left_length;
333 }
334 // Check right side.
335 if (cons_string->second()->IsConsString()) {
336 stats->right_traversals_++;
337 AccumulateStats(ConsString::cast(cons_string->second()), stats);
338 } else {
339 if (right_length == 0) {
340 stats->empty_leaves_++;
341 CHECK(!left_is_cons);
342 }
343 stats->leaves_++;
344 stats->chars_ += right_length;
345 }
346}
347
348
349void AccumulateStats(Handle<String> cons_string, ConsStringStats* stats) {
350 DisallowHeapAllocation no_allocation;
351 if (cons_string->IsConsString()) {
352 return AccumulateStats(ConsString::cast(*cons_string), stats);
353 }
354 // This string got flattened by gc.
355 stats->chars_ += cons_string->length();
356}
357
358
359void AccumulateStatsWithOperator(
360 ConsString* cons_string, ConsStringStats* stats) {
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400361 ConsStringIterator iter(cons_string);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000362 String* string;
363 int offset;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400364 while (NULL != (string = iter.Next(&offset))) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000365 // Accumulate stats.
366 CHECK_EQ(0, offset);
367 stats->leaves_++;
368 stats->chars_ += string->length();
369 }
370}
371
372
373void VerifyConsString(Handle<String> root, ConsStringGenerationData* data) {
374 // Verify basic data.
375 CHECK(root->IsConsString());
376 CHECK_EQ(root->length(), data->stats_.chars_);
377 // Recursive verify.
378 ConsStringStats stats;
379 AccumulateStats(ConsString::cast(*root), &stats);
380 stats.VerifyEqual(data->stats_);
381 // Iteratively verify.
382 stats.Reset();
383 AccumulateStatsWithOperator(ConsString::cast(*root), &stats);
384 // Don't see these. Must copy over.
385 stats.empty_leaves_ = data->stats_.empty_leaves_;
386 stats.left_traversals_ = data->stats_.left_traversals_;
387 stats.right_traversals_ = data->stats_.right_traversals_;
388 // Adjust total leaves to compensate.
389 stats.leaves_ += stats.empty_leaves_;
390 stats.VerifyEqual(data->stats_);
391}
392
393
394static Handle<String> ConstructRandomString(ConsStringGenerationData* data,
395 unsigned max_recursion) {
396 Factory* factory = CcTest::i_isolate()->factory();
397 // Compute termination characteristics.
398 bool terminate = false;
399 bool flat = data->rng_.next(data->empty_leaf_threshold_);
400 bool terminate_early = data->rng_.next(data->early_termination_threshold_);
401 if (terminate_early) data->early_terminations_++;
402 // The obvious condition.
403 terminate |= max_recursion == 0;
404 // Flat cons string terminate by definition.
405 terminate |= flat;
406 // Cap for max leaves.
407 terminate |= data->stats_.leaves_ >= data->max_leaves_;
408 // Roll the dice.
409 terminate |= terminate_early;
410 // Compute termination characteristics for each side.
411 bool terminate_left = terminate || !data->rng_.next(data->leftness_);
412 bool terminate_right = terminate || !data->rng_.next(data->rightness_);
413 // Generate left string.
414 Handle<String> left;
415 if (terminate_left) {
416 left = data->block(data->rng_.next());
417 data->stats_.leaves_++;
418 data->stats_.chars_ += left->length();
419 } else {
420 data->stats_.left_traversals_++;
421 }
422 // Generate right string.
423 Handle<String> right;
424 if (terminate_right) {
425 right = data->block(data->rng_.next());
426 data->stats_.leaves_++;
427 data->stats_.chars_ += right->length();
428 } else {
429 data->stats_.right_traversals_++;
430 }
431 // Generate the necessary sub-nodes recursively.
432 if (!terminate_right) {
433 // Need to balance generation fairly.
434 if (!terminate_left && data->rng_.next(0.5)) {
435 left = ConstructRandomString(data, max_recursion - 1);
436 }
437 right = ConstructRandomString(data, max_recursion - 1);
438 }
439 if (!terminate_left && left.is_null()) {
440 left = ConstructRandomString(data, max_recursion - 1);
441 }
442 // Build the cons string.
443 Handle<String> root = factory->NewConsString(left, right).ToHandleChecked();
444 CHECK(root->IsConsString() && !root->IsFlat());
445 // Special work needed for flat string.
446 if (flat) {
447 data->stats_.empty_leaves_++;
448 String::Flatten(root);
449 CHECK(root->IsConsString() && root->IsFlat());
450 }
451 return root;
452}
453
454
455static Handle<String> ConstructLeft(
456 ConsStringGenerationData* data,
457 int depth) {
458 Factory* factory = CcTest::i_isolate()->factory();
459 Handle<String> answer = factory->NewStringFromStaticChars("");
460 data->stats_.leaves_++;
461 for (int i = 0; i < depth; i++) {
462 Handle<String> block = data->block(i);
463 Handle<String> next =
464 factory->NewConsString(answer, block).ToHandleChecked();
465 if (next->IsConsString()) data->stats_.leaves_++;
466 data->stats_.chars_ += block->length();
467 answer = next;
468 }
469 data->stats_.left_traversals_ = data->stats_.leaves_ - 2;
Steve Blocka7e24c12009-10-30 11:49:00 +0000470 return answer;
471}
472
473
474static Handle<String> ConstructRight(
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000475 ConsStringGenerationData* data,
Steve Blocka7e24c12009-10-30 11:49:00 +0000476 int depth) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000477 Factory* factory = CcTest::i_isolate()->factory();
478 Handle<String> answer = factory->NewStringFromStaticChars("");
479 data->stats_.leaves_++;
Steve Blocka7e24c12009-10-30 11:49:00 +0000480 for (int i = depth - 1; i >= 0; i--) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000481 Handle<String> block = data->block(i);
482 Handle<String> next =
483 factory->NewConsString(block, answer).ToHandleChecked();
484 if (next->IsConsString()) data->stats_.leaves_++;
485 data->stats_.chars_ += block->length();
486 answer = next;
Steve Blocka7e24c12009-10-30 11:49:00 +0000487 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000488 data->stats_.right_traversals_ = data->stats_.leaves_ - 2;
Steve Blocka7e24c12009-10-30 11:49:00 +0000489 return answer;
490}
491
492
493static Handle<String> ConstructBalancedHelper(
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000494 ConsStringGenerationData* data,
Steve Blocka7e24c12009-10-30 11:49:00 +0000495 int from,
496 int to) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000497 Factory* factory = CcTest::i_isolate()->factory();
Steve Blocka7e24c12009-10-30 11:49:00 +0000498 CHECK(to > from);
499 if (to - from == 1) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000500 data->stats_.chars_ += data->block(from)->length();
501 return data->block(from);
Steve Blocka7e24c12009-10-30 11:49:00 +0000502 }
503 if (to - from == 2) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000504 data->stats_.chars_ += data->block(from)->length();
505 data->stats_.chars_ += data->block(from+1)->length();
506 return factory->NewConsString(data->block(from), data->block(from+1))
507 .ToHandleChecked();
Steve Blocka7e24c12009-10-30 11:49:00 +0000508 }
509 Handle<String> part1 =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000510 ConstructBalancedHelper(data, from, from + ((to - from) / 2));
Steve Blocka7e24c12009-10-30 11:49:00 +0000511 Handle<String> part2 =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000512 ConstructBalancedHelper(data, from + ((to - from) / 2), to);
513 if (part1->IsConsString()) data->stats_.left_traversals_++;
514 if (part2->IsConsString()) data->stats_.right_traversals_++;
515 return factory->NewConsString(part1, part2).ToHandleChecked();
Steve Blocka7e24c12009-10-30 11:49:00 +0000516}
517
518
519static Handle<String> ConstructBalanced(
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000520 ConsStringGenerationData* data, int depth = DEEP_DEPTH) {
521 Handle<String> string = ConstructBalancedHelper(data, 0, depth);
522 data->stats_.leaves_ =
523 data->stats_.left_traversals_ + data->stats_.right_traversals_ + 2;
524 return string;
Steve Blocka7e24c12009-10-30 11:49:00 +0000525}
526
527
Steve Blocka7e24c12009-10-30 11:49:00 +0000528static void Traverse(Handle<String> s1, Handle<String> s2) {
529 int i = 0;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400530 StringCharacterStream character_stream_1(*s1);
531 StringCharacterStream character_stream_2(*s2);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000532 while (character_stream_1.HasMore()) {
533 CHECK(character_stream_2.HasMore());
534 uint16_t c = character_stream_1.GetNext();
535 CHECK_EQ(c, character_stream_2.GetNext());
Steve Blocka7e24c12009-10-30 11:49:00 +0000536 i++;
537 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000538 CHECK(!character_stream_1.HasMore());
539 CHECK(!character_stream_2.HasMore());
Steve Blocka7e24c12009-10-30 11:49:00 +0000540 CHECK_EQ(s1->length(), i);
541 CHECK_EQ(s2->length(), i);
542}
543
544
545static void TraverseFirst(Handle<String> s1, Handle<String> s2, int chars) {
546 int i = 0;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400547 StringCharacterStream character_stream_1(*s1);
548 StringCharacterStream character_stream_2(*s2);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000549 while (character_stream_1.HasMore() && i < chars) {
550 CHECK(character_stream_2.HasMore());
551 uint16_t c = character_stream_1.GetNext();
552 CHECK_EQ(c, character_stream_2.GetNext());
Steve Blocka7e24c12009-10-30 11:49:00 +0000553 i++;
554 }
555 s1->Get(s1->length() - 1);
556 s2->Get(s2->length() - 1);
557}
558
559
560TEST(Traverse) {
561 printf("TestTraverse\n");
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000562 CcTest::InitializeVM();
563 v8::HandleScope scope(CcTest::isolate());
564 ConsStringGenerationData data(false);
565 Handle<String> flat = ConstructBalanced(&data);
566 String::Flatten(flat);
567 Handle<String> left_asymmetric = ConstructLeft(&data, DEEP_DEPTH);
568 Handle<String> right_asymmetric = ConstructRight(&data, DEEP_DEPTH);
569 Handle<String> symmetric = ConstructBalanced(&data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000570 printf("1\n");
571 Traverse(flat, symmetric);
572 printf("2\n");
573 Traverse(flat, left_asymmetric);
574 printf("3\n");
575 Traverse(flat, right_asymmetric);
576 printf("4\n");
577 Handle<String> left_deep_asymmetric =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000578 ConstructLeft(&data, SUPER_DEEP_DEPTH);
Steve Blocka7e24c12009-10-30 11:49:00 +0000579 Handle<String> right_deep_asymmetric =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000580 ConstructRight(&data, SUPER_DEEP_DEPTH);
Steve Blocka7e24c12009-10-30 11:49:00 +0000581 printf("5\n");
582 TraverseFirst(left_asymmetric, left_deep_asymmetric, 1050);
583 printf("6\n");
584 TraverseFirst(left_asymmetric, right_deep_asymmetric, 65536);
585 printf("7\n");
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000586 String::Flatten(left_asymmetric);
Steve Blocka7e24c12009-10-30 11:49:00 +0000587 printf("10\n");
588 Traverse(flat, left_asymmetric);
589 printf("11\n");
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000590 String::Flatten(right_asymmetric);
Steve Blocka7e24c12009-10-30 11:49:00 +0000591 printf("12\n");
592 Traverse(flat, right_asymmetric);
593 printf("14\n");
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000594 String::Flatten(symmetric);
Steve Blocka7e24c12009-10-30 11:49:00 +0000595 printf("15\n");
596 Traverse(flat, symmetric);
597 printf("16\n");
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000598 String::Flatten(left_deep_asymmetric);
Steve Blocka7e24c12009-10-30 11:49:00 +0000599 printf("18\n");
600}
601
602
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000603static void VerifyCharacterStream(
604 String* flat_string, String* cons_string) {
605 // Do not want to test ConString traversal on flat string.
606 CHECK(flat_string->IsFlat() && !flat_string->IsConsString());
607 CHECK(cons_string->IsConsString());
608 // TODO(dcarney) Test stream reset as well.
609 int length = flat_string->length();
610 // Iterate start search in multiple places in the string.
611 int outer_iterations = length > 20 ? 20 : length;
612 for (int j = 0; j <= outer_iterations; j++) {
613 int offset = length * j / outer_iterations;
614 if (offset < 0) offset = 0;
615 // Want to test the offset == length case.
616 if (offset > length) offset = length;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400617 StringCharacterStream flat_stream(flat_string, offset);
618 StringCharacterStream cons_stream(cons_string, offset);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000619 for (int i = offset; i < length; i++) {
620 uint16_t c = flat_string->Get(i);
621 CHECK(flat_stream.HasMore());
622 CHECK(cons_stream.HasMore());
623 CHECK_EQ(c, flat_stream.GetNext());
624 CHECK_EQ(c, cons_stream.GetNext());
625 }
626 CHECK(!flat_stream.HasMore());
627 CHECK(!cons_stream.HasMore());
628 }
629}
Steve Blocka7e24c12009-10-30 11:49:00 +0000630
631
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000632static inline void PrintStats(const ConsStringGenerationData& data) {
633#ifdef DEBUG
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400634 printf("%s: [%u], %s: [%u], %s: [%u], %s: [%u], %s: [%u], %s: [%u]\n",
635 "leaves", data.stats_.leaves_, "empty", data.stats_.empty_leaves_,
636 "chars", data.stats_.chars_, "lefts", data.stats_.left_traversals_,
637 "rights", data.stats_.right_traversals_, "early_terminations",
638 data.early_terminations_);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000639#endif
640}
Steve Blocka7e24c12009-10-30 11:49:00 +0000641
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000642
643template<typename BuildString>
644void TestStringCharacterStream(BuildString build, int test_cases) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000645 FLAG_gc_global = true;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000646 CcTest::InitializeVM();
647 Isolate* isolate = CcTest::i_isolate();
648 HandleScope outer_scope(isolate);
649 ConsStringGenerationData data(true);
650 for (int i = 0; i < test_cases; i++) {
651 printf("%d\n", i);
652 HandleScope inner_scope(isolate);
653 AlwaysAllocateScope always_allocate(isolate);
654 // Build flat version of cons string.
655 Handle<String> flat_string = build(i, &data);
656 ConsStringStats flat_string_stats;
657 AccumulateStats(flat_string, &flat_string_stats);
658 // Flatten string.
659 String::Flatten(flat_string);
660 // Build unflattened version of cons string to test.
661 Handle<String> cons_string = build(i, &data);
662 ConsStringStats cons_string_stats;
663 AccumulateStats(cons_string, &cons_string_stats);
664 DisallowHeapAllocation no_allocation;
665 PrintStats(data);
666 // Full verify of cons string.
667 cons_string_stats.VerifyEqual(flat_string_stats);
668 cons_string_stats.VerifyEqual(data.stats_);
669 VerifyConsString(cons_string, &data);
670 String* flat_string_ptr =
671 flat_string->IsConsString() ?
672 ConsString::cast(*flat_string)->first() :
673 *flat_string;
674 VerifyCharacterStream(flat_string_ptr, *cons_string);
675 }
676}
677
678
679static const int kCharacterStreamNonRandomCases = 8;
680
681
682static Handle<String> BuildEdgeCaseConsString(
683 int test_case, ConsStringGenerationData* data) {
684 Factory* factory = CcTest::i_isolate()->factory();
685 data->Reset();
686 switch (test_case) {
687 case 0:
688 return ConstructBalanced(data, 71);
689 case 1:
690 return ConstructLeft(data, 71);
691 case 2:
692 return ConstructRight(data, 71);
693 case 3:
694 return ConstructLeft(data, 10);
695 case 4:
696 return ConstructRight(data, 10);
697 case 5:
698 // 2 element balanced tree.
699 data->stats_.chars_ += data->block(0)->length();
700 data->stats_.chars_ += data->block(1)->length();
701 data->stats_.leaves_ += 2;
702 return factory->NewConsString(data->block(0), data->block(1))
703 .ToHandleChecked();
704 case 6:
705 // Simple flattened tree.
706 data->stats_.chars_ += data->block(0)->length();
707 data->stats_.chars_ += data->block(1)->length();
708 data->stats_.leaves_ += 2;
709 data->stats_.empty_leaves_ += 1;
710 {
711 Handle<String> string =
712 factory->NewConsString(data->block(0), data->block(1))
713 .ToHandleChecked();
714 String::Flatten(string);
715 return string;
716 }
717 case 7:
718 // Left node flattened.
719 data->stats_.chars_ += data->block(0)->length();
720 data->stats_.chars_ += data->block(1)->length();
721 data->stats_.chars_ += data->block(2)->length();
722 data->stats_.leaves_ += 3;
723 data->stats_.empty_leaves_ += 1;
724 data->stats_.left_traversals_ += 1;
725 {
726 Handle<String> left =
727 factory->NewConsString(data->block(0), data->block(1))
728 .ToHandleChecked();
729 String::Flatten(left);
730 return factory->NewConsString(left, data->block(2)).ToHandleChecked();
731 }
732 case 8:
733 // Left node and right node flattened.
734 data->stats_.chars_ += data->block(0)->length();
735 data->stats_.chars_ += data->block(1)->length();
736 data->stats_.chars_ += data->block(2)->length();
737 data->stats_.chars_ += data->block(3)->length();
738 data->stats_.leaves_ += 4;
739 data->stats_.empty_leaves_ += 2;
740 data->stats_.left_traversals_ += 1;
741 data->stats_.right_traversals_ += 1;
742 {
743 Handle<String> left =
744 factory->NewConsString(data->block(0), data->block(1))
745 .ToHandleChecked();
746 String::Flatten(left);
747 Handle<String> right =
748 factory->NewConsString(data->block(2), data->block(2))
749 .ToHandleChecked();
750 String::Flatten(right);
751 return factory->NewConsString(left, right).ToHandleChecked();
752 }
753 }
754 UNREACHABLE();
755 return Handle<String>();
756}
757
758
759TEST(StringCharacterStreamEdgeCases) {
760 printf("TestStringCharacterStreamEdgeCases\n");
761 TestStringCharacterStream(
762 BuildEdgeCaseConsString, kCharacterStreamNonRandomCases);
763}
764
765
766static const int kBalances = 3;
767static const int kTreeLengths = 4;
768static const int kEmptyLeaves = 4;
769static const int kUniqueRandomParameters =
770 kBalances*kTreeLengths*kEmptyLeaves;
771
772
773static void InitializeGenerationData(
774 int test_case, ConsStringGenerationData* data) {
775 // Clear the settings and reinit the rng.
776 data->Reset();
777 // Spin up the rng to a known location that is unique per test.
778 static const int kPerTestJump = 501;
779 for (int j = 0; j < test_case*kPerTestJump; j++) {
780 data->rng_.next();
781 }
782 // Choose balanced, left or right heavy trees.
783 switch (test_case % kBalances) {
784 case 0:
785 // Nothing to do. Already balanced.
786 break;
787 case 1:
788 // Left balanced.
789 data->leftness_ = 0.90;
790 data->rightness_ = 0.15;
791 break;
792 case 2:
793 // Right balanced.
794 data->leftness_ = 0.15;
795 data->rightness_ = 0.90;
796 break;
797 default:
798 UNREACHABLE();
799 break;
800 }
801 // Must remove the influence of the above decision.
802 test_case /= kBalances;
803 // Choose tree length.
804 switch (test_case % kTreeLengths) {
805 case 0:
806 data->max_leaves_ = 16;
807 data->early_termination_threshold_ = 0.2;
808 break;
809 case 1:
810 data->max_leaves_ = 50;
811 data->early_termination_threshold_ = 0.05;
812 break;
813 case 2:
814 data->max_leaves_ = 500;
815 data->early_termination_threshold_ = 0.03;
816 break;
817 case 3:
818 data->max_leaves_ = 5000;
819 data->early_termination_threshold_ = 0.001;
820 break;
821 default:
822 UNREACHABLE();
823 break;
824 }
825 // Must remove the influence of the above decision.
826 test_case /= kTreeLengths;
827 // Choose how much we allow empty nodes, including not at all.
828 data->empty_leaf_threshold_ =
829 0.03 * static_cast<double>(test_case % kEmptyLeaves);
830}
831
832
833static Handle<String> BuildRandomConsString(
834 int test_case, ConsStringGenerationData* data) {
835 InitializeGenerationData(test_case, data);
836 return ConstructRandomString(data, 200);
837}
838
839
840TEST(StringCharacterStreamRandom) {
841 printf("StringCharacterStreamRandom\n");
842 TestStringCharacterStream(BuildRandomConsString, kUniqueRandomParameters*7);
843}
844
845
846static const int kDeepOneByteDepth = 100000;
847
848
849TEST(DeepOneByte) {
850 CcTest::InitializeVM();
851 Factory* factory = CcTest::i_isolate()->factory();
852 v8::HandleScope scope(CcTest::isolate());
853
854 char* foo = NewArray<char>(kDeepOneByteDepth);
855 for (int i = 0; i < kDeepOneByteDepth; i++) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000856 foo[i] = "foo "[i % 4];
857 }
858 Handle<String> string =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000859 factory->NewStringFromOneByte(OneByteVector(foo, kDeepOneByteDepth))
860 .ToHandleChecked();
861 Handle<String> foo_string = factory->NewStringFromStaticChars("foo");
862 for (int i = 0; i < kDeepOneByteDepth; i += 10) {
863 string = factory->NewConsString(string, foo_string).ToHandleChecked();
Steve Blocka7e24c12009-10-30 11:49:00 +0000864 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000865 Handle<String> flat_string =
866 factory->NewConsString(string, foo_string).ToHandleChecked();
867 String::Flatten(flat_string);
Steve Blocka7e24c12009-10-30 11:49:00 +0000868
869 for (int i = 0; i < 500; i++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000870 TraverseFirst(flat_string, string, kDeepOneByteDepth);
Steve Blocka7e24c12009-10-30 11:49:00 +0000871 }
872 DeleteArray<char>(foo);
873}
874
875
876TEST(Utf8Conversion) {
877 // Smoke test for converting strings to utf-8.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000878 CcTest::InitializeVM();
879 v8::HandleScope handle_scope(CcTest::isolate());
880 // A simple one-byte string
881 const char* one_byte_string = "abcdef12345";
882 int len = v8::String::NewFromUtf8(CcTest::isolate(), one_byte_string,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000883 v8::NewStringType::kNormal,
884 StrLength(one_byte_string))
885 .ToLocalChecked()
886 ->Utf8Length();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000887 CHECK_EQ(StrLength(one_byte_string), len);
888 // A mixed one-byte and two-byte string
Steve Blocka7e24c12009-10-30 11:49:00 +0000889 // U+02E4 -> CB A4
890 // U+0064 -> 64
891 // U+12E4 -> E1 8B A4
892 // U+0030 -> 30
893 // U+3045 -> E3 81 85
894 const uint16_t mixed_string[] = {0x02E4, 0x0064, 0x12E4, 0x0030, 0x3045};
895 // The characters we expect to be output
896 const unsigned char as_utf8[11] = {0xCB, 0xA4, 0x64, 0xE1, 0x8B, 0xA4, 0x30,
897 0xE3, 0x81, 0x85, 0x00};
898 // The number of bytes expected to be written for each length
899 const int lengths[12] = {0, 0, 2, 3, 3, 3, 6, 7, 7, 7, 10, 11};
Steve Block6ded16b2010-05-10 14:33:55 +0100900 const int char_lengths[12] = {0, 0, 1, 2, 2, 2, 3, 4, 4, 4, 5, 5};
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000901 v8::Local<v8::String> mixed =
902 v8::String::NewFromTwoByte(CcTest::isolate(), mixed_string,
903 v8::NewStringType::kNormal, 5)
904 .ToLocalChecked();
Steve Blocka7e24c12009-10-30 11:49:00 +0000905 CHECK_EQ(10, mixed->Utf8Length());
906 // Try encoding the string with all capacities
907 char buffer[11];
908 const char kNoChar = static_cast<char>(-1);
909 for (int i = 0; i <= 11; i++) {
910 // Clear the buffer before reusing it
911 for (int j = 0; j < 11; j++)
912 buffer[j] = kNoChar;
Steve Block6ded16b2010-05-10 14:33:55 +0100913 int chars_written;
914 int written = mixed->WriteUtf8(buffer, i, &chars_written);
Steve Blocka7e24c12009-10-30 11:49:00 +0000915 CHECK_EQ(lengths[i], written);
Steve Block6ded16b2010-05-10 14:33:55 +0100916 CHECK_EQ(char_lengths[i], chars_written);
Steve Blocka7e24c12009-10-30 11:49:00 +0000917 // Check that the contents are correct
918 for (int j = 0; j < lengths[i]; j++)
919 CHECK_EQ(as_utf8[j], static_cast<unsigned char>(buffer[j]));
920 // Check that the rest of the buffer hasn't been touched
921 for (int j = lengths[i]; j < 11; j++)
922 CHECK_EQ(kNoChar, buffer[j]);
923 }
924}
925
926
Steve Blockd0582a62009-12-15 09:54:21 +0000927TEST(ExternalShortStringAdd) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000928 LocalContext context;
929 v8::HandleScope handle_scope(CcTest::isolate());
Steve Blocka7e24c12009-10-30 11:49:00 +0000930
Steve Blockd0582a62009-12-15 09:54:21 +0000931 // Make sure we cover all always-flat lengths and at least one above.
932 static const int kMaxLength = 20;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100933 CHECK_GT(kMaxLength, i::ConsString::kMinLength);
Steve Blocka7e24c12009-10-30 11:49:00 +0000934
Steve Blockd0582a62009-12-15 09:54:21 +0000935 // Allocate two JavaScript arrays for holding short strings.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000936 v8::Local<v8::Array> one_byte_external_strings =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000937 v8::Array::New(CcTest::isolate(), kMaxLength + 1);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000938 v8::Local<v8::Array> non_one_byte_external_strings =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000939 v8::Array::New(CcTest::isolate(), kMaxLength + 1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000940
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000941 // Generate short one-byte and two-byte external strings.
Steve Blockd0582a62009-12-15 09:54:21 +0000942 for (int i = 0; i <= kMaxLength; i++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000943 char* one_byte = NewArray<char>(i + 1);
Steve Blockd0582a62009-12-15 09:54:21 +0000944 for (int j = 0; j < i; j++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000945 one_byte[j] = 'a';
Steve Blocka7e24c12009-10-30 11:49:00 +0000946 }
Steve Blockd0582a62009-12-15 09:54:21 +0000947 // Terminating '\0' is left out on purpose. It is not required for external
948 // string data.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000949 OneByteResource* one_byte_resource = new OneByteResource(one_byte, i);
950 v8::Local<v8::String> one_byte_external_string =
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000951 v8::String::NewExternalOneByte(CcTest::isolate(), one_byte_resource)
952 .ToLocalChecked();
Steve Blocka7e24c12009-10-30 11:49:00 +0000953
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000954 one_byte_external_strings->Set(context.local(),
955 v8::Integer::New(CcTest::isolate(), i),
956 one_byte_external_string)
957 .FromJust();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000958 uc16* non_one_byte = NewArray<uc16>(i + 1);
Steve Blockd0582a62009-12-15 09:54:21 +0000959 for (int j = 0; j < i; j++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000960 non_one_byte[j] = 0x1234;
Steve Blockd0582a62009-12-15 09:54:21 +0000961 }
962 // Terminating '\0' is left out on purpose. It is not required for external
963 // string data.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000964 Resource* resource = new Resource(non_one_byte, i);
965 v8::Local<v8::String> non_one_byte_external_string =
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000966 v8::String::NewExternalTwoByte(CcTest::isolate(), resource)
967 .ToLocalChecked();
968 non_one_byte_external_strings->Set(context.local(),
969 v8::Integer::New(CcTest::isolate(), i),
970 non_one_byte_external_string)
971 .FromJust();
Steve Blocka7e24c12009-10-30 11:49:00 +0000972 }
973
Steve Blockd0582a62009-12-15 09:54:21 +0000974 // Add the arrays with the short external strings in the global object.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000975 v8::Local<v8::Object> global = context->Global();
976 global->Set(context.local(), v8_str("external_one_byte"),
977 one_byte_external_strings)
978 .FromJust();
979 global->Set(context.local(), v8_str("external_non_one_byte"),
980 non_one_byte_external_strings)
981 .FromJust();
982 global->Set(context.local(), v8_str("max_length"),
983 v8::Integer::New(CcTest::isolate(), kMaxLength))
984 .FromJust();
Steve Blocka7e24c12009-10-30 11:49:00 +0000985
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000986 // Add short external one-byte and two-byte strings checking the result.
Steve Blockd0582a62009-12-15 09:54:21 +0000987 static const char* source =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000988 "function test() {"
989 " var one_byte_chars = 'aaaaaaaaaaaaaaaaaaaa';"
990 " var non_one_byte_chars = "
991 "'\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1"
992 "234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\"
993 "u1234';" // NOLINT
994 " if (one_byte_chars.length != max_length) return 1;"
995 " if (non_one_byte_chars.length != max_length) return 2;"
996 " var one_byte = Array(max_length + 1);"
997 " var non_one_byte = Array(max_length + 1);"
998 " for (var i = 0; i <= max_length; i++) {"
999 " one_byte[i] = one_byte_chars.substring(0, i);"
1000 " non_one_byte[i] = non_one_byte_chars.substring(0, i);"
1001 " };"
1002 " for (var i = 0; i <= max_length; i++) {"
1003 " if (one_byte[i] != external_one_byte[i]) return 3;"
1004 " if (non_one_byte[i] != external_non_one_byte[i]) return 4;"
1005 " for (var j = 0; j < i; j++) {"
1006 " if (external_one_byte[i] !="
1007 " (external_one_byte[j] + external_one_byte[i - j])) return "
1008 "5;"
1009 " if (external_non_one_byte[i] !="
1010 " (external_non_one_byte[j] + external_non_one_byte[i - "
1011 "j])) return 6;"
1012 " if (non_one_byte[i] != (non_one_byte[j] + non_one_byte[i - "
1013 "j])) return 7;"
1014 " if (one_byte[i] != (one_byte[j] + one_byte[i - j])) return 8;"
1015 " if (one_byte[i] != (external_one_byte[j] + one_byte[i - j])) "
1016 "return 9;"
1017 " if (one_byte[i] != (one_byte[j] + external_one_byte[i - j])) "
1018 "return 10;"
1019 " if (non_one_byte[i] !="
1020 " (external_non_one_byte[j] + non_one_byte[i - j])) return "
1021 "11;"
1022 " if (non_one_byte[i] !="
1023 " (non_one_byte[j] + external_non_one_byte[i - j])) return "
1024 "12;"
1025 " }"
1026 " }"
1027 " return 0;"
1028 "};"
1029 "test()";
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001030 CHECK_EQ(0, CompileRun(source)->Int32Value(context.local()).FromJust());
Steve Blocka7e24c12009-10-30 11:49:00 +00001031}
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001032
1033
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001034TEST(JSONStringifySliceMadeExternal) {
1035 CcTest::InitializeVM();
1036 // Create a sliced string from a one-byte string. The latter is turned
1037 // into a two-byte external string. Check that JSON.stringify works.
1038 v8::HandleScope handle_scope(CcTest::isolate());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001039 v8::Local<v8::String> underlying =
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001040 CompileRun(
1041 "var underlying = 'abcdefghijklmnopqrstuvwxyz';"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001042 "underlying")
1043 ->ToString(CcTest::isolate()->GetCurrentContext())
1044 .ToLocalChecked();
1045 v8::Local<v8::String> slice =
1046 CompileRun(
1047 "var slice = '';"
1048 "slice = underlying.slice(1);"
1049 "slice")
1050 ->ToString(CcTest::isolate()->GetCurrentContext())
1051 .ToLocalChecked();
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001052 CHECK(v8::Utils::OpenHandle(*slice)->IsSlicedString());
1053 CHECK(v8::Utils::OpenHandle(*underlying)->IsSeqOneByteString());
1054
1055 int length = underlying->Length();
1056 uc16* two_byte = NewArray<uc16>(length + 1);
1057 underlying->Write(two_byte);
1058 Resource* resource = new Resource(two_byte, length);
1059 CHECK(underlying->MakeExternal(resource));
1060 CHECK(v8::Utils::OpenHandle(*slice)->IsSlicedString());
1061 CHECK(v8::Utils::OpenHandle(*underlying)->IsExternalTwoByteString());
1062
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001063 CHECK_EQ(0,
1064 strcmp("\"bcdefghijklmnopqrstuvwxyz\"",
1065 *v8::String::Utf8Value(CompileRun("JSON.stringify(slice)"))));
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001066}
1067
1068
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001069TEST(CachedHashOverflow) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001070 CcTest::InitializeVM();
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001071 // We incorrectly allowed strings to be tagged as array indices even if their
1072 // values didn't fit in the hash field.
1073 // See http://code.google.com/p/v8/issues/detail?id=728
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001074 Isolate* isolate = CcTest::i_isolate();
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001075
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001076 v8::HandleScope handle_scope(CcTest::isolate());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001077 // Lines must be executed sequentially. Combining them into one script
1078 // makes the bug go away.
1079 const char* lines[] = {
1080 "var x = [];",
1081 "x[4] = 42;",
1082 "var s = \"1073741828\";",
1083 "x[s];",
1084 "x[s] = 37;",
1085 "x[4];",
1086 "x[s];",
1087 NULL
1088 };
1089
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001090 Handle<Smi> fortytwo(Smi::FromInt(42), isolate);
1091 Handle<Smi> thirtyseven(Smi::FromInt(37), isolate);
1092 Handle<Object> results[] = { isolate->factory()->undefined_value(),
1093 fortytwo,
1094 isolate->factory()->undefined_value(),
1095 isolate->factory()->undefined_value(),
1096 thirtyseven,
1097 fortytwo,
1098 thirtyseven // Bug yielded 42 here.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001099 };
1100
1101 const char* line;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001102 v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext();
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001103 for (int i = 0; (line = lines[i]); i++) {
1104 printf("%s\n", line);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001105 v8::Local<v8::Value> result =
1106 v8::Script::Compile(context,
1107 v8::String::NewFromUtf8(CcTest::isolate(), line,
1108 v8::NewStringType::kNormal)
1109 .ToLocalChecked())
1110 .ToLocalChecked()
1111 ->Run(context)
1112 .ToLocalChecked();
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001113 CHECK_EQ(results[i]->IsUndefined(), result->IsUndefined());
1114 CHECK_EQ(results[i]->IsNumber(), result->IsNumber());
1115 if (result->IsNumber()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001116 int32_t value = 0;
1117 CHECK(results[i]->ToInt32(&value));
1118 CHECK_EQ(value, result->ToInt32(context).ToLocalChecked()->Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001119 }
1120 }
1121}
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001122
1123
1124TEST(SliceFromCons) {
1125 FLAG_string_slices = true;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001126 CcTest::InitializeVM();
1127 Factory* factory = CcTest::i_isolate()->factory();
1128 v8::HandleScope scope(CcTest::isolate());
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001129 Handle<String> string =
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001130 factory->NewStringFromStaticChars("parentparentparent");
1131 Handle<String> parent =
1132 factory->NewConsString(string, string).ToHandleChecked();
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001133 CHECK(parent->IsConsString());
1134 CHECK(!parent->IsFlat());
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001135 Handle<String> slice = factory->NewSubString(parent, 1, 25);
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001136 // After slicing, the original string becomes a flat cons.
1137 CHECK(parent->IsFlat());
1138 CHECK(slice->IsSlicedString());
1139 CHECK_EQ(SlicedString::cast(*slice)->parent(),
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001140 // Parent could have been short-circuited.
1141 parent->IsConsString() ? ConsString::cast(*parent)->first()
1142 : *parent);
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001143 CHECK(SlicedString::cast(*slice)->parent()->IsSeqString());
1144 CHECK(slice->IsFlat());
1145}
1146
1147
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001148class OneByteVectorResource : public v8::String::ExternalOneByteStringResource {
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001149 public:
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001150 explicit OneByteVectorResource(i::Vector<const char> vector)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001151 : data_(vector) {}
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001152 virtual ~OneByteVectorResource() {}
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001153 virtual size_t length() const { return data_.length(); }
1154 virtual const char* data() const { return data_.start(); }
1155 private:
1156 i::Vector<const char> data_;
1157};
1158
1159
1160TEST(SliceFromExternal) {
1161 FLAG_string_slices = true;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001162 CcTest::InitializeVM();
1163 Factory* factory = CcTest::i_isolate()->factory();
1164 v8::HandleScope scope(CcTest::isolate());
1165 OneByteVectorResource resource(
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001166 i::Vector<const char>("abcdefghijklmnopqrstuvwxyz", 26));
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001167 Handle<String> string =
1168 factory->NewExternalStringFromOneByte(&resource).ToHandleChecked();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001169 CHECK(string->IsExternalString());
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001170 Handle<String> slice = factory->NewSubString(string, 1, 25);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001171 CHECK(slice->IsSlicedString());
1172 CHECK(string->IsExternalString());
1173 CHECK_EQ(SlicedString::cast(*slice)->parent(), *string);
1174 CHECK(SlicedString::cast(*slice)->parent()->IsExternalString());
1175 CHECK(slice->IsFlat());
1176}
1177
1178
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001179TEST(TrivialSlice) {
1180 // This tests whether a slice that contains the entire parent string
1181 // actually creates a new string (it should not).
1182 FLAG_string_slices = true;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001183 CcTest::InitializeVM();
1184 Factory* factory = CcTest::i_isolate()->factory();
1185 v8::HandleScope scope(CcTest::isolate());
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001186 v8::Local<v8::Value> result;
1187 Handle<String> string;
1188 const char* init = "var str = 'abcdefghijklmnopqrstuvwxyz';";
1189 const char* check = "str.slice(0,26)";
1190 const char* crosscheck = "str.slice(1,25)";
1191
1192 CompileRun(init);
1193
1194 result = CompileRun(check);
1195 CHECK(result->IsString());
1196 string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1197 CHECK(!string->IsSlicedString());
1198
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001199 string = factory->NewSubString(string, 0, 26);
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001200 CHECK(!string->IsSlicedString());
1201 result = CompileRun(crosscheck);
1202 CHECK(result->IsString());
1203 string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1204 CHECK(string->IsSlicedString());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001205 CHECK_EQ(0, strcmp("bcdefghijklmnopqrstuvwxy", string->ToCString().get()));
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001206}
Ben Murdoch589d6972011-11-30 16:04:58 +00001207
1208
1209TEST(SliceFromSlice) {
1210 // This tests whether a slice that contains the entire parent string
1211 // actually creates a new string (it should not).
1212 FLAG_string_slices = true;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001213 CcTest::InitializeVM();
1214 v8::HandleScope scope(CcTest::isolate());
Ben Murdoch589d6972011-11-30 16:04:58 +00001215 v8::Local<v8::Value> result;
1216 Handle<String> string;
1217 const char* init = "var str = 'abcdefghijklmnopqrstuvwxyz';";
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001218 const char* slice = "var slice = ''; slice = str.slice(1,-1); slice";
Ben Murdoch589d6972011-11-30 16:04:58 +00001219 const char* slice_from_slice = "slice.slice(1,-1);";
1220
1221 CompileRun(init);
1222 result = CompileRun(slice);
1223 CHECK(result->IsString());
1224 string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1225 CHECK(string->IsSlicedString());
1226 CHECK(SlicedString::cast(*string)->parent()->IsSeqString());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001227 CHECK_EQ(0, strcmp("bcdefghijklmnopqrstuvwxy", string->ToCString().get()));
Ben Murdoch589d6972011-11-30 16:04:58 +00001228
1229 result = CompileRun(slice_from_slice);
1230 CHECK(result->IsString());
1231 string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1232 CHECK(string->IsSlicedString());
1233 CHECK(SlicedString::cast(*string)->parent()->IsSeqString());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001234 CHECK_EQ(0, strcmp("cdefghijklmnopqrstuvwx", string->ToCString().get()));
Ben Murdoch589d6972011-11-30 16:04:58 +00001235}
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001236
1237
1238UNINITIALIZED_TEST(OneByteArrayJoin) {
1239 v8::Isolate::CreateParams create_params;
1240 // Set heap limits.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001241 create_params.constraints.set_max_semi_space_size(1 * Page::kPageSize / MB);
1242 create_params.constraints.set_max_old_space_size(6 * Page::kPageSize / MB);
1243 create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001244 v8::Isolate* isolate = v8::Isolate::New(create_params);
1245 isolate->Enter();
1246
1247 {
1248 // String s is made of 2^17 = 131072 'c' characters and a is an array
1249 // starting with 'bad', followed by 2^14 times the string s. That means the
1250 // total length of the concatenated strings is 2^31 + 3. So on 32bit systems
1251 // summing the lengths of the strings (as Smis) overflows and wraps.
1252 LocalContext context(isolate);
1253 v8::HandleScope scope(isolate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001254 v8::TryCatch try_catch(isolate);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001255 CHECK(CompileRun(
1256 "var two_14 = Math.pow(2, 14);"
1257 "var two_17 = Math.pow(2, 17);"
1258 "var s = Array(two_17 + 1).join('c');"
1259 "var a = ['bad'];"
1260 "for (var i = 1; i <= two_14; i++) a.push(s);"
1261 "a.join("
1262 ");").IsEmpty());
1263 CHECK(try_catch.HasCaught());
1264 }
1265 isolate->Exit();
1266 isolate->Dispose();
1267}
1268
1269
1270static void CheckException(const char* source) {
1271 // An empty handle is returned upon exception.
1272 CHECK(CompileRun(source).IsEmpty());
1273}
1274
1275
1276TEST(RobustSubStringStub) {
1277 // This tests whether the SubStringStub can handle unsafe arguments.
1278 // If not recognized, those unsafe arguments lead to out-of-bounds reads.
1279 FLAG_allow_natives_syntax = true;
1280 CcTest::InitializeVM();
1281 v8::HandleScope scope(CcTest::isolate());
1282 v8::Local<v8::Value> result;
1283 Handle<String> string;
1284 CompileRun("var short = 'abcdef';");
1285
1286 // Invalid indices.
1287 CheckException("%_SubString(short, 0, 10000);");
1288 CheckException("%_SubString(short, -1234, 5);");
1289 CheckException("%_SubString(short, 5, 2);");
1290 // Special HeapNumbers.
1291 CheckException("%_SubString(short, 1, Infinity);");
1292 CheckException("%_SubString(short, NaN, 5);");
1293 // String arguments.
1294 CheckException("%_SubString(short, '2', '5');");
1295 // Ordinary HeapNumbers can be handled (in runtime).
1296 result = CompileRun("%_SubString(short, Math.sqrt(4), 5.1);");
1297 string = v8::Utils::OpenHandle(v8::String::Cast(*result));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001298 CHECK_EQ(0, strcmp("cde", string->ToCString().get()));
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001299
1300 CompileRun("var long = 'abcdefghijklmnopqrstuvwxyz';");
1301 // Invalid indices.
1302 CheckException("%_SubString(long, 0, 10000);");
1303 CheckException("%_SubString(long, -1234, 17);");
1304 CheckException("%_SubString(long, 17, 2);");
1305 // Special HeapNumbers.
1306 CheckException("%_SubString(long, 1, Infinity);");
1307 CheckException("%_SubString(long, NaN, 17);");
1308 // String arguments.
1309 CheckException("%_SubString(long, '2', '17');");
1310 // Ordinary HeapNumbers within bounds can be handled (in runtime).
1311 result = CompileRun("%_SubString(long, Math.sqrt(4), 17.1);");
1312 string = v8::Utils::OpenHandle(v8::String::Cast(*result));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001313 CHECK_EQ(0, strcmp("cdefghijklmnopq", string->ToCString().get()));
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001314
1315 // Test that out-of-bounds substring of a slice fails when the indices
1316 // would have been valid for the underlying string.
1317 CompileRun("var slice = long.slice(1, 15);");
1318 CheckException("%_SubString(slice, 0, 17);");
1319}
1320
1321
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001322namespace {
1323
1324int* global_use_counts = NULL;
1325
1326void MockUseCounterCallback(v8::Isolate* isolate,
1327 v8::Isolate::UseCounterFeature feature) {
1328 ++global_use_counts[feature];
1329}
1330}
1331
1332
1333TEST(CountBreakIterator) {
1334 CcTest::InitializeVM();
1335 v8::HandleScope scope(CcTest::isolate());
1336 LocalContext context;
1337 int use_counts[v8::Isolate::kUseCounterFeatureCount] = {};
1338 global_use_counts = use_counts;
1339 CcTest::isolate()->SetUseCounterCallback(MockUseCounterCallback);
1340 CHECK_EQ(0, use_counts[v8::Isolate::kBreakIterator]);
1341 v8::Local<v8::Value> result = CompileRun(
1342 "(function() {"
1343 " if (!this.Intl) return 0;"
1344 " var iterator = Intl.v8BreakIterator(['en']);"
1345 " iterator.adoptText('Now is the time');"
1346 " iterator.next();"
1347 " return iterator.next();"
1348 "})();");
1349 CHECK(result->IsNumber());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001350 int uses =
1351 result->ToInt32(context.local()).ToLocalChecked()->Value() == 0 ? 0 : 1;
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001352 CHECK_EQ(uses, use_counts[v8::Isolate::kBreakIterator]);
1353 // Make sure GC cleans up the break iterator, so we don't get a memory leak
1354 // reported by ASAN.
1355 CcTest::isolate()->LowMemoryNotification();
1356}
1357
1358
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001359TEST(StringReplaceAtomTwoByteResult) {
1360 CcTest::InitializeVM();
1361 v8::HandleScope scope(CcTest::isolate());
1362 LocalContext context;
1363 v8::Local<v8::Value> result = CompileRun(
1364 "var subject = 'one_byte~only~string~'; "
1365 "var replace = '\x80'; "
1366 "subject.replace(/~/g, replace); ");
1367 CHECK(result->IsString());
1368 Handle<String> string = v8::Utils::OpenHandle(v8::String::Cast(*result));
1369 CHECK(string->IsSeqTwoByteString());
1370
1371 v8::Local<v8::String> expected = v8_str("one_byte\x80only\x80string\x80");
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001372 CHECK(expected->Equals(context.local(), result).FromJust());
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001373}
1374
1375
1376TEST(IsAscii) {
1377 CHECK(String::IsAscii(static_cast<char*>(NULL), 0));
1378 CHECK(String::IsOneByte(static_cast<uc16*>(NULL), 0));
1379}
1380
1381
1382
1383template<typename Op, bool return_first>
1384static uint16_t ConvertLatin1(uint16_t c) {
1385 uint32_t result[Op::kMaxWidth];
1386 int chars;
1387 chars = Op::Convert(c, 0, result, NULL);
1388 if (chars == 0) return 0;
1389 CHECK_LE(chars, static_cast<int>(sizeof(result)));
1390 if (!return_first && chars > 1) {
1391 return 0;
1392 }
1393 return result[0];
1394}
1395
1396
1397static void CheckCanonicalEquivalence(uint16_t c, uint16_t test) {
1398 uint16_t expect = ConvertLatin1<unibrow::Ecma262UnCanonicalize, true>(c);
1399 if (expect > unibrow::Latin1::kMaxChar) expect = 0;
1400 CHECK_EQ(expect, test);
1401}
1402
1403
1404TEST(Latin1IgnoreCase) {
1405 using namespace unibrow;
1406 for (uint16_t c = Latin1::kMaxChar + 1; c != 0; c++) {
1407 uint16_t lower = ConvertLatin1<ToLowercase, false>(c);
1408 uint16_t upper = ConvertLatin1<ToUppercase, false>(c);
1409 uint16_t test = Latin1::ConvertNonLatin1ToLatin1(c);
1410 // Filter out all character whose upper is not their lower or vice versa.
1411 if (lower == 0 && upper == 0) {
1412 CheckCanonicalEquivalence(c, test);
1413 continue;
1414 }
1415 if (lower > Latin1::kMaxChar && upper > Latin1::kMaxChar) {
1416 CheckCanonicalEquivalence(c, test);
1417 continue;
1418 }
1419 if (lower == 0 && upper != 0) {
1420 lower = ConvertLatin1<ToLowercase, false>(upper);
1421 }
1422 if (upper == 0 && lower != c) {
1423 upper = ConvertLatin1<ToUppercase, false>(lower);
1424 }
1425 if (lower > Latin1::kMaxChar && upper > Latin1::kMaxChar) {
1426 CheckCanonicalEquivalence(c, test);
1427 continue;
1428 }
1429 if (upper != c && lower != c) {
1430 CheckCanonicalEquivalence(c, test);
1431 continue;
1432 }
1433 CHECK_EQ(Min(upper, lower), test);
1434 }
1435}
1436
1437
1438class DummyResource: public v8::String::ExternalStringResource {
1439 public:
1440 virtual const uint16_t* data() const { return NULL; }
1441 virtual size_t length() const { return 1 << 30; }
1442};
1443
1444
1445class DummyOneByteResource: public v8::String::ExternalOneByteStringResource {
1446 public:
1447 virtual const char* data() const { return NULL; }
1448 virtual size_t length() const { return 1 << 30; }
1449};
1450
1451
1452TEST(InvalidExternalString) {
1453 CcTest::InitializeVM();
1454 LocalContext context;
1455 Isolate* isolate = CcTest::i_isolate();
1456 { HandleScope scope(isolate);
1457 DummyOneByteResource r;
1458 CHECK(isolate->factory()->NewExternalStringFromOneByte(&r).is_null());
1459 CHECK(isolate->has_pending_exception());
1460 isolate->clear_pending_exception();
1461 }
1462
1463 { HandleScope scope(isolate);
1464 DummyResource r;
1465 CHECK(isolate->factory()->NewExternalStringFromTwoByte(&r).is_null());
1466 CHECK(isolate->has_pending_exception());
1467 isolate->clear_pending_exception();
1468 }
1469}
1470
1471
1472#define INVALID_STRING_TEST(FUN, TYPE) \
1473 TEST(StringOOM##FUN) { \
1474 CcTest::InitializeVM(); \
1475 LocalContext context; \
1476 Isolate* isolate = CcTest::i_isolate(); \
1477 STATIC_ASSERT(String::kMaxLength < kMaxInt); \
1478 static const int invalid = String::kMaxLength + 1; \
1479 HandleScope scope(isolate); \
1480 Vector<TYPE> dummy = Vector<TYPE>::New(invalid); \
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001481 memset(dummy.start(), 0x0, dummy.length() * sizeof(TYPE)); \
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001482 CHECK(isolate->factory()->FUN(Vector<const TYPE>::cast(dummy)).is_null()); \
1483 memset(dummy.start(), 0x20, dummy.length() * sizeof(TYPE)); \
1484 CHECK(isolate->has_pending_exception()); \
1485 isolate->clear_pending_exception(); \
1486 dummy.Dispose(); \
1487 }
1488
1489INVALID_STRING_TEST(NewStringFromAscii, char)
1490INVALID_STRING_TEST(NewStringFromUtf8, char)
1491INVALID_STRING_TEST(NewStringFromOneByte, uint8_t)
1492
1493#undef INVALID_STRING_TEST
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001494
1495
1496TEST(FormatMessage) {
1497 CcTest::InitializeVM();
1498 LocalContext context;
1499 Isolate* isolate = CcTest::i_isolate();
1500 HandleScope scope(isolate);
1501 Handle<String> arg0 = isolate->factory()->NewStringFromAsciiChecked("arg0");
1502 Handle<String> arg1 = isolate->factory()->NewStringFromAsciiChecked("arg1");
1503 Handle<String> arg2 = isolate->factory()->NewStringFromAsciiChecked("arg2");
1504 Handle<String> result =
1505 MessageTemplate::FormatMessage(MessageTemplate::kPropertyNotFunction,
1506 arg0, arg1, arg2).ToHandleChecked();
1507 Handle<String> expected = isolate->factory()->NewStringFromAsciiChecked(
1508 "'arg0' returned for property 'arg1' of object 'arg2' is not a function");
1509 CHECK(String::Equals(result, expected));
1510}
Ben Murdochda12d292016-06-02 14:46:10 +01001511
1512TEST(Regress609831) {
1513 CcTest::InitializeVM();
1514 LocalContext context;
1515 Isolate* isolate = CcTest::i_isolate();
1516 {
1517 HandleScope scope(isolate);
1518 v8::Local<v8::Value> result = CompileRun(
1519 "String.fromCharCode(32, 32, 32, 32, 32, "
1520 "32, 32, 32, 32, 32, 32, 32, 32, 32, 32, "
1521 "32, 32, 32, 32, 32, 32, 32, 32, 32, 32)");
1522 CHECK(v8::Utils::OpenHandle(*result)->IsSeqOneByteString());
1523 }
1524 {
1525 HandleScope scope(isolate);
1526 v8::Local<v8::Value> result = CompileRun(
1527 "String.fromCharCode(432, 432, 432, 432, 432, "
1528 "432, 432, 432, 432, 432, 432, 432, 432, 432, "
1529 "432, 432, 432, 432, 432, 432, 432, 432, 432)");
1530 CHECK(v8::Utils::OpenHandle(*result)->IsSeqTwoByteString());
1531 }
1532}