LCOV - code coverage report
Current view: top level - src/builtins - builtins-conversion-gen.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 219 219 100.0 %
Date: 2019-04-18 Functions: 45 45 100.0 %

          Line data    Source code
       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             : #include "src/objects/oddball.h"
      11             : 
      12             : namespace v8 {
      13             : namespace internal {
      14             : 
      15         280 : class ConversionBuiltinsAssembler : public CodeStubAssembler {
      16             :  public:
      17             :   explicit ConversionBuiltinsAssembler(compiler::CodeAssemblerState* state)
      18         280 :       : CodeStubAssembler(state) {}
      19             : 
      20             :  protected:
      21             :   void Generate_NonPrimitiveToPrimitive(Node* context, Node* input,
      22             :                                         ToPrimitiveHint hint);
      23             : 
      24             :   void Generate_OrdinaryToPrimitive(Node* context, Node* input,
      25             :                                     OrdinaryToPrimitiveHint hint);
      26             : };
      27             : 
      28             : // ES6 section 7.1.1 ToPrimitive ( input [ , PreferredType ] )
      29         168 : void ConversionBuiltinsAssembler::Generate_NonPrimitiveToPrimitive(
      30             :     Node* context, Node* input, ToPrimitiveHint hint) {
      31             :   // Lookup the @@toPrimitive property on the {input}.
      32             :   Node* exotic_to_prim =
      33         504 :       GetProperty(context, input, factory()->to_primitive_symbol());
      34             : 
      35             :   // Check if {exotic_to_prim} is neither null nor undefined.
      36         168 :   Label ordinary_to_primitive(this);
      37         336 :   GotoIf(IsNullOrUndefined(exotic_to_prim), &ordinary_to_primitive);
      38             :   {
      39             :     // Invoke the {exotic_to_prim} method on the {input} with a string
      40             :     // representation of the {hint}.
      41             :     Callable callable =
      42         168 :         CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined);
      43         168 :     Node* hint_string = HeapConstant(factory()->ToPrimitiveHintString(hint));
      44             :     Node* result =
      45         168 :         CallJS(callable, context, exotic_to_prim, input, hint_string);
      46             : 
      47             :     // Verify that the {result} is actually a primitive.
      48         168 :     Label if_resultisprimitive(this),
      49         168 :         if_resultisnotprimitive(this, Label::kDeferred);
      50         336 :     GotoIf(TaggedIsSmi(result), &if_resultisprimitive);
      51         336 :     Node* result_instance_type = LoadInstanceType(result);
      52         336 :     Branch(IsPrimitiveInstanceType(result_instance_type), &if_resultisprimitive,
      53         168 :            &if_resultisnotprimitive);
      54             : 
      55         168 :     BIND(&if_resultisprimitive);
      56             :     {
      57             :       // Just return the {result}.
      58         168 :       Return(result);
      59             :     }
      60             : 
      61         168 :     BIND(&if_resultisnotprimitive);
      62             :     {
      63             :       // Somehow the @@toPrimitive method on {input} didn't yield a primitive.
      64         168 :       ThrowTypeError(context, MessageTemplate::kCannotConvertToPrimitive);
      65             :     }
      66             :   }
      67             : 
      68             :   // Convert using the OrdinaryToPrimitive algorithm instead.
      69         168 :   BIND(&ordinary_to_primitive);
      70             :   {
      71             :     Callable callable = CodeFactory::OrdinaryToPrimitive(
      72             :         isolate(), (hint == ToPrimitiveHint::kString)
      73             :                        ? OrdinaryToPrimitiveHint::kString
      74         168 :                        : OrdinaryToPrimitiveHint::kNumber);
      75         168 :     TailCallStub(callable, context, input);
      76             :   }
      77         168 : }
      78             : 
      79         224 : TF_BUILTIN(NonPrimitiveToPrimitive_Default, ConversionBuiltinsAssembler) {
      80             :   Node* context = Parameter(Descriptor::kContext);
      81             :   Node* input = Parameter(Descriptor::kArgument);
      82             : 
      83          56 :   Generate_NonPrimitiveToPrimitive(context, input, ToPrimitiveHint::kDefault);
      84          56 : }
      85             : 
      86         224 : TF_BUILTIN(NonPrimitiveToPrimitive_Number, ConversionBuiltinsAssembler) {
      87             :   Node* context = Parameter(Descriptor::kContext);
      88             :   Node* input = Parameter(Descriptor::kArgument);
      89             : 
      90          56 :   Generate_NonPrimitiveToPrimitive(context, input, ToPrimitiveHint::kNumber);
      91          56 : }
      92             : 
      93         224 : TF_BUILTIN(NonPrimitiveToPrimitive_String, ConversionBuiltinsAssembler) {
      94             :   Node* context = Parameter(Descriptor::kContext);
      95             :   Node* input = Parameter(Descriptor::kArgument);
      96             : 
      97          56 :   Generate_NonPrimitiveToPrimitive(context, input, ToPrimitiveHint::kString);
      98          56 : }
      99             : 
     100         168 : TF_BUILTIN(StringToNumber, CodeStubAssembler) {
     101          56 :   TNode<String> input = CAST(Parameter(Descriptor::kArgument));
     102             : 
     103         112 :   Return(StringToNumber(input));
     104          56 : }
     105             : 
     106         168 : TF_BUILTIN(ToName, CodeStubAssembler) {
     107             :   Node* context = Parameter(Descriptor::kContext);
     108             :   Node* input = Parameter(Descriptor::kArgument);
     109             : 
     110         112 :   VARIABLE(var_input, MachineRepresentation::kTagged, input);
     111          56 :   Label loop(this, &var_input);
     112          56 :   Goto(&loop);
     113          56 :   BIND(&loop);
     114             :   {
     115             :     // Load the current {input} value.
     116          56 :     Node* input = var_input.value();
     117             : 
     118             :     // Dispatch based on the type of the {input.}
     119          56 :     Label if_inputisbigint(this), if_inputisname(this), if_inputisnumber(this),
     120          56 :         if_inputisoddball(this), if_inputisreceiver(this, Label::kDeferred);
     121         112 :     GotoIf(TaggedIsSmi(input), &if_inputisnumber);
     122         112 :     Node* input_instance_type = LoadInstanceType(input);
     123             :     STATIC_ASSERT(FIRST_NAME_TYPE == FIRST_TYPE);
     124         112 :     GotoIf(IsNameInstanceType(input_instance_type), &if_inputisname);
     125         112 :     GotoIf(IsJSReceiverInstanceType(input_instance_type), &if_inputisreceiver);
     126         112 :     GotoIf(IsHeapNumberInstanceType(input_instance_type), &if_inputisnumber);
     127         112 :     Branch(IsBigIntInstanceType(input_instance_type), &if_inputisbigint,
     128          56 :            &if_inputisoddball);
     129             : 
     130          56 :     BIND(&if_inputisbigint);
     131             :     {
     132             :       // We don't have a fast-path for BigInt currently, so just
     133             :       // tail call to the %ToString runtime function here for now.
     134          56 :       TailCallRuntime(Runtime::kToString, context, input);
     135             :     }
     136             : 
     137          56 :     BIND(&if_inputisname);
     138             :     {
     139             :       // The {input} is already a Name.
     140          56 :       Return(input);
     141             :     }
     142             : 
     143          56 :     BIND(&if_inputisnumber);
     144             :     {
     145             :       // Convert the String {input} to a Number.
     146          56 :       TailCallBuiltin(Builtins::kNumberToString, context, input);
     147             :     }
     148             : 
     149          56 :     BIND(&if_inputisoddball);
     150             :     {
     151             :       // Just return the {input}'s string representation.
     152             :       CSA_ASSERT(this, IsOddballInstanceType(input_instance_type));
     153          56 :       Return(LoadObjectField(input, Oddball::kToStringOffset));
     154             :     }
     155             : 
     156          56 :     BIND(&if_inputisreceiver);
     157             :     {
     158             :       // Convert the JSReceiver {input} to a primitive first,
     159             :       // and then run the loop again with the new {input},
     160             :       // which is then a primitive value.
     161         112 :       var_input.Bind(CallBuiltin(Builtins::kNonPrimitiveToPrimitive_String,
     162         112 :                                  context, input));
     163          56 :       Goto(&loop);
     164             :     }
     165             :   }
     166          56 : }
     167             : 
     168         168 : TF_BUILTIN(NonNumberToNumber, CodeStubAssembler) {
     169             :   Node* context = Parameter(Descriptor::kContext);
     170             :   Node* input = Parameter(Descriptor::kArgument);
     171             : 
     172         112 :   Return(NonNumberToNumber(context, input));
     173          56 : }
     174             : 
     175         168 : TF_BUILTIN(NonNumberToNumeric, CodeStubAssembler) {
     176             :   Node* context = Parameter(Descriptor::kContext);
     177             :   Node* input = Parameter(Descriptor::kArgument);
     178             : 
     179         112 :   Return(NonNumberToNumeric(context, input));
     180          56 : }
     181             : 
     182         168 : TF_BUILTIN(ToNumeric, CodeStubAssembler) {
     183             :   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
     184             :   TNode<Object> input = CAST(Parameter(Descriptor::kArgument));
     185             : 
     186         112 :   Return(Select<Numeric>(
     187         112 :       IsNumber(input), [=] { return CAST(input); },
     188         336 :       [=] { return NonNumberToNumeric(context, CAST(input)); }));
     189          56 : }
     190             : 
     191             : // ES6 section 7.1.3 ToNumber ( argument )
     192         168 : TF_BUILTIN(ToNumber, CodeStubAssembler) {
     193             :   Node* context = Parameter(Descriptor::kContext);
     194             :   Node* input = Parameter(Descriptor::kArgument);
     195             : 
     196         112 :   Return(ToNumber(context, input));
     197          56 : }
     198             : 
     199             : // Like ToNumber, but also converts BigInts.
     200         168 : TF_BUILTIN(ToNumberConvertBigInt, CodeStubAssembler) {
     201             :   Node* context = Parameter(Descriptor::kContext);
     202             :   Node* input = Parameter(Descriptor::kArgument);
     203             : 
     204         112 :   Return(ToNumber(context, input, BigIntHandling::kConvertToNumber));
     205          56 : }
     206             : 
     207             : // ES section #sec-tostring-applied-to-the-number-type
     208         168 : TF_BUILTIN(NumberToString, CodeStubAssembler) {
     209          56 :   TNode<Number> input = CAST(Parameter(Descriptor::kArgument));
     210             : 
     211         112 :   Return(NumberToString(input));
     212          56 : }
     213             : 
     214             : // ES section #sec-tostring
     215         168 : TF_BUILTIN(ToString, CodeStubAssembler) {
     216             :   Node* context = Parameter(Descriptor::kContext);
     217             :   Node* input = Parameter(Descriptor::kArgument);
     218             : 
     219         112 :   Return(ToString(context, input));
     220          56 : }
     221             : 
     222             : // 7.1.1.1 OrdinaryToPrimitive ( O, hint )
     223         112 : void ConversionBuiltinsAssembler::Generate_OrdinaryToPrimitive(
     224             :     Node* context, Node* input, OrdinaryToPrimitiveHint hint) {
     225         224 :   VARIABLE(var_result, MachineRepresentation::kTagged);
     226         112 :   Label return_result(this, &var_result);
     227             : 
     228         560 :   Handle<String> method_names[2];
     229         112 :   switch (hint) {
     230             :     case OrdinaryToPrimitiveHint::kNumber:
     231         112 :       method_names[0] = factory()->valueOf_string();
     232         112 :       method_names[1] = factory()->toString_string();
     233          56 :       break;
     234             :     case OrdinaryToPrimitiveHint::kString:
     235         112 :       method_names[0] = factory()->toString_string();
     236         112 :       method_names[1] = factory()->valueOf_string();
     237          56 :       break;
     238             :   }
     239         560 :   for (Handle<String> name : method_names) {
     240             :     // Lookup the {name} on the {input}.
     241         448 :     Node* method = GetProperty(context, input, name);
     242             : 
     243             :     // Check if the {method} is callable.
     244         224 :     Label if_methodiscallable(this),
     245         224 :         if_methodisnotcallable(this, Label::kDeferred);
     246         448 :     GotoIf(TaggedIsSmi(method), &if_methodisnotcallable);
     247         448 :     Node* method_map = LoadMap(method);
     248         448 :     Branch(IsCallableMap(method_map), &if_methodiscallable,
     249         224 :            &if_methodisnotcallable);
     250             : 
     251         224 :     BIND(&if_methodiscallable);
     252             :     {
     253             :       // Call the {method} on the {input}.
     254             :       Callable callable = CodeFactory::Call(
     255         224 :           isolate(), ConvertReceiverMode::kNotNullOrUndefined);
     256         224 :       Node* result = CallJS(callable, context, method, input);
     257         224 :       var_result.Bind(result);
     258             : 
     259             :       // Return the {result} if it is a primitive.
     260         448 :       GotoIf(TaggedIsSmi(result), &return_result);
     261         448 :       Node* result_instance_type = LoadInstanceType(result);
     262         448 :       GotoIf(IsPrimitiveInstanceType(result_instance_type), &return_result);
     263             :     }
     264             : 
     265             :     // Just continue with the next {name} if the {method} is not callable.
     266         224 :     Goto(&if_methodisnotcallable);
     267         224 :     BIND(&if_methodisnotcallable);
     268             :   }
     269             : 
     270         112 :   ThrowTypeError(context, MessageTemplate::kCannotConvertToPrimitive);
     271             : 
     272         112 :   BIND(&return_result);
     273         224 :   Return(var_result.value());
     274         112 : }
     275             : 
     276         224 : TF_BUILTIN(OrdinaryToPrimitive_Number, ConversionBuiltinsAssembler) {
     277             :   Node* context = Parameter(Descriptor::kContext);
     278             :   Node* input = Parameter(Descriptor::kArgument);
     279          56 :   Generate_OrdinaryToPrimitive(context, input,
     280          56 :                                OrdinaryToPrimitiveHint::kNumber);
     281          56 : }
     282             : 
     283         224 : TF_BUILTIN(OrdinaryToPrimitive_String, ConversionBuiltinsAssembler) {
     284             :   Node* context = Parameter(Descriptor::kContext);
     285             :   Node* input = Parameter(Descriptor::kArgument);
     286          56 :   Generate_OrdinaryToPrimitive(context, input,
     287          56 :                                OrdinaryToPrimitiveHint::kString);
     288          56 : }
     289             : 
     290             : // ES6 section 7.1.2 ToBoolean ( argument )
     291         168 : TF_BUILTIN(ToBoolean, CodeStubAssembler) {
     292             :   Node* value = Parameter(Descriptor::kArgument);
     293             : 
     294          56 :   Label return_true(this), return_false(this);
     295          56 :   BranchIfToBooleanIsTrue(value, &return_true, &return_false);
     296             : 
     297          56 :   BIND(&return_true);
     298         112 :   Return(TrueConstant());
     299             : 
     300          56 :   BIND(&return_false);
     301         112 :   Return(FalseConstant());
     302          56 : }
     303             : 
     304             : // ES6 section 7.1.2 ToBoolean ( argument )
     305             : // Requires parameter on stack so that it can be used as a continuation from a
     306             : // LAZY deopt.
     307         168 : TF_BUILTIN(ToBooleanLazyDeoptContinuation, CodeStubAssembler) {
     308             :   Node* value = Parameter(Descriptor::kArgument);
     309             : 
     310          56 :   Label return_true(this), return_false(this);
     311          56 :   BranchIfToBooleanIsTrue(value, &return_true, &return_false);
     312             : 
     313          56 :   BIND(&return_true);
     314         112 :   Return(TrueConstant());
     315             : 
     316          56 :   BIND(&return_false);
     317         112 :   Return(FalseConstant());
     318          56 : }
     319             : 
     320         168 : TF_BUILTIN(ToLength, CodeStubAssembler) {
     321             :   Node* context = Parameter(Descriptor::kContext);
     322             : 
     323             :   // We might need to loop once for ToNumber conversion.
     324         112 :   VARIABLE(var_len, MachineRepresentation::kTagged,
     325             :            Parameter(Descriptor::kArgument));
     326          56 :   Label loop(this, &var_len);
     327          56 :   Goto(&loop);
     328          56 :   BIND(&loop);
     329             :   {
     330             :     // Shared entry points.
     331          56 :     Label return_len(this), return_two53minus1(this, Label::kDeferred),
     332          56 :         return_zero(this, Label::kDeferred);
     333             : 
     334             :     // Load the current {len} value.
     335          56 :     Node* len = var_len.value();
     336             : 
     337             :     // Check if {len} is a positive Smi.
     338         112 :     GotoIf(TaggedIsPositiveSmi(len), &return_len);
     339             : 
     340             :     // Check if {len} is a (negative) Smi.
     341         112 :     GotoIf(TaggedIsSmi(len), &return_zero);
     342             : 
     343             :     // Check if {len} is a HeapNumber.
     344          56 :     Label if_lenisheapnumber(this),
     345          56 :         if_lenisnotheapnumber(this, Label::kDeferred);
     346         112 :     Branch(IsHeapNumber(len), &if_lenisheapnumber, &if_lenisnotheapnumber);
     347             : 
     348          56 :     BIND(&if_lenisheapnumber);
     349             :     {
     350             :       // Load the floating-point value of {len}.
     351         112 :       Node* len_value = LoadHeapNumberValue(len);
     352             : 
     353             :       // Check if {len} is not greater than zero.
     354         168 :       GotoIfNot(Float64GreaterThan(len_value, Float64Constant(0.0)),
     355          56 :                 &return_zero);
     356             : 
     357             :       // Check if {len} is greater than or equal to 2^53-1.
     358         112 :       GotoIf(Float64GreaterThanOrEqual(len_value,
     359         112 :                                        Float64Constant(kMaxSafeInteger)),
     360          56 :              &return_two53minus1);
     361             : 
     362             :       // Round the {len} towards -Infinity.
     363         112 :       Node* value = Float64Floor(len_value);
     364         112 :       Node* result = ChangeFloat64ToTagged(value);
     365          56 :       Return(result);
     366             :     }
     367             : 
     368          56 :     BIND(&if_lenisnotheapnumber);
     369             :     {
     370             :       // Need to convert {len} to a Number first.
     371         112 :       var_len.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, len));
     372          56 :       Goto(&loop);
     373             :     }
     374             : 
     375          56 :     BIND(&return_len);
     376         112 :     Return(var_len.value());
     377             : 
     378          56 :     BIND(&return_two53minus1);
     379         112 :     Return(NumberConstant(kMaxSafeInteger));
     380             : 
     381          56 :     BIND(&return_zero);
     382         112 :     Return(SmiConstant(0));
     383             :   }
     384          56 : }
     385             : 
     386         168 : TF_BUILTIN(ToInteger, CodeStubAssembler) {
     387             :   Node* context = Parameter(Descriptor::kContext);
     388             :   Node* input = Parameter(Descriptor::kArgument);
     389             : 
     390         112 :   Return(ToInteger(context, input, kNoTruncation));
     391          56 : }
     392             : 
     393         168 : TF_BUILTIN(ToInteger_TruncateMinusZero, CodeStubAssembler) {
     394             :   Node* context = Parameter(Descriptor::kContext);
     395             :   Node* input = Parameter(Descriptor::kArgument);
     396             : 
     397         112 :   Return(ToInteger(context, input, kTruncateMinusZero));
     398          56 : }
     399             : 
     400             : // ES6 section 7.1.13 ToObject (argument)
     401         224 : TF_BUILTIN(ToObject, CodeStubAssembler) {
     402         112 :   Label if_smi(this, Label::kDeferred), if_jsreceiver(this),
     403          56 :       if_noconstructor(this, Label::kDeferred), if_wrapjsvalue(this);
     404             : 
     405             :   Node* context = Parameter(Descriptor::kContext);
     406             :   Node* object = Parameter(Descriptor::kArgument);
     407             : 
     408         112 :   VARIABLE(constructor_function_index_var,
     409             :            MachineType::PointerRepresentation());
     410             : 
     411         112 :   GotoIf(TaggedIsSmi(object), &if_smi);
     412             : 
     413         112 :   Node* map = LoadMap(object);
     414         112 :   Node* instance_type = LoadMapInstanceType(map);
     415         112 :   GotoIf(IsJSReceiverInstanceType(instance_type), &if_jsreceiver);
     416             : 
     417         112 :   Node* constructor_function_index = LoadMapConstructorFunctionIndex(map);
     418         112 :   GotoIf(WordEqual(constructor_function_index,
     419         112 :                    IntPtrConstant(Map::kNoConstructorFunctionIndex)),
     420          56 :          &if_noconstructor);
     421          56 :   constructor_function_index_var.Bind(constructor_function_index);
     422          56 :   Goto(&if_wrapjsvalue);
     423             : 
     424          56 :   BIND(&if_smi);
     425             :   constructor_function_index_var.Bind(
     426         112 :       IntPtrConstant(Context::NUMBER_FUNCTION_INDEX));
     427          56 :   Goto(&if_wrapjsvalue);
     428             : 
     429          56 :   BIND(&if_wrapjsvalue);
     430          56 :   TNode<Context> native_context = LoadNativeContext(context);
     431         112 :   Node* constructor = LoadContextElement(
     432         112 :       native_context, constructor_function_index_var.value());
     433             :   Node* initial_map =
     434             :       LoadObjectField(constructor, JSFunction::kPrototypeOrInitialMapOffset);
     435         112 :   Node* js_value = Allocate(JSValue::kSize);
     436          56 :   StoreMapNoWriteBarrier(js_value, initial_map);
     437             :   StoreObjectFieldRoot(js_value, JSValue::kPropertiesOrHashOffset,
     438          56 :                        RootIndex::kEmptyFixedArray);
     439             :   StoreObjectFieldRoot(js_value, JSObject::kElementsOffset,
     440          56 :                        RootIndex::kEmptyFixedArray);
     441          56 :   StoreObjectField(js_value, JSValue::kValueOffset, object);
     442          56 :   Return(js_value);
     443             : 
     444          56 :   BIND(&if_noconstructor);
     445             :   ThrowTypeError(context, MessageTemplate::kUndefinedOrNullToObject,
     446          56 :                  "ToObject");
     447             : 
     448          56 :   BIND(&if_jsreceiver);
     449          56 :   Return(object);
     450          56 : }
     451             : 
     452             : // ES6 section 12.5.5 typeof operator
     453         168 : TF_BUILTIN(Typeof, CodeStubAssembler) {
     454             :   Node* object = Parameter(Descriptor::kObject);
     455             : 
     456         112 :   Return(Typeof(object));
     457          56 : }
     458             : 
     459             : }  // namespace internal
     460       59480 : }  // namespace v8

Generated by: LCOV version 1.10