From 5152d2e0da3991a179b96fc1fc41c6754cd898ec Mon Sep 17 00:00:00 2001 From: "lrn@chromium.org" Date: Mon, 17 Oct 2011 12:44:16 +0000 Subject: [PATCH] Reimplement Function.prototype.bind. Make instanceof work correctly. BUG=v8:893 Review URL: http://codereview.chromium.org/8199004 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9659 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/code-stubs-arm.cc | 2 +- src/arm/macro-assembler-arm.cc | 12 ++- src/arm/macro-assembler-arm.h | 3 +- src/factory.cc | 22 ++-- src/heap.cc | 2 +- src/ia32/code-stubs-ia32.cc | 2 +- src/ia32/macro-assembler-ia32.cc | 25 ++++- src/ia32/macro-assembler-ia32.h | 6 +- src/macros.py | 5 + src/objects-inl.h | 31 +++++- src/objects.h | 22 +++- src/profile-generator.cc | 6 +- src/runtime.cc | 158 +++++++++++++++++++---------- src/runtime.h | 6 +- src/runtime.js | 6 ++ src/v8natives.js | 96 ++++++++---------- src/x64/code-stubs-x64.cc | 2 +- src/x64/macro-assembler-x64.cc | 21 +++- src/x64/macro-assembler-x64.h | 7 +- test/mjsunit/function-bind.js | 166 +++++++++++++++++++++++-------- test/mjsunit/harmony/proxies-function.js | 147 ++++++++++++++++++--------- test/mjsunit/regress/regress-1229.js | 4 +- 22 files changed, 529 insertions(+), 222 deletions(-) diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index 44923a1..3afa76c 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -3913,7 +3913,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) { } // Get the prototype of the function. - __ TryGetFunctionPrototype(function, prototype, scratch, &slow); + __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true); // Check that the function prototype is a JS object. __ JumpIfSmi(prototype, &slow); diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 918f9eb..d9ba5c2 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -2030,7 +2030,8 @@ void MacroAssembler::DispatchMap(Register obj, void MacroAssembler::TryGetFunctionPrototype(Register function, Register result, Register scratch, - Label* miss) { + Label* miss, + bool miss_on_bound_function) { // Check that the receiver isn't a smi. JumpIfSmi(function, miss); @@ -2038,6 +2039,15 @@ void MacroAssembler::TryGetFunctionPrototype(Register function, CompareObjectType(function, result, scratch, JS_FUNCTION_TYPE); b(ne, miss); + if (miss_on_bound_function) { + ldr(scratch, + FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset)); + ldr(scratch, + FieldMemOperand(scratch, SharedFunctionInfo::kCompilerHintsOffset)); + tst(scratch, Operand(1 << SharedFunctionInfo::kBoundFunction)); + b(ne, miss); + } + // Make sure that the function has an instance prototype. Label non_instance; ldrb(scratch, FieldMemOperand(result, Map::kBitFieldOffset)); diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index 8ee468a..2222baa 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -672,7 +672,8 @@ class MacroAssembler: public Assembler { void TryGetFunctionPrototype(Register function, Register result, Register scratch, - Label* miss); + Label* miss, + bool miss_on_bound_function = false); // Compare object type for heap object. heap_object contains a non-Smi // whose object type should be compared with the given type. This both diff --git a/src/factory.cc b/src/factory.cc index 143b342..987d281 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -497,16 +497,20 @@ Handle Factory::NewFunctionFromSharedFunctionInfo( pretenure); result->set_context(*context); - int number_of_literals = function_info->num_literals(); - Handle literals = NewFixedArray(number_of_literals, pretenure); - if (number_of_literals > 0) { - // Store the object, regexp and array functions in the literals - // array prefix. These functions will be used when creating - // object, regexp and array literals in this function. - literals->set(JSFunction::kLiteralGlobalContextIndex, - context->global_context()); + if (!function_info->bound()) { + int number_of_literals = function_info->num_literals(); + Handle literals = NewFixedArray(number_of_literals, pretenure); + if (number_of_literals > 0) { + // Store the object, regexp and array functions in the literals + // array prefix. These functions will be used when creating + // object, regexp and array literals in this function. + literals->set(JSFunction::kLiteralGlobalContextIndex, + context->global_context()); + } + result->set_literals(*literals); + } else { + result->set_function_bindings(isolate()->heap()->empty_fixed_array()); } - result->set_literals(*literals); result->set_next_function_link(isolate()->heap()->undefined_value()); if (V8::UseCrankshaft() && diff --git a/src/heap.cc b/src/heap.cc index 2a21aa8..3dde96d 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -3269,7 +3269,7 @@ void Heap::InitializeFunction(JSFunction* function, function->set_code(shared->code()); function->set_prototype_or_initial_map(prototype); function->set_context(undefined_value()); - function->set_literals(empty_fixed_array()); + function->set_literals_or_bindings(empty_fixed_array()); function->set_next_function_link(undefined_value()); } diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index c22b45f..7a1967e 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -4869,7 +4869,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) { } // Get the prototype of the function. - __ TryGetFunctionPrototype(function, prototype, scratch, &slow); + __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true); // Check that the function prototype is a JS object. __ JumpIfSmi(prototype, &slow); diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index 3aaa22a..fbb9037 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -1492,6 +1492,19 @@ void MacroAssembler::InitializeFieldsWithFiller(Register start_offset, } +void MacroAssembler::BooleanBitTest(Register object, + int field_offset, + int bit_index) { + bit_index += kSmiTagSize + kSmiShiftSize; + ASSERT(IsPowerOf2(kBitsPerByte)); + int byte_index = bit_index / kBitsPerByte; + int byte_bit_index = bit_index & (kBitsPerByte - 1); + test_b(FieldOperand(object, field_offset + byte_index), + static_cast(1 << byte_bit_index)); +} + + + void MacroAssembler::NegativeZeroTest(Register result, Register op, Label* then_label) { @@ -1522,7 +1535,8 @@ void MacroAssembler::NegativeZeroTest(Register result, void MacroAssembler::TryGetFunctionPrototype(Register function, Register result, Register scratch, - Label* miss) { + Label* miss, + bool miss_on_bound_function) { // Check that the receiver isn't a smi. JumpIfSmi(function, miss); @@ -1530,6 +1544,15 @@ void MacroAssembler::TryGetFunctionPrototype(Register function, CmpObjectType(function, JS_FUNCTION_TYPE, result); j(not_equal, miss); + if (miss_on_bound_function) { + // If a bound function, go to miss label. + mov(scratch, + FieldOperand(function, JSFunction::kSharedFunctionInfoOffset)); + BooleanBitTest(scratch, SharedFunctionInfo::kCompilerHintsOffset, + SharedFunctionInfo::kBoundFunction); + j(not_zero, miss); + } + // Make sure that the function has an instance prototype. Label non_instance; movzx_b(scratch, FieldOperand(result, Map::kBitFieldOffset)); diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index a1b42c2..8528c55 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -594,6 +594,9 @@ class MacroAssembler: public Assembler { // --------------------------------------------------------------------------- // Support functions. + // Check a boolean-bit of a Smi field. + void BooleanBitTest(Register object, int field_offset, int bit_index); + // Check if result is zero and op is negative. void NegativeZeroTest(Register result, Register op, Label* then_label); @@ -610,7 +613,8 @@ class MacroAssembler: public Assembler { void TryGetFunctionPrototype(Register function, Register result, Register scratch, - Label* miss); + Label* miss, + bool miss_on_bound_function = false); // Generates code for reporting that an illegal operation has // occurred. diff --git a/src/macros.py b/src/macros.py index 7a493ca..a42e83c 100644 --- a/src/macros.py +++ b/src/macros.py @@ -128,6 +128,11 @@ macro IS_SPEC_OBJECT(arg) = (%_IsSpecObject(arg)); # we cannot handle those anyway. macro IS_SPEC_FUNCTION(arg) = (%_ClassOf(arg) === 'Function'); +# Indices in bound function info retrieved by %BoundFunctionGetBindings(...). +const kBoundFunctionIndex = 0; +const kBoundThisIndex = 1; +const kBoundArgumentsStartIndex = 2; + # Inline macros. Use %IS_VAR to make sure arg is evaluated only once. macro NUMBER_IS_NAN(arg) = (!%_IsSmi(%IS_VAR(arg)) && !(arg == arg)); macro NUMBER_IS_FINITE(arg) = (%_IsSmi(%IS_VAR(arg)) || ((arg == arg) && (arg != 1/0) && (arg != -1/0))); diff --git a/src/objects-inl.h b/src/objects-inl.h index 267880d..001c739 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -3307,7 +3307,7 @@ ACCESSORS(Map, prototype_transitions, FixedArray, kPrototypeTransitionsOffset) ACCESSORS(Map, constructor, Object, kConstructorOffset) ACCESSORS(JSFunction, shared, SharedFunctionInfo, kSharedFunctionInfoOffset) -ACCESSORS(JSFunction, literals, FixedArray, kLiteralsOffset) +ACCESSORS(JSFunction, literals_or_bindings, FixedArray, kLiteralsOffset) ACCESSORS(JSFunction, next_function_link, Object, @@ -3826,7 +3826,36 @@ bool JSFunction::is_compiled() { } +FixedArray* JSFunction::literals() { + ASSERT(!shared()->bound()); + return literals_or_bindings(); +} + + +void JSFunction::set_literals(FixedArray* literals) { + ASSERT(!shared()->bound()); + set_literals_or_bindings(literals); +} + + +FixedArray* JSFunction::function_bindings() { + ASSERT(shared()->bound()); + return literals_or_bindings(); +} + + +void JSFunction::set_function_bindings(FixedArray* bindings) { + ASSERT(shared()->bound()); + // Bound function literal may be initialized to the empty fixed array + // before the bindings are set. + ASSERT(bindings == GetHeap()->empty_fixed_array() || + bindings->map() == GetHeap()->fixed_cow_array_map()); + set_literals_or_bindings(bindings); +} + + int JSFunction::NumberOfLiterals() { + ASSERT(!shared()->bound()); return literals()->length(); } diff --git a/src/objects.h b/src/objects.h index b95fa57..5dfe037 100644 --- a/src/objects.h +++ b/src/objects.h @@ -5038,7 +5038,7 @@ class SharedFunctionInfo: public HeapObject { public: // Constants for optimizing codegen for strict mode function and // native tests. - // Allows to use byte-widgh instructions. + // Allows to use byte-width instructions. static const int kStrictModeBitWithinByte = (kStrictModeFunction + kCompilerHintsSmiTagSize) % kBitsPerByte; @@ -5116,7 +5116,8 @@ class JSFunction: public JSObject { // Check whether or not this function is inlineable. bool IsInlineable(); - // [literals]: Fixed array holding the materialized literals. + // [literals_or_bindings]: Fixed array holding either + // the materialized literals or the bindings of a bound function. // // If the function contains object, regexp or array literals, the // literals array prefix contains the object, regexp, and array @@ -5125,7 +5126,17 @@ class JSFunction: public JSObject { // or array functions. Performing a dynamic lookup, we might end up // using the functions from a new context that we should not have // access to. - DECL_ACCESSORS(literals, FixedArray) + // + // On bound functions, the array is a (copy-on-write) fixed-array containing + // the function that was bound, bound this-value and any bound + // arguments. Bound functions never contain literals. + DECL_ACCESSORS(literals_or_bindings, FixedArray) + + inline FixedArray* literals(); + inline void set_literals(FixedArray* literals); + + inline FixedArray* function_bindings(); + inline void set_function_bindings(FixedArray* bindings); // The initial map for an object created by this constructor. inline Map* initial_map(); @@ -5213,6 +5224,11 @@ class JSFunction: public JSObject { static const int kLiteralsPrefixSize = 1; static const int kLiteralGlobalContextIndex = 0; + // Layout of the bound-function binding array. + static const int kBoundFunctionIndex = 0; + static const int kBoundThisIndex = 1; + static const int kBoundArgumentsStartIndex = 2; + private: DISALLOW_IMPLICIT_CONSTRUCTORS(JSFunction); }; diff --git a/src/profile-generator.cc b/src/profile-generator.cc index bae35c8..e41ebfa 100644 --- a/src/profile-generator.cc +++ b/src/profile-generator.cc @@ -1930,9 +1930,11 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { SetInternalReference(js_fun, entry, "context", js_fun->unchecked_context(), JSFunction::kContextOffset); - TagObject(js_fun->literals(), "(function literals)"); + TagObject(js_fun->literals_or_bindings(), + "(function literals_or_bindings)"); SetInternalReference(js_fun, entry, - "literals", js_fun->literals(), + "literals_or_bindings", + js_fun->literals_or_bindings(), JSFunction::kLiteralsOffset); } TagObject(js_obj->properties(), "(object properties)"); diff --git a/src/runtime.cc b/src/runtime.cc index e0f507e..e614103 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -1930,15 +1930,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); @@ -2017,24 +2008,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(DONT_DELETE | DONT_ENUM | READ_ONLY); - return fun->AddProperty(name, length, attr, kNonStrictMode); -} - - RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetPrototype) { NoHandleAllocation ha; ASSERT(args.length() == 2); @@ -7926,8 +7899,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewClosure) { } -static SmartArrayPointer > 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 > GetCallerArguments( + int prefix_argc, int* total_argc) { // Find frame containing arguments passed to the caller. JavaScriptFrameIterator it; @@ -7943,12 +7919,12 @@ static SmartArrayPointer > GetNonBoundArguments( inlined_frame_index, &args_slots); - *total_argc = bound_argc + args_count; + *total_argc = prefix_argc + args_count; SmartArrayPointer > param_data( NewArray >(*total_argc)); for (int i = 0; i < args_count; i++) { Handle val = args_slots[i].GetValue(); - param_data[bound_argc + i] = val; + param_data[prefix_argc + i] = val; } return param_data; } else { @@ -7956,49 +7932,131 @@ static SmartArrayPointer > GetNonBoundArguments( frame = it.frame(); int args_count = frame->ComputeParametersCount(); - *total_argc = bound_argc + args_count; + *total_argc = prefix_argc + args_count; SmartArrayPointer > param_data( NewArray >(*total_argc)); for (int i = 0; i < args_count; i++) { Handle val = Handle(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() == 4); + CONVERT_ARG_CHECKED(JSFunction, bound_function, 0); + RUNTIME_ASSERT(args[3]->IsNumber()); + Handle bindee = args.at(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 > 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). + Handle new_bindings; + int i; + if (bindee->IsJSFunction() && JSFunction::cast(*bindee)->shared()->bound()) { + Handle old_bindings( + JSFunction::cast(*bindee)->function_bindings()); + new_bindings = + isolate->factory()->NewFixedArray(old_bindings->length() + argc); + bindee = Handle(old_bindings->get(JSFunction::kBoundFunctionIndex)); + i = 0; + for (int n = old_bindings->length(); i < n; i++) { + new_bindings->set(i, old_bindings->get(i)); + } + } 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; + } + // 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. + Handle length_symbol = isolate->factory()->length_symbol(); + Handle new_length(args.at(3)); + PropertyAttributes attr = + static_cast(DONT_DELETE | DONT_ENUM | READ_ONLY); + ForceSetProperty(bound_function, length_symbol, 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 function = Handle::cast(callable); + if (function->shared()->bound()) { + Handle 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 bound_args; - int bound_argc = 0; - if (!args[1]->IsNull()) { - CONVERT_ARG_CHECKED(JSArray, params, 1); - RUNTIME_ASSERT(params->HasFastTypeElements()); - bound_args = Handle(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 bound_args = + Handle(FixedArray::cast(function->function_bindings())); + int bound_argc = bound_args->length() - JSFunction::kBoundArgumentsStartIndex; + Handle bound_function( + JSReceiver::cast(bound_args->get(JSFunction::kBoundFunctionIndex))); + ASSERT(!bound_function->IsJSFunction() || + !Handle::cast(bound_function)->shared()->bound()); int total_argc = 0; SmartArrayPointer > param_data = - GetNonBoundArguments(bound_argc, &total_argc); + GetCallerArguments(bound_argc, &total_argc); for (int i = 0; i < bound_argc; i++) { - Handle val = Handle(bound_args->get(i)); - param_data[i] = val; + param_data[i] = Handle(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 result = - Execution::New(function, total_argc, *param_data, &exception); + Execution::New(Handle::cast(bound_function), + total_argc, *param_data, &exception); if (exception) { - return Failure::Exception(); + return Failure::Exception(); } - ASSERT(!result.is_null()); return *result; } diff --git a/src/runtime.h b/src/runtime.h index ed9c2b8..7ab5948 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -211,14 +211,14 @@ namespace internal { /* Reflection */ \ F(FunctionSetInstanceClassName, 2, 1) \ F(FunctionSetLength, 2, 1) \ - F(BoundFunctionSetLength, 2, 1) \ F(FunctionSetPrototype, 2, 1) \ F(FunctionSetReadOnlyPrototype, 1, 1) \ F(FunctionGetName, 1, 1) \ F(FunctionSetName, 2, 1) \ F(FunctionNameShouldPrintAsAnonymous, 1, 1) \ F(FunctionMarkNameShouldPrintAsAnonymous, 1, 1) \ - F(FunctionSetBound, 1, 1) \ + F(FunctionBindArguments, 4, 1) \ + F(BoundFunctionGetBindings, 1, 1) \ F(FunctionRemovePrototype, 1, 1) \ F(FunctionGetSourceCode, 1, 1) \ F(FunctionGetScript, 1, 1) \ @@ -304,7 +304,7 @@ namespace internal { /* Statements */ \ F(NewClosure, 3, 1) \ F(NewObject, 1, 1) \ - F(NewObjectFromBound, 2, 1) \ + F(NewObjectFromBound, 1, 1) \ F(FinalizeInstanceSize, 1, 1) \ F(Throw, 1, 1) \ F(ReThrow, 1, 1) \ diff --git a/src/runtime.js b/src/runtime.js index a12f6c7..c3554c6 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -375,6 +375,12 @@ function INSTANCE_OF(F) { return 1; } + // Check if function is bound, if so, get [[BoundFunction]] from it + // and use that instead of F. + var bindings = %BoundFunctionGetBindings(F); + if (bindings) { + F = bindings[kBoundFunctionIndex]; // Always a non-bound function. + } // Get the prototype of F; if it is not an object, throw an error. var O = F.prototype; if (!IS_SPEC_OBJECT(O)) { diff --git a/src/v8natives.js b/src/v8natives.js index dee3032..de80067 100644 --- a/src/v8natives.js +++ b/src/v8natives.js @@ -1517,53 +1517,51 @@ function FunctionToString() { // ES5 15.3.4.5 function FunctionBind(this_arg) { // Length is 1. if (!IS_SPEC_FUNCTION(this)) { - throw new $TypeError('Bind must be called on a function'); - } - // this_arg is not an argument that should be bound. - var argc_bound = (%_ArgumentsLength() || 1) - 1; - var fn = this; - - if (argc_bound == 0) { - var result = function() { - if (%_IsConstructCall()) { - // %NewObjectFromBound implicitly uses arguments passed to this - // function. We do not pass the arguments object explicitly to avoid - // materializing it and guarantee that this function will be optimized. - return %NewObjectFromBound(fn, null); - } - return %Apply(fn, this_arg, arguments, 0, %_ArgumentsLength()); - }; - } else { - var bound_args = new InternalArray(argc_bound); - for(var i = 0; i < argc_bound; i++) { - bound_args[i] = %_Arguments(i+1); + throw new $TypeError('Bind must be called on a function'); + } + var boundFunction = function () { + // This function must not use any object literals (Object, Array, RegExp), + // since the literals-array is being used to store the bound data. + if (%_IsConstructCall()) { + return %NewObjectFromBound(boundFunction); } + var bindings = %BoundFunctionGetBindings(boundFunction); - var result = function() { - // If this is a construct call we use a special runtime method - // to generate the actual object using the bound function. - if (%_IsConstructCall()) { - // %NewObjectFromBound implicitly uses arguments passed to this - // function. We do not pass the arguments object explicitly to avoid - // materializing it and guarantee that this function will be optimized. - return %NewObjectFromBound(fn, bound_args); - } - - // Combine the args we got from the bind call with the args - // given as argument to the invocation. + var argc = %_ArgumentsLength(); + if (argc == 0) { + return %Apply(bindings[0], bindings[1], bindings, 2, bindings.length - 2); + } + if (bindings.length === 2) { + return %Apply(bindings[0], bindings[1], arguments, 0, argc); + } + var bound_argc = bindings.length - 2; + var argv = new InternalArray(bound_argc + argc); + for (var i = 0; i < bound_argc; i++) { + argv[i] = bindings[i + 2]; + } + for (var j = 0; j < argc; j++) { + argv[i++] = %_Arguments(j); + } + return %Apply(bindings[0], bindings[1], argv, 0, bound_argc + argc); + }; + + %FunctionRemovePrototype(boundFunction); + var new_length = 0; + if (%_ClassOf(this) == "Function") { + // Function or FunctionProxy. + var old_length = this.length; + // FunctionProxies might provide a non-UInt32 value. If so, ignore it. + if ((typeof old_length === "number") && + ((old_length >>> 0) === old_length)) { var argc = %_ArgumentsLength(); - var args = new InternalArray(argc + argc_bound); - // Add bound arguments. - for (var i = 0; i < argc_bound; i++) { - args[i] = bound_args[i]; - } - // Add arguments from call. - for (var i = 0; i < argc; i++) { - args[argc_bound + i] = %_Arguments(i); - } - return %Apply(fn, this_arg, args, 0, argc + argc_bound); - }; + if (argc > 0) argc--; // Don't count the thisArg as parameter. + new_length = old_length - argc; + if (new_length < 0) new_length = 0; + } } + // This runtime function finds any remaining arguments on the stack, + // so we don't pass the arguments object. + var result = %FunctionBindArguments(boundFunction, this, this_arg, new_length); // We already have caller and arguments properties on functions, // which are non-configurable. It therefore makes no sence to @@ -1571,17 +1569,7 @@ function FunctionBind(this_arg) { // Length is 1. // that bind should make these throw a TypeError if get or set // is called and make them non-enumerable and non-configurable. // To be consistent with our normal functions we leave this as it is. - - %FunctionRemovePrototype(result); - %FunctionSetBound(result); - // Set the correct length. If this is a function proxy, this.length might - // throw, or return a bogus result. Leave length alone in that case. - // TODO(rossberg): This is underspecified in the current proxy proposal. - try { - var old_length = ToInteger(this.length); - var length = (old_length - argc_bound) > 0 ? old_length - argc_bound : 0; - %BoundFunctionSetLength(result, length); - } catch(x) {} + // TODO(lrn): Do set these to be thrower. return result; } diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index 9dfd169..b210f74 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -3879,7 +3879,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ bind(&miss); } - __ TryGetFunctionPrototype(rdx, rbx, &slow); + __ TryGetFunctionPrototype(rdx, rbx, &slow, true); // Check that the function prototype is a JS object. __ JumpIfSmi(rbx, &slow); diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index 8a275a5..f9459dd 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -2351,6 +2351,13 @@ void MacroAssembler::Test(const Operand& src, Smi* source) { } +void MacroAssembler::TestBit(const Operand& src, int bits) { + int byte_offset = bits / kBitsPerByte; + int bit_in_byte = bits & (kBitsPerByte - 1); + testb(Operand(src, byte_offset), Immediate(1 << bit_in_byte)); +} + + void MacroAssembler::Jump(ExternalReference ext) { LoadAddress(kScratchRegister, ext); jmp(kScratchRegister); @@ -2900,7 +2907,8 @@ Condition MacroAssembler::IsObjectStringType(Register heap_object, void MacroAssembler::TryGetFunctionPrototype(Register function, Register result, - Label* miss) { + Label* miss, + bool miss_on_bound_function) { // Check that the receiver isn't a smi. testl(function, Immediate(kSmiTagMask)); j(zero, miss); @@ -2909,6 +2917,17 @@ void MacroAssembler::TryGetFunctionPrototype(Register function, CmpObjectType(function, JS_FUNCTION_TYPE, result); j(not_equal, miss); + if (miss_on_bound_function) { + movq(kScratchRegister, + FieldOperand(function, JSFunction::kSharedFunctionInfoOffset)); + // It's not smi-tagged (stored in the top half of a smi-tagged 8-byte + // field). + TestBit(FieldOperand(kScratchRegister, + SharedFunctionInfo::kCompilerHintsOffset), + SharedFunctionInfo::kBoundFunction); + j(not_zero, miss); + } + // Make sure that the function has an instance prototype. Label non_instance; testb(FieldOperand(result, Map::kBitFieldOffset), diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h index 526ea7f..cdccb9b 100644 --- a/src/x64/macro-assembler-x64.h +++ b/src/x64/macro-assembler-x64.h @@ -726,6 +726,7 @@ class MacroAssembler: public Assembler { void Push(Smi* smi); void Test(const Operand& dst, Smi* source); + // --------------------------------------------------------------------------- // String macros. @@ -771,6 +772,9 @@ class MacroAssembler: public Assembler { // Move if the registers are not identical. void Move(Register target, Register source); + // Bit-field support. + void TestBit(const Operand& dst, int bit_index); + // Handle support void Move(Register dst, Handle source); void Move(const Operand& dst, Handle source); @@ -1074,7 +1078,8 @@ class MacroAssembler: public Assembler { // clobbered. void TryGetFunctionPrototype(Register function, Register result, - Label* miss); + Label* miss, + bool miss_on_bound_function = false); // Generates code for reporting that an illegal operation has // occurred. diff --git a/test/mjsunit/function-bind.js b/test/mjsunit/function-bind.js index e9d0221..b6fcac4 100644 --- a/test/mjsunit/function-bind.js +++ b/test/mjsunit/function-bind.js @@ -29,29 +29,31 @@ // Simple tests. function foo(x, y, z) { - return x + y + z; + return [this, arguments.length, x]; } +assertEquals(3, foo.length); + var f = foo.bind(foo); -assertEquals(3, f(1, 1, 1)); +assertEquals([foo, 3, 1], f(1, 2, 3)); assertEquals(3, f.length); -f = foo.bind(foo, 2); -assertEquals(4, f(1, 1)); +f = foo.bind(foo, 1); +assertEquals([foo, 3, 1], f(2, 3)); assertEquals(2, f.length); -f = foo.bind(foo, 2, 2); -assertEquals(5, f(1)); +f = foo.bind(foo, 1, 2); +assertEquals([foo, 3, 1], f(3)); assertEquals(1, f.length); -f = foo.bind(foo, 2, 2, 2); -assertEquals(6, f()); +f = foo.bind(foo, 1, 2, 3); +assertEquals([foo, 3, 1], f()); assertEquals(0, f.length); // Test that length works correctly even if more than the actual number // of arguments are given when binding. f = foo.bind(foo, 1, 2, 3, 4, 5, 6, 7, 8, 9); -assertEquals(6, f()); +assertEquals([foo, 9, 1], f()); assertEquals(0, f.length); // Use a different bound object. @@ -78,64 +80,97 @@ assertEquals(0, f.length); // When only giving the thisArg, any number of binds should have // the same effect. f = foo.bind(foo); -assertEquals(3, f(1, 1, 1)); -f = foo.bind(foo).bind(foo).bind(foo).bind(foo); -assertEquals(3, f(1, 1, 1)); +assertEquals([foo, 3, 1], f(1, 2, 3)); + +var not_foo = {}; +f = foo.bind(foo).bind(not_foo).bind(not_foo).bind(not_foo); +assertEquals([foo, 3, 1], f(1, 2, 3)); assertEquals(3, f.length); // Giving bound parameters should work at any place in the chain. -f = foo.bind(foo, 1).bind(foo).bind(foo).bind(foo); -assertEquals(3, f(1, 1)); +f = foo.bind(foo, 1).bind(not_foo).bind(not_foo).bind(not_foo); +assertEquals([foo, 3, 1], f(2, 3)); assertEquals(2, f.length); -f = foo.bind(foo).bind(foo, 1).bind(foo).bind(foo); -assertEquals(3, f(1, 1)); +f = foo.bind(foo).bind(not_foo, 1).bind(not_foo).bind(not_foo); +assertEquals([foo, 3, 1], f(2, 3)); assertEquals(2, f.length); -f = foo.bind(foo).bind(foo).bind(foo,1 ).bind(foo); -assertEquals(3, f(1, 1)); +f = foo.bind(foo).bind(not_foo).bind(not_foo,1 ).bind(not_foo); +assertEquals([foo, 3, 1], f(2, 3)); assertEquals(2, f.length); -f = foo.bind(foo).bind(foo).bind(foo).bind(foo, 1); -assertEquals(3, f(1, 1)); +f = foo.bind(foo).bind(not_foo).bind(not_foo).bind(not_foo, 1); +assertEquals([foo, 3, 1], f(2, 3)); assertEquals(2, f.length); -// Several parameters can be given, and given in different bind invokations. -f = foo.bind(foo, 1, 1).bind(foo).bind(foo).bind(foo); -assertEquals(3, f(1)); +// Several parameters can be given, and given in different bind invocations. +f = foo.bind(foo, 1, 2).bind(not_foo).bind(not_foo).bind(not_foo); +assertEquals([foo, 3, 1], f(3)); assertEquals(1, f.length); -f = foo.bind(foo).bind(foo, 1, 1).bind(foo).bind(foo); -assertEquals(3, f(1)); +f = foo.bind(foo).bind(not_foo, 1, 2).bind(not_foo).bind(not_foo); +assertEquals([foo, 3, 1], f(1)); assertEquals(1, f.length); -f = foo.bind(foo).bind(foo, 1, 1).bind(foo).bind(foo); -assertEquals(3, f(1)); +f = foo.bind(foo).bind(not_foo, 1, 2).bind(not_foo).bind(not_foo); +assertEquals([foo, 3, 1], f(3)); assertEquals(1, f.length); -f = foo.bind(foo).bind(foo).bind(foo, 1, 1).bind(foo); -assertEquals(3, f(1)); +f = foo.bind(foo).bind(not_foo).bind(not_foo, 1, 2).bind(not_foo); +assertEquals([foo, 3, 1], f(1)); assertEquals(1, f.length); -f = foo.bind(foo).bind(foo).bind(foo).bind(foo, 1, 1); -assertEquals(3, f(1)); +f = foo.bind(foo).bind(not_foo).bind(not_foo).bind(not_foo, 1, 2); +assertEquals([foo, 3, 1], f(3)); assertEquals(1, f.length); -f = foo.bind(foo, 1).bind(foo, 1).bind(foo).bind(foo); -assertEquals(3, f(1)); +f = foo.bind(foo, 1).bind(not_foo, 2).bind(not_foo).bind(not_foo); +assertEquals([foo, 3, 1], f(3)); assertEquals(1, f.length); -f = foo.bind(foo, 1).bind(foo).bind(foo, 1).bind(foo); -assertEquals(3, f(1)); +f = foo.bind(foo, 1).bind(not_foo).bind(not_foo, 2).bind(not_foo); +assertEquals([foo, 3, 1], f(3)); assertEquals(1, f.length); -f = foo.bind(foo, 1).bind(foo).bind(foo).bind(foo, 1); -assertEquals(3, f(1)); +f = foo.bind(foo, 1).bind(not_foo).bind(not_foo).bind(not_foo, 2); +assertEquals([foo, 3, 1], f(3)); assertEquals(1, f.length); -f = foo.bind(foo).bind(foo, 1).bind(foo).bind(foo, 1); -assertEquals(3, f(1)); +f = foo.bind(foo).bind(not_foo, 1).bind(not_foo).bind(not_foo, 2); +assertEquals([foo, 3, 1], f(3)); +assertEquals(1, f.length); + +// The wrong number of arguments can be given to bound functions too. +f = foo.bind(foo); +assertEquals(3, f.length); +assertEquals([foo, 0, undefined], f()); +assertEquals([foo, 1, 1], f(1)); +assertEquals([foo, 2, 1], f(1, 2)); +assertEquals([foo, 3, 1], f(1, 2, 3)); +assertEquals([foo, 4, 1], f(1, 2, 3, 4)); + +f = foo.bind(foo, 1); +assertEquals(2, f.length); +assertEquals([foo, 1, 1], f()); +assertEquals([foo, 2, 1], f(2)); +assertEquals([foo, 3, 1], f(2, 3)); +assertEquals([foo, 4, 1], f(2, 3, 4)); + +f = foo.bind(foo, 1, 2); assertEquals(1, f.length); +assertEquals([foo, 2, 1], f()); +assertEquals([foo, 3, 1], f(3)); +assertEquals([foo, 4, 1], f(3, 4)); + +f = foo.bind(foo, 1, 2, 3); +assertEquals(0, f.length); +assertEquals([foo, 3, 1], f()); +assertEquals([foo, 4, 1], f(4)); + +f = foo.bind(foo, 1, 2, 3, 4); +assertEquals(0, f.length); +assertEquals([foo, 4, 1], f()); // Test constructor calls. @@ -171,13 +206,60 @@ assertEquals(3, obj2.z); // Test bind chains when used as a constructor. - f = bar.bind(bar, 1).bind(bar, 2).bind(bar, 3); obj2 = new f(); assertEquals(1, obj2.x); assertEquals(2, obj2.y); assertEquals(3, obj2.z); -// Test instanceof obj2 is bar, not f. +// Test obj2 is instanceof both bar and f. assertTrue(obj2 instanceof bar); -assertFalse(obj2 instanceof f); +assertTrue(obj2 instanceof f); + +// This-args are not relevant to instanceof. +f = bar.bind(foo.prototype, 1). + bind(String.prototype, 2). + bind(Function.prototype, 3); +var obj3 = new f(); +assertTrue(obj3 instanceof bar); +assertTrue(obj3 instanceof f); +assertFalse(obj3 instanceof foo); +assertFalse(obj3 instanceof Function); +assertFalse(obj3 instanceof String); + +// thisArg is converted to object. +f = foo.bind(undefined); +assertEquals([this, 0, undefined], f()); + +f = foo.bind(null); +assertEquals([this, 0, undefined], f()); + +f = foo.bind(42); +assertEquals([Object(42), 0, undefined], f()); + +f = foo.bind("foo"); +assertEquals([Object("foo"), 0, undefined], f()); + +f = foo.bind(true); +assertEquals([Object(true), 0, undefined], f()); + +// Strict functions don't convert thisArg. +function soo(x, y, z) { + "use strict"; + return [this, arguments.length, x]; +} + +var s = soo.bind(undefined); +assertEquals([undefined, 0, undefined], s()); + +s = soo.bind(null); +assertEquals([null, 0, undefined], s()); + +s = soo.bind(42); +assertEquals([42, 0, undefined], s()); + +s = soo.bind("foo"); +assertEquals(["foo", 0, undefined], s()); + +s = soo.bind(true); +assertEquals([true, 0, undefined], s()); diff --git a/test/mjsunit/harmony/proxies-function.js b/test/mjsunit/harmony/proxies-function.js index 7b08ad7..32fb1fb 100644 --- a/test/mjsunit/harmony/proxies-function.js +++ b/test/mjsunit/harmony/proxies-function.js @@ -38,6 +38,13 @@ function CreateFrozen(handler, callTrap, constructTrap) { } +// Ensures that checking the "length" property of a function proxy doesn't +// crash due to lack of a [[Get]] method. +var handler = { + get : function(r, n) { return n == "length" ? 2 : undefined } +} + + // Calling (call, Function.prototype.call, Function.prototype.apply, // Function.prototype.bind). @@ -145,29 +152,34 @@ function TestCall(isStrict, callTrap) { } TestCall(false, function(x, y) { - receiver = this; return x + y + receiver = this + return x + y }) TestCall(true, function(x, y) { - "use strict"; - receiver = this; return x + y + "use strict" + receiver = this + return x + y }) TestCall(false, function() { receiver = this; return arguments[0] + arguments[1] }) -TestCall(false, Proxy.createFunction({}, function(x, y) { - receiver = this; return x + y +TestCall(false, Proxy.createFunction(handler, function(x, y) { + receiver = this + return x + y })) -TestCall(true, Proxy.createFunction({}, function(x, y) { - "use strict"; - receiver = this; return x + y +TestCall(true, Proxy.createFunction(handler, function(x, y) { + "use strict" + receiver = this + return x + y })) -TestCall(false, CreateFrozen({}, function(x, y) { - receiver = this; return x + y +TestCall(false, CreateFrozen(handler, function(x, y) { + receiver = this + return x + y })) @@ -218,24 +230,48 @@ var prototype = {} var receiver var handlerWithPrototype = { - fix: function() { return {prototype: prototype} }, - get: function(r, n) { assertEquals("prototype", n); return prototype } + fix: function() { return { prototype: { value: prototype } }; }, + get: function(r, n) { + if (n == "length") return 2; + assertEquals("prototype", n); + return prototype; + } } var handlerSansPrototype = { - fix: function() { return {} }, - get: function(r, n) { assertEquals("prototype", n); return undefined } + fix: function() { return { length: { value: 2 } } }, + get: function(r, n) { + if (n == "length") return 2; + assertEquals("prototype", n); + return undefined; + } +} + +function ReturnUndef(x, y) { + "use strict"; + receiver = this; + this.sum = x + y; +} + +function ReturnThis(x, y) { + "use strict"; + receiver = this; + this.sum = x + y; + return this; +} + +function ReturnNew(x, y) { + "use strict"; + receiver = this; + return {sum: x + y}; } -function ReturnUndef(x, y) { "use strict"; receiver = this; this.sum = x + y } -function ReturnThis(x, y) { "use strict"; receiver = this; this.sum = x + y; return this } -function ReturnNew(x, y) { "use strict"; receiver = this; return {sum: x + y} } function ReturnNewWithProto(x, y) { "use strict"; receiver = this; - var result = Object.create(prototype) - result.sum = x + y - return result + var result = Object.create(prototype); + result.sum = x + y; + return result; } function TestConstruct(proto, constructTrap) { @@ -273,11 +309,11 @@ function TestConstruct2(proto, constructTrap, handler) { TestConstruct(Object.prototype, ReturnNew) TestConstruct(prototype, ReturnNewWithProto) -TestConstruct(Object.prototype, Proxy.createFunction({}, ReturnNew)) -TestConstruct(prototype, Proxy.createFunction({}, ReturnNewWithProto)) +TestConstruct(Object.prototype, Proxy.createFunction(handler, ReturnNew)) +TestConstruct(prototype, Proxy.createFunction(handler, ReturnNewWithProto)) -TestConstruct(Object.prototype, CreateFrozen({}, ReturnNew)) -TestConstruct(prototype, CreateFrozen({}, ReturnNewWithProto)) +TestConstruct(Object.prototype, CreateFrozen(handler, ReturnNew)) +TestConstruct(prototype, CreateFrozen(handler, ReturnNewWithProto)) @@ -321,10 +357,14 @@ TestConstructFromCall(Object.prototype, true, ReturnThis) TestConstructFromCall(Object.prototype, false, ReturnNew) TestConstructFromCall(prototype, false, ReturnNewWithProto) -TestConstructFromCall(Object.prototype, true, Proxy.createFunction({}, ReturnUndef)) -TestConstructFromCall(Object.prototype, true, Proxy.createFunction({}, ReturnThis)) -TestConstructFromCall(Object.prototype, false, Proxy.createFunction({}, ReturnNew)) -TestConstructFromCall(prototype, false, Proxy.createFunction({}, ReturnNewWithProto)) +TestConstructFromCall(Object.prototype, true, + Proxy.createFunction(handler, ReturnUndef)) +TestConstructFromCall(Object.prototype, true, + Proxy.createFunction(handler, ReturnThis)) +TestConstructFromCall(Object.prototype, false, + Proxy.createFunction(handler, ReturnNew)) +TestConstructFromCall(prototype, false, + Proxy.createFunction(handler, ReturnNewWithProto)) TestConstructFromCall(Object.prototype, true, CreateFrozen({}, ReturnUndef)) TestConstructFromCall(Object.prototype, true, CreateFrozen({}, ReturnThis)) @@ -341,29 +381,44 @@ TestConstructFromCall(prototype, true, ReturnThis) TestConstructFromCall(Object.prototype, false, ReturnNew) TestConstructFromCall(prototype, false, ReturnNewWithProto) -TestConstructFromCall(Object.prototype, true, Proxy.createFunction({}, ReturnUndef)) -TestConstructFromCall(Object.prototype, true, Proxy.createFunction({}, ReturnThis)) -TestConstructFromCall(Object.prototype, false, Proxy.createFunction({}, ReturnNew)) -TestConstructFromCall(prototype, false, Proxy.createFunction({}, ReturnNewWithProto)) - -TestConstructFromCall(prototype, true, Proxy.createFunction(handlerWithPrototype, ReturnUndef)) -TestConstructFromCall(prototype, true, Proxy.createFunction(handlerWithPrototype, ReturnThis)) -TestConstructFromCall(Object.prototype, false, Proxy.createFunction(handlerWithPrototype, ReturnNew)) -TestConstructFromCall(prototype, false, Proxy.createFunction(handlerWithPrototype, ReturnNewWithProto)) - -TestConstructFromCall(prototype, true, CreateFrozen(handlerWithPrototype, ReturnUndef)) -TestConstructFromCall(prototype, true, CreateFrozen(handlerWithPrototype, ReturnThis)) -TestConstructFromCall(Object.prototype, false, CreateFrozen(handlerWithPrototype, ReturnNew)) -TestConstructFromCall(prototype, false, CreateFrozen(handlerWithPrototype, ReturnNewWithProto)) +TestConstructFromCall(Object.prototype, true, + Proxy.createFunction(handler, ReturnUndef)) +TestConstructFromCall(Object.prototype, true, + Proxy.createFunction(handler, ReturnThis)) +TestConstructFromCall(Object.prototype, false, + Proxy.createFunction(handler, ReturnNew)) +TestConstructFromCall(prototype, false, + Proxy.createFunction(handler, ReturnNewWithProto)) + +TestConstructFromCall(prototype, true, + Proxy.createFunction(handlerWithPrototype, ReturnUndef)) +TestConstructFromCall(prototype, true, + Proxy.createFunction(handlerWithPrototype, ReturnThis)) +TestConstructFromCall(Object.prototype, false, + Proxy.createFunction(handlerWithPrototype, ReturnNew)) +TestConstructFromCall(prototype, false, + Proxy.createFunction(handlerWithPrototype, + ReturnNewWithProto)) + +TestConstructFromCall(prototype, true, + CreateFrozen(handlerWithPrototype, ReturnUndef)) +TestConstructFromCall(prototype, true, + CreateFrozen(handlerWithPrototype, ReturnThis)) +TestConstructFromCall(Object.prototype, false, + CreateFrozen(handlerWithPrototype, ReturnNew)) +TestConstructFromCall(prototype, false, + CreateFrozen(handlerWithPrototype, ReturnNewWithProto)) // Throwing from the construct trap. function TestConstructThrow(trap) { - TestConstructThrow2(Proxy.createFunction({fix: function() {return {}}}, trap)) - TestConstructThrow2(Proxy.createFunction({fix: function() {return {}}}, - function() {}, trap)) + TestConstructThrow2(Proxy.createFunction({ fix: function() {return {};} }, + trap)) + TestConstructThrow2(Proxy.createFunction({ fix: function() {return {};} }, + function() {}, + trap)) } function TestConstructThrow2(f) { @@ -384,7 +439,7 @@ var value var receiver function TestAccessorCall(getterCallTrap, setterCallTrap) { - var handler = {fix: function() { return {} }} + var handler = { fix: function() { return {} } } var pgetter = Proxy.createFunction(handler, getterCallTrap) var psetter = Proxy.createFunction(handler, setterCallTrap) diff --git a/test/mjsunit/regress/regress-1229.js b/test/mjsunit/regress/regress-1229.js index e16d278..c0dcba9 100644 --- a/test/mjsunit/regress/regress-1229.js +++ b/test/mjsunit/regress/regress-1229.js @@ -35,10 +35,10 @@ function foo(x, y, z) { assertEquals(3, z); } -var bound_arg = [1]; +var foob = foo.bind({}, 1); function f(y, z) { - return %NewObjectFromBound(foo, bound_arg); + return %NewObjectFromBound(foob); } // Check that %NewObjectFromBound looks at correct frame for inlined function. -- 2.7.4