1 // Copyright 2017 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/builtins/builtins-utils-gen.h"
6 #include "src/builtins/builtins.h"
7 #include "src/code-stub-assembler.h"
8 #include "src/frame-constants.h"
9 #include "src/objects/api-callbacks.h"
10 #include "src/objects/descriptor-array.h"
11
12 namespace v8 {
13 namespace internal {
14
TF_BUILTIN(FastFunctionPrototypeBind,CodeStubAssembler)15 TF_BUILTIN(FastFunctionPrototypeBind, CodeStubAssembler) {
16 Label slow(this);
17
18 // TODO(ishell): use constants from Descriptor once the JSFunction linkage
19 // arguments are reordered.
20 Node* argc = Parameter(Descriptor::kJSActualArgumentsCount);
21 Node* context = Parameter(Descriptor::kContext);
22 Node* new_target = Parameter(Descriptor::kJSNewTarget);
23
24 CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
25
26 // Check that receiver has instance type of JS_FUNCTION_TYPE
27 Node* receiver = args.GetReceiver();
28 GotoIf(TaggedIsSmi(receiver), &slow);
29
30 Node* receiver_map = LoadMap(receiver);
31 {
32 Node* instance_type = LoadMapInstanceType(receiver_map);
33 GotoIfNot(
34 Word32Or(InstanceTypeEqual(instance_type, JS_FUNCTION_TYPE),
35 InstanceTypeEqual(instance_type, JS_BOUND_FUNCTION_TYPE)),
36 &slow);
37 }
38
39 // Disallow binding of slow-mode functions. We need to figure out whether the
40 // length and name property are in the original state.
41 Comment("Disallow binding of slow-mode functions");
42 GotoIf(IsDictionaryMap(receiver_map), &slow);
43
44 // Check whether the length and name properties are still present as
45 // AccessorInfo objects. In that case, their value can be recomputed even if
46 // the actual value on the object changes.
47 Comment("Check descriptor array length");
48 TNode<DescriptorArray> descriptors = LoadMapDescriptors(receiver_map);
49 // Minimum descriptor array length required for fast path.
50 const int min_descriptors_length = DescriptorArray::LengthFor(Max(
51 JSFunction::kLengthDescriptorIndex, JSFunction::kNameDescriptorIndex));
52 TNode<Smi> descriptors_length = LoadWeakFixedArrayLength(descriptors);
53 GotoIf(SmiLessThanOrEqual(descriptors_length,
54 SmiConstant(min_descriptors_length)),
55 &slow);
56
57 // Check whether the length and name properties are still present as
58 // AccessorInfo objects. In that case, their value can be recomputed even if
59 // the actual value on the object changes.
60 Comment("Check name and length properties");
61 {
62 const int length_index = JSFunction::kLengthDescriptorIndex;
63 TNode<Name> maybe_length = CAST(LoadWeakFixedArrayElement(
64 descriptors, DescriptorArray::ToKeyIndex(length_index)));
65 GotoIf(WordNotEqual(maybe_length, LoadRoot(Heap::klength_stringRootIndex)),
66 &slow);
67
68 TNode<Object> maybe_length_accessor = CAST(LoadWeakFixedArrayElement(
69 descriptors, DescriptorArray::ToValueIndex(length_index)));
70 GotoIf(TaggedIsSmi(maybe_length_accessor), &slow);
71 Node* length_value_map = LoadMap(CAST(maybe_length_accessor));
72 GotoIfNot(IsAccessorInfoMap(length_value_map), &slow);
73
74 const int name_index = JSFunction::kNameDescriptorIndex;
75 TNode<Name> maybe_name = CAST(LoadWeakFixedArrayElement(
76 descriptors, DescriptorArray::ToKeyIndex(name_index)));
77 GotoIf(WordNotEqual(maybe_name, LoadRoot(Heap::kname_stringRootIndex)),
78 &slow);
79
80 TNode<Object> maybe_name_accessor = CAST(LoadWeakFixedArrayElement(
81 descriptors, DescriptorArray::ToValueIndex(name_index)));
82 GotoIf(TaggedIsSmi(maybe_name_accessor), &slow);
83 TNode<Map> name_value_map = LoadMap(CAST(maybe_name_accessor));
84 GotoIfNot(IsAccessorInfoMap(name_value_map), &slow);
85 }
86
87 // Choose the right bound function map based on whether the target is
88 // constructable.
89 Comment("Choose the right bound function map");
90 VARIABLE(bound_function_map, MachineRepresentation::kTagged);
91 {
92 Label with_constructor(this);
93 VariableList vars({&bound_function_map}, zone());
94 Node* native_context = LoadNativeContext(context);
95
96 Label map_done(this, vars);
97 GotoIf(IsConstructorMap(receiver_map), &with_constructor);
98
99 bound_function_map.Bind(LoadContextElement(
100 native_context, Context::BOUND_FUNCTION_WITHOUT_CONSTRUCTOR_MAP_INDEX));
101 Goto(&map_done);
102
103 BIND(&with_constructor);
104 bound_function_map.Bind(LoadContextElement(
105 native_context, Context::BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX));
106 Goto(&map_done);
107
108 BIND(&map_done);
109 }
110
111 // Verify that __proto__ matches that of a the target bound function.
112 Comment("Verify that __proto__ matches target bound function");
113 Node* prototype = LoadMapPrototype(receiver_map);
114 Node* expected_prototype = LoadMapPrototype(bound_function_map.value());
115 GotoIf(WordNotEqual(prototype, expected_prototype), &slow);
116
117 // Allocate the arguments array.
118 Comment("Allocate the arguments array");
119 VARIABLE(argument_array, MachineRepresentation::kTagged);
120 {
121 Label empty_arguments(this);
122 Label arguments_done(this, &argument_array);
123 GotoIf(Uint32LessThanOrEqual(argc, Int32Constant(1)), &empty_arguments);
124 TNode<IntPtrT> elements_length =
125 Signed(ChangeUint32ToWord(Unsigned(Int32Sub(argc, Int32Constant(1)))));
126 TNode<FixedArray> elements = CAST(AllocateFixedArray(
127 PACKED_ELEMENTS, elements_length, kAllowLargeObjectAllocation));
128 VARIABLE(index, MachineType::PointerRepresentation());
129 index.Bind(IntPtrConstant(0));
130 VariableList foreach_vars({&index}, zone());
131 args.ForEach(foreach_vars,
132 [this, elements, &index](Node* arg) {
133 StoreFixedArrayElement(elements, index.value(), arg);
134 Increment(&index);
135 },
136 IntPtrConstant(1));
137 argument_array.Bind(elements);
138 Goto(&arguments_done);
139
140 BIND(&empty_arguments);
141 argument_array.Bind(EmptyFixedArrayConstant());
142 Goto(&arguments_done);
143
144 BIND(&arguments_done);
145 }
146
147 // Determine bound receiver.
148 Comment("Determine bound receiver");
149 VARIABLE(bound_receiver, MachineRepresentation::kTagged);
150 {
151 Label has_receiver(this);
152 Label receiver_done(this, &bound_receiver);
153 GotoIf(Word32NotEqual(argc, Int32Constant(0)), &has_receiver);
154 bound_receiver.Bind(UndefinedConstant());
155 Goto(&receiver_done);
156
157 BIND(&has_receiver);
158 bound_receiver.Bind(args.AtIndex(0));
159 Goto(&receiver_done);
160
161 BIND(&receiver_done);
162 }
163
164 // Allocate the resulting bound function.
165 Comment("Allocate the resulting bound function");
166 {
167 Node* bound_function = Allocate(JSBoundFunction::kSize);
168 StoreMapNoWriteBarrier(bound_function, bound_function_map.value());
169 StoreObjectFieldNoWriteBarrier(
170 bound_function, JSBoundFunction::kBoundTargetFunctionOffset, receiver);
171 StoreObjectFieldNoWriteBarrier(bound_function,
172 JSBoundFunction::kBoundThisOffset,
173 bound_receiver.value());
174 StoreObjectFieldNoWriteBarrier(bound_function,
175 JSBoundFunction::kBoundArgumentsOffset,
176 argument_array.value());
177 Node* empty_fixed_array = EmptyFixedArrayConstant();
178 StoreObjectFieldNoWriteBarrier(
179 bound_function, JSObject::kPropertiesOrHashOffset, empty_fixed_array);
180 StoreObjectFieldNoWriteBarrier(bound_function, JSObject::kElementsOffset,
181 empty_fixed_array);
182
183 args.PopAndReturn(bound_function);
184 }
185
186 BIND(&slow);
187 {
188 // We are not using Parameter(Descriptor::kJSTarget) and loading the value
189 // from the current frame here in order to reduce register pressure on the
190 // fast path.
191 TNode<JSFunction> target = LoadTargetFromFrame();
192 TailCallBuiltin(Builtins::kFunctionPrototypeBind, context, target,
193 new_target, argc);
194 }
195 }
196
197 // ES6 #sec-function.prototype-@@hasinstance
TF_BUILTIN(FunctionPrototypeHasInstance,CodeStubAssembler)198 TF_BUILTIN(FunctionPrototypeHasInstance, CodeStubAssembler) {
199 Node* context = Parameter(Descriptor::kContext);
200 Node* f = Parameter(Descriptor::kReceiver);
201 Node* v = Parameter(Descriptor::kV);
202 Node* result = OrdinaryHasInstance(context, f, v);
203 Return(result);
204 }
205
206 } // namespace internal
207 } // namespace v8
208