• 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.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                         &not_string);
173   assembler->Return(input);
174 
175   Label not_heap_number(assembler);
176 
177   assembler->Bind(&not_string);
178   {
179     assembler->GotoUnless(
180         assembler->WordEqual(input_map, assembler->HeapNumberMapConstant()),
181         &not_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(&not_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