| // Copyright 2020 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| namespace runtime { |
| extern transitioning runtime ToString(Context, BigInt): String; |
| } |
| |
| extern enum OrdinaryToPrimitiveHint { kString, kNumber } |
| |
| extern macro OrdinaryToPrimitive(implicit context: Context)( |
| JSAny, constexpr OrdinaryToPrimitiveHint): JSPrimitive; |
| |
| namespace conversion { |
| |
| builtin StringToNumber(implicit context: Context)(input: String): Number { |
| return ::StringToNumber(input); |
| } |
| |
| transitioning builtin NonNumberToNumber(implicit context: Context)( |
| input: JSAnyNotNumber): Number { |
| return ::NonNumberToNumber(input); |
| } |
| |
| transitioning builtin NonNumberToNumeric(implicit context: Context)( |
| input: JSAnyNotNumber): Numeric { |
| return ::NonNumberToNumeric(input); |
| } |
| |
| transitioning builtin ToNumeric(implicit context: Context)(input: JSAny): |
| Numeric { |
| typeswitch (input) { |
| case (n: Number): { |
| return n; |
| } |
| case (h: JSAnyNotNumber): { |
| return conversion::NonNumberToNumeric(h); |
| } |
| } |
| } |
| |
| // ES section #sec-tostring-applied-to-the-number-type |
| builtin NumberToString(implicit context: Context)(input: Number): String { |
| return ::NumberToString(input); |
| } |
| |
| // ES6 section 7.1.2 ToBoolean ( argument ) |
| builtin ToBoolean(implicit context: Context)(input: JSAny): Boolean { |
| BranchIfToBooleanIsTrue(input) otherwise return TrueConstant(), |
| return FalseConstant(); |
| } |
| |
| transitioning builtin ToLength(implicit context: Context)(input: JSAny): |
| Number { |
| // We might need to loop once for ToNumber conversion. |
| let x: JSAny = input; |
| while (true) { |
| typeswitch (x) { |
| case (s: Smi): { |
| if (s < 0) return 0; |
| return s; |
| } |
| case (h: HeapNumber): { |
| let value: float64 = Convert<float64>(h); |
| // The sense of this test is important for the NaN and -0 cases. |
| if (!(value > 0)) return 0; |
| if (value > kMaxSafeInteger) return kMaxSafeInteger; |
| value = math::Float64Floor(value); |
| return ChangeFloat64ToTagged(value); |
| } |
| case (h: JSAnyNotNumber): { |
| x = ::NonNumberToNumber(h); |
| } |
| } |
| } |
| VerifiedUnreachable(); |
| } |
| |
| transitioning builtin ToName(implicit context: Context)(input: JSAny): Name { |
| // We might need to loop once for ToNumber conversion. |
| let x: JSAny = input; |
| while (true) { |
| typeswitch (x) { |
| case (n: Name): { |
| return n; |
| } |
| case (n: Number): { |
| return ::NumberToString(n); |
| } |
| case (b: BigInt): { |
| // We don't have a fast-path for BigInt currently, so just |
| // tail call to the %ToString runtime function here for now. |
| tail runtime::ToString(context, b); |
| } |
| case (o: Oddball): { |
| return o.to_string; |
| } |
| case (o: JSReceiver): { |
| x = NonPrimitiveToPrimitive_String(o); |
| } |
| } |
| } |
| VerifiedUnreachable(); |
| } |
| |
| const kNoConstructorFunctionIndex: |
| constexpr int31 generates 'Map::kNoConstructorFunctionIndex'; |
| |
| // ES6 section 7.1.13 ToObject (argument) |
| transitioning builtin ToObject(implicit context: Context)(input: JSAny): |
| JSReceiver { |
| try { |
| typeswitch (input) { |
| case (Smi): { |
| goto WrapPrimitive(ContextSlot::NUMBER_FUNCTION_INDEX); |
| } |
| case (o: JSReceiver): { |
| return o; |
| } |
| case (o: JSAnyNotSmi): { |
| const index: intptr = Convert<intptr>( |
| o.map.in_object_properties_start_or_constructor_function_index); |
| if (index != kNoConstructorFunctionIndex) |
| goto WrapPrimitive( |
| %RawDownCast<Slot<NativeContext, JSFunction>>(index)); |
| ThrowTypeError(MessageTemplate::kUndefinedOrNullToObject, 'ToObject'); |
| } |
| } |
| } label WrapPrimitive(constructorIndex: Slot<NativeContext, JSFunction>) { |
| const constructor = *NativeContextSlot(constructorIndex); |
| const map: Map = UnsafeCast<Map>(constructor.prototype_or_initial_map); |
| const wrapper = |
| UnsafeCast<JSPrimitiveWrapper>(AllocateFastOrSlowJSObjectFromMap(map)); |
| wrapper.value = input; |
| return wrapper; |
| } |
| } |
| |
| // ES6 section 7.1.1 ToPrimitive ( input [ , PreferredType ] ) |
| |
| transitioning macro TryGetExoticToPrimitive(implicit context: Context)( |
| input: JSAny): JSAny labels OrdinaryToPrimitive { |
| // Look up the @@toPrimitive property. |
| const exoticToPrimitive: JSAny = |
| GetProperty(input, ToPrimitiveSymbolConstant()); |
| if (IsNullOrUndefined(exoticToPrimitive)) goto OrdinaryToPrimitive; |
| return exoticToPrimitive; |
| } |
| |
| transitioning macro CallExoticToPrimitive(implicit context: Context)( |
| input: JSAny, exoticToPrimitive: JSAny, hint: String): JSPrimitive { |
| // Invoke the exoticToPrimitive method on the input with a string |
| // representation of the hint. |
| const result: JSAny = Call(context, exoticToPrimitive, input, hint); |
| |
| // Verify that the result is primitive. |
| typeswitch (result) { |
| case (o: JSPrimitive): { |
| return o; |
| } |
| case (JSReceiver): { |
| // Somehow the @@toPrimitive method on input didn't yield a primitive. |
| ThrowTypeError(MessageTemplate::kCannotConvertToPrimitive); |
| } |
| } |
| } |
| |
| transitioning builtin NonPrimitiveToPrimitive_Default( |
| implicit context: Context)(input: JSReceiver): JSPrimitive { |
| const exoticToPrimitive: JSAny = TryGetExoticToPrimitive(input) |
| otherwise return OrdinaryToPrimitive_Number(input); |
| return CallExoticToPrimitive( |
| input, exoticToPrimitive, DefaultStringConstant()); |
| } |
| |
| transitioning builtin NonPrimitiveToPrimitive_Number(implicit context: Context)( |
| input: JSReceiver): JSPrimitive { |
| const exoticToPrimitive: JSAny = TryGetExoticToPrimitive(input) |
| otherwise return OrdinaryToPrimitive_Number(input); |
| return CallExoticToPrimitive( |
| input, exoticToPrimitive, NumberStringConstant()); |
| } |
| |
| transitioning builtin NonPrimitiveToPrimitive_String(implicit context: Context)( |
| input: JSReceiver): JSPrimitive { |
| const exoticToPrimitive: JSAny = TryGetExoticToPrimitive(input) |
| otherwise return OrdinaryToPrimitive_String(input); |
| return CallExoticToPrimitive( |
| input, exoticToPrimitive, StringStringConstant()); |
| } |
| |
| // 7.1.1.1 OrdinaryToPrimitive ( O, hint ) |
| |
| transitioning macro TryToPrimitiveMethod(implicit context: Context)( |
| input: JSAny, name: String): JSPrimitive labels Continue { |
| const method: JSAny = GetProperty(input, name); |
| typeswitch (method) { |
| case (Callable): { |
| const value: JSAny = Call(context, method, input); |
| return Cast<JSPrimitive>(value) otherwise Continue; |
| } |
| case (JSAny): { |
| goto Continue; |
| } |
| } |
| } |
| |
| transitioning builtin OrdinaryToPrimitive_Number(implicit context: Context)( |
| input: JSAny): JSPrimitive { |
| try { |
| return TryToPrimitiveMethod(input, ValueOfStringConstant()) |
| otherwise String; |
| } label String { |
| return TryToPrimitiveMethod(input, ToStringStringConstant()) |
| otherwise Throw; |
| } label Throw { |
| ThrowTypeError(MessageTemplate::kCannotConvertToPrimitive); |
| } |
| } |
| |
| transitioning builtin OrdinaryToPrimitive_String(implicit context: Context)( |
| input: JSAny): JSPrimitive { |
| try { |
| return TryToPrimitiveMethod(input, ToStringStringConstant()) |
| otherwise String; |
| } label String { |
| return TryToPrimitiveMethod(input, ValueOfStringConstant()) otherwise Throw; |
| } label Throw { |
| ThrowTypeError(MessageTemplate::kCannotConvertToPrimitive); |
| } |
| } |
| |
| } // namespace conversion |