using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using Jint.Native;
using Jint.Native.Number;
using Jint.Native.Object;
using Jint.Native.String;
using Jint.Native.Symbol;
using Jint.Runtime.Interop;
using Jint.Extensions;
namespace Jint.Runtime;
public static class TypeConverter
{
// how many decimals to check when determining if double is actually an int
private const double DoubleIsIntegerTolerance = double.Epsilon * 100;
private static readonly string[] intToString = new string[1024];
private static readonly string[] charToString = new string[256];
static TypeConverter()
{
for (var i = 0; i < intToString.Length; ++i)
{
intToString[i] = i.ToString(CultureInfo.InvariantCulture);
}
for (var i = 0; i < charToString.Length; ++i)
{
var c = (char) i;
charToString[i] = c.ToString();
}
}
///
/// https://tc39.es/ecma262/#sec-toprimitive
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static JsValue ToPrimitive(JsValue input, Types preferredType = Types.Empty)
{
return input is not ObjectInstance oi
? input
: ToPrimitiveObjectInstance(oi, preferredType);
}
private static JsValue ToPrimitiveObjectInstance(ObjectInstance oi, Types preferredType)
{
var exoticToPrim = oi.GetMethod(GlobalSymbolRegistry.ToPrimitive);
if (exoticToPrim is not null)
{
var hint = preferredType switch
{
Types.String => JsString.StringString,
Types.Number => JsString.NumberString,
_ => JsString.DefaultString
};
var str = exoticToPrim.Call(oi, hint);
if (str.IsPrimitive())
{
return str;
}
if (str.IsObject())
{
Throw.TypeError(oi.Engine.Realm, "Cannot convert object to primitive value");
}
}
return OrdinaryToPrimitive(oi, preferredType == Types.Empty ? Types.Number : preferredType);
}
///
/// https://tc39.es/ecma262/#sec-ordinarytoprimitive
///
internal static JsValue OrdinaryToPrimitive(ObjectInstance input, Types hint = Types.Empty)
{
JsString property1;
JsString property2;
if (hint == Types.String)
{
property1 = (JsString) "toString";
property2 = (JsString) "valueOf";
}
else if (hint == Types.Number)
{
property1 = (JsString) "valueOf";
property2 = (JsString) "toString";
}
else
{
Throw.TypeError(input.Engine.Realm);
return null;
}
if (input.Get(property1) is ICallable method1)
{
var val = method1.Call(input, Arguments.Empty);
if (val.IsPrimitive())
{
return val;
}
}
if (input.Get(property2) is ICallable method2)
{
var val = method2.Call(input, Arguments.Empty);
if (val.IsPrimitive())
{
return val;
}
}
Throw.TypeError(input.Engine.Realm);
return null;
}
///
/// https://tc39.es/ecma262/#sec-toboolean
///
public static bool ToBoolean(JsValue o) => o.ToBoolean();
///
/// https://tc39.es/ecma262/#sec-tonumeric
///
public static JsValue ToNumeric(JsValue value)
{
if (value.IsNumber() || value.IsBigInt())
{
return value;
}
var primValue = ToPrimitive(value, Types.Number);
if (primValue.IsBigInt())
{
return primValue;
}
return ToNumber(primValue);
}
///
/// https://tc39.es/ecma262/#sec-tonumber
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double ToNumber(JsValue o)
{
return o.IsNumber()
? ((JsNumber) o)._value
: ToNumberUnlikely(o);
}
private static double ToNumberUnlikely(JsValue o)
{
var type = o._type & ~InternalTypes.InternalFlags;
switch (type)
{
case InternalTypes.Undefined:
return double.NaN;
case InternalTypes.Null:
return 0;
case InternalTypes.Boolean:
return ((JsBoolean) o)._value ? 1 : 0;
case InternalTypes.String:
return ToNumber(o.ToString());
case InternalTypes.Symbol:
case InternalTypes.BigInt:
case InternalTypes.Empty:
// TODO proper TypeError would require Engine instance and a lot of API changes
Throw.TypeErrorNoEngine("Cannot convert a " + type + " value to a number");
return 0;
default:
return ToNumber(ToPrimitive(o, Types.Number));
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static JsNumber ToJsNumber(JsValue o)
{
return o.IsNumber() ? (JsNumber) o : ToJsNumberUnlikely(o);
}
private static JsNumber ToJsNumberUnlikely(JsValue o)
{
var type = o._type & ~InternalTypes.InternalFlags;
switch (type)
{
case InternalTypes.Undefined:
return JsNumber.DoubleNaN;
case InternalTypes.Null:
return JsNumber.PositiveZero;
case InternalTypes.Boolean:
return ((JsBoolean) o)._value ? JsNumber.PositiveOne : JsNumber.PositiveZero;
case InternalTypes.String:
return new JsNumber(ToNumber(o.ToString()));
case InternalTypes.Symbol:
case InternalTypes.BigInt:
case InternalTypes.Empty:
// TODO proper TypeError would require Engine instance and a lot of API changes
Throw.TypeErrorNoEngine("Cannot convert a " + type + " value to a number");
return JsNumber.PositiveZero;
default:
return new JsNumber(ToNumber(ToPrimitive(o, Types.Number)));
}
}
private static double ToNumber(string input)
{
if (string.IsNullOrWhiteSpace(input))
{
return 0;
}
var firstChar = input[0];
if (input.Length == 1)
{
return firstChar is >= '0' and <= '9' ? firstChar - '0' : double.NaN;
}
input = StringPrototype.TrimEx(input);
firstChar = input[0];
const NumberStyles NumberStyles = NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign |
NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite |
NumberStyles.AllowExponent;
if (long.TryParse(input, NumberStyles, CultureInfo.InvariantCulture, out var longValue))
{
return longValue == 0 && firstChar == '-' ? -0.0 : longValue;
}
if (input.Length is 8 or 9)
{
switch (input)
{
case "+Infinity":
case "Infinity":
return double.PositiveInfinity;
case "-Infinity":
return double.NegativeInfinity;
}
if (input.EndsWith("infinity", StringComparison.OrdinalIgnoreCase))
{
// we don't accept other that case-sensitive
return double.NaN;
}
}
if (input.Length > 2 && firstChar == '0' && char.IsLetter(input[1]))
{
var fromBase = input[1] switch
{
'x' or 'X' => 16,
'o' or 'O' => 8,
'b' or 'B' => 2,
_ => 0
};
if (fromBase > 0)
{
try
{
return Convert.ToInt32(input.Substring(2), fromBase);
}
catch
{
return double.NaN;
}
}
}
#if NETFRAMEWORK
// if we are on full framework, one extra check for whether it was actually over the bounds of double
// in modern NET parsing was fixed to be IEEE 754 compliant, full framework is not and cannot detect positive infinity
try
{
var targetString = firstChar == '-' ? input.Substring(1) : input;
var n = double.Parse(targetString, NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent, CultureInfo.InvariantCulture);
if (n == 0 && firstChar == '-')
{
return -0.0;
}
return firstChar == '-' ? -1 * n : n;
}
catch (Exception e) when (e is OverflowException)
{
return firstChar == '-' ? double.NegativeInfinity : double.PositiveInfinity;
}
catch
{
return double.NaN;
}
#else
if (double.TryParse(input, NumberStyles, CultureInfo.InvariantCulture, out var n))
{
return n == 0 && firstChar == '-' ? -0.0 : n;
}
return double.NaN;
#endif
}
///
/// https://tc39.es/ecma262/#sec-tolength
///
public static ulong ToLength(JsValue o)
{
var len = ToInteger(o);
if (len <= 0)
{
return 0;
}
return (ulong) Math.Min(len, NumberConstructor.MaxSafeInteger);
}
///
/// https://tc39.es/ecma262/#sec-tointegerorinfinity
///
public static double ToIntegerOrInfinity(JsValue argument)
{
var number = ToNumber(argument);
if (double.IsNaN(number) || number == 0)
{
return 0;
}
if (double.IsInfinity(number))
{
return number;
}
var integer = (long) Math.Floor(Math.Abs(number));
if (number < 0)
{
integer *= -1;
}
return integer;
}
///
/// https://tc39.es/ecma262/#sec-tointeger
///
public static double ToInteger(JsValue o)
{
return ToInteger(ToNumber(o));
}
///
/// https://tc39.es/ecma262/#sec-tointeger
///
internal static double ToInteger(double number)
{
if (double.IsNaN(number))
{
return 0;
}
if (number == 0 || double.IsInfinity(number))
{
return number;
}
if (number is >= long.MinValue and <= long.MaxValue)
{
return (long) number;
}
var integer = Math.Floor(Math.Abs(number));
if (number < 0)
{
integer *= -1;
}
return integer;
}
internal static int DoubleToInt32Slow(double o)
{
// Computes the integral value of the number mod 2^32.
var doubleBits = BitConverter.DoubleToInt64Bits(o);
var sign = (int) (doubleBits >> 63); // 0 if positive, -1 if negative
var exponent = (int) ((doubleBits >> 52) & 0x7FF) - 1023;
if ((uint) exponent >= 84)
{
// Anything with an exponent that is negative or >= 84 will convert to zero.
// This includes infinities and NaNs, which have exponent = 1024
// The 84 comes from 52 (bits in double mantissa) + 32 (bits in integer)
return 0;
}
var mantissa = (doubleBits & 0xFFFFFFFFFFFFFL) | 0x10000000000000L;
var int32Value = exponent >= 52 ? (int) (mantissa << (exponent - 52)) : (int) (mantissa >> (52 - exponent));
return (int32Value + sign) ^ sign;
}
///
/// http://www.ecma-international.org/ecma-262/5.1/#sec-9.5
///
public static int ToInt32(JsValue o)
{
if (o._type == InternalTypes.Integer)
{
return o.AsInteger();
}
var doubleVal = ToNumber(o);
if (doubleVal >= -(double) int.MinValue && doubleVal <= int.MaxValue)
{
// Double-to-int cast is correct in this range
return (int) doubleVal;
}
return DoubleToInt32Slow(doubleVal);
}
///
/// https://tc39.es/ecma262/#sec-touint32
///
public static uint ToUint32(JsValue o)
{
if (o._type == InternalTypes.Integer)
{
return (uint) o.AsInteger();
}
var doubleVal = ToNumber(o);
if (doubleVal is >= 0.0 and <= uint.MaxValue)
{
// Double-to-uint cast is correct in this range
return (uint) doubleVal;
}
return (uint) DoubleToInt32Slow(doubleVal);
}
///
/// https://tc39.es/ecma262/#sec-touint16
///
public static ushort ToUint16(JsValue o)
{
if (o._type == InternalTypes.Integer)
{
var integer = o.AsInteger();
if (integer is >= 0 and <= ushort.MaxValue)
{
return (ushort) integer;
}
}
var number = ToNumber(o);
if (double.IsNaN(number) || number == 0 || double.IsInfinity(number))
{
return 0;
}
var intValue = Math.Floor(Math.Abs(number));
if (number < 0)
{
intValue *= -1;
}
var int16Bit = intValue % 65_536; // 2^16
return (ushort) int16Bit;
}
///
/// https://tc39.es/ecma262/#sec-toint16
///
internal static double ToInt16(JsValue o)
{
return o._type == InternalTypes.Integer
? (short) o.AsInteger()
: (short) (long) ToNumber(o);
}
///
/// https://tc39.es/ecma262/#sec-toint8
///
internal static double ToInt8(JsValue o)
{
return o._type == InternalTypes.Integer
? (sbyte) o.AsInteger()
: (sbyte) (long) ToNumber(o);
}
///
/// https://tc39.es/ecma262/#sec-touint8
///
internal static double ToUint8(JsValue o)
{
return o._type == InternalTypes.Integer
? (byte) o.AsInteger()
: (byte) (long) ToNumber(o);
}
///
/// https://tc39.es/ecma262/#sec-touint8clamp
///
internal static byte ToUint8Clamp(JsValue o)
{
if (o._type == InternalTypes.Integer)
{
var intValue = o.AsInteger();
if (intValue is > -1 and < 256)
{
return (byte) intValue;
}
}
return ToUint8ClampUnlikely(o);
}
private static byte ToUint8ClampUnlikely(JsValue o)
{
var number = ToNumber(o);
if (double.IsNaN(number))
{
return 0;
}
if (number <= 0)
{
return 0;
}
if (number >= 255)
{
return 255;
}
var f = Math.Floor(number);
if (f + 0.5 < number)
{
return (byte) (f + 1);
}
if (number < f + 0.5)
{
return (byte) f;
}
if (f % 2 != 0)
{
return (byte) (f + 1);
}
return (byte) f;
}
///
/// https://tc39.es/ecma262/#sec-tobigint
///
public static BigInteger ToBigInt(JsValue value)
{
return value is JsBigInt bigInt
? bigInt._value
: ToBigIntUnlikely(value);
}
private static BigInteger ToBigIntUnlikely(JsValue value)
{
var prim = ToPrimitive(value, Types.Number);
switch (prim.Type)
{
case Types.BigInt:
return ((JsBigInt) prim)._value;
case Types.Boolean:
return ((JsBoolean) prim)._value ? BigInteger.One : BigInteger.Zero;
case Types.String:
return StringToBigInt(prim.ToString());
default:
Throw.TypeErrorNoEngine("Cannot convert a " + prim.Type + " to a BigInt");
return BigInteger.One;
}
}
public static JsBigInt ToJsBigInt(JsValue value)
{
return value as JsBigInt ?? ToJsBigIntUnlikely(value);
}
private static JsBigInt ToJsBigIntUnlikely(JsValue value)
{
var prim = ToPrimitive(value, Types.Number);
switch (prim.Type)
{
case Types.BigInt:
return (JsBigInt) prim;
case Types.Boolean:
return ((JsBoolean) prim)._value ? JsBigInt.One : JsBigInt.Zero;
case Types.String:
return new JsBigInt(StringToBigInt(prim.ToString()));
default:
Throw.TypeErrorNoEngine("Cannot convert a " + prim.Type + " to a BigInt");
return JsBigInt.One;
}
}
internal static BigInteger StringToBigInt(string str)
{
if (!TryStringToBigInt(str, out var result))
{
// TODO: this doesn't seem a JS syntax error, use a dedicated exception type?
throw new SyntaxError("CannotConvertToBigInt", " Cannot convert " + str + " to a BigInt").ToException();
}
return result;
}
internal static bool TryStringToBigInt(string str, out BigInteger result)
{
if (string.IsNullOrWhiteSpace(str))
{
result = BigInteger.Zero;
return true;
}
str = str.Trim();
for (var i = 0; i < str.Length; i++)
{
var c = str[i];
if (!char.IsDigit(c))
{
if (i == 0 && (c == '-' || Character.IsDecimalDigit(c)))
{
// ok
continue;
}
if (i != 1 && Character.IsHexDigit(c))
{
// ok
continue;
}
if (i == 1 && (Character.IsDecimalDigit(c) || c is 'x' or 'X' or 'b' or 'B' or 'o' or 'O'))
{
// allowed, can be probably parsed
continue;
}
result = default;
return false;
}
}
// check if we can get by using plain parsing
if (BigInteger.TryParse(str, NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
{
return true;
}
if (str.Length > 2)
{
if (str.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
// we get better precision if we don't hit floating point parsing that is performed by Esprima
#if SUPPORTS_SPAN_PARSE
var source = str.AsSpan(2);
#else
var source = str.Substring(2);
#endif
var c = source[0];
if (c > 7 && Character.IsHexDigit(c))
{
// ensure we get positive number
source = "0" + source.ToString();
}
if (BigInteger.TryParse(source, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out result))
{
return true;
}
}
else if (str.StartsWith("0o", StringComparison.OrdinalIgnoreCase) && Character.IsOctalDigit(str[2]))
{
// try parse large octal
var bigInteger = new BigInteger();
for (var i = 2; i < str.Length; i++)
{
var c = str[i];
if (!Character.IsHexDigit(c))
{
return false;
}
bigInteger = bigInteger * 8 + c - '0';
}
result = bigInteger;
return true;
}
else if (str.StartsWith("0b", StringComparison.OrdinalIgnoreCase) && Character.IsDecimalDigit(str[2]))
{
// try parse large binary
var bigInteger = new BigInteger();
for (var i = 2; i < str.Length; i++)
{
var c = str[i];
if (c != '0' && c != '1')
{
// not good
return false;
}
bigInteger <<= 1;
bigInteger += c == '1' ? 1 : 0;
}
result = bigInteger;
return true;
}
}
return false;
}
///
/// https://tc39.es/ecma262/#sec-tobigint64
///
internal static long ToBigInt64(BigInteger value)
{
var int64bit = BigIntegerModulo(value, BigInteger.Pow(2, 64));
if (int64bit >= BigInteger.Pow(2, 63))
{
return (long) (int64bit - BigInteger.Pow(2, 64));
}
return (long) int64bit;
}
///
/// https://tc39.es/ecma262/#sec-tobiguint64
///
internal static ulong ToBigUint64(BigInteger value)
{
return (ulong) BigIntegerModulo(value, BigInteger.Pow(2, 64));
}
///
/// Implements the JS spec modulo operation as expected.
///
internal static BigInteger BigIntegerModulo(BigInteger a, BigInteger n)
{
return (a %= n) < 0 && n > 0 || a > 0 && n < 0 ? a + n : a;
}
///
/// https://tc39.es/ecma262/#sec-canonicalnumericindexstring
///
internal static double? CanonicalNumericIndexString(JsValue value)
{
if (value is JsNumber jsNumber)
{
return jsNumber._value;
}
if (value is JsString jsString)
{
if (string.Equals(jsString.ToString(), "-0", StringComparison.Ordinal))
{
return JsNumber.NegativeZero._value;
}
var n = ToNumber(value);
if (!JsValue.SameValue(ToString(n), value))
{
return null;
}
return n;
}
return null;
}
///
/// https://tc39.es/ecma262/#sec-toindex
///
public static uint ToIndex(Realm realm, JsValue value)
{
if (value.IsUndefined())
{
return 0;
}
var integerIndex = ToIntegerOrInfinity(value);
if (integerIndex < 0)
{
Throw.RangeError(realm);
}
var index = ToLength(integerIndex);
if (integerIndex != index)
{
Throw.RangeError(realm, "Invalid index");
}
return (uint) Math.Min(uint.MaxValue, index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static string ToString(long i)
{
var temp = intToString;
return (ulong) i < (ulong) temp.Length
? temp[i]
: i.ToString(CultureInfo.InvariantCulture);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static string ToString(int i)
{
var temp = intToString;
return (uint) i < (uint) temp.Length
? temp[i]
: i.ToString(CultureInfo.InvariantCulture);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static string ToString(uint i)
{
var temp = intToString;
return i < (uint) temp.Length
? temp[i]
: i.ToString(CultureInfo.InvariantCulture);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static string ToString(char c)
{
var temp = charToString;
return (uint) c < (uint) temp.Length
? temp[c]
: c.ToString();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static string ToString(ulong i)
{
var temp = intToString;
return i < (ulong) temp.Length
? temp[i]
: i.ToString(CultureInfo.InvariantCulture);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static string ToString(double d)
{
if (CanBeStringifiedAsLong(d))
{
// we are dealing with integer that can be cached
return ToString((long) d);
}
return NumberPrototype.ToNumberString(d);
}
///
/// Returns true if can be used for the
/// provided value , otherwise false.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool CanBeStringifiedAsLong(double d)
{
return d > long.MinValue && d < long.MaxValue && Math.Abs(d % 1) <= DoubleIsIntegerTolerance;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static string ToString(BigInteger bigInteger)
{
return bigInteger.ToString(CultureInfo.InvariantCulture);
}
///
/// http://www.ecma-international.org/ecma-262/6.0/#sec-topropertykey
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static JsValue ToPropertyKey(JsValue o)
{
const InternalTypes PropertyKeys = InternalTypes.String | InternalTypes.Symbol | InternalTypes.PrivateName;
return (o._type & PropertyKeys) != InternalTypes.Empty
? o
: ToPropertyKeyNonString(o);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static JsValue ToPropertyKeyNonString(JsValue o)
{
const InternalTypes PropertyKeys = InternalTypes.String | InternalTypes.Symbol | InternalTypes.PrivateName;
var primitive = ToPrimitive(o, Types.String);
return (primitive._type & PropertyKeys) != InternalTypes.Empty
? primitive
: ToStringNonString(primitive);
}
///
/// https://tc39.es/ecma262/#sec-tostring
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string ToString(JsValue o)
{
return o.IsString() ? o.ToString() : ToStringNonString(o);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static JsString ToJsString(JsValue o)
{
if (o is JsString s)
{
return s;
}
return JsString.Create(ToStringNonString(o));
}
private static string ToStringNonString(JsValue o)
{
var type = o._type & ~InternalTypes.InternalFlags;
switch (type)
{
case InternalTypes.Boolean:
return ((JsBoolean) o)._value ? "true" : "false";
case InternalTypes.Integer:
return ToString((int) ((JsNumber) o)._value);
case InternalTypes.Number:
return ToString(((JsNumber) o)._value);
case InternalTypes.BigInt:
return ToString(((JsBigInt) o)._value);
case InternalTypes.Symbol:
Throw.TypeErrorNoEngine("Cannot convert a Symbol value to a string");
return null;
case InternalTypes.Undefined:
return "undefined";
case InternalTypes.Null:
return "null";
case InternalTypes.PrivateName:
return o.ToString();
case InternalTypes.Object when o is IObjectWrapper p:
return p.Target?.ToString()!;
default:
return ToString(ToPrimitive(o, Types.String));
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ObjectInstance ToObject(Realm realm, JsValue value)
{
if (value is ObjectInstance oi)
{
return oi;
}
return ToObjectNonObject(realm, value);
}
///
/// https://tc39.es/ecma262/#sec-isintegralnumber
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool IsIntegralNumber(double value)
{
return !double.IsNaN(value) && !double.IsInfinity(value) && value % 1 == 0;
}
private static ObjectInstance ToObjectNonObject(Realm realm, JsValue value)
{
var type = value._type & ~InternalTypes.InternalFlags;
var intrinsics = realm.Intrinsics;
switch (type)
{
case InternalTypes.Boolean:
return intrinsics.Boolean.Construct((JsBoolean) value);
case InternalTypes.Number:
case InternalTypes.Integer:
return intrinsics.Number.Construct((JsNumber) value);
case InternalTypes.BigInt:
return intrinsics.BigInt.Construct((JsBigInt) value);
case InternalTypes.String:
return intrinsics.String.Construct(value as JsString ?? JsString.Create(value.ToString()));
case InternalTypes.Symbol:
return intrinsics.Symbol.Construct((JsSymbol) value);
case InternalTypes.Null:
case InternalTypes.Undefined:
Throw.TypeError(realm, "Cannot convert undefined or null to object");
return null;
default:
Throw.TypeError(realm, "Cannot convert given item to object");
return null;
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
internal static void CheckObjectCoercible(
Engine engine,
JsValue o,
Node sourceNode,
string referenceName)
{
if (!engine._referenceResolver.CheckCoercible(o))
{
ThrowMemberNullOrUndefinedError(engine, o, sourceNode, referenceName);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ThrowMemberNullOrUndefinedError(
Engine engine,
JsValue o,
Node sourceNode,
string referencedName)
{
referencedName ??= "unknown";
var message = $"Cannot read property '{referencedName}' of {o}";
throw new JavaScriptException(engine.Realm.Intrinsics.TypeError, message)
.SetJavaScriptCallstack(engine, sourceNode.Location, overwriteExisting: true);
}
[Obsolete("Use TypeConverter.RequireObjectCoercible")]
public static void CheckObjectCoercible(Engine engine, JsValue o) => RequireObjectCoercible(engine, o);
///
/// https://tc39.es/ecma262/#sec-requireobjectcoercible
///
public static void RequireObjectCoercible(Engine engine, JsValue o)
{
if (o._type < InternalTypes.Boolean)
{
Throw.TypeError(engine.Realm, $"Cannot call method on {o}");
}
}
}