Chromium Code Reviews| Index: src/runtime.cc |
| diff --git a/src/runtime.cc b/src/runtime.cc |
| index 995b1cccf09e6570652f94eda27883e92dbf6ba1..850055a961ca1a4454fe30951b4c000bc983aa54 100644 |
| --- a/src/runtime.cc |
| +++ b/src/runtime.cc |
| @@ -1925,15 +1925,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionMarkNameShouldPrintAsAnonymous) { |
| } |
| -RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetBound) { |
| - HandleScope scope(isolate); |
| - ASSERT(args.length() == 1); |
| - |
| - CONVERT_CHECKED(JSFunction, fun, args[0]); |
| - fun->shared()->set_bound(true); |
| - return isolate->heap()->undefined_value(); |
| -} |
| - |
| RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionRemovePrototype) { |
| NoHandleAllocation ha; |
| ASSERT(args.length() == 1); |
| @@ -2012,24 +2003,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetLength) { |
| } |
| -// Creates a local, readonly, property called length with the correct |
| -// length (when read by the user). This effectively overwrites the |
| -// interceptor used to normally provide the length. |
| -RUNTIME_FUNCTION(MaybeObject*, Runtime_BoundFunctionSetLength) { |
| - NoHandleAllocation ha; |
| - ASSERT(args.length() == 2); |
| - CONVERT_CHECKED(JSFunction, fun, args[0]); |
| - CONVERT_CHECKED(Smi, length, args[1]); |
| - MaybeObject* maybe_name = |
| - isolate->heap()->AllocateStringFromAscii(CStrVector("length")); |
| - String* name; |
| - if (!maybe_name->To(&name)) return maybe_name; |
| - PropertyAttributes attr = |
| - static_cast<PropertyAttributes>(DONT_DELETE | DONT_ENUM | READ_ONLY); |
| - return fun->AddProperty(name, length, attr, kNonStrictMode); |
| -} |
| - |
| - |
| RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetPrototype) { |
| NoHandleAllocation ha; |
| ASSERT(args.length() == 2); |
| @@ -7921,8 +7894,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewClosure) { |
| } |
| -static SmartArrayPointer<Handle<Object> > GetNonBoundArguments( |
| - int bound_argc, |
| +// Find the arguments of the JavaScript function invocation that called |
| +// into C++ code. Collect these in a newly allocated array of handles (possibly |
| +// prefixed by a number of empty handles). |
| +static SmartArrayPointer<Handle<Object> > GetCallerArguments( |
| + int prefix_argc, |
| int* total_argc) { |
| // Find frame containing arguments passed to the caller. |
| JavaScriptFrameIterator it; |
| @@ -7938,12 +7914,12 @@ static SmartArrayPointer<Handle<Object> > GetNonBoundArguments( |
| inlined_frame_index, |
| &args_slots); |
| - *total_argc = bound_argc + args_count; |
| + *total_argc = prefix_argc + args_count; |
| SmartArrayPointer<Handle<Object> > param_data( |
| NewArray<Handle<Object> >(*total_argc)); |
| for (int i = 0; i < args_count; i++) { |
| Handle<Object> val = args_slots[i].GetValue(); |
| - param_data[bound_argc + i] = val; |
| + param_data[prefix_argc + i] = val; |
| } |
| return param_data; |
| } else { |
| @@ -7951,49 +7927,139 @@ static SmartArrayPointer<Handle<Object> > GetNonBoundArguments( |
| frame = it.frame(); |
| int args_count = frame->ComputeParametersCount(); |
| - *total_argc = bound_argc + args_count; |
| + *total_argc = prefix_argc + args_count; |
| SmartArrayPointer<Handle<Object> > param_data( |
| NewArray<Handle<Object> >(*total_argc)); |
| for (int i = 0; i < args_count; i++) { |
| Handle<Object> val = Handle<Object>(frame->GetParameter(i)); |
| - param_data[bound_argc + i] = val; |
| + param_data[prefix_argc + i] = val; |
| } |
| return param_data; |
| } |
| } |
| +RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionBindArguments) { |
| + HandleScope scope(isolate); |
| + ASSERT(args.length() == 3); |
| + CONVERT_ARG_CHECKED(JSFunction, bound_function, 0); |
| + Handle<Object> bindee = args.at<Object>(1); |
| + |
| + // TODO(lrn): Create bound function in C++ code from premade shared info. |
| + bound_function->shared()->set_bound(true); |
| + // Get all arguments of calling function (Function.prototype.bind). |
| + int argc = 0; |
| + SmartArrayPointer<Handle<Object> > arguments = GetCallerArguments(0, &argc); |
| + // Don't count the this-arg. |
| + if (argc > 0) { |
| + ASSERT(*arguments[0] == args[2]); |
| + argc--; |
| + } else { |
| + ASSERT(args[2]->IsUndefined()); |
| + } |
| + // Initialize array of bindings (function, this, and any existing arguments |
| + // if the function was already bound). |
| + int total_argc; |
| + Handle<FixedArray> new_bindings; |
| + int i; |
| + if (bindee->IsJSFunction() && JSFunction::cast(*bindee)->shared()->bound()) { |
| + Handle<FixedArray> old_bindings( |
| + JSFunction::cast(*bindee)->function_bindings()); |
| + new_bindings = |
| + isolate->factory()->NewFixedArray(old_bindings->length() + argc); |
| + bindee = Handle<Object>(old_bindings->get(JSFunction::kBoundFunctionIndex)); |
| + i = 0; |
| + for (int n = old_bindings->length(); i < n; i++) { |
| + new_bindings->set(i, old_bindings->get(i)); |
| + } |
| + total_argc = argc + |
| + old_bindings->length() - JSFunction::kBoundArgumentsStartIndex; |
| + } else { |
| + int array_size = JSFunction::kBoundArgumentsStartIndex + argc; |
| + new_bindings = isolate->factory()->NewFixedArray(array_size); |
| + new_bindings->set(JSFunction::kBoundFunctionIndex, *bindee); |
| + new_bindings->set(JSFunction::kBoundThisIndex, args[2]); |
| + i = 2; |
| + total_argc = argc; |
| + } |
| + // Copy arguments, skipping the first which is "this_arg". |
| + for (int j = 0; j < argc; j++, i++) { |
| + new_bindings->set(i, *arguments[j + 1]); |
| + } |
| + new_bindings->set_map(isolate->heap()->fixed_cow_array_map()); |
| + bound_function->set_function_bindings(*new_bindings); |
| + // Update length. |
| + if (!bindee->IsJSFunction()) { |
| + bindee = Execution::GetFunctionDelegate(bindee); |
|
rossberg
2011/10/13 14:25:01
Unfortunately, that is not quite right for proxies
Lasse Reichstein
2011/10/14 11:19:37
I think it's an easy fix, if we just read the leng
rossberg
2011/10/17 11:49:08
Yes, that's one of the spec issues I raised. The c
|
| + } |
| + int length = JSFunction::cast(*bindee)->shared()->length(); |
| + int new_length = length - total_argc; |
| + if (new_length < 0) new_length = 0; |
| + Handle<String> length_symbol = isolate->factory()->length_symbol(); |
| + PropertyAttributes attr = |
| + static_cast<PropertyAttributes>(DONT_DELETE | DONT_ENUM | READ_ONLY); |
| + ForceSetProperty(bound_function, length_symbol, |
| + Handle<Smi>(Smi::FromInt(new_length)), attr); |
| + return *bound_function; |
| +} |
| + |
| + |
| +RUNTIME_FUNCTION(MaybeObject*, Runtime_BoundFunctionGetBindings) { |
| + HandleScope handles(isolate); |
| + ASSERT(args.length() == 1); |
| + CONVERT_ARG_CHECKED(JSObject, callable, 0); |
| + if (callable->IsJSFunction()) { |
| + Handle<JSFunction> function = Handle<JSFunction>::cast(callable); |
| + if (function->shared()->bound()) { |
| + Handle<FixedArray> bindings(function->function_bindings()); |
| + ASSERT(bindings->map() == isolate->heap()->fixed_cow_array_map()); |
| + return *isolate->factory()->NewJSArrayWithElements(bindings); |
| + } |
| + } |
| + return isolate->heap()->undefined_value(); |
| +} |
| + |
| + |
| RUNTIME_FUNCTION(MaybeObject*, Runtime_NewObjectFromBound) { |
| HandleScope scope(isolate); |
| - ASSERT(args.length() == 2); |
| + ASSERT(args.length() == 1); |
| // First argument is a function to use as a constructor. |
| CONVERT_ARG_CHECKED(JSFunction, function, 0); |
| - |
| - // Second argument is either null or an array of bound arguments. |
| - Handle<FixedArray> bound_args; |
| - int bound_argc = 0; |
| - if (!args[1]->IsNull()) { |
| - CONVERT_ARG_CHECKED(JSArray, params, 1); |
| - RUNTIME_ASSERT(params->HasFastTypeElements()); |
| - bound_args = Handle<FixedArray>(FixedArray::cast(params->elements())); |
| - bound_argc = Smi::cast(params->length())->value(); |
| - } |
| + RUNTIME_ASSERT(function->shared()->bound()); |
| + |
| + // The argument is a bound function. Extract its bound arguments |
| + // and callable. |
| + Handle<FixedArray> bound_args = |
| + Handle<FixedArray>(FixedArray::cast(function->function_bindings())); |
| + int bound_argc = bound_args->length() - JSFunction::kBoundArgumentsStartIndex; |
| + Handle<Object> bound_function( |
| + JSReceiver::cast(bound_args->get(JSFunction::kBoundFunctionIndex))); |
| + ASSERT(!bound_function->IsJSFunction() || |
| + !Handle<JSFunction>::cast(bound_function)->shared()->bound()); |
| int total_argc = 0; |
| SmartArrayPointer<Handle<Object> > param_data = |
| - GetNonBoundArguments(bound_argc, &total_argc); |
| + GetCallerArguments(bound_argc, &total_argc); |
| for (int i = 0; i < bound_argc; i++) { |
| - Handle<Object> val = Handle<Object>(bound_args->get(i)); |
| - param_data[i] = val; |
| + param_data[i] = Handle<Object>(bound_args->get( |
| + JSFunction::kBoundArgumentsStartIndex + i)); |
| } |
| + if (!bound_function->IsJSFunction()) { |
| + bool exception_thrown; |
| + bound_function = Execution::TryGetConstructorDelegate(bound_function, |
| + &exception_thrown); |
| + if (exception_thrown) return Failure::Exception(); |
| + } |
| + ASSERT(bound_function->IsJSFunction()); |
| + |
| bool exception = false; |
| Handle<Object> result = |
| - Execution::New(function, total_argc, *param_data, &exception); |
| + Execution::New(Handle<JSFunction>::cast(bound_function), |
| + total_argc, *param_data, &exception); |
| if (exception) { |
| - return Failure::Exception(); |
| + return Failure::Exception(); |
| } |
| - |
| ASSERT(!result.is_null()); |
| return *result; |
| } |