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(input: JSAny): Boolean { 49 BranchIfToBooleanIsTrue(input) otherwise return TrueConstant(), 50 return FalseConstant(); 51} 52 53struct ToBooleanForBaselineJumpResult { 54 value: JSAny; 55 is_to_boolean: Smi; 56} 57// ToBoolean for baseline code jumps, which 58// a) returns the original value as the first return value, to avoid needing 59// to save it in the caller, and 60// b) returns the true/false value as a Smi, to make the baseline-side 61// comparison cheaper. 62builtin ToBooleanForBaselineJump(input: JSAny): ToBooleanForBaselineJumpResult { 63 try { 64 BranchIfToBooleanIsTrue(input) otherwise IsTrue, IsFalse; 65 } label IsTrue { 66 return ToBooleanForBaselineJumpResult{value: input, is_to_boolean: 1}; 67 } label IsFalse { 68 return ToBooleanForBaselineJumpResult{value: input, is_to_boolean: 0}; 69 } 70} 71 72transitioning builtin ToLength(implicit context: Context)(input: JSAny): 73 Number { 74 // We might need to loop once for ToNumber conversion. 75 let x: JSAny = input; 76 while (true) { 77 typeswitch (x) { 78 case (s: Smi): { 79 if (s < 0) return 0; 80 return s; 81 } 82 case (h: HeapNumber): { 83 let value: float64 = Convert<float64>(h); 84 // The sense of this test is important for the NaN and -0 cases. 85 if (!(value > 0)) return 0; 86 if (value > kMaxSafeInteger) return kMaxSafeInteger; 87 value = math::Float64Floor(value); 88 return ChangeFloat64ToTagged(value); 89 } 90 case (h: JSAnyNotNumber): { 91 x = ::NonNumberToNumber(h); 92 } 93 } 94 } 95 VerifiedUnreachable(); 96} 97 98transitioning builtin ToName(implicit context: Context)(input: JSAny): Name { 99 // We might need to loop once for ToNumber conversion. 100 let x: JSAny = input; 101 while (true) { 102 typeswitch (x) { 103 case (n: Name): { 104 return n; 105 } 106 case (n: Number): { 107 return ::NumberToString(n); 108 } 109 case (b: BigInt): { 110 // We don't have a fast-path for BigInt currently, so just 111 // tail call to the %ToString runtime function here for now. 112 tail runtime::ToString(context, b); 113 } 114 case (o: Oddball): { 115 return o.to_string; 116 } 117 case (o: JSReceiver): { 118 x = NonPrimitiveToPrimitive_String(o); 119 } 120 } 121 } 122 VerifiedUnreachable(); 123} 124 125const kNoConstructorFunctionIndex: 126 constexpr int31 generates 'Map::kNoConstructorFunctionIndex'; 127 128// ES6 section 7.1.13 ToObject (argument) 129transitioning builtin ToObject(implicit context: Context)(input: JSAny): 130 JSReceiver { 131 try { 132 typeswitch (input) { 133 case (Smi): { 134 goto WrapPrimitive(ContextSlot::NUMBER_FUNCTION_INDEX); 135 } 136 case (o: JSReceiver): { 137 return o; 138 } 139 case (o: JSAnyNotSmi): { 140 const index: intptr = Convert<intptr>( 141 o.map.inobject_properties_start_or_constructor_function_index); 142 if (index != kNoConstructorFunctionIndex) 143 goto WrapPrimitive( 144 %RawDownCast<Slot<NativeContext, JSFunction>>(index)); 145 ThrowTypeError(MessageTemplate::kUndefinedOrNullToObject, 'ToObject'); 146 } 147 } 148 } label WrapPrimitive(constructorIndex: Slot<NativeContext, JSFunction>) { 149 const constructor = *NativeContextSlot(constructorIndex); 150 const map: Map = UnsafeCast<Map>(constructor.prototype_or_initial_map); 151 const wrapper = 152 UnsafeCast<JSPrimitiveWrapper>(AllocateFastOrSlowJSObjectFromMap(map)); 153 wrapper.value = input; 154 return wrapper; 155 } 156} 157 158// ES6 section 7.1.1 ToPrimitive ( input [ , PreferredType ] ) 159 160transitioning macro TryGetExoticToPrimitive(implicit context: Context)( 161 input: JSAny): JSAny labels OrdinaryToPrimitive { 162 // Look up the @@toPrimitive property. 163 const exoticToPrimitive: JSAny = 164 GetProperty(input, ToPrimitiveSymbolConstant()); 165 if (IsNullOrUndefined(exoticToPrimitive)) goto OrdinaryToPrimitive; 166 return exoticToPrimitive; 167} 168 169transitioning macro CallExoticToPrimitive(implicit context: Context)( 170 input: JSAny, exoticToPrimitive: JSAny, hint: String): JSPrimitive { 171 // Invoke the exoticToPrimitive method on the input with a string 172 // representation of the hint. 173 const result: JSAny = Call(context, exoticToPrimitive, input, hint); 174 175 // Verify that the result is primitive. 176 typeswitch (result) { 177 case (o: JSPrimitive): { 178 return o; 179 } 180 case (JSReceiver): { 181 // Somehow the @@toPrimitive method on input didn't yield a primitive. 182 ThrowTypeError(MessageTemplate::kCannotConvertToPrimitive); 183 } 184 } 185} 186 187transitioning builtin NonPrimitiveToPrimitive_Default( 188 implicit context: Context)(input: JSReceiver): JSPrimitive { 189 const exoticToPrimitive: JSAny = TryGetExoticToPrimitive(input) 190 otherwise return OrdinaryToPrimitive_Number(input); 191 return CallExoticToPrimitive( 192 input, exoticToPrimitive, DefaultStringConstant()); 193} 194 195transitioning builtin NonPrimitiveToPrimitive_Number(implicit context: Context)( 196 input: JSReceiver): JSPrimitive { 197 const exoticToPrimitive: JSAny = TryGetExoticToPrimitive(input) 198 otherwise return OrdinaryToPrimitive_Number(input); 199 return CallExoticToPrimitive( 200 input, exoticToPrimitive, NumberStringConstant()); 201} 202 203transitioning builtin NonPrimitiveToPrimitive_String(implicit context: Context)( 204 input: JSReceiver): JSPrimitive { 205 const exoticToPrimitive: JSAny = TryGetExoticToPrimitive(input) 206 otherwise return OrdinaryToPrimitive_String(input); 207 return CallExoticToPrimitive( 208 input, exoticToPrimitive, StringStringConstant()); 209} 210 211// 7.1.1.1 OrdinaryToPrimitive ( O, hint ) 212 213transitioning macro TryToPrimitiveMethod(implicit context: Context)( 214 input: JSAny, name: String): JSPrimitive labels Continue { 215 const method: JSAny = GetProperty(input, name); 216 typeswitch (method) { 217 case (Callable): { 218 const value: JSAny = Call(context, method, input); 219 return Cast<JSPrimitive>(value) otherwise Continue; 220 } 221 case (JSAny): { 222 goto Continue; 223 } 224 } 225} 226 227transitioning builtin OrdinaryToPrimitive_Number(implicit context: Context)( 228 input: JSAny): JSPrimitive { 229 try { 230 return TryToPrimitiveMethod(input, ValueOfStringConstant()) 231 otherwise String; 232 } label String { 233 return TryToPrimitiveMethod(input, ToStringStringConstant()) 234 otherwise Throw; 235 } label Throw { 236 ThrowTypeError(MessageTemplate::kCannotConvertToPrimitive); 237 } 238} 239 240transitioning builtin OrdinaryToPrimitive_String(implicit context: Context)( 241 input: JSAny): JSPrimitive { 242 try { 243 return TryToPrimitiveMethod(input, ToStringStringConstant()) 244 otherwise String; 245 } label String { 246 return TryToPrimitiveMethod(input, ValueOfStringConstant()) otherwise Throw; 247 } label Throw { 248 ThrowTypeError(MessageTemplate::kCannotConvertToPrimitive); 249 } 250} 251 252} // namespace conversion 253