}
// 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);
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);
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));
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
pretenure);
result->set_context(*context);
- int number_of_literals = function_info->num_literals();
- Handle<FixedArray> 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<FixedArray> 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() &&
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());
}
}
// 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);
}
+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<byte>(1 << byte_bit_index));
+}
+
+
+
void MacroAssembler::NegativeZeroTest(Register result,
Register op,
Label* then_label) {
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);
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));
// ---------------------------------------------------------------------------
// 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);
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.
# 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)));
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,
}
+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();
}
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;
// 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
// 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();
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);
};
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)");
}
-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);
}
-// 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);
}
-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;
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 {
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() == 4);
+ CONVERT_ARG_CHECKED(JSFunction, bound_function, 0);
+ RUNTIME_ASSERT(args[3]->IsNumber());
+ 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).
+ 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));
+ }
+ } 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<String> length_symbol = isolate->factory()->length_symbol();
+ Handle<Object> new_length(args.at<Object>(3));
+ PropertyAttributes attr =
+ static_cast<PropertyAttributes>(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<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;
}
/* 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) \
/* 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) \
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)) {
// 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
// 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;
}
__ bind(&miss);
}
- __ TryGetFunctionPrototype(rdx, rbx, &slow);
+ __ TryGetFunctionPrototype(rdx, rbx, &slow, true);
// Check that the function prototype is a JS object.
__ JumpIfSmi(rbx, &slow);
}
+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);
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);
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),
void Push(Smi* smi);
void Test(const Operand& dst, Smi* source);
+
// ---------------------------------------------------------------------------
// String macros.
// 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<Object> source);
void Move(const Operand& dst, Handle<Object> source);
// 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.
// 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.
// 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.
// 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());
}
+// 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).
}
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
}))
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) {
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))
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))
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) {
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)
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.