• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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