Line data Source code
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 :
15 224 : TF_BUILTIN(FastFunctionPrototypeBind, CodeStubAssembler) {
16 112 : 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 168 : CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
25 :
26 : // Check that receiver has instance type of JS_FUNCTION_TYPE
27 112 : Node* receiver = args.GetReceiver();
28 112 : GotoIf(TaggedIsSmi(receiver), &slow);
29 :
30 112 : Node* receiver_map = LoadMap(receiver);
31 : {
32 112 : Node* instance_type = LoadMapInstanceType(receiver_map);
33 56 : GotoIfNot(
34 224 : Word32Or(InstanceTypeEqual(instance_type, JS_FUNCTION_TYPE),
35 168 : InstanceTypeEqual(instance_type, JS_BOUND_FUNCTION_TYPE)),
36 56 : &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 56 : Comment("Disallow binding of slow-mode functions");
42 112 : 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 56 : Comment("Check descriptor array length");
48 56 : TNode<DescriptorArray> descriptors = LoadMapDescriptors(receiver_map);
49 : // Minimum descriptor array length required for fast path.
50 : const int min_nof_descriptors = i::Max(JSFunction::kLengthDescriptorIndex,
51 : JSFunction::kNameDescriptorIndex);
52 56 : TNode<Int32T> nof_descriptors = LoadNumberOfDescriptors(descriptors);
53 56 : GotoIf(
54 168 : Int32LessThanOrEqual(nof_descriptors, Int32Constant(min_nof_descriptors)),
55 56 : &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 56 : Comment("Check name and length properties");
61 : {
62 : const int length_index = JSFunction::kLengthDescriptorIndex;
63 : TNode<Name> maybe_length =
64 56 : LoadKeyByDescriptorEntry(descriptors, length_index);
65 112 : GotoIf(WordNotEqual(maybe_length, LoadRoot(RootIndex::klength_string)),
66 56 : &slow);
67 :
68 : TNode<Object> maybe_length_accessor =
69 56 : LoadValueByDescriptorEntry(descriptors, length_index);
70 112 : GotoIf(TaggedIsSmi(maybe_length_accessor), &slow);
71 112 : Node* length_value_map = LoadMap(CAST(maybe_length_accessor));
72 112 : GotoIfNot(IsAccessorInfoMap(length_value_map), &slow);
73 :
74 : const int name_index = JSFunction::kNameDescriptorIndex;
75 56 : TNode<Name> maybe_name = LoadKeyByDescriptorEntry(descriptors, name_index);
76 112 : GotoIf(WordNotEqual(maybe_name, LoadRoot(RootIndex::kname_string)), &slow);
77 :
78 : TNode<Object> maybe_name_accessor =
79 56 : LoadValueByDescriptorEntry(descriptors, name_index);
80 112 : GotoIf(TaggedIsSmi(maybe_name_accessor), &slow);
81 56 : TNode<Map> name_value_map = LoadMap(CAST(maybe_name_accessor));
82 112 : GotoIfNot(IsAccessorInfoMap(name_value_map), &slow);
83 : }
84 :
85 : // Choose the right bound function map based on whether the target is
86 : // constructable.
87 56 : Comment("Choose the right bound function map");
88 112 : VARIABLE(bound_function_map, MachineRepresentation::kTagged);
89 : {
90 56 : Label with_constructor(this);
91 112 : VariableList vars({&bound_function_map}, zone());
92 112 : Node* native_context = LoadNativeContext(context);
93 :
94 56 : Label map_done(this, vars);
95 112 : GotoIf(IsConstructorMap(receiver_map), &with_constructor);
96 :
97 112 : bound_function_map.Bind(LoadContextElement(
98 112 : native_context, Context::BOUND_FUNCTION_WITHOUT_CONSTRUCTOR_MAP_INDEX));
99 56 : Goto(&map_done);
100 :
101 56 : BIND(&with_constructor);
102 112 : bound_function_map.Bind(LoadContextElement(
103 112 : native_context, Context::BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX));
104 56 : Goto(&map_done);
105 :
106 56 : BIND(&map_done);
107 : }
108 :
109 : // Verify that __proto__ matches that of a the target bound function.
110 56 : Comment("Verify that __proto__ matches target bound function");
111 112 : Node* prototype = LoadMapPrototype(receiver_map);
112 168 : Node* expected_prototype = LoadMapPrototype(bound_function_map.value());
113 112 : GotoIf(WordNotEqual(prototype, expected_prototype), &slow);
114 :
115 : // Allocate the arguments array.
116 56 : Comment("Allocate the arguments array");
117 112 : VARIABLE(argument_array, MachineRepresentation::kTagged);
118 : {
119 56 : Label empty_arguments(this);
120 56 : Label arguments_done(this, &argument_array);
121 168 : GotoIf(Uint32LessThanOrEqual(argc, Int32Constant(1)), &empty_arguments);
122 : TNode<IntPtrT> elements_length =
123 224 : Signed(ChangeUint32ToWord(Unsigned(Int32Sub(argc, Int32Constant(1)))));
124 : TNode<FixedArray> elements = CAST(AllocateFixedArray(
125 : PACKED_ELEMENTS, elements_length, kAllowLargeObjectAllocation));
126 112 : VARIABLE(index, MachineType::PointerRepresentation());
127 112 : index.Bind(IntPtrConstant(0));
128 112 : VariableList foreach_vars({&index}, zone());
129 56 : args.ForEach(foreach_vars,
130 224 : [this, elements, &index](Node* arg) {
131 112 : StoreFixedArrayElement(elements, index.value(), arg);
132 56 : Increment(&index);
133 56 : },
134 168 : IntPtrConstant(1));
135 56 : argument_array.Bind(elements);
136 56 : Goto(&arguments_done);
137 :
138 56 : BIND(&empty_arguments);
139 112 : argument_array.Bind(EmptyFixedArrayConstant());
140 56 : Goto(&arguments_done);
141 :
142 56 : BIND(&arguments_done);
143 : }
144 :
145 : // Determine bound receiver.
146 56 : Comment("Determine bound receiver");
147 112 : VARIABLE(bound_receiver, MachineRepresentation::kTagged);
148 : {
149 56 : Label has_receiver(this);
150 56 : Label receiver_done(this, &bound_receiver);
151 168 : GotoIf(Word32NotEqual(argc, Int32Constant(0)), &has_receiver);
152 112 : bound_receiver.Bind(UndefinedConstant());
153 56 : Goto(&receiver_done);
154 :
155 56 : BIND(&has_receiver);
156 112 : bound_receiver.Bind(args.AtIndex(0));
157 56 : Goto(&receiver_done);
158 :
159 56 : BIND(&receiver_done);
160 : }
161 :
162 : // Allocate the resulting bound function.
163 56 : Comment("Allocate the resulting bound function");
164 : {
165 112 : Node* bound_function = Allocate(JSBoundFunction::kSize);
166 56 : StoreMapNoWriteBarrier(bound_function, bound_function_map.value());
167 : StoreObjectFieldNoWriteBarrier(
168 56 : bound_function, JSBoundFunction::kBoundTargetFunctionOffset, receiver);
169 56 : StoreObjectFieldNoWriteBarrier(bound_function,
170 : JSBoundFunction::kBoundThisOffset,
171 56 : bound_receiver.value());
172 56 : StoreObjectFieldNoWriteBarrier(bound_function,
173 : JSBoundFunction::kBoundArgumentsOffset,
174 56 : argument_array.value());
175 112 : Node* empty_fixed_array = EmptyFixedArrayConstant();
176 : StoreObjectFieldNoWriteBarrier(
177 56 : bound_function, JSObject::kPropertiesOrHashOffset, empty_fixed_array);
178 : StoreObjectFieldNoWriteBarrier(bound_function, JSObject::kElementsOffset,
179 56 : empty_fixed_array);
180 :
181 56 : args.PopAndReturn(bound_function);
182 : }
183 :
184 56 : BIND(&slow);
185 : {
186 : // We are not using Parameter(Descriptor::kJSTarget) and loading the value
187 : // from the current frame here in order to reduce register pressure on the
188 : // fast path.
189 56 : TNode<JSFunction> target = LoadTargetFromFrame();
190 56 : TailCallBuiltin(Builtins::kFunctionPrototypeBind, context, target,
191 56 : new_target, argc);
192 : }
193 56 : }
194 :
195 : // ES6 #sec-function.prototype-@@hasinstance
196 168 : TF_BUILTIN(FunctionPrototypeHasInstance, CodeStubAssembler) {
197 : Node* context = Parameter(Descriptor::kContext);
198 : Node* f = Parameter(Descriptor::kReceiver);
199 : Node* v = Parameter(Descriptor::kV);
200 56 : Node* result = OrdinaryHasInstance(context, f, v);
201 56 : Return(result);
202 56 : }
203 :
204 : } // namespace internal
205 59456 : } // namespace v8
|