1// Copyright 2020 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 5namespace runtime { 6extern transitioning runtime ToString(Context, BigInt): String; 7} 8 9extern enum OrdinaryToPrimitiveHint { kString, kNumber } 10 11extern macro OrdinaryToPrimitive(implicit context: Context)( 12 JSAny, constexpr OrdinaryToPrimitiveHint): JSPrimitive; 13 14namespace conversion { 15 16builtin StringToNumber(implicit context: Context)(input: String): Number { 17 return ::StringToNumber(input); 18} 19 20transitioning builtin NonNumberToNumber(implicit context: Context)( 21 input: JSAnyNotNumber): Number { 22 return ::NonNumberToNumber(input); 23} 24 25transitioning builtin NonNumberToNumeric(implicit context: Context)( 26 input: JSAnyNotNumber): Numeric { 27 return ::NonNumberToNumeric(input); 28} 29 30transitioning builtin ToNumeric(implicit context: Context)(input: JSAny): 31 Numeric { 32 typeswitch (input) { 33 case (n: Number): { 34 return n; 35 } 36 case (h: JSAnyNotNumber): { 37 return conversion::NonNumberToNumeric(h); 38 } 39 } 40} 41 42// ES section #sec-tostring-applied-to-the-number-type 43builtin NumberToString(implicit context: Context)(input: Number): String { 44 return ::NumberToString(input); 45} 46 47// ES6 section 7.1.2 ToBoolean ( argument ) 48builtin ToBoolean(implicit context: Context)(input: JSAny): Boolean { 49 BranchIfToBooleanIsTrue(input) otherwise return TrueConstant(), 50 return FalseConstant(); 51} 52 53transitioning builtin ToLength(implicit context: Context)(input: JSAny): 54 Number { 55 // We might need to loop once for ToNumber conversion. 56 let x: JSAny = input; 57 while (true) { 58 typeswitch (x) { 59 case (s: Smi): { 60 if (s < 0) return 0; 61 return s; 62 } 63 case (h: HeapNumber): { 64 let value: float64 = Convert<float64>(h); 65 // The sense of this test is important for the NaN and -0 cases. 66 if (!(value > 0)) return 0; 67 if (value > kMaxSafeInteger) return kMaxSafeInteger; 68 value = math::Float64Floor(value); 69 return ChangeFloat64ToTagged(value); 70 } 71 case (h: JSAnyNotNumber): { 72 x = ::NonNumberToNumber(h); 73 } 74 } 75 } 76 VerifiedUnreachable(); 77} 78 79transitioning builtin ToName(implicit context: Context)(input: JSAny): Name { 80 // We might need to loop once for ToNumber conversion. 81 let x: JSAny = input; 82 while (true) { 83 typeswitch (x) { 84 case (n: Name): { 85 return n; 86 } 87 case (n: Number): { 88 return ::NumberToString(n); 89 } 90 case (b: BigInt): { 91 // We don't have a fast-path for BigInt currently, so just 92 // tail call to the %ToString runtime function here for now. 93 tail runtime::ToString(context, b); 94 } 95 case (o: Oddball): { 96 return o.to_string; 97 } 98 case (o: JSReceiver): { 99 x = NonPrimitiveToPrimitive_String(o); 100 } 101 } 102 } 103 VerifiedUnreachable(); 104} 105 106const kNoConstructorFunctionIndex: 107 constexpr int31 generates 'Map::kNoConstructorFunctionIndex'; 108 109// ES6 section 7.1.13 ToObject (argument) 110transitioning builtin ToObject(implicit context: Context)(input: JSAny): 111 JSReceiver { 112 try { 113 typeswitch (input) { 114 case (Smi): { 115 goto WrapPrimitive(ContextSlot::NUMBER_FUNCTION_INDEX); 116 } 117 case (o: JSReceiver): { 118 return o; 119 } 120 case (o: JSAnyNotSmi): { 121 const index: intptr = Convert<intptr>( 122 o.map.in_object_properties_start_or_constructor_function_index); 123 if (index != kNoConstructorFunctionIndex) 124 goto WrapPrimitive( 125 %RawDownCast<Slot<NativeContext, JSFunction>>(index)); 126 ThrowTypeError(MessageTemplate::kUndefinedOrNullToObject, 'ToObject'); 127 } 128 } 129 } label WrapPrimitive(constructorIndex: Slot<NativeContext, JSFunction>) { 130 const constructor = *NativeContextSlot(constructorIndex); 131 const map: Map = UnsafeCast<Map>(constructor.prototype_or_initial_map); 132 const wrapper = 133 UnsafeCast<JSPrimitiveWrapper>(AllocateFastOrSlowJSObjectFromMap(map)); 134 wrapper.value = input; 135 return wrapper; 136 } 137} 138 139// ES6 section 7.1.1 ToPrimitive ( input [ , PreferredType ] ) 140 141transitioning macro TryGetExoticToPrimitive(implicit context: Context)( 142 input: JSAny): JSAny labels OrdinaryToPrimitive { 143 // Look up the @@toPrimitive property. 144 const exoticToPrimitive: JSAny = 145 GetProperty(input, ToPrimitiveSymbolConstant()); 146 if (IsNullOrUndefined(exoticToPrimitive)) goto OrdinaryToPrimitive; 147 return exoticToPrimitive; 148} 149 150transitioning macro CallExoticToPrimitive(implicit context: Context)( 151 input: JSAny, exoticToPrimitive: JSAny, hint: String): JSPrimitive { 152 // Invoke the exoticToPrimitive method on the input with a string 153 // representation of the hint. 154 const result: JSAny = Call(context, exoticToPrimitive, input, hint); 155 156 // Verify that the result is primitive. 157 typeswitch (result) { 158 case (o: JSPrimitive): { 159 return o; 160 } 161 case (JSReceiver): { 162 // Somehow the @@toPrimitive method on input didn't yield a primitive. 163 ThrowTypeError(MessageTemplate::kCannotConvertToPrimitive); 164 } 165 } 166} 167 168transitioning builtin NonPrimitiveToPrimitive_Default( 169 implicit context: Context)(input: JSReceiver): JSPrimitive { 170 const exoticToPrimitive: JSAny = TryGetExoticToPrimitive(input) 171 otherwise return OrdinaryToPrimitive_Number(input); 172 return CallExoticToPrimitive( 173 input, exoticToPrimitive, DefaultStringConstant()); 174} 175 176transitioning builtin NonPrimitiveToPrimitive_Number(implicit context: Context)( 177 input: JSReceiver): JSPrimitive { 178 const exoticToPrimitive: JSAny = TryGetExoticToPrimitive(input) 179 otherwise return OrdinaryToPrimitive_Number(input); 180 return CallExoticToPrimitive( 181 input, exoticToPrimitive, NumberStringConstant()); 182} 183 184transitioning builtin NonPrimitiveToPrimitive_String(implicit context: Context)( 185 input: JSReceiver): JSPrimitive { 186 const exoticToPrimitive: JSAny = TryGetExoticToPrimitive(input) 187 otherwise return OrdinaryToPrimitive_String(input); 188 return CallExoticToPrimitive( 189 input, exoticToPrimitive, StringStringConstant()); 190} 191 192// 7.1.1.1 OrdinaryToPrimitive ( O, hint ) 193 194transitioning macro TryToPrimitiveMethod(implicit context: Context)( 195 input: JSAny, name: String): JSPrimitive labels Continue { 196 const method: JSAny = GetProperty(input, name); 197 typeswitch (method) { 198 case (Callable): { 199 const value: JSAny = Call(context, method, input); 200 return Cast<JSPrimitive>(value) otherwise Continue; 201 } 202 case (JSAny): { 203 goto Continue; 204 } 205 } 206} 207 208transitioning builtin OrdinaryToPrimitive_Number(implicit context: Context)( 209 input: JSAny): JSPrimitive { 210 try { 211 return TryToPrimitiveMethod(input, ValueOfStringConstant()) 212 otherwise String; 213 } label String { 214 return TryToPrimitiveMethod(input, ToStringStringConstant()) 215 otherwise Throw; 216 } label Throw { 217 ThrowTypeError(MessageTemplate::kCannotConvertToPrimitive); 218 } 219} 220 221transitioning builtin OrdinaryToPrimitive_String(implicit context: Context)( 222 input: JSAny): JSPrimitive { 223 try { 224 return TryToPrimitiveMethod(input, ToStringStringConstant()) 225 otherwise String; 226 } label String { 227 return TryToPrimitiveMethod(input, ValueOfStringConstant()) otherwise Throw; 228 } label Throw { 229 ThrowTypeError(MessageTemplate::kCannotConvertToPrimitive); 230 } 231} 232 233} // namespace conversion 234