Revision: 10422
Author:   [email protected]
Date:     Tue Jan 17 06:29:17 2012
Log: Recursion limit for one-char string replace and retire String::kMinNonFlatLength.

TEST=mjsunit/string-replace-one-char.js

Review URL: https://chromiumcodereview.appspot.com/9231017
http://code.google.com/p/v8/source/detail?r=10422

Added:
 /branches/bleeding_edge/test/mjsunit/string-replace-one-char.js
Modified:
 /branches/bleeding_edge/src/arm/code-stubs-arm.cc
 /branches/bleeding_edge/src/heap.cc
 /branches/bleeding_edge/src/ia32/code-stubs-ia32.cc
 /branches/bleeding_edge/src/mips/code-stubs-mips.cc
 /branches/bleeding_edge/src/objects-debug.cc
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/src/runtime.h
 /branches/bleeding_edge/src/string.js
 /branches/bleeding_edge/src/x64/code-stubs-x64.cc
 /branches/bleeding_edge/test/cctest/test-strings.cc

=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/string-replace-one-char.js Tue Jan 17 06:29:17 2012
@@ -0,0 +1,92 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Make sure the strings are long enough to trigger the one-char string replace.
+var prefix1024 = "0123456789ABCDEF";
+for (var i = 0; i < 6; i++) prefix1024 += prefix1024;
+
+function test_replace(result, expected, search, replace) {
+  assertEquals(expected, result.replace(search, replace));
+}
+
+// '$' in the replace string.
+test_replace(prefix1024 + "abcdefghijklmnopqrstuvwxyz",
+             prefix1024 + "abcdefghijk#l#mnopqrstuvwxyz",
+             "l", "#$&#");
+
+test_replace(prefix1024 + "abcdefghijklmnopqrstuvwxyz\u1234",
+             prefix1024 + "abcdefghijk\u2012l\u2012mnopqrstuvwxyz\u1234",
+             "l", "\u2012$&\u2012");
+
+test_replace(prefix1024 + "abcdefghijklmnopqrstuvwxyz",
+             prefix1024 + "abcdefghijk$mnopqrstuvwxyz",
+             "l", "$$");
+
+test_replace(prefix1024 + "abcdefghijklmnopqrstuvwxyz\u1234",
+             prefix1024 + "abcdefghijk$mnopqrstuvwxyz\u1234",
+             "l", "$$");
+
+// Zero length replace string.
+test_replace(prefix1024 + "abcdefghijklmnopqrstuvwxyz",
+             prefix1024 + "abcdefghijklmnopqstuvwxyz",
+             "r", "");
+
+test_replace(prefix1024 + "abcdefghijklmnopq\u1234stuvwxyz",
+             prefix1024 + "abcdefghijklmnopqstuvwxyz",
+             "\u1234", "");
+
+// Search char not found.
+var not_found_1 = prefix1024 + "abcdefghijklmnopqrstuvwxyz";
+test_replace(not_found_1, not_found_1, "+", "-");
+
+var not_found_2 = prefix1024 + "abcdefghijklm\u1234nopqrstuvwxyz";
+test_replace(not_found_2, not_found_2, "+", "---");
+
+var not_found_3 = prefix1024 + "abcdefghijklmnopqrstuvwxyz";
+test_replace(not_found_3, not_found_3, "\u1234", "ZZZ");
+
+// Deep cons tree.
+var nested_1 = "";
+for (var i = 0; i < 1000000; i++) nested_1 += "y";
+var nested_1_result = prefix1024 + nested_1 + "aa";
+nested_1 = prefix1024 + nested_1 + "z";
+test_replace(nested_1, nested_1_result, "z", "aa");
+
+var nested_2 = "\u2244";
+for (var i = 0; i < 1000000; i++) nested_2 += "y";
+var nested_2_result = prefix1024 + nested_2 + "aa";
+nested_2 = prefix1024 + nested_2 + "\u2012";
+test_replace(nested_2, nested_2_result, "\u2012", "aa");
+
+// Sliced string as input. A cons string is always flattened before sliced. +var slice_1 = ("ab" + prefix1024 + "cdefghijklmnopqrstuvwxyz").slice(1, -1);
+var slice_1_result = "b" + prefix1024 + "cdefghijklmnopqrstuvwxQ";
+test_replace(slice_1, slice_1_result, "y", "Q");
+
+var slice_2 = (prefix1024 + "abcdefghijklmno\u1234\u1234p").slice(1, -1);
+var slice_2_result = prefix1024.substr(1) + "abcdefghijklmnoQ\u1234";
+test_replace(slice_2, slice_2_result, "\u1234", "Q");
=======================================
--- /branches/bleeding_edge/src/arm/code-stubs-arm.cc Mon Jan 16 04:38:59 2012 +++ /branches/bleeding_edge/src/arm/code-stubs-arm.cc Tue Jan 17 06:29:17 2012
@@ -6268,7 +6268,7 @@

   __ bind(&longer_than_two);
   // Check if resulting string will be flat.
-  __ cmp(r6, Operand(String::kMinNonFlatLength));
+  __ cmp(r6, Operand(ConsString::kMinLength));
   __ b(lt, &string_add_flat_result);
   // Handle exceptionally long strings in the runtime system.
   STATIC_ASSERT((String::kMaxLength & 0x80000000) == 0);
@@ -6322,7 +6322,7 @@
   __ jmp(&allocated);

   // We cannot encounter sliced strings or cons strings here since:
-  STATIC_ASSERT(SlicedString::kMinLength >= String::kMinNonFlatLength);
+  STATIC_ASSERT(SlicedString::kMinLength >= ConsString::kMinLength);
// Handle creating a flat result from either external or sequential strings.
   // Locate the first characters' locations.
   // r0: first string
=======================================
--- /branches/bleeding_edge/src/heap.cc Tue Jan 17 05:13:55 2012
+++ /branches/bleeding_edge/src/heap.cc Tue Jan 17 06:29:17 2012
@@ -2933,9 +2933,9 @@
   }

   // If the resulting string is small make a flat string.
-  if (length < String::kMinNonFlatLength) {
+  if (length < ConsString::kMinLength) {
     // Note that neither of the two inputs can be a slice because:
-    STATIC_ASSERT(String::kMinNonFlatLength <= SlicedString::kMinLength);
+    STATIC_ASSERT(ConsString::kMinLength <= SlicedString::kMinLength);
     ASSERT(first->IsFlat());
     ASSERT(second->IsFlat());
     if (is_ascii) {
=======================================
--- /branches/bleeding_edge/src/ia32/code-stubs-ia32.cc Mon Jan 16 04:38:59 2012 +++ /branches/bleeding_edge/src/ia32/code-stubs-ia32.cc Tue Jan 17 06:29:17 2012
@@ -5585,7 +5585,7 @@

   __ bind(&longer_than_two);
   // Check if resulting string will be flat.
-  __ cmp(ebx, Immediate(Smi::FromInt(String::kMinNonFlatLength)));
+  __ cmp(ebx, Immediate(Smi::FromInt(ConsString::kMinLength)));
   __ j(below, &string_add_flat_result);

// If result is not supposed to be flat allocate a cons string object. If both
@@ -5633,7 +5633,7 @@
   __ jmp(&allocated);

   // We cannot encounter sliced strings or cons strings here since:
-  STATIC_ASSERT(SlicedString::kMinLength >= String::kMinNonFlatLength);
+  STATIC_ASSERT(SlicedString::kMinLength >= ConsString::kMinLength);
// Handle creating a flat result from either external or sequential strings.
   // Locate the first characters' locations.
   // eax: first string
=======================================
--- /branches/bleeding_edge/src/mips/code-stubs-mips.cc Mon Jan 16 04:38:59 2012 +++ /branches/bleeding_edge/src/mips/code-stubs-mips.cc Tue Jan 17 06:29:17 2012
@@ -6491,7 +6491,7 @@
   __ bind(&longer_than_two);
   // Check if resulting string will be flat.
   __ Branch(&string_add_flat_result, lt, t2,
-           Operand(String::kMinNonFlatLength));
+           Operand(ConsString::kMinLength));
   // Handle exceptionally long strings in the runtime system.
   STATIC_ASSERT((String::kMaxLength & 0x80000000) == 0);
   ASSERT(IsPowerOf2(String::kMaxLength + 1));
@@ -6543,7 +6543,7 @@
   __ Branch(&allocated);

   // We cannot encounter sliced strings or cons strings here since:
-  STATIC_ASSERT(SlicedString::kMinLength >= String::kMinNonFlatLength);
+  STATIC_ASSERT(SlicedString::kMinLength >= ConsString::kMinLength);
// Handle creating a flat result from either external or sequential strings.
   // Locate the first characters' locations.
   // a0: first string
=======================================
--- /branches/bleeding_edge/src/objects-debug.cc        Mon Jan 16 01:44:35 2012
+++ /branches/bleeding_edge/src/objects-debug.cc        Tue Jan 17 06:29:17 2012
@@ -388,7 +388,7 @@
   CHECK(this->first()->IsString());
   CHECK(this->second() == GetHeap()->empty_string() ||
         this->second()->IsString());
-  CHECK(this->length() >= String::kMinNonFlatLength);
+  CHECK(this->length() >= ConsString::kMinLength);
   if (this->IsFlat()) {
     // A flat cons can only be created by String::SlowTryFlatten.
     // Afterwards, the first part may be externalized.
=======================================
--- /branches/bleeding_edge/src/objects.h       Mon Jan 16 04:38:59 2012
+++ /branches/bleeding_edge/src/objects.h       Tue Jan 17 06:29:17 2012
@@ -6591,9 +6591,6 @@
static const unsigned kMaxAsciiCharCodeU = unibrow::Utf8::kMaxOneByteChar;
   static const int kMaxUC16CharCode = 0xffff;

-  // Minimum length for a cons string.
-  static const int kMinNonFlatLength = 13;
-
   // Mask constant for checking if a string has a computed hash code
   // and if it is an array index.  The least significant bit indicates
   // whether a hash code has been computed.  If the hash code has been
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Mon Jan 16 07:21:38 2012
+++ /branches/bleeding_edge/src/runtime.cc      Tue Jan 17 06:29:17 2012
@@ -3237,26 +3237,34 @@
Handle<String> subject, Handle<String> search, Handle<String> replace,
-                                                       bool* found) {
+                                                       bool* found,
+ int recursion_limit) {
+  if (recursion_limit == 0) return Handle<String>::null();
   if (subject->IsConsString()) {
     ConsString* cons = ConsString::cast(*subject);
     Handle<String> first = Handle<String>(cons->first());
     Handle<String> second = Handle<String>(cons->second());
-    Handle<String> new_first = StringReplaceOneCharWithString(isolate,
-                                                              first,
-                                                              search,
-                                                              replace,
-                                                              found);
-    if (*found) {
-      return isolate->factory()->NewConsString(new_first, second);
-    } else {
-      Handle<String> new_second = StringReplaceOneCharWithString(isolate,
-                                                                 second,
-                                                                 search,
-                                                                 replace,
-                                                                 found);
-      return isolate->factory()->NewConsString(first, new_second);
-    }
+    Handle<String> new_first =
+        StringReplaceOneCharWithString(isolate,
+                                       first,
+                                       search,
+                                       replace,
+                                       found,
+                                       recursion_limit - 1);
+ if (*found) return isolate->factory()->NewConsString(new_first, second);
+    if (new_first.is_null()) return new_first;
+
+    Handle<String> new_second =
+        StringReplaceOneCharWithString(isolate,
+                                       second,
+                                       search,
+                                       replace,
+                                       found,
+                                       recursion_limit - 1);
+ if (*found) return isolate->factory()->NewConsString(first, new_second);
+    if (new_second.is_null()) return new_second;
+
+    return subject;
   } else {
     int index = StringMatch(isolate, subject, search, 0);
     if (index == -1) return subject;
@@ -3276,13 +3284,25 @@
   CONVERT_ARG_CHECKED(String, subject, 0);
   CONVERT_ARG_CHECKED(String, search, 1);
   CONVERT_ARG_CHECKED(String, replace, 2);
-  bool found = false;
-
-  return *(Runtime::StringReplaceOneCharWithString(isolate,
-                                                   subject,
-                                                   search,
-                                                   replace,
-                                                   &found));
+
+  // If the cons string tree is too deep, we simply abort the recursion and
+  // retry with a flattened subject string.
+  const int kRecursionLimit = 0x1000;
+  bool found = false;
+  Handle<String> result =
+      Runtime::StringReplaceOneCharWithString(isolate,
+                                              subject,
+                                              search,
+                                              replace,
+                                              &found,
+                                              kRecursionLimit);
+  if (!result.is_null()) return *result;
+  return *Runtime::StringReplaceOneCharWithString(isolate,
+ FlattenGetString(subject),
+                                                  search,
+                                                  replace,
+                                                  &found,
+                                                  kRecursionLimit);
 }


=======================================
--- /branches/bleeding_edge/src/runtime.h       Mon Jan 16 07:21:38 2012
+++ /branches/bleeding_edge/src/runtime.h       Tue Jan 17 06:29:17 2012
@@ -634,7 +634,8 @@
Handle<String> subject, Handle<String> search, Handle<String> replace,
-                                                       bool* found);
+                                                       bool* found,
+ int recursion_limit);

   // General-purpose helper functions for runtime system.
   static int StringMatch(Isolate* isolate,
=======================================
--- /branches/bleeding_edge/src/string.js       Mon Jan 16 07:21:38 2012
+++ /branches/bleeding_edge/src/string.js       Tue Jan 17 06:29:17 2012
@@ -245,8 +245,12 @@
   // Convert the search argument to a string and search for it.
   search = TO_STRING_INLINE(search);
   if (search.length == 1 &&
+      subject.length > 0xFF &&
       IS_STRING(replace) &&
       %StringIndexOf(replace, '$', 0) < 0) {
+    // Searching by traversing a cons string tree and replace with cons of
+ // slices works only when the replaced string is a single character, being
+    // replaced by a simple string and only pays off for long strings.
     return %StringReplaceOneCharWithString(subject, search, replace);
   }
   var start = %StringIndexOf(subject, search, 0);
=======================================
--- /branches/bleeding_edge/src/x64/code-stubs-x64.cc Mon Jan 16 07:11:56 2012 +++ /branches/bleeding_edge/src/x64/code-stubs-x64.cc Tue Jan 17 06:29:17 2012
@@ -4547,7 +4547,7 @@

   __ bind(&longer_than_two);
   // Check if resulting string will be flat.
-  __ SmiCompare(rbx, Smi::FromInt(String::kMinNonFlatLength));
+  __ SmiCompare(rbx, Smi::FromInt(ConsString::kMinLength));
   __ j(below, &string_add_flat_result);
   // Handle exceptionally long strings in the runtime system.
   STATIC_ASSERT((String::kMaxLength & 0x80000000) == 0);
@@ -4599,7 +4599,7 @@
   __ jmp(&allocated);

   // We cannot encounter sliced strings or cons strings here since:
-  STATIC_ASSERT(SlicedString::kMinLength >= String::kMinNonFlatLength);
+  STATIC_ASSERT(SlicedString::kMinLength >= ConsString::kMinLength);
// Handle creating a flat result from either external or sequential strings.
   // Locate the first characters' locations.
   // rax: first string
=======================================
--- /branches/bleeding_edge/test/cctest/test-strings.cc Thu Sep 15 05:47:06 2011 +++ /branches/bleeding_edge/test/cctest/test-strings.cc Tue Jan 17 06:29:17 2012
@@ -355,7 +355,7 @@

   // Make sure we cover all always-flat lengths and at least one above.
   static const int kMaxLength = 20;
-  CHECK_GT(kMaxLength, i::String::kMinNonFlatLength);
+  CHECK_GT(kMaxLength, i::ConsString::kMinLength);

   // Allocate two JavaScript arrays for holding short strings.
   v8::Handle<v8::Array> ascii_external_strings =

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to