Line data Source code
1 : // Copyright 2016 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-factory.h"
8 : #include "src/code-stub-assembler.h"
9 : #include "src/objects-inl.h"
10 :
11 : namespace v8 {
12 : namespace internal {
13 :
14 : class ConversionBuiltinsAssembler : public CodeStubAssembler {
15 : public:
16 : explicit ConversionBuiltinsAssembler(compiler::CodeAssemblerState* state)
17 155 : : CodeStubAssembler(state) {}
18 :
19 : protected:
20 : void Generate_NonPrimitiveToPrimitive(Node* context, Node* input,
21 : ToPrimitiveHint hint);
22 :
23 : void Generate_OrdinaryToPrimitive(Node* context, Node* input,
24 : OrdinaryToPrimitiveHint hint);
25 : };
26 :
27 : // ES6 section 7.1.1 ToPrimitive ( input [ , PreferredType ] )
28 93 : void ConversionBuiltinsAssembler::Generate_NonPrimitiveToPrimitive(
29 : Node* context, Node* input, ToPrimitiveHint hint) {
30 : // Lookup the @@toPrimitive property on the {input}.
31 : Node* exotic_to_prim =
32 186 : GetProperty(context, input, factory()->to_primitive_symbol());
33 :
34 : // Check if {exotic_to_prim} is neither null nor undefined.
35 : Label ordinary_to_primitive(this);
36 186 : GotoIf(WordEqual(exotic_to_prim, NullConstant()), &ordinary_to_primitive);
37 186 : GotoIf(WordEqual(exotic_to_prim, UndefinedConstant()),
38 93 : &ordinary_to_primitive);
39 : {
40 : // Invoke the {exotic_to_prim} method on the {input} with a string
41 : // representation of the {hint}.
42 : Callable callable =
43 93 : CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined);
44 93 : Node* hint_string = HeapConstant(factory()->ToPrimitiveHintString(hint));
45 : Node* result =
46 93 : CallJS(callable, context, exotic_to_prim, input, hint_string);
47 :
48 : // Verify that the {result} is actually a primitive.
49 93 : Label if_resultisprimitive(this),
50 93 : if_resultisnotprimitive(this, Label::kDeferred);
51 186 : GotoIf(TaggedIsSmi(result), &if_resultisprimitive);
52 186 : Node* result_instance_type = LoadInstanceType(result);
53 : STATIC_ASSERT(FIRST_PRIMITIVE_TYPE == FIRST_TYPE);
54 : Branch(Int32LessThanOrEqual(result_instance_type,
55 186 : Int32Constant(LAST_PRIMITIVE_TYPE)),
56 186 : &if_resultisprimitive, &if_resultisnotprimitive);
57 :
58 93 : BIND(&if_resultisprimitive);
59 : {
60 : // Just return the {result}.
61 93 : Return(result);
62 : }
63 :
64 93 : BIND(&if_resultisnotprimitive);
65 : {
66 : // Somehow the @@toPrimitive method on {input} didn't yield a primitive.
67 : TailCallRuntime(Runtime::kThrowCannotConvertToPrimitive, context);
68 : }
69 : }
70 :
71 : // Convert using the OrdinaryToPrimitive algorithm instead.
72 93 : BIND(&ordinary_to_primitive);
73 : {
74 : Callable callable = CodeFactory::OrdinaryToPrimitive(
75 : isolate(), (hint == ToPrimitiveHint::kString)
76 : ? OrdinaryToPrimitiveHint::kString
77 93 : : OrdinaryToPrimitiveHint::kNumber);
78 93 : TailCallStub(callable, context, input);
79 93 : }
80 93 : }
81 :
82 124 : TF_BUILTIN(NonPrimitiveToPrimitive_Default, ConversionBuiltinsAssembler) {
83 : Node* context = Parameter(Descriptor::kContext);
84 : Node* input = Parameter(Descriptor::kArgument);
85 :
86 31 : Generate_NonPrimitiveToPrimitive(context, input, ToPrimitiveHint::kDefault);
87 31 : }
88 :
89 124 : TF_BUILTIN(NonPrimitiveToPrimitive_Number, ConversionBuiltinsAssembler) {
90 : Node* context = Parameter(Descriptor::kContext);
91 : Node* input = Parameter(Descriptor::kArgument);
92 :
93 31 : Generate_NonPrimitiveToPrimitive(context, input, ToPrimitiveHint::kNumber);
94 31 : }
95 :
96 124 : TF_BUILTIN(NonPrimitiveToPrimitive_String, ConversionBuiltinsAssembler) {
97 : Node* context = Parameter(Descriptor::kContext);
98 : Node* input = Parameter(Descriptor::kArgument);
99 :
100 31 : Generate_NonPrimitiveToPrimitive(context, input, ToPrimitiveHint::kString);
101 31 : }
102 :
103 93 : TF_BUILTIN(StringToNumber, CodeStubAssembler) {
104 : Node* context = Parameter(Descriptor::kContext);
105 : Node* input = Parameter(Descriptor::kArgument);
106 :
107 62 : Return(StringToNumber(context, input));
108 31 : }
109 :
110 93 : TF_BUILTIN(ToName, CodeStubAssembler) {
111 : Node* context = Parameter(Descriptor::kContext);
112 : Node* input = Parameter(Descriptor::kArgument);
113 :
114 62 : Return(ToName(context, input));
115 31 : }
116 :
117 93 : TF_BUILTIN(NonNumberToNumber, CodeStubAssembler) {
118 : Node* context = Parameter(Descriptor::kContext);
119 : Node* input = Parameter(Descriptor::kArgument);
120 :
121 62 : Return(NonNumberToNumber(context, input));
122 31 : }
123 :
124 93 : TF_BUILTIN(NonNumberToNumeric, CodeStubAssembler) {
125 : Node* context = Parameter(Descriptor::kContext);
126 : Node* input = Parameter(Descriptor::kArgument);
127 :
128 62 : Return(NonNumberToNumeric(context, input));
129 31 : }
130 :
131 : // ES6 section 7.1.3 ToNumber ( argument )
132 93 : TF_BUILTIN(ToNumber, CodeStubAssembler) {
133 : Node* context = Parameter(Descriptor::kContext);
134 : Node* input = Parameter(Descriptor::kArgument);
135 :
136 62 : Return(ToNumber(context, input));
137 31 : }
138 :
139 93 : TF_BUILTIN(ToString, CodeStubAssembler) {
140 : Node* context = Parameter(Descriptor::kContext);
141 : Node* input = Parameter(Descriptor::kArgument);
142 :
143 62 : Return(ToString(context, input));
144 31 : }
145 :
146 : // 7.1.1.1 OrdinaryToPrimitive ( O, hint )
147 62 : void ConversionBuiltinsAssembler::Generate_OrdinaryToPrimitive(
148 : Node* context, Node* input, OrdinaryToPrimitiveHint hint) {
149 62 : VARIABLE(var_result, MachineRepresentation::kTagged);
150 62 : Label return_result(this, &var_result);
151 :
152 186 : Handle<String> method_names[2];
153 62 : switch (hint) {
154 : case OrdinaryToPrimitiveHint::kNumber:
155 62 : method_names[0] = factory()->valueOf_string();
156 62 : method_names[1] = factory()->toString_string();
157 31 : break;
158 : case OrdinaryToPrimitiveHint::kString:
159 62 : method_names[0] = factory()->toString_string();
160 62 : method_names[1] = factory()->valueOf_string();
161 31 : break;
162 : }
163 248 : for (Handle<String> name : method_names) {
164 : // Lookup the {name} on the {input}.
165 124 : Node* method = GetProperty(context, input, name);
166 :
167 : // Check if the {method} is callable.
168 : Label if_methodiscallable(this),
169 124 : if_methodisnotcallable(this, Label::kDeferred);
170 248 : GotoIf(TaggedIsSmi(method), &if_methodisnotcallable);
171 248 : Node* method_map = LoadMap(method);
172 : Branch(IsCallableMap(method_map), &if_methodiscallable,
173 248 : &if_methodisnotcallable);
174 :
175 124 : BIND(&if_methodiscallable);
176 : {
177 : // Call the {method} on the {input}.
178 : Callable callable = CodeFactory::Call(
179 124 : isolate(), ConvertReceiverMode::kNotNullOrUndefined);
180 124 : Node* result = CallJS(callable, context, method, input);
181 124 : var_result.Bind(result);
182 :
183 : // Return the {result} if it is a primitive.
184 248 : GotoIf(TaggedIsSmi(result), &return_result);
185 248 : Node* result_instance_type = LoadInstanceType(result);
186 : STATIC_ASSERT(FIRST_PRIMITIVE_TYPE == FIRST_TYPE);
187 : GotoIf(Int32LessThanOrEqual(result_instance_type,
188 248 : Int32Constant(LAST_PRIMITIVE_TYPE)),
189 248 : &return_result);
190 : }
191 :
192 : // Just continue with the next {name} if the {method} is not callable.
193 124 : Goto(&if_methodisnotcallable);
194 124 : BIND(&if_methodisnotcallable);
195 124 : }
196 :
197 : TailCallRuntime(Runtime::kThrowCannotConvertToPrimitive, context);
198 :
199 62 : BIND(&return_result);
200 186 : Return(var_result.value());
201 62 : }
202 :
203 124 : TF_BUILTIN(OrdinaryToPrimitive_Number, ConversionBuiltinsAssembler) {
204 : Node* context = Parameter(Descriptor::kContext);
205 : Node* input = Parameter(Descriptor::kArgument);
206 : Generate_OrdinaryToPrimitive(context, input,
207 31 : OrdinaryToPrimitiveHint::kNumber);
208 31 : }
209 :
210 124 : TF_BUILTIN(OrdinaryToPrimitive_String, ConversionBuiltinsAssembler) {
211 : Node* context = Parameter(Descriptor::kContext);
212 : Node* input = Parameter(Descriptor::kArgument);
213 : Generate_OrdinaryToPrimitive(context, input,
214 31 : OrdinaryToPrimitiveHint::kString);
215 31 : }
216 :
217 : // ES6 section 7.1.2 ToBoolean ( argument )
218 93 : TF_BUILTIN(ToBoolean, CodeStubAssembler) {
219 : Node* value = Parameter(Descriptor::kArgument);
220 :
221 31 : Label return_true(this), return_false(this);
222 31 : BranchIfToBooleanIsTrue(value, &return_true, &return_false);
223 :
224 31 : BIND(&return_true);
225 62 : Return(BooleanConstant(true));
226 :
227 31 : BIND(&return_false);
228 93 : Return(BooleanConstant(false));
229 31 : }
230 :
231 : // ES6 section 7.1.2 ToBoolean ( argument )
232 : // Requires parameter on stack so that it can be used as a continuation from a
233 : // LAZY deopt.
234 93 : TF_BUILTIN(ToBooleanLazyDeoptContinuation, CodeStubAssembler) {
235 : Node* value = Parameter(Descriptor::kArgument);
236 :
237 31 : Label return_true(this), return_false(this);
238 31 : BranchIfToBooleanIsTrue(value, &return_true, &return_false);
239 :
240 31 : BIND(&return_true);
241 62 : Return(BooleanConstant(true));
242 :
243 31 : BIND(&return_false);
244 93 : Return(BooleanConstant(false));
245 31 : }
246 :
247 93 : TF_BUILTIN(ToLength, CodeStubAssembler) {
248 : Node* context = Parameter(Descriptor::kContext);
249 :
250 : // We might need to loop once for ToNumber conversion.
251 31 : VARIABLE(var_len, MachineRepresentation::kTagged,
252 : Parameter(Descriptor::kArgument));
253 31 : Label loop(this, &var_len);
254 31 : Goto(&loop);
255 31 : BIND(&loop);
256 : {
257 : // Shared entry points.
258 31 : Label return_len(this), return_two53minus1(this, Label::kDeferred),
259 31 : return_zero(this, Label::kDeferred);
260 :
261 : // Load the current {len} value.
262 31 : Node* len = var_len.value();
263 :
264 : // Check if {len} is a positive Smi.
265 62 : GotoIf(TaggedIsPositiveSmi(len), &return_len);
266 :
267 : // Check if {len} is a (negative) Smi.
268 62 : GotoIf(TaggedIsSmi(len), &return_zero);
269 :
270 : // Check if {len} is a HeapNumber.
271 31 : Label if_lenisheapnumber(this),
272 31 : if_lenisnotheapnumber(this, Label::kDeferred);
273 62 : Branch(IsHeapNumber(len), &if_lenisheapnumber, &if_lenisnotheapnumber);
274 :
275 31 : BIND(&if_lenisheapnumber);
276 : {
277 : // Load the floating-point value of {len}.
278 62 : Node* len_value = LoadHeapNumberValue(len);
279 :
280 : // Check if {len} is not greater than zero.
281 62 : GotoIfNot(Float64GreaterThan(len_value, Float64Constant(0.0)),
282 62 : &return_zero);
283 :
284 : // Check if {len} is greater than or equal to 2^53-1.
285 : GotoIf(Float64GreaterThanOrEqual(len_value,
286 62 : Float64Constant(kMaxSafeInteger)),
287 62 : &return_two53minus1);
288 :
289 : // Round the {len} towards -Infinity.
290 62 : Node* value = Float64Floor(len_value);
291 62 : Node* result = ChangeFloat64ToTagged(value);
292 31 : Return(result);
293 : }
294 :
295 31 : BIND(&if_lenisnotheapnumber);
296 : {
297 : // Need to convert {len} to a Number first.
298 31 : var_len.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, len));
299 31 : Goto(&loop);
300 : }
301 :
302 31 : BIND(&return_len);
303 62 : Return(var_len.value());
304 :
305 31 : BIND(&return_two53minus1);
306 62 : Return(NumberConstant(kMaxSafeInteger));
307 :
308 31 : BIND(&return_zero);
309 93 : Return(SmiConstant(0));
310 31 : }
311 31 : }
312 :
313 93 : TF_BUILTIN(ToInteger, CodeStubAssembler) {
314 : Node* context = Parameter(Descriptor::kContext);
315 : Node* input = Parameter(Descriptor::kArgument);
316 :
317 62 : Return(ToInteger(context, input));
318 31 : }
319 :
320 : // ES6 section 7.1.13 ToObject (argument)
321 124 : TF_BUILTIN(ToObject, CodeStubAssembler) {
322 62 : Label if_smi(this, Label::kDeferred), if_jsreceiver(this),
323 31 : if_noconstructor(this, Label::kDeferred), if_wrapjsvalue(this);
324 :
325 : Node* context = Parameter(Descriptor::kContext);
326 : Node* object = Parameter(Descriptor::kArgument);
327 :
328 62 : VARIABLE(constructor_function_index_var,
329 : MachineType::PointerRepresentation());
330 :
331 62 : GotoIf(TaggedIsSmi(object), &if_smi);
332 :
333 62 : Node* map = LoadMap(object);
334 62 : Node* instance_type = LoadMapInstanceType(map);
335 62 : GotoIf(IsJSReceiverInstanceType(instance_type), &if_jsreceiver);
336 :
337 62 : Node* constructor_function_index = LoadMapConstructorFunctionIndex(map);
338 : GotoIf(WordEqual(constructor_function_index,
339 62 : IntPtrConstant(Map::kNoConstructorFunctionIndex)),
340 62 : &if_noconstructor);
341 31 : constructor_function_index_var.Bind(constructor_function_index);
342 31 : Goto(&if_wrapjsvalue);
343 :
344 31 : BIND(&if_smi);
345 : constructor_function_index_var.Bind(
346 62 : IntPtrConstant(Context::NUMBER_FUNCTION_INDEX));
347 31 : Goto(&if_wrapjsvalue);
348 :
349 31 : BIND(&if_wrapjsvalue);
350 31 : Node* native_context = LoadNativeContext(context);
351 : Node* constructor = LoadFixedArrayElement(
352 31 : native_context, constructor_function_index_var.value());
353 : Node* initial_map =
354 : LoadObjectField(constructor, JSFunction::kPrototypeOrInitialMapOffset);
355 31 : Node* js_value = Allocate(JSValue::kSize);
356 31 : StoreMapNoWriteBarrier(js_value, initial_map);
357 : StoreObjectFieldRoot(js_value, JSValue::kPropertiesOrHashOffset,
358 31 : Heap::kEmptyFixedArrayRootIndex);
359 : StoreObjectFieldRoot(js_value, JSObject::kElementsOffset,
360 31 : Heap::kEmptyFixedArrayRootIndex);
361 31 : StoreObjectField(js_value, JSValue::kValueOffset, object);
362 31 : Return(js_value);
363 :
364 31 : BIND(&if_noconstructor);
365 : TailCallRuntime(Runtime::kThrowUndefinedOrNullToObject, context,
366 31 : StringConstant("ToObject"));
367 :
368 31 : BIND(&if_jsreceiver);
369 62 : Return(object);
370 31 : }
371 :
372 : // Deprecated ES5 [[Class]] internal property (used to implement %_ClassOf).
373 93 : TF_BUILTIN(ClassOf, CodeStubAssembler) {
374 : Node* object = Parameter(TypeofDescriptor::kObject);
375 :
376 62 : Return(ClassOf(object));
377 31 : }
378 :
379 : // ES6 section 12.5.5 typeof operator
380 93 : TF_BUILTIN(Typeof, CodeStubAssembler) {
381 : Node* object = Parameter(TypeofDescriptor::kObject);
382 :
383 62 : Return(Typeof(object));
384 31 : }
385 :
386 : } // namespace internal
387 : } // namespace v8
|