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 :
9 : namespace v8 {
10 : namespace internal {
11 :
12 172 : TF_BUILTIN(FastFunctionPrototypeBind, CodeStubAssembler) {
13 43 : Label slow(this);
14 :
15 : // TODO(ishell): use constants from Descriptor once the JSFunction linkage
16 : // arguments are reordered.
17 : Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
18 : Node* context = Parameter(BuiltinDescriptor::kContext);
19 : Node* new_target = Parameter(BuiltinDescriptor::kNewTarget);
20 :
21 43 : CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
22 :
23 : // Check that receiver has instance type of JS_FUNCTION_TYPE
24 43 : Node* receiver = args.GetReceiver();
25 43 : GotoIf(TaggedIsSmi(receiver), &slow);
26 :
27 43 : Node* receiver_map = LoadMap(receiver);
28 43 : Node* instance_type = LoadMapInstanceType(receiver_map);
29 43 : GotoIf(Word32NotEqual(instance_type, Int32Constant(JS_FUNCTION_TYPE)), &slow);
30 :
31 : // Disallow binding of slow-mode functions. We need to figure out whether the
32 : // length and name property are in the original state.
33 43 : Comment("Disallow binding of slow-mode functions");
34 43 : GotoIf(IsDictionaryMap(receiver_map), &slow);
35 :
36 : // Check whether the length and name properties are still present as
37 : // AccessorInfo objects. In that case, their value can be recomputed even if
38 : // the actual value on the object changes.
39 43 : Comment("Check descriptor array length");
40 43 : Node* descriptors = LoadMapDescriptors(receiver_map);
41 43 : Node* descriptors_length = LoadFixedArrayBaseLength(descriptors);
42 43 : GotoIf(SmiLessThanOrEqual(descriptors_length, SmiConstant(1)), &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 43 : Comment("Check name and length properties");
48 : const int length_index = JSFunction::kLengthDescriptorIndex;
49 : Node* maybe_length = LoadFixedArrayElement(
50 43 : descriptors, DescriptorArray::ToKeyIndex(length_index));
51 : GotoIf(WordNotEqual(maybe_length, LoadRoot(Heap::klength_stringRootIndex)),
52 43 : &slow);
53 :
54 : Node* maybe_length_accessor = LoadFixedArrayElement(
55 43 : descriptors, DescriptorArray::ToValueIndex(length_index));
56 43 : GotoIf(TaggedIsSmi(maybe_length_accessor), &slow);
57 43 : Node* length_value_map = LoadMap(maybe_length_accessor);
58 43 : GotoIfNot(IsAccessorInfoMap(length_value_map), &slow);
59 :
60 : const int name_index = JSFunction::kNameDescriptorIndex;
61 : Node* maybe_name = LoadFixedArrayElement(
62 43 : descriptors, DescriptorArray::ToKeyIndex(name_index));
63 : GotoIf(WordNotEqual(maybe_name, LoadRoot(Heap::kname_stringRootIndex)),
64 43 : &slow);
65 :
66 : Node* maybe_name_accessor = LoadFixedArrayElement(
67 43 : descriptors, DescriptorArray::ToValueIndex(name_index));
68 43 : GotoIf(TaggedIsSmi(maybe_name_accessor), &slow);
69 43 : Node* name_value_map = LoadMap(maybe_name_accessor);
70 43 : GotoIfNot(IsAccessorInfoMap(name_value_map), &slow);
71 :
72 : // Choose the right bound function map based on whether the target is
73 : // constructable.
74 43 : Comment("Choose the right bound function map");
75 86 : VARIABLE(bound_function_map, MachineRepresentation::kTagged);
76 43 : Label with_constructor(this);
77 43 : VariableList vars({&bound_function_map}, zone());
78 43 : Node* native_context = LoadNativeContext(context);
79 :
80 43 : Label map_done(this, vars);
81 43 : Node* bit_field = LoadMapBitField(receiver_map);
82 : int mask = static_cast<int>(1 << Map::kIsConstructor);
83 43 : GotoIf(IsSetWord32(bit_field, mask), &with_constructor);
84 :
85 : bound_function_map.Bind(LoadContextElement(
86 43 : native_context, Context::BOUND_FUNCTION_WITHOUT_CONSTRUCTOR_MAP_INDEX));
87 43 : Goto(&map_done);
88 :
89 43 : BIND(&with_constructor);
90 : bound_function_map.Bind(LoadContextElement(
91 43 : native_context, Context::BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX));
92 43 : Goto(&map_done);
93 :
94 43 : BIND(&map_done);
95 :
96 : // Verify that __proto__ matches that of a the target bound function.
97 43 : Comment("Verify that __proto__ matches target bound function");
98 43 : Node* prototype = LoadMapPrototype(receiver_map);
99 43 : Node* expected_prototype = LoadMapPrototype(bound_function_map.value());
100 43 : GotoIf(WordNotEqual(prototype, expected_prototype), &slow);
101 :
102 : // Allocate the arguments array.
103 43 : Comment("Allocate the arguments array");
104 86 : VARIABLE(argument_array, MachineRepresentation::kTagged);
105 43 : Label empty_arguments(this);
106 43 : Label arguments_done(this, &argument_array);
107 43 : GotoIf(Uint32LessThanOrEqual(argc, Int32Constant(1)), &empty_arguments);
108 43 : Node* elements_length = ChangeUint32ToWord(Int32Sub(argc, Int32Constant(1)));
109 43 : Node* elements = AllocateFixedArray(FAST_ELEMENTS, elements_length);
110 86 : VARIABLE(index, MachineType::PointerRepresentation());
111 43 : index.Bind(IntPtrConstant(0));
112 43 : VariableList foreach_vars({&index}, zone());
113 : args.ForEach(foreach_vars,
114 43 : [this, elements, &index](Node* arg) {
115 43 : StoreFixedArrayElement(elements, index.value(), arg);
116 43 : Increment(index);
117 43 : },
118 129 : IntPtrConstant(1));
119 43 : argument_array.Bind(elements);
120 43 : Goto(&arguments_done);
121 :
122 43 : BIND(&empty_arguments);
123 43 : argument_array.Bind(EmptyFixedArrayConstant());
124 43 : Goto(&arguments_done);
125 :
126 43 : BIND(&arguments_done);
127 :
128 : // Determine bound receiver.
129 43 : Comment("Determine bound receiver");
130 86 : VARIABLE(bound_receiver, MachineRepresentation::kTagged);
131 43 : Label has_receiver(this);
132 43 : Label receiver_done(this, &bound_receiver);
133 43 : GotoIf(Word32NotEqual(argc, Int32Constant(0)), &has_receiver);
134 43 : bound_receiver.Bind(UndefinedConstant());
135 43 : Goto(&receiver_done);
136 :
137 43 : BIND(&has_receiver);
138 43 : bound_receiver.Bind(args.AtIndex(0));
139 43 : Goto(&receiver_done);
140 :
141 43 : BIND(&receiver_done);
142 :
143 : // Allocate the resulting bound function.
144 43 : Comment("Allocate the resulting bound function");
145 43 : Node* bound_function = Allocate(JSBoundFunction::kSize);
146 43 : StoreMapNoWriteBarrier(bound_function, bound_function_map.value());
147 : StoreObjectFieldNoWriteBarrier(
148 43 : bound_function, JSBoundFunction::kBoundTargetFunctionOffset, receiver);
149 : StoreObjectFieldNoWriteBarrier(bound_function,
150 : JSBoundFunction::kBoundThisOffset,
151 43 : bound_receiver.value());
152 : StoreObjectFieldNoWriteBarrier(bound_function,
153 : JSBoundFunction::kBoundArgumentsOffset,
154 43 : argument_array.value());
155 43 : Node* empty_fixed_array = EmptyFixedArrayConstant();
156 : StoreObjectFieldNoWriteBarrier(bound_function, JSObject::kPropertiesOffset,
157 43 : empty_fixed_array);
158 : StoreObjectFieldNoWriteBarrier(bound_function, JSObject::kElementsOffset,
159 43 : empty_fixed_array);
160 :
161 43 : args.PopAndReturn(bound_function);
162 43 : BIND(&slow);
163 :
164 : Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset,
165 43 : MachineType::TaggedPointer());
166 : TailCallStub(CodeFactory::FunctionPrototypeBind(isolate()), context, target,
167 129 : new_target, argc);
168 43 : }
169 :
170 : // ES6 #sec-function.prototype-@@hasinstance
171 129 : TF_BUILTIN(FunctionPrototypeHasInstance, CodeStubAssembler) {
172 : Node* context = Parameter(Descriptor::kContext);
173 : Node* f = Parameter(Descriptor::kReceiver);
174 : Node* v = Parameter(Descriptor::kV);
175 43 : Node* result = OrdinaryHasInstance(context, f, v);
176 43 : Return(result);
177 43 : }
178 :
179 : } // namespace internal
180 : } // namespace v8
|