• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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