use boa_macros::utf16;
use crate::{
builtins::{string::is_trimmable_whitespace, BuiltInBuilder, BuiltInObject, IntrinsicObject},
context::intrinsics::Intrinsics,
object::JsObject,
realm::Realm,
string::Utf16Trim,
Context, JsArgs, JsResult, JsValue,
};
use num_traits::Num;
fn is_finite(_: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult<JsValue> {
if let Some(value) = args.get(0) {
let number = value.to_number(context)?;
Ok(number.is_finite().into())
} else {
Ok(false.into())
}
}
pub(crate) struct IsFinite;
impl IntrinsicObject for IsFinite {
fn init(realm: &Realm) {
BuiltInBuilder::callable_with_intrinsic::<Self>(realm, is_finite)
.name(Self::NAME)
.length(1)
.build();
}
fn get(intrinsics: &Intrinsics) -> JsObject {
intrinsics.objects().is_finite().into()
}
}
impl BuiltInObject for IsFinite {
const NAME: &'static str = "isFinite";
}
pub(crate) fn is_nan(
_: &JsValue,
args: &[JsValue],
context: &mut Context<'_>,
) -> JsResult<JsValue> {
if let Some(value) = args.get(0) {
let number = value.to_number(context)?;
Ok(number.is_nan().into())
} else {
Ok(true.into())
}
}
pub(crate) struct IsNaN;
impl IntrinsicObject for IsNaN {
fn init(realm: &Realm) {
BuiltInBuilder::callable_with_intrinsic::<Self>(realm, is_nan)
.name(Self::NAME)
.length(1)
.build();
}
fn get(intrinsics: &Intrinsics) -> JsObject {
intrinsics.objects().is_nan().into()
}
}
impl BuiltInObject for IsNaN {
const NAME: &'static str = "isNaN";
}
pub(crate) fn parse_int(
_: &JsValue,
args: &[JsValue],
context: &mut Context<'_>,
) -> JsResult<JsValue> {
if let (Some(val), radix) = (args.get(0), args.get_or_undefined(1)) {
let input_string = val.to_string(context)?;
let mut var_s = input_string.trim_start();
let sign = if !var_s.is_empty() && var_s.starts_with(utf16!("-")) {
-1
} else {
1
};
if !var_s.is_empty() && (var_s.starts_with(utf16!("+")) || var_s.starts_with(utf16!("-"))) {
var_s = &var_s[1..];
}
let mut var_r = radix.to_i32(context)?;
let mut strip_prefix = true;
#[allow(clippy::if_not_else)]
if var_r != 0 {
if !(2..=36).contains(&var_r) {
return Ok(JsValue::nan());
}
if var_r != 16 {
strip_prefix = false;
}
} else {
var_r = 10;
}
if strip_prefix
&& var_s.len() >= 2
&& (var_s.starts_with(utf16!("0x")) || var_s.starts_with(utf16!("0X")))
{
var_s = &var_s[2..];
var_r = 16;
}
let end = char::decode_utf16(var_s.iter().copied())
.position(|code| !code.map(|c| c.is_digit(var_r as u32)).unwrap_or_default())
.unwrap_or(var_s.len());
let var_z = String::from_utf16_lossy(&var_s[..end]);
if var_z.is_empty() {
return Ok(JsValue::nan());
}
let math_int = u64::from_str_radix(&var_z, var_r as u32).map_or_else(
|_| f64::from_str_radix(&var_z, var_r as u32).expect("invalid_float_conversion"),
|i| i as f64,
);
if math_int == 0_f64 {
if sign == -1 {
return Ok(JsValue::new(-0_f64));
}
return Ok(JsValue::new(0_f64));
}
Ok(JsValue::new(f64::from(sign) * math_int))
} else {
Ok(JsValue::nan())
}
}
pub(crate) struct ParseInt;
impl IntrinsicObject for ParseInt {
fn init(realm: &Realm) {
BuiltInBuilder::callable_with_intrinsic::<Self>(realm, parse_int)
.name(Self::NAME)
.length(2)
.build();
}
fn get(intrinsics: &Intrinsics) -> JsObject {
intrinsics.objects().parse_int().into()
}
}
impl BuiltInObject for ParseInt {
const NAME: &'static str = "parseInt";
}
pub(crate) fn parse_float(
_: &JsValue,
args: &[JsValue],
context: &mut Context<'_>,
) -> JsResult<JsValue> {
if let Some(val) = args.get(0) {
let input_string = val.to_string(context)?.to_std_string_escaped();
let s = input_string.trim_start_matches(is_trimmable_whitespace);
let s_prefix_lower = s.chars().take(4).collect::<String>().to_ascii_lowercase();
if s.starts_with("Infinity") || s.starts_with("+Infinity") {
Ok(JsValue::new(f64::INFINITY))
} else if s.starts_with("-Infinity") {
Ok(JsValue::new(f64::NEG_INFINITY))
} else if s_prefix_lower.starts_with("inf")
|| s_prefix_lower.starts_with("+inf")
|| s_prefix_lower.starts_with("-inf")
{
Ok(JsValue::nan())
} else {
Ok(fast_float::parse_partial::<f64, _>(s).map_or_else(
|_| JsValue::nan(),
|(f, len)| {
if len > 0 {
JsValue::new(f)
} else {
JsValue::nan()
}
},
))
}
} else {
Ok(JsValue::nan())
}
}
pub(crate) struct ParseFloat;
impl IntrinsicObject for ParseFloat {
fn init(realm: &Realm) {
BuiltInBuilder::callable_with_intrinsic::<Self>(realm, parse_float)
.name(Self::NAME)
.length(1)
.build();
}
fn get(intrinsics: &Intrinsics) -> JsObject {
intrinsics.objects().parse_float().into()
}
}
impl BuiltInObject for ParseFloat {
const NAME: &'static str = "parseFloat";
}