PageRenderTime 66ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/deps/v8/test/cctest/test-strings.cc

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