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.h"
6 #include "src/builtins/builtins-utils.h"
7 #include "src/code-factory.h"
8
9 namespace v8 {
10 namespace internal {
11
NonPrimitiveToPrimitive(ToPrimitiveHint hint)12 Handle<Code> Builtins::NonPrimitiveToPrimitive(ToPrimitiveHint hint) {
13 switch (hint) {
14 case ToPrimitiveHint::kDefault:
15 return NonPrimitiveToPrimitive_Default();
16 case ToPrimitiveHint::kNumber:
17 return NonPrimitiveToPrimitive_Number();
18 case ToPrimitiveHint::kString:
19 return NonPrimitiveToPrimitive_String();
20 }
21 UNREACHABLE();
22 return Handle<Code>::null();
23 }
24
25 namespace {
26 // ES6 section 7.1.1 ToPrimitive ( input [ , PreferredType ] )
Generate_NonPrimitiveToPrimitive(CodeStubAssembler * assembler,ToPrimitiveHint hint)27 void Generate_NonPrimitiveToPrimitive(CodeStubAssembler* assembler,
28 ToPrimitiveHint hint) {
29 typedef CodeStubAssembler::Label Label;
30 typedef compiler::Node Node;
31 typedef TypeConversionDescriptor Descriptor;
32
33 Node* input = assembler->Parameter(Descriptor::kArgument);
34 Node* context = assembler->Parameter(Descriptor::kContext);
35
36 // Lookup the @@toPrimitive property on the {input}.
37 Callable callable = CodeFactory::GetProperty(assembler->isolate());
38 Node* to_primitive_symbol =
39 assembler->HeapConstant(assembler->factory()->to_primitive_symbol());
40 Node* exotic_to_prim =
41 assembler->CallStub(callable, context, input, to_primitive_symbol);
42
43 // Check if {exotic_to_prim} is neither null nor undefined.
44 Label ordinary_to_primitive(assembler);
45 assembler->GotoIf(
46 assembler->WordEqual(exotic_to_prim, assembler->NullConstant()),
47 &ordinary_to_primitive);
48 assembler->GotoIf(
49 assembler->WordEqual(exotic_to_prim, assembler->UndefinedConstant()),
50 &ordinary_to_primitive);
51 {
52 // Invoke the {exotic_to_prim} method on the {input} with a string
53 // representation of the {hint}.
54 Callable callable = CodeFactory::Call(assembler->isolate());
55 Node* hint_string = assembler->HeapConstant(
56 assembler->factory()->ToPrimitiveHintString(hint));
57 Node* result = assembler->CallJS(callable, context, exotic_to_prim, input,
58 hint_string);
59
60 // Verify that the {result} is actually a primitive.
61 Label if_resultisprimitive(assembler),
62 if_resultisnotprimitive(assembler, Label::kDeferred);
63 assembler->GotoIf(assembler->TaggedIsSmi(result), &if_resultisprimitive);
64 Node* result_instance_type = assembler->LoadInstanceType(result);
65 STATIC_ASSERT(FIRST_PRIMITIVE_TYPE == FIRST_TYPE);
66 assembler->Branch(assembler->Int32LessThanOrEqual(
67 result_instance_type,
68 assembler->Int32Constant(LAST_PRIMITIVE_TYPE)),
69 &if_resultisprimitive, &if_resultisnotprimitive);
70
71 assembler->Bind(&if_resultisprimitive);
72 {
73 // Just return the {result}.
74 assembler->Return(result);
75 }
76
77 assembler->Bind(&if_resultisnotprimitive);
78 {
79 // Somehow the @@toPrimitive method on {input} didn't yield a primitive.
80 assembler->TailCallRuntime(Runtime::kThrowCannotConvertToPrimitive,
81 context);
82 }
83 }
84
85 // Convert using the OrdinaryToPrimitive algorithm instead.
86 assembler->Bind(&ordinary_to_primitive);
87 {
88 Callable callable = CodeFactory::OrdinaryToPrimitive(
89 assembler->isolate(), (hint == ToPrimitiveHint::kString)
90 ? OrdinaryToPrimitiveHint::kString
91 : OrdinaryToPrimitiveHint::kNumber);
92 assembler->TailCallStub(callable, context, input);
93 }
94 }
95 } // anonymous namespace
96
Generate_NonPrimitiveToPrimitive_Default(CodeStubAssembler * assembler)97 void Builtins::Generate_NonPrimitiveToPrimitive_Default(
98 CodeStubAssembler* assembler) {
99 Generate_NonPrimitiveToPrimitive(assembler, ToPrimitiveHint::kDefault);
100 }
101
Generate_NonPrimitiveToPrimitive_Number(CodeStubAssembler * assembler)102 void Builtins::Generate_NonPrimitiveToPrimitive_Number(
103 CodeStubAssembler* assembler) {
104 Generate_NonPrimitiveToPrimitive(assembler, ToPrimitiveHint::kNumber);
105 }
106
Generate_NonPrimitiveToPrimitive_String(CodeStubAssembler * assembler)107 void Builtins::Generate_NonPrimitiveToPrimitive_String(
108 CodeStubAssembler* assembler) {
109 Generate_NonPrimitiveToPrimitive(assembler, ToPrimitiveHint::kString);
110 }
111
Generate_StringToNumber(CodeStubAssembler * assembler)112 void Builtins::Generate_StringToNumber(CodeStubAssembler* assembler) {
113 typedef compiler::Node Node;
114 typedef TypeConversionDescriptor Descriptor;
115
116 Node* input = assembler->Parameter(Descriptor::kArgument);
117 Node* context = assembler->Parameter(Descriptor::kContext);
118
119 assembler->Return(assembler->StringToNumber(context, input));
120 }
121
Generate_ToName(CodeStubAssembler * assembler)122 void Builtins::Generate_ToName(CodeStubAssembler* assembler) {
123 typedef compiler::Node Node;
124 typedef TypeConversionDescriptor Descriptor;
125
126 Node* input = assembler->Parameter(Descriptor::kArgument);
127 Node* context = assembler->Parameter(Descriptor::kContext);
128
129 assembler->Return(assembler->ToName(context, input));
130 }
131
132 // static
Generate_NonNumberToNumber(CodeStubAssembler * assembler)133 void Builtins::Generate_NonNumberToNumber(CodeStubAssembler* assembler) {
134 typedef compiler::Node Node;
135 typedef TypeConversionDescriptor Descriptor;
136
137 Node* input = assembler->Parameter(Descriptor::kArgument);
138 Node* context = assembler->Parameter(Descriptor::kContext);
139
140 assembler->Return(assembler->NonNumberToNumber(context, input));
141 }
142
143 // ES6 section 7.1.3 ToNumber ( argument )
Generate_ToNumber(CodeStubAssembler * assembler)144 void Builtins::Generate_ToNumber(CodeStubAssembler* assembler) {
145 typedef compiler::Node Node;
146 typedef TypeConversionDescriptor Descriptor;
147
148 Node* input = assembler->Parameter(Descriptor::kArgument);
149 Node* context = assembler->Parameter(Descriptor::kContext);
150
151 assembler->Return(assembler->ToNumber(context, input));
152 }
153
Generate_ToString(CodeStubAssembler * assembler)154 void Builtins::Generate_ToString(CodeStubAssembler* assembler) {
155 typedef CodeStubAssembler::Label Label;
156 typedef compiler::Node Node;
157 typedef TypeConversionDescriptor Descriptor;
158
159 Node* input = assembler->Parameter(Descriptor::kArgument);
160 Node* context = assembler->Parameter(Descriptor::kContext);
161
162 Label is_number(assembler);
163 Label runtime(assembler);
164
165 assembler->GotoIf(assembler->TaggedIsSmi(input), &is_number);
166
167 Node* input_map = assembler->LoadMap(input);
168 Node* input_instance_type = assembler->LoadMapInstanceType(input_map);
169
170 Label not_string(assembler);
171 assembler->GotoUnless(assembler->IsStringInstanceType(input_instance_type),
172 ¬_string);
173 assembler->Return(input);
174
175 Label not_heap_number(assembler);
176
177 assembler->Bind(¬_string);
178 {
179 assembler->GotoUnless(
180 assembler->WordEqual(input_map, assembler->HeapNumberMapConstant()),
181 ¬_heap_number);
182 assembler->Goto(&is_number);
183 }
184
185 assembler->Bind(&is_number);
186 { assembler->Return(assembler->NumberToString(context, input)); }
187
188 assembler->Bind(¬_heap_number);
189 {
190 assembler->GotoIf(
191 assembler->Word32NotEqual(input_instance_type,
192 assembler->Int32Constant(ODDBALL_TYPE)),
193 &runtime);
194 assembler->Return(
195 assembler->LoadObjectField(input, Oddball::kToStringOffset));
196 }
197
198 assembler->Bind(&runtime);
199 {
200 assembler->Return(
201 assembler->CallRuntime(Runtime::kToString, context, input));
202 }
203 }
204
OrdinaryToPrimitive(OrdinaryToPrimitiveHint hint)205 Handle<Code> Builtins::OrdinaryToPrimitive(OrdinaryToPrimitiveHint hint) {
206 switch (hint) {
207 case OrdinaryToPrimitiveHint::kNumber:
208 return OrdinaryToPrimitive_Number();
209 case OrdinaryToPrimitiveHint::kString:
210 return OrdinaryToPrimitive_String();
211 }
212 UNREACHABLE();
213 return Handle<Code>::null();
214 }
215
216 namespace {
217 // 7.1.1.1 OrdinaryToPrimitive ( O, hint )
Generate_OrdinaryToPrimitive(CodeStubAssembler * assembler,OrdinaryToPrimitiveHint hint)218 void Generate_OrdinaryToPrimitive(CodeStubAssembler* assembler,
219 OrdinaryToPrimitiveHint hint) {
220 typedef CodeStubAssembler::Label Label;
221 typedef compiler::Node Node;
222 typedef CodeStubAssembler::Variable Variable;
223 typedef TypeConversionDescriptor Descriptor;
224
225 Node* input = assembler->Parameter(Descriptor::kArgument);
226 Node* context = assembler->Parameter(Descriptor::kContext);
227
228 Variable var_result(assembler, MachineRepresentation::kTagged);
229 Label return_result(assembler, &var_result);
230
231 Handle<String> method_names[2];
232 switch (hint) {
233 case OrdinaryToPrimitiveHint::kNumber:
234 method_names[0] = assembler->factory()->valueOf_string();
235 method_names[1] = assembler->factory()->toString_string();
236 break;
237 case OrdinaryToPrimitiveHint::kString:
238 method_names[0] = assembler->factory()->toString_string();
239 method_names[1] = assembler->factory()->valueOf_string();
240 break;
241 }
242 for (Handle<String> name : method_names) {
243 // Lookup the {name} on the {input}.
244 Callable callable = CodeFactory::GetProperty(assembler->isolate());
245 Node* name_string = assembler->HeapConstant(name);
246 Node* method = assembler->CallStub(callable, context, input, name_string);
247
248 // Check if the {method} is callable.
249 Label if_methodiscallable(assembler),
250 if_methodisnotcallable(assembler, Label::kDeferred);
251 assembler->GotoIf(assembler->TaggedIsSmi(method), &if_methodisnotcallable);
252 Node* method_map = assembler->LoadMap(method);
253 assembler->Branch(assembler->IsCallableMap(method_map),
254 &if_methodiscallable, &if_methodisnotcallable);
255
256 assembler->Bind(&if_methodiscallable);
257 {
258 // Call the {method} on the {input}.
259 Callable callable = CodeFactory::Call(assembler->isolate());
260 Node* result = assembler->CallJS(callable, context, method, input);
261 var_result.Bind(result);
262
263 // Return the {result} if it is a primitive.
264 assembler->GotoIf(assembler->TaggedIsSmi(result), &return_result);
265 Node* result_instance_type = assembler->LoadInstanceType(result);
266 STATIC_ASSERT(FIRST_PRIMITIVE_TYPE == FIRST_TYPE);
267 assembler->GotoIf(assembler->Int32LessThanOrEqual(
268 result_instance_type,
269 assembler->Int32Constant(LAST_PRIMITIVE_TYPE)),
270 &return_result);
271 }
272
273 // Just continue with the next {name} if the {method} is not callable.
274 assembler->Goto(&if_methodisnotcallable);
275 assembler->Bind(&if_methodisnotcallable);
276 }
277
278 assembler->TailCallRuntime(Runtime::kThrowCannotConvertToPrimitive, context);
279
280 assembler->Bind(&return_result);
281 assembler->Return(var_result.value());
282 }
283 } // anonymous namespace
284
Generate_OrdinaryToPrimitive_Number(CodeStubAssembler * assembler)285 void Builtins::Generate_OrdinaryToPrimitive_Number(
286 CodeStubAssembler* assembler) {
287 Generate_OrdinaryToPrimitive(assembler, OrdinaryToPrimitiveHint::kNumber);
288 }
289
Generate_OrdinaryToPrimitive_String(CodeStubAssembler * assembler)290 void Builtins::Generate_OrdinaryToPrimitive_String(
291 CodeStubAssembler* assembler) {
292 Generate_OrdinaryToPrimitive(assembler, OrdinaryToPrimitiveHint::kString);
293 }
294
295 // ES6 section 7.1.2 ToBoolean ( argument )
Generate_ToBoolean(CodeStubAssembler * assembler)296 void Builtins::Generate_ToBoolean(CodeStubAssembler* assembler) {
297 typedef compiler::Node Node;
298 typedef CodeStubAssembler::Label Label;
299 typedef TypeConversionDescriptor Descriptor;
300
301 Node* value = assembler->Parameter(Descriptor::kArgument);
302
303 Label return_true(assembler), return_false(assembler);
304 assembler->BranchIfToBooleanIsTrue(value, &return_true, &return_false);
305
306 assembler->Bind(&return_true);
307 assembler->Return(assembler->BooleanConstant(true));
308
309 assembler->Bind(&return_false);
310 assembler->Return(assembler->BooleanConstant(false));
311 }
312
Generate_ToLength(CodeStubAssembler * assembler)313 void Builtins::Generate_ToLength(CodeStubAssembler* assembler) {
314 typedef CodeStubAssembler::Label Label;
315 typedef compiler::Node Node;
316 typedef CodeStubAssembler::Variable Variable;
317
318 Node* context = assembler->Parameter(1);
319
320 // We might need to loop once for ToNumber conversion.
321 Variable var_len(assembler, MachineRepresentation::kTagged);
322 Label loop(assembler, &var_len);
323 var_len.Bind(assembler->Parameter(0));
324 assembler->Goto(&loop);
325 assembler->Bind(&loop);
326 {
327 // Shared entry points.
328 Label return_len(assembler),
329 return_two53minus1(assembler, Label::kDeferred),
330 return_zero(assembler, Label::kDeferred);
331
332 // Load the current {len} value.
333 Node* len = var_len.value();
334
335 // Check if {len} is a positive Smi.
336 assembler->GotoIf(assembler->WordIsPositiveSmi(len), &return_len);
337
338 // Check if {len} is a (negative) Smi.
339 assembler->GotoIf(assembler->TaggedIsSmi(len), &return_zero);
340
341 // Check if {len} is a HeapNumber.
342 Label if_lenisheapnumber(assembler),
343 if_lenisnotheapnumber(assembler, Label::kDeferred);
344 assembler->Branch(assembler->IsHeapNumberMap(assembler->LoadMap(len)),
345 &if_lenisheapnumber, &if_lenisnotheapnumber);
346
347 assembler->Bind(&if_lenisheapnumber);
348 {
349 // Load the floating-point value of {len}.
350 Node* len_value = assembler->LoadHeapNumberValue(len);
351
352 // Check if {len} is not greater than zero.
353 assembler->GotoUnless(assembler->Float64GreaterThan(
354 len_value, assembler->Float64Constant(0.0)),
355 &return_zero);
356
357 // Check if {len} is greater than or equal to 2^53-1.
358 assembler->GotoIf(
359 assembler->Float64GreaterThanOrEqual(
360 len_value, assembler->Float64Constant(kMaxSafeInteger)),
361 &return_two53minus1);
362
363 // Round the {len} towards -Infinity.
364 Node* value = assembler->Float64Floor(len_value);
365 Node* result = assembler->ChangeFloat64ToTagged(value);
366 assembler->Return(result);
367 }
368
369 assembler->Bind(&if_lenisnotheapnumber);
370 {
371 // Need to convert {len} to a Number first.
372 Callable callable = CodeFactory::NonNumberToNumber(assembler->isolate());
373 var_len.Bind(assembler->CallStub(callable, context, len));
374 assembler->Goto(&loop);
375 }
376
377 assembler->Bind(&return_len);
378 assembler->Return(var_len.value());
379
380 assembler->Bind(&return_two53minus1);
381 assembler->Return(assembler->NumberConstant(kMaxSafeInteger));
382
383 assembler->Bind(&return_zero);
384 assembler->Return(assembler->SmiConstant(Smi::kZero));
385 }
386 }
387
Generate_ToInteger(CodeStubAssembler * assembler)388 void Builtins::Generate_ToInteger(CodeStubAssembler* assembler) {
389 typedef TypeConversionDescriptor Descriptor;
390
391 compiler::Node* input = assembler->Parameter(Descriptor::kArgument);
392 compiler::Node* context = assembler->Parameter(Descriptor::kContext);
393
394 assembler->Return(assembler->ToInteger(context, input));
395 }
396
397 // ES6 section 7.1.13 ToObject (argument)
Generate_ToObject(CodeStubAssembler * assembler)398 void Builtins::Generate_ToObject(CodeStubAssembler* assembler) {
399 typedef compiler::Node Node;
400 typedef CodeStubAssembler::Label Label;
401 typedef CodeStubAssembler::Variable Variable;
402 typedef TypeConversionDescriptor Descriptor;
403
404 Label if_number(assembler, Label::kDeferred), if_notsmi(assembler),
405 if_jsreceiver(assembler), if_noconstructor(assembler, Label::kDeferred),
406 if_wrapjsvalue(assembler);
407
408 Node* object = assembler->Parameter(Descriptor::kArgument);
409 Node* context = assembler->Parameter(Descriptor::kContext);
410
411 Variable constructor_function_index_var(assembler,
412 MachineType::PointerRepresentation());
413
414 assembler->Branch(assembler->TaggedIsSmi(object), &if_number, &if_notsmi);
415
416 assembler->Bind(&if_notsmi);
417 Node* map = assembler->LoadMap(object);
418
419 assembler->GotoIf(assembler->IsHeapNumberMap(map), &if_number);
420
421 Node* instance_type = assembler->LoadMapInstanceType(map);
422 assembler->GotoIf(assembler->IsJSReceiverInstanceType(instance_type),
423 &if_jsreceiver);
424
425 Node* constructor_function_index =
426 assembler->LoadMapConstructorFunctionIndex(map);
427 assembler->GotoIf(assembler->WordEqual(constructor_function_index,
428 assembler->IntPtrConstant(
429 Map::kNoConstructorFunctionIndex)),
430 &if_noconstructor);
431 constructor_function_index_var.Bind(constructor_function_index);
432 assembler->Goto(&if_wrapjsvalue);
433
434 assembler->Bind(&if_number);
435 constructor_function_index_var.Bind(
436 assembler->IntPtrConstant(Context::NUMBER_FUNCTION_INDEX));
437 assembler->Goto(&if_wrapjsvalue);
438
439 assembler->Bind(&if_wrapjsvalue);
440 Node* native_context = assembler->LoadNativeContext(context);
441 Node* constructor = assembler->LoadFixedArrayElement(
442 native_context, constructor_function_index_var.value(), 0,
443 CodeStubAssembler::INTPTR_PARAMETERS);
444 Node* initial_map = assembler->LoadObjectField(
445 constructor, JSFunction::kPrototypeOrInitialMapOffset);
446 Node* js_value = assembler->Allocate(JSValue::kSize);
447 assembler->StoreMapNoWriteBarrier(js_value, initial_map);
448 assembler->StoreObjectFieldRoot(js_value, JSValue::kPropertiesOffset,
449 Heap::kEmptyFixedArrayRootIndex);
450 assembler->StoreObjectFieldRoot(js_value, JSObject::kElementsOffset,
451 Heap::kEmptyFixedArrayRootIndex);
452 assembler->StoreObjectField(js_value, JSValue::kValueOffset, object);
453 assembler->Return(js_value);
454
455 assembler->Bind(&if_noconstructor);
456 assembler->TailCallRuntime(
457 Runtime::kThrowUndefinedOrNullToObject, context,
458 assembler->HeapConstant(assembler->factory()->NewStringFromAsciiChecked(
459 "ToObject", TENURED)));
460
461 assembler->Bind(&if_jsreceiver);
462 assembler->Return(object);
463 }
464
465 // ES6 section 12.5.5 typeof operator
Generate_Typeof(CodeStubAssembler * assembler)466 void Builtins::Generate_Typeof(CodeStubAssembler* assembler) {
467 typedef compiler::Node Node;
468 typedef TypeofDescriptor Descriptor;
469
470 Node* object = assembler->Parameter(Descriptor::kObject);
471 Node* context = assembler->Parameter(Descriptor::kContext);
472
473 assembler->Return(assembler->Typeof(object, context));
474 }
475
476 } // namespace internal
477 } // namespace v8
478