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