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
11namespace v8 {
12namespace internal {
13
14Handle<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
27namespace {
28
29// ES6 section 7.1.1 ToPrimitive ( input [ , PreferredType ] )
30void 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
102void Builtins::Generate_NonPrimitiveToPrimitive_Default(
103    compiler::CodeAssemblerState* state) {
104  CodeStubAssembler assembler(state);
105  Generate_NonPrimitiveToPrimitive(&assembler, ToPrimitiveHint::kDefault);
106}
107
108void Builtins::Generate_NonPrimitiveToPrimitive_Number(
109    compiler::CodeAssemblerState* state) {
110  CodeStubAssembler assembler(state);
111  Generate_NonPrimitiveToPrimitive(&assembler, ToPrimitiveHint::kNumber);
112}
113
114void Builtins::Generate_NonPrimitiveToPrimitive_String(
115    compiler::CodeAssemblerState* state) {
116  CodeStubAssembler assembler(state);
117  Generate_NonPrimitiveToPrimitive(&assembler, ToPrimitiveHint::kString);
118}
119
120void 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
131void 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
143void 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 )
155void 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
166void 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                      &not_string);
186  assembler.Return(input);
187
188  Label not_heap_number(&assembler);
189
190  assembler.Bind(&not_string);
191  {
192    assembler.GotoIfNot(assembler.IsHeapNumberMap(input_map), &not_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(&not_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
215Handle<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
226namespace {
227
228// 7.1.1.1 OrdinaryToPrimitive ( O, hint )
229void 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
298void Builtins::Generate_OrdinaryToPrimitive_Number(
299    compiler::CodeAssemblerState* state) {
300  CodeStubAssembler assembler(state);
301  Generate_OrdinaryToPrimitive(&assembler, OrdinaryToPrimitiveHint::kNumber);
302}
303
304void 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 )
311void 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
329void 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
405void 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)
416void 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).
484void 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
495void 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