From 30af5579558b66bb4e21666c0ba13a34cf1c9d18 Mon Sep 17 00:00:00 2001 From: "rossberg@chromium.org" Date: Tue, 19 Aug 2014 11:38:38 +0000 Subject: [PATCH] Implement ES6 Array.of() BUG=v8:3427 LOG=N R=rossberg@chromium.org Review URL: https://codereview.chromium.org/364853009 Patch from Diego Pino . git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23194 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/harmony-array.js | 18 +++++ src/runtime.cc | 27 +++++++ src/runtime.h | 1 + test/mjsunit/harmony/array-of.js | 164 +++++++++++++++++++++++++++++++++++++++ tools/generate-runtime-tests.py | 6 +- 5 files changed, 213 insertions(+), 3 deletions(-) create mode 100644 test/mjsunit/harmony/array-of.js diff --git a/src/harmony-array.js b/src/harmony-array.js index dbcb292..88b878f 100644 --- a/src/harmony-array.js +++ b/src/harmony-array.js @@ -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, diff --git a/src/runtime.cc b/src/runtime.cc index abf1935..b6c4525 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -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(unchecked_attributes); + + uint32_t index = 0; + key->ToArrayIndex(&index); + + Handle 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); diff --git a/src/runtime.h b/src/runtime.h index 81b12b1..bcdbc60 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -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 index 0000000..c0a8ed1 --- /dev/null +++ b/test/mjsunit/harmony/array-of.js @@ -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); +}); diff --git a/tools/generate-runtime-tests.py b/tools/generate-runtime-tests.py index 3081667..68a88d2 100755 --- a/tools/generate-runtime-tests.py +++ b/tools/generate-runtime-tests.py @@ -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. -- 2.7.4