|  | // 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" | 
|  | #include "src/objects/api-callbacks.h" | 
|  | #include "src/objects/descriptor-array.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(Descriptor::kJSActualArgumentsCount); | 
|  | Node* context = Parameter(Descriptor::kContext); | 
|  | Node* new_target = Parameter(Descriptor::kJSNewTarget); | 
|  |  | 
|  | 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"); | 
|  | TNode<DescriptorArray> descriptors = LoadMapDescriptors(receiver_map); | 
|  | // Minimum descriptor array length required for fast path. | 
|  | const int min_descriptors_length = DescriptorArray::LengthFor(Max( | 
|  | JSFunction::kLengthDescriptorIndex, JSFunction::kNameDescriptorIndex)); | 
|  | TNode<Smi> descriptors_length = LoadWeakFixedArrayLength(descriptors); | 
|  | GotoIf(SmiLessThanOrEqual(descriptors_length, | 
|  | SmiConstant(min_descriptors_length)), | 
|  | &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; | 
|  | TNode<Name> maybe_length = CAST(LoadWeakFixedArrayElement( | 
|  | descriptors, DescriptorArray::ToKeyIndex(length_index))); | 
|  | GotoIf(WordNotEqual(maybe_length, LoadRoot(Heap::klength_stringRootIndex)), | 
|  | &slow); | 
|  |  | 
|  | TNode<Object> maybe_length_accessor = CAST(LoadWeakFixedArrayElement( | 
|  | descriptors, DescriptorArray::ToValueIndex(length_index))); | 
|  | GotoIf(TaggedIsSmi(maybe_length_accessor), &slow); | 
|  | Node* length_value_map = LoadMap(CAST(maybe_length_accessor)); | 
|  | GotoIfNot(IsAccessorInfoMap(length_value_map), &slow); | 
|  |  | 
|  | const int name_index = JSFunction::kNameDescriptorIndex; | 
|  | TNode<Name> maybe_name = CAST(LoadWeakFixedArrayElement( | 
|  | descriptors, DescriptorArray::ToKeyIndex(name_index))); | 
|  | GotoIf(WordNotEqual(maybe_name, LoadRoot(Heap::kname_stringRootIndex)), | 
|  | &slow); | 
|  |  | 
|  | TNode<Object> maybe_name_accessor = CAST(LoadWeakFixedArrayElement( | 
|  | descriptors, DescriptorArray::ToValueIndex(name_index))); | 
|  | GotoIf(TaggedIsSmi(maybe_name_accessor), &slow); | 
|  | TNode<Map> name_value_map = LoadMap(CAST(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); | 
|  | TNode<IntPtrT> elements_length = | 
|  | Signed(ChangeUint32ToWord(Unsigned(Int32Sub(argc, Int32Constant(1))))); | 
|  | Node* elements = AllocateFixedArray(PACKED_ELEMENTS, elements_length, | 
|  | 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); | 
|  | { | 
|  | // We are not using Parameter(Descriptor::kJSTarget) and loading the value | 
|  | // from the current frame here in order to reduce register pressure on the | 
|  | // fast path. | 
|  | TNode<JSFunction> target = LoadTargetFromFrame(); | 
|  | TailCallBuiltin(Builtins::kFunctionPrototypeBind, 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 |