blob: 5a2dccd068c817febe27d20cd7e61e45644c0edb [file] [log] [blame]
// 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