Home | History | Annotate | Download | only in builtins
      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 
     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 ] )
     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 
     97 void Builtins::Generate_NonPrimitiveToPrimitive_Default(
     98     CodeStubAssembler* assembler) {
     99   Generate_NonPrimitiveToPrimitive(assembler, ToPrimitiveHint::kDefault);
    100 }
    101 
    102 void Builtins::Generate_NonPrimitiveToPrimitive_Number(
    103     CodeStubAssembler* assembler) {
    104   Generate_NonPrimitiveToPrimitive(assembler, ToPrimitiveHint::kNumber);
    105 }
    106 
    107 void Builtins::Generate_NonPrimitiveToPrimitive_String(
    108     CodeStubAssembler* assembler) {
    109   Generate_NonPrimitiveToPrimitive(assembler, ToPrimitiveHint::kString);
    110 }
    111 
    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 
    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
    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 )
    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 
    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 
    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 )
    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 
    285 void Builtins::Generate_OrdinaryToPrimitive_Number(
    286     CodeStubAssembler* assembler) {
    287   Generate_OrdinaryToPrimitive(assembler, OrdinaryToPrimitiveHint::kNumber);
    288 }
    289 
    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 )
    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 
    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 
    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)
    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
    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