Revision: 3871
Author: [email protected]
Date: Tue Feb 16 04:14:23 2010
Log: Introduce builtin for Array.slice function.

Review URL: http://codereview.chromium.org/604059
http://code.google.com/p/v8/source/detail?r=3871

Added:
 /branches/bleeding_edge/test/mjsunit/array-slice.js
Modified:
 /branches/bleeding_edge/src/bootstrapper.cc
 /branches/bleeding_edge/src/builtins.cc
 /branches/bleeding_edge/src/builtins.h
 /branches/bleeding_edge/src/execution.cc
 /branches/bleeding_edge/src/ic.cc

=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/array-slice.js Tue Feb 16 04:14:23 2010
@@ -0,0 +1,162 @@
+// 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 slicing array of holes keeps it as array of holes
+(function() {
+  var array = new Array(10);
+  for (var i = 0; i < 7; i++) {
+    var sliced = array.slice();
+    assertEquals(array.length, sliced.length);
+    assertFalse(0 in sliced);
+  }
+})();
+
+
+// Check various forms of arguments omission.
+(function() {
+  var array = new Array(7);
+
+  for (var i = 0; i < 7; i++) {
+    assertEquals(array, array.slice());
+    assertEquals(array, array.slice(0));
+    assertEquals(array, array.slice(undefined));
+    assertEquals(array, array.slice("foobar"));
+    assertEquals(array, array.slice(undefined, undefined));
+  }
+})();
+
+
+// Check variants of negatives and positive indices.
+(function() {
+  var array = new Array(7);
+
+  for (var i = 0; i < 7; i++) {
+    assertEquals(7, array.slice(-100).length);
+    assertEquals(3, array.slice(-3).length);
+    assertEquals(3, array.slice(4).length);
+    assertEquals(1, array.slice(6).length);
+    assertEquals(0, array.slice(7).length);
+    assertEquals(0, array.slice(8).length);
+    assertEquals(0, array.slice(100).length);
+
+    assertEquals(0, array.slice(0, -100).length);
+    assertEquals(4, array.slice(0, -3).length);
+    assertEquals(4, array.slice(0, 4).length);
+    assertEquals(6, array.slice(0, 6).length);
+    assertEquals(7, array.slice(0, 7).length);
+    assertEquals(7, array.slice(0, 8).length);
+    assertEquals(7, array.slice(0, 100).length);
+
+    // Some exotic cases.
+
+    obj = { toString: function() { throw 'Exception'; } };
+
+    // More than 2 arguments:
+    assertEquals(7, array.slice(0, 7, obj, null, undefined).length);
+
+    // Custom conversion:
+    assertEquals(1, array.slice({valueOf: function() { return 1; }},
+ {toString: function() { return 2; }}).length);
+
+    // Throwing an exception in conversion:
+    try {
+      assertEquals(7, array.slice(0, obj).length);
+      throw 'Should have thrown';
+    } catch (e) {
+      assertEquals('Exception', e);
+    }
+  }
+})();
+
+
+// Nasty: modify the array in ToInteger.
+(function() {
+  var array = [];
+  var expected = []
+ bad_guy = { valueOf: function() { array.push(array.length); return -1; } };
+
+  for (var i = 0; i < 13; i++) {
+    var sliced = array.slice(bad_guy);
+    expected.push(i);
+    assertEquals(expected, array);
+    // According to the spec (15.4.4.10), length is calculated before
+    // performing ToInteger on arguments.
+    if (i == 0) {
+      assertEquals([], sliced);  // Length was 0, nothing to get.
+    } else {
+      // Actually out of array [0..i] we get [i - 1] as length is i.
+      assertEquals([i - 1], sliced);
+    }
+  }
+})();
+
+
+// Now check the case with array of holes and some elements on prototype.
+(function() {
+  var len = 9;
+  var array = new Array(len);
+
+  var at3 = "@3";
+  var at7 = "@7";
+
+  for (var i = 0; i < 7; i++) {
+    Array.prototype[3] = at3;
+    Array.prototype[7] = at7;
+
+    assertEquals(len, array.length);
+    for (var i = 0; i < array.length; i++) {
+      assertEquals(array[i], Array.prototype[i]);
+    }
+
+    var sliced = array.slice();
+
+    assertEquals(len, sliced.length);
+
+    assertTrue(delete Array.prototype[3]);
+    assertTrue(delete Array.prototype[7]);
+
+    // Note that slice copies values from prototype into the array.
+    assertEquals(array[3], undefined);
+    assertFalse(array.hasOwnProperty(3));
+    assertEquals(sliced[3], at3);
+    assertTrue(sliced.hasOwnProperty(3));
+
+    assertEquals(array[7], undefined);
+    assertFalse(array.hasOwnProperty(7));
+    assertEquals(sliced[7], at7);
+    assertTrue(sliced.hasOwnProperty(7));
+
+    // ... but keeps the rest as holes:
+    Array.prototype[5] = "@5";
+    assertEquals(array[5], Array.prototype[5]);
+    assertFalse(array.hasOwnProperty(5));
+    assertEquals(sliced[5], Array.prototype[5]);
+    assertFalse(sliced.hasOwnProperty(5));
+
+    assertTrue(delete Array.prototype[5]);
+  }
+})();
=======================================
--- /branches/bleeding_edge/src/bootstrapper.cc Tue Feb 16 01:24:14 2010
+++ /branches/bleeding_edge/src/bootstrapper.cc Tue Feb 16 04:14:23 2010
@@ -1495,6 +1495,8 @@
Handle<Code>(Builtins::builtin(Builtins::ArrayShift)));
   AddSpecialFunction(special_prototype, "unshift",
Handle<Code>(Builtins::builtin(Builtins::ArrayUnshift)));
+  AddSpecialFunction(special_prototype, "slice",
+ Handle<Code>(Builtins::builtin(Builtins::ArraySlice)));
 }


=======================================
--- /branches/bleeding_edge/src/builtins.cc     Mon Feb 15 05:25:06 2010
+++ /branches/bleeding_edge/src/builtins.cc     Tue Feb 16 04:14:23 2010
@@ -417,6 +417,110 @@
   array->set_length(Smi::FromInt(new_length));
   return Smi::FromInt(new_length);
 }
+
+
+static Object* SlowArraySlice(Handle<Object> receiver,
+                              Object** arg0,
+                              Object** arg1 = NULL) {
+  HandleScope handleScope;
+
+  Handle<Object> slow_slice =
+      GetProperty(Handle<JSObject>(Top::global_context()->builtins()),
+                  "ArraySlice");
+  ASSERT(slow_slice->IsJSFunction());
+  Handle<JSFunction> function(Handle<JSFunction>::cast(slow_slice));
+  Object** args[] = { arg0, arg1 };
+  bool pending_exception = false;
+  Handle<Object> result = Execution::Call(function,
+                                          receiver,
+                                          arg1 == NULL ? 1 : 2,
+                                          args,
+                                          &pending_exception);
+  if (pending_exception) return Failure::Exception();
+  return *result;
+}
+
+
+BUILTIN(ArraySlice) {
+  JSArray* array = JSArray::cast(*args.receiver());
+  ASSERT(array->HasFastElements());
+
+  int len = Smi::cast(array->length())->value();
+
+  int n_arguments = args.length() - 1;
+
+  // Note carefully choosen defaults---if argument is missing,
+  // it's undefined which gets converted to 0 for relativeStart
+  // and to len for relativeEnd.
+  int relativeStart = 0;
+  int relativeEnd = len;
+  if (n_arguments > 0) {
+    Object* arg1 = args[1];
+    if (arg1->IsSmi()) {
+      relativeStart = Smi::cast(arg1)->value();
+    } else if (!arg1->IsUndefined()) {
+      if (n_arguments > 1) {
+        return SlowArraySlice(args.receiver(), &args[1], &args[2]);
+      } else {
+        return SlowArraySlice(args.receiver(), &args[1]);
+      }
+    }
+    if (n_arguments > 1) {
+      Object* arg2 = args[2];
+      if (arg2->IsSmi()) {
+        relativeEnd = Smi::cast(arg2)->value();
+      } else if (!arg2->IsUndefined()) {
+        return SlowArraySlice(args.receiver(), &args[1], &args[2]);
+      }
+    }
+  }
+
+  // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 6.
+  int k = (relativeStart < 0) ? Max(len + relativeStart, 0)
+                              : Min(relativeStart, len);
+
+  // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 8.
+  int final = (relativeEnd < 0) ? Max(len + relativeEnd, 0)
+                                : Min(relativeEnd, len);
+
+  // Calculate the length of result array.
+  int result_len = final - k;
+  if (result_len < 0) {
+    result_len = 0;
+  }
+
+  JSFunction* array_function =
+      Top::context()->global_context()->array_function();
+  Object* result = Heap::AllocateJSObject(array_function);
+  if (result->IsFailure()) return result;
+  JSArray* result_array = JSArray::cast(result);
+
+  result = Heap::AllocateFixedArrayWithHoles(result_len);
+  if (result->IsFailure()) return result;
+  FixedArray* result_elms = FixedArray::cast(result);
+
+  FixedArray* elms = FixedArray::cast(array->elements());
+
+  // Fetch the prototype.
+  JSObject* prototype = JSObject::cast(array_function->prototype());
+
+  AssertNoAllocation no_gc;
+  WriteBarrierMode mode = result_elms->GetWriteBarrierMode(no_gc);
+
+  // Fill newly created array.
+  for (int i = 0; i < result_len; i++) {
+    result_elms->set(i,
+                     GetElementToMove(k + i, elms, prototype),
+                     mode);
+  }
+
+  // Set elements.
+  result_array->set_elements(result_elms);
+
+  // Set the length.
+  result_array->set_length(Smi::FromInt(result_len));
+  return result_array;
+}


// -----------------------------------------------------------------------------
=======================================
--- /branches/bleeding_edge/src/builtins.h      Mon Feb 15 05:25:06 2010
+++ /branches/bleeding_edge/src/builtins.h      Tue Feb 16 04:14:23 2010
@@ -50,6 +50,7 @@
   V(ArrayPop, NO_EXTRA_ARGUMENTS)                                   \
   V(ArrayShift, NO_EXTRA_ARGUMENTS)                                 \
   V(ArrayUnshift, NO_EXTRA_ARGUMENTS)                               \
+  V(ArraySlice, NO_EXTRA_ARGUMENTS)                                 \
                                                                     \
   V(HandleApiCall, NEEDS_CALLED_FUNCTION)                           \
   V(FastHandleApiCall, NO_EXTRA_ARGUMENTS)                          \
=======================================
--- /branches/bleeding_edge/src/execution.cc    Fri Jan 15 13:14:56 2010
+++ /branches/bleeding_edge/src/execution.cc    Tue Feb 16 04:14:23 2010
@@ -91,7 +91,7 @@
     JSEntryFunction entry = FUNCTION_CAST<JSEntryFunction>(code->entry());

     // Call the function through the right JS entry stub.
-    byte* entry_address= func->code()->entry();
+    byte* entry_address = func->code()->entry();
     JSFunction* function = *func;
     Object* receiver_pointer = *receiver;
     value = CALL_GENERATED_CODE(entry, entry_address, function,
=======================================
--- /branches/bleeding_edge/src/ic.cc   Fri Feb 12 06:21:18 2010
+++ /branches/bleeding_edge/src/ic.cc   Tue Feb 16 04:14:23 2010
@@ -455,7 +455,7 @@

   if (result->IsJSFunction()) {
     // Check if there is an optimized (builtin) version of the function.
- // Ignored this will degrade performance for Array.prototype.{push,pop}.
+    // Ignored this will degrade performance for some Array functions.
     // Please note we only return the optimized function iff
     // the JSObject has FastElements.
if (object->IsJSObject() && JSObject::cast(*object)->HasFastElements()) {

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to