Implement %TypedArray%.prototype.sort
authorDaniel Ehrenberg <dehrenberg@chromium.org>
Wed, 20 May 2015 00:56:15 +0000 (17:56 -0700)
committerAdam Klein <adamk@chromium.org>
Wed, 20 May 2015 00:56:06 +0000 (00:56 +0000)
The sort method of TypedArrays sorts in numerical order by default.
This patch implements sorting based on Arrays and adds a test.
The length of %TypedArray%.prototype.sort, like Array.prototype.sort,
seems to be unspecified in ES6, so this patch lets it have the value
1, to match our interpretation for Array.prototype.sort (though 0
would also be a sensible length).

R=arv@chromium.org
BUG=v8:3578
LOG=Y

Review URL: https://codereview.chromium.org/1148513002

Patch from Daniel Ehrenberg <dehrenberg@chromium.org>.

Cr-Commit-Position: refs/heads/master@{#28494}

src/array.js
src/harmony-typedarray.js
test/mjsunit/harmony/typedarray-sort.js [new file with mode: 0644]

index 0d25f99..573b729 100644 (file)
@@ -13,6 +13,7 @@ var $arrayUnshift;
 var $innerArrayForEach;
 var $innerArrayEvery;
 var $innerArrayReverse;
+var $innerArraySort;
 
 (function(global, shared, exports) {
 
@@ -867,9 +868,7 @@ function ArraySplice(start, delete_count) {
 }
 
 
-function ArraySort(comparefn) {
-  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.sort");
-
+function InnerArraySort(length, comparefn) {
   // In-place QuickSort algorithm.
   // For short (length <= 22) arrays, insertion sort is used for efficiency.
 
@@ -1114,7 +1113,6 @@ function ArraySort(comparefn) {
     return first_undefined;
   };
 
-  var length = TO_UINT32(this.length);
   if (length < 2) return this;
 
   var is_array = IS_ARRAY(this);
@@ -1153,6 +1151,14 @@ function ArraySort(comparefn) {
 }
 
 
+function ArraySort(comparefn) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.sort");
+
+  var length = TO_UINT32(this.length);
+  return %_CallFunction(this, length, comparefn, InnerArraySort);
+}
+
+
 // The following functions cannot be made efficient on sparse arrays while
 // preserving the semantics, since the calls to the receiver function can add
 // or delete elements from the array.
@@ -1616,5 +1622,6 @@ $arrayUnshift = ArrayUnshift;
 $innerArrayForEach = InnerArrayForEach;
 $innerArrayEvery = InnerArrayEvery;
 $innerArrayReverse = InnerArrayReverse;
+$innerArraySort = InnerArraySort;
 
 });
index 59c9a79..025de55 100644 (file)
@@ -100,6 +100,40 @@ function TypedArrayReverse() {
 }
 
 
+function TypedArrayComparefn(x, y) {
+  if ($isNaN(x) && $isNaN(y)) {
+    return $isNaN(y) ? 0 : 1;
+  }
+  if ($isNaN(x)) {
+    return 1;
+  }
+  if (x === 0 && x === y) {
+    if (%_IsMinusZero(x)) {
+      if (!%_IsMinusZero(y)) {
+        return -1;
+      }
+    } else if (%_IsMinusZero(y)) {
+      return 1;
+    }
+  }
+  return x - y;
+}
+
+
+// ES6 draft 05-18-15, section 22.2.3.25
+function TypedArraySort(comparefn) {
+  if (!%IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  if (IS_UNDEFINED(comparefn)) {
+    comparefn = TypedArrayComparefn;
+  }
+
+  return %_CallFunction(this, length, comparefn, $innerArraySort);
+}
+
+
 // ES6 draft 08-24-14, section 22.2.2.2
 function TypedArrayOf() {
   var length = %_ArgumentsLength();
@@ -150,7 +184,8 @@ macro EXTEND_TYPED_ARRAY(NAME)
     "find", TypedArrayFind,
     "findIndex", TypedArrayFindIndex,
     "fill", TypedArrayFill,
-    "reverse", TypedArrayReverse
+    "reverse", TypedArrayReverse,
+    "sort", TypedArraySort
   ]);
 endmacro
 
diff --git a/test/mjsunit/harmony/typedarray-sort.js b/test/mjsunit/harmony/typedarray-sort.js
new file mode 100644 (file)
index 0000000..f6f49ce
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --harmony-arrays
+
+var typedArrayConstructors = [
+  Uint8Array,
+  Int8Array,
+  Uint16Array,
+  Int16Array,
+  Uint32Array,
+  Int32Array,
+  Uint8ClampedArray,
+  Float32Array,
+  Float64Array
+];
+
+function assertArrayLikeEquals(value, expected, type) {
+  assertEquals(value.__proto__, type.prototype);
+  // Don't test value.length because we mess with that;
+  // instead in certain callsites we check that length
+  // is set appropriately.
+  for (var i = 0; i < expected.length; ++i) {
+    // Use Object.is to differentiate between +-0
+    assertSame(expected[i], value[i]);
+  }
+}
+
+for (var constructor of typedArrayConstructors) {
+  // Test default numerical sorting order
+  var a = new constructor([100, 7, 45])
+  assertEquals(a.sort(), a);
+  assertArrayLikeEquals(a, [7, 45, 100], constructor);
+  assertEquals(a.length, 3);
+
+  // For arrays of floats, certain handling of +-0/NaN
+  if (constructor === Float32Array || constructor === Float64Array) {
+    var b = new constructor([+0, -0, NaN, -0, NaN, +0])
+    b.sort();
+    assertArrayLikeEquals(b, [-0, -0, +0, +0, NaN, NaN], constructor);
+    assertEquals(b.length, 6);
+  }
+
+  // Custom sort--backwards
+  a.sort(function(x, y) { return y - x; });
+  assertArrayLikeEquals(a, [100, 45, 7], constructor);
+
+  // Basic TypedArray method properties:
+  // Length field is ignored
+  Object.defineProperty(a, 'length', {value: 1});
+  assertEquals(a.sort(), a);
+  assertArrayLikeEquals(a, [7, 45, 100], constructor);
+  assertEquals(a.length, 1);
+  // Method doesn't work on other objects
+  assertThrows(function() { a.sort.call([]); }, TypeError);
+}