diff --git a/nova_vm/src/ecmascript.rs b/nova_vm/src/ecmascript.rs index e5451e495..6d31db4ae 100644 --- a/nova_vm/src/ecmascript.rs +++ b/nova_vm/src/ecmascript.rs @@ -1,4 +1,4 @@ -mod abstract_operations; +pub(crate) mod abstract_operations; pub mod builtins; pub mod execution; pub mod scripts_and_modules; diff --git a/nova_vm/src/ecmascript/abstract_operations.rs b/nova_vm/src/ecmascript/abstract_operations.rs index ff60dde27..6eb2da6e8 100644 --- a/nova_vm/src/ecmascript/abstract_operations.rs +++ b/nova_vm/src/ecmascript/abstract_operations.rs @@ -1,3 +1,3 @@ -mod operations_on_objects; -mod testing_and_comparison; -mod type_conversion; +pub(crate) mod operations_on_objects; +pub(crate) mod testing_and_comparison; +pub(crate) mod type_conversion; diff --git a/nova_vm/src/ecmascript/abstract_operations/type_conversion.rs b/nova_vm/src/ecmascript/abstract_operations/type_conversion.rs index 798aa7f13..2e24b3345 100644 --- a/nova_vm/src/ecmascript/abstract_operations/type_conversion.rs +++ b/nova_vm/src/ecmascript/abstract_operations/type_conversion.rs @@ -13,7 +13,7 @@ use crate::{ ecmascript::{ execution::{agent::JsError, Agent, JsResult}, - types::{Object, PropertyKey, String, Value}, + types::{BigInt, Number, Object, PropertyKey, String, Value}, }, heap::WellKnownSymbolIndexes, }; @@ -139,6 +139,312 @@ pub(crate) fn ordinary_to_primitive( Err(JsError {}) } +/// ### [7.1.2 ToBoolean ( argument )](https://tc39.es/ecma262/#sec-toboolean) +pub(crate) fn to_boolean(agent: &mut Agent, argument: Value) -> JsResult { + // 1. If argument is a Boolean, return argument. + if argument.is_boolean() { + return Ok(argument); + } + + // 2. If argument is one of undefined, null, +0๐”ฝ, -0๐”ฝ, NaN, 0โ„ค, or the empty String, return false. + // TODO: checks for 0โ„ค + if argument.is_undefined() + || argument.is_null() + || argument.is_pos_zero(agent) + || argument.is_neg_zero(agent) + || argument.is_nan(agent) + || argument.is_empty_string() + { + return Ok(false.into()); + } + + // 3. NOTE: This step is replaced in section B.3.6.1. + + // 4. Return true. + Ok(true.into()) +} + +/// ### [7.1.3 ToNumeric ( value )](https://tc39.es/ecma262/#sec-tonumeric) +pub(crate) fn to_numeric(agent: &mut Agent, value: Value) -> JsResult { + // 1. Let primValue be ? ToPrimitive(value, number). + let prim_value = to_primitive(agent, value, Some(PreferredType::Number))?; + + // 2. If primValue is a BigInt, return primValue. + if prim_value.is_bigint() { + return Ok(prim_value); + } + + // 3. Return ? ToNumber(primValue). + to_number(agent, value).map(|n| n.into_value()) +} + +/// ### [7.1.4 ToNumber ( argument )](https://tc39.es/ecma262/#sec-tonumber) +pub(crate) fn to_number(agent: &mut Agent, argument: Value) -> JsResult { + // 1. If argument is a Number, return argument. + if let Ok(argument) = Number::try_from(argument) { + return Ok(argument); + } + + // 2. If argument is either a Symbol or a BigInt, throw a TypeError exception. + if argument.is_symbol() || argument.is_bigint() { + todo!(); + } + + // 3. If argument is undefined, return NaN. + if argument.is_undefined() { + return Ok(Number::nan()); + } + + // 4. If argument is either null or false, return +0๐”ฝ. + if argument.is_null() || argument.is_false() { + return Ok(Number::from(0)); + } + + // 5. If argument is true, return 1๐”ฝ. + if argument.is_true() { + return Ok(Number::from(1)); + } + + // 6. If argument is a String, return StringToNumber(argument). + if argument.is_string() { + todo!(); + } + + // 7. Assert: argument is an Object. + debug_assert!(argument.is_object()); + + // 8. Let primValue be ? ToPrimitive(argument, number). + let prim_value = to_primitive(agent, argument, Some(PreferredType::Number))?; + + // 9. Assert: primValue is not an Object. + debug_assert!(!prim_value.is_object()); + + // 10. Return ? ToNumber(primValue). + to_number(agent, prim_value) +} + +/// ### [7.1.5 ToIntegerOrInfinity ( argument )](https://tc39.es/ecma262/#sec-tointegerorinfinity) +// TODO: Should we add another [`Value`] newtype for IntegerOrInfinity? +pub(crate) fn to_integer_or_infinty(agent: &mut Agent, argument: Value) -> JsResult { + // 1. Let number be ? ToNumber(argument). + let number = to_number(agent, argument)?; + + // 2. If number is one of NaN, +0๐”ฝ, or -0๐”ฝ, return 0. + if number.is_nan(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { + return Ok(Number::pos_zero()); + } + + // 3. If number is +โˆž๐”ฝ, return +โˆž. + if number.is_pos_infinity(agent) { + return Ok(Number::pos_inf()); + } + + // 4. If number is -โˆž๐”ฝ, return -โˆž. + if number.is_neg_infinity(agent) { + return Ok(Number::neg_inf()); + } + + // 5. Return truncate(โ„(number)). + Ok(number.truncate(agent)) +} + +/// ### [7.1.6 ToInt32 ( argument )](https://tc39.es/ecma262/#sec-toint32) +pub(crate) fn to_int32(agent: &mut Agent, argument: Value) -> JsResult { + // 1. Let number be ? ToNumber(argument). + let number = to_number(agent, argument)?; + + // 2. If number is not finite or number is either +0๐”ฝ or -0๐”ฝ, return +0๐”ฝ. + if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { + return Ok(0); + } + + // 3. Let int be truncate(โ„(number)). + let int = number.truncate(agent).into_f64(agent); + + // 4. Let int32bit be int modulo 2^32. + let int32bit = int % 2f64.powi(32); + + // 5. If int32bit โ‰ฅ 2^31, return ๐”ฝ(int32bit - 2^32); otherwise return ๐”ฝ(int32bit). + Ok(if int32bit >= 2f64.powi(32) { + int32bit - 2f64.powi(32) + } else { + int32bit + } as i32) +} + +/// ### [7.1.7 ToUint32 ( argument )](https://tc39.es/ecma262/#sec-touint32) +pub(crate) fn to_uint32(agent: &mut Agent, argument: Value) -> JsResult { + // 1. Let number be ? ToNumber(argument). + let number = to_number(agent, argument)?; + + // 2. If number is not finite or number is either +0๐”ฝ or -0๐”ฝ, return +0๐”ฝ. + if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { + return Ok(0); + } + + // 3. Let int be truncate(โ„(number)). + let int = number.truncate(agent).into_f64(agent); + + // 4. Let int32bit be int modulo 2^32. + let int32bit = int % 2f64.powi(32); + + // 5. Return ๐”ฝ(int32bit). + Ok(int32bit as u32) +} + +/// ### [7.1.8 ToInt16 ( argument )](https://tc39.es/ecma262/#sec-toint16) +pub(crate) fn to_int16(agent: &mut Agent, argument: Value) -> JsResult { + // 1. Let number be ? ToNumber(argument). + let number = to_number(agent, argument)?; + + // 2. If number is not finite or number is either +0๐”ฝ or -0๐”ฝ, return +0๐”ฝ. + if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { + return Ok(0); + } + + // 3. Let int be truncate(โ„(number)). + let int = number.truncate(agent).into_f64(agent); + + // 4. Let int16bit be int modulo 2^16. + let int16bit = int % 2f64.powi(16); + + // 5. If int16bit โ‰ฅ 2^15, return ๐”ฝ(int16bit - 2^16); otherwise return ๐”ฝ(int16bit). + Ok(if int16bit >= 2f64.powi(15) { + int16bit - 2f64.powi(16) + } else { + int16bit + } as i16) +} + +/// ### [7.1.9 ToUint16 ( argument )](https://tc39.es/ecma262/#sec-touint16) +pub(crate) fn to_uint16(agent: &mut Agent, argument: Value) -> JsResult { + // 1. Let number be ? ToNumber(argument). + let number = to_number(agent, argument)?; + + // 2. If number is not finite or number is either +0๐”ฝ or -0๐”ฝ, return +0๐”ฝ. + if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { + return Ok(0); + } + + // 3. Let int be truncate(โ„(number)). + let int = number.truncate(agent).into_f64(agent); + + // 4. Let int16bit be int modulo 2^16. + let int16bit = int % 2f64.powi(16); + + // Return ๐”ฝ(int16bit). + Ok(int16bit as i16) +} + +/// ### [7.1.10 ToInt8 ( argument )](https://tc39.es/ecma262/#sec-toint8) +pub(crate) fn to_int8(agent: &mut Agent, argument: Value) -> JsResult { + // 1. Let number be ? ToNumber(argument). + let number = to_number(agent, argument)?; + + // 2. If number is not finite or number is either +0๐”ฝ or -0๐”ฝ, return +0๐”ฝ. + if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { + return Ok(0); + } + + // 3. Let int be truncate(โ„(number)). + let int = number.truncate(agent).into_f64(agent); + + // 4. Let int8bit be int modulo 2^8. + let int8bit = int % 2f64.powi(8); + + // 5. If int8bit โ‰ฅ 2^7, return ๐”ฝ(int8bit - 2^8); otherwise return ๐”ฝ(int8bit). + Ok(if int8bit >= 2f64.powi(7) { + int8bit - 2f64.powi(8) + } else { + int8bit + } as i8) +} + +/// ### [7.1.11 ToUint8 ( argument )](https://tc39.es/ecma262/#sec-touint8) +pub(crate) fn to_uint8(agent: &mut Agent, argument: Value) -> JsResult { + // 1. Let number be ? ToNumber(argument). + let number = to_number(agent, argument)?; + + // 2. If number is not finite or number is either +0๐”ฝ or -0๐”ฝ, return +0๐”ฝ. + if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { + return Ok(0); + } + + // 3. Let int be truncate(โ„(number)). + let int = number.truncate(agent).into_f64(agent); + + // 4. Let int8bit be int modulo 2^8. + let int8bit = int % 2f64.powi(8); + + // 5. Return ๐”ฝ(int8bit). + Ok(int8bit as u8) +} + +/// ### [7.1.12 ToUint8Clamp ( argument )](https://tc39.es/ecma262/#sec-touint8clamp) +pub(crate) fn to_uint8_clamp(agent: &mut Agent, argument: Value) -> JsResult { + // 1. Let number be ? ToNumber(argument). + let number = to_number(agent, argument)?; + + // 2. If number is NaN, return +0๐”ฝ. + if number.is_nan(agent) { + return Ok(0); + } + + // 3. Let mv be the extended mathematical value of number. + // TODO: Is there a better way? + let mv = number.into_f64(agent); + + // 4. Let clamped be the result of clamping mv between 0 and 255. + let clamped = mv.clamp(0.0, 255.0); + + // 5. Let f be floor(clamped). + let f = clamped.floor(); + + Ok( + // 6. If clamped < f + 0.5, return ๐”ฝ(f). + if clamped < f + 0.5 { + f as u8 + } + // 7. If clamped > f + 0.5, return ๐”ฝ(f + 1). + else if clamped > f + 0.5 { + f as u8 + 1 + } + // 8. If f is even, return ๐”ฝ(f). Otherwise, return ๐”ฝ(f + 1). + else if f % 2.0 == 0.0 { + f as u8 + } else { + f as u8 + 1 + }, + ) +} + +/// ### [7.1.13 ToBigInt ( argument )](https://tc39.es/ecma262/#sec-tobigint) +pub(crate) fn to_big_int(agent: &mut Agent, argument: Value) -> JsResult { + // 1. Let prim be ? ToPrimitive(argument, number). + let _prim = to_primitive(agent, argument, Some(PreferredType::Number))?; + + // 2. Return the value that prim corresponds to in Table 12. + todo!() +} + +/// ### [7.1.17 ToString ( argument )](https://tc39.es/ecma262/#sec-tostring) +pub(crate) fn to_string(_agent: &mut Agent, _argument: Value) -> JsResult { + // TODO: 1. If argument is a String, return argument. + // 2. If argument is a Symbol, throw a TypeError exception. + // 3. If argument is undefined, return "undefined". + // 4. If argument is null, return "null". + // 5. If argument is true, return "true". + // 6. If argument is false, return "false". + // 7. If argument is a Number, return Number::toString(argument, 10). + // 8. If argument is a BigInt, return BigInt::toString(argument, 10). + // 9. Assert: argument is an Object. + // 10. Let primValue be ? ToPrimitive(argument, string). + // 11. Assert: primValue is not an Object. + // 12. Return ? ToString(primValue). + + todo!() +} + /// ### [7.1.18 ToObject ( argument )](https://tc39.es/ecma262/#sec-toobject) /// /// The abstract operation ToObject takes argument argument (an ECMAScript diff --git a/nova_vm/src/ecmascript/types.rs b/nova_vm/src/ecmascript/types.rs index 3dcc35fc7..5cafefac1 100644 --- a/nova_vm/src/ecmascript/types.rs +++ b/nova_vm/src/ecmascript/types.rs @@ -1,12 +1,12 @@ mod language; mod spec; +pub use language::{ + BigInt, Function, InternalMethods, Number, Object, OrdinaryObject, PropertyKey, String, Value, +}; pub(crate) use language::{ BigIntHeapData, FunctionHeapData, NumberHeapData, ObjectHeapData, StringHeapData, }; -pub use language::{ - Function, InternalMethods, Number, Object, OrdinaryObject, PropertyKey, String, Value, -}; pub(crate) use spec::DataBlock; pub use spec::{Base, PropertyDescriptor, Reference, ReferencedName}; diff --git a/nova_vm/src/ecmascript/types/language/value.rs b/nova_vm/src/ecmascript/types/language/value.rs index b3393c196..540e7b4a9 100644 --- a/nova_vm/src/ecmascript/types/language/value.rs +++ b/nova_vm/src/ecmascript/types/language/value.rs @@ -1,7 +1,10 @@ use std::mem::size_of; use crate::{ - ecmascript::execution::{Agent, JsResult}, + ecmascript::{ + abstract_operations::type_conversion::{to_int32, to_number, to_numeric, to_uint32}, + execution::{Agent, JsResult}, + }, heap::indexes::{ ArrayBufferIndex, ArrayIndex, BigIntIndex, DateIndex, ErrorIndex, FunctionIndex, NumberIndex, ObjectIndex, RegExpIndex, StringIndex, SymbolIndex, @@ -9,7 +12,7 @@ use crate::{ Heap, SmallInteger, SmallString, }; -use super::{BigInt, Number}; +use super::Number; /// 6.1 ECMAScript Language Types /// https://tc39.es/ecma262/#sec-ecmascript-language-types @@ -232,410 +235,20 @@ impl Value { } } - /// 7.1.1 ToPrimitive ( input [ , preferredType ] ) - /// https://tc39.es/ecma262/#sec-toprimitive - pub fn to_primitive( - self, - _agent: &mut Agent, - _preferred_type: Option, - ) -> JsResult { - let input = self; - - // 1. If input is an Object, then - if input.is_object() { - // a. Let exoticToPrim be ? GetMethod(input, @@toPrimitive). - // b. If exoticToPrim is not undefined, then - // i. If preferredType is not present, then - // 1. Let hint be "default". - // ii. Else if preferredType is string, then - // 1. Let hint be "string". - // iii. Else, - // 1. Assert: preferredType is number. - // 2. Let hint be "number". - // iv. Let result be ? Call(exoticToPrim, input, ยซ hint ยป). - // v. If result is not an Object, return result. - // vi. Throw a TypeError exception. - // c. If preferredType is not present, let preferredType be number. - // d. Return ? OrdinaryToPrimitive(input, preferredType). - todo!(); - } - - // 2. Return input. - Ok(input) - } - - /// 7.1.1.1 OrdinaryToPrimitive ( O, hint ) - /// https://tc39.es/ecma262/#sec-ordinarytoprimitive - pub fn ordinary_to_primitive(self, _agent: &mut Agent, hint: PreferredType) -> JsResult { - // TODO: This takes in an object...so probably put it in Object. - let _o = self; - - // 1. If hint is string, then - let method_names = if matches!(hint, PreferredType::String) { - // a. Let methodNames be ยซ "toString", "valueOf" ยป. - &["toString", "valueOf"] - } - // 2. Else, - else { - // a. Let methodNames be ยซ "valueOf", "toString" ยป. - &["valueOf", "toString"] - }; - - // TODO: 3. For each element name of methodNames, do - for _name in method_names.iter() { - // a. Let method be ? Get(O, name). - // b. If IsCallable(method) is true, then - // i. Let result be ? Call(method, O). - // ii. If result is not an Object, return result. - // 4. Throw a TypeError exception. - } - - todo!() - } - - /// 7.1.2 ToBoolean ( argument ) - /// https://tc39.es/ecma262/#sec-toboolean - pub fn to_boolean(self, agent: &mut Agent) -> JsResult { - let argument = self; - - // 1. If argument is a Boolean, return argument. - if argument.is_boolean() { - return Ok(argument); - } - - // 2. If argument is one of undefined, null, +0๐”ฝ, -0๐”ฝ, NaN, 0โ„ค, or the empty String, return false. - // TODO: checks for 0โ„ค - if argument.is_undefined() - || argument.is_null() - || argument.is_pos_zero(agent) - || argument.is_neg_zero(agent) - || argument.is_nan(agent) - || argument.is_empty_string() - { - return Ok(false.into()); - } - - // 3. NOTE: This step is replaced in section B.3.6.1. - - // 4. Return true. - Ok(true.into()) - } - - /// 7.1.3 ToNumeric ( value ) - /// https://tc39.es/ecma262/#sec-tonumeric - pub fn to_numeric(self, agent: &mut Agent) -> JsResult { - let value = self; - - // 1. Let primValue be ? ToPrimitive(value, number). - let prim_value = value.to_primitive(agent, Some(PreferredType::Number))?; - - // 2. If primValue is a BigInt, return primValue. - if prim_value.is_bigint() { - return Ok(prim_value); - } - - // 3. Return ? ToNumber(primValue). - prim_value.to_number(agent).map(|n| n.into_value()) - } - - /// 7.1.4 ToNumber ( argument ) - /// https://tc39.es/ecma262/#sec-tonumber pub fn to_number(self, agent: &mut Agent) -> JsResult { - let argument = self; - - // 1. If argument is a Number, return argument. - if let Ok(argument) = Number::try_from(argument) { - return Ok(argument); - } - - // 2. If argument is either a Symbol or a BigInt, throw a TypeError exception. - if argument.is_symbol() || argument.is_bigint() { - todo!(); - } - - // 3. If argument is undefined, return NaN. - if argument.is_undefined() { - return Ok(Number::nan()); - } - - // 4. If argument is either null or false, return +0๐”ฝ. - if argument.is_null() || argument.is_false() { - return Ok(Number::from(0)); - } - - // 5. If argument is true, return 1๐”ฝ. - if argument.is_true() { - return Ok(Number::from(1)); - } - - // 6. If argument is a String, return StringToNumber(argument). - if argument.is_string() { - todo!(); - } - - // 7. Assert: argument is an Object. - debug_assert!(argument.is_object()); - - // 8. Let primValue be ? ToPrimitive(argument, number). - let prim_value = argument.to_primitive(agent, Some(PreferredType::Number))?; - - // 9. Assert: primValue is not an Object. - debug_assert!(!prim_value.is_object()); - - // 10. Return ? ToNumber(primValue). - prim_value.to_number(agent) + to_number(agent, self) } - /// 7.1.5 ToIntegerOrInfinity ( argument ) - /// https://tc39.es/ecma262/#sec-tointegerorinfinity - // TODO: Should we add another [`Value`] newtype for IntegerOrInfinity? - pub fn to_integer_or_infinty(self, agent: &mut Agent) -> JsResult { - let argument: Value = self; - - // 1. Let number be ? ToNumber(argument). - let number = argument.to_number(agent)?; - - // 2. If number is one of NaN, +0๐”ฝ, or -0๐”ฝ, return 0. - if number.is_nan(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { - return Ok(Number::pos_zero()); - } - - // 3. If number is +โˆž๐”ฝ, return +โˆž. - if number.is_pos_infinity(agent) { - return Ok(Number::pos_inf()); - } - - // 4. If number is -โˆž๐”ฝ, return -โˆž. - if number.is_neg_infinity(agent) { - return Ok(Number::neg_inf()); - } - - // 5. Return truncate(โ„(number)). - Ok(number.truncate(agent)) + pub fn to_numeric(self, agent: &mut Agent) -> JsResult { + to_numeric(agent, self) } - /// 7.1.6 ToInt32 ( argument ) - /// https://tc39.es/ecma262/#sec-toint32 pub fn to_int32(self, agent: &mut Agent) -> JsResult { - let argument = self; - - // 1. Let number be ? ToNumber(argument). - let number = argument.to_number(agent)?; - - // 2. If number is not finite or number is either +0๐”ฝ or -0๐”ฝ, return +0๐”ฝ. - if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { - return Ok(0); - } - - // 3. Let int be truncate(โ„(number)). - let int = number.truncate(agent).into_f64(agent); - - // 4. Let int32bit be int modulo 2^32. - let int32bit = int % 2f64.powi(32); - - // 5. If int32bit โ‰ฅ 2^31, return ๐”ฝ(int32bit - 2^32); otherwise return ๐”ฝ(int32bit). - Ok(if int32bit >= 2f64.powi(32) { - int32bit - 2f64.powi(32) - } else { - int32bit - } as i32) + to_int32(agent, self) } - /// 7.1.7 ToUint32 ( argument ) - /// https://tc39.es/ecma262/#sec-touint32 pub fn to_uint32(self, agent: &mut Agent) -> JsResult { - let argument = self; - - // 1. Let number be ? ToNumber(argument). - let number = argument.to_number(agent)?; - - // 2. If number is not finite or number is either +0๐”ฝ or -0๐”ฝ, return +0๐”ฝ. - if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { - return Ok(0); - } - - // 3. Let int be truncate(โ„(number)). - let int = number.truncate(agent).into_f64(agent); - - // 4. Let int32bit be int modulo 2^32. - let int32bit = int % 2f64.powi(32); - - // 5. Return ๐”ฝ(int32bit). - Ok(int32bit as u32) - } - - /// 7.1.8 ToInt16 ( argument ) - /// https://tc39.es/ecma262/#sec-toint16 - pub fn to_int16(self, agent: &mut Agent) -> JsResult { - let argument = self; - - // 1. Let number be ? ToNumber(argument). - let number = argument.to_number(agent)?; - - // 2. If number is not finite or number is either +0๐”ฝ or -0๐”ฝ, return +0๐”ฝ. - if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { - return Ok(0); - } - - // 3. Let int be truncate(โ„(number)). - let int = number.truncate(agent).into_f64(agent); - - // 4. Let int16bit be int modulo 2^16. - let int16bit = int % 2f64.powi(16); - - // 5. If int16bit โ‰ฅ 2^15, return ๐”ฝ(int16bit - 2^16); otherwise return ๐”ฝ(int16bit). - Ok(if int16bit >= 2f64.powi(15) { - int16bit - 2f64.powi(16) - } else { - int16bit - } as i16) - } - - /// 7.1.9 ToUint16 ( argument ) - /// https://tc39.es/ecma262/#sec-touint16 - pub fn to_uint16(self, agent: &mut Agent) -> JsResult { - let argument = self; - - // 1. Let number be ? ToNumber(argument). - let number = argument.to_number(agent)?; - - // 2. If number is not finite or number is either +0๐”ฝ or -0๐”ฝ, return +0๐”ฝ. - if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { - return Ok(0); - } - - // 3. Let int be truncate(โ„(number)). - let int = number.truncate(agent).into_f64(agent); - - // 4. Let int16bit be int modulo 2^16. - let int16bit = int % 2f64.powi(16); - - // Return ๐”ฝ(int16bit). - Ok(int16bit as i16) - } - - /// 7.1.10 ToInt8 ( argument ) - /// https://tc39.es/ecma262/#sec-toint8 - pub fn to_int8(self, agent: &mut Agent) -> JsResult { - let argument = self; - - // 1. Let number be ? ToNumber(argument). - let number = argument.to_number(agent)?; - - // 2. If number is not finite or number is either +0๐”ฝ or -0๐”ฝ, return +0๐”ฝ. - if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { - return Ok(0); - } - - // 3. Let int be truncate(โ„(number)). - let int = number.truncate(agent).into_f64(agent); - - // 4. Let int8bit be int modulo 2^8. - let int8bit = int % 2f64.powi(8); - - // 5. If int8bit โ‰ฅ 2^7, return ๐”ฝ(int8bit - 2^8); otherwise return ๐”ฝ(int8bit). - Ok(if int8bit >= 2f64.powi(7) { - int8bit - 2f64.powi(8) - } else { - int8bit - } as i8) - } - - /// 7.1.11 ToUint8 ( argument ) - /// https://tc39.es/ecma262/#sec-touint8 - pub fn to_uint8(self, agent: &mut Agent) -> JsResult { - let argument = self; - - // 1. Let number be ? ToNumber(argument). - let number = argument.to_number(agent)?; - - // 2. If number is not finite or number is either +0๐”ฝ or -0๐”ฝ, return +0๐”ฝ. - if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { - return Ok(0); - } - - // 3. Let int be truncate(โ„(number)). - let int = number.truncate(agent).into_f64(agent); - - // 4. Let int8bit be int modulo 2^8. - let int8bit = int % 2f64.powi(8); - - // 5. Return ๐”ฝ(int8bit). - Ok(int8bit as u8) - } - - /// 7.1.12 ToUint8Clamp ( argument ) - /// https://tc39.es/ecma262/#sec-touint8clamp - pub fn to_uint8_clamp(self, agent: &mut Agent) -> JsResult { - let argument = self; - - // 1. Let number be ? ToNumber(argument). - let number = argument.to_number(agent)?; - - // 2. If number is NaN, return +0๐”ฝ. - if number.is_nan(agent) { - return Ok(0); - } - - // 3. Let mv be the extended mathematical value of number. - // TODO: Is there a better way? - let mv = number.into_f64(agent); - - // 4. Let clamped be the result of clamping mv between 0 and 255. - let clamped = mv.clamp(0.0, 255.0); - - // 5. Let f be floor(clamped). - let f = clamped.floor(); - - Ok( - // 6. If clamped < f + 0.5, return ๐”ฝ(f). - if clamped < f + 0.5 { - f as u8 - } - // 7. If clamped > f + 0.5, return ๐”ฝ(f + 1). - else if clamped > f + 0.5 { - f as u8 + 1 - } - // 8. If f is even, return ๐”ฝ(f). Otherwise, return ๐”ฝ(f + 1). - else if f % 2.0 == 0.0 { - f as u8 - } else { - f as u8 + 1 - }, - ) - } - - /// 7.1.13 ToBigInt ( argument ) - /// https://tc39.es/ecma262/#sec-tobigint - pub fn to_big_int(self, agent: &mut Agent) -> JsResult { - let argument = self; - - // 1. Let prim be ? ToPrimitive(argument, number). - let _prim = argument.to_primitive(agent, Some(PreferredType::Number))?; - - // 2. Return the value that prim corresponds to in Table 12. - todo!() - } - - /// 7.1.17 ToString ( argument ) - /// https://tc39.es/ecma262/#sec-tostring - pub fn to_string(self, _agent: &mut Agent) -> JsResult { - let _argument = self; - - // TODO: 1. If argument is a String, return argument. - // 2. If argument is a Symbol, throw a TypeError exception. - // 3. If argument is undefined, return "undefined". - // 4. If argument is null, return "null". - // 5. If argument is true, return "true". - // 6. If argument is false, return "false". - // 7. If argument is a Number, return Number::toString(argument, 10). - // 8. If argument is a BigInt, return BigInt::toString(argument, 10). - // 9. Assert: argument is an Object. - // 10. Let primValue be ? ToPrimitive(argument, string). - // 11. Assert: primValue is not an Object. - // 12. Return ? ToString(primValue). - - todo!() + to_uint32(agent, self) } fn is_same_type(self, y: Self) -> bool {