Implement ES6 Array.of()
authorrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 19 Aug 2014 11:38:38 +0000 (11:38 +0000)
committerrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 19 Aug 2014 11:38:38 +0000 (11:38 +0000)
BUG=v8:3427
LOG=N
R=rossberg@chromium.org

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

Patch from Diego Pino <dpino@igalia.com>.

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

src/harmony-array.js
src/runtime.cc
src/runtime.h
test/mjsunit/harmony/array-of.js [new file with mode: 0644]
tools/generate-runtime-tests.py

index dbcb292..88b878f 100644 (file)
@@ -123,11 +123,29 @@ function ArrayFill(value /* [, start [, end ] ] */) {  // length == 1
   return array;
 }
 
+// ES6, draft 05-22-14, section 22.1.2.3
+function ArrayOf() {
+  var length = %_ArgumentsLength();
+  var constructor = this;
+  // TODO: Implement IsConstructor (ES6 section 7.2.5)
+  var array = IS_SPEC_FUNCTION(constructor) ? new constructor(length) : [];
+  for (var i = 0; i < length; i++) {
+    %AddElement(array, i, %_Arguments(i), NONE);
+  }
+  array.length = length;
+  return array;
+}
+
 // -------------------------------------------------------------------
 
 function HarmonyArrayExtendArrayPrototype() {
   %CheckIsBootstrapping();
 
+  // Set up non-enumerable functions on the Array object.
+  InstallFunctions($Array, DONT_ENUM, $Array(
+    "of", ArrayOf
+  ));
+
   // Set up the non-enumerable functions on the Array prototype object.
   InstallFunctions($Array.prototype, DONT_ENUM, $Array(
     "find", ArrayFind,
index abf1935..b6c4525 100644 (file)
@@ -5354,6 +5354,33 @@ RUNTIME_FUNCTION(Runtime_SetProperty) {
 }
 
 
+// Adds an element to an array.
+// This is used to create an indexed data property into an array.
+RUNTIME_FUNCTION(Runtime_AddElement) {
+  HandleScope scope(isolate);
+  RUNTIME_ASSERT(args.length() == 4);
+
+  CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
+  CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+  CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
+  CONVERT_SMI_ARG_CHECKED(unchecked_attributes, 3);
+  RUNTIME_ASSERT(
+      (unchecked_attributes & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
+  // Compute attributes.
+  PropertyAttributes attributes =
+      static_cast<PropertyAttributes>(unchecked_attributes);
+
+  uint32_t index = 0;
+  key->ToArrayIndex(&index);
+
+  Handle<Object> result;
+  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+      isolate, result, JSObject::SetElement(object, index, value, attributes,
+                                            SLOPPY, false, DEFINE_PROPERTY));
+  return *result;
+}
+
+
 RUNTIME_FUNCTION(Runtime_TransitionElementsKind) {
   HandleScope scope(isolate);
   RUNTIME_ASSERT(args.length() == 2);
index 81b12b1..bcdbc60 100644 (file)
@@ -233,6 +233,7 @@ namespace internal {
   F(AddNamedProperty, 4, 1)                                           \
   F(AddPropertyForTemplate, 4, 1)                                     \
   F(SetProperty, 4, 1)                                                \
+  F(AddElement, 4, 1)                                                 \
   F(DefineApiAccessorProperty, 5, 1)                                  \
   F(DefineDataPropertyUnchecked, 4, 1)                                \
   F(DefineAccessorPropertyUnchecked, 5, 1)                            \
diff --git a/test/mjsunit/harmony/array-of.js b/test/mjsunit/harmony/array-of.js
new file mode 100644 (file)
index 0000000..c0a8ed1
--- /dev/null
@@ -0,0 +1,164 @@
+// 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
+
+
+
+// Array.of makes real arrays.
+
+function check(a) {
+    assertEquals(Object.getPrototypeOf(a), Array.prototype);
+    assertEquals(Array.isArray(a), true);
+    a[9] = 9;
+    assertEquals(a.length, 10);
+}
+
+
+check(Array.of());
+check(Array.of(0));
+check(Array.of(0, 1, 2));
+var f = Array.of;
+check(f());
+
+
+// Array.of basics
+
+var a = Array.of();
+
+assertEquals(a.length, 0);
+a = Array.of(undefined, null, 3.14, []);
+assertEquals(a, [undefined, null, 3.14, []]);
+a = [];
+for (var i = 0; i < 1000; i++)
+    a[i] = i;
+assertEquals(Array.of.apply(null, a), a);
+
+
+// Array.of does not leave holes
+
+assertEquals(Array.of(undefined), [undefined]);
+assertEquals(Array.of(undefined, undefined), [undefined, undefined]);
+assertEquals(Array.of.apply(null, [,,undefined]), [undefined, undefined, undefined]);
+assertEquals(Array.of.apply(null, Array(4)), [undefined, undefined, undefined, undefined]);
+
+
+// Array.of can be transplanted to other classes.
+
+var hits = 0;
+function Bag() {
+    hits++;
+}
+Bag.of = Array.of;
+
+hits = 0;
+var actual = Bag.of("zero", "one");
+assertEquals(hits, 1);
+
+hits = 0;
+var expected = new Bag;
+expected[0] = "zero";
+expected[1] = "one";
+expected.length = 2;
+assertEquals(areSame(actual, expected), true);
+
+hits = 0;
+actual = Array.of.call(Bag, "zero", "one");
+assertEquals(hits, 1);
+assertEquals(areSame(actual, expected), true);
+
+function areSame(object, array) {
+    var result = object.length == array.length;
+    for (var i = 0; i < object.length; i++) {
+        result = result && object[i] == array[i];
+    }
+    return result;
+}
+
+
+// Array.of does not trigger prototype setters.
+// (It defines elements rather than assigning to them.)
+
+var status = "pass";
+Object.defineProperty(Array.prototype, "0", {set: function(v) {status = "FAIL 1"}});
+assertEquals(Array.of(1)[0], 1);
+assertEquals(status, "pass");
+
+Object.defineProperty(Bag.prototype, "0", {set: function(v) {status = "FAIL 2"}});
+assertEquals(Bag.of(1)[0], 1);
+assertEquals(status, "pass");
+
+
+// Array.of passes the number of arguments to the constructor it calls.
+
+var hits = 0;
+
+function Herd(n) {
+    assertEquals(arguments.length, 1);
+    assertEquals(n, 5);
+    hits++;
+}
+
+Herd.of = Array.of;
+Herd.of("sheep", "cattle", "elephants", "whales", "seals");
+assertEquals(hits, 1);
+
+
+// Array.of calls a "length" setter if one is present.
+
+var hits = 0;
+var lastObj = null, lastVal = undefined;
+function setter(v) {
+    hits++;
+    lastObj = this;
+    lastVal = v;
+}
+
+// when the setter is on the new object
+function Pack() {
+    Object.defineProperty(this, "length", {set: setter});
+}
+Pack.of = Array.of;
+var pack = Pack.of("wolves", "cards", "cigarettes", "lies");
+assertEquals(lastObj, pack);
+assertEquals(lastVal, 4);
+
+// when the setter is on the new object's prototype
+function Bevy() {}
+Object.defineProperty(Bevy.prototype, "length", {set: setter});
+Bevy.of = Array.of;
+var bevy = Bevy.of("quail");
+assertEquals(lastObj, bevy);
+assertEquals(lastVal, 1);
+
+
+// Array.of does a strict assignment to the new object's .length.
+// The assignment is strict even if the code we're calling from is not strict.
+
+function Empty() {}
+Empty.of = Array.of;
+Object.defineProperty(Empty.prototype, "length", {get: function() { return 0; }});
+
+var nothing = new Empty;
+nothing.length = 2;  // no exception; this is not a strict mode assignment
+
+assertThrows(function() { Empty.of(); }, TypeError);
+
+
+// Check superficial features of Array.of.
+
+var desc = Object.getOwnPropertyDescriptor(Array, "of");
+
+assertEquals(desc.configurable, true);
+assertEquals(desc.enumerable, false);
+assertEquals(desc.writable, true);
+assertEquals(Array.of.length, 0);
+assertThrows(function() { new Array.of() }, TypeError);  // not a constructor
+
+// When the this-value passed in is not a constructor, the result is an array.
+[undefined, null, false, "cow"].forEach(function(val) {
+    assertEquals(Array.isArray(Array.of(val)), true);
+});
index 3081667..68a88d2 100755 (executable)
@@ -47,11 +47,11 @@ EXPAND_MACROS = [
 # that the parser doesn't bit-rot. Change the values as needed when you add,
 # remove or change runtime functions, but make sure we don't lose our ability
 # to parse them!
-EXPECTED_FUNCTION_COUNT = 428
+EXPECTED_FUNCTION_COUNT = 429
 EXPECTED_FUZZABLE_COUNT = 331
 EXPECTED_CCTEST_COUNT = 7
-EXPECTED_UNKNOWN_COUNT = 16
-EXPECTED_BUILTINS_COUNT = 808
+EXPECTED_UNKNOWN_COUNT = 17
+EXPECTED_BUILTINS_COUNT = 809
 
 
 # Don't call these at all.