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