blob: 7c1db5093db7fd96e7b1bcdd3e48972cf6e4236b [file] [log] [blame]
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
#include "src/code-stub-assembler.h"
#include "src/frame-constants.h"
namespace v8 {
namespace internal {
TF_BUILTIN(FastFunctionPrototypeBind, CodeStubAssembler) {
Label slow(this);
// TODO(ishell): use constants from Descriptor once the JSFunction linkage
// arguments are reordered.
Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
Node* context = Parameter(BuiltinDescriptor::kContext);
Node* new_target = Parameter(BuiltinDescriptor::kNewTarget);
CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
// Check that receiver has instance type of JS_FUNCTION_TYPE
Node* receiver = args.GetReceiver();
GotoIf(TaggedIsSmi(receiver), &slow);
Node* receiver_map = LoadMap(receiver);
{
Node* instance_type = LoadMapInstanceType(receiver_map);
GotoIfNot(
Word32Or(InstanceTypeEqual(instance_type, JS_FUNCTION_TYPE),
InstanceTypeEqual(instance_type, JS_BOUND_FUNCTION_TYPE)),
&slow);
}
// Disallow binding of slow-mode functions. We need to figure out whether the
// length and name property are in the original state.
Comment("Disallow binding of slow-mode functions");
GotoIf(IsDictionaryMap(receiver_map), &slow);
// Check whether the length and name properties are still present as
// AccessorInfo objects. In that case, their value can be recomputed even if
// the actual value on the object changes.
Comment("Check descriptor array length");
Node* descriptors = LoadMapDescriptors(receiver_map);
Node* descriptors_length = LoadFixedArrayBaseLength(descriptors);
GotoIf(SmiLessThanOrEqual(descriptors_length, SmiConstant(1)), &slow);
// Check whether the length and name properties are still present as
// AccessorInfo objects. In that case, their value can be recomputed even if
// the actual value on the object changes.
Comment("Check name and length properties");
{
const int length_index = JSFunction::kLengthDescriptorIndex;
Node* maybe_length = LoadFixedArrayElement(
descriptors, DescriptorArray::ToKeyIndex(length_index));
GotoIf(WordNotEqual(maybe_length, LoadRoot(Heap::klength_stringRootIndex)),
&slow);
Node* maybe_length_accessor = LoadFixedArrayElement(
descriptors, DescriptorArray::ToValueIndex(length_index));
GotoIf(TaggedIsSmi(maybe_length_accessor), &slow);
Node* length_value_map = LoadMap(maybe_length_accessor);
GotoIfNot(IsAccessorInfoMap(length_value_map), &slow);
const int name_index = JSFunction::kNameDescriptorIndex;
Node* maybe_name = LoadFixedArrayElement(
descriptors, DescriptorArray::ToKeyIndex(name_index));
GotoIf(WordNotEqual(maybe_name, LoadRoot(Heap::kname_stringRootIndex)),
&slow);
Node* maybe_name_accessor = LoadFixedArrayElement(
descriptors, DescriptorArray::ToValueIndex(name_index));
GotoIf(TaggedIsSmi(maybe_name_accessor), &slow);
Node* name_value_map = LoadMap(maybe_name_accessor);
GotoIfNot(IsAccessorInfoMap(name_value_map), &slow);
}
// Choose the right bound function map based on whether the target is
// constructable.
Comment("Choose the right bound function map");
VARIABLE(bound_function_map, MachineRepresentation::kTagged);
{
Label with_constructor(this);
VariableList vars({&bound_function_map}, zone());
Node* native_context = LoadNativeContext(context);
Label map_done(this, vars);
GotoIf(IsConstructorMap(receiver_map), &with_constructor);
bound_function_map.Bind(LoadContextElement(
native_context, Context::BOUND_FUNCTION_WITHOUT_CONSTRUCTOR_MAP_INDEX));
Goto(&map_done);
BIND(&with_constructor);
bound_function_map.Bind(LoadContextElement(
native_context, Context::BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX));
Goto(&map_done);
BIND(&map_done);
}
// Verify that __proto__ matches that of a the target bound function.
Comment("Verify that __proto__ matches target bound function");
Node* prototype = LoadMapPrototype(receiver_map);
Node* expected_prototype = LoadMapPrototype(bound_function_map.value());
GotoIf(WordNotEqual(prototype, expected_prototype), &slow);
// Allocate the arguments array.
Comment("Allocate the arguments array");
VARIABLE(argument_array, MachineRepresentation::kTagged);
{
Label empty_arguments(this);
Label arguments_done(this, &argument_array);
GotoIf(Uint32LessThanOrEqual(argc, Int32Constant(1)), &empty_arguments);
Node* elements_length =
ChangeUint32ToWord(Unsigned(Int32Sub(argc, Int32Constant(1))));
Node* elements =
AllocateFixedArray(PACKED_ELEMENTS, elements_length, INTPTR_PARAMETERS,
kAllowLargeObjectAllocation);
VARIABLE(index, MachineType::PointerRepresentation());
index.Bind(IntPtrConstant(0));
VariableList foreach_vars({&index}, zone());
args.ForEach(foreach_vars,
[this, elements, &index](Node* arg) {
StoreFixedArrayElement(elements, index.value(), arg);
Increment(&index);
},
IntPtrConstant(1));
argument_array.Bind(elements);
Goto(&arguments_done);
BIND(&empty_arguments);
argument_array.Bind(EmptyFixedArrayConstant());
Goto(&arguments_done);
BIND(&arguments_done);
}
// Determine bound receiver.
Comment("Determine bound receiver");
VARIABLE(bound_receiver, MachineRepresentation::kTagged);
{
Label has_receiver(this);
Label receiver_done(this, &bound_receiver);
GotoIf(Word32NotEqual(argc, Int32Constant(0)), &has_receiver);
bound_receiver.Bind(UndefinedConstant());
Goto(&receiver_done);
BIND(&has_receiver);
bound_receiver.Bind(args.AtIndex(0));
Goto(&receiver_done);
BIND(&receiver_done);
}
// Allocate the resulting bound function.
Comment("Allocate the resulting bound function");
{
Node* bound_function = Allocate(JSBoundFunction::kSize);
StoreMapNoWriteBarrier(bound_function, bound_function_map.value());
StoreObjectFieldNoWriteBarrier(
bound_function, JSBoundFunction::kBoundTargetFunctionOffset, receiver);
StoreObjectFieldNoWriteBarrier(bound_function,
JSBoundFunction::kBoundThisOffset,
bound_receiver.value());
StoreObjectFieldNoWriteBarrier(bound_function,
JSBoundFunction::kBoundArgumentsOffset,
argument_array.value());
Node* empty_fixed_array = EmptyFixedArrayConstant();
StoreObjectFieldNoWriteBarrier(
bound_function, JSObject::kPropertiesOrHashOffset, empty_fixed_array);
StoreObjectFieldNoWriteBarrier(bound_function, JSObject::kElementsOffset,
empty_fixed_array);
args.PopAndReturn(bound_function);
}
BIND(&slow);
Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset,
MachineType::TaggedPointer());
TailCallStub(CodeFactory::FunctionPrototypeBind(isolate()), context, target,
new_target, argc);
}
// ES6 #sec-function.prototype-@@hasinstance
TF_BUILTIN(FunctionPrototypeHasInstance, CodeStubAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* f = Parameter(Descriptor::kReceiver);
Node* v = Parameter(Descriptor::kV);
Node* result = OrdinaryHasInstance(context, f, v);
Return(result);
}
} // namespace internal
} // namespace v8