Index: src/builtins.cc |
diff --git a/src/builtins.cc b/src/builtins.cc |
index e8e7109d19d9610eac690e268503291bc1f2a989..7ded532241be6fb193059a1943f24bb7ac838308 100644 |
--- a/src/builtins.cc |
+++ b/src/builtins.cc |
@@ -419,22 +419,25 @@ BUILTIN(ArrayUnshift) { |
} |
-static Object* SlowArraySlice(Handle<Object> receiver, |
- Object** arg0, |
- Object** arg1 = NULL) { |
+static Object* CallJsBuiltin(const char* name, |
+ BuiltinArguments<NO_EXTRA_ARGUMENTS> args) { |
HandleScope handleScope; |
- Handle<Object> slow_slice = |
+ Handle<Object> js_builtin = |
GetProperty(Handle<JSObject>(Top::global_context()->builtins()), |
- "ArraySlice"); |
- ASSERT(slow_slice->IsJSFunction()); |
- Handle<JSFunction> function(Handle<JSFunction>::cast(slow_slice)); |
- Object** args[] = { arg0, arg1 }; |
+ name); |
+ ASSERT(js_builtin->IsJSFunction()); |
+ Handle<JSFunction> function(Handle<JSFunction>::cast(js_builtin)); |
+ Vector<Object**> argv(Vector<Object**>::New(args.length() - 1)); |
+ int n_args = args.length() - 1; |
+ for (int i = 0; i < n_args; i++) { |
+ argv[i] = &args[i + 1]; |
+ } |
bool pending_exception = false; |
Handle<Object> result = Execution::Call(function, |
- receiver, |
- arg1 == NULL ? 1 : 2, |
- args, |
+ args.receiver(), |
+ n_args, |
+ argv.start(), |
&pending_exception); |
if (pending_exception) return Failure::Exception(); |
return *result; |
@@ -459,18 +462,14 @@ BUILTIN(ArraySlice) { |
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]); |
- } |
+ return CallJsBuiltin("ArraySlice", args); |
} |
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]); |
+ return CallJsBuiltin("ArraySlice", args); |
} |
} |
} |
@@ -523,6 +522,140 @@ BUILTIN(ArraySlice) { |
} |
+BUILTIN(ArraySplice) { |
+ JSArray* array = JSArray::cast(*args.receiver()); |
+ ASSERT(array->HasFastElements()); |
+ |
+ int len = Smi::cast(array->length())->value(); |
+ |
+ int n_arguments = args.length() - 1; |
+ |
+ // SpiderMonkey and JSC return undefined in the case where no |
+ // arguments are given instead of using the implicit undefined |
+ // arguments. This does not follow ECMA-262, but we do the same for |
+ // compatibility. |
+ // TraceMonkey follows ECMA-262 though. |
+ if (n_arguments == 0) { |
+ return Heap::undefined_value(); |
+ } |
+ |
+ int relativeStart = 0; |
+ Object* arg1 = args[1]; |
+ if (arg1->IsSmi()) { |
+ relativeStart = Smi::cast(arg1)->value(); |
+ } else if (!arg1->IsUndefined()) { |
+ return CallJsBuiltin("ArraySplice", args); |
+ } |
+ int actualStart = (relativeStart < 0) ? Max(len + relativeStart, 0) |
+ : Min(relativeStart, len); |
+ |
+ // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is |
+ // given differently from when an undefined delete count is given. |
+ // This does not follow ECMA-262, but we do the same for |
+ // compatibility. |
+ int deleteCount = len; |
+ if (n_arguments > 1) { |
+ Object* arg2 = args[2]; |
+ if (arg2->IsSmi()) { |
+ deleteCount = Smi::cast(arg2)->value(); |
+ } else { |
+ return CallJsBuiltin("ArraySplice", args); |
+ } |
+ } |
+ int actualDeleteCount = Min(Max(deleteCount, 0), len - actualStart); |
+ |
+ JSFunction* array_function = |
+ Top::context()->global_context()->array_function(); |
+ |
+ // Allocate result array. |
+ Object* result = Heap::AllocateJSObject(array_function); |
+ if (result->IsFailure()) return result; |
+ JSArray* result_array = JSArray::cast(result); |
+ |
+ result = Heap::AllocateFixedArrayWithHoles(actualDeleteCount); |
+ 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 k = 0; k < actualDeleteCount; k++) { |
+ result_elms->set(k, |
+ GetElementToMove(actualStart + k, elms, prototype), |
+ mode); |
+ } |
+ |
+ // Set elements. |
+ result_array->set_elements(result_elms); |
+ |
+ // Set the length. |
+ result_array->set_length(Smi::FromInt(actualDeleteCount)); |
+ |
+ int itemCount = (n_arguments > 1) ? (n_arguments - 2) : 0; |
+ |
+ int new_length = len - actualDeleteCount + itemCount; |
+ |
+ mode = elms->GetWriteBarrierMode(no_gc); |
+ if (itemCount < actualDeleteCount) { |
+ // Shrink the array. |
+ for (int k = actualStart; k < (len - actualDeleteCount); k++) { |
+ elms->set(k + itemCount, |
+ GetElementToMove(k + actualDeleteCount, elms, prototype), |
+ mode); |
+ } |
+ |
+ for (int k = len; k > new_length; k--) { |
+ elms->set(k - 1, Heap::the_hole_value()); |
+ } |
+ } else if (itemCount > actualDeleteCount) { |
+ FixedArray* source_elms = elms; |
+ |
+ // Check if array need to grow. |
+ 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; |
+ |
+ FixedArray* new_elms = FixedArray::cast(obj); |
+ mode = new_elms->GetWriteBarrierMode(no_gc); |
+ |
+ // Copy the part before actualStart as is. |
+ for (int k = 0; k < actualStart; k++) { |
+ new_elms->set(k, elms->get(k), mode); |
+ } |
+ |
+ source_elms = elms; |
+ elms = new_elms; |
+ array->set_elements(elms); |
+ } |
+ |
+ for (int k = len - actualDeleteCount; k > actualStart; k--) { |
+ elms->set(k + itemCount - 1, |
+ GetElementToMove(k + actualDeleteCount - 1, |
+ source_elms, |
+ prototype), |
+ mode); |
+ } |
+ } |
+ |
+ for (int k = actualStart; k < actualStart + itemCount; k++) { |
+ elms->set(k, args[3 + k - actualStart], mode); |
+ } |
+ |
+ // Set the length. |
+ array->set_length(Smi::FromInt(new_length)); |
+ |
+ return result_array; |
+} |
+ |
+ |
// ----------------------------------------------------------------------------- |
// |