Avoid arbitrarily deep recursion in Array.sort.
authorerikcorry <erikcorry@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 18 Jun 2012 09:23:05 +0000 (09:23 +0000)
committererikcorry <erikcorry@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 18 Jun 2012 09:23:05 +0000 (09:23 +0000)
BUG=v8:2185
Review URL: http://codereview.chromium.org/10561017

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@11839 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/array.js
test/mjsunit/regress/regress-2185.js [new file with mode: 0644]

index a1cc5b6..dc7d6c5 100644 (file)
@@ -778,77 +778,84 @@ function ArraySort(comparefn) {
   };
 
   var QuickSort = function QuickSort(a, from, to) {
-    // Insertion sort is faster for short arrays.
-    if (to - from <= 10) {
-      InsertionSort(a, from, to);
-      return;
-    }
-    // Find a pivot as the median of first, last and middle element.
-    var v0 = a[from];
-    var v1 = a[to - 1];
-    var middle_index = from + ((to - from) >> 1);
-    var v2 = a[middle_index];
-    var c01 = %_CallFunction(receiver, v0, v1, comparefn);
-    if (c01 > 0) {
-      // v1 < v0, so swap them.
-      var tmp = v0;
-      v0 = v1;
-      v1 = tmp;
-    } // v0 <= v1.
-    var c02 = %_CallFunction(receiver, v0, v2, comparefn);
-    if (c02 >= 0) {
-      // v2 <= v0 <= v1.
-      var tmp = v0;
-      v0 = v2;
-      v2 = v1;
-      v1 = tmp;
-    } else {
-      // v0 <= v1 && v0 < v2
-      var c12 = %_CallFunction(receiver, v1, v2, comparefn);
-      if (c12 > 0) {
-        // v0 <= v2 < v1
-        var tmp = v1;
-        v1 = v2;
-        v2 = tmp;
+    while (true) {
+      // Insertion sort is faster for short arrays.
+      if (to - from <= 10) {
+        InsertionSort(a, from, to);
+        return;
       }
-    }
-    // v0 <= v1 <= v2
-    a[from] = v0;
-    a[to - 1] = v2;
-    var pivot = v1;
-    var low_end = from + 1;   // Upper bound of elements lower than pivot.
-    var high_start = to - 1;  // Lower bound of elements greater than pivot.
-    a[middle_index] = a[low_end];
-    a[low_end] = pivot;
-
-    // From low_end to i are elements equal to pivot.
-    // From i to high_start are elements that haven't been compared yet.
-    partition: for (var i = low_end + 1; i < high_start; i++) {
-      var element = a[i];
-      var order = %_CallFunction(receiver, element, pivot, comparefn);
-      if (order < 0) {
-        a[i] = a[low_end];
-        a[low_end] = element;
-        low_end++;
-      } else if (order > 0) {
-        do {
-          high_start--;
-          if (high_start == i) break partition;
-          var top_elem = a[high_start];
-          order = %_CallFunction(receiver, top_elem, pivot, comparefn);
-        } while (order > 0);
-        a[i] = a[high_start];
-        a[high_start] = element;
+      // Find a pivot as the median of first, last and middle element.
+      var v0 = a[from];
+      var v1 = a[to - 1];
+      var middle_index = from + ((to - from) >> 1);
+      var v2 = a[middle_index];
+      var c01 = %_CallFunction(receiver, v0, v1, comparefn);
+      if (c01 > 0) {
+        // v1 < v0, so swap them.
+        var tmp = v0;
+        v0 = v1;
+        v1 = tmp;
+      } // v0 <= v1.
+      var c02 = %_CallFunction(receiver, v0, v2, comparefn);
+      if (c02 >= 0) {
+        // v2 <= v0 <= v1.
+        var tmp = v0;
+        v0 = v2;
+        v2 = v1;
+        v1 = tmp;
+      } else {
+        // v0 <= v1 && v0 < v2
+        var c12 = %_CallFunction(receiver, v1, v2, comparefn);
+        if (c12 > 0) {
+          // v0 <= v2 < v1
+          var tmp = v1;
+          v1 = v2;
+          v2 = tmp;
+        }
+      }
+      // v0 <= v1 <= v2
+      a[from] = v0;
+      a[to - 1] = v2;
+      var pivot = v1;
+      var low_end = from + 1;   // Upper bound of elements lower than pivot.
+      var high_start = to - 1;  // Lower bound of elements greater than pivot.
+      a[middle_index] = a[low_end];
+      a[low_end] = pivot;
+
+      // From low_end to i are elements equal to pivot.
+      // From i to high_start are elements that haven't been compared yet.
+      partition: for (var i = low_end + 1; i < high_start; i++) {
+        var element = a[i];
+        var order = %_CallFunction(receiver, element, pivot, comparefn);
         if (order < 0) {
-          element = a[i];
           a[i] = a[low_end];
           a[low_end] = element;
           low_end++;
+        } else if (order > 0) {
+          do {
+            high_start--;
+            if (high_start == i) break partition;
+            var top_elem = a[high_start];
+            order = %_CallFunction(receiver, top_elem, pivot, comparefn);
+          } while (order > 0);
+          a[i] = a[high_start];
+          a[high_start] = element;
+          if (order < 0) {
+            element = a[i];
+            a[i] = a[low_end];
+            a[low_end] = element;
+            low_end++;
+          }
         }
       }
+      if (to - high_start < low_end - from) {
+        QuickSort(a, high_start, to);
+        to = low_end;
+      } else {
+        QuickSort(a, from, low_end);
+        from = high_start;
+      }
     }
-    QuickSort(a, from, low_end);
-    QuickSort(a, high_start, to);
   };
 
   // Copy elements in the range 0..length from obj's prototype chain
diff --git a/test/mjsunit/regress/regress-2185.js b/test/mjsunit/regress/regress-2185.js
new file mode 100644 (file)
index 0000000..895f322
--- /dev/null
@@ -0,0 +1,36 @@
+// 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.
+
+var a = [];
+
+for (var i = 0; i < 2; i++) {
+  for (var j = 0; j < 30000; j++) {
+    a.push(j);
+  }
+}
+
+a.sort(function(a, b) { return a - b; } );