• 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.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                       &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 
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