LCOV - code coverage report
Current view: top level - src/builtins - builtins-conversion-gen.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 259 259 100.0 %
Date: 2019-03-21 Functions: 113 113 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         280 :   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         336 :       GetProperty(context, input, factory()->to_primitive_symbol());
      34             : 
      35             :   // Check if {exotic_to_prim} is neither null nor undefined.
      36         336 :   Label ordinary_to_primitive(this);
      37         168 :   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         336 :         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         336 :     Label if_resultisprimitive(this),
      49         336 :         if_resultisnotprimitive(this, Label::kDeferred);
      50         168 :     GotoIf(TaggedIsSmi(result), &if_resultisprimitive);
      51         168 :     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         336 :                        : OrdinaryToPrimitiveHint::kNumber);
      75         168 :     TailCallStub(callable, context, input);
      76             :   }
      77         168 : }
      78             : 
      79         336 : TF_BUILTIN(NonPrimitiveToPrimitive_Default, ConversionBuiltinsAssembler) {
      80          56 :   Node* context = Parameter(Descriptor::kContext);
      81          56 :   Node* input = Parameter(Descriptor::kArgument);
      82             : 
      83          56 :   Generate_NonPrimitiveToPrimitive(context, input, ToPrimitiveHint::kDefault);
      84          56 : }
      85             : 
      86         336 : TF_BUILTIN(NonPrimitiveToPrimitive_Number, ConversionBuiltinsAssembler) {
      87          56 :   Node* context = Parameter(Descriptor::kContext);
      88          56 :   Node* input = Parameter(Descriptor::kArgument);
      89             : 
      90          56 :   Generate_NonPrimitiveToPrimitive(context, input, ToPrimitiveHint::kNumber);
      91          56 : }
      92             : 
      93         336 : TF_BUILTIN(NonPrimitiveToPrimitive_String, ConversionBuiltinsAssembler) {
      94          56 :   Node* context = Parameter(Descriptor::kContext);
      95          56 :   Node* input = Parameter(Descriptor::kArgument);
      96             : 
      97          56 :   Generate_NonPrimitiveToPrimitive(context, input, ToPrimitiveHint::kString);
      98          56 : }
      99             : 
     100         280 : TF_BUILTIN(StringToNumber, CodeStubAssembler) {
     101          56 :   TNode<String> input = CAST(Parameter(Descriptor::kArgument));
     102             : 
     103          56 :   Return(StringToNumber(input));
     104          56 : }
     105             : 
     106         336 : TF_BUILTIN(ToName, CodeStubAssembler) {
     107          56 :   Node* context = Parameter(Descriptor::kContext);
     108          56 :   Node* input = Parameter(Descriptor::kArgument);
     109             : 
     110         112 :   VARIABLE(var_input, MachineRepresentation::kTagged, input);
     111         112 :   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         112 :     Label if_inputisbigint(this), if_inputisname(this), if_inputisnumber(this),
     120         112 :         if_inputisoddball(this), if_inputisreceiver(this, Label::kDeferred);
     121          56 :     GotoIf(TaggedIsSmi(input), &if_inputisnumber);
     122          56 :     Node* input_instance_type = LoadInstanceType(input);
     123             :     STATIC_ASSERT(FIRST_NAME_TYPE == FIRST_TYPE);
     124          56 :     GotoIf(IsNameInstanceType(input_instance_type), &if_inputisname);
     125          56 :     GotoIf(IsJSReceiverInstanceType(input_instance_type), &if_inputisreceiver);
     126          56 :     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         168 :                                  context, input));
     163          56 :       Goto(&loop);
     164             :     }
     165             :   }
     166          56 : }
     167             : 
     168         336 : TF_BUILTIN(NonNumberToNumber, CodeStubAssembler) {
     169          56 :   Node* context = Parameter(Descriptor::kContext);
     170          56 :   Node* input = Parameter(Descriptor::kArgument);
     171             : 
     172          56 :   Return(NonNumberToNumber(context, input));
     173          56 : }
     174             : 
     175         336 : TF_BUILTIN(NonNumberToNumeric, CodeStubAssembler) {
     176          56 :   Node* context = Parameter(Descriptor::kContext);
     177          56 :   Node* input = Parameter(Descriptor::kArgument);
     178             : 
     179          56 :   Return(NonNumberToNumeric(context, input));
     180          56 : }
     181             : 
     182         336 : TF_BUILTIN(ToNumeric, CodeStubAssembler) {
     183          56 :   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
     184          56 :   TNode<Object> input = CAST(Parameter(Descriptor::kArgument));
     185             : 
     186         112 :   Return(Select<Numeric>(
     187         168 :       IsNumber(input), [=] { return CAST(input); },
     188         336 :       [=] { return NonNumberToNumeric(context, CAST(input)); }));
     189          56 : }
     190             : 
     191             : // ES6 section 7.1.3 ToNumber ( argument )
     192         336 : TF_BUILTIN(ToNumber, CodeStubAssembler) {
     193          56 :   Node* context = Parameter(Descriptor::kContext);
     194          56 :   Node* input = Parameter(Descriptor::kArgument);
     195             : 
     196          56 :   Return(ToNumber(context, input));
     197          56 : }
     198             : 
     199             : // Like ToNumber, but also converts BigInts.
     200         336 : TF_BUILTIN(ToNumberConvertBigInt, CodeStubAssembler) {
     201          56 :   Node* context = Parameter(Descriptor::kContext);
     202          56 :   Node* input = Parameter(Descriptor::kArgument);
     203             : 
     204          56 :   Return(ToNumber(context, input, BigIntHandling::kConvertToNumber));
     205          56 : }
     206             : 
     207             : // ES section #sec-tostring-applied-to-the-number-type
     208         280 : TF_BUILTIN(NumberToString, CodeStubAssembler) {
     209          56 :   TNode<Number> input = CAST(Parameter(Descriptor::kArgument));
     210             : 
     211          56 :   Return(NumberToString(input));
     212          56 : }
     213             : 
     214             : // ES section #sec-tostring
     215         336 : TF_BUILTIN(ToString, CodeStubAssembler) {
     216          56 :   Node* context = Parameter(Descriptor::kContext);
     217          56 :   Node* input = Parameter(Descriptor::kArgument);
     218             : 
     219          56 :   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         224 :   Label return_result(this, &var_result);
     227             : 
     228         336 :   Handle<String> method_names[2];
     229         112 :   switch (hint) {
     230             :     case OrdinaryToPrimitiveHint::kNumber:
     231          56 :       method_names[0] = factory()->valueOf_string();
     232          56 :       method_names[1] = factory()->toString_string();
     233          56 :       break;
     234             :     case OrdinaryToPrimitiveHint::kString:
     235          56 :       method_names[0] = factory()->toString_string();
     236          56 :       method_names[1] = factory()->valueOf_string();
     237          56 :       break;
     238             :   }
     239         336 :   for (Handle<String> name : method_names) {
     240             :     // Lookup the {name} on the {input}.
     241         224 :     Node* method = GetProperty(context, input, name);
     242             : 
     243             :     // Check if the {method} is callable.
     244         448 :     Label if_methodiscallable(this),
     245         448 :         if_methodisnotcallable(this, Label::kDeferred);
     246         224 :     GotoIf(TaggedIsSmi(method), &if_methodisnotcallable);
     247         224 :     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         448 :           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         224 :       GotoIf(TaggedIsSmi(result), &return_result);
     261         224 :       Node* result_instance_type = LoadInstanceType(result);
     262         224 :       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         112 :   Return(var_result.value());
     274         112 : }
     275             : 
     276         336 : TF_BUILTIN(OrdinaryToPrimitive_Number, ConversionBuiltinsAssembler) {
     277          56 :   Node* context = Parameter(Descriptor::kContext);
     278          56 :   Node* input = Parameter(Descriptor::kArgument);
     279          56 :   Generate_OrdinaryToPrimitive(context, input,
     280          56 :                                OrdinaryToPrimitiveHint::kNumber);
     281          56 : }
     282             : 
     283         336 : TF_BUILTIN(OrdinaryToPrimitive_String, ConversionBuiltinsAssembler) {
     284          56 :   Node* context = Parameter(Descriptor::kContext);
     285          56 :   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         280 : TF_BUILTIN(ToBoolean, CodeStubAssembler) {
     292          56 :   Node* value = Parameter(Descriptor::kArgument);
     293             : 
     294         112 :   Label return_true(this), return_false(this);
     295          56 :   BranchIfToBooleanIsTrue(value, &return_true, &return_false);
     296             : 
     297          56 :   BIND(&return_true);
     298          56 :   Return(TrueConstant());
     299             : 
     300          56 :   BIND(&return_false);
     301          56 :   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         280 : TF_BUILTIN(ToBooleanLazyDeoptContinuation, CodeStubAssembler) {
     308          56 :   Node* value = Parameter(Descriptor::kArgument);
     309             : 
     310         112 :   Label return_true(this), return_false(this);
     311          56 :   BranchIfToBooleanIsTrue(value, &return_true, &return_false);
     312             : 
     313          56 :   BIND(&return_true);
     314          56 :   Return(TrueConstant());
     315             : 
     316          56 :   BIND(&return_false);
     317          56 :   Return(FalseConstant());
     318          56 : }
     319             : 
     320         336 : TF_BUILTIN(ToLength, CodeStubAssembler) {
     321          56 :   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         112 :   Label loop(this, &var_len);
     327          56 :   Goto(&loop);
     328          56 :   BIND(&loop);
     329             :   {
     330             :     // Shared entry points.
     331         112 :     Label return_len(this), return_two53minus1(this, Label::kDeferred),
     332         112 :         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          56 :     GotoIf(TaggedIsPositiveSmi(len), &return_len);
     339             : 
     340             :     // Check if {len} is a (negative) Smi.
     341          56 :     GotoIf(TaggedIsSmi(len), &return_zero);
     342             : 
     343             :     // Check if {len} is a HeapNumber.
     344         112 :     Label if_lenisheapnumber(this),
     345         112 :         if_lenisnotheapnumber(this, Label::kDeferred);
     346          56 :     Branch(IsHeapNumber(len), &if_lenisheapnumber, &if_lenisnotheapnumber);
     347             : 
     348          56 :     BIND(&if_lenisheapnumber);
     349             :     {
     350             :       // Load the floating-point value of {len}.
     351          56 :       Node* len_value = LoadHeapNumberValue(len);
     352             : 
     353             :       // Check if {len} is not greater than zero.
     354         112 :       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          56 :       Node* value = Float64Floor(len_value);
     364          56 :       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          56 :       var_len.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, len));
     372          56 :       Goto(&loop);
     373             :     }
     374             : 
     375          56 :     BIND(&return_len);
     376          56 :     Return(var_len.value());
     377             : 
     378          56 :     BIND(&return_two53minus1);
     379          56 :     Return(NumberConstant(kMaxSafeInteger));
     380             : 
     381          56 :     BIND(&return_zero);
     382          56 :     Return(SmiConstant(0));
     383             :   }
     384          56 : }
     385             : 
     386         336 : TF_BUILTIN(ToInteger, CodeStubAssembler) {
     387          56 :   Node* context = Parameter(Descriptor::kContext);
     388          56 :   Node* input = Parameter(Descriptor::kArgument);
     389             : 
     390          56 :   Return(ToInteger(context, input, kNoTruncation));
     391          56 : }
     392             : 
     393         336 : TF_BUILTIN(ToInteger_TruncateMinusZero, CodeStubAssembler) {
     394          56 :   Node* context = Parameter(Descriptor::kContext);
     395          56 :   Node* input = Parameter(Descriptor::kArgument);
     396             : 
     397          56 :   Return(ToInteger(context, input, kTruncateMinusZero));
     398          56 : }
     399             : 
     400             : // ES6 section 7.1.13 ToObject (argument)
     401         336 : TF_BUILTIN(ToObject, CodeStubAssembler) {
     402         112 :   Label if_smi(this, Label::kDeferred), if_jsreceiver(this),
     403         112 :       if_noconstructor(this, Label::kDeferred), if_wrapjsvalue(this);
     404             : 
     405          56 :   Node* context = Parameter(Descriptor::kContext);
     406          56 :   Node* object = Parameter(Descriptor::kArgument);
     407             : 
     408         112 :   VARIABLE(constructor_function_index_var,
     409             :            MachineType::PointerRepresentation());
     410             : 
     411          56 :   GotoIf(TaggedIsSmi(object), &if_smi);
     412             : 
     413          56 :   Node* map = LoadMap(object);
     414          56 :   Node* instance_type = LoadMapInstanceType(map);
     415          56 :   GotoIf(IsJSReceiverInstanceType(instance_type), &if_jsreceiver);
     416             : 
     417          56 :   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          56 :   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         168 :       native_context, constructor_function_index_var.value());
     433             :   Node* initial_map =
     434          56 :       LoadObjectField(constructor, JSFunction::kPrototypeOrInitialMapOffset);
     435          56 :   Node* js_value = Allocate(JSValue::kSize);
     436          56 :   StoreMapNoWriteBarrier(js_value, initial_map);
     437          56 :   StoreObjectFieldRoot(js_value, JSValue::kPropertiesOrHashOffset,
     438          56 :                        RootIndex::kEmptyFixedArray);
     439          56 :   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          56 :   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         280 : TF_BUILTIN(Typeof, CodeStubAssembler) {
     454          56 :   Node* object = Parameter(Descriptor::kObject);
     455             : 
     456          56 :   Return(Typeof(object));
     457          56 : }
     458             : 
     459             : }  // namespace internal
     460       87414 : }  // namespace v8

Generated by: LCOV version 1.10