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