Commit e02a5aa6 authored by antonm@chromium.org's avatar antonm@chromium.org

Introduce builtin for Array.unshift function.

Review URL: http://codereview.chromium.org/600124

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3859 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent a2bb2156
......@@ -1494,6 +1494,8 @@ void Genesis::BuildSpecialFunctionTable() {
Handle<Code>(Builtins::builtin(Builtins::ArrayPush)));
AddSpecialFunction(special_prototype, "shift",
Handle<Code>(Builtins::builtin(Builtins::ArrayShift)));
AddSpecialFunction(special_prototype, "unshift",
Handle<Code>(Builtins::builtin(Builtins::ArrayUnshift)));
}
......
......@@ -246,19 +246,16 @@ BUILTIN(ArrayPush) {
JSArray* array = JSArray::cast(*args.receiver());
ASSERT(array->HasFastElements());
// Make sure we have space for the elements.
int len = Smi::cast(array->length())->value();
int to_add = args.length() - 1;
if (to_add == 0) {
return Smi::FromInt(len);
}
// Set new length.
int new_length = len + args.length() - 1;
int new_length = len + to_add;
FixedArray* elms = FixedArray::cast(array->elements());
if (new_length <= elms->length()) {
// Backing storage has extra space for the provided values.
for (int index = 0; index < args.length() - 1; index++) {
elms->set(index + len, args[index+1]);
}
} else {
if (new_length > elms->length()) {
// New backing storage is needed.
int capacity = new_length + (new_length >> 1) + 16;
Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
......@@ -269,16 +266,21 @@ BUILTIN(ArrayPush) {
WriteBarrierMode mode = new_elms->GetWriteBarrierMode(no_gc);
// Fill out the new array with old elements.
for (int i = 0; i < len; i++) new_elms->set(i, elms->get(i), mode);
// Add the provided values.
for (int index = 0; index < args.length() - 1; index++) {
new_elms->set(index + len, args[index+1], mode);
}
// Set the new backing storage.
array->set_elements(new_elms);
elms = new_elms;
array->set_elements(elms);
}
AssertNoAllocation no_gc;
WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
// Add the provided values.
for (int index = 0; index < to_add; index++) {
elms->set(index + len, args[index + 1], mode);
}
// Set the length.
array->set_length(Smi::FromInt(new_length));
return array->length();
return Smi::FromInt(new_length);
}
......@@ -313,6 +315,17 @@ BUILTIN(ArrayPop) {
}
static Object* GetElementToMove(uint32_t index,
FixedArray* elms,
JSObject* prototype) {
Object* e = elms->get(index);
if (e->IsTheHole() && prototype->HasElement(index)) {
e = prototype->GetElement(index);
}
return e;
}
BUILTIN(ArrayShift) {
JSArray* array = JSArray::cast(*args.receiver());
ASSERT(array->HasFastElements());
......@@ -325,21 +338,17 @@ BUILTIN(ArrayShift) {
Top::context()->global_context()->array_function();
JSObject* prototype = JSObject::cast(array_function->prototype());
// Get first element
FixedArray* elms = FixedArray::cast(array->elements());
Object* first = elms->get(0);
// Get first element
Object* first = elms->get(0);
if (first->IsTheHole()) {
first = prototype->GetElement(0);
}
// Shift the elements.
for (int i = 0; i < len - 1; i++) {
Object* e = elms->get(i + 1);
if (e->IsTheHole() && prototype->HasElement(i + 1)) {
e = prototype->GetElement(i + 1);
}
elms->set(i, e);
elms->set(i, GetElementToMove(i + 1, elms, prototype));
}
elms->set(len - 1, Heap::the_hole_value());
......@@ -350,6 +359,66 @@ BUILTIN(ArrayShift) {
}
BUILTIN(ArrayUnshift) {
JSArray* array = JSArray::cast(*args.receiver());
ASSERT(array->HasFastElements());
int len = Smi::cast(array->length())->value();
int to_add = args.length() - 1;
// Note that we cannot quit early if to_add == 0 as
// values should be lifted from prototype into
// the array.
int new_length = len + to_add;
FixedArray* elms = FixedArray::cast(array->elements());
// Fetch the prototype.
JSFunction* array_function =
Top::context()->global_context()->array_function();
JSObject* prototype = JSObject::cast(array_function->prototype());
if (new_length > elms->length()) {
// New backing storage is needed.
int capacity = new_length + (new_length >> 1) + 16;
Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
if (obj->IsFailure()) return obj;
AssertNoAllocation no_gc;
FixedArray* new_elms = FixedArray::cast(obj);
WriteBarrierMode mode = new_elms->GetWriteBarrierMode(no_gc);
// Fill out the new array with old elements.
for (int i = 0; i < len; i++)
new_elms->set(to_add + i,
GetElementToMove(i, elms, prototype),
mode);
elms = new_elms;
array->set_elements(elms);
} else {
AssertNoAllocation no_gc;
WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
// Move elements to the right
for (int i = 0; i < len; i++) {
elms->set(new_length - i - 1,
GetElementToMove(len - i - 1, elms, prototype),
mode);
}
}
// Add the provided values.
AssertNoAllocation no_gc;
WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
for (int i = 0; i < to_add; i++) {
elms->set(i, args[i + 1], mode);
}
// Set the length.
array->set_length(Smi::FromInt(new_length));
return Smi::FromInt(new_length);
}
// -----------------------------------------------------------------------------
//
......
......@@ -49,6 +49,7 @@ enum BuiltinExtraArguments {
V(ArrayPush, NO_EXTRA_ARGUMENTS) \
V(ArrayPop, NO_EXTRA_ARGUMENTS) \
V(ArrayShift, NO_EXTRA_ARGUMENTS) \
V(ArrayUnshift, NO_EXTRA_ARGUMENTS) \
\
V(HandleApiCall, NEEDS_CALLED_FUNCTION) \
V(FastHandleApiCall, NO_EXTRA_ARGUMENTS) \
......
// Copyright 2010 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.
// Check that unshifting array of holes keeps the original array
// as array of holes
(function() {
var array = new Array(10);
assertEquals(13, array.unshift('1st', '2ns', '3rd'));
assertTrue(0 in array);
assertTrue(1 in array);
assertTrue(2 in array);
assertFalse(3 in array);
})();
// Check that unshif with no args has a side-effect of
// feeling the holes with elements from the prototype
// (if present, of course)
(function() {
var len = 3;
var array = new Array(len);
var at0 = '@0';
var at2 = '@2';
Array.prototype[0] = at0;
Array.prototype[2] = at2;
// array owns nothing...
assertFalse(array.hasOwnProperty(0));
assertFalse(array.hasOwnProperty(1));
assertFalse(array.hasOwnProperty(2));
// ... but sees values from Array.prototype
assertEquals(array[0], at0);
assertEquals(array[1], undefined);
assertEquals(array[2], at2);
assertEquals(len, array.unshift());
assertTrue(delete Array.prototype[0]);
assertTrue(delete Array.prototype[2]);
// unshift makes array own 0 and 2...
assertTrue(array.hasOwnProperty(0));
assertFalse(array.hasOwnProperty(1));
assertTrue(array.hasOwnProperty(2));
// ... so they are not affected be delete.
assertEquals(array[0], at0);
assertEquals(array[1], undefined);
assertEquals(array[2], at2);
})();
// Now check the case with array of holes and some elements on prototype.
(function() {
var len = 9;
var array = new Array(len);
Array.prototype[3] = "@3";
Array.prototype[7] = "@7";
assertEquals(len, array.length);
for (var i = 0; i < array.length; i++) {
assertEquals(array[i], Array.prototype[i]);
}
assertEquals(len + 1, array.unshift('head'));
assertEquals(len + 1, array.length);
// Note that unshift copies values from prototype into the array.
assertEquals(array[4], Array.prototype[3]);
assertTrue(array.hasOwnProperty(4));
assertEquals(array[8], Array.prototype[7]);
assertTrue(array.hasOwnProperty(8));
// ... but keeps the rest as holes:
Array.prototype[5] = "@5";
assertEquals(array[5], Array.prototype[5]);
assertFalse(array.hasOwnProperty(5));
assertEquals(array[3], Array.prototype[3]);
assertFalse(array.hasOwnProperty(3));
assertEquals(array[7], Array.prototype[7]);
assertFalse(array.hasOwnProperty(7));
assertTrue(delete Array.prototype[3]);
assertTrue(delete Array.prototype[5]);
assertTrue(delete Array.prototype[7]);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment