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