Implement .of() on typed arrays
authorAdrian Perez <aperez@igalia.com>
Thu, 13 Nov 2014 11:20:01 +0000 (12:20 +0100)
committerAndy Wingo <wingo@igalia.com>
Thu, 13 Nov 2014 11:20:13 +0000 (11:20 +0000)
BUG=v8:3578
LOG=Y
R=dslomov@chromium.org, wingo@igalia.com

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

Patch from Adrian Perez <aperez@igalia.com>.

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

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

index 0129f38..014206c 100644 (file)
@@ -58,6 +58,17 @@ function NAMEForEach(f /* thisArg */) {  // length == 1
     %_CallFunction(new_receiver, TO_OBJECT_INLINE(element), i, this, f);
   }
 }
+
+// ES6 draft 08-24-14, section 22.2.2.2
+function NAMEOf() {  // length == 0
+  var length = %_ArgumentsLength();
+  var array = new this(length);
+  for (var i = 0; i < length; i++) {
+    array[i] = %_Arguments(i);
+  }
+  return array;
+}
+
 endmacro
 
 TYPED_ARRAYS(TYPED_ARRAY_HARMONY_ADDITIONS)
@@ -67,6 +78,11 @@ function HarmonyTypedArrayExtendPrototypes() {
 macro EXTEND_TYPED_ARRAY(ARRAY_ID, NAME, ELEMENT_SIZE)
   %CheckIsBootstrapping();
 
+  // Set up non-enumerable functions on the object.
+  InstallFunctions(global.NAME, DONT_ENUM | DONT_DELETE | READ_ONLY, $Array(
+    "of", NAMEOf
+  ));
+
   // Set up non-enumerable functions on the prototype object.
   InstallFunctions(global.NAME.prototype, DONT_ENUM, $Array(
     "forEach", NAMEForEach
diff --git a/test/mjsunit/harmony/typedarrays-of.js b/test/mjsunit/harmony/typedarrays-of.js
new file mode 100644 (file)
index 0000000..9df1d30
--- /dev/null
@@ -0,0 +1,135 @@
+// Copyright 2014 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.
+
+// Based on Mozilla Array.of() tests at http://dxr.mozilla.org/mozilla-central/source/js/src/jit-test/tests/collections
+
+// Flags: --harmony-arrays
+
+var typedArrayConstructors = [
+  Uint8Array,
+  Int8Array,
+  Uint16Array,
+  Int16Array,
+  Uint32Array,
+  Int32Array,
+  Uint8ClampedArray,
+  Float32Array,
+  Float64Array];
+
+
+function TestTypedArrayOf(constructor) {
+  // %TypedArray%.of basics.
+  var a = constructor.of();
+  assertEquals(0, a.length);
+  assertEquals(constructor.prototype, Object.getPrototypeOf(a));
+  assertEquals(false, Array.isArray(a));
+
+  // Items are coerced to numerical values.
+  a = constructor.of(undefined, null, [], true, false, 3.14);
+
+  // For typed arrays of floating point values, values are not rounded.
+  if (constructor === Float32Array || constructor === Float64Array) {
+    assertEquals(NaN, a[0]);
+    assertEquals(0, a[1]);
+    assertEquals(0, a[2]);
+    assertEquals(1, a[3]);
+    assertEquals(0, a[4]);
+    assertEquals(true, Math.abs(a[5] - 3.14) < 1e-6);
+  } else {
+    assertEquals(0, a[0]);
+    assertEquals(0, a[1]);
+    assertEquals(0, a[2]);
+    assertEquals(1, a[3]);
+    assertEquals(0, a[4]);
+    assertEquals(3, a[5]);
+  }
+
+  var aux = [];
+  for (var i = 0; i < 100; i++)
+    aux[i] = i;
+
+  a = constructor.of.apply(constructor, aux);
+  assertEquals(aux.length, a.length);
+  assertArrayEquals(aux, a);
+
+  // %TypedArray%.of can be transplanted to other constructors.
+  var hits = 0;
+  function Bag(length) {
+    assertEquals(arguments.length, 1);
+    assertEquals(length, 2);
+    this.length = length;
+    hits++;
+  }
+  Bag.of = constructor.of;
+
+  hits = 0;
+  a = Bag.of("zero", "one");
+  assertEquals(1, hits);
+  assertEquals(2, a.length);
+  assertArrayEquals(["zero", "one"], a);
+  assertEquals(Bag.prototype, a.__proto__);
+
+  hits = 0;
+  actual = constructor.of.call(Bag, "zero", "one");
+  assertEquals(1, hits);
+  assertEquals(2, a.length);
+  assertArrayEquals(["zero", "one"], a);
+  assertEquals(Bag.prototype, a.__proto__);
+
+  // %TypedArray%.of does not trigger prototype setters.
+  // (It defines elements rather than assigning to them.)
+  var status = "pass";
+  Object.defineProperty(constructor.prototype, "0", {
+    set: function(v) { status = "fail"; }
+  });
+  assertEquals(1, constructor.of(1)[0], 1);
+  assertEquals("pass", status);
+
+  // Note that %TypedArray%.of does not trigger "length" setter itself, as
+  // it relies on the constructor to set "length" to the value passed to it.
+  // If the constructor does not assign "length", the setter should not be
+  // invoked.
+
+  // Setter on the newly created object.
+  function Pack() {
+    Object.defineProperty(this, "length", {
+      set: function (v) { status = "fail"; }
+    });
+  }
+  Pack.of = constructor.of;
+  var pack = Pack.of("wolves", "cards", "cigarettes", "lies");
+  assertEquals("pass", status);
+
+  // when the setter is on the new object's prototype
+  function Bevy() {}
+  Object.defineProperty(Bevy.prototype, "length", {
+    set: function (v) { status = "fail"; }
+  });
+  Bevy.of = constructor.of;
+  var bevy = Bevy.of("quail");
+  assertEquals("pass", status);
+
+  // Check superficial features of %TypedArray%.of.
+  var desc = Object.getOwnPropertyDescriptor(constructor, "of");
+
+  assertEquals(desc.configurable, false);
+  assertEquals(desc.enumerable, false);
+  assertEquals(desc.writable, false);
+  assertEquals(constructor.of.length, 0);
+
+  // %TypedArray%.of is not a constructor.
+  assertThrows(function() { new constructor.of(); }, TypeError);
+
+  // For receivers which are not constructors %TypedArray%.of does not
+  // allocate a typed array using a default constructor, but throws an
+  // exception. Note that this is different from Array.of, which uses
+  // Array as default constructor.
+  for (var x of [undefined, null, false, true, "cow", 42, 3.14]) {
+    assertThrows(function () { constructor.of.call(x); }, TypeError);
+  }
+}
+
+for (var constructor of typedArrayConstructors) {
+  TestTypedArrayOf(constructor);
+}