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-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 
     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 ] )
     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 
    102 void Builtins::Generate_NonPrimitiveToPrimitive_Default(
    103     compiler::CodeAssemblerState* state) {
    104   CodeStubAssembler assembler(state);
    105   Generate_NonPrimitiveToPrimitive(&assembler, ToPrimitiveHint::kDefault);
    106 }
    107 
    108 void Builtins::Generate_NonPrimitiveToPrimitive_Number(
    109     compiler::CodeAssemblerState* state) {
    110   CodeStubAssembler assembler(state);
    111   Generate_NonPrimitiveToPrimitive(&assembler, ToPrimitiveHint::kNumber);
    112 }
    113 
    114 void Builtins::Generate_NonPrimitiveToPrimitive_String(
    115     compiler::CodeAssemblerState* state) {
    116   CodeStubAssembler assembler(state);
    117   Generate_NonPrimitiveToPrimitive(&assembler, ToPrimitiveHint::kString);
    118 }
    119 
    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 
    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
    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 )
    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 
    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 
    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 )
    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 
    298 void Builtins::Generate_OrdinaryToPrimitive_Number(
    299     compiler::CodeAssemblerState* state) {
    300   CodeStubAssembler assembler(state);
    301   Generate_OrdinaryToPrimitive(&assembler, OrdinaryToPrimitiveHint::kNumber);
    302 }
    303 
    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 )
    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 
    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 
    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)
    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).
    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
    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