From 3471a06522a059d73b7b8d3a1900abd22c16fc0a Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Sun, 8 Dec 2024 10:33:33 -0800 Subject: [PATCH 01/19] WIP --- core/engine/src/value/mod.rs | 161 ++++++++++++++++--------------- core/engine/src/value/variant.rs | 51 ++++++++++ 2 files changed, 132 insertions(+), 80 deletions(-) create mode 100644 core/engine/src/value/variant.rs diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index 6d19592028e..f01335067d5 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -29,6 +29,7 @@ pub use self::{ }; use crate::builtins::RegExp; use crate::object::{JsFunction, JsPromise, JsRegExp}; +use crate::value::variant::JsVariant; use crate::{ builtins::{ number::{f64_to_int32, f64_to_uint32}, @@ -49,6 +50,7 @@ mod hash; mod integer; mod operations; mod r#type; +mod variant; #[cfg(test)] mod tests; @@ -63,30 +65,25 @@ static TWO_E_63: Lazy = Lazy::new(|| { BigInt::from(TWO_E_63) }); -/// A Javascript value +/// The Inner type of a [JsValue]. This is the actual value that the JsValue holds. +/// This is not a public API and should not be used directly. +/// +/// If you need access to the variant, use [JsValue::variant] instead. #[derive(Finalize, Debug, Clone)] -pub enum JsValue { - /// `null` - A null value, for when a value doesn't exist. +enum InnerValue { Null, - /// `undefined` - An undefined value, for when a field or index doesn't exist. Undefined, - /// `boolean` - A `true` / `false` value, for if a certain criteria is met. Boolean(bool), - /// `String` - A UTF-16 string, such as `"Hello, world"`. - String(JsString), - /// `Number` - A 64-bit floating point number, such as `3.1415` - Rational(f64), - /// `Number` - A 32-bit integer, such as `42`. - Integer(i32), - /// `BigInt` - holds any arbitrary large signed integer. + Float64(f64), + Integer32(i32), BigInt(JsBigInt), - /// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values. - Object(JsObject), - /// `Symbol` - A Symbol Primitive type. + String(JsString), Symbol(JsSymbol), + Object(JsObject), } -unsafe impl Trace for JsValue { +#[allow(unsafe_op_in_unsafe_fn)] +unsafe impl Trace for InnerValue { custom_trace! {this, mark, { if let Self::Object(o) = this { mark(o); @@ -94,6 +91,12 @@ unsafe impl Trace for JsValue { }} } +/// A Javascript value +#[derive(Finalize, Debug, Clone, Trace)] +pub struct JsValue { + inner: InnerValue, +} + impl JsValue { /// Create a new [`JsValue`]. pub fn new(value: T) -> Self @@ -103,55 +106,61 @@ impl JsValue { value.into() } + /// Return the variant of this value. + pub fn variant(&self) -> JsVariant { + (&self.inner).into() + } + /// Creates a new `undefined` value. #[inline] #[must_use] pub const fn undefined() -> Self { - Self::Undefined + Self { inner: InnerValue::Undefined } } /// Creates a new `null` value. #[inline] #[must_use] pub const fn null() -> Self { - Self::Null + Self { inner: InnerValue::Null } } /// Creates a new number with `NaN` value. #[inline] #[must_use] pub const fn nan() -> Self { - Self::Rational(f64::NAN) + Self { inner: InnerValue::Float64(f64::NAN) } } /// Creates a new number with `Infinity` value. #[inline] #[must_use] pub const fn positive_infinity() -> Self { - Self::Rational(f64::INFINITY) + Self { inner: InnerValue::Float64(f64::INFINITY) } } /// Creates a new number with `-Infinity` value. #[inline] #[must_use] pub const fn negative_infinity() -> Self { - Self::Rational(f64::NEG_INFINITY) + Self { inner: InnerValue::Float64(f64::NEG_INFINITY) } } /// Returns true if the value is an object. #[inline] #[must_use] pub const fn is_object(&self) -> bool { - matches!(self, Self::Object(_)) + matches!(self.inner, InnerValue::Object(_)) } /// Returns the object if the value is object, otherwise `None`. #[inline] #[must_use] pub const fn as_object(&self) -> Option<&JsObject> { - match *self { - Self::Object(ref o) => Some(o), - _ => None, + if let InnerValue::Object(obj) = &self.inner { + Some(obj) + } else { + None } } @@ -164,14 +173,22 @@ impl JsValue { #[inline] #[must_use] pub fn is_callable(&self) -> bool { - matches!(self, Self::Object(obj) if obj.is_callable()) + if let InnerValue::Object(obj) = &self.inner { + obj.is_callable() + } else { + false + } } /// Returns the callable value if the value is callable, otherwise `None`. #[inline] #[must_use] pub fn as_callable(&self) -> Option<&JsObject> { - self.as_object().filter(|obj| obj.is_callable()) + if let InnerValue::Object(obj) = &self.inner { + obj.is_callable().then(|| obj) + } else { + None + } } /// Returns a [`JsFunction`] if the value is callable, otherwise `None`. @@ -188,7 +205,7 @@ impl JsValue { #[inline] #[must_use] pub fn is_constructor(&self) -> bool { - matches!(self, Self::Object(obj) if obj.is_constructor()) + matches!(&self.inner, InnerValue::Object(obj) if obj.is_constructor()) } /// Returns the constructor if the value is a constructor, otherwise `None`. @@ -202,14 +219,22 @@ impl JsValue { #[inline] #[must_use] pub fn is_promise(&self) -> bool { - matches!(self, Self::Object(obj) if obj.is::()) + if let InnerValue::Object(obj) = &self.inner { + obj.is::() + } else { + false + } } /// Returns the value as an object if the value is a promise, otherwise `None`. #[inline] #[must_use] pub(crate) fn as_promise_object(&self) -> Option<&JsObject> { - self.as_object().filter(|obj| obj.is::()) + if let InnerValue::Object(obj) = &self.inner { + obj.is::().then(|| obj) + } else { + None + } } /// Returns the value as a promise if the value is a promise, otherwise `None`. @@ -225,7 +250,11 @@ impl JsValue { #[inline] #[must_use] pub fn is_regexp(&self) -> bool { - matches!(self, Self::Object(obj) if obj.is::()) + if let InnerValue::Object(obj) = &self.inner { + obj.is::() + } else { + false + } } /// Returns the value as a regular expression if the value is a regexp, otherwise `None`. @@ -242,16 +271,17 @@ impl JsValue { #[inline] #[must_use] pub const fn is_symbol(&self) -> bool { - matches!(self, Self::Symbol(_)) + matches!(self.inner, InnerValue::Symbol(_)) } /// Returns the symbol if the value is a symbol, otherwise `None`. #[inline] #[must_use] pub fn as_symbol(&self) -> Option { - match self { - Self::Symbol(symbol) => Some(symbol.clone()), - _ => None, + if let InnerValue::Symbol(symbol) = &self.inner { + Some(symbol.clone()) + } else { + None } } @@ -259,28 +289,21 @@ impl JsValue { #[inline] #[must_use] pub const fn is_undefined(&self) -> bool { - matches!(self, Self::Undefined) + self.inner == InnerValue::Undefined } /// Returns true if the value is null. #[inline] #[must_use] pub const fn is_null(&self) -> bool { - matches!(self, Self::Null) + self.inner == InnerValue::Null } /// Returns true if the value is null or undefined. #[inline] #[must_use] pub const fn is_null_or_undefined(&self) -> bool { - matches!(self, Self::Null | Self::Undefined) - } - - /// Returns true if the value is a 64-bit floating-point number. - #[inline] - #[must_use] - pub const fn is_double(&self) -> bool { - matches!(self, Self::Rational(_)) + self.is_undefined() || self.is_null() } /// Determines if argument is a finite integral Number value. @@ -292,48 +315,25 @@ impl JsValue { #[must_use] #[allow(clippy::float_cmp)] pub fn is_integral_number(&self) -> bool { - // If it can fit in a i32 and the truncated version is - // equal to the original then it is an integer. - let is_rational_integer = |n: f64| n == f64::from(n as i32); - - match *self { - Self::Integer(_) => true, - Self::Rational(n) if is_rational_integer(n) => true, - _ => false, - } - } - - /// Returns true if the value can be reprented as an integer. - /// - /// Similar to [`JsValue::is_integral_number()`] except that it returns `false` for `-0`. - #[must_use] - #[allow(clippy::float_cmp)] - pub fn is_integer(&self) -> bool { - // If it can fit in a i32 and the truncated version is - // equal to the original then it is an integer. - let is_rational_integer = |n: f64| n.to_bits() == f64::from(n as i32).to_bits(); - - match *self { - Self::Integer(_) => true, - Self::Rational(n) if is_rational_integer(n) => true, - _ => false, - } + // When creating the inner value, we verify that the float is rational or + // an integer, so we can safely unwrap here. + matches!(&self.inner, InnerValue::Integer32(_)) } /// Returns true if the value is a number. #[inline] #[must_use] pub const fn is_number(&self) -> bool { - matches!(self, Self::Rational(_) | Self::Integer(_)) + matches!(self.inner, InnerValue::Float64(_) | InnerValue::Integer32(_)) } /// Returns the number if the value is a number, otherwise `None`. #[inline] #[must_use] pub fn as_number(&self) -> Option { - match *self { - Self::Integer(integer) => Some(integer.into()), - Self::Rational(rational) => Some(rational), + match self.inner { + InnerValue::Integer32(integer) => Some(integer.into()), + InnerValue::Float64(rational) => Some(rational), _ => None, } } @@ -342,16 +342,17 @@ impl JsValue { #[inline] #[must_use] pub const fn is_string(&self) -> bool { - matches!(self, Self::String(_)) + matches!(self.inner, InnerValue::String(_)) } /// Returns the string if the value is a string, otherwise `None`. #[inline] #[must_use] pub const fn as_string(&self) -> Option<&JsString> { - match self { - Self::String(ref string) => Some(string), - _ => None, + if let InnerValue::String(string) = &self.inner { + Some(string) + } else { + None } } @@ -435,7 +436,7 @@ impl JsValue { PreferredType::String => js_string!("string"), PreferredType::Number => js_string!("number"), } - .into(); + .into(); // iv. Let result be ? Call(exoticToPrim, input, « hint »). let result = exotic_to_prim.call(self, &[hint], context)?; diff --git a/core/engine/src/value/variant.rs b/core/engine/src/value/variant.rs new file mode 100644 index 00000000000..ba315b7d488 --- /dev/null +++ b/core/engine/src/value/variant.rs @@ -0,0 +1,51 @@ +use super::InnerValue; +use crate::object::Ref; +use crate::{JsBigInt, JsObject, JsSymbol}; +use boa_string::JsString; + +/// A non-mutable variant of a JsValue. +/// Represents either a primitive value ([`bool`], [`f64`], [`i32`]) or a reference +/// to a heap allocated value ([`JsString`], [`JsSymbol`]). +/// +/// References to heap allocated values are represented by [`Ref`], since +/// more exotic implementations of [`JsValue`] such as nan-boxed ones cannot +/// effectively return references. +#[derive(Debug)] +pub enum JsVariant<'a> { + /// `null` - A null value, for when a value doesn't exist. + Null, + /// `undefined` - An undefined value, for when a field or index doesn't exist. + Undefined, + /// `boolean` - A `true` / `false` value, for if a certain criteria is met. + Boolean(bool), + /// `String` - A UTF-16 string, such as `"Hello, world"`. + String(&'a JsString), + /// `Number` - A 64-bit floating point number, such as `3.1415` or `Infinity`. + /// This is the default representation of a number. If a number can be represented + /// as an integer, it will be stored as an `Integer` variant instead. + Float64(f64), + /// `Number` - A 32-bit integer, such as `42`. + Integer32(i32), + /// `BigInt` - holds any arbitrary large signed integer. + BigInt(&'a JsBigInt), + /// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values. + Object(&'a JsObject), + /// `Symbol` - A Symbol Primitive type. + Symbol(&'a JsSymbol), +} + +impl<'a> From<&'a InnerValue> for JsVariant<'a> { + fn from(value: &'a InnerValue) -> Self { + match value { + InnerValue::Null => JsVariant::Null, + InnerValue::Undefined => JsVariant::Undefined, + InnerValue::Integer32(i) => JsVariant::Integer32(*i), + InnerValue::Float64(d) => JsVariant::Float64(*d), + InnerValue::Boolean(b) => JsVariant::Boolean(*b), + InnerValue::Object(inner) => JsVariant::Object(inner), + InnerValue::String(inner) => JsVariant::String(inner), + InnerValue::Symbol(inner) => JsVariant::Symbol(inner), + InnerValue::BigInt(inner) => JsVariant::BigInt(inner), + } + } +} From 88b0ad62a0ace846732e877966fa0258d32c9263 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Sun, 8 Dec 2024 22:02:34 -0800 Subject: [PATCH 02/19] WIP --- cli/src/debug/gc.rs | 2 +- cli/src/debug/limits.rs | 6 +- cli/src/debug/optimizer.rs | 16 +- core/engine/src/builtins/array/mod.rs | 48 +-- core/engine/src/builtins/array/tests.rs | 4 +- core/engine/src/builtins/array_buffer/mod.rs | 12 +- core/engine/src/builtins/function/mod.rs | 22 +- .../iterable/async_from_sync_iterator.rs | 20 +- core/engine/src/builtins/iterable/mod.rs | 4 +- core/engine/src/builtins/map/mod.rs | 21 +- core/engine/src/builtins/number/globals.rs | 2 +- core/engine/src/builtins/number/mod.rs | 33 +- core/engine/src/builtins/object/mod.rs | 10 +- core/engine/src/builtins/regexp/mod.rs | 4 +- core/engine/src/builtins/set/mod.rs | 16 +- core/engine/src/lib.rs | 21 +- .../src/object/builtins/jstypedarray.rs | 60 ++-- core/engine/src/object/jsobject.rs | 2 +- core/engine/src/object/operations.rs | 14 +- core/engine/src/object/property_map.rs | 21 +- core/engine/src/value/conversions/convert.rs | 6 +- core/engine/src/value/conversions/mod.rs | 144 +++------ .../src/value/conversions/try_from_js.rs | 300 ++++-------------- core/engine/src/value/display.rs | 30 +- core/engine/src/value/equality.rs | 47 +-- core/engine/src/value/mod.rs | 246 ++++++++------ core/engine/src/value/variant.rs | 19 +- core/engine/src/vm/opcode/call/mod.rs | 12 +- .../engine/src/vm/opcode/control_flow/jump.rs | 4 +- core/engine/src/vm/opcode/push/array.rs | 2 +- .../src/vm/opcode/set/class_prototype.rs | 11 +- .../src/vm/opcode/unary_ops/decrement.rs | 11 +- .../src/vm/opcode/unary_ops/increment.rs | 11 +- core/engine/tests/imports.rs | 5 +- core/interop/src/lib.rs | 20 +- core/macros/src/lib.rs | 12 +- core/runtime/src/console/mod.rs | 224 ++++++------- examples/src/bin/derive.rs | 7 +- examples/src/bin/jsarray.rs | 8 +- examples/src/bin/jsmap.rs | 2 +- examples/src/bin/jstypedarray.rs | 36 +-- examples/src/bin/modulehandler.rs | 2 +- examples/src/bin/synthetic.rs | 32 +- 43 files changed, 685 insertions(+), 844 deletions(-) diff --git a/cli/src/debug/gc.rs b/cli/src/debug/gc.rs index 746fd899403..94fd9ff0520 100644 --- a/cli/src/debug/gc.rs +++ b/cli/src/debug/gc.rs @@ -5,7 +5,7 @@ use boa_engine::{ /// Trigger garbage collection. fn collect(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { boa_gc::force_collect(); - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } pub(super) fn create_object(context: &mut Context) -> JsObject { diff --git a/cli/src/debug/limits.rs b/cli/src/debug/limits.rs index b73e7a45fa5..93b5cb441dd 100644 --- a/cli/src/debug/limits.rs +++ b/cli/src/debug/limits.rs @@ -13,7 +13,7 @@ fn get_loop(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult JsResult { let value = args.get_or_undefined(0).to_length(context)?; context.runtime_limits_mut().set_loop_iteration_limit(value); - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } fn get_stack(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { @@ -29,7 +29,7 @@ fn set_stack(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult JsResult { @@ -45,7 +45,7 @@ fn set_recursion(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResu .into()); }; context.runtime_limits_mut().set_recursion_limit(value); - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } pub(super) fn create_object(context: &mut Context) -> JsObject { diff --git a/cli/src/debug/optimizer.rs b/cli/src/debug/optimizer.rs index 92793e93ba0..5ccbb9cf314 100644 --- a/cli/src/debug/optimizer.rs +++ b/cli/src/debug/optimizer.rs @@ -18,7 +18,7 @@ fn set_constant_folding(_: &JsValue, args: &[JsValue], context: &mut Context) -> let mut options = context.optimizer_options(); options.set(OptimizerOptions::CONSTANT_FOLDING, value); context.set_optimizer_options(options); - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } fn get_statistics(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { @@ -33,7 +33,7 @@ fn set_statistics(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsRes let mut options = context.optimizer_options(); options.set(OptimizerOptions::STATISTICS, value); context.set_optimizer_options(options); - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } pub(super) fn create_object(context: &mut Context) -> JsObject { @@ -41,16 +41,16 @@ pub(super) fn create_object(context: &mut Context) -> JsObject { context.realm(), NativeFunction::from_fn_ptr(get_constant_folding), ) - .name("get constantFolding") - .length(0) - .build(); + .name("get constantFolding") + .length(0) + .build(); let set_constant_folding = FunctionObjectBuilder::new( context.realm(), NativeFunction::from_fn_ptr(set_constant_folding), ) - .name("set constantFolding") - .length(1) - .build(); + .name("set constantFolding") + .length(1) + .build(); let get_statistics = FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(get_statistics)) diff --git a/core/engine/src/builtins/array/mod.rs b/core/engine/src/builtins/array/mod.rs index f392efbe74f..3c0a02626b1 100644 --- a/core/engine/src/builtins/array/mod.rs +++ b/core/engine/src/builtins/array/mod.rs @@ -38,7 +38,9 @@ use std::cmp::{min, Ordering}; use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject}; mod array_iterator; +use crate::value::JsVariant; pub(crate) use array_iterator::ArrayIterator; + #[cfg(test)] mod tests; @@ -87,8 +89,8 @@ impl IntrinsicObject for Array { realm.intrinsics().objects().array_prototype_values().into(), Self::values, ) - .name(js_string!("values")) - .build(); + .name(js_string!("values")) + .build(); let to_string_function = BuiltInBuilder::callable_with_object( realm, @@ -99,8 +101,8 @@ impl IntrinsicObject for Array { .into(), Self::to_string, ) - .name(js_string!("toString")) - .build(); + .name(js_string!("toString")) + .build(); let unscopables_object = Self::unscopables_object(); @@ -294,11 +296,11 @@ impl Array { let mut borrowed_object = o.borrow_mut(); if borrowed_object.properties().shape.to_addr_usize() == context - .intrinsics() - .templates() - .array() - .shape() - .to_addr_usize() + .intrinsics() + .templates() + .array() + .shape() + .to_addr_usize() { // NOTE: The "length" property is the first element. borrowed_object.properties_mut().storage[0] = JsValue::new(len); @@ -386,7 +388,7 @@ impl Array { /// [spec]: https://tc39.es/ecma262/#sec-createarrayfromlist pub(crate) fn create_array_from_list(elements: I, context: &Context) -> JsObject where - I: IntoIterator, + I: IntoIterator, { // 1. Assert: elements is a List whose elements are all ECMAScript language values. // 2. Let array be ! ArrayCreate(0). @@ -537,9 +539,9 @@ impl Array { // 3. Else, // a. If IsCallable(mapfn) is false, throw a TypeError exception. // b. Let mapping be true. - let mapping = match mapfn { - JsValue::Undefined => None, - JsValue::Object(o) if o.is_callable() => Some(o), + let mapping = match mapfn.variant() { + JsVariant::Undefined => None, + JsVariant::Object(o) if o.is_callable() => Some(o), _ => { return Err(JsNativeError::typ() .with_message(format!("`{}` is not callable", mapfn.type_of())) @@ -2665,9 +2667,9 @@ impl Array { context: &mut Context, ) -> JsResult { // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception. - let comparefn = match args.get_or_undefined(0) { - JsValue::Object(ref obj) if obj.is_callable() => Some(obj), - JsValue::Undefined => None, + let comparefn = match args.get_or_undefined(0).variant() { + JsVariant::Object(obj) if obj.is_callable() => Some(obj), + JsVariant::Undefined => None, _ => { return Err(JsNativeError::typ() .with_message("The comparison function must be either a function or undefined") @@ -2728,9 +2730,9 @@ impl Array { context: &mut Context, ) -> JsResult { // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception. - let comparefn = match args.get_or_undefined(0) { - JsValue::Object(ref obj) if obj.is_callable() => Some(obj), - JsValue::Undefined => None, + let comparefn = match args.get_or_undefined(0).variant() { + JsVariant::Object(obj) if obj.is_callable() => Some(obj), + JsVariant::Undefined => None, _ => { return Err(JsNativeError::typ() .with_message("The comparison function must be either a function or undefined") @@ -3319,7 +3321,7 @@ fn compare_array_elements( let args = [x.clone(), y.clone()]; // a. Let v be ? ToNumber(? Call(comparefn, undefined, « x, y »)). let v = cmp - .call(&JsValue::Undefined, &args, context)? + .call(&JsValue::UNDEFINED, &args, context)? .to_number(context)?; // b. If v is NaN, return +0𝔽. // c. Return v. @@ -3573,7 +3575,7 @@ fn array_set_length( new_len_desc.clone().build(), context, ) - .expect("this OrdinaryDefineOwnProperty call must not fail") + .expect("this OrdinaryDefineOwnProperty call must not fail") { return Ok(false); } @@ -3610,7 +3612,7 @@ fn array_set_length( new_len_desc.build(), context, ) - .expect("this OrdinaryDefineOwnProperty call must not fail"); + .expect("this OrdinaryDefineOwnProperty call must not fail"); // iv. Return false. return Ok(false); @@ -3627,7 +3629,7 @@ fn array_set_length( PropertyDescriptor::builder().writable(false).build(), context, ) - .expect("this OrdinaryDefineOwnProperty call must not fail"); + .expect("this OrdinaryDefineOwnProperty call must not fail"); // b. Assert: succeeded is true. debug_assert!(succeeded); diff --git a/core/engine/src/builtins/array/tests.rs b/core/engine/src/builtins/array/tests.rs index 4df066ca815..152fa3ef16a 100644 --- a/core/engine/src/builtins/array/tests.rs +++ b/core/engine/src/builtins/array/tests.rs @@ -875,7 +875,7 @@ fn array_spread_non_iterable() { fn get_relative_start() { #[track_caller] fn assert(context: &mut Context, arg: Option<&JsValue>, len: u64, expected: u64) { - let arg = arg.unwrap_or(&JsValue::Undefined); + let arg = arg.unwrap_or(&JsValue::UNDEFINED); assert_eq!( Array::get_relative_start(context, arg, len).unwrap(), expected @@ -902,7 +902,7 @@ fn get_relative_start() { fn get_relative_end() { #[track_caller] fn assert(context: &mut Context, arg: Option<&JsValue>, len: u64, expected: u64) { - let arg = arg.unwrap_or(&JsValue::Undefined); + let arg = arg.unwrap_or(&JsValue::UNDEFINED); assert_eq!( Array::get_relative_end(context, arg, len).unwrap(), expected diff --git a/core/engine/src/builtins/array_buffer/mod.rs b/core/engine/src/builtins/array_buffer/mod.rs index 7c2e3197022..ac0aea47ea4 100644 --- a/core/engine/src/builtins/array_buffer/mod.rs +++ b/core/engine/src/builtins/array_buffer/mod.rs @@ -50,8 +50,8 @@ pub(crate) enum BufferRef { impl BufferRef where - B: Deref, - S: Deref, + B: Deref, + S: Deref, { /// Gets the inner data of the buffer. pub(crate) fn bytes(&self, ordering: Ordering) -> Option> { @@ -89,8 +89,8 @@ pub(crate) enum BufferRefMut { impl BufferRefMut where - B: DerefMut, - S: DerefMut, + B: DerefMut, + S: DerefMut, { pub(crate) fn bytes(&mut self, ordering: Ordering) -> Option> { match self { @@ -828,7 +828,7 @@ impl ArrayBuffer { detach_key: JsValue::undefined(), }, ) - .into()) + .into()) } /// `AllocateArrayBuffer ( constructor, byteLength )` @@ -880,7 +880,7 @@ impl ArrayBuffer { // 8. If allocatingResizableBuffer is true, then // c. Set obj.[[ArrayBufferMaxByteLength]] to maxByteLength. max_byte_len, - detach_key: JsValue::Undefined, + detach_key: JsValue::UNDEFINED, }, ); diff --git a/core/engine/src/builtins/function/mod.rs b/core/engine/src/builtins/function/mod.rs index 94751379289..e5848c72f3a 100644 --- a/core/engine/src/builtins/function/mod.rs +++ b/core/engine/src/builtins/function/mod.rs @@ -494,8 +494,8 @@ impl BuiltInFunctionObject { param_list.iter().map(JsString::iter), js_str!(",").iter(), ) - .flatten() - .collect::>(); + .flatten() + .collect::>(); let mut parser = Parser::new(Source::from_utf16(¶meters)); parser.set_identifier(context.next_parser_identifier()); @@ -511,12 +511,12 @@ impl BuiltInFunctionObject { // It is a Syntax Error if FormalParameters Contains YieldExpression is true. if generator && contains(¶meters, ContainsSymbol::YieldExpression) { return Err(JsNativeError::syntax().with_message( - if r#async { - "yield expression is not allowed in formal parameter list of async generator" - } else { - "yield expression is not allowed in formal parameter list of generator" - } - ).into()); + if r#async { + "yield expression is not allowed in formal parameter list of async generator" + } else { + "yield expression is not allowed in formal parameter list of generator" + } + ).into()); } // It is a Syntax Error if FormalParameters Contains AwaitExpression is true. @@ -735,7 +735,7 @@ impl BuiltInFunctionObject { let f = BoundFunction::create(target.clone(), this_arg, bound_args, context)?; // 4. Let L be 0. - let mut l = JsValue::new(0); + let mut l = JsValue::ZERO; // 5. Let targetHasLength be ? HasOwnProperty(Target, "length"). // 6. If targetHasLength is true, then @@ -774,7 +774,7 @@ impl BuiltInFunctionObject { .configurable(true), context, ) - .expect("defining the `length` property for a new object should not fail"); + .expect("defining the `length` property for a new object should not fail"); // 8. Let targetName be ? Get(Target, "name"). let target_name = target.get(js_string!("name"), context)?; @@ -875,7 +875,7 @@ impl BuiltInFunctionObject { code.name(), js_str!("() { [native code] }") ) - .into()) + .into()) } /// `Function.prototype [ @@hasInstance ] ( V )` diff --git a/core/engine/src/builtins/iterable/async_from_sync_iterator.rs b/core/engine/src/builtins/iterable/async_from_sync_iterator.rs index 00c51940f9e..91b9fba9c7f 100644 --- a/core/engine/src/builtins/iterable/async_from_sync_iterator.rs +++ b/core/engine/src/builtins/iterable/async_from_sync_iterator.rs @@ -109,7 +109,7 @@ impl AsyncFromSyncIterator { &context.intrinsics().constructors().promise().constructor(), context, ) - .expect("cannot fail with promise constructor"); + .expect("cannot fail with promise constructor"); // 5. If value is present, then // a. Let result be Completion(IteratorNext(syncIteratorRecord, value)). @@ -154,7 +154,7 @@ impl AsyncFromSyncIterator { &context.intrinsics().constructors().promise().constructor(), context, ) - .expect("cannot fail with promise constructor"); + .expect("cannot fail with promise constructor"); // 6. Let return be Completion(GetMethod(syncIterator, "return")). let r#return = sync_iterator.get_method(js_string!("return"), context); @@ -172,7 +172,7 @@ impl AsyncFromSyncIterator { // b. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »). promise_capability .resolve() - .call(&JsValue::Undefined, &[iter_result], context) + .call(&JsValue::UNDEFINED, &[iter_result], context) .expect("cannot fail according to spec"); // c. Return promiseCapability.[[Promise]]. @@ -231,7 +231,7 @@ impl AsyncFromSyncIterator { &context.intrinsics().constructors().promise().constructor(), context, ) - .expect("cannot fail with promise constructor"); + .expect("cannot fail with promise constructor"); // 6. Let throw be Completion(GetMethod(syncIterator, "throw")). let throw = sync_iterator.get_method(js_string!("throw"), context); @@ -359,9 +359,9 @@ impl AsyncFromSyncIterator { )) }), ) - .name(js_string!()) - .length(1) - .build(); + .name(js_string!()) + .length(1) + .build(); // 11. NOTE: onFulfilled is used when processing the "value" property of an // IteratorResult object in order to wait for its value if it is a promise and @@ -392,9 +392,9 @@ impl AsyncFromSyncIterator { sync_iterator_record, ), ) - .name(js_string!()) - .length(1) - .build(), + .name(js_string!()) + .length(1) + .build(), ) }; diff --git a/core/engine/src/builtins/iterable/mod.rs b/core/engine/src/builtins/iterable/mod.rs index ba5a1d6ad57..ae06b53798d 100644 --- a/core/engine/src/builtins/iterable/mod.rs +++ b/core/engine/src/builtins/iterable/mod.rs @@ -314,8 +314,8 @@ impl IteratorResult { /// Gets a new `IteratorResult` from a value. Returns `Err` if /// the value is not a [`JsObject`] pub(crate) fn from_value(value: JsValue) -> JsResult { - if let JsValue::Object(o) = value { - Ok(Self { object: o }) + if let Some(object) = value.into_object() { + Ok(Self { object }) } else { Err(JsNativeError::typ() .with_message("next value should be an object") diff --git a/core/engine/src/builtins/map/mod.rs b/core/engine/src/builtins/map/mod.rs index bdd39e52c76..ea6bfc5d52a 100644 --- a/core/engine/src/builtins/map/mod.rs +++ b/core/engine/src/builtins/map/mod.rs @@ -33,7 +33,9 @@ mod map_iterator; pub(crate) use map_iterator::MapIterator; pub mod ordered_map; +use crate::value::JsVariant; use ordered_map::OrderedMap; + #[cfg(test)] mod tests; @@ -238,11 +240,11 @@ impl Map { // 2. Perform ? RequireInternalSlot(M, [[MapData]]). // 3. Let entries be the List that is M.[[MapData]]. if let Some(mut map) = object.downcast_mut::>() { - let key = match key { - JsValue::Rational(r) => { + let key = match key.variant() { + JsVariant::Float64(r) => { // 5. If key is -0𝔽, set key to +0𝔽. if r.is_zero() { - JsValue::Rational(0f64) + JsValue::ZERO } else { key.clone() } @@ -306,10 +308,9 @@ impl Map { /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.delete /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete pub(crate) fn delete(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { - const JS_ZERO: &JsValue = &JsValue::Integer(0); let key = args.get_or_undefined(0); let key = match key.as_number() { - Some(n) if n.is_zero() => JS_ZERO, + Some(n) if n.is_zero() => &JsValue::ZERO, _ => key, }; @@ -342,15 +343,14 @@ impl Map { /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.get /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get pub(crate) fn get(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { - const JS_ZERO: &JsValue = &JsValue::Integer(0); let key = args.get_or_undefined(0); let key = match key.as_number() { - Some(n) if n.is_zero() => JS_ZERO, + Some(n) if n.is_zero() => &JsValue::ZERO, _ => key, }; // 1. Let M be the this value. - if let JsValue::Object(ref object) = this { + if let Some(object) = this.as_object() { // 2. Perform ? RequireInternalSlot(M, [[MapData]]). // 3. Let entries be the List that is M.[[MapData]]. if let Some(map) = object.downcast_ref::>() { @@ -407,15 +407,14 @@ impl Map { /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.has /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has pub(crate) fn has(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { - const JS_ZERO: &JsValue = &JsValue::Integer(0); let key = args.get_or_undefined(0); let key = match key.as_number() { - Some(n) if n.is_zero() => JS_ZERO, + Some(n) if n.is_zero() => &JsValue::ZERO, _ => key, }; // 1. Let M be the this value. - if let JsValue::Object(ref object) = this { + if let Some(object) = this.as_object() { // 2. Perform ? RequireInternalSlot(M, [[MapData]]). // 3. Let entries be the List that is M.[[MapData]]. if let Some(map) = object.downcast_ref::>() { diff --git a/core/engine/src/builtins/number/globals.rs b/core/engine/src/builtins/number/globals.rs index 6fa8813f8d9..9c34578d0b5 100644 --- a/core/engine/src/builtins/number/globals.rs +++ b/core/engine/src/builtins/number/globals.rs @@ -254,7 +254,7 @@ pub(crate) fn parse_int(_: &JsValue, args: &[JsValue], context: &mut Context) -> return Ok(JsValue::new(-0_f64)); } - return Ok(JsValue::new(0_f64)); + return Ok(JsValue::ZERO); } // 16. Return 𝔽(sign × mathInt). diff --git a/core/engine/src/builtins/number/mod.rs b/core/engine/src/builtins/number/mod.rs index d1b09d4ecfe..6f931b87c92 100644 --- a/core/engine/src/builtins/number/mod.rs +++ b/core/engine/src/builtins/number/mod.rs @@ -33,9 +33,9 @@ pub(crate) use globals::{IsFinite, IsNaN, ParseFloat, ParseInt}; mod conversions; -pub(crate) use conversions::{f64_to_int32, f64_to_uint32}; - use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject}; +use crate::value::JsVariant; +pub(crate) use conversions::{f64_to_int32, f64_to_uint32}; #[cfg(test)] mod tests; @@ -224,7 +224,8 @@ impl Number { // 1. Let x be ? thisNumberValue(this value). let this_num = Self::this_number_value(this)?; let precision = match args.first() { - None | Some(JsValue::Undefined) => None, + None => None, + Some(x) if x.is_undefined() => None, // 2. Let f be ? ToIntegerOrInfinity(fractionDigits). Some(n) => Some(n.to_integer_or_infinity(context)?), }; @@ -238,9 +239,9 @@ impl Number { None => f64_to_exponential(this_num), Some(IntegerOrInfinity::Integer(precision)) if (0..=100).contains(&precision) => // 5. If f < 0 or f > 100, throw a RangeError exception. - { - f64_to_exponential_with_precision(this_num, precision as usize) - } + { + f64_to_exponential_with_precision(this_num, precision as usize) + } _ => { return Err(JsNativeError::range() .with_message("toExponential() argument must be between 0 and 100") @@ -750,9 +751,9 @@ impl Number { // 1. If number is not a Number, return false. // 2. If number is not finite, return false. // 3. Otherwise, return true. - Ok(JsValue::new(args.first().map_or(false, |val| match val { - JsValue::Integer(_) => true, - JsValue::Rational(number) => number.is_finite(), + Ok(JsValue::new(args.first().map_or(false, |val| match val.variant() { + JsVariant::Integer32(_) => true, + JsVariant::Float64(number) => number.is_finite(), _ => false, }))) } @@ -797,7 +798,7 @@ impl Number { _ctx: &mut Context, ) -> JsResult { Ok(JsValue::new( - if let Some(&JsValue::Rational(number)) = args.first() { + if let Some(number) = args.first().and_then(JsValue::as_number) { number.is_nan() } else { false @@ -825,9 +826,9 @@ impl Number { args: &[JsValue], _ctx: &mut Context, ) -> JsResult { - Ok(JsValue::new(match args.first() { - Some(JsValue::Integer(_)) => true, - Some(JsValue::Rational(number)) if Self::is_float_integer(*number) => { + Ok(JsValue::new(match args.first().map(JsValue::variant) { + Some(JsVariant::Integer32(_)) => true, + Some(JsVariant::Float64(number)) if Self::is_float_integer(number) => { number.abs() <= Self::MAX_SAFE_INTEGER } _ => false, @@ -841,9 +842,9 @@ impl Number { /// /// [spec]: https://tc39.es/ecma262/#sec-isinteger pub(crate) fn is_integer(val: &JsValue) -> bool { - match val { - JsValue::Integer(_) => true, - JsValue::Rational(number) => Self::is_float_integer(*number), + match val.variant() { + JsVariant::Integer32(_) => true, + JsVariant::Float64(number) => Self::is_float_integer(number), _ => false, } } diff --git a/core/engine/src/builtins/object/mod.rs b/core/engine/src/builtins/object/mod.rs index 30a022875b7..171725d354a 100644 --- a/core/engine/src/builtins/object/mod.rs +++ b/core/engine/src/builtins/object/mod.rs @@ -164,10 +164,10 @@ impl BuiltInConstructor for OrdinaryObject { // 1. If NewTarget is neither undefined nor the active function object, then if !new_target.is_undefined() && new_target - != &context - .active_function_object() - .unwrap_or_else(|| context.intrinsics().constructors().object().constructor()) - .into() + != &context + .active_function_object() + .unwrap_or_else(|| context.intrinsics().constructors().object().constructor()) + .into() { // a. Return ? OrdinaryCreateFromConstructor(NewTarget, "%Object.prototype%"). let prototype = @@ -215,7 +215,7 @@ impl OrdinaryObject { // 2. Return ? O.[[GetPrototypeOf]](). let proto = obj.__get_prototype_of__(&mut InternalMethodContext::new(context))?; - Ok(proto.map_or(JsValue::Null, JsValue::new)) + Ok(proto.map_or(JsValue::NULL, JsValue::new)) } /// `set Object.prototype.__proto__` diff --git a/core/engine/src/builtins/regexp/mod.rs b/core/engine/src/builtins/regexp/mod.rs index 9fb6a1f7c29..53799b93f6c 100644 --- a/core/engine/src/builtins/regexp/mod.rs +++ b/core/engine/src/builtins/regexp/mod.rs @@ -1640,7 +1640,7 @@ impl RegExp { &JsString::from(&accumulated_result[..]), s.get_expect(next_source_position..) ) - .into()) + .into()) } /// `RegExp.prototype[ @@search ]( string )` @@ -1672,7 +1672,7 @@ impl RegExp { let previous_last_index = rx.get(js_string!("lastIndex"), context)?; // 5. If SameValue(previousLastIndex, +0𝔽) is false, then - if !JsValue::same_value(&previous_last_index, &JsValue::new(0)) { + if !JsValue::same_value(&previous_last_index, &JsValue::ZERO) { // a. Perform ? Set(rx, "lastIndex", +0𝔽, true). rx.set(js_string!("lastIndex"), 0, true, context)?; } diff --git a/core/engine/src/builtins/set/mod.rs b/core/engine/src/builtins/set/mod.rs index 1b7d463ea50..893863d96bb 100644 --- a/core/engine/src/builtins/set/mod.rs +++ b/core/engine/src/builtins/set/mod.rs @@ -186,7 +186,7 @@ impl Set { /// Utility for constructing `Set` objects from an iterator of `JsValue`'s. pub(crate) fn create_set_from_list(elements: I, context: &mut Context) -> JsObject where - I: IntoIterator, + I: IntoIterator, { // Create empty Set let set = Self::set_create(None, context); @@ -226,8 +226,6 @@ impl Set { /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.add /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/add pub(crate) fn add(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { - const JS_ZERO: &JsValue = &JsValue::Integer(0); - // 1. Let S be the this value. // 2. Perform ? RequireInternalSlot(S, [[SetData]]). let Some(mut set) = this @@ -245,7 +243,7 @@ impl Set { // 4. If value is -0𝔽, set value to +0𝔽. let value = args.get_or_undefined(0); let value = match value.as_number() { - Some(n) if n.is_zero() => JS_ZERO, + Some(n) if n.is_zero() => &JsValue::ZERO, _ => value, }; @@ -293,8 +291,6 @@ impl Set { /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.delete /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/delete pub(crate) fn delete(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { - const JS_ZERO: &JsValue = &JsValue::Integer(0); - // 1. Let S be the this value. // 2. Perform ? RequireInternalSlot(S, [[SetData]]). let Some(mut set) = this @@ -308,7 +304,7 @@ impl Set { let value = args.get_or_undefined(0); let value = match value.as_number() { - Some(n) if n.is_zero() => JS_ZERO, + Some(n) if n.is_zero() => &JsValue::ZERO, _ => value, }; @@ -426,7 +422,7 @@ impl Set { drop(lock); // 8. Return undefined. - Ok(JsValue::Undefined) + Ok(JsValue::UNDEFINED) } /// `Map.prototype.has( key )` @@ -440,8 +436,6 @@ impl Set { /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.has /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has pub(crate) fn has(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { - const JS_ZERO: &JsValue = &JsValue::Integer(0); - // 1. Let S be the this value. // 2. Perform ? RequireInternalSlot(S, [[SetData]]). let Some(set) = this @@ -455,7 +449,7 @@ impl Set { let value = args.get_or_undefined(0); let value = match value.as_number() { - Some(n) if n.is_zero() => JS_ZERO, + Some(n) if n.is_zero() => &JsValue::ZERO, _ => value, }; diff --git a/core/engine/src/lib.rs b/core/engine/src/lib.rs index 7f1ad5d0027..28dc6b46f0b 100644 --- a/core/engine/src/lib.rs +++ b/core/engine/src/lib.rs @@ -50,7 +50,10 @@ html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" )] -#![cfg_attr(test, allow(clippy::needless_raw_string_hashes))] // Makes strings a bit more copy-pastable +#![cfg_attr( + test, + allow(clippy::needless_raw_string_hashes) +)] // Makes strings a bit more copy-pastable #![cfg_attr(not(test), forbid(clippy::unwrap_used))] #![allow( // Currently throws a false positive regarding dependencies that are only used in benchmarks. @@ -73,11 +76,9 @@ clippy::missing_panics_doc, )] -#[cfg(not(target_has_atomic = "ptr"))] +extern crate self as boa_engine;#[cfg(not(target_has_atomic = "ptr"))] compile_error!("Boa requires a lock free `AtomicUsize` in order to work properly."); -extern crate self as boa_engine; - pub use boa_ast as ast; pub use boa_gc as gc; pub use boa_interner as interner; @@ -162,8 +163,8 @@ mod try_into_js_result_impls; /// A utility trait to make working with function arguments easier. pub trait JsArgs { - /// Utility function to `get` a parameter from a `[JsValue]` or default to `JsValue::Undefined` - /// if `get` returns `None`. + /// Utility function to `get` a parameter from a `[JsValue]` or default to + /// `JsValue::undefined()` if `get` returns `None`. /// /// Call this if you are thinking of calling something similar to /// `args.get(n).cloned().unwrap_or_default()` or @@ -175,7 +176,7 @@ pub trait JsArgs { impl JsArgs for [JsValue] { fn get_or_undefined(&self, index: usize) -> &JsValue { - const UNDEFINED: &JsValue = &JsValue::Undefined; + const UNDEFINED: &JsValue = &JsValue::undefined(); self.get(index).unwrap_or(UNDEFINED) } } @@ -305,7 +306,7 @@ impl TestAction { /// Executes a list of test actions on a new, default context. #[cfg(test)] #[track_caller] -fn run_test_actions(actions: impl IntoIterator) { +fn run_test_actions(actions: impl IntoIterator) { let context = &mut Context::default(); run_test_actions_with(actions, context); } @@ -313,7 +314,7 @@ fn run_test_actions(actions: impl IntoIterator) { /// Executes a list of test actions on the provided context. #[cfg(test)] #[track_caller] -fn run_test_actions_with(actions: impl IntoIterator, context: &mut Context) { +fn run_test_actions_with(actions: impl IntoIterator, context: &mut Context) { #[track_caller] fn forward_val(context: &mut Context, source: &str) -> JsResult { context.eval(Source::from_bytes(source)) @@ -352,7 +353,7 @@ fn run_test_actions_with(actions: impl IntoIterator, context: } "#, ) - .expect("failed to evaluate test harness"); + .expect("failed to evaluate test harness"); } Inner::Run { source } => { if let Err(e) = forward_val(context, &source) { diff --git a/core/engine/src/object/builtins/jstypedarray.rs b/core/engine/src/object/builtins/jstypedarray.rs index e694e2d0868..ac577f30033 100644 --- a/core/engine/src/object/builtins/jstypedarray.rs +++ b/core/engine/src/object/builtins/jstypedarray.rs @@ -222,8 +222,8 @@ impl JsTypedArray { &[predicate.into(), this_arg.into_or_undefined()], context, )? - .as_boolean() - .expect("TypedArray.prototype.every should always return boolean"); + .as_boolean() + .expect("TypedArray.prototype.every should always return boolean"); Ok(result) } @@ -241,8 +241,8 @@ impl JsTypedArray { &[callback.into(), this_arg.into_or_undefined()], context, )? - .as_boolean() - .expect("TypedArray.prototype.some should always return boolean"); + .as_boolean() + .expect("TypedArray.prototype.some should always return boolean"); Ok(result) } @@ -418,15 +418,15 @@ impl JsTypedArray { /// Some(3), /// context, /// )?; - /// assert_eq!(initialized8_array.get(0, context)?, JsValue::new(0)); - /// assert_eq!(initialized8_array.get(1, context)?, JsValue::new(0)); - /// assert_eq!(initialized8_array.get(2, context)?, JsValue::new(0)); + /// assert_eq!(initialized8_array.get(0, context)?, JsValue::ZERO); + /// assert_eq!(initialized8_array.get(1, context)?, JsValue::ZERO); + /// assert_eq!(initialized8_array.get(2, context)?, JsValue::ZERO); /// assert_eq!(initialized8_array.get(3, context)?, JsValue::new(1.0)); /// assert_eq!(initialized8_array.get(4, context)?, JsValue::new(2.0)); - /// assert_eq!(initialized8_array.get(5, context)?, JsValue::new(0)); - /// assert_eq!(initialized8_array.get(6, context)?, JsValue::new(0)); - /// assert_eq!(initialized8_array.get(7, context)?, JsValue::new(0)); - /// assert_eq!(initialized8_array.get(8, context)?, JsValue::Undefined); + /// assert_eq!(initialized8_array.get(5, context)?, JsValue::ZERO); + /// assert_eq!(initialized8_array.get(6, context)?, JsValue::ZERO); + /// assert_eq!(initialized8_array.get(7, context)?, JsValue::ZERO); + /// assert_eq!(initialized8_array.get(8, context)?, JsValue::UNDEFINED); /// /// # Ok(()) /// # } @@ -506,7 +506,7 @@ impl JsTypedArray { /// .unwrap_or_default() /// .as_number() /// .expect("error at number conversion"); - /// Ok(JsValue::Boolean(element > 10.0)) + /// Ok(JsValue::from(element > 10.0)) /// }), /// ) /// .build(); @@ -530,8 +530,8 @@ impl JsTypedArray { &[predicate.into(), this_arg.into_or_undefined()], context, )? - .as_number() - .expect("TypedArray.prototype.findIndex() should always return number"); + .as_number() + .expect("TypedArray.prototype.findIndex() should always return number"); if index >= 0.0 { Ok(Some(index as u64)) @@ -564,13 +564,13 @@ impl JsTypedArray { /// .unwrap_or_default() /// .as_number() /// .expect("error at number conversion"); - /// Ok(JsValue::Boolean(element < 200.0)) + /// Ok(JsValue::from(element < 200.0)) /// }), /// ) /// .build(); /// assert_eq!( /// array.find_last(lower_than_200_predicate.clone(), None, context), - /// Ok(JsValue::Integer(199)) + /// Ok(JsValue::new(199)) /// ); /// /// # Ok(()) @@ -614,7 +614,7 @@ impl JsTypedArray { /// .unwrap_or_default() /// .as_number() /// .expect("error at number conversion"); - /// Ok(JsValue::Boolean(element < 200.0)) + /// Ok(JsValue::from(element < 200.0)) /// }), /// ) /// .build(); @@ -638,8 +638,8 @@ impl JsTypedArray { &[predicate.into(), this_arg.into_or_undefined()], context, )? - .as_number() - .expect("TypedArray.prototype.findLastIndex() should always return number"); + .as_number() + .expect("TypedArray.prototype.findLastIndex() should always return number"); if index >= 0.0 { Ok(Some(index as u64)) @@ -741,8 +741,8 @@ impl JsTypedArray { &[search_element.into(), from_index.into_or_undefined()], context, )? - .as_boolean() - .expect("TypedArray.prototype.includes should always return boolean"); + .as_boolean() + .expect("TypedArray.prototype.includes should always return boolean"); Ok(result) } @@ -762,8 +762,8 @@ impl JsTypedArray { &[search_element.into(), from_index.into_or_undefined()], context, )? - .as_number() - .expect("TypedArray.prototype.indexOf should always return number"); + .as_number() + .expect("TypedArray.prototype.indexOf should always return number"); #[allow(clippy::float_cmp)] if index == -1.0 { @@ -788,8 +788,8 @@ impl JsTypedArray { &[search_element.into(), from_index.into_or_undefined()], context, )? - .as_number() - .expect("TypedArray.prototype.lastIndexOf should always return number"); + .as_number() + .expect("TypedArray.prototype.lastIndexOf should always return number"); #[allow(clippy::float_cmp)] if index == -1.0 { @@ -807,11 +807,11 @@ impl JsTypedArray { &[separator.into_or_undefined()], context, ) - .map(|x| { - x.as_string() - .cloned() - .expect("TypedArray.prototype.join always returns string") - }) + .map(|x| { + x.as_string() + .cloned() + .expect("TypedArray.prototype.join always returns string") + }) } /// Calls `TypedArray.prototype.toReversed ( )`. diff --git a/core/engine/src/object/jsobject.rs b/core/engine/src/object/jsobject.rs index db02acd3c37..0d28545a35c 100644 --- a/core/engine/src/object/jsobject.rs +++ b/core/engine/src/object/jsobject.rs @@ -319,7 +319,7 @@ impl JsObject { if recursion_limiter.live { // we're in a recursive object, bail return Ok(match hint { - PreferredType::Number => JsValue::new(0), + PreferredType::Number => JsValue::ZERO, PreferredType::String => JsValue::new(js_string!()), PreferredType::Default => unreachable!("checked type hint in step 2"), }); diff --git a/core/engine/src/object/operations.rs b/core/engine/src/object/operations.rs index 9a989b5cc28..f42a83e122d 100644 --- a/core/engine/src/object/operations.rs +++ b/core/engine/src/object/operations.rs @@ -1,3 +1,5 @@ +use super::internal_methods::InternalMethodContext; +use crate::value::JsVariant; use crate::{ builtins::{ function::{set_function_name, BoundFunction, ClassFieldDefinition, OrdinaryFunction}, @@ -14,8 +16,6 @@ use crate::{ Context, JsResult, JsSymbol, JsValue, }; -use super::internal_methods::InternalMethodContext; - /// Object integrity level. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum IntegrityLevel { @@ -726,7 +726,7 @@ impl JsObject { [key_str.into(), self.get(key.clone(), context)?], context, ) - .into(), + .into(), ), } } @@ -756,15 +756,15 @@ impl JsObject { // 1. Assert: IsPropertyKey(P) is true. // 2. Let func be ? GetV(V, P). - match &self.__get__( + match self.__get__( &key.into(), self.clone().into(), &mut InternalMethodContext::new(context), - )? { + )?.variant() { // 3. If func is either undefined or null, return undefined. - JsValue::Undefined | JsValue::Null => Ok(None), + JsVariant::Undefined | JsVariant::Null => Ok(None), // 5. Return func. - JsValue::Object(obj) if obj.is_callable() => Ok(Some(obj.clone())), + JsVariant::Object(obj) if obj.is_callable() => Ok(Some(obj.clone())), // 4. If IsCallable(func) is false, throw a TypeError exception. _ => Err(JsNativeError::typ() .with_message("value returned for property of object is not a function") diff --git a/core/engine/src/object/property_map.rs b/core/engine/src/object/property_map.rs index f995f66872a..cae390c6367 100644 --- a/core/engine/src/object/property_map.rs +++ b/core/engine/src/object/property_map.rs @@ -7,6 +7,7 @@ use super::{ }, JsPrototype, ObjectStorage, PropertyDescriptor, PropertyKey, }; +use crate::value::JsVariant; use crate::{property::PropertyDescriptorBuilder, JsValue}; use boa_gc::{custom_trace, Finalize, Trace}; use indexmap::IndexMap; @@ -160,10 +161,10 @@ impl IndexedProperties { // equal to the original then it is an integer. let is_rational_integer = |n: f64| n.to_bits() == f64::from(n as i32).to_bits(); - let value = match value { - JsValue::Integer(n) => n, - JsValue::Rational(n) if is_rational_integer(n) => n as i32, - JsValue::Rational(value) => { + let value = match value.variant() { + JsVariant::Integer32(n) => n, + JsVariant::Float64(n) if is_rational_integer(n) => n as i32, + JsVariant::Float64(value) => { let mut vec = vec.iter().copied().map(f64::from).collect::>(); // If the key is pointing one past the last element, we push it! @@ -180,7 +181,7 @@ impl IndexedProperties { *self = Self::DenseF64(vec); return true; } - value => { + _ => { let mut vec = vec .iter() .copied() @@ -624,10 +625,10 @@ impl PropertyMap { // equal to the original then it is an integer. let is_rational_integer = |n: f64| n.to_bits() == f64::from(n as i32).to_bits(); - let value = match value { - JsValue::Integer(n) => *n, - JsValue::Rational(n) if is_rational_integer(*n) => *n as i32, - JsValue::Rational(value) => { + let value = match value.variant() { + JsVariant::Integer32(n) => *n, + JsVariant::Float64(n) if is_rational_integer(*n) => *n as i32, + JsVariant::Float64(value) => { let mut properties = properties .iter() .copied() @@ -638,7 +639,7 @@ impl PropertyMap { return true; } - value => { + _ => { let mut properties = properties .iter() .copied() diff --git a/core/engine/src/value/conversions/convert.rs b/core/engine/src/value/conversions/convert.rs index a73344d6855..5a2c82cc808 100644 --- a/core/engine/src/value/conversions/convert.rs +++ b/core/engine/src/value/conversions/convert.rs @@ -33,11 +33,11 @@ use crate::{Context, JsData, JsResult, JsString, JsValue}; /// # use boa_engine::value::{Convert, TryFromJs}; /// # let mut context = Context::default(); /// let Convert(conv0): Convert = -/// Convert::try_from_js(&JsValue::Integer(0), &mut context).unwrap(); +/// Convert::try_from_js(&JsValue::ZERO, &mut context).unwrap(); /// let Convert(conv5): Convert = -/// Convert::try_from_js(&JsValue::Integer(5), &mut context).unwrap(); +/// Convert::try_from_js(&JsValue::new(5), &mut context).unwrap(); /// let Convert(conv_nan): Convert = -/// Convert::try_from_js(&JsValue::Rational(f64::NAN), &mut context).unwrap(); +/// Convert::try_from_js(&JsValue::new(f64::NAN), &mut context).unwrap(); /// /// assert_eq!(conv0, false); /// assert_eq!(conv5, true); diff --git a/core/engine/src/value/conversions/mod.rs b/core/engine/src/value/conversions/mod.rs index 3fa3841212e..273f6fe8c86 100644 --- a/core/engine/src/value/conversions/mod.rs +++ b/core/engine/src/value/conversions/mod.rs @@ -2,7 +2,7 @@ use crate::{js_string, string::JsStr}; -use super::{JsBigInt, JsObject, JsString, JsSymbol, JsValue, Profiler}; +use super::{InnerValue, JsBigInt, JsObject, JsString, JsSymbol, JsValue, Profiler}; mod either; mod serde_json; @@ -15,7 +15,7 @@ impl From> for JsValue { fn from(value: JsStr<'_>) -> Self { let _timer = Profiler::global().start_event("From>", "value"); - Self::String(value.into()) + Self::from_inner(InnerValue::String(value.into())) } } @@ -23,7 +23,7 @@ impl From for JsValue { fn from(value: JsString) -> Self { let _timer = Profiler::global().start_event("From", "value"); - Self::String(value) + Self::from_inner(InnerValue::String(value.into())) } } @@ -45,115 +45,54 @@ impl From for JsValue { fn from(value: JsSymbol) -> Self { let _timer = Profiler::global().start_event("From", "value"); - Self::Symbol(value) + Self::from_inner(InnerValue::Symbol(value)) } } -impl From for JsValue { - #[inline] - fn from(value: f32) -> Self { - let _timer = Profiler::global().start_event("From", "value"); - - Self::Rational(value.into()) - } -} - -impl From for JsValue { - #[inline] - fn from(value: f64) -> Self { - let _timer = Profiler::global().start_event("From", "value"); - - Self::Rational(value) - } -} - -impl From for JsValue { - #[inline] - fn from(value: u8) -> Self { - let _timer = Profiler::global().start_event("From", "value"); - - Self::Integer(value.into()) - } -} - -impl From for JsValue { - #[inline] - fn from(value: i8) -> Self { - let _timer = Profiler::global().start_event("From", "value"); - - Self::Integer(value.into()) - } -} - -impl From for JsValue { - #[inline] - fn from(value: u16) -> Self { - let _timer = Profiler::global().start_event("From", "value"); - - Self::Integer(value.into()) - } -} +macro_rules! impl_from_float { + ( $( $type_:ty ),* ) => { + $( + impl From<$type_> for JsValue { + #[inline] + #[allow(trivial_numeric_casts)] + fn from(value: $type_) -> Self { + let _timer = Profiler::global().start_event(concat!("From<", stringify!($type_), ">"), "value"); -impl From for JsValue { - #[inline] - fn from(value: i16) -> Self { - let _timer = Profiler::global().start_event("From", "value"); - - Self::Integer(value.into()) - } + if value.fract() == 0.0 && value <= i32::MAX as $type_ && value >= i32::MIN as $type_ { + Self::from(value as i32) + } else { + Self::from(value as f64) + } + } + } + )* + }; } -impl From for JsValue { - #[inline] - fn from(value: u32) -> Self { - let _timer = Profiler::global().start_event("From", "value"); +macro_rules! impl_from_integer { + ( $( $type_:ty ),* ) => { + $( + impl From<$type_> for JsValue { + #[inline] + fn from(value: $type_) -> Self { + let _timer = Profiler::global().start_event(concat!("From<", stringify!($type_), ">"), "value"); - i32::try_from(value).map_or_else(|_| Self::Rational(value.into()), Self::Integer) - } + i32::try_from(value).map_or_else(|_| Self::from(value as f64), Self::from) + } + } + )* + }; } -impl From for JsValue { - #[inline] - fn from(value: i32) -> Self { - let _timer = Profiler::global().start_event("From", "value"); - - Self::Integer(value) - } -} +impl_from_float!(f32, f64); +impl_from_integer!(u8, i8, u16, i16, u32, i32, u64, i64, usize, isize); impl From for JsValue { #[inline] fn from(value: JsBigInt) -> Self { let _timer = Profiler::global().start_event("From", "value"); - Self::BigInt(value) - } -} - -impl From for JsValue { - #[inline] - fn from(value: usize) -> Self { - let _timer = Profiler::global().start_event("From", "value"); - - i32::try_from(value).map_or(Self::Rational(value as f64), Self::Integer) - } -} - -impl From for JsValue { - #[inline] - fn from(value: u64) -> Self { - let _timer = Profiler::global().start_event("From", "value"); - - i32::try_from(value).map_or(Self::Rational(value as f64), Self::Integer) - } -} - -impl From for JsValue { - #[inline] - fn from(value: i64) -> Self { - let _timer = Profiler::global().start_event("From", "value"); - - i32::try_from(value).map_or(Self::Rational(value as f64), Self::Integer) + Self::from_inner(InnerValue::BigInt(value)) } } @@ -162,7 +101,7 @@ impl From for JsValue { fn from(value: bool) -> Self { let _timer = Profiler::global().start_event("From", "value"); - Self::Boolean(value) + Self::from_inner(InnerValue::Boolean(value)) } } @@ -171,7 +110,7 @@ impl From for JsValue { fn from(object: JsObject) -> Self { let _timer = Profiler::global().start_event("From", "value"); - Self::Object(object) + Self::from_inner(InnerValue::Object(object)) } } @@ -181,7 +120,7 @@ impl From<()> for JsValue { fn from(_: ()) -> Self { let _timer = Profiler::global().start_event("From<()>", "value"); - Self::null() + Self::NULL } } @@ -200,6 +139,9 @@ where { #[inline] fn into_or_undefined(self) -> JsValue { - self.map_or_else(JsValue::undefined, Into::into) + match self { + Some(value) => value.into(), + None => JsValue::UNDEFINED, + } } } diff --git a/core/engine/src/value/conversions/try_from_js.rs b/core/engine/src/value/conversions/try_from_js.rs index 42068c6c9ab..134a073f465 100644 --- a/core/engine/src/value/conversions/try_from_js.rs +++ b/core/engine/src/value/conversions/try_from_js.rs @@ -3,6 +3,7 @@ use num_bigint::BigInt; use num_traits::AsPrimitive; +use crate::value::InnerValue; use crate::{js_string, Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsValue}; mod collections; @@ -27,8 +28,8 @@ impl JsValue { impl TryFromJs for bool { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Boolean(b) => Ok(*b), + match value.inner { + InnerValue::Boolean(b) => Ok(*b), _ => Err(JsNativeError::typ() .with_message("cannot convert value to a boolean") .into()), @@ -44,8 +45,8 @@ impl TryFromJs for () { impl TryFromJs for String { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::String(s) => s.to_std_string().map_err(|e| { + match &value.inner { + InnerValue::String(s) => s.to_std_string().map_err(|e| { JsNativeError::typ() .with_message(format!("could not convert JsString to Rust string: {e}")) .into() @@ -59,8 +60,8 @@ impl TryFromJs for String { impl TryFromJs for JsString { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::String(s) => Ok(s.clone()), + match &value.inner { + InnerValue::String(s) => Ok(s.clone()), _ => Err(JsNativeError::typ() .with_message("cannot convert value to a String") .into()), @@ -73,9 +74,11 @@ where T: TryFromJs, { fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult { - match value { - JsValue::Null | JsValue::Undefined => Ok(None), - value => Ok(Some(T::try_from_js(value, context)?)), + // TODO: remove NULL -> None conversion. + if value.is_null_or_undefined() { + Ok(None) + } else { + Ok(Some(T::try_from_js(value, context)?)) } } } @@ -85,7 +88,7 @@ where T: TryFromJs, { fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult { - let JsValue::Object(object) = value else { + let InnerValue::Object(object) = &value.inner else { return Err(JsNativeError::typ() .with_message("cannot convert value to a Vec") .into()); @@ -114,8 +117,8 @@ where impl TryFromJs for JsObject { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Ok(o.clone()), + match &value.inner { + InnerValue::Object(o) => Ok(o.clone()), _ => Err(JsNativeError::typ() .with_message("cannot convert value to a Object") .into()), @@ -125,8 +128,8 @@ impl TryFromJs for JsObject { impl TryFromJs for JsBigInt { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::BigInt(b) => Ok(b.clone()), + match &value.inner { + InnerValue::BigInt(b) => Ok(b.clone()), _ => Err(JsNativeError::typ() .with_message("cannot convert value to a BigInt") .into()), @@ -136,8 +139,8 @@ impl TryFromJs for JsBigInt { impl TryFromJs for BigInt { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::BigInt(b) => Ok(b.as_inner().clone()), + match &value.inner { + InnerValue::BigInt(b) => Ok(b.as_inner().clone()), _ => Err(JsNativeError::typ() .with_message("cannot convert value to a BigInt") .into()), @@ -153,9 +156,9 @@ impl TryFromJs for JsValue { impl TryFromJs for f64 { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Integer(i) => Ok((*i).into()), - JsValue::Rational(r) => Ok(*r), + match &value.inner { + InnerValue::Integer32(i) => Ok((*i).into()), + InnerValue::Float64(r) => Ok(*r), _ => Err(JsNativeError::typ() .with_message("cannot convert value to a f64") .into()), @@ -174,213 +177,36 @@ where None } -impl TryFromJs for i8 { - fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Integer(i) => (*i).try_into().map_err(|e| { - JsNativeError::typ() - .with_message(format!("cannot convert value to a i8: {e}")) - .into() - }), - JsValue::Rational(f) => from_f64(*f).ok_or_else(|| { - JsNativeError::typ() - .with_message("cannot convert value to a i8") - .into() - }), - _ => Err(JsNativeError::typ() - .with_message("cannot convert value to a i8") - .into()), - } - } -} - -impl TryFromJs for u8 { - fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Integer(i) => (*i).try_into().map_err(|e| { - JsNativeError::typ() - .with_message(format!("cannot convert value to a u8: {e}")) - .into() - }), - JsValue::Rational(f) => from_f64(*f).ok_or_else(|| { - JsNativeError::typ() - .with_message("cannot convert value to a u8") - .into() - }), - _ => Err(JsNativeError::typ() - .with_message("cannot convert value to a u8") - .into()), - } - } -} - -impl TryFromJs for i16 { - fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Integer(i) => (*i).try_into().map_err(|e| { - JsNativeError::typ() - .with_message(format!("cannot convert value to a i16: {e}")) - .into() - }), - JsValue::Rational(f) => from_f64(*f).ok_or_else(|| { - JsNativeError::typ() - .with_message("cannot convert value to a i16") - .into() - }), - _ => Err(JsNativeError::typ() - .with_message("cannot convert value to a i16") - .into()), - } - } -} - -impl TryFromJs for u16 { - fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Integer(i) => (*i).try_into().map_err(|e| { - JsNativeError::typ() - .with_message(format!("cannot convert value to a iu16: {e}")) - .into() - }), - JsValue::Rational(f) => from_f64(*f).ok_or_else(|| { - JsNativeError::typ() - .with_message("cannot convert value to a u16") - .into() - }), - _ => Err(JsNativeError::typ() - .with_message("cannot convert value to a u16") - .into()), - } - } -} - -impl TryFromJs for i32 { - fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Integer(i) => Ok(*i), - JsValue::Rational(f) => from_f64(*f).ok_or_else(|| { - JsNativeError::typ() - .with_message("cannot convert value to a i32") - .into() - }), - _ => Err(JsNativeError::typ() - .with_message("cannot convert value to a i32") - .into()), - } - } -} - -impl TryFromJs for u32 { - fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Integer(i) => (*i).try_into().map_err(|e| { - JsNativeError::typ() - .with_message(format!("cannot convert value to a u32: {e}")) - .into() - }), - JsValue::Rational(f) => from_f64(*f).ok_or_else(|| { - JsNativeError::typ() - .with_message("cannot convert value to a u32") - .into() - }), - _ => Err(JsNativeError::typ() - .with_message("cannot convert value to a u32") - .into()), - } - } -} - -impl TryFromJs for i64 { - fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Integer(i) => Ok((*i).into()), - JsValue::Rational(f) => from_f64(*f).ok_or_else(|| { - JsNativeError::typ() - .with_message("cannot convert value to a i64") - .into() - }), - _ => Err(JsNativeError::typ() - .with_message("cannot convert value to a i64") - .into()), - } - } -} - -impl TryFromJs for u64 { - fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Integer(i) => (*i).try_into().map_err(|e| { - JsNativeError::typ() - .with_message(format!("cannot convert value to a u64: {e}")) - .into() - }), - JsValue::Rational(f) => from_f64(*f).ok_or_else(|| { - JsNativeError::typ() - .with_message("cannot convert value to a u64") - .into() - }), - _ => Err(JsNativeError::typ() - .with_message("cannot convert value to a u64") - .into()), - } - } -} - -impl TryFromJs for usize { - fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Integer(i) => (*i).try_into().map_err(|e| { - JsNativeError::typ() - .with_message(format!("cannot convert value to a usize: {e}")) - .into() - }), - JsValue::Rational(f) => from_f64(*f).ok_or_else(|| { - JsNativeError::typ() - .with_message("cannot convert value to a usize") - .into() - }), - _ => Err(JsNativeError::typ() - .with_message("cannot convert value to a usize") - .into()), - } +macro_rules! impl_try_from_js_integer { + ( $( $type: ty ),* ) => { + $( + impl TryFromJs for $type { + fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { + match &value.inner { + InnerValue::Integer32(i) => (*i).try_into().map_err(|e| { + JsNativeError::typ() + .with_message(format!( + concat!("cannot convert value to a ", stringify!($type), ": {}"), + e) + ) + .into() + }), + InnerValue::Float64(f) => from_f64(*f).ok_or_else(|| { + JsNativeError::typ() + .with_message(concat!("cannot convert value to a ", stringify!($type))) + .into() + }), + _ => Err(JsNativeError::typ() + .with_message(concat!("cannot convert value to a ", stringify!($type))) + .into()), + } + } + } + )* } } -impl TryFromJs for i128 { - fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Integer(i) => Ok((*i).into()), - JsValue::Rational(f) => from_f64(*f).ok_or_else(|| { - JsNativeError::typ() - .with_message("cannot convert value to a i128") - .into() - }), - _ => Err(JsNativeError::typ() - .with_message("cannot convert value to a i128") - .into()), - } - } -} - -impl TryFromJs for u128 { - fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Integer(i) => (*i).try_into().map_err(|e| { - JsNativeError::typ() - .with_message(format!("cannot convert value to a u128: {e}")) - .into() - }), - JsValue::Rational(f) => from_f64(*f).ok_or_else(|| { - JsNativeError::typ() - .with_message("cannot convert value to a u128") - .into() - }), - _ => Err(JsNativeError::typ() - .with_message("cannot convert value to a u128") - .into()), - } - } -} +impl_try_from_js_integer!(i8, u8, i16, u16, i32, u32, i64, u64, usize, i128, u128); #[test] fn integer_floating_js_value_to_integer() { @@ -441,10 +267,10 @@ fn value_into_vec() { Ok(value) => { value == TestStruct { - inner: true, - my_int: 11, - my_vec: vec!["a".to_string(), "b".to_string(), "c".to_string()], - } + inner: true, + my_int: 11, + my_vec: vec!["a".to_string(), "b".to_string(), "c".to_string()], + } } _ => false, } @@ -534,10 +360,10 @@ fn value_into_map() { Ok(value) => { value == vec![ - ("a".to_string(), 1), - ("b".to_string(), 2), - ("c".to_string(), 3), - ] + ("a".to_string(), 1), + ("b".to_string(), 2), + ("c".to_string(), 3), + ] .into_iter() .collect::>() } @@ -551,14 +377,14 @@ fn value_into_map() { Ok(value) => { value == std::collections::HashMap::from_iter( - vec![ - ("a".to_string(), 1), - ("b".to_string(), 2), - ("c".to_string(), 3), - ] + vec![ + ("a".to_string(), 1), + ("b".to_string(), 2), + ("c".to_string(), 3), + ] .into_iter() .collect::>(), - ) + ) } _ => false, } diff --git a/core/engine/src/value/display.rs b/core/engine/src/value/display.rs index 9320bb5dd67..44b86f1965a 100644 --- a/core/engine/src/value/display.rs +++ b/core/engine/src/value/display.rs @@ -1,4 +1,4 @@ -use super::{fmt, Display, HashSet, JsValue}; +use super::{fmt, Display, HashSet, JsValue, JsVariant}; use crate::{ builtins::{ error::ErrorObject, map::ordered_map::OrderedMap, promise::PromiseState, @@ -63,7 +63,7 @@ macro_rules! print_obj_value { vec![format!( "{:>width$}: {}", "__proto__", - JsValue::Null.display(), + JsValue::NULL.display(), width = $indent, )] } @@ -98,9 +98,9 @@ macro_rules! print_obj_value { } pub(crate) fn log_string_from(x: &JsValue, print_internals: bool, print_children: bool) -> String { - match x { + match x.variant() { // We don't want to print private (compiler) or prototype properties - JsValue::Object(ref v) => { + JsVariant::Object(v) => { // Can use the private "type" field of an Object to match on // which type of Object it represents for special printing let v_bor = v.borrow(); @@ -263,7 +263,7 @@ impl JsValue { indent: usize, print_internals: bool, ) -> String { - if let JsValue::Object(ref v) = *data { + if let Some(v) = data.as_object() { // The in-memory address of the current object let addr = address_of(v.as_ref()); @@ -307,20 +307,20 @@ impl JsValue { impl Display for ValueDisplay<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.value { - JsValue::Null => write!(f, "null"), - JsValue::Undefined => write!(f, "undefined"), - JsValue::Boolean(v) => write!(f, "{v}"), - JsValue::Symbol(ref symbol) => { + match self.value.variant() { + JsVariant::Null => write!(f, "null"), + JsVariant::Undefined => write!(f, "undefined"), + JsVariant::Boolean(v) => write!(f, "{v}"), + JsVariant::Symbol(symbol) => { write!(f, "{}", symbol.descriptive_string().to_std_string_escaped()) } - JsValue::String(ref v) => write!(f, "\"{}\"", v.to_std_string_escaped()), - JsValue::Rational(v) => format_rational(*v, f), - JsValue::Object(_) => { + JsVariant::String(v) => write!(f, "\"{}\"", v.to_std_string_escaped()), + JsVariant::Float64(v) => format_rational(v, f), + JsVariant::Object(_) => { write!(f, "{}", log_string_from(self.value, self.internals, true)) } - JsValue::Integer(v) => write!(f, "{v}"), - JsValue::BigInt(ref num) => write!(f, "{num}n"), + JsVariant::Integer32(v) => write!(f, "{v}"), + JsVariant::BigInt(num) => write!(f, "{num}n"), } } } diff --git a/core/engine/src/value/equality.rs b/core/engine/src/value/equality.rs index d14a95d1af2..e8858022003 100644 --- a/core/engine/src/value/equality.rs +++ b/core/engine/src/value/equality.rs @@ -1,4 +1,4 @@ -use super::{JsBigInt, JsObject, JsResult, JsValue, PreferredType}; +use super::{InnerValue, JsBigInt, JsObject, JsResult, JsValue, PreferredType}; use crate::{builtins::Number, Context}; impl JsValue { @@ -139,14 +139,18 @@ impl JsValue { return false; } - match (x, y) { + match (&x.inner, &y.inner) { // 2. If Type(x) is Number or BigInt, then // a. Return ! Type(x)::SameValue(x, y). - (Self::BigInt(x), Self::BigInt(y)) => JsBigInt::same_value(x, y), - (Self::Rational(x), Self::Rational(y)) => Number::same_value(*x, *y), - (Self::Rational(x), Self::Integer(y)) => Number::same_value(*x, f64::from(*y)), - (Self::Integer(x), Self::Rational(y)) => Number::same_value(f64::from(*x), *y), - (Self::Integer(x), Self::Integer(y)) => x == y, + (InnerValue::BigInt(x), InnerValue::BigInt(y)) => JsBigInt::same_value(x, y), + (InnerValue::Float64(x), InnerValue::Float64(y)) => Number::same_value(*x, *y), + (InnerValue::Float64(x), InnerValue::Integer32(y)) => { + Number::same_value(*x, f64::from(*y)) + } + (InnerValue::Integer32(x), InnerValue::Float64(y)) => { + Number::same_value(f64::from(*x), *y) + } + (InnerValue::Integer32(x), InnerValue::Integer32(y)) => x == y, // 3. Return ! SameValueNonNumeric(x, y). (_, _) => Self::same_value_non_numeric(x, y), @@ -168,15 +172,19 @@ impl JsValue { return false; } - match (x, y) { + match (&x.inner, &y.inner) { // 2. If Type(x) is Number or BigInt, then // a. Return ! Type(x)::SameValueZero(x, y). - (Self::BigInt(x), Self::BigInt(y)) => JsBigInt::same_value_zero(x, y), + (InnerValue::BigInt(x), InnerValue::BigInt(y)) => JsBigInt::same_value_zero(x, y), - (Self::Rational(x), Self::Rational(y)) => Number::same_value_zero(*x, *y), - (Self::Rational(x), Self::Integer(y)) => Number::same_value_zero(*x, f64::from(*y)), - (Self::Integer(x), Self::Rational(y)) => Number::same_value_zero(f64::from(*x), *y), - (Self::Integer(x), Self::Integer(y)) => x == y, + (InnerValue::Float64(x), InnerValue::Float64(y)) => Number::same_value_zero(*x, *y), + (InnerValue::Float64(x), InnerValue::Integer32(y)) => { + Number::same_value_zero(*x, f64::from(*y)) + } + (InnerValue::Integer32(x), InnerValue::Float64(y)) => { + Number::same_value_zero(f64::from(*x), *y) + } + (InnerValue::Integer32(x), InnerValue::Integer32(y)) => x == y, // 3. Return ! SameValueNonNumeric(x, y). (_, _) => Self::same_value_non_numeric(x, y), @@ -185,12 +193,13 @@ impl JsValue { fn same_value_non_numeric(x: &Self, y: &Self) -> bool { debug_assert!(x.get_type() == y.get_type()); - match (x, y) { - (Self::Null, Self::Null) | (Self::Undefined, Self::Undefined) => true, - (Self::String(ref x), Self::String(ref y)) => x == y, - (Self::Boolean(x), Self::Boolean(y)) => x == y, - (Self::Object(ref x), Self::Object(ref y)) => JsObject::equals(x, y), - (Self::Symbol(ref x), Self::Symbol(ref y)) => x == y, + match (&x.inner, &y.inner) { + (InnerValue::Null, InnerValue::Null) + | (InnerValue::Undefined, InnerValue::Undefined) => true, + (InnerValue::String(x), InnerValue::String(y)) => x == y, + (InnerValue::Boolean(x), InnerValue::Boolean(y)) => x == y, + (InnerValue::Object(x), InnerValue::Object(y)) => JsObject::equals(x, y), + (InnerValue::Symbol(x), InnerValue::Symbol(y)) => x == y, _ => false, } } diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index f01335067d5..036f363b241 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -26,10 +26,10 @@ pub(crate) use self::conversions::IntoOrUndefined; pub use self::{ conversions::try_from_js::TryFromJs, conversions::try_into_js::TryIntoJs, display::ValueDisplay, integer::IntegerOrInfinity, operations::*, r#type::Type, + variant::JsVariant, }; use crate::builtins::RegExp; use crate::object::{JsFunction, JsPromise, JsRegExp}; -use crate::value::variant::JsVariant; use crate::{ builtins::{ number::{f64_to_int32, f64_to_uint32}, @@ -69,7 +69,7 @@ static TWO_E_63: Lazy = Lazy::new(|| { /// This is not a public API and should not be used directly. /// /// If you need access to the variant, use [JsValue::variant] instead. -#[derive(Finalize, Debug, Clone)] +#[derive(Finalize, Debug, Clone, PartialEq, Eq)] enum InnerValue { Null, Undefined, @@ -98,6 +98,34 @@ pub struct JsValue { } impl JsValue { + pub const ZERO: Self = Self { + inner: InnerValue::Integer32(0), + }; + pub const ONE: Self = Self { + inner: InnerValue::Integer32(1), + }; + pub const NAN: Self = Self { + inner: InnerValue::Float64(f64::NAN), + }; + pub const POSITIVE_INFINITY: Self = Self { + inner: InnerValue::Float64(f64::INFINITY), + }; + pub const NEGATIVE_INFINITY: Self = Self { + inner: InnerValue::Float64(f64::NEG_INFINITY), + }; + + pub const UNDEFINED: Self = Self { + inner: InnerValue::Undefined, + }; + pub const NULL: Self = Self { + inner: InnerValue::Null, + }; + + /// Create a new [`JsValue`] from an inner value. + pub(crate) const fn from_inner(inner: InnerValue) -> Self { + Self { inner } + } + /// Create a new [`JsValue`]. pub fn new(value: T) -> Self where @@ -107,7 +135,8 @@ impl JsValue { } /// Return the variant of this value. - pub fn variant(&self) -> JsVariant { + #[inline] + pub fn variant(&self) -> JsVariant<'_> { (&self.inner).into() } @@ -129,21 +158,21 @@ impl JsValue { #[inline] #[must_use] pub const fn nan() -> Self { - Self { inner: InnerValue::Float64(f64::NAN) } + Self::NAN } /// Creates a new number with `Infinity` value. #[inline] #[must_use] pub const fn positive_infinity() -> Self { - Self { inner: InnerValue::Float64(f64::INFINITY) } + Self::POSITIVE_INFINITY } /// Creates a new number with `-Infinity` value. #[inline] #[must_use] pub const fn negative_infinity() -> Self { - Self { inner: InnerValue::Float64(f64::NEG_INFINITY) } + Self::NEGATIVE_INFINITY } /// Returns true if the value is an object. @@ -172,7 +201,7 @@ impl JsValue { /// [spec]: https://tc39.es/ecma262/#sec-iscallable #[inline] #[must_use] - pub fn is_callable(&self) -> bool { + pub const fn is_callable(&self) -> bool { if let InnerValue::Object(obj) = &self.inner { obj.is_callable() } else { @@ -289,14 +318,14 @@ impl JsValue { #[inline] #[must_use] pub const fn is_undefined(&self) -> bool { - self.inner == InnerValue::Undefined + matches!(&self.inner, InnerValue::Undefined) } /// Returns true if the value is null. #[inline] #[must_use] pub const fn is_null(&self) -> bool { - self.inner == InnerValue::Null + matches!(self.inner, InnerValue::Null) } /// Returns true if the value is null or undefined. @@ -320,6 +349,17 @@ impl JsValue { matches!(&self.inner, InnerValue::Integer32(_)) } + /// Returns the number if the value is an integer, otherwise `None`. + #[inline] + #[must_use] + pub const fn as_integer32(&self) -> Option { + if let InnerValue::Integer32(i) = self.inner { + Some(i) + } else { + None + } + } + /// Returns true if the value is a number. #[inline] #[must_use] @@ -360,15 +400,15 @@ impl JsValue { #[inline] #[must_use] pub const fn is_boolean(&self) -> bool { - matches!(self, Self::Boolean(_)) + matches!(self.inner, InnerValue::Boolean(_)) } /// Returns the boolean if the value is a boolean, otherwise `None`. #[inline] #[must_use] pub const fn as_boolean(&self) -> Option { - match self { - Self::Boolean(boolean) => Some(*boolean), + match &self.inner { + InnerValue::Boolean(boolean) => Some(*boolean), _ => None, } } @@ -377,15 +417,15 @@ impl JsValue { #[inline] #[must_use] pub const fn is_bigint(&self) -> bool { - matches!(self, Self::BigInt(_)) + matches!(self.inner, InnerValue::BigInt(_)) } /// Returns an optional reference to a `BigInt` if the value is a `BigInt` primitive. #[inline] #[must_use] pub const fn as_bigint(&self) -> Option<&JsBigInt> { - match self { - Self::BigInt(bigint) => Some(bigint), + match &self.inner { + InnerValue::BigInt(bigint) => Some(bigint), _ => None, } } @@ -398,13 +438,13 @@ impl JsValue { /// [spec]: https://tc39.es/ecma262/#sec-toboolean #[must_use] pub fn to_boolean(&self) -> bool { - match *self { - Self::Symbol(_) | Self::Object(_) => true, - Self::String(ref s) if !s.is_empty() => true, - Self::Rational(n) if n != 0.0 && !n.is_nan() => true, - Self::Integer(n) if n != 0 => true, - Self::BigInt(ref n) if !n.is_zero() => true, - Self::Boolean(v) => v, + match self.inner { + InnerValue::Symbol(_) | InnerValue::Object(_) => true, + InnerValue::String(ref s) if !s.is_empty() => true, + InnerValue::Float64(n) if !n.is_nan() => true, + InnerValue::Integer32(n) if n != 0 => true, + InnerValue::BigInt(ref n) if !n.is_zero() => true, + InnerValue::Boolean(v) => *v, _ => false, } } @@ -472,14 +512,14 @@ impl JsValue { /// /// [spec]: https://tc39.es/ecma262/#sec-tobigint pub fn to_bigint(&self, context: &mut Context) -> JsResult { - match self { - Self::Null => Err(JsNativeError::typ() + match self.inner { + InnerValue::Null => Err(JsNativeError::typ() .with_message("cannot convert null to a BigInt") .into()), - Self::Undefined => Err(JsNativeError::typ() + InnerValue::Undefined => Err(JsNativeError::typ() .with_message("cannot convert undefined to a BigInt") .into()), - Self::String(ref string) => JsBigInt::from_js_string(string).map_or_else( + InnerValue::String(ref string) => JsBigInt::from_js_string(string).map_or_else( || { Err(JsNativeError::syntax() .with_message(format!( @@ -490,17 +530,17 @@ impl JsValue { }, Ok, ), - Self::Boolean(true) => Ok(JsBigInt::one()), - Self::Boolean(false) => Ok(JsBigInt::zero()), - Self::Integer(_) | Self::Rational(_) => Err(JsNativeError::typ() + InnerValue::Boolean(true) => Ok(JsBigInt::one()), + InnerValue::Boolean(false) => Ok(JsBigInt::zero()), + InnerValue::Integer32(_) | InnerValue::Float64(_) => Err(JsNativeError::typ() .with_message("cannot convert Number to a BigInt") .into()), - Self::BigInt(b) => Ok(b.clone()), - Self::Object(_) => { + InnerValue::BigInt(b) => Ok(b.clone()), + InnerValue::Object(_) => { let primitive = self.to_primitive(context, PreferredType::Number)?; primitive.to_bigint(context) } - Self::Symbol(_) => Err(JsNativeError::typ() + InnerValue::Symbol(_) => Err(JsNativeError::typ() .with_message("cannot convert Symbol to a BigInt") .into()), } @@ -533,69 +573,78 @@ impl JsValue { /// /// This function is equivalent to `String(value)` in JavaScript. pub fn to_string(&self, context: &mut Context) -> JsResult { - match self { - Self::Null => Ok(js_string!("null")), - Self::Undefined => Ok(js_string!("undefined")), - Self::Boolean(boolean) => Ok(if *boolean { - js_string!("true") - } else { - js_string!("false") - }), - Self::Rational(rational) => Ok(Number::to_js_string(*rational)), - Self::Integer(integer) => Ok(integer.to_string().into()), - Self::String(string) => Ok(string.clone()), - Self::Symbol(_) => Err(JsNativeError::typ() + match self.inner { + InnerValue::Null => Ok(js_string!("null")), + InnerValue::Undefined => Ok(js_string!("undefined")), + InnerValue::Boolean(true) => Ok(js_string!("true")), + InnerValue::Boolean(false) => Ok(js_string!("false")), + InnerValue::Float64(rational) => Ok(Number::to_js_string(*rational)), + InnerValue::Integer32(integer) => Ok(integer.to_string().into()), + InnerValue::String(ref string) => Ok(string.clone()), + InnerValue::Symbol(_) => Err(JsNativeError::typ() .with_message("can't convert symbol to string") .into()), - Self::BigInt(ref bigint) => Ok(bigint.to_string().into()), - Self::Object(_) => { + InnerValue::BigInt(ref bigint) => Ok(bigint.to_string().into()), + InnerValue::Object(_) => { let primitive = self.to_primitive(context, PreferredType::String)?; primitive.to_string(context) } } } + /// Consumes the value and return an object if it is an object. + /// Otherwise, it returns `None`. + #[inline] + #[must_use] + pub fn into_object(self) -> Option { + if let InnerValue::Object(obj) = self.inner { + Some(obj) + } else { + None + } + } + /// Converts the value to an Object. /// /// This function is equivalent to `Object(value)` in JavaScript. /// /// See: pub fn to_object(&self, context: &mut Context) -> JsResult { - match self { - Self::Undefined | Self::Null => Err(JsNativeError::typ() + match &self.inner { + InnerValue::Undefined | InnerValue::Null => Err(JsNativeError::typ() .with_message("cannot convert 'null' or 'undefined' to object") .into()), - Self::Boolean(boolean) => Ok(context + InnerValue::Boolean(boolean) => Ok(context .intrinsics() .templates() .boolean() .create(*boolean, Vec::default())), - Self::Integer(integer) => Ok(context + InnerValue::Integer32(integer) => Ok(context .intrinsics() .templates() .number() .create(f64::from(*integer), Vec::default())), - Self::Rational(rational) => Ok(context + InnerValue::Float64(rational) => Ok(context .intrinsics() .templates() .number() .create(*rational, Vec::default())), - Self::String(ref string) => Ok(context + InnerValue::String(ref string) => Ok(context .intrinsics() .templates() .string() .create(string.clone(), vec![string.len().into()])), - Self::Symbol(ref symbol) => Ok(context + InnerValue::Symbol(ref symbol) => Ok(context .intrinsics() .templates() .symbol() .create(symbol.clone(), Vec::default())), - Self::BigInt(ref bigint) => Ok(context + InnerValue::BigInt(ref bigint) => Ok(context .intrinsics() .templates() .bigint() .create(bigint.clone(), Vec::default())), - Self::Object(jsobject) => Ok(jsobject.clone()), + InnerValue::Object(jsobject) => Ok(jsobject.clone()), } } @@ -603,18 +652,21 @@ impl JsValue { /// /// See pub fn to_property_key(&self, context: &mut Context) -> JsResult { - Ok(match self { + Ok(match &self.inner { // Fast path: - Self::String(string) => string.clone().into(), - Self::Symbol(symbol) => symbol.clone().into(), - Self::Integer(integer) => (*integer).into(), + InnerValue::String(string) => string.clone().into(), + InnerValue::Symbol(symbol) => symbol.clone().into(), + InnerValue::Integer32(integer) => (*integer).into(), // Slow path: - Self::Object(_) => match self.to_primitive(context, PreferredType::String)? { - Self::String(ref string) => string.clone().into(), - Self::Symbol(ref symbol) => symbol.clone().into(), - Self::Integer(integer) => integer.into(), - primitive => primitive.to_string(context)?.into(), - }, + InnerValue::Object(_) => { + let primitive = self.to_primitive(context, PreferredType::String)?; + match primitive.inner { + InnerValue::String(ref string) => string.clone().into(), + InnerValue::Symbol(ref symbol) => symbol.clone().into(), + InnerValue::Integer32(integer) => integer.into(), + primitive => primitive.to_string(context)?.into(), + } + } primitive => primitive.to_string(context)?.into(), }) } @@ -642,7 +694,7 @@ impl JsValue { /// See: pub fn to_u32(&self, context: &mut Context) -> JsResult { // This is the fast path, if the value is Integer we can just return it. - if let Self::Integer(number) = *self { + if let InnerValue::Integer32(number) = self.inner { if let Ok(number) = u32::try_from(number) { return Ok(number); } @@ -657,7 +709,7 @@ impl JsValue { /// See: pub fn to_i32(&self, context: &mut Context) -> JsResult { // This is the fast path, if the value is Integer we can just return it. - if let Self::Integer(number) = *self { + if let InnerValue::Integer32(number) = self.inner { return Ok(number); } let number = self.to_number(context)?; @@ -929,20 +981,20 @@ impl JsValue { /// /// See: pub fn to_number(&self, context: &mut Context) -> JsResult { - match *self { - Self::Null => Ok(0.0), - Self::Undefined => Ok(f64::NAN), - Self::Boolean(b) => Ok(if b { 1.0 } else { 0.0 }), - Self::String(ref string) => Ok(string.to_number()), - Self::Rational(number) => Ok(number), - Self::Integer(integer) => Ok(f64::from(integer)), - Self::Symbol(_) => Err(JsNativeError::typ() + match self.inner { + InnerValue::Null => Ok(0.0), + InnerValue::Undefined => Ok(f64::NAN), + InnerValue::Boolean(b) => Ok(if b { 1.0 } else { 0.0 }), + InnerValue::String(ref string) => Ok(string.to_number()), + InnerValue::Float64(number) => Ok(number), + InnerValue::Integer32(integer) => Ok(f64::from(integer)), + InnerValue::Symbol(_) => Err(JsNativeError::typ() .with_message("argument must not be a symbol") .into()), - Self::BigInt(_) => Err(JsNativeError::typ() + InnerValue::BigInt(_) => Err(JsNativeError::typ() .with_message("argument must not be a bigint") .into()), - Self::Object(_) => { + InnerValue::Object(_) => { let primitive = self.to_primitive(context, PreferredType::Number)?; primitive.to_number(context) } @@ -1016,15 +1068,15 @@ impl JsValue { /// [spec]: https://tc39.es/ecma262/#sec-typeof-operator #[must_use] pub fn type_of(&self) -> &'static str { - match *self { - Self::Rational(_) | Self::Integer(_) => "number", - Self::String(_) => "string", - Self::Boolean(_) => "boolean", - Self::Symbol(_) => "symbol", - Self::Null => "object", - Self::Undefined => "undefined", - Self::BigInt(_) => "bigint", - Self::Object(ref object) => { + match self.inner { + InnerValue::Float64(_) | InnerValue::Integer32(_) => "number", + InnerValue::String(_) => "string", + InnerValue::Boolean(_) => "boolean", + InnerValue::Symbol(_) => "symbol", + InnerValue::Null => "object", + InnerValue::Undefined => "undefined", + InnerValue::BigInt(_) => "bigint", + InnerValue::Object(ref object) => { if object.is_callable() { "function" } else { @@ -1037,15 +1089,15 @@ impl JsValue { /// Same as [`JsValue::type_of`], but returning a [`JsString`] instead. #[must_use] pub fn js_type_of(&self) -> JsString { - match *self { - Self::Rational(_) | Self::Integer(_) => js_string!("number"), - Self::String(_) => js_string!("string"), - Self::Boolean(_) => js_string!("boolean"), - Self::Symbol(_) => js_string!("symbol"), - Self::Null => js_string!("object"), - Self::Undefined => js_string!("undefined"), - Self::BigInt(_) => js_string!("bigint"), - Self::Object(ref object) => { + match self.inner { + InnerValue::Float64(_) | InnerValue::Integer32(_) => js_string!("number"), + InnerValue::String(_) => js_string!("string"), + InnerValue::Boolean(_) => js_string!("boolean"), + InnerValue::Symbol(_) => js_string!("symbol"), + InnerValue::Null => js_string!("object"), + InnerValue::Undefined => js_string!("undefined"), + InnerValue::BigInt(_) => js_string!("bigint"), + InnerValue::Object(ref object) => { if object.is_callable() { js_string!("function") } else { @@ -1072,7 +1124,7 @@ impl JsValue { /// let defined_result = defined_value.map(|v| v.add(&JsValue::from(5), &mut context)).transpose().unwrap(); /// let undefined_result = undefined.map(|v| v.add(&JsValue::from(5), &mut context)).transpose().unwrap(); /// - /// assert_eq!(defined_result, Some(JsValue::Integer(10))); + /// assert_eq!(defined_result, Some(JsValue::from(10u8))); /// assert_eq!(undefined_result, None); /// /// ``` @@ -1109,7 +1161,7 @@ impl JsValue { impl Default for JsValue { fn default() -> Self { - Self::Undefined + Self::undefined() } } diff --git a/core/engine/src/value/variant.rs b/core/engine/src/value/variant.rs index ba315b7d488..e6f43140376 100644 --- a/core/engine/src/value/variant.rs +++ b/core/engine/src/value/variant.rs @@ -1,6 +1,5 @@ use super::InnerValue; -use crate::object::Ref; -use crate::{JsBigInt, JsObject, JsSymbol}; +use crate::{JsBigInt, JsObject, JsSymbol, JsValue}; use boa_string::JsString; /// A non-mutable variant of a JsValue. @@ -49,3 +48,19 @@ impl<'a> From<&'a InnerValue> for JsVariant<'a> { } } } + +impl<'a> From> for JsValue { + fn from(value: JsVariant<'a>) -> Self { + match value { + JsVariant::Null => JsValue::null(), + JsVariant::Undefined => JsValue::undefined(), + JsVariant::Boolean(b) => JsValue::new(b), + JsVariant::String(s) => JsValue::new(s.clone()), + JsVariant::Float64(f) => JsValue::new(f), + JsVariant::Integer32(i) => JsValue::new(i), + JsVariant::BigInt(b) => JsValue::new(b.clone()), + JsVariant::Object(o) => JsValue::new(o.clone()), + JsVariant::Symbol(s) => JsValue::new(s.clone()), + } + } +} diff --git a/core/engine/src/vm/opcode/call/mod.rs b/core/engine/src/vm/opcode/call/mod.rs index 7db445a0cd9..4e8ca5e9585 100644 --- a/core/engine/src/vm/opcode/call/mod.rs +++ b/core/engine/src/vm/opcode/call/mod.rs @@ -60,7 +60,7 @@ impl CallEval { context.vm.push(result); } else { // NOTE: This is a deviation from the spec, to optimize the case when we dont pass anything to `eval`. - context.vm.push(JsValue::Undefined); + context.vm.push(JsValue::undefined()); } return Ok(CompletionType::Normal); @@ -154,7 +154,7 @@ impl CallEvalSpread { context.vm.push(result); } else { // NOTE: This is a deviation from the spec, to optimize the case when we dont pass anything to `eval`. - context.vm.push(JsValue::Undefined); + context.vm.push(JsValue::undefined()); } return Ok(CompletionType::Normal); @@ -302,7 +302,7 @@ impl Operation for ImportCall { &context.intrinsics().constructors().promise().constructor(), context, ) - .expect("operation cannot fail for the %Promise% intrinsic"); + .expect("operation cannot fail for the %Promise% intrinsic"); let promise = cap.promise().clone(); // 6. Let specifierString be Completion(ToString(specifier)). @@ -397,7 +397,7 @@ impl Operation for ImportCall { cap.clone(), ), ) - .build(); + .build(); // 6. Let linkAndEvaluateClosure be a new Abstract Closure with no parameters that captures module, promiseCapability, and onRejected and performs the following steps when called: // 7. Let linkAndEvaluate be CreateBuiltinFunction(linkAndEvaluateClosure, 0, "", « »). @@ -444,7 +444,7 @@ impl Operation for ImportCall { (module.clone(), cap.clone()), ), ) - .build(); + .build(); // f. Perform PerformPromiseThen(evaluatePromise, onFulfilled, onRejected). Promise::perform_promise_then( @@ -461,7 +461,7 @@ impl Operation for ImportCall { (module.clone(), cap.clone(), on_rejected.clone()), ), ) - .build(); + .build(); // 8. Perform PerformPromiseThen(loadPromise, linkAndEvaluate, onRejected). Promise::perform_promise_then( diff --git a/core/engine/src/vm/opcode/control_flow/jump.rs b/core/engine/src/vm/opcode/control_flow/jump.rs index 5ed13643b5d..9d402c15887 100644 --- a/core/engine/src/vm/opcode/control_flow/jump.rs +++ b/core/engine/src/vm/opcode/control_flow/jump.rs @@ -1,6 +1,6 @@ use crate::{ vm::{opcode::Operation, CompletionType}, - Context, JsResult, JsValue, + Context, JsResult, }; /// `Jump` implements the Opcode Operation for `Opcode::Jump` @@ -128,7 +128,7 @@ impl Operation for JumpTable { let count = context.vm.read::(); let value = context.vm.pop(); - if let JsValue::Integer(value) = &value { + if let Some(value) = value.as_integer32() { let value = *value as u32; let mut target = None; for i in 0..count { diff --git a/core/engine/src/vm/opcode/push/array.rs b/core/engine/src/vm/opcode/push/array.rs index d5650c46503..c2b170272a1 100644 --- a/core/engine/src/vm/opcode/push/array.rs +++ b/core/engine/src/vm/opcode/push/array.rs @@ -22,7 +22,7 @@ impl Operation for PushNewArray { .intrinsics() .templates() .array() - .create(Array, vec![JsValue::new(0)]); + .create(Array, vec![JsValue::ZERO]); context.vm.push(array); Ok(CompletionType::Normal) } diff --git a/core/engine/src/vm/opcode/set/class_prototype.rs b/core/engine/src/vm/opcode/set/class_prototype.rs index be0d89c2054..8cd7737b96d 100644 --- a/core/engine/src/vm/opcode/set/class_prototype.rs +++ b/core/engine/src/vm/opcode/set/class_prototype.rs @@ -1,9 +1,10 @@ +use crate::value::JsVariant; use crate::{ builtins::{function::OrdinaryFunction, OrdinaryObject}, object::{internal_methods::InternalMethodContext, JsObject, CONSTRUCTOR, PROTOTYPE}, property::PropertyDescriptorBuilder, vm::{opcode::Operation, CompletionType}, - Context, JsResult, JsValue, + Context, JsResult, }; /// `SetClassProtoType` implements the Opcode Operation for `Opcode::SetClassPrototype` @@ -20,10 +21,10 @@ impl Operation for SetClassPrototype { fn execute(context: &mut Context) -> JsResult { let prototype_value = context.vm.pop(); - let prototype = match &prototype_value { - JsValue::Object(proto) => Some(proto.clone()), - JsValue::Null => None, - JsValue::Undefined => Some(context.intrinsics().constructors().object().prototype()), + let prototype = match prototype_value.variant() { + JsVariant::Object(proto) => Some(proto.clone()), + JsVariant::Null => None, + JsVariant::Undefined => Some(context.intrinsics().constructors().object().prototype()), _ => unreachable!(), }; diff --git a/core/engine/src/vm/opcode/unary_ops/decrement.rs b/core/engine/src/vm/opcode/unary_ops/decrement.rs index 978b10607fc..074704dd334 100644 --- a/core/engine/src/vm/opcode/unary_ops/decrement.rs +++ b/core/engine/src/vm/opcode/unary_ops/decrement.rs @@ -1,5 +1,6 @@ +use crate::value::JsVariant; use crate::{ - value::{JsValue, Numeric}, + value::Numeric, vm::{opcode::Operation, CompletionType}, Context, JsBigInt, JsResult, }; @@ -18,8 +19,8 @@ impl Operation for Dec { fn execute(context: &mut Context) -> JsResult { let value = context.vm.pop(); - match value { - JsValue::Integer(number) if number > i32::MIN => { + match value.variant() { + JsVariant::Integer32(number) if number > i32::MIN => { context.vm.push(number - 1); } _ => match value.to_numeric(context)? { @@ -47,8 +48,8 @@ impl Operation for DecPost { fn execute(context: &mut Context) -> JsResult { let value = context.vm.pop(); - match value { - JsValue::Integer(number) if number > i32::MIN => { + match value.variant() { + JsVariant::Integer32(number) if number > i32::MIN => { context.vm.push(number - 1); context.vm.push(value); } diff --git a/core/engine/src/vm/opcode/unary_ops/increment.rs b/core/engine/src/vm/opcode/unary_ops/increment.rs index 87fefb21934..45f9d8de0cf 100644 --- a/core/engine/src/vm/opcode/unary_ops/increment.rs +++ b/core/engine/src/vm/opcode/unary_ops/increment.rs @@ -1,5 +1,6 @@ +use crate::value::JsVariant; use crate::{ - value::{JsValue, Numeric}, + value::Numeric, vm::{opcode::Operation, CompletionType}, Context, JsBigInt, JsResult, }; @@ -18,8 +19,8 @@ impl Operation for Inc { fn execute(context: &mut Context) -> JsResult { let value = context.vm.pop(); - match value { - JsValue::Integer(number) if number < i32::MAX => { + match value.variant() { + JsVariant::Integer32(number) if number < i32::MAX => { context.vm.push(number + 1); } _ => match value.to_numeric(context)? { @@ -47,8 +48,8 @@ impl Operation for IncPost { fn execute(context: &mut Context) -> JsResult { let value = context.vm.pop(); - match value { - JsValue::Integer(number) if number < i32::MAX => { + match value.variant() { + JsVariant::Integer32(number) if number < i32::MAX => { context.vm.push(number + 1); context.vm.push(value); } diff --git a/core/engine/tests/imports.rs b/core/engine/tests/imports.rs index d3f38e2c68b..36def275947 100644 --- a/core/engine/tests/imports.rs +++ b/core/engine/tests/imports.rs @@ -38,10 +38,7 @@ fn subdirectories() { .call(&JsValue::undefined(), &[], &mut context) .unwrap(); - assert_eq!( - foo_value, - JsValue::String(js_string!("file1..file1_1.file1_2")) - ); + assert_eq!(foo_value, js_string!("file1..file1_1.file1_2").into()); } PromiseState::Rejected(reason) => { panic!("Module failed to load: {}", reason.display()); diff --git a/core/interop/src/lib.rs b/core/interop/src/lib.rs index 722f3e99b87..7374f072c6b 100644 --- a/core/interop/src/lib.rs +++ b/core/interop/src/lib.rs @@ -28,7 +28,7 @@ pub trait IntoJsModule { fn into_js_module(self, context: &mut Context) -> Module; } -impl + Clone> IntoJsModule for T { +impl + Clone> IntoJsModule for T { fn into_js_module(self, context: &mut Context) -> Module { let (names, fns): (Vec<_>, Vec<_>) = self.into_iter().unzip(); let exports = names.clone(); @@ -155,7 +155,7 @@ impl<'a, T: TryFromJs> TryFromJsArgument<'a> for T { ) -> JsResult<(Self, &'a [JsValue])> { match rest.split_first() { Some((first, rest)) => Ok((first.try_js_into(context)?, rest)), - None => T::try_from_js(&JsValue::undefined(), context).map(|v| (v, rest)), + None => T::try_from_js(&JsValue::UNDEFINED, context).map(|v| (v, rest)), } } } @@ -219,7 +219,7 @@ impl<'a> JsRest<'a> { } /// Returns an iterator over the inner list of `JsValue`. - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { self.0.iter() } @@ -270,7 +270,7 @@ impl<'a> IntoIterator for JsRest<'a> { /// JsValue::from(1), /// JsValue::from(2), /// JsValue::from(3), -/// JsValue::Boolean(true), +/// JsValue::from(true), /// JsValue::from(4), /// ], /// &mut context, @@ -289,12 +289,12 @@ impl JsAll { } /// Returns an iterator over the inner list of `T`. - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { self.0.iter() } /// Returns a mutable iterator over the inner list of `T`. - pub fn iter_mut(&mut self) -> impl Iterator { + pub fn iter_mut(&mut self) -> impl Iterator { self.0.iter_mut() } @@ -505,7 +505,7 @@ fn into_js_module() { result } } - .into_js_function_unsafe(&mut context), + .into_js_function_unsafe(&mut context), ), ( js_string!("bar"), @@ -539,11 +539,11 @@ fn into_js_module() { (move |value: JsValue, ContextData(result): ContextData| { *result.borrow_mut() = value; }) - .into_js_function_copied(&mut context), + .into_js_function_copied(&mut context), ), ] } - .into_js_module(&mut context); + .into_js_module(&mut context); loader.register(js_string!("test"), module); @@ -600,7 +600,7 @@ fn can_throw_exception() { &mut context, ), )] - .into_js_module(&mut context); + .into_js_module(&mut context); loader.register(js_string!("test"), module); diff --git a/core/macros/src/lib.rs b/core/macros/src/lib.rs index 5913ef43ecb..b5247c4b59b 100644 --- a/core/macros/src/lib.rs +++ b/core/macros/src/lib.rs @@ -402,13 +402,11 @@ pub fn derive_try_from_js(input: TokenStream) -> TokenStream { impl ::boa_engine::value::TryFromJs for #type_name { fn try_from_js(value: &boa_engine::JsValue, context: &mut boa_engine::Context) -> boa_engine::JsResult { - match value { - boa_engine::JsValue::Object(o) => {#conv}, - _ => Err(boa_engine::JsError::from( - boa_engine::JsNativeError::typ() - .with_message("cannot convert value to a #type_name") - )), - } + let o = value.as_object().ok_or_else(|| ::boa_engine::JsError::from( + ::boa_engine::JsNativeError::typ() + .with_message("value is not an object") + ))?; + #conv } } }; diff --git a/core/runtime/src/console/mod.rs b/core/runtime/src/console/mod.rs index 020c9fcdace..21e56db645b 100644 --- a/core/runtime/src/console/mod.rs +++ b/core/runtime/src/console/mod.rs @@ -300,102 +300,102 @@ impl Console { JsObject::with_object_proto(context.realm().intrinsics()), context, ) - .property( - JsSymbol::to_string_tag(), - Self::NAME, - Attribute::CONFIGURABLE, - ) - .function( - console_method(Self::assert, state.clone(), logger.clone()), - js_string!("assert"), - 0, - ) - .function( - console_method_mut(Self::clear, state.clone(), logger.clone()), - js_string!("clear"), - 0, - ) - .function( - console_method(Self::debug, state.clone(), logger.clone()), - js_string!("debug"), - 0, - ) - .function( - console_method(Self::error, state.clone(), logger.clone()), - js_string!("error"), - 0, - ) - .function( - console_method(Self::info, state.clone(), logger.clone()), - js_string!("info"), - 0, - ) - .function( - console_method(Self::log, state.clone(), logger.clone()), - js_string!("log"), - 0, - ) - .function( - console_method(Self::trace, state.clone(), logger.clone()), - js_string!("trace"), - 0, - ) - .function( - console_method(Self::warn, state.clone(), logger.clone()), - js_string!("warn"), - 0, - ) - .function( - console_method_mut(Self::count, state.clone(), logger.clone()), - js_string!("count"), - 0, - ) - .function( - console_method_mut(Self::count_reset, state.clone(), logger.clone()), - js_string!("countReset"), - 0, - ) - .function( - console_method_mut(Self::group, state.clone(), logger.clone()), - js_string!("group"), - 0, - ) - .function( - console_method_mut(Self::group_collapsed, state.clone(), logger.clone()), - js_string!("groupCollapsed"), - 0, - ) - .function( - console_method_mut(Self::group_end, state.clone(), logger.clone()), - js_string!("groupEnd"), - 0, - ) - .function( - console_method_mut(Self::time, state.clone(), logger.clone()), - js_string!("time"), - 0, - ) - .function( - console_method(Self::time_log, state.clone(), logger.clone()), - js_string!("timeLog"), - 0, - ) - .function( - console_method_mut(Self::time_end, state.clone(), logger.clone()), - js_string!("timeEnd"), - 0, - ) - .function( - console_method(Self::dir, state.clone(), logger.clone()), - js_string!("dir"), - 0, - ) - .function( - console_method(Self::dir, state, logger.clone()), - js_string!("dirxml"), - 0, - ) - .build() + .property( + JsSymbol::to_string_tag(), + Self::NAME, + Attribute::CONFIGURABLE, + ) + .function( + console_method(Self::assert, state.clone(), logger.clone()), + js_string!("assert"), + 0, + ) + .function( + console_method_mut(Self::clear, state.clone(), logger.clone()), + js_string!("clear"), + 0, + ) + .function( + console_method(Self::debug, state.clone(), logger.clone()), + js_string!("debug"), + 0, + ) + .function( + console_method(Self::error, state.clone(), logger.clone()), + js_string!("error"), + 0, + ) + .function( + console_method(Self::info, state.clone(), logger.clone()), + js_string!("info"), + 0, + ) + .function( + console_method(Self::log, state.clone(), logger.clone()), + js_string!("log"), + 0, + ) + .function( + console_method(Self::trace, state.clone(), logger.clone()), + js_string!("trace"), + 0, + ) + .function( + console_method(Self::warn, state.clone(), logger.clone()), + js_string!("warn"), + 0, + ) + .function( + console_method_mut(Self::count, state.clone(), logger.clone()), + js_string!("count"), + 0, + ) + .function( + console_method_mut(Self::count_reset, state.clone(), logger.clone()), + js_string!("countReset"), + 0, + ) + .function( + console_method_mut(Self::group, state.clone(), logger.clone()), + js_string!("group"), + 0, + ) + .function( + console_method_mut(Self::group_collapsed, state.clone(), logger.clone()), + js_string!("groupCollapsed"), + 0, + ) + .function( + console_method_mut(Self::group_end, state.clone(), logger.clone()), + js_string!("groupEnd"), + 0, + ) + .function( + console_method_mut(Self::time, state.clone(), logger.clone()), + js_string!("time"), + 0, + ) + .function( + console_method(Self::time_log, state.clone(), logger.clone()), + js_string!("timeLog"), + 0, + ) + .function( + console_method_mut(Self::time_end, state.clone(), logger.clone()), + js_string!("timeEnd"), + 0, + ) + .function( + console_method(Self::dir, state.clone(), logger.clone()), + js_string!("dir"), + 0, + ) + .function( + console_method(Self::dir, state, logger.clone()), + js_string!("dirxml"), + 0, + ) + .build() } /// Initializes the `console` built-in object. @@ -439,7 +439,7 @@ impl Console { logger.error(formatter(&args, context)?, &console.state, context)?; } - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } /// `console.clear()` @@ -461,7 +461,7 @@ impl Console { _: &mut Context, ) -> JsResult { console.state.groups.clear(); - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } /// `console.debug(...data)` @@ -482,7 +482,7 @@ impl Console { context: &mut Context, ) -> JsResult { logger.debug(formatter(args, context)?, &console.state, context)?; - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } /// `console.error(...data)` @@ -503,7 +503,7 @@ impl Console { context: &mut Context, ) -> JsResult { logger.error(formatter(args, context)?, &console.state, context)?; - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } /// `console.info(...data)` @@ -524,7 +524,7 @@ impl Console { context: &mut Context, ) -> JsResult { logger.info(formatter(args, context)?, &console.state, context)?; - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } /// `console.log(...data)` @@ -545,7 +545,7 @@ impl Console { context: &mut Context, ) -> JsResult { logger.log(formatter(args, context)?, &console.state, context)?; - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } /// `console.trace(...data)` @@ -579,7 +579,7 @@ impl Console { .join("\n"); logger.log(stack_trace_dump, &console.state, context)?; - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } /// `console.warn(...data)` @@ -600,7 +600,7 @@ impl Console { context: &mut Context, ) -> JsResult { logger.warn(formatter(args, context)?, &console.state, context)?; - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } /// `console.count(label)` @@ -630,7 +630,7 @@ impl Console { *c += 1; logger.info(format!("{msg} {c}"), &console.state, context)?; - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } /// `console.countReset(label)` @@ -663,7 +663,7 @@ impl Console { context, )?; - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } /// Returns current system time in ms. @@ -707,7 +707,7 @@ impl Console { )?; } - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } /// `console.timeLog(label, ...data)` @@ -747,7 +747,7 @@ impl Console { )?; } - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } /// `console.timeEnd(label)` @@ -791,7 +791,7 @@ impl Console { )?; }; - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } /// `console.group(...data)` @@ -816,7 +816,7 @@ impl Console { logger.info(format!("group: {group_label}"), &console.state, context)?; console.state.groups.push(group_label); - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } /// `console.groupCollapsed(...data)` @@ -859,7 +859,7 @@ impl Console { ) -> JsResult { console.state.groups.pop(); - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } /// `console.dir(item, options)` @@ -885,6 +885,6 @@ impl Console { &console.state, context, )?; - Ok(JsValue::undefined()) + Ok(JsValue::UNDEFINED) } } diff --git a/examples/src/bin/derive.rs b/examples/src/bin/derive.rs index 81deb460574..515e56f0ec4 100644 --- a/examples/src/bin/derive.rs +++ b/examples/src/bin/derive.rs @@ -1,3 +1,4 @@ +use boa_engine::value::JsVariant; use boa_engine::{value::TryFromJs, Context, JsNativeError, JsResult, JsValue, Source}; /// You can easily derive `TryFromJs` for structures with base Rust types. @@ -37,9 +38,9 @@ fn main() { /// Converts the value lossly fn lossy_conversion(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Rational(r) => Ok(r.round() as i16), - JsValue::Integer(i) => Ok(*i as i16), + match value.variant() { + JsVariant::Float64(r) => Ok(r.round() as i16), + JsVariant::Integer32(i) => Ok(*i as i16), _ => Err(JsNativeError::typ() .with_message("cannot convert value to an i16") .into()), diff --git a/examples/src/bin/jsarray.rs b/examples/src/bin/jsarray.rs index bf65c3815f6..62e2ba42b3c 100644 --- a/examples/src/bin/jsarray.rs +++ b/examples/src/bin/jsarray.rs @@ -70,7 +70,7 @@ fn main() -> JsResult<()> { Ok(args.first().cloned().unwrap_or_default().is_number().into()) }), ) - .build(); + .build(); let map_callback = FunctionObjectBuilder::new( context.realm(), @@ -81,7 +81,7 @@ fn main() -> JsResult<()> { .pow(&JsValue::new(2), context) }), ) - .build(); + .build(); let mut data = Vec::new(); for i in 1..=5 { @@ -107,10 +107,10 @@ fn main() -> JsResult<()> { accumulator.add(&value, context) }), ) - .build(); + .build(); assert_eq!( - chained_array.reduce(reduce_callback, Some(JsValue::new(0)), context)?, + chained_array.reduce(reduce_callback, Some(JsValue::ZERO), context)?, JsValue::new(202) ); diff --git a/examples/src/bin/jsmap.rs b/examples/src/bin/jsmap.rs index 39257497866..b52cd07611a 100644 --- a/examples/src/bin/jsmap.rs +++ b/examples/src/bin/jsmap.rs @@ -33,7 +33,7 @@ fn main() -> JsResult<()> { let deleted_key_one = map.get(js_string!("Key-1"), context)?; - assert_eq!(deleted_key_one, JsValue::undefined()); + assert_eq!(deleted_key_one, JsValue::UNDEFINED); // Retrieve a MapIterator for all entries in the Map. let entries = map.entries(context)?; diff --git a/examples/src/bin/jstypedarray.rs b/examples/src/bin/jstypedarray.rs index 7ffec384740..9b6a7b5109c 100644 --- a/examples/src/bin/jstypedarray.rs +++ b/examples/src/bin/jstypedarray.rs @@ -20,7 +20,7 @@ fn main() -> JsResult<()> { let array = JsUint8Array::from_iter(data, context)?; - assert_eq!(array.get(0, context)?, JsValue::new(0)); + assert_eq!(array.get(0, context)?, JsValue::ZERO); let mut sum = 0; @@ -38,14 +38,14 @@ fn main() -> JsResult<()> { accumulator.add(&value, context) }), ) - .build(); + .build(); assert_eq!( - array.reduce(callback, Some(JsValue::new(0)), context)?, + array.reduce(callback, Some(JsValue::ZERO), context)?, JsValue::new(sum) ); - let greter_than_10_predicate = FunctionObjectBuilder::new( + let greater_than_10_predicate = FunctionObjectBuilder::new( context.realm(), NativeFunction::from_fn_ptr(|_this, args, _context| { let element = args @@ -54,13 +54,13 @@ fn main() -> JsResult<()> { .unwrap_or_default() .as_number() .expect("error at number conversion"); - Ok(JsValue::Boolean(element > 10.0)) + Ok(JsValue::from(element > 10.0)) }), ) - .build(); + .build(); assert_eq!( - array.find_index(greter_than_10_predicate, None, context), + array.find_index(greater_than_10_predicate, None, context), Ok(Some(11)) ); @@ -73,14 +73,14 @@ fn main() -> JsResult<()> { .unwrap_or_default() .as_number() .expect("error at number conversion"); - Ok(JsValue::Boolean(element < 200.0)) + Ok(JsValue::from(element < 200.0)) }), ) - .build(); + .build(); assert_eq!( array.find_last(lower_than_200_predicate.clone(), None, context), - Ok(JsValue::Integer(199)) + Ok(JsValue::from(199u8)) ); let data: Vec = vec![90, 120, 150, 180, 210, 240]; @@ -107,12 +107,12 @@ fn main() -> JsResult<()> { .expect("error at number conversion"); *captures.borrow_mut() += element; - Ok(JsValue::Undefined) + Ok(JsValue::UNDEFINED) }, Gc::clone(&num_to_modify), ), ) - .build(); + .build(); let _unused = array.for_each(js_function, None, context); @@ -135,14 +135,14 @@ fn main() -> JsResult<()> { Some(3), context, )?; - assert_eq!(initialized8_array.get(0, context)?, JsValue::new(0)); - assert_eq!(initialized8_array.get(1, context)?, JsValue::new(0)); - assert_eq!(initialized8_array.get(2, context)?, JsValue::new(0)); + assert_eq!(initialized8_array.get(0, context)?, JsValue::ZERO); + assert_eq!(initialized8_array.get(1, context)?, JsValue::ZERO); + assert_eq!(initialized8_array.get(2, context)?, JsValue::ZERO); assert_eq!(initialized8_array.get(3, context)?, JsValue::new(1.0)); assert_eq!(initialized8_array.get(4, context)?, JsValue::new(2.0)); - assert_eq!(initialized8_array.get(5, context)?, JsValue::new(0)); - assert_eq!(initialized8_array.get(6, context)?, JsValue::new(0)); - assert_eq!(initialized8_array.get(7, context)?, JsValue::new(0)); + assert_eq!(initialized8_array.get(5, context)?, JsValue::ZERO); + assert_eq!(initialized8_array.get(6, context)?, JsValue::ZERO); + assert_eq!(initialized8_array.get(7, context)?, JsValue::ZERO); assert_eq!(initialized8_array.get(8, context)?, JsValue::Undefined); // subarray diff --git a/examples/src/bin/modulehandler.rs b/examples/src/bin/modulehandler.rs index c36aaa2600d..fff1eb664fd 100644 --- a/examples/src/bin/modulehandler.rs +++ b/examples/src/bin/modulehandler.rs @@ -34,7 +34,7 @@ fn main() -> Result<(), Box> { let moduleobj = JsObject::default(); moduleobj.set( js_string!("exports"), - JsValue::from(js_string!(" ")), + js_string!(" "), false, &mut ctx, )?; diff --git a/examples/src/bin/synthetic.rs b/examples/src/bin/synthetic.rs index e38562cde4b..ccf5f81b8be 100644 --- a/examples/src/bin/synthetic.rs +++ b/examples/src/bin/synthetic.rs @@ -68,7 +68,7 @@ fn main() -> Result<(), Box> { match promise_result.state() { PromiseState::Pending => return Err("module didn't execute!".into()), PromiseState::Fulfilled(v) => { - assert_eq!(v, JsValue::undefined()); + assert_eq!(v, JsValue::UNDEFINED); } PromiseState::Rejected(err) => { return Err(JsError::from_opaque(err).try_native(context)?.into()) @@ -110,36 +110,36 @@ fn create_operations_module(context: &mut Context) -> Module { args.get_or_undefined(0).add(args.get_or_undefined(1), ctx) }), ) - .length(2) - .name("sum") - .build(); + .length(2) + .name("sum") + .build(); let sub = FunctionObjectBuilder::new( context.realm(), NativeFunction::from_fn_ptr(|_, args, ctx| { args.get_or_undefined(0).sub(args.get_or_undefined(1), ctx) }), ) - .length(2) - .name("sub") - .build(); + .length(2) + .name("sub") + .build(); let mult = FunctionObjectBuilder::new( context.realm(), NativeFunction::from_fn_ptr(|_, args, ctx| { args.get_or_undefined(0).mul(args.get_or_undefined(1), ctx) }), ) - .length(2) - .name("mult") - .build(); + .length(2) + .name("mult") + .build(); let div = FunctionObjectBuilder::new( context.realm(), NativeFunction::from_fn_ptr(|_, args, ctx| { args.get_or_undefined(0).div(args.get_or_undefined(1), ctx) }), ) - .length(2) - .name("div") - .build(); + .length(2) + .name("div") + .build(); let sqrt = FunctionObjectBuilder::new( context.realm(), NativeFunction::from_fn_ptr(|_, args, ctx| { @@ -147,9 +147,9 @@ fn create_operations_module(context: &mut Context) -> Module { Ok(JsValue::from(a.sqrt())) }), ) - .length(1) - .name("sqrt") - .build(); + .length(1) + .name("sqrt") + .build(); Module::synthetic( // Make sure to list all exports beforehand. From 20a54624874bd50713399abe6a0dc1e18d10f685 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Mon, 9 Dec 2024 11:47:26 -0800 Subject: [PATCH 03/19] WIP --- core/engine/src/builtins/object/mod.rs | 48 ++++++++++++----------- core/engine/src/builtins/promise/mod.rs | 32 ++++++++-------- core/engine/src/builtins/proxy/mod.rs | 22 +++++------ core/engine/src/builtins/reflect/mod.rs | 9 +++-- core/engine/src/value/mod.rs | 49 +++++++----------------- core/engine/src/value/variant.rs | 51 +++++++++++++++++++++++++ 6 files changed, 121 insertions(+), 90 deletions(-) diff --git a/core/engine/src/builtins/object/mod.rs b/core/engine/src/builtins/object/mod.rs index 171725d354a..faa50834a55 100644 --- a/core/engine/src/builtins/object/mod.rs +++ b/core/engine/src/builtins/object/mod.rs @@ -16,6 +16,7 @@ use super::{ error::ErrorObject, Array, BuiltInBuilder, BuiltInConstructor, Date, IntrinsicObject, RegExp, }; +use crate::value::JsVariant; use crate::{ builtins::{iterable::IteratorHint, map, BuiltInObject}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, @@ -164,10 +165,10 @@ impl BuiltInConstructor for OrdinaryObject { // 1. If NewTarget is neither undefined nor the active function object, then if !new_target.is_undefined() && new_target - != &context - .active_function_object() - .unwrap_or_else(|| context.intrinsics().constructors().object().constructor()) - .into() + != &context + .active_function_object() + .unwrap_or_else(|| context.intrinsics().constructors().object().constructor()) + .into() { // a. Return ? OrdinaryCreateFromConstructor(NewTarget, "%Object.prototype%"). let prototype = @@ -238,14 +239,14 @@ impl OrdinaryObject { let this = this.require_object_coercible()?; // 2. If Type(proto) is neither Object nor Null, return undefined. - let proto = match args.get_or_undefined(0) { - JsValue::Object(proto) => Some(proto.clone()), - JsValue::Null => None, + let proto = match args.get_or_undefined(0).variant() { + JsVariant::Object(proto) => Some(proto.clone()), + JsVariant::Null => None, _ => return Ok(JsValue::undefined()), }; // 3. If Type(O) is not Object, return undefined. - let JsValue::Object(object) = this else { + let JsVariant::Object(object) = this.variant() else { return Ok(JsValue::undefined()); }; @@ -455,12 +456,14 @@ impl OrdinaryObject { let prototype = args.get_or_undefined(0); let properties = args.get_or_undefined(1); - let obj = match prototype { - JsValue::Object(_) | JsValue::Null => JsObject::from_proto_and_data_with_shared_shape( - context.root_shape(), - prototype.as_object().cloned(), - OrdinaryObject, - ), + let obj = match prototype.variant() { + JsVariant::Object(_) | JsVariant::Null => { + JsObject::from_proto_and_data_with_shared_shape( + context.root_shape(), + prototype.as_object().cloned(), + OrdinaryObject, + ) + } _ => { return Err(JsNativeError::typ() .with_message(format!( @@ -650,7 +653,7 @@ impl OrdinaryObject { // 2. Return ? obj.[[GetPrototypeOf]](). Ok(obj .__get_prototype_of__(&mut InternalMethodContext::new(context))? - .map_or(JsValue::Null, JsValue::new)) + .map_or(JsValue::NULL, JsValue::new)) } /// Set the `prototype` of an object. @@ -680,9 +683,9 @@ impl OrdinaryObject { .require_object_coercible()? .clone(); - let proto = match args.get_or_undefined(1) { - JsValue::Object(obj) => Some(obj.clone()), - JsValue::Null => None, + let proto = match args.get_or_undefined(1).variant() { + JsVariant::Object(obj) => Some(obj.clone()), + JsVariant::Null => None, // 2. If Type(proto) is neither Object nor Null, throw a TypeError exception. val => { return Err(JsNativeError::typ() @@ -751,15 +754,14 @@ impl OrdinaryObject { args: &[JsValue], context: &mut Context, ) -> JsResult { - let object = args.get_or_undefined(0); - if let JsValue::Object(object) = object { + if let Some(object) = args.get_or_undefined(0).as_object() { let key = args .get(1) - .unwrap_or(&JsValue::Undefined) + .unwrap_or(&JsValue::UNDEFINED) .to_property_key(context)?; let desc = args .get(2) - .unwrap_or(&JsValue::Undefined) + .unwrap_or(&JsValue::UNDEFINED) .to_property_descriptor(context)?; object.define_property_or_throw(key, desc, context)?; @@ -788,7 +790,7 @@ impl OrdinaryObject { context: &mut Context, ) -> JsResult { let arg = args.get_or_undefined(0); - if let JsValue::Object(obj) = arg { + if let Some(obj) = arg.as_object() { let props = args.get_or_undefined(1); object_define_properties(obj, props, context)?; Ok(arg.clone()) diff --git a/core/engine/src/builtins/promise/mod.rs b/core/engine/src/builtins/promise/mod.rs index d97be26b683..e4e44c68b77 100644 --- a/core/engine/src/builtins/promise/mod.rs +++ b/core/engine/src/builtins/promise/mod.rs @@ -275,7 +275,7 @@ impl PromiseCapability { promise_capability.reject = reject.clone(); // e. Return undefined. - Ok(JsValue::Undefined) + Ok(JsValue::UNDEFINED) }, promise_capability.clone(), ), @@ -430,7 +430,7 @@ impl BuiltInConstructor for Promise { // 9. Let completion Completion(Call(executor, undefined, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »)be ). let completion = executor.call( - &JsValue::Undefined, + &JsValue::UNDEFINED, &[ resolving_functions.resolve.clone().into(), resolving_functions.reject.clone().into(), @@ -444,7 +444,7 @@ impl BuiltInConstructor for Promise { // a. Perform ? Call(resolvingFunctions.[[Reject]], undefined, « completion.[[Value]] »). resolving_functions .reject - .call(&JsValue::Undefined, &[e], context)?; + .call(&JsValue::UNDEFINED, &[e], context)?; } // 11. Return promise. @@ -658,7 +658,7 @@ impl Promise { // 4. Repeat, while let Some(next) = iterator_record.step_value(context)? { // c. Append undefined to values. - values.borrow_mut().push(JsValue::Undefined); + values.borrow_mut().push(JsValue::UNDEFINED); // d. Let nextPromise be ? Call(promiseResolve, constructor, « next »). let next_promise = @@ -1535,7 +1535,7 @@ impl Promise { promise_capability .functions .resolve - .call(&JsValue::Undefined, &[x], context)?; + .call(&JsValue::UNDEFINED, &[x], context)?; // 4. Return promiseCapability.[[Promise]]. Ok(promise_capability.promise.clone()) @@ -2126,7 +2126,7 @@ impl Promise { reject_promise(&promise, self_resolution_error.into(), context); // c. Return undefined. - return Ok(JsValue::Undefined); + return Ok(JsValue::UNDEFINED); } let Some(then) = resolution.as_object() else { @@ -2135,7 +2135,7 @@ impl Promise { fulfill_promise(&promise, resolution.clone(), context); // b. Return undefined. - return Ok(JsValue::Undefined); + return Ok(JsValue::UNDEFINED); }; // 9. Let then be Completion(Get(resolution, "then")). @@ -2146,7 +2146,7 @@ impl Promise { reject_promise(&promise, e.to_opaque(context), context); // b. Return undefined. - return Ok(JsValue::Undefined); + return Ok(JsValue::UNDEFINED); } // 11. Let thenAction be then.[[Value]]. Ok(then) => then, @@ -2162,7 +2162,7 @@ impl Promise { fulfill_promise(&promise, resolution.clone(), context); // b. Return undefined. - return Ok(JsValue::Undefined); + return Ok(JsValue::UNDEFINED); }; // 13. Let thenJobCallback be HostMakeJobCallback(thenAction). @@ -2181,7 +2181,7 @@ impl Promise { context.job_queue().enqueue_promise_job(job, context); // 16. Return undefined. - Ok(JsValue::Undefined) + Ok(JsValue::UNDEFINED) }, promise.clone(), ), @@ -2216,7 +2216,7 @@ impl Promise { reject_promise(&promise, args.get_or_undefined(0).clone(), context); // 8. Return undefined. - Ok(JsValue::Undefined) + Ok(JsValue::UNDEFINED) }, promise, ), @@ -2279,7 +2279,7 @@ fn new_promise_reaction_job( // e. Else, let handlerResult be Completion(HostCallJobCallback(handler, undefined, « argument »)). Some(handler) => context .host_hooks() - .call_job_callback(handler, &JsValue::Undefined, &[argument.clone()], context) + .call_job_callback(handler, &JsValue::UNDEFINED, &[argument.clone()], context) .map_err(|e| e.to_opaque(context)), }; @@ -2293,7 +2293,7 @@ fn new_promise_reaction_job( ); // ii. Return empty. - Ok(JsValue::Undefined) + Ok(JsValue::UNDEFINED) } Some(promise_capability_record) => { // g. Assert: promiseCapability is a PromiseCapability Record. @@ -2306,13 +2306,13 @@ fn new_promise_reaction_job( // h. If handlerResult is an abrupt completion, then Err(value) => { // i. Return ? Call(promiseCapability.[[Reject]], undefined, « handlerResult.[[Value]] »). - reject.call(&JsValue::Undefined, &[value], context) + reject.call(&JsValue::UNDEFINED, &[value], context) } // i. Else, Ok(value) => { // i. Return ? Call(promiseCapability.[[Resolve]], undefined, « handlerResult.[[Value]] »). - resolve.call(&JsValue::Undefined, &[value], context) + resolve.call(&JsValue::UNDEFINED, &[value], context) } } } @@ -2366,7 +2366,7 @@ fn new_promise_resolve_thenable_job( // i. Return ? Call(resolvingFunctions.[[Reject]], undefined, « thenCallResult.[[Value]] »). return resolving_functions .reject - .call(&JsValue::Undefined, &[value], context); + .call(&JsValue::UNDEFINED, &[value], context); } // d. Return ? thenCallResult. diff --git a/core/engine/src/builtins/proxy/mod.rs b/core/engine/src/builtins/proxy/mod.rs index f99ed9bc07a..ce82380f164 100644 --- a/core/engine/src/builtins/proxy/mod.rs +++ b/core/engine/src/builtins/proxy/mod.rs @@ -10,6 +10,8 @@ //! [spec]: https://tc39.es/ecma262/#sec-proxy-objects //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy +use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject, OrdinaryObject}; +use crate::value::JsVariant; use crate::{ builtins::{array, BuiltInObject}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, @@ -33,8 +35,6 @@ use crate::{ use boa_gc::{Finalize, GcRefCell, Trace}; use boa_profiler::Profiler; use rustc_hash::FxHashSet; - -use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject, OrdinaryObject}; /// Javascript `Proxy` object. #[derive(Debug, Clone, Trace, Finalize)] pub struct Proxy { @@ -279,9 +279,9 @@ pub(crate) fn proxy_exotic_get_prototype_of( let handler_proto = trap.call(&handler.into(), &[target.clone().into()], context)?; // 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError exception. - let handler_proto = match &handler_proto { - JsValue::Object(obj) => Some(obj.clone()), - JsValue::Null => None, + let handler_proto = match handler_proto.variant() { + JsVariant::Object(obj) => Some(obj.clone()), + JsVariant::Null => None, _ => { return Err(JsNativeError::typ() .with_message("Proxy trap result is neither object nor null") @@ -343,7 +343,7 @@ pub(crate) fn proxy_exotic_set_prototype_of( &handler.into(), &[ target.clone().into(), - val.clone().map_or(JsValue::Null, Into::into), + val.clone().map_or(JsValue::NULL, Into::into), ], context, )? @@ -923,8 +923,8 @@ pub(crate) fn proxy_exotic_set( // b. If IsAccessorDescriptor(targetDesc) is true, then if target_desc.is_accessor_descriptor() { // i. If targetDesc.[[Set]] is undefined, throw a TypeError exception. - match target_desc.set() { - None | Some(&JsValue::Undefined) => { + match target_desc.set().map(JsValue::is_undefined) { + None | Some(true) => { return Err(JsNativeError::typ() .with_message("Proxy trap set unexpected accessor descriptor") .into()); @@ -1042,8 +1042,8 @@ pub(crate) fn proxy_exotic_own_property_keys( let mut unchecked_result_keys: FxHashSet = FxHashSet::default(); let mut trap_result = Vec::new(); for value in &trap_result_raw { - match value { - JsValue::String(s) => { + match value.variant() { + JsVariant::String(s) => { if !unchecked_result_keys.insert(s.clone().into()) { return Err(JsNativeError::typ() .with_message("Proxy trap result contains duplicate string property keys") @@ -1051,7 +1051,7 @@ pub(crate) fn proxy_exotic_own_property_keys( } trap_result.push(s.clone().into()); } - JsValue::Symbol(s) => { + JsVariant::Symbol(s) => { if !unchecked_result_keys.insert(s.clone().into()) { return Err(JsNativeError::typ() .with_message("Proxy trap result contains duplicate symbol property keys") diff --git a/core/engine/src/builtins/reflect/mod.rs b/core/engine/src/builtins/reflect/mod.rs index 841a24d4b3d..d36e9804b10 100644 --- a/core/engine/src/builtins/reflect/mod.rs +++ b/core/engine/src/builtins/reflect/mod.rs @@ -11,6 +11,7 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect use super::{Array, BuiltInBuilder, IntrinsicObject}; +use crate::value::JsVariant; use crate::{ builtins::{self, BuiltInObject}, context::intrinsics::Intrinsics, @@ -272,7 +273,7 @@ impl Reflect { .ok_or_else(|| JsNativeError::typ().with_message("target must be an object"))?; Ok(target .__get_prototype_of__(&mut InternalMethodContext::new(context))? - .map_or(JsValue::Null, JsValue::new)) + .map_or(JsValue::NULL, JsValue::new)) } /// Returns `true` if the object has the property, `false` otherwise. @@ -417,9 +418,9 @@ impl Reflect { .first() .and_then(JsValue::as_object) .ok_or_else(|| JsNativeError::typ().with_message("target must be an object"))?; - let proto = match args.get_or_undefined(1) { - JsValue::Object(obj) => Some(obj.clone()), - JsValue::Null => None, + let proto = match args.get_or_undefined(1).variant() { + JsVariant::Object(obj) => Some(obj.clone()), + JsVariant::Null => None, _ => { return Err(JsNativeError::typ() .with_message("proto must be an object or null") diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index 036f363b241..ccf8c642919 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -144,14 +144,18 @@ impl JsValue { #[inline] #[must_use] pub const fn undefined() -> Self { - Self { inner: InnerValue::Undefined } + Self { + inner: InnerValue::Undefined, + } } /// Creates a new `null` value. #[inline] #[must_use] pub const fn null() -> Self { - Self { inner: InnerValue::Null } + Self { + inner: InnerValue::Null, + } } /// Creates a new number with `NaN` value. @@ -364,7 +368,10 @@ impl JsValue { #[inline] #[must_use] pub const fn is_number(&self) -> bool { - matches!(self.inner, InnerValue::Float64(_) | InnerValue::Integer32(_)) + matches!( + self.inner, + InnerValue::Float64(_) | InnerValue::Integer32(_) + ) } /// Returns the number if the value is a number, otherwise `None`. @@ -476,7 +483,7 @@ impl JsValue { PreferredType::String => js_string!("string"), PreferredType::Number => js_string!("number"), } - .into(); + .into(); // iv. Let result be ? Call(exoticToPrim, input, « hint »). let result = exotic_to_prim.call(self, &[hint], context)?; @@ -1068,43 +1075,13 @@ impl JsValue { /// [spec]: https://tc39.es/ecma262/#sec-typeof-operator #[must_use] pub fn type_of(&self) -> &'static str { - match self.inner { - InnerValue::Float64(_) | InnerValue::Integer32(_) => "number", - InnerValue::String(_) => "string", - InnerValue::Boolean(_) => "boolean", - InnerValue::Symbol(_) => "symbol", - InnerValue::Null => "object", - InnerValue::Undefined => "undefined", - InnerValue::BigInt(_) => "bigint", - InnerValue::Object(ref object) => { - if object.is_callable() { - "function" - } else { - "object" - } - } - } + self.variant().type_of() } /// Same as [`JsValue::type_of`], but returning a [`JsString`] instead. #[must_use] pub fn js_type_of(&self) -> JsString { - match self.inner { - InnerValue::Float64(_) | InnerValue::Integer32(_) => js_string!("number"), - InnerValue::String(_) => js_string!("string"), - InnerValue::Boolean(_) => js_string!("boolean"), - InnerValue::Symbol(_) => js_string!("symbol"), - InnerValue::Null => js_string!("object"), - InnerValue::Undefined => js_string!("undefined"), - InnerValue::BigInt(_) => js_string!("bigint"), - InnerValue::Object(ref object) => { - if object.is_callable() { - js_string!("function") - } else { - js_string!("object") - } - } - } + self.variant().js_type_of() } /// Maps a `JsValue` into a `Option` where T is the result of an diff --git a/core/engine/src/value/variant.rs b/core/engine/src/value/variant.rs index e6f43140376..777120c8825 100644 --- a/core/engine/src/value/variant.rs +++ b/core/engine/src/value/variant.rs @@ -1,5 +1,6 @@ use super::InnerValue; use crate::{JsBigInt, JsObject, JsSymbol, JsValue}; +use boa_engine::js_string; use boa_string::JsString; /// A non-mutable variant of a JsValue. @@ -64,3 +65,53 @@ impl<'a> From> for JsValue { } } } + +impl JsVariant<'_> { + /// `typeof` operator. Returns a string representing the type of the + /// given ECMA Value. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-typeof-operator + #[must_use] + pub fn type_of(&self) -> &'static str { + match self { + JsVariant::Float64(_) | JsVariant::Integer32(_) => "number", + JsVariant::String(_) => "string", + JsVariant::Boolean(_) => "boolean", + JsVariant::Symbol(_) => "symbol", + JsVariant::Null => "object", + JsVariant::Undefined => "undefined", + JsVariant::BigInt(_) => "bigint", + JsVariant::Object(ref object) => { + if object.is_callable() { + "function" + } else { + "object" + } + } + } + } + + /// Same as [`JsVariant::type_of`], but returning a [`JsString`] instead. + #[must_use] + pub fn js_type_of(&self) -> JsString { + match self { + JsVariant::Float64(_) | JsVariant::Integer32(_) => js_string!("number"), + JsVariant::String(_) => js_string!("string"), + JsVariant::Boolean(_) => js_string!("boolean"), + JsVariant::Symbol(_) => js_string!("symbol"), + JsVariant::Null => js_string!("object"), + JsVariant::Undefined => js_string!("undefined"), + JsVariant::BigInt(_) => js_string!("bigint"), + JsVariant::Object(ref object) => { + if object.is_callable() { + js_string!("function") + } else { + js_string!("object") + } + } + } + } +} From a1a3923a4a0bf38240dd35f3385cb57342fb5921 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Mon, 9 Dec 2024 18:28:54 -0800 Subject: [PATCH 04/19] WIP --- core/engine/src/builtins/array/tests.rs | 6 +- core/engine/src/builtins/date/mod.rs | 2 +- core/engine/src/builtins/intl/locale/mod.rs | 2 +- .../src/builtins/intl/number_format/mod.rs | 2 +- core/engine/src/builtins/iterable/mod.rs | 4 +- core/engine/src/builtins/string/mod.rs | 30 +- core/engine/src/builtins/symbol/mod.rs | 2 +- .../src/builtins/typed_array/builtin.rs | 31 ++- core/engine/src/lib.rs | 16 +- core/engine/src/native_function.rs | 5 +- core/engine/src/object/builtins/jsarray.rs | 9 +- .../src/object/builtins/jsarraybuffer.rs | 11 +- core/engine/src/object/builtins/jsdataview.rs | 11 +- core/engine/src/object/builtins/jsdate.rs | 67 ++--- core/engine/src/object/builtins/jsfunction.rs | 22 +- .../engine/src/object/builtins/jsgenerator.rs | 9 +- core/engine/src/object/builtins/jsmap.rs | 9 +- .../src/object/builtins/jsmap_iterator.rs | 9 +- core/engine/src/object/builtins/jspromise.rs | 11 +- core/engine/src/object/builtins/jsproxy.rs | 9 +- core/engine/src/object/builtins/jsregexp.rs | 9 +- core/engine/src/object/builtins/jsset.rs | 23 +- .../src/object/builtins/jsset_iterator.rs | 11 +- .../object/builtins/jssharedarraybuffer.rs | 9 +- .../src/object/builtins/jstypedarray.rs | 56 ++-- .../engine/src/object/internal_methods/mod.rs | 2 +- core/engine/src/object/property_map.rs | 6 +- .../src/optimizer/pass/constant_folding.rs | 21 +- .../src/value/conversions/serde_json.rs | 30 +- .../src/value/conversions/try_from_js.rs | 48 ++-- .../conversions/try_from_js/collections.rs | 4 +- .../src/value/conversions/try_into_js.rs | 12 +- core/engine/src/value/equality.rs | 70 ++--- core/engine/src/value/hash.rs | 22 +- core/engine/src/value/mod.rs | 62 +++-- core/engine/src/value/operations.rs | 260 ++++++++++-------- core/engine/src/value/type.rs | 20 +- core/engine/src/value/variant.rs | 6 + core/engine/src/vm/call_frame/mod.rs | 4 +- .../engine/src/vm/opcode/control_flow/jump.rs | 2 +- core/engine/src/vm/opcode/set/property.rs | 9 +- examples/src/bin/try_into_js_derive.rs | 2 +- 42 files changed, 528 insertions(+), 427 deletions(-) diff --git a/core/engine/src/builtins/array/tests.rs b/core/engine/src/builtins/array/tests.rs index 152fa3ef16a..5bacad5545c 100644 --- a/core/engine/src/builtins/array/tests.rs +++ b/core/engine/src/builtins/array/tests.rs @@ -875,7 +875,8 @@ fn array_spread_non_iterable() { fn get_relative_start() { #[track_caller] fn assert(context: &mut Context, arg: Option<&JsValue>, len: u64, expected: u64) { - let arg = arg.unwrap_or(&JsValue::UNDEFINED); + const UNDEFINED: &JsValue = &JsValue::UNDEFINED; + let arg = arg.unwrap_or(UNDEFINED); assert_eq!( Array::get_relative_start(context, arg, len).unwrap(), expected @@ -902,7 +903,8 @@ fn get_relative_start() { fn get_relative_end() { #[track_caller] fn assert(context: &mut Context, arg: Option<&JsValue>, len: u64, expected: u64) { - let arg = arg.unwrap_or(&JsValue::UNDEFINED); + const UNDEFINED: &JsValue = &JsValue::UNDEFINED; + let arg = arg.unwrap_or(UNDEFINED); assert_eq!( Array::get_relative_end(context, arg, len).unwrap(), expected diff --git a/core/engine/src/builtins/date/mod.rs b/core/engine/src/builtins/date/mod.rs index 9e255893e23..6cb9a1de4ed 100644 --- a/core/engine/src/builtins/date/mod.rs +++ b/core/engine/src/builtins/date/mod.rs @@ -423,7 +423,7 @@ impl Date { // 4. If t is NaN, return NaN. if t.is_nan() { - return Ok(JsValue::from(f64::NAN)); + return Ok(JsValue::NAN); }; if LOCAL { diff --git a/core/engine/src/builtins/intl/locale/mod.rs b/core/engine/src/builtins/intl/locale/mod.rs index 39e3cb9d3e8..dc50e03a396 100644 --- a/core/engine/src/builtins/intl/locale/mod.rs +++ b/core/engine/src/builtins/intl/locale/mod.rs @@ -649,7 +649,7 @@ impl Locale { .keywords .get(&key!("kn")) .map(Value::as_tinystr_slice); - Ok(JsValue::Boolean(match kn { + Ok(JsValue::new(match kn { Some([]) => true, Some([kn]) if kn == "true" => true, _ => false, diff --git a/core/engine/src/builtins/intl/number_format/mod.rs b/core/engine/src/builtins/intl/number_format/mod.rs index 12c77c0a3c2..0bbd3439eb3 100644 --- a/core/engine/src/builtins/intl/number_format/mod.rs +++ b/core/engine/src/builtins/intl/number_format/mod.rs @@ -338,7 +338,7 @@ impl BuiltInConstructor for NumberFormat { break 'block default_use_grouping; } // 3. If value is true, return true. - if let &JsValue::Boolean(true) = &value { + if let Some(true) = value.as_boolean() { break 'block GroupingStrategy::Always; } diff --git a/core/engine/src/builtins/iterable/mod.rs b/core/engine/src/builtins/iterable/mod.rs index ae06b53798d..b591afcc351 100644 --- a/core/engine/src/builtins/iterable/mod.rs +++ b/core/engine/src/builtins/iterable/mod.rs @@ -314,8 +314,8 @@ impl IteratorResult { /// Gets a new `IteratorResult` from a value. Returns `Err` if /// the value is not a [`JsObject`] pub(crate) fn from_value(value: JsValue) -> JsResult { - if let Some(object) = value.into_object() { - Ok(Self { object }) + if let Some(o) = value.as_object() { + Ok(Self { object: o.clone() }) } else { Err(JsNativeError::typ() .with_message("next value should be an object") diff --git a/core/engine/src/builtins/string/mod.rs b/core/engine/src/builtins/string/mod.rs index baa4b24d771..eb0dc9fe5e6 100644 --- a/core/engine/src/builtins/string/mod.rs +++ b/core/engine/src/builtins/string/mod.rs @@ -218,8 +218,12 @@ impl BuiltInConstructor for String { let string = match args.first() { // 2. Else, // a. If NewTarget is undefined and Type(value) is Symbol, return SymbolDescriptiveString(value). - Some(JsValue::Symbol(ref sym)) if new_target.is_undefined() => { - return Ok(sym.descriptive_string().into()) + Some(value) if new_target.is_undefined() && value.is_symbol() => { + return Ok(value + .as_symbol() + .expect("Already checked for a symbol") + .descriptive_string() + .into()) } // b. Let s be ? ToString(value). Some(value) => value.to_string(context)?, @@ -840,9 +844,9 @@ impl String { let len = string.len() as i64; // 7. If position is undefined, let pos be 0; else let pos be ? ToIntegerOrInfinity(position). - let pos = match args.get_or_undefined(1) { - &JsValue::Undefined => IntegerOrInfinity::Integer(0), - position => position.to_integer_or_infinity(context)?, + let pos = match args.get_or_undefined(1).as_defined() { + None => IntegerOrInfinity::Integer(0), + Some(pos) => pos.to_integer_or_infinity(context)?, }; // 8. Let start be the result of clamping pos between 0 and len. @@ -1496,7 +1500,7 @@ impl String { let s = o.to_string(context)?; // 4. Let rx be ? RegExpCreate(regexp, undefined). - let rx = RegExp::create(regexp, &JsValue::Undefined, context)?; + let rx = RegExp::create(regexp, &JsValue::UNDEFINED, context)?; // 5. Return ? Invoke(rx, @@match, « S »). rx.invoke(JsSymbol::r#match(), &[JsValue::new(s)], context) @@ -1887,9 +1891,9 @@ impl String { let int_start = args.get_or_undefined(0).to_integer_or_infinity(context)?; // 5. If end is undefined, let intEnd be len; else let intEnd be ? ToIntegerOrInfinity(end). - let int_end = match args.get_or_undefined(1) { - &JsValue::Undefined => IntegerOrInfinity::Integer(len), - end => end.to_integer_or_infinity(context)?, + let int_end = match args.get_or_undefined(1).as_defined() { + None => IntegerOrInfinity::Integer(len), + Some(end) => end.to_integer_or_infinity(context)?, }; // 6. Let finalStart be the result of clamping intStart between 0 and len. @@ -2140,11 +2144,11 @@ impl String { // 6. Let ns be the String value that is the result of normalizing S // into the normalization form named by f as specified in // https://unicode.org/reports/tr15/. - let normalization = match args.get_or_undefined(0) { + let normalization = match args.get_or_undefined(0).as_defined() { // 3. If form is undefined, let f be "NFC". - &JsValue::Undefined => Normalization::Nfc, + None => Normalization::Nfc, // 4. Else, let f be ? ToString(form). - f => match f.to_string(context)? { + Some(f) => match f.to_string(context)? { ntype if &ntype == "NFC" => Normalization::Nfc, ntype if &ntype == "NFD" => Normalization::Nfd, ntype if &ntype == "NFKC" => Normalization::Nfkc, @@ -2224,7 +2228,7 @@ impl String { let string = o.to_string(context)?; // 4. Let rx be ? RegExpCreate(regexp, undefined). - let rx = RegExp::create(regexp, &JsValue::Undefined, context)?; + let rx = RegExp::create(regexp, &JsValue::UNDEFINED, context)?; // 5. Return ? Invoke(rx, @@search, « string »). rx.invoke(JsSymbol::search(), &[JsValue::new(string)], context) diff --git a/core/engine/src/builtins/symbol/mod.rs b/core/engine/src/builtins/symbol/mod.rs index c0d915a873f..43e3651c2fb 100644 --- a/core/engine/src/builtins/symbol/mod.rs +++ b/core/engine/src/builtins/symbol/mod.rs @@ -280,7 +280,7 @@ impl Symbol { pub(crate) fn value_of(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Return ? thisSymbolValue(this value). let symbol = Self::this_symbol_value(this)?; - Ok(JsValue::Symbol(symbol)) + Ok(symbol.into()) } /// `get Symbol.prototype.description` diff --git a/core/engine/src/builtins/typed_array/builtin.rs b/core/engine/src/builtins/typed_array/builtin.rs index 51512ee7bc7..54dda4de6ad 100644 --- a/core/engine/src/builtins/typed_array/builtin.rs +++ b/core/engine/src/builtins/typed_array/builtin.rs @@ -9,6 +9,7 @@ use num_traits::Zero; use super::{ object::typed_array_set_element, ContentType, TypedArray, TypedArrayKind, TypedArrayMarker, }; +use crate::value::JsVariant; use crate::{ builtins::{ array::{find_via_predicate, ArrayIterator, Direction}, @@ -204,9 +205,9 @@ impl BuiltinTypedArray { } }; - let mapping = match args.get(1) { + let mapping = match args.get_or_undefined(1).as_defined() { // 3. If mapfn is undefined, let mapping be false. - None | Some(JsValue::Undefined) => None, + None => None, // 4. Else, Some(v) => match v.as_object() { // b. Let mapping be true. @@ -2219,9 +2220,9 @@ impl BuiltinTypedArray { context: &mut Context, ) -> JsResult { // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception. - let compare_fn = match args.first() { - None | Some(JsValue::Undefined) => None, - Some(JsValue::Object(obj)) if obj.is_callable() => Some(obj), + let compare_fn = match args.first().and_then(JsValue::as_defined) { + None => None, + Some(obj) if obj.is_callable() => obj.as_callable(), _ => { return Err(JsNativeError::typ() .with_message("TypedArray.sort called with non-callable comparefn") @@ -2271,9 +2272,9 @@ impl BuiltinTypedArray { context: &mut Context, ) -> JsResult { // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception. - let compare_fn = match args.first() { - None | Some(JsValue::Undefined) => None, - Some(JsValue::Object(obj)) if obj.is_callable() => Some(obj), + let compare_fn = match args.first().and_then(JsValue::as_defined) { + None => None, + Some(obj) if obj.is_callable() => obj.as_callable(), _ => { return Err(JsNativeError::typ() .with_message("TypedArray.sort called with non-callable comparefn") @@ -2616,7 +2617,7 @@ impl BuiltinTypedArray { obj.downcast_ref::() .map(|o| o.kind().js_name().into()) }) - .unwrap_or(JsValue::Undefined)) + .unwrap_or(JsValue::UNDEFINED)) } /// `TypedArraySpeciesCreate ( exemplar, argumentList )` @@ -2665,7 +2666,7 @@ impl BuiltinTypedArray { // 2. Let taRecord be ? ValidateTypedArray(newTypedArray, seq-cst). let (new_ta, buf_len) = - TypedArray::validate(&JsValue::Object(new_typed_array), Ordering::SeqCst)?; + TypedArray::validate(&JsValue::new(new_typed_array), Ordering::SeqCst)?; // 3. If the number of elements in argumentList is 1 and argumentList[0] is a Number, then if args.len() == 1 { @@ -3138,22 +3139,22 @@ fn compare_typed_array_elements( return Ok(cmp::Ordering::Less); } - match (x, y) { - (JsValue::BigInt(x), JsValue::BigInt(y)) => { + match (x.variant(), y.variant()) { + (JsVariant::BigInt(x), JsVariant::BigInt(y)) => { // Note: Other steps are not relevant for BigInts. // 6. If x < y, return -1𝔽. // 7. If x > y, return 1𝔽. // 10. Return +0𝔽. Ok(x.cmp(y)) } - (JsValue::Integer(x), JsValue::Integer(y)) => { + (JsVariant::Integer32(x), JsVariant::Integer32(y)) => { // Note: Other steps are not relevant for integers. // 6. If x < y, return -1𝔽. // 7. If x > y, return 1𝔽. // 10. Return +0𝔽. - Ok(x.cmp(y)) + Ok(x.cmp(&y)) } - (JsValue::Rational(x), JsValue::Rational(y)) => { + (JsVariant::Float64(x), JsVariant::Float64(y)) => { // 3. If x and y are both NaN, return +0𝔽. if x.is_nan() && y.is_nan() { return Ok(cmp::Ordering::Equal); diff --git a/core/engine/src/lib.rs b/core/engine/src/lib.rs index 28dc6b46f0b..b8fe1aad4bf 100644 --- a/core/engine/src/lib.rs +++ b/core/engine/src/lib.rs @@ -50,10 +50,7 @@ html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" )] -#![cfg_attr( - test, - allow(clippy::needless_raw_string_hashes) -)] // Makes strings a bit more copy-pastable +#![cfg_attr(test, allow(clippy::needless_raw_string_hashes))] // Makes strings a bit more copy-pastable #![cfg_attr(not(test), forbid(clippy::unwrap_used))] #![allow( // Currently throws a false positive regarding dependencies that are only used in benchmarks. @@ -76,7 +73,8 @@ clippy::missing_panics_doc, )] -extern crate self as boa_engine;#[cfg(not(target_has_atomic = "ptr"))] +extern crate self as boa_engine; +#[cfg(not(target_has_atomic = "ptr"))] compile_error!("Boa requires a lock free `AtomicUsize` in order to work properly."); pub use boa_ast as ast; @@ -176,7 +174,7 @@ pub trait JsArgs { impl JsArgs for [JsValue] { fn get_or_undefined(&self, index: usize) -> &JsValue { - const UNDEFINED: &JsValue = &JsValue::undefined(); + const UNDEFINED: &JsValue = &JsValue::UNDEFINED; self.get(index).unwrap_or(UNDEFINED) } } @@ -306,7 +304,7 @@ impl TestAction { /// Executes a list of test actions on a new, default context. #[cfg(test)] #[track_caller] -fn run_test_actions(actions: impl IntoIterator) { +fn run_test_actions(actions: impl IntoIterator) { let context = &mut Context::default(); run_test_actions_with(actions, context); } @@ -314,7 +312,7 @@ fn run_test_actions(actions: impl IntoIterator) { /// Executes a list of test actions on the provided context. #[cfg(test)] #[track_caller] -fn run_test_actions_with(actions: impl IntoIterator, context: &mut Context) { +fn run_test_actions_with(actions: impl IntoIterator, context: &mut Context) { #[track_caller] fn forward_val(context: &mut Context, source: &str) -> JsResult { context.eval(Source::from_bytes(source)) @@ -353,7 +351,7 @@ fn run_test_actions_with(actions: impl IntoIterator, context: & } "#, ) - .expect("failed to evaluate test harness"); + .expect("failed to evaluate test harness"); } Inner::Run { source } => { if let Err(e) = forward_val(context, &source) { diff --git a/core/engine/src/native_function.rs b/core/engine/src/native_function.rs index 721f53291fd..75f3b933abc 100644 --- a/core/engine/src/native_function.rs +++ b/core/engine/src/native_function.rs @@ -5,6 +5,7 @@ use boa_gc::{custom_trace, Finalize, Gc, Trace}; +use crate::value::JsVariant; use crate::{ builtins::{function::ConstructorKind, OrdinaryObject}, context::intrinsics::StandardConstructors, @@ -418,8 +419,8 @@ fn native_function_construct( let result = function .call(&new_target, &args, context) .map_err(|err| err.inject_realm(context.realm().clone())) - .and_then(|v| match v { - JsValue::Object(ref o) => Ok(o.clone()), + .and_then(|v| match v.variant() { + JsVariant::Object(o) => Ok(o.clone()), val => { if constructor.expect("must be a constructor").is_base() || val.is_undefined() { let prototype = get_prototype_from_constructor( diff --git a/core/engine/src/object/builtins/jsarray.rs b/core/engine/src/object/builtins/jsarray.rs index 7b7aff8d0e7..27457869443 100644 --- a/core/engine/src/object/builtins/jsarray.rs +++ b/core/engine/src/object/builtins/jsarray.rs @@ -439,11 +439,12 @@ impl Deref for JsArray { impl TryFromJs for JsArray { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Self::from_object(o.clone()), - _ => Err(JsNativeError::typ() + if let Some(o) = value.as_object() { + Self::from_object(o.clone()) + } else { + Err(JsNativeError::typ() .with_message("value is not an Array object") - .into()), + .into()) } } } diff --git a/core/engine/src/object/builtins/jsarraybuffer.rs b/core/engine/src/object/builtins/jsarraybuffer.rs index 684e69f3307..166c99c4f40 100644 --- a/core/engine/src/object/builtins/jsarraybuffer.rs +++ b/core/engine/src/object/builtins/jsarraybuffer.rs @@ -120,7 +120,7 @@ impl JsArrayBuffer { let obj = JsObject::new( context.root_shape(), prototype, - ArrayBuffer::from_data(block, JsValue::Undefined), + ArrayBuffer::from_data(block, JsValue::UNDEFINED), ); Ok(Self { inner: obj }) @@ -302,11 +302,12 @@ impl Deref for JsArrayBuffer { impl TryFromJs for JsArrayBuffer { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Self::from_object(o.clone()), - _ => Err(JsNativeError::typ() + if let Some(o) = value.as_object() { + Self::from_object(o.clone()) + } else { + Err(JsNativeError::typ() .with_message("value is not an ArrayBuffer object") - .into()), + .into()) } } } diff --git a/core/engine/src/object/builtins/jsdataview.rs b/core/engine/src/object/builtins/jsdataview.rs index b5157155b36..28cde98acb7 100644 --- a/core/engine/src/object/builtins/jsdataview.rs +++ b/core/engine/src/object/builtins/jsdataview.rs @@ -528,11 +528,12 @@ impl Deref for JsDataView { impl TryFromJs for JsDataView { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Self::from_object(o.clone()), - _ => Err(JsNativeError::typ() - .with_message("value is not an DataView object") - .into()), + if let Some(o) = value.as_object() { + Self::from_object(o.clone()) + } else { + Err(JsNativeError::typ() + .with_message("value is not a DataView object") + .into()) } } } diff --git a/core/engine/src/object/builtins/jsdate.rs b/core/engine/src/object/builtins/jsdate.rs index 2b080d1c8d6..3a4a8574945 100644 --- a/core/engine/src/object/builtins/jsdate.rs +++ b/core/engine/src/object/builtins/jsdate.rs @@ -68,7 +68,7 @@ impl JsDate { /// Same as JavaScript's `Date.now()` #[inline] pub fn now(context: &mut Context) -> JsResult { - Date::now(&JsValue::Null, &[JsValue::Null], context) + Date::now(&JsValue::NULL, &[JsValue::NULL], context) } // DEBUG: Uses RFC3339 internally therefore could match es6 spec of ISO8601 <======== @@ -80,7 +80,7 @@ impl JsDate { /// Same as JavaScript's `Date.parse(value)`. #[inline] pub fn parse(value: JsValue, context: &mut Context) -> JsResult { - Date::parse(&JsValue::Null, &[value], context) + Date::parse(&JsValue::NULL, &[value], context) } /// Takes a [year, month, day, hour, minute, second, millisecond] @@ -89,7 +89,7 @@ impl JsDate { /// Same as JavaScript's `Date.UTC()` #[inline] pub fn utc(values: &[JsValue], context: &mut Context) -> JsResult { - Date::utc(&JsValue::Null, values, context) + Date::utc(&JsValue::NULL, values, context) } /// Returns the day of the month(1-31) for the specified date @@ -98,7 +98,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getDate()`. #[inline] pub fn get_date(&self, context: &mut Context) -> JsResult { - Date::get_date::(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_date::(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the day of the week (0–6) for the specified date @@ -107,7 +107,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getDay()`. #[inline] pub fn get_day(&self, context: &mut Context) -> JsResult { - Date::get_day::(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_day::(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the year (4 digits for 4-digit years) of the specified date @@ -116,7 +116,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getFullYear()`. #[inline] pub fn get_full_year(&self, context: &mut Context) -> JsResult { - Date::get_full_year::(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_full_year::(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the hour (0–23) in the specified date according to local time. @@ -124,7 +124,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getHours()`. #[inline] pub fn get_hours(&self, context: &mut Context) -> JsResult { - Date::get_hours::(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_hours::(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the milliseconds (0–999) in the specified date according @@ -133,7 +133,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getMilliseconds()`. #[inline] pub fn get_milliseconds(&self, context: &mut Context) -> JsResult { - Date::get_milliseconds::(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_milliseconds::(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the minutes (0–59) in the specified date according to local time. @@ -141,7 +141,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getMinutes()`. #[inline] pub fn get_minutes(&self, context: &mut Context) -> JsResult { - Date::get_minutes::(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_minutes::(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the month (0–11) in the specified date according to local time. @@ -149,7 +149,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getMonth()`. #[inline] pub fn get_month(&self, context: &mut Context) -> JsResult { - Date::get_month::(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_month::(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the seconds (0–59) in the specified date according to local time. @@ -157,7 +157,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getSeconds()`. #[inline] pub fn get_seconds(&self, context: &mut Context) -> JsResult { - Date::get_seconds::(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_seconds::(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the numeric value of the specified date as the number @@ -167,7 +167,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getTime()`. #[inline] pub fn get_time(&self, context: &mut Context) -> JsResult { - Date::get_time(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_time(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the time-zone offset in minutes for the current locale. @@ -175,7 +175,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getTimezoneOffset()`. #[inline] pub fn get_timezone_offset(&self, context: &mut Context) -> JsResult { - Date::get_timezone_offset(&self.inner.clone().into(), &[JsValue::Null], context) + Date::get_timezone_offset(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the day (date) of the month (1–31) in the specified @@ -184,7 +184,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCDate()`. #[inline] pub fn get_utc_date(&self, context: &mut Context) -> JsResult { - Date::get_date::(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_date::(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the day of the week (0–6) in the specified @@ -193,7 +193,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCDay()`. #[inline] pub fn get_utc_day(&self, context: &mut Context) -> JsResult { - Date::get_day::(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_day::(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the year (4 digits for 4-digit years) in the specified @@ -202,7 +202,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCFullYear()`. #[inline] pub fn get_utc_full_year(&self, context: &mut Context) -> JsResult { - Date::get_full_year::(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_full_year::(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the hours (0–23) in the specified date according @@ -211,7 +211,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCHours()`. #[inline] pub fn get_utc_hours(&self, context: &mut Context) -> JsResult { - Date::get_hours::(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_hours::(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the milliseconds (0–999) in the specified date @@ -220,7 +220,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCMilliseconds()`. #[inline] pub fn get_utc_milliseconds(&self, context: &mut Context) -> JsResult { - Date::get_milliseconds::(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_milliseconds::(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the minutes (0–59) in the specified date according @@ -229,7 +229,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCMinutes()`. #[inline] pub fn get_utc_minutes(&self, context: &mut Context) -> JsResult { - Date::get_minutes::(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_minutes::(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the month (0–11) in the specified date according @@ -238,7 +238,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCMonth()`. #[inline] pub fn get_utc_month(&self, context: &mut Context) -> JsResult { - Date::get_month::(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_month::(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the seconds (0–59) in the specified date according @@ -247,7 +247,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCSeconds()`. #[inline] pub fn get_utc_seconds(&self, context: &mut Context) -> JsResult { - Date::get_seconds::(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_seconds::(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Sets the day of the month for a specified date according @@ -440,7 +440,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.toDateString()`. #[inline] pub fn to_date_string(&self, context: &mut Context) -> JsResult { - Date::to_date_string(&self.inner.clone().into(), &[JsValue::Null], context) + Date::to_date_string(&self.inner.clone().into(), &[JsValue::NULL], context) } /// DEPRECATED: This feature is no longer recommended. @@ -451,7 +451,7 @@ impl JsDate { #[deprecated] #[inline] pub fn to_gmt_string(&self, context: &mut Context) -> JsResult { - Date::to_utc_string(&self.inner.clone().into(), &[JsValue::Null], context) + Date::to_utc_string(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the given date in the ISO 8601 format according to universal @@ -460,7 +460,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.toISOString()`. #[inline] pub fn to_iso_string(&self, context: &mut Context) -> JsResult { - Date::to_iso_string(&self.inner.clone().into(), &[JsValue::Null], context) + Date::to_iso_string(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns a string representing the Date using `to_iso_string()`. @@ -468,7 +468,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.toJSON()`. #[inline] pub fn to_json(&self, context: &mut Context) -> JsResult { - Date::to_json(&self.inner.clone().into(), &[JsValue::Null], context) + Date::to_json(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns a string representing the date portion of the given Date instance @@ -511,7 +511,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.toString()`. #[inline] pub fn to_string(&self, context: &mut Context) -> JsResult { - Date::to_string(&self.inner.clone().into(), &[JsValue::Null], context) + Date::to_string(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the "time" portion of the Date as human-readable string. @@ -519,7 +519,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.toTimeString()`. #[inline] pub fn to_time_string(&self, context: &mut Context) -> JsResult { - Date::to_time_string(&self.inner.clone().into(), &[JsValue::Null], context) + Date::to_time_string(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns a string representing the given date using the UTC time zone. @@ -527,7 +527,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.toUTCString()`. #[inline] pub fn to_utc_string(&self, context: &mut Context) -> JsResult { - Date::to_utc_string(&self.inner.clone().into(), &[JsValue::Null], context) + Date::to_utc_string(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Returns the primitive value pf Date object. @@ -535,7 +535,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.valueOf()`. #[inline] pub fn value_of(&self, context: &mut Context) -> JsResult { - Date::value_of(&self.inner.clone().into(), &[JsValue::Null], context) + Date::value_of(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Utility create a `Date` object from RFC3339 string @@ -584,11 +584,12 @@ impl Deref for JsDate { impl TryFromJs for JsDate { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Self::from_object(o.clone()), - _ => Err(JsNativeError::typ() + if let Some(o) = value.as_object() { + Self::from_object(o.clone()) + } else { + Err(JsNativeError::typ() .with_message("value is not a Date object") - .into()), + .into()) } } } diff --git a/core/engine/src/object/builtins/jsfunction.rs b/core/engine/src/object/builtins/jsfunction.rs index 16ff8fdd898..efe107231f5 100644 --- a/core/engine/src/object/builtins/jsfunction.rs +++ b/core/engine/src/object/builtins/jsfunction.rs @@ -77,17 +77,18 @@ impl TypedJsFunction { impl TryFromJs for TypedJsFunction { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => JsFunction::from_object(o.clone()) + if let Some(o) = value.as_object() { + JsFunction::from_object(o.clone()) .ok_or_else(|| { JsNativeError::typ() .with_message("object is not a function") .into() }) - .map(JsFunction::typed), - _ => Err(JsNativeError::typ() + .map(JsFunction::typed) + } else { + Err(JsNativeError::typ() .with_message("value is not a Function object") - .into()), + .into()) } } } @@ -183,15 +184,16 @@ impl Deref for JsFunction { impl TryFromJs for JsFunction { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Self::from_object(o.clone()).ok_or_else(|| { + if let Some(o) = value.as_object() { + Self::from_object(o.clone()).ok_or_else(|| { JsNativeError::typ() .with_message("object is not a function") .into() - }), - _ => Err(JsNativeError::typ() + }) + } else { + Err(JsNativeError::typ() .with_message("value is not a Function object") - .into()), + .into()) } } } diff --git a/core/engine/src/object/builtins/jsgenerator.rs b/core/engine/src/object/builtins/jsgenerator.rs index 33a6ba6bb5c..460638f364e 100644 --- a/core/engine/src/object/builtins/jsgenerator.rs +++ b/core/engine/src/object/builtins/jsgenerator.rs @@ -83,11 +83,12 @@ impl Deref for JsGenerator { impl TryFromJs for JsGenerator { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Self::from_object(o.clone()), - _ => Err(JsNativeError::typ() + if let Some(o) = value.as_object() { + Self::from_object(o.clone()) + } else { + Err(JsNativeError::typ() .with_message("value is not a Generator object") - .into()), + .into()) } } } diff --git a/core/engine/src/object/builtins/jsmap.rs b/core/engine/src/object/builtins/jsmap.rs index fa27c52dd8d..89dca24b6df 100644 --- a/core/engine/src/object/builtins/jsmap.rs +++ b/core/engine/src/object/builtins/jsmap.rs @@ -447,11 +447,12 @@ impl Deref for JsMap { impl TryFromJs for JsMap { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Self::from_object(o.clone()), - _ => Err(JsNativeError::typ() + if let Some(o) = value.as_object() { + Self::from_object(o.clone()) + } else { + Err(JsNativeError::typ() .with_message("value is not a Map object") - .into()), + .into()) } } } diff --git a/core/engine/src/object/builtins/jsmap_iterator.rs b/core/engine/src/object/builtins/jsmap_iterator.rs index d6fa7a82faf..1efbbdada2c 100644 --- a/core/engine/src/object/builtins/jsmap_iterator.rs +++ b/core/engine/src/object/builtins/jsmap_iterator.rs @@ -57,11 +57,12 @@ impl Deref for JsMapIterator { impl TryFromJs for JsMapIterator { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Self::from_object(o.clone()), - _ => Err(JsNativeError::typ() + if let Some(o) = value.as_object() { + Self::from_object(o.clone()) + } else { + Err(JsNativeError::typ() .with_message("value is not a MapIterator object") - .into()), + .into()) } } } diff --git a/core/engine/src/object/builtins/jspromise.rs b/core/engine/src/object/builtins/jspromise.rs index c8af0652403..a8225e1d1ba 100644 --- a/core/engine/src/object/builtins/jspromise.rs +++ b/core/engine/src/object/builtins/jspromise.rs @@ -1123,7 +1123,7 @@ impl JsPromise { /// /// let context = &mut Context::default(); /// let p1 = JsPromise::new(|fns, context| { - /// fns.resolve.call(&JsValue::Undefined, &[], context) + /// fns.resolve.call(&JsValue::UNDEFINED, &[], context) /// }, context) /// .then( /// Some( @@ -1179,11 +1179,12 @@ impl std::ops::Deref for JsPromise { impl TryFromJs for JsPromise { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Self::from_object(o.clone()), - _ => Err(JsNativeError::typ() + if let Some(o) = value.as_object() { + Self::from_object(o.clone()) + } else { + Err(JsNativeError::typ() .with_message("value is not a Promise object") - .into()), + .into()) } } } diff --git a/core/engine/src/object/builtins/jsproxy.rs b/core/engine/src/object/builtins/jsproxy.rs index 125723f71cb..e29ef3bb410 100644 --- a/core/engine/src/object/builtins/jsproxy.rs +++ b/core/engine/src/object/builtins/jsproxy.rs @@ -72,11 +72,12 @@ impl std::ops::Deref for JsProxy { impl TryFromJs for JsProxy { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Self::from_object(o.clone()), - _ => Err(JsNativeError::typ() + if let Some(o) = value.as_object() { + Self::from_object(o.clone()) + } else { + Err(JsNativeError::typ() .with_message("value is not a Proxy object") - .into()), + .into()) } } } diff --git a/core/engine/src/object/builtins/jsregexp.rs b/core/engine/src/object/builtins/jsregexp.rs index fef40e6e0cc..93a76e62bb2 100644 --- a/core/engine/src/object/builtins/jsregexp.rs +++ b/core/engine/src/object/builtins/jsregexp.rs @@ -272,11 +272,12 @@ impl Deref for JsRegExp { impl TryFromJs for JsRegExp { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Self::from_object(o.clone()), - _ => Err(JsNativeError::typ() + if let Some(o) = value.as_object() { + Self::from_object(o.clone()) + } else { + Err(JsNativeError::typ() .with_message("value is not a RegExp object") - .into()), + .into()) } } } diff --git a/core/engine/src/object/builtins/jsset.rs b/core/engine/src/object/builtins/jsset.rs index 6889fc9c254..99c35462e26 100644 --- a/core/engine/src/object/builtins/jsset.rs +++ b/core/engine/src/object/builtins/jsset.rs @@ -63,7 +63,7 @@ impl JsSet { /// Same as JavaScript's `set.clear()`. #[inline] pub fn clear(&self, context: &mut Context) -> JsResult { - Set::clear(&self.inner.clone().into(), &[JsValue::Null], context) + Set::clear(&self.inner.clone().into(), &[JsValue::NULL], context) } /// Removes the element associated to the value. @@ -76,8 +76,8 @@ impl JsSet { T: Into, { // TODO: Make `delete` return a native `bool` - match Set::delete(&self.inner.clone().into(), &[value.into()], context)? { - JsValue::Boolean(bool) => Ok(bool), + match Set::delete(&self.inner.clone().into(), &[value.into()], context)?.as_boolean() { + Some(bool) => Ok(bool), _ => unreachable!("`delete` must always return a bool"), } } @@ -91,8 +91,8 @@ impl JsSet { T: Into, { // TODO: Make `has` return a native `bool` - match Set::has(&self.inner.clone().into(), &[value.into()], context)? { - JsValue::Boolean(bool) => Ok(bool), + match Set::has(&self.inner.clone().into(), &[value.into()], context)?.as_boolean() { + Some(bool) => Ok(bool), _ => unreachable!("`has` must always return a bool"), } } @@ -103,7 +103,7 @@ impl JsSet { /// Same as JavaScript's `set.values()`. #[inline] pub fn values(&self, context: &mut Context) -> JsResult { - let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::Null], context)? + let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::NULL], context)? .get_iterator(IteratorHint::Sync, context)?; JsSetIterator::from_object(iterator_object.iterator().clone()) @@ -116,7 +116,7 @@ impl JsSet { /// Same as JavaScript's `set.keys()`. #[inline] pub fn keys(&self, context: &mut Context) -> JsResult { - let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::Null], context)? + let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::NULL], context)? .get_iterator(IteratorHint::Sync, context)?; JsSetIterator::from_object(iterator_object.iterator().clone()) @@ -187,11 +187,12 @@ impl Deref for JsSet { impl TryFromJs for JsSet { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Self::from_object(o.clone()), - _ => Err(JsNativeError::typ() + if let Some(o) = value.as_object() { + Self::from_object(o.clone()) + } else { + Err(JsNativeError::typ() .with_message("value is not a Set object") - .into()), + .into()) } } } diff --git a/core/engine/src/object/builtins/jsset_iterator.rs b/core/engine/src/object/builtins/jsset_iterator.rs index 124b987f334..519ea97e7f3 100644 --- a/core/engine/src/object/builtins/jsset_iterator.rs +++ b/core/engine/src/object/builtins/jsset_iterator.rs @@ -28,7 +28,7 @@ impl JsSetIterator { } /// Advances the `JsSetIterator` and gets the next result in the `JsSet`. pub fn next(&self, context: &mut Context) -> JsResult { - SetIterator::next(&self.inner.clone().into(), &[JsValue::Null], context) + SetIterator::next(&self.inner.clone().into(), &[JsValue::NULL], context) } } @@ -57,11 +57,12 @@ impl Deref for JsSetIterator { impl TryFromJs for JsSetIterator { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Self::from_object(o.clone()), - _ => Err(JsNativeError::typ() + if let Some(o) = value.as_object() { + Self::from_object(o.clone()) + } else { + Err(JsNativeError::typ() .with_message("value is not a SetIterator object") - .into()), + .into()) } } } diff --git a/core/engine/src/object/builtins/jssharedarraybuffer.rs b/core/engine/src/object/builtins/jssharedarraybuffer.rs index d5dc69f8b5a..dcc9f5cca99 100644 --- a/core/engine/src/object/builtins/jssharedarraybuffer.rs +++ b/core/engine/src/object/builtins/jssharedarraybuffer.rs @@ -117,11 +117,12 @@ impl Deref for JsSharedArrayBuffer { impl TryFromJs for JsSharedArrayBuffer { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Self::from_object(o.clone()), - _ => Err(JsNativeError::typ() + if let Some(o) = value.as_object() { + Self::from_object(o.clone()) + } else { + Err(JsNativeError::typ() .with_message("value is not a SharedArrayBuffer object") - .into()), + .into()) } } } diff --git a/core/engine/src/object/builtins/jstypedarray.rs b/core/engine/src/object/builtins/jstypedarray.rs index ac577f30033..1dff825dae3 100644 --- a/core/engine/src/object/builtins/jstypedarray.rs +++ b/core/engine/src/object/builtins/jstypedarray.rs @@ -222,8 +222,8 @@ impl JsTypedArray { &[predicate.into(), this_arg.into_or_undefined()], context, )? - .as_boolean() - .expect("TypedArray.prototype.every should always return boolean"); + .as_boolean() + .expect("TypedArray.prototype.every should always return boolean"); Ok(result) } @@ -241,8 +241,8 @@ impl JsTypedArray { &[callback.into(), this_arg.into_or_undefined()], context, )? - .as_boolean() - .expect("TypedArray.prototype.some should always return boolean"); + .as_boolean() + .expect("TypedArray.prototype.some should always return boolean"); Ok(result) } @@ -530,8 +530,8 @@ impl JsTypedArray { &[predicate.into(), this_arg.into_or_undefined()], context, )? - .as_number() - .expect("TypedArray.prototype.findIndex() should always return number"); + .as_number() + .expect("TypedArray.prototype.findIndex() should always return number"); if index >= 0.0 { Ok(Some(index as u64)) @@ -638,8 +638,8 @@ impl JsTypedArray { &[predicate.into(), this_arg.into_or_undefined()], context, )? - .as_number() - .expect("TypedArray.prototype.findLastIndex() should always return number"); + .as_number() + .expect("TypedArray.prototype.findLastIndex() should always return number"); if index >= 0.0 { Ok(Some(index as u64)) @@ -741,8 +741,8 @@ impl JsTypedArray { &[search_element.into(), from_index.into_or_undefined()], context, )? - .as_boolean() - .expect("TypedArray.prototype.includes should always return boolean"); + .as_boolean() + .expect("TypedArray.prototype.includes should always return boolean"); Ok(result) } @@ -762,8 +762,8 @@ impl JsTypedArray { &[search_element.into(), from_index.into_or_undefined()], context, )? - .as_number() - .expect("TypedArray.prototype.indexOf should always return number"); + .as_number() + .expect("TypedArray.prototype.indexOf should always return number"); #[allow(clippy::float_cmp)] if index == -1.0 { @@ -788,8 +788,8 @@ impl JsTypedArray { &[search_element.into(), from_index.into_or_undefined()], context, )? - .as_number() - .expect("TypedArray.prototype.lastIndexOf should always return number"); + .as_number() + .expect("TypedArray.prototype.lastIndexOf should always return number"); #[allow(clippy::float_cmp)] if index == -1.0 { @@ -807,11 +807,11 @@ impl JsTypedArray { &[separator.into_or_undefined()], context, ) - .map(|x| { - x.as_string() - .cloned() - .expect("TypedArray.prototype.join always returns string") - }) + .map(|x| { + x.as_string() + .cloned() + .expect("TypedArray.prototype.join always returns string") + }) } /// Calls `TypedArray.prototype.toReversed ( )`. @@ -912,11 +912,12 @@ impl Deref for JsTypedArray { impl TryFromJs for JsTypedArray { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Self::from_object(o.clone()), - _ => Err(JsNativeError::typ() + if let Some(o) = value.as_object() { + Self::from_object(o.clone()) + } else { + Err(JsNativeError::typ() .with_message("value is not a TypedArray object") - .into()), + .into()) } } } @@ -1062,15 +1063,16 @@ macro_rules! JsTypedArrayType { impl TryFromJs for $name { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Object(o) => Self::from_object(o.clone()), - _ => Err(JsNativeError::typ() + if let Some(o) = value.as_object() { + Self::from_object(o.clone()) + } else { + Err(JsNativeError::typ() .with_message(concat!( "value is not a ", stringify!($constructor_function), " object" )) - .into()), + .into()) } } } diff --git a/core/engine/src/object/internal_methods/mod.rs b/core/engine/src/object/internal_methods/mod.rs index ac671752f3c..c7a72a94a85 100644 --- a/core/engine/src/object/internal_methods/mod.rs +++ b/core/engine/src/object/internal_methods/mod.rs @@ -320,7 +320,7 @@ impl JsObject { /// Then, reference this static in the creation phase of an `ObjectData`. /// /// E.g. `ObjectData::string` -pub(crate) static ORDINARY_INTERNAL_METHODS: InternalObjectMethods = InternalObjectMethods { +pub(crate) const ORDINARY_INTERNAL_METHODS: InternalObjectMethods = InternalObjectMethods { __get_prototype_of__: ordinary_get_prototype_of, __set_prototype_of__: ordinary_set_prototype_of, __is_extensible__: ordinary_is_extensible, diff --git a/core/engine/src/object/property_map.rs b/core/engine/src/object/property_map.rs index cae390c6367..d639c8092b6 100644 --- a/core/engine/src/object/property_map.rs +++ b/core/engine/src/object/property_map.rs @@ -626,15 +626,15 @@ impl PropertyMap { let is_rational_integer = |n: f64| n.to_bits() == f64::from(n as i32).to_bits(); let value = match value.variant() { - JsVariant::Integer32(n) => *n, - JsVariant::Float64(n) if is_rational_integer(*n) => *n as i32, + JsVariant::Integer32(n) => n, + JsVariant::Float64(n) if is_rational_integer(n) => n as i32, JsVariant::Float64(value) => { let mut properties = properties .iter() .copied() .map(f64::from) .collect::>(); - properties[index] = *value; + properties[index] = value; self.indexed_properties = IndexedProperties::DenseF64(properties); return true; diff --git a/core/engine/src/optimizer/pass/constant_folding.rs b/core/engine/src/optimizer/pass/constant_folding.rs index c1725ddfa38..a4fef11c889 100644 --- a/core/engine/src/optimizer/pass/constant_folding.rs +++ b/core/engine/src/optimizer/pass/constant_folding.rs @@ -1,3 +1,4 @@ +use crate::value::JsVariant; use crate::{ builtins::Number, bytecompiler::ToJsString, optimizer::PassAction, value::Numeric, Context, JsBigInt, JsValue, @@ -28,20 +29,20 @@ fn literal_to_js_value(literal: &Literal, context: &mut Context) -> JsValue { } fn js_value_to_literal(value: JsValue, context: &mut Context) -> Literal { - match value { - JsValue::Null => Literal::Null, - JsValue::Undefined => Literal::Undefined, - JsValue::Boolean(v) => Literal::Bool(v), - JsValue::String(v) => { + match value.variant() { + JsVariant::Null => Literal::Null, + JsVariant::Undefined => Literal::Undefined, + JsVariant::Boolean(v) => Literal::Bool(v), + JsVariant::String(v) => { // TODO: Replace JStrRef with JsStr this would eliminate the to_vec call. let v = v.to_vec(); Literal::String(context.interner_mut().get_or_intern(JStrRef::Utf16(&v))) } - JsValue::Rational(v) => Literal::Num(v), - JsValue::Integer(v) => Literal::Int(v), - JsValue::BigInt(v) => Literal::BigInt(Box::new(v.as_inner().clone())), - JsValue::Object(_) | JsValue::Symbol(_) => { - unreachable!("value must not be a object or symbol") + JsVariant::Float64(v) => Literal::Num(v), + JsVariant::Integer32(v) => Literal::Int(v), + JsVariant::BigInt(v) => Literal::BigInt(Box::new(v.as_inner().clone())), + JsVariant::Object(_) | JsVariant::Symbol(_) => { + unreachable!("value must not be an object or symbol") } } } diff --git a/core/engine/src/value/conversions/serde_json.rs b/core/engine/src/value/conversions/serde_json.rs index 95bbd5e884f..6cf06466c45 100644 --- a/core/engine/src/value/conversions/serde_json.rs +++ b/core/engine/src/value/conversions/serde_json.rs @@ -1,6 +1,6 @@ //! This module implements the conversions from and into [`serde_json::Value`]. -use super::JsValue; +use super::{InnerValue, JsValue}; use crate::{ builtins::Array, error::JsNativeError, @@ -44,13 +44,13 @@ impl JsValue { const MIN_INT: i64 = i32::MIN as i64; match json { - Value::Null => Ok(Self::Null), - Value::Bool(b) => Ok(Self::Boolean(*b)), + Value::Null => Ok(Self::NULL), + Value::Bool(b) => Ok(Self::new(*b)), Value::Number(num) => num .as_i64() .filter(|n| (MIN_INT..=MAX_INT).contains(n)) - .map(|i| Self::Integer(i as i32)) - .or_else(|| num.as_f64().map(Self::Rational)) + .map(|i| Self::new(i as i32)) + .or_else(|| num.as_f64().map(Self::new)) .ok_or_else(|| { JsNativeError::typ() .with_message(format!("could not convert JSON number {num} to JsValue")) @@ -113,17 +113,17 @@ impl JsValue { /// /// Panics if the `JsValue` is `Undefined`. pub fn to_json(&self, context: &mut Context) -> JsResult { - match self { - Self::Null => Ok(Value::Null), - Self::Undefined => todo!("undefined to JSON"), - &Self::Boolean(b) => Ok(b.into()), - Self::String(string) => Ok(string.to_std_string_escaped().into()), - &Self::Rational(rat) => Ok(rat.into()), - &Self::Integer(int) => Ok(int.into()), - Self::BigInt(_bigint) => Err(JsNativeError::typ() + match &self.inner { + InnerValue::Null => Ok(Value::Null), + InnerValue::Undefined => todo!("undefined to JSON"), + InnerValue::Boolean(b) => Ok(Value::from(*b)), + InnerValue::String(string) => Ok(string.to_std_string_escaped().into()), + InnerValue::Float64(rat) => Ok(Value::from(*rat)), + InnerValue::Integer32(int) => Ok(Value::from(*int)), + InnerValue::BigInt(_bigint) => Err(JsNativeError::typ() .with_message("cannot convert bigint to JSON") .into()), - Self::Object(obj) => { + InnerValue::Object(obj) => { let value_by_prop_key = |property_key, context: &mut Context| { obj.borrow() .properties() @@ -168,7 +168,7 @@ impl JsValue { Ok(Value::Object(map)) } } - Self::Symbol(_sym) => Err(JsNativeError::typ() + InnerValue::Symbol(_sym) => Err(JsNativeError::typ() .with_message("cannot convert Symbol to JSON") .into()), } diff --git a/core/engine/src/value/conversions/try_from_js.rs b/core/engine/src/value/conversions/try_from_js.rs index 134a073f465..63844269612 100644 --- a/core/engine/src/value/conversions/try_from_js.rs +++ b/core/engine/src/value/conversions/try_from_js.rs @@ -28,11 +28,12 @@ impl JsValue { impl TryFromJs for bool { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match value.inner { - InnerValue::Boolean(b) => Ok(*b), - _ => Err(JsNativeError::typ() + if let Some(b) = value.as_boolean() { + Ok(b) + } else { + Err(JsNativeError::typ() .with_message("cannot convert value to a boolean") - .into()), + .into()) } } } @@ -45,15 +46,16 @@ impl TryFromJs for () { impl TryFromJs for String { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - match &value.inner { - InnerValue::String(s) => s.to_std_string().map_err(|e| { + if let Some(s) = value.as_string() { + s.to_std_string().map_err(|e| { JsNativeError::typ() .with_message(format!("could not convert JsString to Rust string: {e}")) .into() - }), - _ => Err(JsNativeError::typ() + }) + } else { + Err(JsNativeError::typ() .with_message("cannot convert value to a String") - .into()), + .into()) } } } @@ -267,10 +269,10 @@ fn value_into_vec() { Ok(value) => { value == TestStruct { - inner: true, - my_int: 11, - my_vec: vec!["a".to_string(), "b".to_string(), "c".to_string()], - } + inner: true, + my_int: 11, + my_vec: vec!["a".to_string(), "b".to_string(), "c".to_string()], + } } _ => false, } @@ -360,10 +362,10 @@ fn value_into_map() { Ok(value) => { value == vec![ - ("a".to_string(), 1), - ("b".to_string(), 2), - ("c".to_string(), 3), - ] + ("a".to_string(), 1), + ("b".to_string(), 2), + ("c".to_string(), 3), + ] .into_iter() .collect::>() } @@ -377,14 +379,14 @@ fn value_into_map() { Ok(value) => { value == std::collections::HashMap::from_iter( - vec![ - ("a".to_string(), 1), - ("b".to_string(), 2), - ("c".to_string(), 3), - ] + vec![ + ("a".to_string(), 1), + ("b".to_string(), 2), + ("c".to_string(), 3), + ] .into_iter() .collect::>(), - ) + ) } _ => false, } diff --git a/core/engine/src/value/conversions/try_from_js/collections.rs b/core/engine/src/value/conversions/try_from_js/collections.rs index 4934de30352..825b72514c5 100644 --- a/core/engine/src/value/conversions/try_from_js/collections.rs +++ b/core/engine/src/value/conversions/try_from_js/collections.rs @@ -13,7 +13,7 @@ where V: TryFromJs, { fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult { - let JsValue::Object(object) = value else { + let Some(object) = value.as_object() else { return Err(JsNativeError::typ() .with_message("cannot convert value to a BTreeMap") .into()); @@ -56,7 +56,7 @@ where S: std::hash::BuildHasher + Default, { fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult { - let JsValue::Object(object) = value else { + let Some(object) = value.as_object() else { return Err(JsNativeError::typ() .with_message("cannot convert value to a BTreeMap") .into()); diff --git a/core/engine/src/value/conversions/try_into_js.rs b/core/engine/src/value/conversions/try_into_js.rs index a4b31ea1564..a2429072a7a 100644 --- a/core/engine/src/value/conversions/try_into_js.rs +++ b/core/engine/src/value/conversions/try_into_js.rs @@ -8,18 +8,18 @@ pub trait TryIntoJs: Sized { impl TryIntoJs for bool { fn try_into_js(&self, _context: &mut Context) -> JsResult { - Ok(JsValue::Boolean(*self)) + Ok(JsValue::from(*self)) } } impl TryIntoJs for &str { fn try_into_js(&self, _context: &mut Context) -> JsResult { - Ok(JsValue::String(JsString::from(*self))) + Ok(JsValue::from(JsString::from(*self))) } } impl TryIntoJs for String { fn try_into_js(&self, _context: &mut Context) -> JsResult { - Ok(JsValue::String(JsString::from(self.as_str()))) + Ok(JsValue::from(JsString::from(self.as_str()))) } } @@ -72,7 +72,7 @@ fn err_outside_safe_range() -> crate::JsError { .into() } fn convert_safe_i64(value: i64) -> JsValue { - i32::try_from(value).map_or(JsValue::Rational(value as f64), JsValue::Integer) + i32::try_from(value).map_or(JsValue::from(value as f64), JsValue::new) } impl TryIntoJs for i64 { @@ -124,7 +124,7 @@ where fn try_into_js(&self, context: &mut Context) -> JsResult { match self { Some(x) => x.try_into_js(context), - None => Ok(JsValue::Undefined), + None => Ok(JsValue::UNDEFINED), } } } @@ -170,7 +170,7 @@ impl_try_into_js_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: impl TryIntoJs for () { fn try_into_js(&self, _context: &mut Context) -> JsResult { - Ok(JsValue::Null) + Ok(JsValue::NULL) } } diff --git a/core/engine/src/value/equality.rs b/core/engine/src/value/equality.rs index e8858022003..58f664ac8bc 100644 --- a/core/engine/src/value/equality.rs +++ b/core/engine/src/value/equality.rs @@ -13,20 +13,20 @@ impl JsValue { return false; } - match (self, other) { + match (&self.inner, &other.inner) { // 2. If Type(x) is Number or BigInt, then // a. Return ! Type(x)::equal(x, y). - (Self::BigInt(x), Self::BigInt(y)) => JsBigInt::equal(x, y), - (Self::Rational(x), Self::Rational(y)) => Number::equal(*x, *y), - (Self::Rational(x), Self::Integer(y)) => Number::equal(*x, f64::from(*y)), - (Self::Integer(x), Self::Rational(y)) => Number::equal(f64::from(*x), *y), - (Self::Integer(x), Self::Integer(y)) => x == y, + (InnerValue::BigInt(x), InnerValue::BigInt(y)) => JsBigInt::equal(x, y), + (InnerValue::Float64(x), InnerValue::Float64(y)) => Number::equal(*x, *y), + (InnerValue::Float64(x), InnerValue::Integer32(y)) => Number::equal(*x, f64::from(*y)), + (InnerValue::Integer32(x), InnerValue::Float64(y)) => Number::equal(f64::from(*x), *y), + (InnerValue::Integer32(x), InnerValue::Integer32(y)) => x == y, //Null has to be handled specially because "typeof null" returns object and if we managed //this without a special case we would compare self and other as if they were actually //objects which unfortunately fails //Specification Link: https://tc39.es/ecma262/#sec-typeof-operator - (Self::Null, Self::Null) => true, + (InnerValue::Null, InnerValue::Null) => true, // 3. Return ! SameValueNonNumeric(x, y). (_, _) => Self::same_value_non_numeric(self, other), @@ -45,17 +45,21 @@ impl JsValue { return Ok(self.strict_equals(other)); } - Ok(match (self, other) { + Ok(match (&self.inner, &other.inner) { // 2. If x is null and y is undefined, return true. // 3. If x is undefined and y is null, return true. - (Self::Null, Self::Undefined) | (Self::Undefined, Self::Null) => true, + (InnerValue::Null, InnerValue::Undefined) + | (InnerValue::Undefined, InnerValue::Null) => true, // 3. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y). // 4. If Type(x) is String and Type(y) is Number, return the result of the comparison ! ToNumber(x) == y. // // https://github.com/rust-lang/rust/issues/54883 - (Self::Integer(_) | Self::Rational(_), Self::String(_) | Self::Boolean(_)) - | (Self::String(_), Self::Integer(_) | Self::Rational(_)) => { + ( + InnerValue::Integer32(_) | InnerValue::Float64(_), + InnerValue::String(_) | InnerValue::Boolean(_), + ) + | (InnerValue::String(_), InnerValue::Integer32(_) | InnerValue::Float64(_)) => { let x = self.to_number(context)?; let y = other.to_number(context)?; Number::equal(x, y) @@ -65,30 +69,34 @@ impl JsValue { // a. Let n be ! StringToBigInt(y). // b. If n is NaN, return false. // c. Return the result of the comparison x == n. - (Self::BigInt(ref a), Self::String(ref b)) => JsBigInt::from_js_string(b) + (InnerValue::BigInt(ref a), InnerValue::String(ref b)) => JsBigInt::from_js_string(b) .as_ref() .map_or(false, |b| a == b), // 7. If Type(x) is String and Type(y) is BigInt, return the result of the comparison y == x. - (Self::String(ref a), Self::BigInt(ref b)) => JsBigInt::from_js_string(a) + (InnerValue::String(ref a), InnerValue::BigInt(ref b)) => JsBigInt::from_js_string(a) .as_ref() .map_or(false, |a| a == b), // 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y. - (Self::Boolean(x), _) => return other.equals(&Self::new(i32::from(*x)), context), + (InnerValue::Boolean(x), _) => { + return other.equals(&JsValue::new(i32::from(*x)), context) + } // 9. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y). - (_, Self::Boolean(y)) => return self.equals(&Self::new(i32::from(*y)), context), + (_, InnerValue::Boolean(y)) => { + return self.equals(&JsValue::new(i32::from(*y)), context) + } // 10. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return the result // of the comparison x == ? ToPrimitive(y). ( - Self::Object(_), - Self::String(_) - | Self::Rational(_) - | Self::Integer(_) - | Self::BigInt(_) - | Self::Symbol(_), + InnerValue::Object(_), + InnerValue::String(_) + | InnerValue::Float64(_) + | InnerValue::Integer32(_) + | InnerValue::BigInt(_) + | InnerValue::Symbol(_), ) => { let primitive = self.to_primitive(context, PreferredType::Default)?; return Ok(primitive @@ -99,12 +107,12 @@ impl JsValue { // 11. If Type(x) is Object and Type(y) is either String, Number, BigInt, or Symbol, return the result // of the comparison ? ToPrimitive(x) == y. ( - Self::String(_) - | Self::Rational(_) - | Self::Integer(_) - | Self::BigInt(_) - | Self::Symbol(_), - Self::Object(_), + InnerValue::String(_) + | InnerValue::Float64(_) + | InnerValue::Integer32(_) + | InnerValue::BigInt(_) + | InnerValue::Symbol(_), + InnerValue::Object(_), ) => { let primitive = other.to_primitive(context, PreferredType::Default)?; return Ok(primitive @@ -115,10 +123,10 @@ impl JsValue { // 12. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is Number and Type(y) is BigInt, then // a. If x or y are any of NaN, +∞, or -∞, return false. // b. If the mathematical value of x is equal to the mathematical value of y, return true; otherwise return false. - (Self::BigInt(ref a), Self::Rational(ref b)) => a == b, - (Self::Rational(ref a), Self::BigInt(ref b)) => a == b, - (Self::BigInt(ref a), Self::Integer(ref b)) => a == b, - (Self::Integer(ref a), Self::BigInt(ref b)) => a == b, + (InnerValue::BigInt(ref a), InnerValue::Float64(ref b)) => a == b, + (InnerValue::Float64(ref a), InnerValue::BigInt(ref b)) => a == b, + (InnerValue::BigInt(ref a), InnerValue::Integer32(ref b)) => a == b, + (InnerValue::Integer32(ref a), InnerValue::BigInt(ref b)) => a == b, // 13. Return false. _ => false, diff --git a/core/engine/src/value/hash.rs b/core/engine/src/value/hash.rs index c657037b1cd..3becb2fcdab 100644 --- a/core/engine/src/value/hash.rs +++ b/core/engine/src/value/hash.rs @@ -1,4 +1,4 @@ -use super::JsValue; +use super::{InnerValue, JsValue}; use crate::builtins::Number; use std::hash::{Hash, Hasher}; @@ -36,16 +36,16 @@ impl Hash for RationalHashable { impl Hash for JsValue { fn hash(&self, state: &mut H) { - match self { - Self::Undefined => UndefinedHashable.hash(state), - Self::Null => NullHashable.hash(state), - Self::String(ref string) => string.hash(state), - Self::Boolean(boolean) => boolean.hash(state), - Self::Integer(integer) => RationalHashable(f64::from(*integer)).hash(state), - Self::BigInt(ref bigint) => bigint.hash(state), - Self::Rational(rational) => RationalHashable(*rational).hash(state), - Self::Symbol(ref symbol) => Hash::hash(symbol, state), - Self::Object(ref object) => object.hash(state), + match self.inner { + InnerValue::Undefined => UndefinedHashable.hash(state), + InnerValue::Null => NullHashable.hash(state), + InnerValue::String(ref string) => string.hash(state), + InnerValue::Boolean(boolean) => boolean.hash(state), + InnerValue::Integer32(integer) => RationalHashable(f64::from(integer)).hash(state), + InnerValue::BigInt(ref bigint) => bigint.hash(state), + InnerValue::Float64(rational) => RationalHashable(rational).hash(state), + InnerValue::Symbol(ref symbol) => Hash::hash(symbol, state), + InnerValue::Object(ref object) => object.hash(state), } } } diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index ccf8c642919..4c4987f5591 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -69,7 +69,7 @@ static TWO_E_63: Lazy = Lazy::new(|| { /// This is not a public API and should not be used directly. /// /// If you need access to the variant, use [JsValue::variant] instead. -#[derive(Finalize, Debug, Clone, PartialEq, Eq)] +#[derive(Finalize, Debug, Clone, PartialEq)] enum InnerValue { Null, Undefined, @@ -98,31 +98,43 @@ pub struct JsValue { } impl JsValue { + /// The integer zero as a [JsValue] constant, for convenience. pub const ZERO: Self = Self { inner: InnerValue::Integer32(0), }; + + /// The integer one as a [JsValue] constant, for convenience. pub const ONE: Self = Self { inner: InnerValue::Integer32(1), }; + + /// NaN as a [JsValue] constant, for convenience. pub const NAN: Self = Self { inner: InnerValue::Float64(f64::NAN), }; + + /// Positive infinity as a [JsValue] constant, for convenience. pub const POSITIVE_INFINITY: Self = Self { inner: InnerValue::Float64(f64::INFINITY), }; + + /// Negative infinity as a [JsValue] constant, for convenience. pub const NEGATIVE_INFINITY: Self = Self { inner: InnerValue::Float64(f64::NEG_INFINITY), }; + /// Undefined as a [JsValue] constant, for convenience. pub const UNDEFINED: Self = Self { inner: InnerValue::Undefined, }; + + /// Null as a [JsValue] constant, for convenience. pub const NULL: Self = Self { inner: InnerValue::Null, }; /// Create a new [`JsValue`] from an inner value. - pub(crate) const fn from_inner(inner: InnerValue) -> Self { + const fn from_inner(inner: InnerValue) -> Self { Self { inner } } @@ -205,7 +217,7 @@ impl JsValue { /// [spec]: https://tc39.es/ecma262/#sec-iscallable #[inline] #[must_use] - pub const fn is_callable(&self) -> bool { + pub fn is_callable(&self) -> bool { if let InnerValue::Object(obj) = &self.inner { obj.is_callable() } else { @@ -325,6 +337,28 @@ impl JsValue { matches!(&self.inner, InnerValue::Undefined) } + /// Returns `()` if the value is undefined, otherwise `None`. + #[inline] + #[must_use] + pub const fn as_undefined(&self) -> Option<()> { + if self.is_undefined() { + Some(()) + } else { + None + } + } + + /// Returns `Some(self)` if the value is defined, otherwise `None`. + #[inline] + #[must_use] + pub const fn as_defined(&self) -> Option<&Self> { + if !self.is_undefined() { + Some(self) + } else { + None + } + } + /// Returns true if the value is null. #[inline] #[must_use] @@ -451,7 +485,7 @@ impl JsValue { InnerValue::Float64(n) if !n.is_nan() => true, InnerValue::Integer32(n) if n != 0 => true, InnerValue::BigInt(ref n) if !n.is_zero() => true, - InnerValue::Boolean(v) => *v, + InnerValue::Boolean(v) => v, _ => false, } } @@ -519,7 +553,7 @@ impl JsValue { /// /// [spec]: https://tc39.es/ecma262/#sec-tobigint pub fn to_bigint(&self, context: &mut Context) -> JsResult { - match self.inner { + match &self.inner { InnerValue::Null => Err(JsNativeError::typ() .with_message("cannot convert null to a BigInt") .into()), @@ -585,7 +619,7 @@ impl JsValue { InnerValue::Undefined => Ok(js_string!("undefined")), InnerValue::Boolean(true) => Ok(js_string!("true")), InnerValue::Boolean(false) => Ok(js_string!("false")), - InnerValue::Float64(rational) => Ok(Number::to_js_string(*rational)), + InnerValue::Float64(rational) => Ok(Number::to_js_string(rational)), InnerValue::Integer32(integer) => Ok(integer.to_string().into()), InnerValue::String(ref string) => Ok(string.clone()), InnerValue::Symbol(_) => Err(JsNativeError::typ() @@ -599,18 +633,6 @@ impl JsValue { } } - /// Consumes the value and return an object if it is an object. - /// Otherwise, it returns `None`. - #[inline] - #[must_use] - pub fn into_object(self) -> Option { - if let InnerValue::Object(obj) = self.inner { - Some(obj) - } else { - None - } - } - /// Converts the value to an Object. /// /// This function is equivalent to `Object(value)` in JavaScript. @@ -671,10 +693,10 @@ impl JsValue { InnerValue::String(ref string) => string.clone().into(), InnerValue::Symbol(ref symbol) => symbol.clone().into(), InnerValue::Integer32(integer) => integer.into(), - primitive => primitive.to_string(context)?.into(), + _ => primitive.to_string(context)?.into(), } } - primitive => primitive.to_string(context)?.into(), + _ => self.to_string(context)?.into(), }) } diff --git a/core/engine/src/value/operations.rs b/core/engine/src/value/operations.rs index d243315beaa..2b0d659c4d2 100644 --- a/core/engine/src/value/operations.rs +++ b/core/engine/src/value/operations.rs @@ -1,3 +1,4 @@ +use crate::value::InnerValue; use crate::{ builtins::{ number::{f64_to_int32, f64_to_uint32}, @@ -12,56 +13,63 @@ use crate::{ impl JsValue { /// Perform the binary `+` operator on the value and return the result. pub fn add(&self, other: &Self, context: &mut Context) -> JsResult { - Ok(match (self, other) { + Ok(match (&self.inner, &other.inner) { // Fast path: // Numeric add - (Self::Integer(x), Self::Integer(y)) => x + (InnerValue::Integer32(x), InnerValue::Integer32(y)) => x .checked_add(*y) .map_or_else(|| Self::new(f64::from(*x) + f64::from(*y)), Self::new), - (Self::Rational(x), Self::Rational(y)) => Self::new(x + y), - (Self::Integer(x), Self::Rational(y)) => Self::new(f64::from(*x) + y), - (Self::Rational(x), Self::Integer(y)) => Self::new(x + f64::from(*y)), - (Self::BigInt(ref x), Self::BigInt(ref y)) => Self::new(JsBigInt::add(x, y)), + (InnerValue::Float64(x), InnerValue::Float64(y)) => Self::new(x + y), + (InnerValue::Integer32(x), InnerValue::Float64(y)) => Self::new(f64::from(*x) + y), + (InnerValue::Float64(x), InnerValue::Integer32(y)) => Self::new(x + f64::from(*y)), + (InnerValue::BigInt(ref x), InnerValue::BigInt(ref y)) => { + Self::new(JsBigInt::add(x, y)) + } // String concat - (Self::String(ref x), Self::String(ref y)) => Self::from(js_string!(x, y)), + (InnerValue::String(ref x), InnerValue::String(ref y)) => Self::from(js_string!(x, y)), // Slow path: - (_, _) => match ( - self.to_primitive(context, PreferredType::Default)?, - other.to_primitive(context, PreferredType::Default)?, - ) { - (Self::String(ref x), ref y) => Self::from(js_string!(x, &y.to_string(context)?)), - (ref x, Self::String(ref y)) => Self::from(js_string!(&x.to_string(context)?, y)), - (x, y) => match (x.to_numeric(context)?, y.to_numeric(context)?) { - (Numeric::Number(x), Numeric::Number(y)) => Self::new(x + y), - (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => { - Self::new(JsBigInt::add(x, y)) + (_, _) => { + let x = self.to_primitive(context, PreferredType::Default)?; + let y = other.to_primitive(context, PreferredType::Default)?; + match (&x.inner, &y.inner) { + (InnerValue::String(ref x), _) => { + Self::from(js_string!(x, &y.to_string(context)?)) } + (_, InnerValue::String(y)) => Self::from(js_string!(&x.to_string(context)?, y)), (_, _) => { - return Err(JsNativeError::typ() - .with_message( - "cannot mix BigInt and other types, use explicit conversions", - ) - .into()) + match (x.to_numeric(context)?, y.to_numeric(context)?) { + (Numeric::Number(x), Numeric::Number(y)) => Self::new(x + y), + (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => { + Self::new(JsBigInt::add(x, y)) + } + (_, _) => return Err(JsNativeError::typ() + .with_message( + "cannot mix BigInt and other types, use explicit conversions", + ) + .into()), + } } - }, - }, + } + } }) } /// Perform the binary `-` operator on the value and return the result. pub fn sub(&self, other: &Self, context: &mut Context) -> JsResult { - Ok(match (self, other) { + Ok(match (&self.inner, &other.inner) { // Fast path: - (Self::Integer(x), Self::Integer(y)) => x + (InnerValue::Integer32(x), InnerValue::Integer32(y)) => x .checked_sub(*y) .map_or_else(|| Self::new(f64::from(*x) - f64::from(*y)), Self::new), - (Self::Rational(x), Self::Rational(y)) => Self::new(x - y), - (Self::Integer(x), Self::Rational(y)) => Self::new(f64::from(*x) - y), - (Self::Rational(x), Self::Integer(y)) => Self::new(x - f64::from(*y)), + (InnerValue::Float64(x), InnerValue::Float64(y)) => Self::new(x - y), + (InnerValue::Integer32(x), InnerValue::Float64(y)) => Self::new(f64::from(*x) - y), + (InnerValue::Float64(x), InnerValue::Integer32(y)) => Self::new(x - f64::from(*y)), - (Self::BigInt(ref x), Self::BigInt(ref y)) => Self::new(JsBigInt::sub(x, y)), + (InnerValue::BigInt(ref x), InnerValue::BigInt(ref y)) => { + Self::new(JsBigInt::sub(x, y)) + } // Slow path: (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) { @@ -78,16 +86,18 @@ impl JsValue { /// Perform the binary `*` operator on the value and return the result. pub fn mul(&self, other: &Self, context: &mut Context) -> JsResult { - Ok(match (self, other) { + Ok(match (&self.inner, &other.inner) { // Fast path: - (Self::Integer(x), Self::Integer(y)) => x + (InnerValue::Integer32(x), InnerValue::Integer32(y)) => x .checked_mul(*y) .map_or_else(|| Self::new(f64::from(*x) * f64::from(*y)), Self::new), - (Self::Rational(x), Self::Rational(y)) => Self::new(x * y), - (Self::Integer(x), Self::Rational(y)) => Self::new(f64::from(*x) * y), - (Self::Rational(x), Self::Integer(y)) => Self::new(x * f64::from(*y)), + (InnerValue::Float64(x), InnerValue::Float64(y)) => Self::new(x * y), + (InnerValue::Integer32(x), InnerValue::Float64(y)) => Self::new(f64::from(*x) * y), + (InnerValue::Float64(x), InnerValue::Integer32(y)) => Self::new(x * f64::from(*y)), - (Self::BigInt(ref x), Self::BigInt(ref y)) => Self::new(JsBigInt::mul(x, y)), + (InnerValue::BigInt(ref x), InnerValue::BigInt(ref y)) => { + Self::new(JsBigInt::mul(x, y)) + } // Slow path: (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) { @@ -104,17 +114,17 @@ impl JsValue { /// Perform the binary `/` operator on the value and return the result. pub fn div(&self, other: &Self, context: &mut Context) -> JsResult { - Ok(match (self, other) { + Ok(match (&self.inner, &other.inner) { // Fast path: - (Self::Integer(x), Self::Integer(y)) => x + (InnerValue::Integer32(x), InnerValue::Integer32(y)) => x .checked_div(*y) .filter(|div| *y * div == *x) .map_or_else(|| Self::new(f64::from(*x) / f64::from(*y)), Self::new), - (Self::Rational(x), Self::Rational(y)) => Self::new(x / y), - (Self::Integer(x), Self::Rational(y)) => Self::new(f64::from(*x) / y), - (Self::Rational(x), Self::Integer(y)) => Self::new(x / f64::from(*y)), + (InnerValue::Float64(x), InnerValue::Float64(y)) => Self::new(x / y), + (InnerValue::Integer32(x), InnerValue::Float64(y)) => Self::new(f64::from(*x) / y), + (InnerValue::Float64(x), InnerValue::Integer32(y)) => Self::new(x / f64::from(*y)), - (Self::BigInt(ref x), Self::BigInt(ref y)) => { + (InnerValue::BigInt(ref x), InnerValue::BigInt(ref y)) => { if y.is_zero() { return Err(JsNativeError::range() .with_message("BigInt division by zero") @@ -145,9 +155,9 @@ impl JsValue { /// Perform the binary `%` operator on the value and return the result. pub fn rem(&self, other: &Self, context: &mut Context) -> JsResult { - Ok(match (self, other) { + Ok(match (&self.inner, &other.inner) { // Fast path: - (Self::Integer(x), Self::Integer(y)) => { + (InnerValue::Integer32(x), InnerValue::Integer32(y)) => { if *y == 0 { Self::nan() } else { @@ -157,15 +167,17 @@ impl JsValue { } } } - (Self::Rational(x), Self::Rational(y)) => Self::new((x % y).copysign(*x)), - (Self::Integer(x), Self::Rational(y)) => { + (InnerValue::Float64(x), InnerValue::Float64(y)) => Self::new((x % y).copysign(*x)), + (InnerValue::Integer32(x), InnerValue::Float64(y)) => { let x = f64::from(*x); Self::new((x % y).copysign(x)) } - (Self::Rational(x), Self::Integer(y)) => Self::new((x % f64::from(*y)).copysign(*x)), + (InnerValue::Float64(x), InnerValue::Integer32(y)) => { + Self::new((x % f64::from(*y)).copysign(*x)) + } - (Self::BigInt(ref x), Self::BigInt(ref y)) => { + (InnerValue::BigInt(x), InnerValue::BigInt(y)) => { if y.is_zero() { return Err(JsNativeError::range() .with_message("BigInt division by zero") @@ -198,28 +210,30 @@ impl JsValue { // NOTE: There are some cases in the spec where we have to compare floats #[allow(clippy::float_cmp)] pub fn pow(&self, other: &Self, context: &mut Context) -> JsResult { - Ok(match (self, other) { + Ok(match (&self.inner, &other.inner) { // Fast path: - (Self::Integer(x), Self::Integer(y)) => u32::try_from(*y) + (InnerValue::Integer32(x), InnerValue::Integer32(y)) => u32::try_from(*y) .ok() .and_then(|y| x.checked_pow(y)) .map_or_else(|| Self::new(f64::from(*x).powi(*y)), Self::new), - (Self::Rational(x), Self::Rational(y)) => { + (InnerValue::Float64(x), InnerValue::Float64(y)) => { if x.abs() == 1.0 && y.is_infinite() { Self::nan() } else { Self::new(x.powf(*y)) } } - (Self::Integer(x), Self::Rational(y)) => { + (InnerValue::Integer32(x), InnerValue::Float64(y)) => { if x.wrapping_abs() == 1 && y.is_infinite() { Self::nan() } else { Self::new(f64::from(*x).powf(*y)) } } - (Self::Rational(x), Self::Integer(y)) => Self::new(x.powi(*y)), - (Self::BigInt(ref a), Self::BigInt(ref b)) => Self::new(JsBigInt::pow(a, b)?), + (InnerValue::Float64(x), InnerValue::Integer32(y)) => Self::new(x.powi(*y)), + (InnerValue::BigInt(ref a), InnerValue::BigInt(ref b)) => { + Self::new(JsBigInt::pow(a, b)?) + } // Slow path: (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) { @@ -242,16 +256,16 @@ impl JsValue { /// Perform the binary `&` operator on the value and return the result. pub fn bitand(&self, other: &Self, context: &mut Context) -> JsResult { - Ok(match (self, other) { + Ok(match (&self.inner, &other.inner) { // Fast path: - (Self::Integer(x), Self::Integer(y)) => Self::new(x & y), - (Self::Rational(x), Self::Rational(y)) => { + (InnerValue::Integer32(x), InnerValue::Integer32(y)) => Self::new(x & y), + (InnerValue::Float64(x), InnerValue::Float64(y)) => { Self::new(f64_to_int32(*x) & f64_to_int32(*y)) } - (Self::Integer(x), Self::Rational(y)) => Self::new(x & f64_to_int32(*y)), - (Self::Rational(x), Self::Integer(y)) => Self::new(f64_to_int32(*x) & y), + (InnerValue::Integer32(x), InnerValue::Float64(y)) => Self::new(x & f64_to_int32(*y)), + (InnerValue::Float64(x), InnerValue::Integer32(y)) => Self::new(f64_to_int32(*x) & y), - (Self::BigInt(ref x), Self::BigInt(ref y)) => Self::new(JsBigInt::bitand(x, y)), + (InnerValue::BigInt(x), InnerValue::BigInt(y)) => Self::new(JsBigInt::bitand(x, y)), // Slow path: (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) { @@ -272,16 +286,16 @@ impl JsValue { /// Perform the binary `|` operator on the value and return the result. pub fn bitor(&self, other: &Self, context: &mut Context) -> JsResult { - Ok(match (self, other) { + Ok(match (&self.inner, &other.inner) { // Fast path: - (Self::Integer(x), Self::Integer(y)) => Self::new(x | y), - (Self::Rational(x), Self::Rational(y)) => { + (InnerValue::Integer32(x), InnerValue::Integer32(y)) => Self::new(x | y), + (InnerValue::Float64(x), InnerValue::Float64(y)) => { Self::new(f64_to_int32(*x) | f64_to_int32(*y)) } - (Self::Integer(x), Self::Rational(y)) => Self::new(x | f64_to_int32(*y)), - (Self::Rational(x), Self::Integer(y)) => Self::new(f64_to_int32(*x) | y), + (InnerValue::Integer32(x), InnerValue::Float64(y)) => Self::new(x | f64_to_int32(*y)), + (InnerValue::Float64(x), InnerValue::Integer32(y)) => Self::new(f64_to_int32(*x) | y), - (Self::BigInt(ref x), Self::BigInt(ref y)) => Self::new(JsBigInt::bitor(x, y)), + (InnerValue::BigInt(x), InnerValue::BigInt(y)) => Self::new(JsBigInt::bitor(x, y)), // Slow path: (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) { @@ -302,16 +316,16 @@ impl JsValue { /// Perform the binary `^` operator on the value and return the result. pub fn bitxor(&self, other: &Self, context: &mut Context) -> JsResult { - Ok(match (self, other) { + Ok(match (&self.inner, &other.inner) { // Fast path: - (Self::Integer(x), Self::Integer(y)) => Self::new(x ^ y), - (Self::Rational(x), Self::Rational(y)) => { + (InnerValue::Integer32(x), InnerValue::Integer32(y)) => Self::new(x ^ y), + (InnerValue::Float64(x), InnerValue::Float64(y)) => { Self::new(f64_to_int32(*x) ^ f64_to_int32(*y)) } - (Self::Integer(x), Self::Rational(y)) => Self::new(x ^ f64_to_int32(*y)), - (Self::Rational(x), Self::Integer(y)) => Self::new(f64_to_int32(*x) ^ y), + (InnerValue::Integer32(x), InnerValue::Float64(y)) => Self::new(x ^ f64_to_int32(*y)), + (InnerValue::Float64(x), InnerValue::Integer32(y)) => Self::new(f64_to_int32(*x) ^ y), - (Self::BigInt(ref x), Self::BigInt(ref y)) => Self::new(JsBigInt::bitxor(x, y)), + (InnerValue::BigInt(x), InnerValue::BigInt(y)) => Self::new(JsBigInt::bitxor(x, y)), // Slow path: (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) { @@ -332,18 +346,24 @@ impl JsValue { /// Perform the binary `<<` operator on the value and return the result. pub fn shl(&self, other: &Self, context: &mut Context) -> JsResult { - Ok(match (self, other) { + Ok(match (&self.inner, &other.inner) { // Fast path: - (Self::Integer(x), Self::Integer(y)) => Self::new(x.wrapping_shl(*y as u32)), - (Self::Rational(x), Self::Rational(y)) => { + (InnerValue::Integer32(x), InnerValue::Integer32(y)) => { + Self::new(x.wrapping_shl(*y as u32)) + } + (InnerValue::Float64(x), InnerValue::Float64(y)) => { Self::new(f64_to_int32(*x).wrapping_shl(f64_to_uint32(*y))) } - (Self::Integer(x), Self::Rational(y)) => Self::new(x.wrapping_shl(f64_to_uint32(*y))), - (Self::Rational(x), Self::Integer(y)) => { + (InnerValue::Integer32(x), InnerValue::Float64(y)) => { + Self::new(x.wrapping_shl(f64_to_uint32(*y))) + } + (InnerValue::Float64(x), InnerValue::Integer32(y)) => { Self::new(f64_to_int32(*x).wrapping_shl(*y as u32)) } - (Self::BigInt(ref a), Self::BigInt(ref b)) => Self::new(JsBigInt::shift_left(a, b)?), + (InnerValue::BigInt(a), InnerValue::BigInt(b)) => { + Self::new(JsBigInt::shift_left(a, b)?) + } // Slow path: (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) { @@ -364,18 +384,24 @@ impl JsValue { /// Perform the binary `>>` operator on the value and return the result. pub fn shr(&self, other: &Self, context: &mut Context) -> JsResult { - Ok(match (self, other) { + Ok(match (&self.inner, &other.inner) { // Fast path: - (Self::Integer(x), Self::Integer(y)) => Self::new(x.wrapping_shr(*y as u32)), - (Self::Rational(x), Self::Rational(y)) => { + (InnerValue::Integer32(x), InnerValue::Integer32(y)) => { + Self::new(x.wrapping_shr(*y as u32)) + } + (InnerValue::Float64(x), InnerValue::Float64(y)) => { Self::new(f64_to_int32(*x).wrapping_shr(f64_to_uint32(*y))) } - (Self::Integer(x), Self::Rational(y)) => Self::new(x.wrapping_shr(f64_to_uint32(*y))), - (Self::Rational(x), Self::Integer(y)) => { + (InnerValue::Integer32(x), InnerValue::Float64(y)) => { + Self::new(x.wrapping_shr(f64_to_uint32(*y))) + } + (InnerValue::Float64(x), InnerValue::Integer32(y)) => { Self::new(f64_to_int32(*x).wrapping_shr(*y as u32)) } - (Self::BigInt(ref a), Self::BigInt(ref b)) => Self::new(JsBigInt::shift_right(a, b)?), + (InnerValue::BigInt(a), InnerValue::BigInt(b)) => { + Self::new(JsBigInt::shift_right(a, b)?) + } // Slow path: (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) { @@ -396,16 +422,18 @@ impl JsValue { /// Perform the binary `>>>` operator on the value and return the result. pub fn ushr(&self, other: &Self, context: &mut Context) -> JsResult { - Ok(match (self, other) { + Ok(match (&self.inner, &other.inner) { // Fast path: - (Self::Integer(x), Self::Integer(y)) => Self::new((*x as u32).wrapping_shr(*y as u32)), - (Self::Rational(x), Self::Rational(y)) => { + (InnerValue::Integer32(x), InnerValue::Integer32(y)) => { + Self::new((*x as u32).wrapping_shr(*y as u32)) + } + (InnerValue::Float64(x), InnerValue::Float64(y)) => { Self::new(f64_to_uint32(*x).wrapping_shr(f64_to_uint32(*y))) } - (Self::Integer(x), Self::Rational(y)) => { + (InnerValue::Integer32(x), InnerValue::Float64(y)) => { Self::new((*x as u32).wrapping_shr(f64_to_uint32(*y))) } - (Self::Rational(x), Self::Integer(y)) => { + (InnerValue::Float64(x), InnerValue::Integer32(y)) => { Self::new(f64_to_uint32(*x).wrapping_shr(*y as u32)) } @@ -469,18 +497,20 @@ impl JsValue { /// Returns the negated value. pub fn neg(&self, context: &mut Context) -> JsResult { - Ok(match *self { - Self::Symbol(_) | Self::Undefined => Self::new(f64::NAN), - Self::Object(_) => Self::new( + Ok(match self.inner { + InnerValue::Symbol(_) | InnerValue::Undefined => Self::new(f64::NAN), + InnerValue::Object(_) => Self::new( self.to_numeric_number(context) .map_or(f64::NAN, std::ops::Neg::neg), ), - Self::String(ref str) => Self::new(-str.to_number()), - Self::Rational(num) => Self::new(-num), - Self::Integer(0) | Self::Boolean(false) | Self::Null => Self::new(-f64::from(0)), - Self::Integer(num) => Self::new(-num), - Self::Boolean(true) => Self::new(-f64::from(1)), - Self::BigInt(ref x) => Self::new(JsBigInt::neg(x)), + InnerValue::String(ref str) => Self::new(-str.to_number()), + InnerValue::Float64(num) => Self::new(-num), + InnerValue::Integer32(0) | InnerValue::Boolean(false) | InnerValue::Null => { + Self::new(-f64::from(0)) + } + InnerValue::Integer32(num) => Self::new(-num), + InnerValue::Boolean(true) => Self::new(-f64::from(1)), + InnerValue::BigInt(ref x) => Self::new(JsBigInt::neg(x)), }) } @@ -513,13 +543,17 @@ impl JsValue { left_first: bool, context: &mut Context, ) -> JsResult { - Ok(match (self, other) { + Ok(match (&self.inner, &other.inner) { // Fast path (for some common operations): - (Self::Integer(x), Self::Integer(y)) => (x < y).into(), - (Self::Integer(x), Self::Rational(y)) => Number::less_than(f64::from(*x), *y), - (Self::Rational(x), Self::Integer(y)) => Number::less_than(*x, f64::from(*y)), - (Self::Rational(x), Self::Rational(y)) => Number::less_than(*x, *y), - (Self::BigInt(ref x), Self::BigInt(ref y)) => (x < y).into(), + (InnerValue::Integer32(x), InnerValue::Integer32(y)) => (x < y).into(), + (InnerValue::Integer32(x), InnerValue::Float64(y)) => { + Number::less_than(f64::from(*x), *y) + } + (InnerValue::Float64(x), InnerValue::Integer32(y)) => { + Number::less_than(*x, f64::from(*y)) + } + (InnerValue::Float64(x), InnerValue::Float64(y)) => Number::less_than(*x, *y), + (InnerValue::BigInt(x), InnerValue::BigInt(y)) => (x < y).into(), // Slow path: (_, _) => { @@ -534,13 +568,17 @@ impl JsValue { (px, py) }; - match (px, py) { - (Self::String(ref x), Self::String(ref y)) => (x < y).into(), - (Self::BigInt(ref x), Self::String(ref y)) => JsBigInt::from_js_string(y) - .map_or(AbstractRelation::Undefined, |y| (*x < y).into()), - (Self::String(ref x), Self::BigInt(ref y)) => JsBigInt::from_js_string(x) - .map_or(AbstractRelation::Undefined, |x| (x < *y).into()), - (px, py) => match (px.to_numeric(context)?, py.to_numeric(context)?) { + match (&px.inner, &py.inner) { + (InnerValue::String(x), InnerValue::String(ref y)) => (x < y).into(), + (InnerValue::BigInt(x), InnerValue::String(ref y)) => { + JsBigInt::from_js_string(y) + .map_or(AbstractRelation::Undefined, |y| (*x < y).into()) + } + (InnerValue::String(ref x), InnerValue::BigInt(ref y)) => { + JsBigInt::from_js_string(x) + .map_or(AbstractRelation::Undefined, |x| (x < *y).into()) + } + (_, _) => match (px.to_numeric(context)?, py.to_numeric(context)?) { (Numeric::Number(x), Numeric::Number(y)) => Number::less_than(x, y), (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => (x < y).into(), (Numeric::BigInt(ref x), Numeric::Number(y)) => { diff --git a/core/engine/src/value/type.rs b/core/engine/src/value/type.rs index 74ca4d4141e..6d4b6d9c184 100644 --- a/core/engine/src/value/type.rs +++ b/core/engine/src/value/type.rs @@ -1,4 +1,4 @@ -use super::JsValue; +use super::{InnerValue, JsValue}; /// Possible types of values as defined at . #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -37,15 +37,15 @@ impl JsValue { /// Check [`JsValue::type_of`] if you need to call the `typeof` operator. #[must_use] pub const fn get_type(&self) -> Type { - match *self { - Self::Rational(_) | Self::Integer(_) => Type::Number, - Self::String(_) => Type::String, - Self::Boolean(_) => Type::Boolean, - Self::Symbol(_) => Type::Symbol, - Self::Null => Type::Null, - Self::Undefined => Type::Undefined, - Self::BigInt(_) => Type::BigInt, - Self::Object(_) => Type::Object, + match self.inner { + InnerValue::Float64(_) | InnerValue::Integer32(_) => Type::Number, + InnerValue::String(_) => Type::String, + InnerValue::Boolean(_) => Type::Boolean, + InnerValue::Symbol(_) => Type::Symbol, + InnerValue::Null => Type::Null, + InnerValue::Undefined => Type::Undefined, + InnerValue::BigInt(_) => Type::BigInt, + InnerValue::Object(_) => Type::Object, } } } diff --git a/core/engine/src/value/variant.rs b/core/engine/src/value/variant.rs index 777120c8825..e7b1daa6070 100644 --- a/core/engine/src/value/variant.rs +++ b/core/engine/src/value/variant.rs @@ -67,6 +67,12 @@ impl<'a> From> for JsValue { } impl JsVariant<'_> { + /// Check if the variant is an `undefined` value. + #[inline] + pub fn is_undefined(&self) -> bool { + matches!(self, JsVariant::Undefined) + } + /// `typeof` operator. Returns a string representing the type of the /// given ECMA Value. /// diff --git a/core/engine/src/vm/call_frame/mod.rs b/core/engine/src/vm/call_frame/mod.rs index 3aca4f33a28..325bd34e74b 100644 --- a/core/engine/src/vm/call_frame/mod.rs +++ b/core/engine/src/vm/call_frame/mod.rs @@ -377,8 +377,8 @@ impl JsValue { /// If not a integer type or not in the range `1..=2`. #[track_caller] pub(crate) fn to_generator_resume_kind(&self) -> GeneratorResumeKind { - if let Self::Integer(value) = self { - match *value { + if let Some(value) = self.as_integer32() { + match value { 0 => return GeneratorResumeKind::Normal, 1 => return GeneratorResumeKind::Throw, 2 => return GeneratorResumeKind::Return, diff --git a/core/engine/src/vm/opcode/control_flow/jump.rs b/core/engine/src/vm/opcode/control_flow/jump.rs index 9d402c15887..5b973633e71 100644 --- a/core/engine/src/vm/opcode/control_flow/jump.rs +++ b/core/engine/src/vm/opcode/control_flow/jump.rs @@ -129,7 +129,7 @@ impl Operation for JumpTable { let value = context.vm.pop(); if let Some(value) = value.as_integer32() { - let value = *value as u32; + let value = value as u32; let mut target = None; for i in 0..count { let address = context.vm.read::(); diff --git a/core/engine/src/vm/opcode/set/property.rs b/core/engine/src/vm/opcode/set/property.rs index 07256681061..870f8dce8ac 100644 --- a/core/engine/src/vm/opcode/set/property.rs +++ b/core/engine/src/vm/opcode/set/property.rs @@ -1,11 +1,12 @@ use boa_macros::js_str; +use crate::value::JsVariant; use crate::{ builtins::function::set_function_name, object::{internal_methods::InternalMethodContext, shape::slot::SlotAttributes}, property::{PropertyDescriptor, PropertyKey}, vm::{opcode::Operation, CompletionType}, - Context, JsNativeError, JsResult, JsValue, + Context, JsNativeError, JsResult, }; /// `SetPropertyByName` implements the Opcode Operation for `Opcode::SetPropertyByName` @@ -379,9 +380,9 @@ impl Operation for SetFunctionName { let function = context.vm.pop(); let name = context.vm.pop(); - let name = match name { - JsValue::String(name) => name.into(), - JsValue::Symbol(name) => name.into(), + let name = match name.variant() { + JsVariant::String(name) => PropertyKey::from(name.clone()), + JsVariant::Symbol(name) => PropertyKey::from(name.clone()), _ => unreachable!(), }; diff --git a/examples/src/bin/try_into_js_derive.rs b/examples/src/bin/try_into_js_derive.rs index 55de96e87a3..ea9c00b43b4 100644 --- a/examples/src/bin/try_into_js_derive.rs +++ b/examples/src/bin/try_into_js_derive.rs @@ -92,5 +92,5 @@ fn main() -> JsResult<()> { } fn readable_into_js(value: &i8, _context: &mut Context) -> JsResult { - Ok(JsValue::Boolean(*value != 0)) + Ok(JsValue::new(*value != 0)) } From aca82f0b962d01e7f96f1351f4b8e3f7fea18043 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Mon, 9 Dec 2024 20:06:03 -0800 Subject: [PATCH 05/19] WIP --- cli/src/debug/optimizer.rs | 12 +- core/engine/src/builtins/array/mod.rs | 26 +-- core/engine/src/builtins/array_buffer/mod.rs | 10 +- core/engine/src/builtins/function/mod.rs | 8 +- .../iterable/async_from_sync_iterator.rs | 18 +- core/engine/src/builtins/number/mod.rs | 19 +- core/engine/src/builtins/regexp/mod.rs | 2 +- core/engine/src/builtins/set/mod.rs | 2 +- core/engine/src/object/operations.rs | 15 +- core/engine/src/value/conversions/mod.rs | 3 +- core/engine/src/value/mod.rs | 31 +-- core/engine/src/value/variant.rs | 7 +- core/engine/src/vm/opcode/call/mod.rs | 8 +- core/interop/src/lib.rs | 16 +- core/runtime/src/console/mod.rs | 192 +++++++++--------- examples/src/bin/jsarray.rs | 6 +- examples/src/bin/jstypedarray.rs | 8 +- examples/src/bin/modulehandler.rs | 7 +- examples/src/bin/synthetic.rs | 30 +-- 19 files changed, 212 insertions(+), 208 deletions(-) diff --git a/cli/src/debug/optimizer.rs b/cli/src/debug/optimizer.rs index 5ccbb9cf314..d35f2f8d9c3 100644 --- a/cli/src/debug/optimizer.rs +++ b/cli/src/debug/optimizer.rs @@ -41,16 +41,16 @@ pub(super) fn create_object(context: &mut Context) -> JsObject { context.realm(), NativeFunction::from_fn_ptr(get_constant_folding), ) - .name("get constantFolding") - .length(0) - .build(); + .name("get constantFolding") + .length(0) + .build(); let set_constant_folding = FunctionObjectBuilder::new( context.realm(), NativeFunction::from_fn_ptr(set_constant_folding), ) - .name("set constantFolding") - .length(1) - .build(); + .name("set constantFolding") + .length(1) + .build(); let get_statistics = FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(get_statistics)) diff --git a/core/engine/src/builtins/array/mod.rs b/core/engine/src/builtins/array/mod.rs index 3c0a02626b1..5babbc24388 100644 --- a/core/engine/src/builtins/array/mod.rs +++ b/core/engine/src/builtins/array/mod.rs @@ -89,8 +89,8 @@ impl IntrinsicObject for Array { realm.intrinsics().objects().array_prototype_values().into(), Self::values, ) - .name(js_string!("values")) - .build(); + .name(js_string!("values")) + .build(); let to_string_function = BuiltInBuilder::callable_with_object( realm, @@ -101,8 +101,8 @@ impl IntrinsicObject for Array { .into(), Self::to_string, ) - .name(js_string!("toString")) - .build(); + .name(js_string!("toString")) + .build(); let unscopables_object = Self::unscopables_object(); @@ -296,11 +296,11 @@ impl Array { let mut borrowed_object = o.borrow_mut(); if borrowed_object.properties().shape.to_addr_usize() == context - .intrinsics() - .templates() - .array() - .shape() - .to_addr_usize() + .intrinsics() + .templates() + .array() + .shape() + .to_addr_usize() { // NOTE: The "length" property is the first element. borrowed_object.properties_mut().storage[0] = JsValue::new(len); @@ -388,7 +388,7 @@ impl Array { /// [spec]: https://tc39.es/ecma262/#sec-createarrayfromlist pub(crate) fn create_array_from_list(elements: I, context: &Context) -> JsObject where - I: IntoIterator, + I: IntoIterator, { // 1. Assert: elements is a List whose elements are all ECMAScript language values. // 2. Let array be ! ArrayCreate(0). @@ -3575,7 +3575,7 @@ fn array_set_length( new_len_desc.clone().build(), context, ) - .expect("this OrdinaryDefineOwnProperty call must not fail") + .expect("this OrdinaryDefineOwnProperty call must not fail") { return Ok(false); } @@ -3612,7 +3612,7 @@ fn array_set_length( new_len_desc.build(), context, ) - .expect("this OrdinaryDefineOwnProperty call must not fail"); + .expect("this OrdinaryDefineOwnProperty call must not fail"); // iv. Return false. return Ok(false); @@ -3629,7 +3629,7 @@ fn array_set_length( PropertyDescriptor::builder().writable(false).build(), context, ) - .expect("this OrdinaryDefineOwnProperty call must not fail"); + .expect("this OrdinaryDefineOwnProperty call must not fail"); // b. Assert: succeeded is true. debug_assert!(succeeded); diff --git a/core/engine/src/builtins/array_buffer/mod.rs b/core/engine/src/builtins/array_buffer/mod.rs index ac0aea47ea4..3ac8f5ef7d7 100644 --- a/core/engine/src/builtins/array_buffer/mod.rs +++ b/core/engine/src/builtins/array_buffer/mod.rs @@ -50,8 +50,8 @@ pub(crate) enum BufferRef { impl BufferRef where - B: Deref, - S: Deref, + B: Deref, + S: Deref, { /// Gets the inner data of the buffer. pub(crate) fn bytes(&self, ordering: Ordering) -> Option> { @@ -89,8 +89,8 @@ pub(crate) enum BufferRefMut { impl BufferRefMut where - B: DerefMut, - S: DerefMut, + B: DerefMut, + S: DerefMut, { pub(crate) fn bytes(&mut self, ordering: Ordering) -> Option> { match self { @@ -828,7 +828,7 @@ impl ArrayBuffer { detach_key: JsValue::undefined(), }, ) - .into()) + .into()) } /// `AllocateArrayBuffer ( constructor, byteLength )` diff --git a/core/engine/src/builtins/function/mod.rs b/core/engine/src/builtins/function/mod.rs index e5848c72f3a..663cdd00cb9 100644 --- a/core/engine/src/builtins/function/mod.rs +++ b/core/engine/src/builtins/function/mod.rs @@ -494,8 +494,8 @@ impl BuiltInFunctionObject { param_list.iter().map(JsString::iter), js_str!(",").iter(), ) - .flatten() - .collect::>(); + .flatten() + .collect::>(); let mut parser = Parser::new(Source::from_utf16(¶meters)); parser.set_identifier(context.next_parser_identifier()); @@ -774,7 +774,7 @@ impl BuiltInFunctionObject { .configurable(true), context, ) - .expect("defining the `length` property for a new object should not fail"); + .expect("defining the `length` property for a new object should not fail"); // 8. Let targetName be ? Get(Target, "name"). let target_name = target.get(js_string!("name"), context)?; @@ -875,7 +875,7 @@ impl BuiltInFunctionObject { code.name(), js_str!("() { [native code] }") ) - .into()) + .into()) } /// `Function.prototype [ @@hasInstance ] ( V )` diff --git a/core/engine/src/builtins/iterable/async_from_sync_iterator.rs b/core/engine/src/builtins/iterable/async_from_sync_iterator.rs index 91b9fba9c7f..e9ae8b5c1bc 100644 --- a/core/engine/src/builtins/iterable/async_from_sync_iterator.rs +++ b/core/engine/src/builtins/iterable/async_from_sync_iterator.rs @@ -109,7 +109,7 @@ impl AsyncFromSyncIterator { &context.intrinsics().constructors().promise().constructor(), context, ) - .expect("cannot fail with promise constructor"); + .expect("cannot fail with promise constructor"); // 5. If value is present, then // a. Let result be Completion(IteratorNext(syncIteratorRecord, value)). @@ -154,7 +154,7 @@ impl AsyncFromSyncIterator { &context.intrinsics().constructors().promise().constructor(), context, ) - .expect("cannot fail with promise constructor"); + .expect("cannot fail with promise constructor"); // 6. Let return be Completion(GetMethod(syncIterator, "return")). let r#return = sync_iterator.get_method(js_string!("return"), context); @@ -231,7 +231,7 @@ impl AsyncFromSyncIterator { &context.intrinsics().constructors().promise().constructor(), context, ) - .expect("cannot fail with promise constructor"); + .expect("cannot fail with promise constructor"); // 6. Let throw be Completion(GetMethod(syncIterator, "throw")). let throw = sync_iterator.get_method(js_string!("throw"), context); @@ -359,9 +359,9 @@ impl AsyncFromSyncIterator { )) }), ) - .name(js_string!()) - .length(1) - .build(); + .name(js_string!()) + .length(1) + .build(); // 11. NOTE: onFulfilled is used when processing the "value" property of an // IteratorResult object in order to wait for its value if it is a promise and @@ -392,9 +392,9 @@ impl AsyncFromSyncIterator { sync_iterator_record, ), ) - .name(js_string!()) - .length(1) - .build(), + .name(js_string!()) + .length(1) + .build(), ) }; diff --git a/core/engine/src/builtins/number/mod.rs b/core/engine/src/builtins/number/mod.rs index 6f931b87c92..8f8cf021984 100644 --- a/core/engine/src/builtins/number/mod.rs +++ b/core/engine/src/builtins/number/mod.rs @@ -239,9 +239,9 @@ impl Number { None => f64_to_exponential(this_num), Some(IntegerOrInfinity::Integer(precision)) if (0..=100).contains(&precision) => // 5. If f < 0 or f > 100, throw a RangeError exception. - { - f64_to_exponential_with_precision(this_num, precision as usize) - } + { + f64_to_exponential_with_precision(this_num, precision as usize) + } _ => { return Err(JsNativeError::range() .with_message("toExponential() argument must be between 0 and 100") @@ -751,11 +751,14 @@ impl Number { // 1. If number is not a Number, return false. // 2. If number is not finite, return false. // 3. Otherwise, return true. - Ok(JsValue::new(args.first().map_or(false, |val| match val.variant() { - JsVariant::Integer32(_) => true, - JsVariant::Float64(number) => number.is_finite(), - _ => false, - }))) + Ok(JsValue::new(args.first().map_or( + false, + |val| match val.variant() { + JsVariant::Integer32(_) => true, + JsVariant::Float64(number) => number.is_finite(), + _ => false, + }, + ))) } /// `Number.isInteger( number )` diff --git a/core/engine/src/builtins/regexp/mod.rs b/core/engine/src/builtins/regexp/mod.rs index 53799b93f6c..25f9bb6dda0 100644 --- a/core/engine/src/builtins/regexp/mod.rs +++ b/core/engine/src/builtins/regexp/mod.rs @@ -1640,7 +1640,7 @@ impl RegExp { &JsString::from(&accumulated_result[..]), s.get_expect(next_source_position..) ) - .into()) + .into()) } /// `RegExp.prototype[ @@search ]( string )` diff --git a/core/engine/src/builtins/set/mod.rs b/core/engine/src/builtins/set/mod.rs index 893863d96bb..724fdf10da0 100644 --- a/core/engine/src/builtins/set/mod.rs +++ b/core/engine/src/builtins/set/mod.rs @@ -186,7 +186,7 @@ impl Set { /// Utility for constructing `Set` objects from an iterator of `JsValue`'s. pub(crate) fn create_set_from_list(elements: I, context: &mut Context) -> JsObject where - I: IntoIterator, + I: IntoIterator, { // Create empty Set let set = Self::set_create(None, context); diff --git a/core/engine/src/object/operations.rs b/core/engine/src/object/operations.rs index f42a83e122d..1419d8b8221 100644 --- a/core/engine/src/object/operations.rs +++ b/core/engine/src/object/operations.rs @@ -726,7 +726,7 @@ impl JsObject { [key_str.into(), self.get(key.clone(), context)?], context, ) - .into(), + .into(), ), } } @@ -756,11 +756,14 @@ impl JsObject { // 1. Assert: IsPropertyKey(P) is true. // 2. Let func be ? GetV(V, P). - match self.__get__( - &key.into(), - self.clone().into(), - &mut InternalMethodContext::new(context), - )?.variant() { + match self + .__get__( + &key.into(), + self.clone().into(), + &mut InternalMethodContext::new(context), + )? + .variant() + { // 3. If func is either undefined or null, return undefined. JsVariant::Undefined | JsVariant::Null => Ok(None), // 5. Return func. diff --git a/core/engine/src/value/conversions/mod.rs b/core/engine/src/value/conversions/mod.rs index 273f6fe8c86..606e99bae5c 100644 --- a/core/engine/src/value/conversions/mod.rs +++ b/core/engine/src/value/conversions/mod.rs @@ -23,7 +23,7 @@ impl From for JsValue { fn from(value: JsString) -> Self { let _timer = Profiler::global().start_event("From", "value"); - Self::from_inner(InnerValue::String(value.into())) + Self::from_inner(InnerValue::String(value)) } } @@ -55,6 +55,7 @@ macro_rules! impl_from_float { impl From<$type_> for JsValue { #[inline] #[allow(trivial_numeric_casts)] + #[allow(clippy::cast_lossless)] fn from(value: $type_) -> Self { let _timer = Profiler::global().start_event(concat!("From<", stringify!($type_), ">"), "value"); diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index 4c4987f5591..8d463ae81d5 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -65,10 +65,10 @@ static TWO_E_63: Lazy = Lazy::new(|| { BigInt::from(TWO_E_63) }); -/// The Inner type of a [JsValue]. This is the actual value that the JsValue holds. +/// The Inner type of a [`JsValue`]. This is the actual value that the `JsValue` holds. /// This is not a public API and should not be used directly. /// -/// If you need access to the variant, use [JsValue::variant] instead. +/// If you need access to the variant, use [`JsValue::variant`] instead. #[derive(Finalize, Debug, Clone, PartialEq)] enum InnerValue { Null, @@ -98,37 +98,37 @@ pub struct JsValue { } impl JsValue { - /// The integer zero as a [JsValue] constant, for convenience. + /// The integer zero as a [`JsValue`] constant, for convenience. pub const ZERO: Self = Self { inner: InnerValue::Integer32(0), }; - /// The integer one as a [JsValue] constant, for convenience. + /// The integer one as a [`JsValue`] constant, for convenience. pub const ONE: Self = Self { inner: InnerValue::Integer32(1), }; - /// NaN as a [JsValue] constant, for convenience. + /// `NaN` as a [`JsValue`] constant, for convenience. pub const NAN: Self = Self { inner: InnerValue::Float64(f64::NAN), }; - /// Positive infinity as a [JsValue] constant, for convenience. + /// Positive infinity as a [`JsValue`] constant, for convenience. pub const POSITIVE_INFINITY: Self = Self { inner: InnerValue::Float64(f64::INFINITY), }; - /// Negative infinity as a [JsValue] constant, for convenience. + /// Negative infinity as a [`JsValue`] constant, for convenience. pub const NEGATIVE_INFINITY: Self = Self { inner: InnerValue::Float64(f64::NEG_INFINITY), }; - /// Undefined as a [JsValue] constant, for convenience. + /// Undefined as a [`JsValue`] constant, for convenience. pub const UNDEFINED: Self = Self { inner: InnerValue::Undefined, }; - /// Null as a [JsValue] constant, for convenience. + /// Null as a [`JsValue`] constant, for convenience. pub const NULL: Self = Self { inner: InnerValue::Null, }; @@ -148,6 +148,7 @@ impl JsValue { /// Return the variant of this value. #[inline] + #[must_use] pub fn variant(&self) -> JsVariant<'_> { (&self.inner).into() } @@ -230,7 +231,7 @@ impl JsValue { #[must_use] pub fn as_callable(&self) -> Option<&JsObject> { if let InnerValue::Object(obj) = &self.inner { - obj.is_callable().then(|| obj) + obj.is_callable().then_some(obj) } else { None } @@ -276,7 +277,7 @@ impl JsValue { #[must_use] pub(crate) fn as_promise_object(&self) -> Option<&JsObject> { if let InnerValue::Object(obj) = &self.inner { - obj.is::().then(|| obj) + obj.is::().then_some(obj) } else { None } @@ -352,10 +353,10 @@ impl JsValue { #[inline] #[must_use] pub const fn as_defined(&self) -> Option<&Self> { - if !self.is_undefined() { - Some(self) - } else { + if self.is_undefined() { None + } else { + Some(self) } } @@ -517,7 +518,7 @@ impl JsValue { PreferredType::String => js_string!("string"), PreferredType::Number => js_string!("number"), } - .into(); + .into(); // iv. Let result be ? Call(exoticToPrim, input, « hint »). let result = exotic_to_prim.call(self, &[hint], context)?; diff --git a/core/engine/src/value/variant.rs b/core/engine/src/value/variant.rs index e7b1daa6070..5a88396c3bb 100644 --- a/core/engine/src/value/variant.rs +++ b/core/engine/src/value/variant.rs @@ -3,7 +3,7 @@ use crate::{JsBigInt, JsObject, JsSymbol, JsValue}; use boa_engine::js_string; use boa_string::JsString; -/// A non-mutable variant of a JsValue. +/// A non-mutable variant of a `JsValue`. /// Represents either a primitive value ([`bool`], [`f64`], [`i32`]) or a reference /// to a heap allocated value ([`JsString`], [`JsSymbol`]). /// @@ -69,6 +69,7 @@ impl<'a> From> for JsValue { impl JsVariant<'_> { /// Check if the variant is an `undefined` value. #[inline] + #[must_use] pub fn is_undefined(&self) -> bool { matches!(self, JsVariant::Undefined) } @@ -90,7 +91,7 @@ impl JsVariant<'_> { JsVariant::Null => "object", JsVariant::Undefined => "undefined", JsVariant::BigInt(_) => "bigint", - JsVariant::Object(ref object) => { + JsVariant::Object(object) => { if object.is_callable() { "function" } else { @@ -111,7 +112,7 @@ impl JsVariant<'_> { JsVariant::Null => js_string!("object"), JsVariant::Undefined => js_string!("undefined"), JsVariant::BigInt(_) => js_string!("bigint"), - JsVariant::Object(ref object) => { + JsVariant::Object(object) => { if object.is_callable() { js_string!("function") } else { diff --git a/core/engine/src/vm/opcode/call/mod.rs b/core/engine/src/vm/opcode/call/mod.rs index 4e8ca5e9585..c164742c56d 100644 --- a/core/engine/src/vm/opcode/call/mod.rs +++ b/core/engine/src/vm/opcode/call/mod.rs @@ -302,7 +302,7 @@ impl Operation for ImportCall { &context.intrinsics().constructors().promise().constructor(), context, ) - .expect("operation cannot fail for the %Promise% intrinsic"); + .expect("operation cannot fail for the %Promise% intrinsic"); let promise = cap.promise().clone(); // 6. Let specifierString be Completion(ToString(specifier)). @@ -397,7 +397,7 @@ impl Operation for ImportCall { cap.clone(), ), ) - .build(); + .build(); // 6. Let linkAndEvaluateClosure be a new Abstract Closure with no parameters that captures module, promiseCapability, and onRejected and performs the following steps when called: // 7. Let linkAndEvaluate be CreateBuiltinFunction(linkAndEvaluateClosure, 0, "", « »). @@ -444,7 +444,7 @@ impl Operation for ImportCall { (module.clone(), cap.clone()), ), ) - .build(); + .build(); // f. Perform PerformPromiseThen(evaluatePromise, onFulfilled, onRejected). Promise::perform_promise_then( @@ -461,7 +461,7 @@ impl Operation for ImportCall { (module.clone(), cap.clone(), on_rejected.clone()), ), ) - .build(); + .build(); // 8. Perform PerformPromiseThen(loadPromise, linkAndEvaluate, onRejected). Promise::perform_promise_then( diff --git a/core/interop/src/lib.rs b/core/interop/src/lib.rs index 7374f072c6b..edd92b4382f 100644 --- a/core/interop/src/lib.rs +++ b/core/interop/src/lib.rs @@ -28,7 +28,7 @@ pub trait IntoJsModule { fn into_js_module(self, context: &mut Context) -> Module; } -impl + Clone> IntoJsModule for T { +impl + Clone> IntoJsModule for T { fn into_js_module(self, context: &mut Context) -> Module { let (names, fns): (Vec<_>, Vec<_>) = self.into_iter().unzip(); let exports = names.clone(); @@ -219,7 +219,7 @@ impl<'a> JsRest<'a> { } /// Returns an iterator over the inner list of `JsValue`. - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { self.0.iter() } @@ -289,12 +289,12 @@ impl JsAll { } /// Returns an iterator over the inner list of `T`. - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { self.0.iter() } /// Returns a mutable iterator over the inner list of `T`. - pub fn iter_mut(&mut self) -> impl Iterator { + pub fn iter_mut(&mut self) -> impl Iterator { self.0.iter_mut() } @@ -505,7 +505,7 @@ fn into_js_module() { result } } - .into_js_function_unsafe(&mut context), + .into_js_function_unsafe(&mut context), ), ( js_string!("bar"), @@ -539,11 +539,11 @@ fn into_js_module() { (move |value: JsValue, ContextData(result): ContextData| { *result.borrow_mut() = value; }) - .into_js_function_copied(&mut context), + .into_js_function_copied(&mut context), ), ] } - .into_js_module(&mut context); + .into_js_module(&mut context); loader.register(js_string!("test"), module); @@ -600,7 +600,7 @@ fn can_throw_exception() { &mut context, ), )] - .into_js_module(&mut context); + .into_js_module(&mut context); loader.register(js_string!("test"), module); diff --git a/core/runtime/src/console/mod.rs b/core/runtime/src/console/mod.rs index 21e56db645b..9fecd4afc58 100644 --- a/core/runtime/src/console/mod.rs +++ b/core/runtime/src/console/mod.rs @@ -300,102 +300,102 @@ impl Console { JsObject::with_object_proto(context.realm().intrinsics()), context, ) - .property( - JsSymbol::to_string_tag(), - Self::NAME, - Attribute::CONFIGURABLE, - ) - .function( - console_method(Self::assert, state.clone(), logger.clone()), - js_string!("assert"), - 0, - ) - .function( - console_method_mut(Self::clear, state.clone(), logger.clone()), - js_string!("clear"), - 0, - ) - .function( - console_method(Self::debug, state.clone(), logger.clone()), - js_string!("debug"), - 0, - ) - .function( - console_method(Self::error, state.clone(), logger.clone()), - js_string!("error"), - 0, - ) - .function( - console_method(Self::info, state.clone(), logger.clone()), - js_string!("info"), - 0, - ) - .function( - console_method(Self::log, state.clone(), logger.clone()), - js_string!("log"), - 0, - ) - .function( - console_method(Self::trace, state.clone(), logger.clone()), - js_string!("trace"), - 0, - ) - .function( - console_method(Self::warn, state.clone(), logger.clone()), - js_string!("warn"), - 0, - ) - .function( - console_method_mut(Self::count, state.clone(), logger.clone()), - js_string!("count"), - 0, - ) - .function( - console_method_mut(Self::count_reset, state.clone(), logger.clone()), - js_string!("countReset"), - 0, - ) - .function( - console_method_mut(Self::group, state.clone(), logger.clone()), - js_string!("group"), - 0, - ) - .function( - console_method_mut(Self::group_collapsed, state.clone(), logger.clone()), - js_string!("groupCollapsed"), - 0, - ) - .function( - console_method_mut(Self::group_end, state.clone(), logger.clone()), - js_string!("groupEnd"), - 0, - ) - .function( - console_method_mut(Self::time, state.clone(), logger.clone()), - js_string!("time"), - 0, - ) - .function( - console_method(Self::time_log, state.clone(), logger.clone()), - js_string!("timeLog"), - 0, - ) - .function( - console_method_mut(Self::time_end, state.clone(), logger.clone()), - js_string!("timeEnd"), - 0, - ) - .function( - console_method(Self::dir, state.clone(), logger.clone()), - js_string!("dir"), - 0, - ) - .function( - console_method(Self::dir, state, logger.clone()), - js_string!("dirxml"), - 0, - ) - .build() + .property( + JsSymbol::to_string_tag(), + Self::NAME, + Attribute::CONFIGURABLE, + ) + .function( + console_method(Self::assert, state.clone(), logger.clone()), + js_string!("assert"), + 0, + ) + .function( + console_method_mut(Self::clear, state.clone(), logger.clone()), + js_string!("clear"), + 0, + ) + .function( + console_method(Self::debug, state.clone(), logger.clone()), + js_string!("debug"), + 0, + ) + .function( + console_method(Self::error, state.clone(), logger.clone()), + js_string!("error"), + 0, + ) + .function( + console_method(Self::info, state.clone(), logger.clone()), + js_string!("info"), + 0, + ) + .function( + console_method(Self::log, state.clone(), logger.clone()), + js_string!("log"), + 0, + ) + .function( + console_method(Self::trace, state.clone(), logger.clone()), + js_string!("trace"), + 0, + ) + .function( + console_method(Self::warn, state.clone(), logger.clone()), + js_string!("warn"), + 0, + ) + .function( + console_method_mut(Self::count, state.clone(), logger.clone()), + js_string!("count"), + 0, + ) + .function( + console_method_mut(Self::count_reset, state.clone(), logger.clone()), + js_string!("countReset"), + 0, + ) + .function( + console_method_mut(Self::group, state.clone(), logger.clone()), + js_string!("group"), + 0, + ) + .function( + console_method_mut(Self::group_collapsed, state.clone(), logger.clone()), + js_string!("groupCollapsed"), + 0, + ) + .function( + console_method_mut(Self::group_end, state.clone(), logger.clone()), + js_string!("groupEnd"), + 0, + ) + .function( + console_method_mut(Self::time, state.clone(), logger.clone()), + js_string!("time"), + 0, + ) + .function( + console_method(Self::time_log, state.clone(), logger.clone()), + js_string!("timeLog"), + 0, + ) + .function( + console_method_mut(Self::time_end, state.clone(), logger.clone()), + js_string!("timeEnd"), + 0, + ) + .function( + console_method(Self::dir, state.clone(), logger.clone()), + js_string!("dir"), + 0, + ) + .function( + console_method(Self::dir, state, logger.clone()), + js_string!("dirxml"), + 0, + ) + .build() } /// Initializes the `console` built-in object. diff --git a/examples/src/bin/jsarray.rs b/examples/src/bin/jsarray.rs index 62e2ba42b3c..6af88571ec5 100644 --- a/examples/src/bin/jsarray.rs +++ b/examples/src/bin/jsarray.rs @@ -70,7 +70,7 @@ fn main() -> JsResult<()> { Ok(args.first().cloned().unwrap_or_default().is_number().into()) }), ) - .build(); + .build(); let map_callback = FunctionObjectBuilder::new( context.realm(), @@ -81,7 +81,7 @@ fn main() -> JsResult<()> { .pow(&JsValue::new(2), context) }), ) - .build(); + .build(); let mut data = Vec::new(); for i in 1..=5 { @@ -107,7 +107,7 @@ fn main() -> JsResult<()> { accumulator.add(&value, context) }), ) - .build(); + .build(); assert_eq!( chained_array.reduce(reduce_callback, Some(JsValue::ZERO), context)?, diff --git a/examples/src/bin/jstypedarray.rs b/examples/src/bin/jstypedarray.rs index 9b6a7b5109c..98508260fc2 100644 --- a/examples/src/bin/jstypedarray.rs +++ b/examples/src/bin/jstypedarray.rs @@ -38,7 +38,7 @@ fn main() -> JsResult<()> { accumulator.add(&value, context) }), ) - .build(); + .build(); assert_eq!( array.reduce(callback, Some(JsValue::ZERO), context)?, @@ -57,7 +57,7 @@ fn main() -> JsResult<()> { Ok(JsValue::from(element > 10.0)) }), ) - .build(); + .build(); assert_eq!( array.find_index(greater_than_10_predicate, None, context), @@ -76,7 +76,7 @@ fn main() -> JsResult<()> { Ok(JsValue::from(element < 200.0)) }), ) - .build(); + .build(); assert_eq!( array.find_last(lower_than_200_predicate.clone(), None, context), @@ -112,7 +112,7 @@ fn main() -> JsResult<()> { Gc::clone(&num_to_modify), ), ) - .build(); + .build(); let _unused = array.for_each(js_function, None, context); diff --git a/examples/src/bin/modulehandler.rs b/examples/src/bin/modulehandler.rs index fff1eb664fd..cca53b5efa8 100644 --- a/examples/src/bin/modulehandler.rs +++ b/examples/src/bin/modulehandler.rs @@ -32,12 +32,7 @@ fn main() -> Result<(), Box> { // Adding custom object that mimics 'module.exports' let moduleobj = JsObject::default(); - moduleobj.set( - js_string!("exports"), - js_string!(" "), - false, - &mut ctx, - )?; + moduleobj.set(js_string!("exports"), js_string!(" "), false, &mut ctx)?; ctx.register_global_property( js_string!("module"), diff --git a/examples/src/bin/synthetic.rs b/examples/src/bin/synthetic.rs index ccf5f81b8be..4d1319400fa 100644 --- a/examples/src/bin/synthetic.rs +++ b/examples/src/bin/synthetic.rs @@ -110,36 +110,36 @@ fn create_operations_module(context: &mut Context) -> Module { args.get_or_undefined(0).add(args.get_or_undefined(1), ctx) }), ) - .length(2) - .name("sum") - .build(); + .length(2) + .name("sum") + .build(); let sub = FunctionObjectBuilder::new( context.realm(), NativeFunction::from_fn_ptr(|_, args, ctx| { args.get_or_undefined(0).sub(args.get_or_undefined(1), ctx) }), ) - .length(2) - .name("sub") - .build(); + .length(2) + .name("sub") + .build(); let mult = FunctionObjectBuilder::new( context.realm(), NativeFunction::from_fn_ptr(|_, args, ctx| { args.get_or_undefined(0).mul(args.get_or_undefined(1), ctx) }), ) - .length(2) - .name("mult") - .build(); + .length(2) + .name("mult") + .build(); let div = FunctionObjectBuilder::new( context.realm(), NativeFunction::from_fn_ptr(|_, args, ctx| { args.get_or_undefined(0).div(args.get_or_undefined(1), ctx) }), ) - .length(2) - .name("div") - .build(); + .length(2) + .name("div") + .build(); let sqrt = FunctionObjectBuilder::new( context.realm(), NativeFunction::from_fn_ptr(|_, args, ctx| { @@ -147,9 +147,9 @@ fn create_operations_module(context: &mut Context) -> Module { Ok(JsValue::from(a.sqrt())) }), ) - .length(1) - .name("sqrt") - .build(); + .length(1) + .name("sqrt") + .build(); Module::synthetic( // Make sure to list all exports beforehand. From d18f19a2b6abe8d6411d0d23f1157323f2bfa49a Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Mon, 9 Dec 2024 20:06:26 -0800 Subject: [PATCH 06/19] WIP --- core/engine/src/value/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index 8d463ae81d5..19629068800 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -518,7 +518,7 @@ impl JsValue { PreferredType::String => js_string!("string"), PreferredType::Number => js_string!("number"), } - .into(); + .into(); // iv. Let result be ? Call(exoticToPrim, input, « hint »). let result = exotic_to_prim.call(self, &[hint], context)?; From a5a8643b655d731531b324579ae03cc03ce4de66 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Mon, 9 Dec 2024 20:08:17 -0800 Subject: [PATCH 07/19] WIP --- core/engine/src/builtins/number/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/engine/src/builtins/number/mod.rs b/core/engine/src/builtins/number/mod.rs index 564c499ac61..9cf22e569e3 100644 --- a/core/engine/src/builtins/number/mod.rs +++ b/core/engine/src/builtins/number/mod.rs @@ -239,9 +239,9 @@ impl Number { None => f64_to_exponential(this_num), Some(IntegerOrInfinity::Integer(precision)) if (0..=100).contains(&precision) => // 5. If f < 0 or f > 100, throw a RangeError exception. - { - f64_to_exponential_with_precision(this_num, precision as usize) - } + { + f64_to_exponential_with_precision(this_num, precision as usize) + } _ => { return Err(JsNativeError::range() .with_message("toExponential() argument must be between 0 and 100") From c887216e40505d4779ab1c5ec19056e2a6e6314c Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Mon, 9 Dec 2024 20:12:54 -0800 Subject: [PATCH 08/19] WIP --- core/engine/src/value/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index 19629068800..58f104a39ee 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -518,7 +518,7 @@ impl JsValue { PreferredType::String => js_string!("string"), PreferredType::Number => js_string!("number"), } - .into(); + .into(); // iv. Let result be ? Call(exoticToPrim, input, « hint »). let result = exotic_to_prim.call(self, &[hint], context)?; @@ -620,7 +620,7 @@ impl JsValue { InnerValue::Undefined => Ok(js_string!("undefined")), InnerValue::Boolean(true) => Ok(js_string!("true")), InnerValue::Boolean(false) => Ok(js_string!("false")), - InnerValue::Float64(rational) => Ok(Number::to_js_string(rational)), + InnerValue::Float64(rational) => Ok(Number::to_js_string_radix(rational, 10)), InnerValue::Integer32(integer) => Ok(integer.to_string().into()), InnerValue::String(ref string) => Ok(string.clone()), InnerValue::Symbol(_) => Err(JsNativeError::typ() @@ -717,7 +717,7 @@ impl JsValue { Ok(primitive.to_number(context)?.into()) } - /// Converts a value to an integral 32 bit unsigned integer. + /// Converts a value to an integral 32-bit unsigned integer. /// /// This function is equivalent to `value | 0` in JavaScript /// @@ -734,7 +734,7 @@ impl JsValue { Ok(f64_to_uint32(number)) } - /// Converts a value to an integral 32 bit signed integer. + /// Converts a value to an integral 32-bit signed integer. /// /// See: pub fn to_i32(&self, context: &mut Context) -> JsResult { From 34d823072c68745385a613d47134ebf788b2a8e0 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Mon, 9 Dec 2024 22:17:51 -0800 Subject: [PATCH 09/19] boa_engine done --- core/engine/src/builtins/intl/mod.rs | 2 +- .../src/builtins/intl/number_format/mod.rs | 4 +- core/engine/src/builtins/options.rs | 7 +-- .../src/builtins/temporal/duration/mod.rs | 44 +++++++++---------- core/engine/src/builtins/temporal/mod.rs | 9 ++-- .../src/object/builtins/jstypedarray.rs | 4 +- core/engine/src/value/conversions/mod.rs | 12 +++-- core/engine/src/value/mod.rs | 4 +- 8 files changed, 46 insertions(+), 40 deletions(-) diff --git a/core/engine/src/builtins/intl/mod.rs b/core/engine/src/builtins/intl/mod.rs index c7128618577..ae269ca52e6 100644 --- a/core/engine/src/builtins/intl/mod.rs +++ b/core/engine/src/builtins/intl/mod.rs @@ -174,7 +174,7 @@ impl Intl { let ll = locale::canonicalize_locale_list(locales, context)?; // 2. Return CreateArrayFromList(ll). - Ok(JsValue::Object(Array::create_array_from_list( + Ok(JsValue::new(Array::create_array_from_list( ll.into_iter().map(|loc| js_string!(loc.to_string()).into()), context, ))) diff --git a/core/engine/src/builtins/intl/number_format/mod.rs b/core/engine/src/builtins/intl/number_format/mod.rs index 0bbd3439eb3..f2a75cbe25d 100644 --- a/core/engine/src/builtins/intl/number_format/mod.rs +++ b/core/engine/src/builtins/intl/number_format/mod.rs @@ -750,8 +750,8 @@ fn unwrap_number_format(nf: &JsValue, context: &mut Context) -> JsResult() { + if let Some(nf) = nf.as_object() { + if let Ok(nf) = nf.clone().downcast::() { return Ok(nf); } } diff --git a/core/engine/src/builtins/options.rs b/core/engine/src/builtins/options.rs index db5c84b4ec9..cf580919337 100644 --- a/core/engine/src/builtins/options.rs +++ b/core/engine/src/builtins/options.rs @@ -2,6 +2,7 @@ use std::{fmt, str::FromStr}; +use crate::value::JsVariant; use crate::{object::JsObject, Context, JsNativeError, JsResult, JsString, JsValue}; /// A type used as an option parameter for [`get_option`]. @@ -74,14 +75,14 @@ pub(crate) fn get_option( /// /// [spec]: https://tc39.es/ecma402/#sec-getoptionsobject pub(crate) fn get_options_object(options: &JsValue) -> JsResult { - match options { + match options.variant() { // If options is undefined, then - JsValue::Undefined => { + JsVariant::Undefined => { // a. Return OrdinaryObjectCreate(null). Ok(JsObject::with_null_proto()) } // 2. If Type(options) is Object, then - JsValue::Object(obj) => { + JsVariant::Object(obj) => { // a. Return options. Ok(obj.clone()) } diff --git a/core/engine/src/builtins/temporal/duration/mod.rs b/core/engine/src/builtins/temporal/duration/mod.rs index 379e4f33100..426b0365be9 100644 --- a/core/engine/src/builtins/temporal/duration/mod.rs +++ b/core/engine/src/builtins/temporal/duration/mod.rs @@ -1,5 +1,10 @@ // Boa's implementation of the `Temporal.Duration` Builtin Object. +use super::{ + options::{get_temporal_unit, TemporalUnitGroup}, + to_integer_if_integral, DateTimeValues, +}; +use crate::value::JsVariant; use crate::{ builtins::{ options::{get_option, get_options_object}, @@ -22,11 +27,6 @@ use temporal_rs::{ Duration as InnerDuration, }; -use super::{ - options::{get_temporal_unit, TemporalUnitGroup}, - to_integer_if_integral, DateTimeValues, -}; - #[cfg(test)] mod tests; @@ -313,16 +313,16 @@ impl Duration { let inner = &duration.inner; match field { - DateTimeValues::Year => Ok(JsValue::Rational(inner.years().as_inner())), - DateTimeValues::Month => Ok(JsValue::Rational(inner.months().as_inner())), - DateTimeValues::Week => Ok(JsValue::Rational(inner.weeks().as_inner())), - DateTimeValues::Day => Ok(JsValue::Rational(inner.days().as_inner())), - DateTimeValues::Hour => Ok(JsValue::Rational(inner.hours().as_inner())), - DateTimeValues::Minute => Ok(JsValue::Rational(inner.minutes().as_inner())), - DateTimeValues::Second => Ok(JsValue::Rational(inner.seconds().as_inner())), - DateTimeValues::Millisecond => Ok(JsValue::Rational(inner.milliseconds().as_inner())), - DateTimeValues::Microsecond => Ok(JsValue::Rational(inner.microseconds().as_inner())), - DateTimeValues::Nanosecond => Ok(JsValue::Rational(inner.nanoseconds().as_inner())), + DateTimeValues::Year => Ok(JsValue::new(inner.years().as_inner())), + DateTimeValues::Month => Ok(JsValue::new(inner.months().as_inner())), + DateTimeValues::Week => Ok(JsValue::new(inner.weeks().as_inner())), + DateTimeValues::Day => Ok(JsValue::new(inner.days().as_inner())), + DateTimeValues::Hour => Ok(JsValue::new(inner.hours().as_inner())), + DateTimeValues::Minute => Ok(JsValue::new(inner.minutes().as_inner())), + DateTimeValues::Second => Ok(JsValue::new(inner.seconds().as_inner())), + DateTimeValues::Millisecond => Ok(JsValue::new(inner.milliseconds().as_inner())), + DateTimeValues::Microsecond => Ok(JsValue::new(inner.microseconds().as_inner())), + DateTimeValues::Nanosecond => Ok(JsValue::new(inner.nanoseconds().as_inner())), DateTimeValues::MonthCode => unreachable!( "Any other DateTimeValue fields on Duration would be an implementation error." ), @@ -644,15 +644,15 @@ impl Duration { JsNativeError::typ().with_message("this value must be a Duration object.") })?; - let round_to = match args.first() { + let round_to = match args.first().map(JsValue::variant) { // 3. If roundTo is undefined, then - None | Some(JsValue::Undefined) => { + None | Some(JsVariant::Undefined) => { return Err(JsNativeError::typ() .with_message("roundTo cannot be undefined.") .into()) } // 4. If Type(roundTo) is String, then - Some(JsValue::String(rt)) => { + Some(JsVariant::String(rt)) => { // a. Let paramString be roundTo. let param_string = rt.clone(); // b. Set roundTo to OrdinaryObjectCreate(null). @@ -668,7 +668,7 @@ impl Duration { // 5. Else, Some(round_to) => { // a. Set roundTo to ? GetOptionsObject(roundTo). - get_options_object(round_to)? + get_options_object(round_to.into())? } }; @@ -834,9 +834,9 @@ pub(crate) fn to_temporal_duration_record( context: &mut Context, ) -> JsResult { // 1. If Type(temporalDurationLike) is not Object, then - let JsValue::Object(duration_obj) = temporal_duration_like else { + let Some(duration_obj) = temporal_duration_like.as_object() else { // a. If temporalDurationLike is not a String, throw a TypeError exception. - let JsValue::String(duration_string) = temporal_duration_like else { + let Some(duration_string) = temporal_duration_like.as_string() else { return Err(JsNativeError::typ() .with_message("Invalid TemporalDurationLike value.") .into()); @@ -923,7 +923,7 @@ pub(crate) fn to_temporal_partial_duration( context: &mut Context, ) -> JsResult { // 1. If Type(temporalDurationLike) is not Object, then - let JsValue::Object(unknown_object) = duration_like else { + let Some(unknown_object) = duration_like.as_object() else { // a. Throw a TypeError exception. return Err(JsNativeError::typ() .with_message("temporalDurationLike must be an object.") diff --git a/core/engine/src/builtins/temporal/mod.rs b/core/engine/src/builtins/temporal/mod.rs index 3dcef45942f..a6c79bea56f 100644 --- a/core/engine/src/builtins/temporal/mod.rs +++ b/core/engine/src/builtins/temporal/mod.rs @@ -26,6 +26,7 @@ pub use self::{ plain_time::*, plain_year_month::*, zoned_date_time::*, }; +use crate::value::JsVariant; use crate::{ builtins::{iterable::IteratorRecord, BuiltInBuilder, BuiltInObject, IntrinsicObject}, context::intrinsics::Intrinsics, @@ -243,10 +244,10 @@ pub(crate) fn to_relative_temporal_object( context: &mut Context, ) -> RelativeTemporalObjectResult { let relative_to = options.get(js_string!("relativeTo"), context)?; - let plain_date = match relative_to { - JsValue::String(relative_to_str) => JsValue::from(relative_to_str), - JsValue::Object(relative_to_obj) => JsValue::from(relative_to_obj), - JsValue::Undefined => return Ok((None, None)), + let plain_date = match relative_to.variant() { + JsVariant::String(relative_to_str) => JsValue::from(relative_to_str.clone()), + JsVariant::Object(relative_to_obj) => JsValue::from(relative_to_obj.clone()), + JsVariant::Undefined => return Ok((None, None)), _ => { return Err(JsNativeError::typ() .with_message("Invalid type for converting to relativeTo object") diff --git a/core/engine/src/object/builtins/jstypedarray.rs b/core/engine/src/object/builtins/jstypedarray.rs index 1dff825dae3..e69faac5315 100644 --- a/core/engine/src/object/builtins/jstypedarray.rs +++ b/core/engine/src/object/builtins/jstypedarray.rs @@ -620,7 +620,7 @@ impl JsTypedArray { /// .build(); /// assert_eq!( /// array.find_last(lower_than_200_predicate.clone(), None, context), - /// Ok(JsValue::Integer(199)) + /// Ok(JsValue::new(199)) /// ); /// /// # Ok(()) @@ -673,7 +673,7 @@ impl JsTypedArray { /// .to_uint8(inner_context) /// .expect("error at number conversion"); /// *captures.borrow_mut() += element; - /// Ok(JsValue::Undefined) + /// Ok(JsValue::UNDEFINED) /// }, /// Gc::clone(&num_to_modify), /// ), diff --git a/core/engine/src/value/conversions/mod.rs b/core/engine/src/value/conversions/mod.rs index 606e99bae5c..7cbedb359ba 100644 --- a/core/engine/src/value/conversions/mod.rs +++ b/core/engine/src/value/conversions/mod.rs @@ -59,10 +59,10 @@ macro_rules! impl_from_float { fn from(value: $type_) -> Self { let _timer = Profiler::global().start_event(concat!("From<", stringify!($type_), ">"), "value"); - if value.fract() == 0.0 && value <= i32::MAX as $type_ && value >= i32::MIN as $type_ { - Self::from(value as i32) + if value != -0.0 && value.fract() == 0.0 && value <= i32::MAX as $type_ && value >= i32::MIN as $type_ { + Self::from_inner(InnerValue::Integer32(value as i32)) } else { - Self::from(value as f64) + Self::from_inner(InnerValue::Float64(value as f64)) } } } @@ -78,7 +78,11 @@ macro_rules! impl_from_integer { fn from(value: $type_) -> Self { let _timer = Profiler::global().start_event(concat!("From<", stringify!($type_), ">"), "value"); - i32::try_from(value).map_or_else(|_| Self::from(value as f64), Self::from) + i32::try_from(value) + .map_or_else( + |_| Self::from(value as f64), + |value| Self::from_inner(InnerValue::Integer32(value)), + ) } } )* diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index 58f104a39ee..d0e1c6cd5d0 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -483,7 +483,7 @@ impl JsValue { match self.inner { InnerValue::Symbol(_) | InnerValue::Object(_) => true, InnerValue::String(ref s) if !s.is_empty() => true, - InnerValue::Float64(n) if !n.is_nan() => true, + InnerValue::Float64(n) if n != 0.0 && !n.is_nan() => true, InnerValue::Integer32(n) if n != 0 => true, InnerValue::BigInt(ref n) if !n.is_zero() => true, InnerValue::Boolean(v) => v, @@ -518,7 +518,7 @@ impl JsValue { PreferredType::String => js_string!("string"), PreferredType::Number => js_string!("number"), } - .into(); + .into(); // iv. Let result be ? Call(exoticToPrim, input, « hint »). let result = exotic_to_prim.call(self, &[hint], context)?; From ac487ac5bbb3fba132e655ed8d611e398449daf3 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Tue, 10 Dec 2024 11:13:48 -0800 Subject: [PATCH 10/19] all crates done --- .../src/builtins/intl/number_format/mod.rs | 22 ++++++++-------- .../src/builtins/temporal/calendar/mod.rs | 2 +- .../src/builtins/temporal/duration/mod.rs | 9 ++++--- .../src/builtins/temporal/instant/mod.rs | 13 +++++----- .../src/builtins/temporal/plain_date/mod.rs | 12 ++++----- .../builtins/temporal/plain_date_time/mod.rs | 20 +++++++------- .../src/builtins/temporal/plain_time/mod.rs | 26 +++++++++---------- core/engine/src/value/conversions/either.rs | 2 +- core/runtime/src/console/mod.rs | 2 +- examples/src/bin/derive.rs | 2 +- examples/src/bin/jstypedarray.rs | 2 +- examples/src/bin/try_into_js_derive.rs | 4 +-- tests/macros/tests/derive/from_js_with.rs | 8 +++--- 13 files changed, 62 insertions(+), 62 deletions(-) diff --git a/core/engine/src/builtins/intl/number_format/mod.rs b/core/engine/src/builtins/intl/number_format/mod.rs index f2a75cbe25d..93c5d450b85 100644 --- a/core/engine/src/builtins/intl/number_format/mod.rs +++ b/core/engine/src/builtins/intl/number_format/mod.rs @@ -19,6 +19,12 @@ use num_bigint::BigInt; use num_traits::Num; pub(crate) use options::*; +use super::{ + locale::{canonicalize_locale_list, filter_locales, resolve_locale, validate_extension}, + options::{coerce_options_to_object, IntlOptions}, + Service, +}; +use crate::value::JsVariant; use crate::{ builtins::{ builder::BuiltInBuilder, options::get_option, string::is_trimmable_whitespace, @@ -41,12 +47,6 @@ use crate::{ NativeFunction, }; -use super::{ - locale::{canonicalize_locale_list, filter_locales, resolve_locale, validate_extension}, - options::{coerce_options_to_object, IntlOptions}, - Service, -}; - #[cfg(test)] mod tests; @@ -771,16 +771,16 @@ fn to_intl_mathematical_value(value: &JsValue, context: &mut Context) -> JsResul // TODO: Add support in `FixedDecimal` for infinity and NaN, which // should remove the returned errors. - match prim_value { + match prim_value.variant() { // 2. If Type(primValue) is BigInt, return ℝ(primValue). - JsValue::BigInt(bi) => { + JsVariant::BigInt(bi) => { let bi = bi.to_string(); FixedDecimal::try_from(bi.as_bytes()) .map_err(|err| JsNativeError::range().with_message(err.to_string()).into()) } // 3. If Type(primValue) is String, then // a. Let str be primValue. - JsValue::String(s) => { + JsVariant::String(s) => { // 5. Let text be StringToCodePoints(str). // 6. Let literal be ParseText(text, StringNumericLiteral). // 7. If literal is a List of errors, return not-a-number. @@ -798,11 +798,11 @@ fn to_intl_mathematical_value(value: &JsValue, context: &mut Context) -> JsResul }) } // 4. Else, - other => { + _ => { // a. Let x be ? ToNumber(primValue). // b. If x is -0𝔽, return negative-zero. // c. Let str be Number::toString(x, 10). - let x = other.to_number(context)?; + let x = prim_value.to_number(context)?; FixedDecimal::try_from_f64(x, FloatPrecision::Floating) .map_err(|err| JsNativeError::range().with_message(err.to_string()).into()) diff --git a/core/engine/src/builtins/temporal/calendar/mod.rs b/core/engine/src/builtins/temporal/calendar/mod.rs index 04ff76f1310..a7117fc104d 100644 --- a/core/engine/src/builtins/temporal/calendar/mod.rs +++ b/core/engine/src/builtins/temporal/calendar/mod.rs @@ -62,7 +62,7 @@ pub(crate) fn to_temporal_calendar_slot_value(calendar_like: &JsValue) -> JsResu } // 3. If temporalCalendarLike is not a String, throw a TypeError exception. - let JsValue::String(calendar_id) = calendar_like else { + let Some(calendar_id) = calendar_like.as_string() else { return Err(JsNativeError::typ() .with_message("temporalCalendarLike is not a string.") .into()); diff --git a/core/engine/src/builtins/temporal/duration/mod.rs b/core/engine/src/builtins/temporal/duration/mod.rs index 426b0365be9..60f6ed683b6 100644 --- a/core/engine/src/builtins/temporal/duration/mod.rs +++ b/core/engine/src/builtins/temporal/duration/mod.rs @@ -667,8 +667,9 @@ impl Duration { } // 5. Else, Some(round_to) => { + // TODO: remove this clone. // a. Set roundTo to ? GetOptionsObject(roundTo). - get_options_object(round_to.into())? + get_options_object(&JsValue::from(round_to))? } }; @@ -740,15 +741,15 @@ impl Duration { let total_of = args.get_or_undefined(0); - let total_of = match total_of { + let total_of = match total_of.variant() { // 3. If totalOf is undefined, throw a TypeError exception. - JsValue::Undefined => { + JsVariant::Undefined => { return Err(JsNativeError::typ() .with_message("totalOf cannot be undefined.") .into()); } // 4. If Type(totalOf) is String, then - JsValue::String(param_string) => { + JsVariant::String(param_string) => { // a. Let paramString be totalOf. // b. Set totalOf to OrdinaryObjectCreate(null). let total_of = JsObject::with_null_proto(); diff --git a/core/engine/src/builtins/temporal/instant/mod.rs b/core/engine/src/builtins/temporal/instant/mod.rs index 6e8d3b22d54..25ffe9d147d 100644 --- a/core/engine/src/builtins/temporal/instant/mod.rs +++ b/core/engine/src/builtins/temporal/instant/mod.rs @@ -1,5 +1,7 @@ //! Boa's implementation of ECMAScript's `Temporal.Instant` builtin object. +use super::options::get_difference_settings; +use crate::value::JsVariant; use crate::{ builtins::{ options::{get_option, get_options_object}, @@ -28,8 +30,6 @@ use temporal_rs::{ Instant as InnerInstant, }; -use super::options::get_difference_settings; - /// The `Temporal.Instant` object. #[derive(Debug, Clone, Trace, Finalize, JsData)] // SAFETY: Instant does not contain any traceable values. @@ -409,15 +409,15 @@ impl Instant { JsNativeError::typ().with_message("the this object must be an instant object.") })?; - let round_to = match args.first() { + let round_to = match args.first().map(JsValue::variant) { // 3. If roundTo is undefined, then - None | Some(JsValue::Undefined) => { + None | Some(JsVariant::Undefined) => { return Err(JsNativeError::typ() .with_message("roundTo cannot be undefined.") .into()) } // 4. If Type(roundTo) is String, then - Some(JsValue::String(rt)) => { + Some(JsVariant::String(rt)) => { // a. Let paramString be roundTo. let param_string = rt.clone(); // b. Set roundTo to OrdinaryObjectCreate(null). @@ -432,8 +432,9 @@ impl Instant { } // 5. Else, Some(round_to) => { + // TODO: remove this clone. // a. Set roundTo to ? GetOptionsObject(roundTo). - get_options_object(round_to)? + get_options_object(&JsValue::from(round_to))? } }; diff --git a/core/engine/src/builtins/temporal/plain_date/mod.rs b/core/engine/src/builtins/temporal/plain_date/mod.rs index c3f2b43789b..e98d8fb7bde 100644 --- a/core/engine/src/builtins/temporal/plain_date/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date/mod.rs @@ -907,7 +907,7 @@ pub(crate) fn to_temporal_date( } // 5. If item is not a String, throw a TypeError exception. - let JsValue::String(date_like_string) = item else { + let Some(date_like_string) = item.as_string() else { return Err(JsNativeError::typ() .with_message("ToTemporalDate item must be an object or string.") .into()); @@ -944,9 +944,8 @@ pub(crate) fn to_partial_date_record( let month_code = partial_object .get(js_string!("monthCode"), context)? .map(|v| { - let JsValue::String(month_code) = - v.to_primitive(context, crate::value::PreferredType::String)? - else { + let v = v.to_primitive(context, crate::value::PreferredType::String)?; + let Some(month_code) = v.as_string() else { return Err(JsNativeError::typ() .with_message("The monthCode field value must be a string.") .into()); @@ -966,9 +965,8 @@ pub(crate) fn to_partial_date_record( let era = partial_object .get(js_string!("era"), context)? .map(|v| { - let JsValue::String(era) = - v.to_primitive(context, crate::value::PreferredType::String)? - else { + let v = v.to_primitive(context, crate::value::PreferredType::String)?; + let Some(era) = v.as_string() else { return Err(JsError::from( JsNativeError::typ() .with_message("The monthCode field value must be a string."), diff --git a/core/engine/src/builtins/temporal/plain_date_time/mod.rs b/core/engine/src/builtins/temporal/plain_date_time/mod.rs index 18cb14c3e56..04e4f37cca9 100644 --- a/core/engine/src/builtins/temporal/plain_date_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date_time/mod.rs @@ -22,18 +22,18 @@ use boa_profiler::Profiler; #[cfg(test)] mod tests; -use temporal_rs::{ - options::{ArithmeticOverflow, RoundingIncrement, RoundingOptions, TemporalRoundingMode}, - partial::PartialDateTime, - PlainDateTime as InnerDateTime, PlainTime, -}; - use super::{ calendar::{get_temporal_calendar_slot_value_with_default, to_temporal_calendar_slot_value}, create_temporal_duration, options::{get_difference_settings, get_temporal_unit, TemporalUnitGroup}, to_temporal_duration_record, to_temporal_time, PlainDate, ZonedDateTime, }; +use crate::value::JsVariant; +use temporal_rs::{ + options::{ArithmeticOverflow, RoundingIncrement, RoundingOptions, TemporalRoundingMode}, + partial::PartialDateTime, + PlainDateTime as InnerDateTime, PlainTime, +}; /// The `Temporal.PlainDateTime` object. #[derive(Debug, Clone, Trace, Finalize, JsData)] @@ -825,15 +825,15 @@ impl PlainDateTime { JsNativeError::typ().with_message("the this object must be a PlainTime object.") })?; - let round_to = match args.first() { + let round_to = match args.first().map(JsValue::variant) { // 3. If roundTo is undefined, then - None | Some(JsValue::Undefined) => { + None | Some(JsVariant::Undefined) => { return Err(JsNativeError::typ() .with_message("roundTo cannot be undefined.") .into()) } // 4. If Type(roundTo) is String, then - Some(JsValue::String(rt)) => { + Some(JsVariant::String(rt)) => { // a. Let paramString be roundTo. let param_string = rt.clone(); // b. Set roundTo to OrdinaryObjectCreate(null). @@ -849,7 +849,7 @@ impl PlainDateTime { // 5. Else, Some(round_to) => { // a. Set roundTo to ? GetOptionsObject(roundTo). - get_options_object(round_to)? + get_options_object(&JsValue::from(round_to))? } }; diff --git a/core/engine/src/builtins/temporal/plain_time/mod.rs b/core/engine/src/builtins/temporal/plain_time/mod.rs index ffa97d54531..b7dd15c6645 100644 --- a/core/engine/src/builtins/temporal/plain_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_time/mod.rs @@ -1,5 +1,11 @@ //! Boa's implementation of the ECMAScript `Temporal.PlainTime` builtin object. +use super::{ + create_temporal_duration, + options::{get_difference_settings, get_temporal_unit, TemporalUnitGroup}, + to_integer_with_truncation, to_temporal_duration_record, PlainDateTime, ZonedDateTime, +}; +use crate::value::JsVariant; use crate::{ builtins::{ options::{get_option, get_options_object}, @@ -21,12 +27,6 @@ use temporal_rs::{ PlainTime as PlainTimeInner, }; -use super::{ - create_temporal_duration, - options::{get_difference_settings, get_temporal_unit, TemporalUnitGroup}, - to_integer_with_truncation, to_temporal_duration_record, PlainDateTime, ZonedDateTime, -}; - /// The `Temporal.PlainTime` object. #[derive(Debug, Clone, Copy, Trace, Finalize, JsData)] // Safety: Time does not contain any traceable types. @@ -446,15 +446,15 @@ impl PlainTime { JsNativeError::typ().with_message("the this object must be a PlainTime object.") })?; - let round_to = match args.first() { + let round_to = match args.first().map(JsValue::variant) { // 3. If roundTo is undefined, then - None | Some(JsValue::Undefined) => { + None | Some(JsVariant::Undefined) => { return Err(JsNativeError::typ() .with_message("roundTo cannot be undefined.") .into()) } // 4. If Type(roundTo) is String, then - Some(JsValue::String(rt)) => { + Some(JsVariant::String(rt)) => { // a. Let paramString be roundTo. let param_string = rt.clone(); // b. Set roundTo to OrdinaryObjectCreate(null). @@ -470,7 +470,7 @@ impl PlainTime { // 5. Else, Some(round_to) => { // a. Set roundTo to ? GetOptionsObject(roundTo). - get_options_object(round_to)? + get_options_object(&JsValue::from(round_to))? } }; @@ -636,8 +636,8 @@ pub(crate) fn to_temporal_time( // 1.If overflow is not present, set overflow to "constrain". let resolved_overflow = overflow.unwrap_or(ArithmeticOverflow::Constrain); // 2. If item is an Object, then - match value { - JsValue::Object(object) => { + match value.variant() { + JsVariant::Object(object) => { // a. If item has an [[InitializedTemporalTime]] internal slot, then if let Some(time) = object.downcast_ref::() { // i. Return item. @@ -678,7 +678,7 @@ pub(crate) fn to_temporal_time( .map_err(Into::into) } // 3. Else, - JsValue::String(str) => { + JsVariant::String(str) => { // b. Let result be ? ParseTemporalTimeString(item). // c. Assert: IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true. str.to_std_string_escaped() diff --git a/core/engine/src/value/conversions/either.rs b/core/engine/src/value/conversions/either.rs index fa8eebe99fb..b64ed5018aa 100644 --- a/core/engine/src/value/conversions/either.rs +++ b/core/engine/src/value/conversions/either.rs @@ -25,7 +25,7 @@ where #[test] fn either() { - let v = JsValue::Integer(123); + let v = JsValue::new(123); let mut context = Context::default(); assert_eq!( diff --git a/core/runtime/src/console/mod.rs b/core/runtime/src/console/mod.rs index 9fecd4afc58..106be79ffb2 100644 --- a/core/runtime/src/console/mod.rs +++ b/core/runtime/src/console/mod.rs @@ -836,7 +836,7 @@ impl Console { logger: &impl Logger, context: &mut Context, ) -> JsResult { - Console::group(&JsValue::Undefined, args, console, logger, context) + Console::group(&JsValue::UNDEFINED, args, console, logger, context) } /// `console.groupEnd(label)` diff --git a/examples/src/bin/derive.rs b/examples/src/bin/derive.rs index 515e56f0ec4..fb9590e6999 100644 --- a/examples/src/bin/derive.rs +++ b/examples/src/bin/derive.rs @@ -40,7 +40,7 @@ fn main() { fn lossy_conversion(value: &JsValue, _context: &mut Context) -> JsResult { match value.variant() { JsVariant::Float64(r) => Ok(r.round() as i16), - JsVariant::Integer32(i) => Ok(*i as i16), + JsVariant::Integer32(i) => Ok(i as i16), _ => Err(JsNativeError::typ() .with_message("cannot convert value to an i16") .into()), diff --git a/examples/src/bin/jstypedarray.rs b/examples/src/bin/jstypedarray.rs index 98508260fc2..c0b3e21262a 100644 --- a/examples/src/bin/jstypedarray.rs +++ b/examples/src/bin/jstypedarray.rs @@ -143,7 +143,7 @@ fn main() -> JsResult<()> { assert_eq!(initialized8_array.get(5, context)?, JsValue::ZERO); assert_eq!(initialized8_array.get(6, context)?, JsValue::ZERO); assert_eq!(initialized8_array.get(7, context)?, JsValue::ZERO); - assert_eq!(initialized8_array.get(8, context)?, JsValue::Undefined); + assert_eq!(initialized8_array.get(8, context)?, JsValue::UNDEFINED); // subarray let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?; diff --git a/examples/src/bin/try_into_js_derive.rs b/examples/src/bin/try_into_js_derive.rs index ea9c00b43b4..736367e0400 100644 --- a/examples/src/bin/try_into_js_derive.rs +++ b/examples/src/bin/try_into_js_derive.rs @@ -69,7 +69,7 @@ fn main() -> JsResult<()> { }; let result = point_shift.call( - &JsValue::Undefined, + &JsValue::UNDEFINED, &[a.try_into_js(context)?, b.try_into_js(context)?], context, )?; @@ -82,7 +82,7 @@ fn main() -> JsResult<()> { assert_eq!(verifier, expect); let result = point_shift.call( - &JsValue::Undefined, + &JsValue::UNDEFINED, &[a.try_into_js(context)?, c.try_into_js(context)?], context, )?; diff --git a/tests/macros/tests/derive/from_js_with.rs b/tests/macros/tests/derive/from_js_with.rs index e7137e7e92e..fd5181475e8 100644 --- a/tests/macros/tests/derive/from_js_with.rs +++ b/tests/macros/tests/derive/from_js_with.rs @@ -1,6 +1,6 @@ #![allow(unused)] -use boa_engine::{value::TryFromJs, Context, JsNativeError, JsResult, JsValue}; +use boa_engine::{value::TryFromJs, Context, JsNativeError, JsResult, JsValue, JsVariant}; #[derive(TryFromJs)] struct TestStruct { @@ -12,9 +12,9 @@ struct TestStruct { fn main() {} fn lossy_float(value: &JsValue, _context: &mut Context) -> JsResult { - match value { - JsValue::Rational(r) => Ok(r.round() as i16), - JsValue::Integer(i) => Ok(*i as i16), + match value.variant() { + JsVariant::Float64(r) => Ok(r.round() as i16), + JsVariant::Integer32(i) => Ok(*i as i16), _ => Err(JsNativeError::typ() .with_message("cannot convert value to an i16") .into()), From 6ed5150e21ffe255d9ce33d182f11eb06f16caad Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Tue, 10 Dec 2024 11:49:16 -0800 Subject: [PATCH 11/19] clippies and fixes --- core/engine/src/builtins/intl/number_format/mod.rs | 2 +- core/engine/src/builtins/iterable/mod.rs | 4 ++-- core/engine/src/lib.rs | 2 +- core/engine/src/optimizer/pass/constant_folding.rs | 6 +++--- core/engine/src/value/conversions/mod.rs | 3 ++- core/engine/src/value/mod.rs | 11 +++++++++++ tests/macros/tests/derive/from_js_with.rs | 2 +- 7 files changed, 21 insertions(+), 9 deletions(-) diff --git a/core/engine/src/builtins/intl/number_format/mod.rs b/core/engine/src/builtins/intl/number_format/mod.rs index 93c5d450b85..d21cd08bd38 100644 --- a/core/engine/src/builtins/intl/number_format/mod.rs +++ b/core/engine/src/builtins/intl/number_format/mod.rs @@ -791,7 +791,7 @@ fn to_intl_mathematical_value(value: &JsValue, context: &mut Context) -> JsResul // c. If rounded is +∞𝔽, return positive-infinity. // d. If rounded is +0𝔽 and intlMV < 0, return negative-zero. // e. If rounded is +0𝔽, return 0. - js_string_to_fixed_decimal(&s).ok_or_else(|| { + js_string_to_fixed_decimal(s).ok_or_else(|| { JsNativeError::syntax() .with_message("could not parse the provided string") .into() diff --git a/core/engine/src/builtins/iterable/mod.rs b/core/engine/src/builtins/iterable/mod.rs index b591afcc351..ae06b53798d 100644 --- a/core/engine/src/builtins/iterable/mod.rs +++ b/core/engine/src/builtins/iterable/mod.rs @@ -314,8 +314,8 @@ impl IteratorResult { /// Gets a new `IteratorResult` from a value. Returns `Err` if /// the value is not a [`JsObject`] pub(crate) fn from_value(value: JsValue) -> JsResult { - if let Some(o) = value.as_object() { - Ok(Self { object: o.clone() }) + if let Some(object) = value.into_object() { + Ok(Self { object }) } else { Err(JsNativeError::typ() .with_message("next value should be an object") diff --git a/core/engine/src/lib.rs b/core/engine/src/lib.rs index b8fe1aad4bf..e94980d36c1 100644 --- a/core/engine/src/lib.rs +++ b/core/engine/src/lib.rs @@ -124,7 +124,7 @@ pub mod prelude { script::Script, string::{JsStr, JsString}, symbol::JsSymbol, - value::JsValue, + value::{JsValue, JsVariant}, }; pub use boa_gc::{Finalize, Trace}; pub use boa_macros::{js_str, JsData}; diff --git a/core/engine/src/optimizer/pass/constant_folding.rs b/core/engine/src/optimizer/pass/constant_folding.rs index a4fef11c889..8dd6e07d98b 100644 --- a/core/engine/src/optimizer/pass/constant_folding.rs +++ b/core/engine/src/optimizer/pass/constant_folding.rs @@ -28,7 +28,7 @@ fn literal_to_js_value(literal: &Literal, context: &mut Context) -> JsValue { } } -fn js_value_to_literal(value: JsValue, context: &mut Context) -> Literal { +fn js_value_to_literal(value: &JsValue, context: &mut Context) -> Literal { match value.variant() { JsVariant::Null => Literal::Null, JsVariant::Undefined => Literal::Undefined, @@ -102,7 +102,7 @@ impl ConstantFolding { return PassAction::Keep; }; - PassAction::Replace(Expression::Literal(js_value_to_literal(value, context))) + PassAction::Replace(Expression::Literal(js_value_to_literal(&value, context))) } fn constant_fold_binary_expr( @@ -229,6 +229,6 @@ impl ConstantFolding { return PassAction::Keep; }; - PassAction::Replace(Expression::Literal(js_value_to_literal(value, context))) + PassAction::Replace(Expression::Literal(js_value_to_literal(&value, context))) } } diff --git a/core/engine/src/value/conversions/mod.rs b/core/engine/src/value/conversions/mod.rs index 7cbedb359ba..e7d2ae69d03 100644 --- a/core/engine/src/value/conversions/mod.rs +++ b/core/engine/src/value/conversions/mod.rs @@ -62,7 +62,7 @@ macro_rules! impl_from_float { if value != -0.0 && value.fract() == 0.0 && value <= i32::MAX as $type_ && value >= i32::MIN as $type_ { Self::from_inner(InnerValue::Integer32(value as i32)) } else { - Self::from_inner(InnerValue::Float64(value as f64)) + Self::from_inner(InnerValue::Float64(f64::from(value))) } } } @@ -75,6 +75,7 @@ macro_rules! impl_from_integer { $( impl From<$type_> for JsValue { #[inline] + #[allow(clippy::cast_lossless)] fn from(value: $type_) -> Self { let _timer = Profiler::global().start_event(concat!("From<", stringify!($type_), ">"), "value"); diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index d0e1c6cd5d0..bf1a039408b 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -210,6 +210,17 @@ impl JsValue { } } + /// Consumes the value and return the inner object if it was an object. + #[inline] + #[must_use] + pub fn into_object(self) -> Option { + if let InnerValue::Object(ref obj) = self.inner { + Some(obj.clone()) + } else { + None + } + } + /// It determines if the value is a callable function with a `[[Call]]` internal method. /// /// More information: diff --git a/tests/macros/tests/derive/from_js_with.rs b/tests/macros/tests/derive/from_js_with.rs index fd5181475e8..d5d94026e79 100644 --- a/tests/macros/tests/derive/from_js_with.rs +++ b/tests/macros/tests/derive/from_js_with.rs @@ -14,7 +14,7 @@ fn main() {} fn lossy_float(value: &JsValue, _context: &mut Context) -> JsResult { match value.variant() { JsVariant::Float64(r) => Ok(r.round() as i16), - JsVariant::Integer32(i) => Ok(*i as i16), + JsVariant::Integer32(i) => Ok(i as i16), _ => Err(JsNativeError::typ() .with_message("cannot convert value to an i16") .into()), From 714427fc32fb99b1a141a8608fd48c6be7933df8 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Tue, 10 Dec 2024 12:06:07 -0800 Subject: [PATCH 12/19] Remove Ref<> for now --- core/engine/src/value/variant.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/engine/src/value/variant.rs b/core/engine/src/value/variant.rs index 5a88396c3bb..dec2827e264 100644 --- a/core/engine/src/value/variant.rs +++ b/core/engine/src/value/variant.rs @@ -6,10 +6,6 @@ use boa_string::JsString; /// A non-mutable variant of a `JsValue`. /// Represents either a primitive value ([`bool`], [`f64`], [`i32`]) or a reference /// to a heap allocated value ([`JsString`], [`JsSymbol`]). -/// -/// References to heap allocated values are represented by [`Ref`], since -/// more exotic implementations of [`JsValue`] such as nan-boxed ones cannot -/// effectively return references. #[derive(Debug)] pub enum JsVariant<'a> { /// `null` - A null value, for when a value doesn't exist. From ec899f67a036b61e1623cf8db8eb0ac15d454688 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Mon, 16 Dec 2024 14:39:44 -0800 Subject: [PATCH 13/19] Address comments --- cli/src/debug/gc.rs | 2 +- cli/src/debug/limits.rs | 6 +- cli/src/debug/optimizer.rs | 4 +- core/engine/src/builtins/array/mod.rs | 2 +- core/engine/src/builtins/array/tests.rs | 4 +- core/engine/src/builtins/array_buffer/mod.rs | 2 +- core/engine/src/builtins/date/mod.rs | 2 +- core/engine/src/builtins/function/mod.rs | 2 +- .../iterable/async_from_sync_iterator.rs | 2 +- core/engine/src/builtins/map/mod.rs | 8 +- core/engine/src/builtins/number/globals.rs | 2 +- core/engine/src/builtins/object/mod.rs | 8 +- core/engine/src/builtins/promise/mod.rs | 32 +++--- core/engine/src/builtins/proxy/mod.rs | 2 +- core/engine/src/builtins/reflect/mod.rs | 2 +- core/engine/src/builtins/regexp/mod.rs | 2 +- core/engine/src/builtins/set/mod.rs | 8 +- core/engine/src/builtins/string/mod.rs | 16 +-- core/engine/src/builtins/temporal/mod.rs | 8 +- .../src/builtins/typed_array/builtin.rs | 2 +- core/engine/src/lib.rs | 2 +- .../src/object/builtins/jsarraybuffer.rs | 2 +- core/engine/src/object/builtins/jsdate.rs | 58 +++++----- core/engine/src/object/builtins/jspromise.rs | 2 +- core/engine/src/object/builtins/jsset.rs | 6 +- .../src/object/builtins/jsset_iterator.rs | 2 +- .../src/object/builtins/jstypedarray.rs | 16 +-- core/engine/src/object/jsobject.rs | 2 +- core/engine/src/value/conversions/convert.rs | 2 +- core/engine/src/value/conversions/mod.rs | 4 +- .../src/value/conversions/serde_json.rs | 2 +- .../src/value/conversions/try_into_js.rs | 4 +- core/engine/src/value/display.rs | 2 +- core/engine/src/value/mod.rs | 103 ++++++------------ core/engine/src/vm/call_frame/mod.rs | 2 +- .../engine/src/vm/opcode/control_flow/jump.rs | 2 +- core/engine/src/vm/opcode/push/array.rs | 2 +- core/interop/src/lib.rs | 2 +- core/runtime/src/console/mod.rs | 34 +++--- examples/src/bin/jsarray.rs | 2 +- examples/src/bin/jsmap.rs | 2 +- examples/src/bin/jstypedarray.rs | 20 ++-- examples/src/bin/synthetic.rs | 2 +- examples/src/bin/try_into_js_derive.rs | 4 +- 44 files changed, 181 insertions(+), 214 deletions(-) diff --git a/cli/src/debug/gc.rs b/cli/src/debug/gc.rs index 94fd9ff0520..746fd899403 100644 --- a/cli/src/debug/gc.rs +++ b/cli/src/debug/gc.rs @@ -5,7 +5,7 @@ use boa_engine::{ /// Trigger garbage collection. fn collect(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { boa_gc::force_collect(); - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } pub(super) fn create_object(context: &mut Context) -> JsObject { diff --git a/cli/src/debug/limits.rs b/cli/src/debug/limits.rs index 93b5cb441dd..b73e7a45fa5 100644 --- a/cli/src/debug/limits.rs +++ b/cli/src/debug/limits.rs @@ -13,7 +13,7 @@ fn get_loop(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult JsResult { let value = args.get_or_undefined(0).to_length(context)?; context.runtime_limits_mut().set_loop_iteration_limit(value); - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } fn get_stack(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { @@ -29,7 +29,7 @@ fn set_stack(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult JsResult { @@ -45,7 +45,7 @@ fn set_recursion(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResu .into()); }; context.runtime_limits_mut().set_recursion_limit(value); - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } pub(super) fn create_object(context: &mut Context) -> JsObject { diff --git a/cli/src/debug/optimizer.rs b/cli/src/debug/optimizer.rs index d35f2f8d9c3..92793e93ba0 100644 --- a/cli/src/debug/optimizer.rs +++ b/cli/src/debug/optimizer.rs @@ -18,7 +18,7 @@ fn set_constant_folding(_: &JsValue, args: &[JsValue], context: &mut Context) -> let mut options = context.optimizer_options(); options.set(OptimizerOptions::CONSTANT_FOLDING, value); context.set_optimizer_options(options); - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } fn get_statistics(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { @@ -33,7 +33,7 @@ fn set_statistics(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsRes let mut options = context.optimizer_options(); options.set(OptimizerOptions::STATISTICS, value); context.set_optimizer_options(options); - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } pub(super) fn create_object(context: &mut Context) -> JsObject { diff --git a/core/engine/src/builtins/array/mod.rs b/core/engine/src/builtins/array/mod.rs index 5babbc24388..ebbc0c80f9c 100644 --- a/core/engine/src/builtins/array/mod.rs +++ b/core/engine/src/builtins/array/mod.rs @@ -3321,7 +3321,7 @@ fn compare_array_elements( let args = [x.clone(), y.clone()]; // a. Let v be ? ToNumber(? Call(comparefn, undefined, « x, y »)). let v = cmp - .call(&JsValue::UNDEFINED, &args, context)? + .call(&JsValue::undefined(), &args, context)? .to_number(context)?; // b. If v is NaN, return +0𝔽. // c. Return v. diff --git a/core/engine/src/builtins/array/tests.rs b/core/engine/src/builtins/array/tests.rs index 5bacad5545c..738bd93512f 100644 --- a/core/engine/src/builtins/array/tests.rs +++ b/core/engine/src/builtins/array/tests.rs @@ -875,7 +875,7 @@ fn array_spread_non_iterable() { fn get_relative_start() { #[track_caller] fn assert(context: &mut Context, arg: Option<&JsValue>, len: u64, expected: u64) { - const UNDEFINED: &JsValue = &JsValue::UNDEFINED; + const UNDEFINED: &JsValue = &JsValue::undefined(); let arg = arg.unwrap_or(UNDEFINED); assert_eq!( Array::get_relative_start(context, arg, len).unwrap(), @@ -903,7 +903,7 @@ fn get_relative_start() { fn get_relative_end() { #[track_caller] fn assert(context: &mut Context, arg: Option<&JsValue>, len: u64, expected: u64) { - const UNDEFINED: &JsValue = &JsValue::UNDEFINED; + const UNDEFINED: &JsValue = &JsValue::undefined(); let arg = arg.unwrap_or(UNDEFINED); assert_eq!( Array::get_relative_end(context, arg, len).unwrap(), diff --git a/core/engine/src/builtins/array_buffer/mod.rs b/core/engine/src/builtins/array_buffer/mod.rs index 3ac8f5ef7d7..49f2ea4ec9b 100644 --- a/core/engine/src/builtins/array_buffer/mod.rs +++ b/core/engine/src/builtins/array_buffer/mod.rs @@ -880,7 +880,7 @@ impl ArrayBuffer { // 8. If allocatingResizableBuffer is true, then // c. Set obj.[[ArrayBufferMaxByteLength]] to maxByteLength. max_byte_len, - detach_key: JsValue::UNDEFINED, + detach_key: JsValue::undefined(), }, ); diff --git a/core/engine/src/builtins/date/mod.rs b/core/engine/src/builtins/date/mod.rs index 6cb9a1de4ed..303c0837870 100644 --- a/core/engine/src/builtins/date/mod.rs +++ b/core/engine/src/builtins/date/mod.rs @@ -423,7 +423,7 @@ impl Date { // 4. If t is NaN, return NaN. if t.is_nan() { - return Ok(JsValue::NAN); + return Ok(JsValue::new(f64::NAN)); }; if LOCAL { diff --git a/core/engine/src/builtins/function/mod.rs b/core/engine/src/builtins/function/mod.rs index 663cdd00cb9..39ac251ed8b 100644 --- a/core/engine/src/builtins/function/mod.rs +++ b/core/engine/src/builtins/function/mod.rs @@ -735,7 +735,7 @@ impl BuiltInFunctionObject { let f = BoundFunction::create(target.clone(), this_arg, bound_args, context)?; // 4. Let L be 0. - let mut l = JsValue::ZERO; + let mut l = JsValue::new(0); // 5. Let targetHasLength be ? HasOwnProperty(Target, "length"). // 6. If targetHasLength is true, then diff --git a/core/engine/src/builtins/iterable/async_from_sync_iterator.rs b/core/engine/src/builtins/iterable/async_from_sync_iterator.rs index e9ae8b5c1bc..9d7dd896a7c 100644 --- a/core/engine/src/builtins/iterable/async_from_sync_iterator.rs +++ b/core/engine/src/builtins/iterable/async_from_sync_iterator.rs @@ -172,7 +172,7 @@ impl AsyncFromSyncIterator { // b. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »). promise_capability .resolve() - .call(&JsValue::UNDEFINED, &[iter_result], context) + .call(&JsValue::undefined(), &[iter_result], context) .expect("cannot fail according to spec"); // c. Return promiseCapability.[[Promise]]. diff --git a/core/engine/src/builtins/map/mod.rs b/core/engine/src/builtins/map/mod.rs index ea6bfc5d52a..b76257f5f91 100644 --- a/core/engine/src/builtins/map/mod.rs +++ b/core/engine/src/builtins/map/mod.rs @@ -244,7 +244,7 @@ impl Map { JsVariant::Float64(r) => { // 5. If key is -0𝔽, set key to +0𝔽. if r.is_zero() { - JsValue::ZERO + JsValue::new(0) } else { key.clone() } @@ -310,7 +310,7 @@ impl Map { pub(crate) fn delete(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { let key = args.get_or_undefined(0); let key = match key.as_number() { - Some(n) if n.is_zero() => &JsValue::ZERO, + Some(n) if n.is_zero() => &JsValue::new(0), _ => key, }; @@ -345,7 +345,7 @@ impl Map { pub(crate) fn get(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { let key = args.get_or_undefined(0); let key = match key.as_number() { - Some(n) if n.is_zero() => &JsValue::ZERO, + Some(n) if n.is_zero() => &JsValue::new(0), _ => key, }; @@ -409,7 +409,7 @@ impl Map { pub(crate) fn has(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { let key = args.get_or_undefined(0); let key = match key.as_number() { - Some(n) if n.is_zero() => &JsValue::ZERO, + Some(n) if n.is_zero() => &JsValue::new(0), _ => key, }; diff --git a/core/engine/src/builtins/number/globals.rs b/core/engine/src/builtins/number/globals.rs index 9c34578d0b5..c9ef2a4b2b8 100644 --- a/core/engine/src/builtins/number/globals.rs +++ b/core/engine/src/builtins/number/globals.rs @@ -254,7 +254,7 @@ pub(crate) fn parse_int(_: &JsValue, args: &[JsValue], context: &mut Context) -> return Ok(JsValue::new(-0_f64)); } - return Ok(JsValue::ZERO); + return Ok(JsValue::new(0)); } // 16. Return 𝔽(sign × mathInt). diff --git a/core/engine/src/builtins/object/mod.rs b/core/engine/src/builtins/object/mod.rs index 4fd42032f2e..d820da17b08 100644 --- a/core/engine/src/builtins/object/mod.rs +++ b/core/engine/src/builtins/object/mod.rs @@ -216,7 +216,7 @@ impl OrdinaryObject { // 2. Return ? O.[[GetPrototypeOf]](). let proto = obj.__get_prototype_of__(&mut InternalMethodContext::new(context))?; - Ok(proto.map_or(JsValue::NULL, JsValue::new)) + Ok(proto.map_or(JsValue::null(), JsValue::new)) } /// `set Object.prototype.__proto__` @@ -653,7 +653,7 @@ impl OrdinaryObject { // 2. Return ? obj.[[GetPrototypeOf]](). Ok(obj .__get_prototype_of__(&mut InternalMethodContext::new(context))? - .map_or(JsValue::NULL, JsValue::new)) + .map_or(JsValue::null(), JsValue::new)) } /// Set the `prototype` of an object. @@ -757,11 +757,11 @@ impl OrdinaryObject { if let Some(object) = args.get_or_undefined(0).as_object() { let key = args .get(1) - .unwrap_or(&JsValue::UNDEFINED) + .unwrap_or(&JsValue::undefined()) .to_property_key(context)?; let desc = args .get(2) - .unwrap_or(&JsValue::UNDEFINED) + .unwrap_or(&JsValue::undefined()) .to_property_descriptor(context)?; object.define_property_or_throw(key, desc, context)?; diff --git a/core/engine/src/builtins/promise/mod.rs b/core/engine/src/builtins/promise/mod.rs index e4e44c68b77..2305b6aeaa0 100644 --- a/core/engine/src/builtins/promise/mod.rs +++ b/core/engine/src/builtins/promise/mod.rs @@ -275,7 +275,7 @@ impl PromiseCapability { promise_capability.reject = reject.clone(); // e. Return undefined. - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) }, promise_capability.clone(), ), @@ -430,7 +430,7 @@ impl BuiltInConstructor for Promise { // 9. Let completion Completion(Call(executor, undefined, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »)be ). let completion = executor.call( - &JsValue::UNDEFINED, + &JsValue::undefined(), &[ resolving_functions.resolve.clone().into(), resolving_functions.reject.clone().into(), @@ -444,7 +444,7 @@ impl BuiltInConstructor for Promise { // a. Perform ? Call(resolvingFunctions.[[Reject]], undefined, « completion.[[Value]] »). resolving_functions .reject - .call(&JsValue::UNDEFINED, &[e], context)?; + .call(&JsValue::undefined(), &[e], context)?; } // 11. Return promise. @@ -658,7 +658,7 @@ impl Promise { // 4. Repeat, while let Some(next) = iterator_record.step_value(context)? { // c. Append undefined to values. - values.borrow_mut().push(JsValue::UNDEFINED); + values.borrow_mut().push(JsValue::undefined()); // d. Let nextPromise be ? Call(promiseResolve, constructor, « next »). let next_promise = @@ -1535,7 +1535,7 @@ impl Promise { promise_capability .functions .resolve - .call(&JsValue::UNDEFINED, &[x], context)?; + .call(&JsValue::undefined(), &[x], context)?; // 4. Return promiseCapability.[[Promise]]. Ok(promise_capability.promise.clone()) @@ -2126,7 +2126,7 @@ impl Promise { reject_promise(&promise, self_resolution_error.into(), context); // c. Return undefined. - return Ok(JsValue::UNDEFINED); + return Ok(JsValue::undefined()); } let Some(then) = resolution.as_object() else { @@ -2135,7 +2135,7 @@ impl Promise { fulfill_promise(&promise, resolution.clone(), context); // b. Return undefined. - return Ok(JsValue::UNDEFINED); + return Ok(JsValue::undefined()); }; // 9. Let then be Completion(Get(resolution, "then")). @@ -2146,7 +2146,7 @@ impl Promise { reject_promise(&promise, e.to_opaque(context), context); // b. Return undefined. - return Ok(JsValue::UNDEFINED); + return Ok(JsValue::undefined()); } // 11. Let thenAction be then.[[Value]]. Ok(then) => then, @@ -2162,7 +2162,7 @@ impl Promise { fulfill_promise(&promise, resolution.clone(), context); // b. Return undefined. - return Ok(JsValue::UNDEFINED); + return Ok(JsValue::undefined()); }; // 13. Let thenJobCallback be HostMakeJobCallback(thenAction). @@ -2181,7 +2181,7 @@ impl Promise { context.job_queue().enqueue_promise_job(job, context); // 16. Return undefined. - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) }, promise.clone(), ), @@ -2216,7 +2216,7 @@ impl Promise { reject_promise(&promise, args.get_or_undefined(0).clone(), context); // 8. Return undefined. - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) }, promise, ), @@ -2279,7 +2279,7 @@ fn new_promise_reaction_job( // e. Else, let handlerResult be Completion(HostCallJobCallback(handler, undefined, « argument »)). Some(handler) => context .host_hooks() - .call_job_callback(handler, &JsValue::UNDEFINED, &[argument.clone()], context) + .call_job_callback(handler, &JsValue::undefined(), &[argument.clone()], context) .map_err(|e| e.to_opaque(context)), }; @@ -2293,7 +2293,7 @@ fn new_promise_reaction_job( ); // ii. Return empty. - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } Some(promise_capability_record) => { // g. Assert: promiseCapability is a PromiseCapability Record. @@ -2306,13 +2306,13 @@ fn new_promise_reaction_job( // h. If handlerResult is an abrupt completion, then Err(value) => { // i. Return ? Call(promiseCapability.[[Reject]], undefined, « handlerResult.[[Value]] »). - reject.call(&JsValue::UNDEFINED, &[value], context) + reject.call(&JsValue::undefined(), &[value], context) } // i. Else, Ok(value) => { // i. Return ? Call(promiseCapability.[[Resolve]], undefined, « handlerResult.[[Value]] »). - resolve.call(&JsValue::UNDEFINED, &[value], context) + resolve.call(&JsValue::undefined(), &[value], context) } } } @@ -2366,7 +2366,7 @@ fn new_promise_resolve_thenable_job( // i. Return ? Call(resolvingFunctions.[[Reject]], undefined, « thenCallResult.[[Value]] »). return resolving_functions .reject - .call(&JsValue::UNDEFINED, &[value], context); + .call(&JsValue::undefined(), &[value], context); } // d. Return ? thenCallResult. diff --git a/core/engine/src/builtins/proxy/mod.rs b/core/engine/src/builtins/proxy/mod.rs index ce82380f164..33a294a4bab 100644 --- a/core/engine/src/builtins/proxy/mod.rs +++ b/core/engine/src/builtins/proxy/mod.rs @@ -343,7 +343,7 @@ pub(crate) fn proxy_exotic_set_prototype_of( &handler.into(), &[ target.clone().into(), - val.clone().map_or(JsValue::NULL, Into::into), + val.clone().map_or(JsValue::null(), Into::into), ], context, )? diff --git a/core/engine/src/builtins/reflect/mod.rs b/core/engine/src/builtins/reflect/mod.rs index d36e9804b10..33b54c25309 100644 --- a/core/engine/src/builtins/reflect/mod.rs +++ b/core/engine/src/builtins/reflect/mod.rs @@ -273,7 +273,7 @@ impl Reflect { .ok_or_else(|| JsNativeError::typ().with_message("target must be an object"))?; Ok(target .__get_prototype_of__(&mut InternalMethodContext::new(context))? - .map_or(JsValue::NULL, JsValue::new)) + .map_or(JsValue::null(), JsValue::new)) } /// Returns `true` if the object has the property, `false` otherwise. diff --git a/core/engine/src/builtins/regexp/mod.rs b/core/engine/src/builtins/regexp/mod.rs index 25f9bb6dda0..9fb6a1f7c29 100644 --- a/core/engine/src/builtins/regexp/mod.rs +++ b/core/engine/src/builtins/regexp/mod.rs @@ -1672,7 +1672,7 @@ impl RegExp { let previous_last_index = rx.get(js_string!("lastIndex"), context)?; // 5. If SameValue(previousLastIndex, +0𝔽) is false, then - if !JsValue::same_value(&previous_last_index, &JsValue::ZERO) { + if !JsValue::same_value(&previous_last_index, &JsValue::new(0)) { // a. Perform ? Set(rx, "lastIndex", +0𝔽, true). rx.set(js_string!("lastIndex"), 0, true, context)?; } diff --git a/core/engine/src/builtins/set/mod.rs b/core/engine/src/builtins/set/mod.rs index 724fdf10da0..0d57bcf89ed 100644 --- a/core/engine/src/builtins/set/mod.rs +++ b/core/engine/src/builtins/set/mod.rs @@ -243,7 +243,7 @@ impl Set { // 4. If value is -0𝔽, set value to +0𝔽. let value = args.get_or_undefined(0); let value = match value.as_number() { - Some(n) if n.is_zero() => &JsValue::ZERO, + Some(n) if n.is_zero() => &JsValue::new(0), _ => value, }; @@ -304,7 +304,7 @@ impl Set { let value = args.get_or_undefined(0); let value = match value.as_number() { - Some(n) if n.is_zero() => &JsValue::ZERO, + Some(n) if n.is_zero() => &JsValue::new(0), _ => value, }; @@ -422,7 +422,7 @@ impl Set { drop(lock); // 8. Return undefined. - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } /// `Map.prototype.has( key )` @@ -449,7 +449,7 @@ impl Set { let value = args.get_or_undefined(0); let value = match value.as_number() { - Some(n) if n.is_zero() => &JsValue::ZERO, + Some(n) if n.is_zero() => &JsValue::new(0), _ => value, }; diff --git a/core/engine/src/builtins/string/mod.rs b/core/engine/src/builtins/string/mod.rs index eb0dc9fe5e6..8af5a5e6e69 100644 --- a/core/engine/src/builtins/string/mod.rs +++ b/core/engine/src/builtins/string/mod.rs @@ -1500,7 +1500,7 @@ impl String { let s = o.to_string(context)?; // 4. Let rx be ? RegExpCreate(regexp, undefined). - let rx = RegExp::create(regexp, &JsValue::UNDEFINED, context)?; + let rx = RegExp::create(regexp, &JsValue::undefined(), context)?; // 5. Return ? Invoke(rx, @@match, « S »). rx.invoke(JsSymbol::r#match(), &[JsValue::new(s)], context) @@ -2144,11 +2144,13 @@ impl String { // 6. Let ns be the String value that is the result of normalizing S // into the normalization form named by f as specified in // https://unicode.org/reports/tr15/. - let normalization = match args.get_or_undefined(0).as_defined() { - // 3. If form is undefined, let f be "NFC". - None => Normalization::Nfc, + let arg0 = args.get_or_undefined(0); + // 3. If form is undefined, let f be "NFC". + let normalization = if arg0.is_undefined() { + Normalization::Nfc + } else { // 4. Else, let f be ? ToString(form). - Some(f) => match f.to_string(context)? { + match arg0.to_string(context)? { ntype if &ntype == "NFC" => Normalization::Nfc, ntype if &ntype == "NFD" => Normalization::Nfd, ntype if &ntype == "NFKC" => Normalization::Nfkc, @@ -2161,7 +2163,7 @@ impl String { ) .into()); } - }, + } }; let normalizers = { @@ -2228,7 +2230,7 @@ impl String { let string = o.to_string(context)?; // 4. Let rx be ? RegExpCreate(regexp, undefined). - let rx = RegExp::create(regexp, &JsValue::UNDEFINED, context)?; + let rx = RegExp::create(regexp, &JsValue::undefined(), context)?; // 5. Return ? Invoke(rx, @@search, « string »). rx.invoke(JsSymbol::search(), &[JsValue::new(string)], context) diff --git a/core/engine/src/builtins/temporal/mod.rs b/core/engine/src/builtins/temporal/mod.rs index a6c79bea56f..4509d044d11 100644 --- a/core/engine/src/builtins/temporal/mod.rs +++ b/core/engine/src/builtins/temporal/mod.rs @@ -354,17 +354,17 @@ pub(crate) fn to_integer_with_truncation(value: &JsValue, context: &mut Context) /// Abstract operation 13.45 `ToIntegerIfIntegral( argument )` #[inline] -pub(crate) fn to_integer_if_integral(arg: &JsValue, context: &mut Context) -> JsResult { +pub(crate) fn to_integer_if_integral(arg: &JsValue, _context: &mut Context) -> JsResult { // 1. Let number be ? ToNumber(argument). // 2. If IsIntegralNumber(number) is false, throw a RangeError exception. // 3. Return ℝ(number). - if !arg.is_integral_number() { + let Some(arg) = arg.as_i32() else { return Err(JsNativeError::range() .with_message("value to convert is not an integral number.") .into()); - } + }; - arg.to_i32(context) + Ok(arg) } // 13.46 `PrepareTemporalFields ( fields, fieldNames, requiredFields [ , duplicateBehaviour ] )` diff --git a/core/engine/src/builtins/typed_array/builtin.rs b/core/engine/src/builtins/typed_array/builtin.rs index 54dda4de6ad..6972a2dd927 100644 --- a/core/engine/src/builtins/typed_array/builtin.rs +++ b/core/engine/src/builtins/typed_array/builtin.rs @@ -2617,7 +2617,7 @@ impl BuiltinTypedArray { obj.downcast_ref::() .map(|o| o.kind().js_name().into()) }) - .unwrap_or(JsValue::UNDEFINED)) + .unwrap_or(JsValue::undefined())) } /// `TypedArraySpeciesCreate ( exemplar, argumentList )` diff --git a/core/engine/src/lib.rs b/core/engine/src/lib.rs index e94980d36c1..3af1e9df233 100644 --- a/core/engine/src/lib.rs +++ b/core/engine/src/lib.rs @@ -174,7 +174,7 @@ pub trait JsArgs { impl JsArgs for [JsValue] { fn get_or_undefined(&self, index: usize) -> &JsValue { - const UNDEFINED: &JsValue = &JsValue::UNDEFINED; + const UNDEFINED: &JsValue = &JsValue::undefined(); self.get(index).unwrap_or(UNDEFINED) } } diff --git a/core/engine/src/object/builtins/jsarraybuffer.rs b/core/engine/src/object/builtins/jsarraybuffer.rs index 166c99c4f40..bc07d86c830 100644 --- a/core/engine/src/object/builtins/jsarraybuffer.rs +++ b/core/engine/src/object/builtins/jsarraybuffer.rs @@ -120,7 +120,7 @@ impl JsArrayBuffer { let obj = JsObject::new( context.root_shape(), prototype, - ArrayBuffer::from_data(block, JsValue::UNDEFINED), + ArrayBuffer::from_data(block, JsValue::undefined()), ); Ok(Self { inner: obj }) diff --git a/core/engine/src/object/builtins/jsdate.rs b/core/engine/src/object/builtins/jsdate.rs index 3a4a8574945..1000c39e0a9 100644 --- a/core/engine/src/object/builtins/jsdate.rs +++ b/core/engine/src/object/builtins/jsdate.rs @@ -68,7 +68,7 @@ impl JsDate { /// Same as JavaScript's `Date.now()` #[inline] pub fn now(context: &mut Context) -> JsResult { - Date::now(&JsValue::NULL, &[JsValue::NULL], context) + Date::now(&JsValue::null(), &[JsValue::null()], context) } // DEBUG: Uses RFC3339 internally therefore could match es6 spec of ISO8601 <======== @@ -80,7 +80,7 @@ impl JsDate { /// Same as JavaScript's `Date.parse(value)`. #[inline] pub fn parse(value: JsValue, context: &mut Context) -> JsResult { - Date::parse(&JsValue::NULL, &[value], context) + Date::parse(&JsValue::null(), &[value], context) } /// Takes a [year, month, day, hour, minute, second, millisecond] @@ -89,7 +89,7 @@ impl JsDate { /// Same as JavaScript's `Date.UTC()` #[inline] pub fn utc(values: &[JsValue], context: &mut Context) -> JsResult { - Date::utc(&JsValue::NULL, values, context) + Date::utc(&JsValue::null(), values, context) } /// Returns the day of the month(1-31) for the specified date @@ -98,7 +98,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getDate()`. #[inline] pub fn get_date(&self, context: &mut Context) -> JsResult { - Date::get_date::(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_date::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the day of the week (0–6) for the specified date @@ -107,7 +107,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getDay()`. #[inline] pub fn get_day(&self, context: &mut Context) -> JsResult { - Date::get_day::(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_day::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the year (4 digits for 4-digit years) of the specified date @@ -116,7 +116,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getFullYear()`. #[inline] pub fn get_full_year(&self, context: &mut Context) -> JsResult { - Date::get_full_year::(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_full_year::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the hour (0–23) in the specified date according to local time. @@ -124,7 +124,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getHours()`. #[inline] pub fn get_hours(&self, context: &mut Context) -> JsResult { - Date::get_hours::(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_hours::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the milliseconds (0–999) in the specified date according @@ -133,7 +133,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getMilliseconds()`. #[inline] pub fn get_milliseconds(&self, context: &mut Context) -> JsResult { - Date::get_milliseconds::(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_milliseconds::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the minutes (0–59) in the specified date according to local time. @@ -141,7 +141,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getMinutes()`. #[inline] pub fn get_minutes(&self, context: &mut Context) -> JsResult { - Date::get_minutes::(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_minutes::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the month (0–11) in the specified date according to local time. @@ -149,7 +149,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getMonth()`. #[inline] pub fn get_month(&self, context: &mut Context) -> JsResult { - Date::get_month::(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_month::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the seconds (0–59) in the specified date according to local time. @@ -157,7 +157,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getSeconds()`. #[inline] pub fn get_seconds(&self, context: &mut Context) -> JsResult { - Date::get_seconds::(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_seconds::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the numeric value of the specified date as the number @@ -167,7 +167,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getTime()`. #[inline] pub fn get_time(&self, context: &mut Context) -> JsResult { - Date::get_time(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_time(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the time-zone offset in minutes for the current locale. @@ -175,7 +175,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getTimezoneOffset()`. #[inline] pub fn get_timezone_offset(&self, context: &mut Context) -> JsResult { - Date::get_timezone_offset(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_timezone_offset(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the day (date) of the month (1–31) in the specified @@ -184,7 +184,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCDate()`. #[inline] pub fn get_utc_date(&self, context: &mut Context) -> JsResult { - Date::get_date::(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_date::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the day of the week (0–6) in the specified @@ -193,7 +193,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCDay()`. #[inline] pub fn get_utc_day(&self, context: &mut Context) -> JsResult { - Date::get_day::(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_day::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the year (4 digits for 4-digit years) in the specified @@ -202,7 +202,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCFullYear()`. #[inline] pub fn get_utc_full_year(&self, context: &mut Context) -> JsResult { - Date::get_full_year::(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_full_year::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the hours (0–23) in the specified date according @@ -211,7 +211,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCHours()`. #[inline] pub fn get_utc_hours(&self, context: &mut Context) -> JsResult { - Date::get_hours::(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_hours::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the milliseconds (0–999) in the specified date @@ -220,7 +220,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCMilliseconds()`. #[inline] pub fn get_utc_milliseconds(&self, context: &mut Context) -> JsResult { - Date::get_milliseconds::(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_milliseconds::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the minutes (0–59) in the specified date according @@ -229,7 +229,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCMinutes()`. #[inline] pub fn get_utc_minutes(&self, context: &mut Context) -> JsResult { - Date::get_minutes::(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_minutes::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the month (0–11) in the specified date according @@ -238,7 +238,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCMonth()`. #[inline] pub fn get_utc_month(&self, context: &mut Context) -> JsResult { - Date::get_month::(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_month::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the seconds (0–59) in the specified date according @@ -247,7 +247,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCSeconds()`. #[inline] pub fn get_utc_seconds(&self, context: &mut Context) -> JsResult { - Date::get_seconds::(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::get_seconds::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Sets the day of the month for a specified date according @@ -440,7 +440,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.toDateString()`. #[inline] pub fn to_date_string(&self, context: &mut Context) -> JsResult { - Date::to_date_string(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::to_date_string(&self.inner.clone().into(), &[JsValue::null()], context) } /// DEPRECATED: This feature is no longer recommended. @@ -451,7 +451,7 @@ impl JsDate { #[deprecated] #[inline] pub fn to_gmt_string(&self, context: &mut Context) -> JsResult { - Date::to_utc_string(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::to_utc_string(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the given date in the ISO 8601 format according to universal @@ -460,7 +460,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.toISOString()`. #[inline] pub fn to_iso_string(&self, context: &mut Context) -> JsResult { - Date::to_iso_string(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::to_iso_string(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns a string representing the Date using `to_iso_string()`. @@ -468,7 +468,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.toJSON()`. #[inline] pub fn to_json(&self, context: &mut Context) -> JsResult { - Date::to_json(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::to_json(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns a string representing the date portion of the given Date instance @@ -511,7 +511,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.toString()`. #[inline] pub fn to_string(&self, context: &mut Context) -> JsResult { - Date::to_string(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::to_string(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the "time" portion of the Date as human-readable string. @@ -519,7 +519,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.toTimeString()`. #[inline] pub fn to_time_string(&self, context: &mut Context) -> JsResult { - Date::to_time_string(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::to_time_string(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns a string representing the given date using the UTC time zone. @@ -527,7 +527,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.toUTCString()`. #[inline] pub fn to_utc_string(&self, context: &mut Context) -> JsResult { - Date::to_utc_string(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::to_utc_string(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the primitive value pf Date object. @@ -535,7 +535,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.valueOf()`. #[inline] pub fn value_of(&self, context: &mut Context) -> JsResult { - Date::value_of(&self.inner.clone().into(), &[JsValue::NULL], context) + Date::value_of(&self.inner.clone().into(), &[JsValue::null()], context) } /// Utility create a `Date` object from RFC3339 string diff --git a/core/engine/src/object/builtins/jspromise.rs b/core/engine/src/object/builtins/jspromise.rs index a8225e1d1ba..777113b8cec 100644 --- a/core/engine/src/object/builtins/jspromise.rs +++ b/core/engine/src/object/builtins/jspromise.rs @@ -1123,7 +1123,7 @@ impl JsPromise { /// /// let context = &mut Context::default(); /// let p1 = JsPromise::new(|fns, context| { - /// fns.resolve.call(&JsValue::UNDEFINED, &[], context) + /// fns.resolve.call(&JsValue::undefined(), &[], context) /// }, context) /// .then( /// Some( diff --git a/core/engine/src/object/builtins/jsset.rs b/core/engine/src/object/builtins/jsset.rs index 99c35462e26..f09f37b59f0 100644 --- a/core/engine/src/object/builtins/jsset.rs +++ b/core/engine/src/object/builtins/jsset.rs @@ -63,7 +63,7 @@ impl JsSet { /// Same as JavaScript's `set.clear()`. #[inline] pub fn clear(&self, context: &mut Context) -> JsResult { - Set::clear(&self.inner.clone().into(), &[JsValue::NULL], context) + Set::clear(&self.inner.clone().into(), &[JsValue::null()], context) } /// Removes the element associated to the value. @@ -103,7 +103,7 @@ impl JsSet { /// Same as JavaScript's `set.values()`. #[inline] pub fn values(&self, context: &mut Context) -> JsResult { - let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::NULL], context)? + let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::null()], context)? .get_iterator(IteratorHint::Sync, context)?; JsSetIterator::from_object(iterator_object.iterator().clone()) @@ -116,7 +116,7 @@ impl JsSet { /// Same as JavaScript's `set.keys()`. #[inline] pub fn keys(&self, context: &mut Context) -> JsResult { - let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::NULL], context)? + let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::null()], context)? .get_iterator(IteratorHint::Sync, context)?; JsSetIterator::from_object(iterator_object.iterator().clone()) diff --git a/core/engine/src/object/builtins/jsset_iterator.rs b/core/engine/src/object/builtins/jsset_iterator.rs index 519ea97e7f3..604b0cc8726 100644 --- a/core/engine/src/object/builtins/jsset_iterator.rs +++ b/core/engine/src/object/builtins/jsset_iterator.rs @@ -28,7 +28,7 @@ impl JsSetIterator { } /// Advances the `JsSetIterator` and gets the next result in the `JsSet`. pub fn next(&self, context: &mut Context) -> JsResult { - SetIterator::next(&self.inner.clone().into(), &[JsValue::NULL], context) + SetIterator::next(&self.inner.clone().into(), &[JsValue::null()], context) } } diff --git a/core/engine/src/object/builtins/jstypedarray.rs b/core/engine/src/object/builtins/jstypedarray.rs index e69faac5315..cbd264e5493 100644 --- a/core/engine/src/object/builtins/jstypedarray.rs +++ b/core/engine/src/object/builtins/jstypedarray.rs @@ -418,15 +418,15 @@ impl JsTypedArray { /// Some(3), /// context, /// )?; - /// assert_eq!(initialized8_array.get(0, context)?, JsValue::ZERO); - /// assert_eq!(initialized8_array.get(1, context)?, JsValue::ZERO); - /// assert_eq!(initialized8_array.get(2, context)?, JsValue::ZERO); + /// assert_eq!(initialized8_array.get(0, context)?, JsValue::new(0)); + /// assert_eq!(initialized8_array.get(1, context)?, JsValue::new(0)); + /// assert_eq!(initialized8_array.get(2, context)?, JsValue::new(0)); /// assert_eq!(initialized8_array.get(3, context)?, JsValue::new(1.0)); /// assert_eq!(initialized8_array.get(4, context)?, JsValue::new(2.0)); - /// assert_eq!(initialized8_array.get(5, context)?, JsValue::ZERO); - /// assert_eq!(initialized8_array.get(6, context)?, JsValue::ZERO); - /// assert_eq!(initialized8_array.get(7, context)?, JsValue::ZERO); - /// assert_eq!(initialized8_array.get(8, context)?, JsValue::UNDEFINED); + /// assert_eq!(initialized8_array.get(5, context)?, JsValue::new(0)); + /// assert_eq!(initialized8_array.get(6, context)?, JsValue::new(0)); + /// assert_eq!(initialized8_array.get(7, context)?, JsValue::new(0)); + /// assert_eq!(initialized8_array.get(8, context)?, JsValue::undefined()); /// /// # Ok(()) /// # } @@ -673,7 +673,7 @@ impl JsTypedArray { /// .to_uint8(inner_context) /// .expect("error at number conversion"); /// *captures.borrow_mut() += element; - /// Ok(JsValue::UNDEFINED) + /// Ok(JsValue::undefined()) /// }, /// Gc::clone(&num_to_modify), /// ), diff --git a/core/engine/src/object/jsobject.rs b/core/engine/src/object/jsobject.rs index 0d28545a35c..db02acd3c37 100644 --- a/core/engine/src/object/jsobject.rs +++ b/core/engine/src/object/jsobject.rs @@ -319,7 +319,7 @@ impl JsObject { if recursion_limiter.live { // we're in a recursive object, bail return Ok(match hint { - PreferredType::Number => JsValue::ZERO, + PreferredType::Number => JsValue::new(0), PreferredType::String => JsValue::new(js_string!()), PreferredType::Default => unreachable!("checked type hint in step 2"), }); diff --git a/core/engine/src/value/conversions/convert.rs b/core/engine/src/value/conversions/convert.rs index 5a2c82cc808..92e89b554b0 100644 --- a/core/engine/src/value/conversions/convert.rs +++ b/core/engine/src/value/conversions/convert.rs @@ -33,7 +33,7 @@ use crate::{Context, JsData, JsResult, JsString, JsValue}; /// # use boa_engine::value::{Convert, TryFromJs}; /// # let mut context = Context::default(); /// let Convert(conv0): Convert = -/// Convert::try_from_js(&JsValue::ZERO, &mut context).unwrap(); +/// Convert::try_from_js(&JsValue::new(0), &mut context).unwrap(); /// let Convert(conv5): Convert = /// Convert::try_from_js(&JsValue::new(5), &mut context).unwrap(); /// let Convert(conv_nan): Convert = diff --git a/core/engine/src/value/conversions/mod.rs b/core/engine/src/value/conversions/mod.rs index e7d2ae69d03..7be6fbcc62f 100644 --- a/core/engine/src/value/conversions/mod.rs +++ b/core/engine/src/value/conversions/mod.rs @@ -126,7 +126,7 @@ impl From<()> for JsValue { fn from(_: ()) -> Self { let _timer = Profiler::global().start_event("From<()>", "value"); - Self::NULL + Self::null() } } @@ -147,7 +147,7 @@ where fn into_or_undefined(self) -> JsValue { match self { Some(value) => value.into(), - None => JsValue::UNDEFINED, + None => JsValue::undefined(), } } } diff --git a/core/engine/src/value/conversions/serde_json.rs b/core/engine/src/value/conversions/serde_json.rs index 6cf06466c45..cad9e9933cf 100644 --- a/core/engine/src/value/conversions/serde_json.rs +++ b/core/engine/src/value/conversions/serde_json.rs @@ -44,7 +44,7 @@ impl JsValue { const MIN_INT: i64 = i32::MIN as i64; match json { - Value::Null => Ok(Self::NULL), + Value::Null => Ok(Self::null()), Value::Bool(b) => Ok(Self::new(*b)), Value::Number(num) => num .as_i64() diff --git a/core/engine/src/value/conversions/try_into_js.rs b/core/engine/src/value/conversions/try_into_js.rs index a2429072a7a..3e12d8be58e 100644 --- a/core/engine/src/value/conversions/try_into_js.rs +++ b/core/engine/src/value/conversions/try_into_js.rs @@ -124,7 +124,7 @@ where fn try_into_js(&self, context: &mut Context) -> JsResult { match self { Some(x) => x.try_into_js(context), - None => Ok(JsValue::UNDEFINED), + None => Ok(JsValue::undefined()), } } } @@ -170,7 +170,7 @@ impl_try_into_js_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: impl TryIntoJs for () { fn try_into_js(&self, _context: &mut Context) -> JsResult { - Ok(JsValue::NULL) + Ok(JsValue::null()) } } diff --git a/core/engine/src/value/display.rs b/core/engine/src/value/display.rs index 44b86f1965a..82d48a4d53c 100644 --- a/core/engine/src/value/display.rs +++ b/core/engine/src/value/display.rs @@ -63,7 +63,7 @@ macro_rules! print_obj_value { vec![format!( "{:>width$}: {}", "__proto__", - JsValue::NULL.display(), + JsValue::null().display(), width = $indent, )] } diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index bf1a039408b..2f54a91a940 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -65,7 +65,7 @@ static TWO_E_63: Lazy = Lazy::new(|| { BigInt::from(TWO_E_63) }); -/// The Inner type of a [`JsValue`]. This is the actual value that the `JsValue` holds. +/// The Inner type of [`JsValue`]. This is the actual value that the `JsValue` holds. /// This is not a public API and should not be used directly. /// /// If you need access to the variant, use [`JsValue::variant`] instead. @@ -91,48 +91,23 @@ unsafe impl Trace for InnerValue { }} } -/// A Javascript value +/// A generic Javascript value. This can be any ECMAScript language valid value. +/// +/// This is a wrapper around the actual value, which is stored in an opaque type. +/// This allows for internal changes to the value without affecting the public API. +/// +/// ``` +/// # use boa_engine::{js_string, Context, JsValue}; +/// let mut context = Context::default(); +/// let value = JsValue::new(3); +/// assert_eq!(value.to_string(&mut context), Ok(js_string!("3"))); +/// ``` #[derive(Finalize, Debug, Clone, Trace)] pub struct JsValue { inner: InnerValue, } impl JsValue { - /// The integer zero as a [`JsValue`] constant, for convenience. - pub const ZERO: Self = Self { - inner: InnerValue::Integer32(0), - }; - - /// The integer one as a [`JsValue`] constant, for convenience. - pub const ONE: Self = Self { - inner: InnerValue::Integer32(1), - }; - - /// `NaN` as a [`JsValue`] constant, for convenience. - pub const NAN: Self = Self { - inner: InnerValue::Float64(f64::NAN), - }; - - /// Positive infinity as a [`JsValue`] constant, for convenience. - pub const POSITIVE_INFINITY: Self = Self { - inner: InnerValue::Float64(f64::INFINITY), - }; - - /// Negative infinity as a [`JsValue`] constant, for convenience. - pub const NEGATIVE_INFINITY: Self = Self { - inner: InnerValue::Float64(f64::NEG_INFINITY), - }; - - /// Undefined as a [`JsValue`] constant, for convenience. - pub const UNDEFINED: Self = Self { - inner: InnerValue::Undefined, - }; - - /// Null as a [`JsValue`] constant, for convenience. - pub const NULL: Self = Self { - inner: InnerValue::Null, - }; - /// Create a new [`JsValue`] from an inner value. const fn from_inner(inner: InnerValue) -> Self { Self { inner } @@ -157,39 +132,49 @@ impl JsValue { #[inline] #[must_use] pub const fn undefined() -> Self { - Self { - inner: InnerValue::Undefined, - } + Self::from_inner(InnerValue::Undefined) } /// Creates a new `null` value. #[inline] #[must_use] pub const fn null() -> Self { - Self { - inner: InnerValue::Null, - } + Self::from_inner(InnerValue::Null) } /// Creates a new number with `NaN` value. #[inline] #[must_use] pub const fn nan() -> Self { - Self::NAN + Self::from_inner(InnerValue::Float64(f64::NAN)) } /// Creates a new number with `Infinity` value. #[inline] #[must_use] pub const fn positive_infinity() -> Self { - Self::POSITIVE_INFINITY + Self::from_inner(InnerValue::Float64(f64::INFINITY)) } /// Creates a new number with `-Infinity` value. #[inline] #[must_use] pub const fn negative_infinity() -> Self { - Self::NEGATIVE_INFINITY + Self::from_inner(InnerValue::Float64(f64::NEG_INFINITY)) + } + + /// Creates a new number from an integer. + #[inline] + #[must_use] + pub const fn integer(integer: i32) -> Self { + Self::from_inner(InnerValue::Integer32(integer)) + } + + /// Creates a new number from a float. + #[inline] + #[must_use] + pub const fn rational(rational: f64) -> Self { + Self::from_inner(InnerValue::Float64(rational)) } /// Returns true if the value is an object. @@ -349,17 +334,6 @@ impl JsValue { matches!(&self.inner, InnerValue::Undefined) } - /// Returns `()` if the value is undefined, otherwise `None`. - #[inline] - #[must_use] - pub const fn as_undefined(&self) -> Option<()> { - if self.is_undefined() { - Some(()) - } else { - None - } - } - /// Returns `Some(self)` if the value is defined, otherwise `None`. #[inline] #[must_use] @@ -385,24 +359,15 @@ impl JsValue { self.is_undefined() || self.is_null() } - /// Determines if argument is a finite integral Number value. + /// Returns the number if the value is a finite integral Number value, otherwise `None`. /// /// More information: /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-isintegralnumber - #[must_use] - #[allow(clippy::float_cmp)] - pub fn is_integral_number(&self) -> bool { - // When creating the inner value, we verify that the float is rational or - // an integer, so we can safely unwrap here. - matches!(&self.inner, InnerValue::Integer32(_)) - } - - /// Returns the number if the value is an integer, otherwise `None`. #[inline] #[must_use] - pub const fn as_integer32(&self) -> Option { + pub const fn as_i32(&self) -> Option { if let InnerValue::Integer32(i) = self.inner { Some(i) } else { @@ -601,7 +566,7 @@ impl JsValue { /// Returns an object that implements `Display`. /// - /// By default the internals are not shown, but they can be toggled + /// By default, the internals are not shown, but they can be toggled /// with [`ValueDisplay::internals`] method. /// /// # Examples diff --git a/core/engine/src/vm/call_frame/mod.rs b/core/engine/src/vm/call_frame/mod.rs index 325bd34e74b..f541a0d296e 100644 --- a/core/engine/src/vm/call_frame/mod.rs +++ b/core/engine/src/vm/call_frame/mod.rs @@ -377,7 +377,7 @@ impl JsValue { /// If not a integer type or not in the range `1..=2`. #[track_caller] pub(crate) fn to_generator_resume_kind(&self) -> GeneratorResumeKind { - if let Some(value) = self.as_integer32() { + if let Some(value) = self.as_i32() { match value { 0 => return GeneratorResumeKind::Normal, 1 => return GeneratorResumeKind::Throw, diff --git a/core/engine/src/vm/opcode/control_flow/jump.rs b/core/engine/src/vm/opcode/control_flow/jump.rs index 5b973633e71..387a2de2e0d 100644 --- a/core/engine/src/vm/opcode/control_flow/jump.rs +++ b/core/engine/src/vm/opcode/control_flow/jump.rs @@ -128,7 +128,7 @@ impl Operation for JumpTable { let count = context.vm.read::(); let value = context.vm.pop(); - if let Some(value) = value.as_integer32() { + if let Some(value) = value.as_i32() { let value = value as u32; let mut target = None; for i in 0..count { diff --git a/core/engine/src/vm/opcode/push/array.rs b/core/engine/src/vm/opcode/push/array.rs index c2b170272a1..d5650c46503 100644 --- a/core/engine/src/vm/opcode/push/array.rs +++ b/core/engine/src/vm/opcode/push/array.rs @@ -22,7 +22,7 @@ impl Operation for PushNewArray { .intrinsics() .templates() .array() - .create(Array, vec![JsValue::ZERO]); + .create(Array, vec![JsValue::new(0)]); context.vm.push(array); Ok(CompletionType::Normal) } diff --git a/core/interop/src/lib.rs b/core/interop/src/lib.rs index edd92b4382f..436680d29db 100644 --- a/core/interop/src/lib.rs +++ b/core/interop/src/lib.rs @@ -155,7 +155,7 @@ impl<'a, T: TryFromJs> TryFromJsArgument<'a> for T { ) -> JsResult<(Self, &'a [JsValue])> { match rest.split_first() { Some((first, rest)) => Ok((first.try_js_into(context)?, rest)), - None => T::try_from_js(&JsValue::UNDEFINED, context).map(|v| (v, rest)), + None => T::try_from_js(&JsValue::undefined(), context).map(|v| (v, rest)), } } } diff --git a/core/runtime/src/console/mod.rs b/core/runtime/src/console/mod.rs index 106be79ffb2..4789f29730b 100644 --- a/core/runtime/src/console/mod.rs +++ b/core/runtime/src/console/mod.rs @@ -439,7 +439,7 @@ impl Console { logger.error(formatter(&args, context)?, &console.state, context)?; } - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } /// `console.clear()` @@ -461,7 +461,7 @@ impl Console { _: &mut Context, ) -> JsResult { console.state.groups.clear(); - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } /// `console.debug(...data)` @@ -482,7 +482,7 @@ impl Console { context: &mut Context, ) -> JsResult { logger.debug(formatter(args, context)?, &console.state, context)?; - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } /// `console.error(...data)` @@ -503,7 +503,7 @@ impl Console { context: &mut Context, ) -> JsResult { logger.error(formatter(args, context)?, &console.state, context)?; - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } /// `console.info(...data)` @@ -524,7 +524,7 @@ impl Console { context: &mut Context, ) -> JsResult { logger.info(formatter(args, context)?, &console.state, context)?; - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } /// `console.log(...data)` @@ -545,7 +545,7 @@ impl Console { context: &mut Context, ) -> JsResult { logger.log(formatter(args, context)?, &console.state, context)?; - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } /// `console.trace(...data)` @@ -579,7 +579,7 @@ impl Console { .join("\n"); logger.log(stack_trace_dump, &console.state, context)?; - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } /// `console.warn(...data)` @@ -600,7 +600,7 @@ impl Console { context: &mut Context, ) -> JsResult { logger.warn(formatter(args, context)?, &console.state, context)?; - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } /// `console.count(label)` @@ -630,7 +630,7 @@ impl Console { *c += 1; logger.info(format!("{msg} {c}"), &console.state, context)?; - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } /// `console.countReset(label)` @@ -663,7 +663,7 @@ impl Console { context, )?; - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } /// Returns current system time in ms. @@ -707,7 +707,7 @@ impl Console { )?; } - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } /// `console.timeLog(label, ...data)` @@ -747,7 +747,7 @@ impl Console { )?; } - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } /// `console.timeEnd(label)` @@ -791,7 +791,7 @@ impl Console { )?; }; - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } /// `console.group(...data)` @@ -816,7 +816,7 @@ impl Console { logger.info(format!("group: {group_label}"), &console.state, context)?; console.state.groups.push(group_label); - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } /// `console.groupCollapsed(...data)` @@ -836,7 +836,7 @@ impl Console { logger: &impl Logger, context: &mut Context, ) -> JsResult { - Console::group(&JsValue::UNDEFINED, args, console, logger, context) + Console::group(&JsValue::undefined(), args, console, logger, context) } /// `console.groupEnd(label)` @@ -859,7 +859,7 @@ impl Console { ) -> JsResult { console.state.groups.pop(); - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } /// `console.dir(item, options)` @@ -885,6 +885,6 @@ impl Console { &console.state, context, )?; - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) } } diff --git a/examples/src/bin/jsarray.rs b/examples/src/bin/jsarray.rs index 6af88571ec5..bf65c3815f6 100644 --- a/examples/src/bin/jsarray.rs +++ b/examples/src/bin/jsarray.rs @@ -110,7 +110,7 @@ fn main() -> JsResult<()> { .build(); assert_eq!( - chained_array.reduce(reduce_callback, Some(JsValue::ZERO), context)?, + chained_array.reduce(reduce_callback, Some(JsValue::new(0)), context)?, JsValue::new(202) ); diff --git a/examples/src/bin/jsmap.rs b/examples/src/bin/jsmap.rs index b52cd07611a..39257497866 100644 --- a/examples/src/bin/jsmap.rs +++ b/examples/src/bin/jsmap.rs @@ -33,7 +33,7 @@ fn main() -> JsResult<()> { let deleted_key_one = map.get(js_string!("Key-1"), context)?; - assert_eq!(deleted_key_one, JsValue::UNDEFINED); + assert_eq!(deleted_key_one, JsValue::undefined()); // Retrieve a MapIterator for all entries in the Map. let entries = map.entries(context)?; diff --git a/examples/src/bin/jstypedarray.rs b/examples/src/bin/jstypedarray.rs index c0b3e21262a..19abcc96831 100644 --- a/examples/src/bin/jstypedarray.rs +++ b/examples/src/bin/jstypedarray.rs @@ -20,7 +20,7 @@ fn main() -> JsResult<()> { let array = JsUint8Array::from_iter(data, context)?; - assert_eq!(array.get(0, context)?, JsValue::ZERO); + assert_eq!(array.get(0, context)?, JsValue::new(0)); let mut sum = 0; @@ -41,7 +41,7 @@ fn main() -> JsResult<()> { .build(); assert_eq!( - array.reduce(callback, Some(JsValue::ZERO), context)?, + array.reduce(callback, Some(JsValue::new(0)), context)?, JsValue::new(sum) ); @@ -107,7 +107,7 @@ fn main() -> JsResult<()> { .expect("error at number conversion"); *captures.borrow_mut() += element; - Ok(JsValue::UNDEFINED) + Ok(JsValue::undefined()) }, Gc::clone(&num_to_modify), ), @@ -135,15 +135,15 @@ fn main() -> JsResult<()> { Some(3), context, )?; - assert_eq!(initialized8_array.get(0, context)?, JsValue::ZERO); - assert_eq!(initialized8_array.get(1, context)?, JsValue::ZERO); - assert_eq!(initialized8_array.get(2, context)?, JsValue::ZERO); + assert_eq!(initialized8_array.get(0, context)?, JsValue::new(0)); + assert_eq!(initialized8_array.get(1, context)?, JsValue::new(0)); + assert_eq!(initialized8_array.get(2, context)?, JsValue::new(0)); assert_eq!(initialized8_array.get(3, context)?, JsValue::new(1.0)); assert_eq!(initialized8_array.get(4, context)?, JsValue::new(2.0)); - assert_eq!(initialized8_array.get(5, context)?, JsValue::ZERO); - assert_eq!(initialized8_array.get(6, context)?, JsValue::ZERO); - assert_eq!(initialized8_array.get(7, context)?, JsValue::ZERO); - assert_eq!(initialized8_array.get(8, context)?, JsValue::UNDEFINED); + assert_eq!(initialized8_array.get(5, context)?, JsValue::new(0)); + assert_eq!(initialized8_array.get(6, context)?, JsValue::new(0)); + assert_eq!(initialized8_array.get(7, context)?, JsValue::new(0)); + assert_eq!(initialized8_array.get(8, context)?, JsValue::undefined()); // subarray let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?; diff --git a/examples/src/bin/synthetic.rs b/examples/src/bin/synthetic.rs index 4d1319400fa..e38562cde4b 100644 --- a/examples/src/bin/synthetic.rs +++ b/examples/src/bin/synthetic.rs @@ -68,7 +68,7 @@ fn main() -> Result<(), Box> { match promise_result.state() { PromiseState::Pending => return Err("module didn't execute!".into()), PromiseState::Fulfilled(v) => { - assert_eq!(v, JsValue::UNDEFINED); + assert_eq!(v, JsValue::undefined()); } PromiseState::Rejected(err) => { return Err(JsError::from_opaque(err).try_native(context)?.into()) diff --git a/examples/src/bin/try_into_js_derive.rs b/examples/src/bin/try_into_js_derive.rs index 736367e0400..52ad5527d6d 100644 --- a/examples/src/bin/try_into_js_derive.rs +++ b/examples/src/bin/try_into_js_derive.rs @@ -69,7 +69,7 @@ fn main() -> JsResult<()> { }; let result = point_shift.call( - &JsValue::UNDEFINED, + &JsValue::undefined(), &[a.try_into_js(context)?, b.try_into_js(context)?], context, )?; @@ -82,7 +82,7 @@ fn main() -> JsResult<()> { assert_eq!(verifier, expect); let result = point_shift.call( - &JsValue::UNDEFINED, + &JsValue::undefined(), &[a.try_into_js(context)?, c.try_into_js(context)?], context, )?; From 81e92b6330726abb636dafb97344c86da8b8ab74 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Mon, 16 Dec 2024 15:13:20 -0800 Subject: [PATCH 14/19] Fix new merges from main --- core/engine/src/builtins/number/conversions.rs | 2 +- core/engine/src/builtins/string/mod.rs | 9 +++++---- .../builtins/temporal/plain_month_day/mod.rs | 5 ++--- .../src/builtins/temporal/plain_time/mod.rs | 3 ++- .../src/builtins/temporal/zoneddatetime/mod.rs | 18 +++++++++--------- core/engine/src/value/mod.rs | 15 ++++----------- 6 files changed, 23 insertions(+), 29 deletions(-) diff --git a/core/engine/src/builtins/number/conversions.rs b/core/engine/src/builtins/number/conversions.rs index 9457febdf86..f572a5b70bc 100644 --- a/core/engine/src/builtins/number/conversions.rs +++ b/core/engine/src/builtins/number/conversions.rs @@ -88,7 +88,7 @@ pub(crate) fn f64_to_int32(number: f64) -> i32 { "fjcvtzs {dst:w}, {src:d}", src = in(vreg) number, dst = out(reg) ret, - ) + ); } ret } diff --git a/core/engine/src/builtins/string/mod.rs b/core/engine/src/builtins/string/mod.rs index 8af5a5e6e69..f6879ac67d8 100644 --- a/core/engine/src/builtins/string/mod.rs +++ b/core/engine/src/builtins/string/mod.rs @@ -844,10 +844,11 @@ impl String { let len = string.len() as i64; // 7. If position is undefined, let pos be 0; else let pos be ? ToIntegerOrInfinity(position). - let pos = match args.get_or_undefined(1).as_defined() { - None => IntegerOrInfinity::Integer(0), - Some(pos) => pos.to_integer_or_infinity(context)?, - }; + let pos = args + .get_or_undefined(1) + .map_or(Ok(IntegerOrInfinity::Integer(0)), |pos| { + pos.to_integer_or_infinity(context) + })?; // 8. Let start be the result of clamping pos between 0 and len. let start = pos.clamp_finite(0, len) as usize; diff --git a/core/engine/src/builtins/temporal/plain_month_day/mod.rs b/core/engine/src/builtins/temporal/plain_month_day/mod.rs index 29d3144891e..1713cc005fc 100644 --- a/core/engine/src/builtins/temporal/plain_month_day/mod.rs +++ b/core/engine/src/builtins/temporal/plain_month_day/mod.rs @@ -337,9 +337,8 @@ fn to_temporal_month_day( let month_code = item .get_v(js_string!("monthCode"), context)? .map(|v| { - let JsValue::String(month_code) = - v.to_primitive(context, crate::value::PreferredType::String)? - else { + let primitive = v.to_primitive(context, crate::value::PreferredType::String)?; + let Some(month_code) = primitive.as_string() else { return Err(JsNativeError::typ() .with_message("The monthCode field value must be a string.") .into()); diff --git a/core/engine/src/builtins/temporal/plain_time/mod.rs b/core/engine/src/builtins/temporal/plain_time/mod.rs index 06863a56468..fd9679b91ec 100644 --- a/core/engine/src/builtins/temporal/plain_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_time/mod.rs @@ -620,7 +620,8 @@ pub(crate) fn to_temporal_time( context: &mut Context, ) -> JsResult { // 1.If overflow is not present, set overflow to "constrain". - let options = options.unwrap_or(&JsValue::Undefined); + let binding = JsValue::undefined(); + let options = options.unwrap_or(&binding); // 2. If item is an Object, then match value.variant() { JsVariant::Object(object) => { diff --git a/core/engine/src/builtins/temporal/zoneddatetime/mod.rs b/core/engine/src/builtins/temporal/zoneddatetime/mod.rs index bfee3ae071f..bf9449d979e 100644 --- a/core/engine/src/builtins/temporal/zoneddatetime/mod.rs +++ b/core/engine/src/builtins/temporal/zoneddatetime/mod.rs @@ -13,7 +13,7 @@ use crate::{ string::StaticJsStrings, value::{IntoOrUndefined, PreferredType}, Context, JsArgs, JsBigInt, JsData, JsError, JsNativeError, JsObject, JsResult, JsString, - JsSymbol, JsValue, + JsSymbol, JsValue, JsVariant, }; use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; @@ -359,7 +359,7 @@ impl BuiltInConstructor for ZonedDateTime { }; // 4. If timeZone is not a String, throw a TypeError exception. - let JsValue::String(timezone_str) = args.get_or_undefined(1) else { + let Some(timezone_str) = args.get_or_undefined(1).as_string() else { return Err(JsNativeError::typ() .with_message("timeZone must be a string.") .into()); @@ -383,7 +383,7 @@ impl BuiltInConstructor for ZonedDateTime { let calendar = args .get(2) .map(|v| { - if let JsValue::String(calendar_str) = v { + if let Some(calendar_str) = v.as_string() { Calendar::from_str(&calendar_str.to_std_string_escaped()) .map_err(Into::::into) } else { @@ -610,7 +610,7 @@ impl ZonedDateTime { JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.") })?; - Ok((zdt.inner.epoch_milliseconds()).into()) + Ok(zdt.inner.epoch_milliseconds().into()) } /// 6.3.18 get `Temporal.ZonedDateTime.prototype.epochNanosecond` @@ -889,8 +889,8 @@ pub(crate) fn to_temporal_zoneddatetime( // 2. Let offsetBehaviour be option. // 3. Let matchBehaviour be match-exactly. // 4. If item is an Object, then - match value { - JsValue::Object(object) => { + match value.variant() { + JsVariant::Object(object) => { // a. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then if let Some(zdt) = object.downcast_ref::() { // i. NOTE: The following steps, and similar ones below, read options @@ -964,7 +964,7 @@ pub(crate) fn to_temporal_zoneddatetime( context.tz_provider(), )?) } - JsValue::String(zdt_source) => { + JsVariant::String(zdt_source) => { // b. Let result be ? ParseISODateTime(item, « TemporalDateTimeString[+Zoned] »). // c. Let annotation be result.[[TimeZone]].[[TimeZoneAnnotation]]. // d. Assert: annotation is not empty. @@ -1025,7 +1025,7 @@ pub(crate) fn to_temporal_timezone_identifier( } // 2. If temporalTimeZoneLike is not a String, throw a TypeError exception. - let JsValue::String(tz_string) = value else { + let Some(tz_string) = value.as_string() else { return Err(JsNativeError::typ() .with_message("timeZone must be a string or Temporal.ZonedDateTime") .into()); @@ -1048,7 +1048,7 @@ fn to_offset_string(value: &JsValue, context: &mut Context) -> JsResult // 1. Let offset be ? ToPrimitive(argument, string). let offset = value.to_primitive(context, PreferredType::String)?; // 2. If offset is not a String, throw a TypeError exception. - let JsValue::String(offset_string) = offset else { + let Some(offset_string) = offset.as_string() else { return Err(JsNativeError::typ() .with_message("offset must be a String.") .into()); diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index 7c7e95db6ba..d257357f99b 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -163,13 +163,6 @@ impl JsValue { Self::from_inner(InnerValue::Float64(f64::NEG_INFINITY)) } - /// Creates a new number from an integer. - #[inline] - #[must_use] - pub const fn integer(integer: i32) -> Self { - Self::from_inner(InnerValue::Integer32(integer)) - } - /// Creates a new number from a float. #[inline] #[must_use] @@ -1131,14 +1124,14 @@ impl JsValue { /// let undefined = JsValue::undefined(); /// /// let defined_result = defined_value - /// .map_or(Ok(JsValue::Boolean(true)), |v| v.add(&JsValue::from(5), &mut context)) + /// .map_or(Ok(JsValue::new(true)), |v| v.add(&JsValue::from(5), &mut context)) /// .unwrap(); /// let undefined_result = undefined - /// .map_or(Ok(JsValue::Boolean(true)), |v| v.add(&JsValue::from(5), &mut context)) + /// .map_or(Ok(JsValue::new(true)), |v| v.add(&JsValue::from(5), &mut context)) /// .unwrap(); /// - /// assert_eq!(defined_result, JsValue::Integer(10)); - /// assert_eq!(undefined_result, JsValue::Boolean(true)); + /// assert_eq!(defined_result, JsValue::new(10)); + /// assert_eq!(undefined_result, JsValue::new(true)); /// /// ``` #[inline] From 0e19cafc0ad4946afa3af2e6b99b626f58709107 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Mon, 16 Dec 2024 19:28:59 -0800 Subject: [PATCH 15/19] Remove as_defined in favor of map/map_or --- core/engine/src/builtins/string/mod.rs | 45 ++++++++++--------- .../src/builtins/typed_array/builtin.rs | 36 +++++++-------- core/engine/src/value/mod.rs | 13 +----- 3 files changed, 41 insertions(+), 53 deletions(-) diff --git a/core/engine/src/builtins/string/mod.rs b/core/engine/src/builtins/string/mod.rs index f6879ac67d8..52709169319 100644 --- a/core/engine/src/builtins/string/mod.rs +++ b/core/engine/src/builtins/string/mod.rs @@ -283,7 +283,7 @@ impl String { .configurable(false), context, ) - .expect("length definition for a new string must not fail"); + .expect("length definition for a new string must not fail"); // 9. Return S. s @@ -714,21 +714,21 @@ impl String { // 3. Let n be ? ToIntegerOrInfinity(count). match args.get_or_undefined(0).to_integer_or_infinity(context)? { IntegerOrInfinity::Integer(n) - if n > 0 && (n as usize) * len <= Self::MAX_STRING_LENGTH => - { - if string.is_empty() { - return Ok(js_string!().into()); - } - let n = n as usize; - let mut result = Vec::with_capacity(n); + if n > 0 && (n as usize) * len <= Self::MAX_STRING_LENGTH => + { + if string.is_empty() { + return Ok(js_string!().into()); + } + let n = n as usize; + let mut result = Vec::with_capacity(n); - std::iter::repeat(string.as_str()) - .take(n) - .for_each(|s| result.push(s)); + std::iter::repeat(string.as_str()) + .take(n) + .for_each(|s| result.push(s)); - // 6. Return the String value that is made from n copies of S appended together. - Ok(JsString::concat_array(&result).into()) - } + // 6. Return the String value that is made from n copies of S appended together. + Ok(JsString::concat_array(&result).into()) + } // 5. If n is 0, return the empty String. IntegerOrInfinity::Integer(0) => Ok(js_string!().into()), // 4. If n < 0 or n is +∞, throw a RangeError exception. @@ -1099,7 +1099,7 @@ impl String { &replacement, &JsString::from(string.get_expect(position + search_length..)) ) - .into()) + .into()) } /// `22.1.3.18 String.prototype.replaceAll ( searchValue, replaceValue )` @@ -1236,7 +1236,7 @@ impl String { replace_str, context, ) - .expect("GetSubstitution should never fail here."), + .expect("GetSubstitution should never fail here."), }; // d. Set result to the string-concatenation of result, preserved, and replacement. @@ -1795,7 +1795,7 @@ impl String { [requested_locale], context.intl_provider(), ) - .unwrap_or(Locale::UND); + .unwrap_or(Locale::UND); let casemapper = context.intl_provider().case_mapper()?; @@ -1892,10 +1892,11 @@ impl String { let int_start = args.get_or_undefined(0).to_integer_or_infinity(context)?; // 5. If end is undefined, let intEnd be len; else let intEnd be ? ToIntegerOrInfinity(end). - let int_end = match args.get_or_undefined(1).as_defined() { - None => IntegerOrInfinity::Integer(len), - Some(end) => end.to_integer_or_infinity(context)?, - }; + let int_end = args + .get_or_undefined(1) + .map_or(Ok(IntegerOrInfinity::Integer(len)), |end| { + end.to_integer_or_infinity(context) + })?; // 6. Let finalStart be the result of clamping intStart between 0 and len. let final_start = int_start.clamp_finite(0, len) as usize; @@ -2014,7 +2015,7 @@ impl String { substrings.into_iter().map(JsValue::from), context, ) - .into()); + .into()); } // d. Set i to j + separatorLength. i = index + separator_length; diff --git a/core/engine/src/builtins/typed_array/builtin.rs b/core/engine/src/builtins/typed_array/builtin.rs index 6972a2dd927..632a799af88 100644 --- a/core/engine/src/builtins/typed_array/builtin.rs +++ b/core/engine/src/builtins/typed_array/builtin.rs @@ -205,20 +205,18 @@ impl BuiltinTypedArray { } }; - let mapping = match args.get_or_undefined(1).as_defined() { + let mapping = match args.first().map(JsValue::variant) { // 3. If mapfn is undefined, let mapping be false. None => None, // 4. Else, - Some(v) => match v.as_object() { - // b. Let mapping be true. - Some(obj) if obj.is_callable() => Some(obj), - // a. If IsCallable(mapfn) is false, throw a TypeError exception. - _ => { - return Err(JsNativeError::typ() - .with_message("TypedArray.from called with non-callable mapfn") - .into()) - } - }, + // b. Let mapping be true. + Some(JsVariant::Object(obj)) if obj.is_callable() => Some(obj), + // a. If IsCallable(mapfn) is false, throw a TypeError exception. + _ => { + return Err(JsNativeError::typ() + .with_message("TypedArray.from called with non-callable mapfn") + .into()) + } }; // 5. Let usingIterator be ? GetMethod(source, @@iterator). @@ -2220,9 +2218,9 @@ impl BuiltinTypedArray { context: &mut Context, ) -> JsResult { // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception. - let compare_fn = match args.first().and_then(JsValue::as_defined) { + let compare_fn = match args.first().map(JsValue::variant) { None => None, - Some(obj) if obj.is_callable() => obj.as_callable(), + Some(JsVariant::Object(obj)) if obj.is_callable() => Some(obj), _ => { return Err(JsNativeError::typ() .with_message("TypedArray.sort called with non-callable comparefn") @@ -2272,9 +2270,9 @@ impl BuiltinTypedArray { context: &mut Context, ) -> JsResult { // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception. - let compare_fn = match args.first().and_then(JsValue::as_defined) { + let compare_fn = match args.first().map(JsValue::variant) { None => None, - Some(obj) if obj.is_callable() => obj.as_callable(), + Some(JsVariant::Object(obj)) if obj.is_callable() => Some(obj), _ => { return Err(JsNativeError::typ() .with_message("TypedArray.sort called with non-callable comparefn") @@ -2394,8 +2392,8 @@ impl BuiltinTypedArray { &[buffer.into(), begin_byte_offset.into()], context, )? - .upcast() - .into()) + .upcast() + .into()) } else { // 16. Else, // a. If end is undefined, let relativeEnd be srcLength; else let relativeEnd be ? ToIntegerOrInfinity(end). @@ -2414,8 +2412,8 @@ impl BuiltinTypedArray { &[buffer.into(), begin_byte_offset.into(), new_len.into()], context, )? - .upcast() - .into()) + .upcast() + .into()) } } diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index d257357f99b..0c3efcfae1d 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -327,17 +327,6 @@ impl JsValue { matches!(&self.inner, InnerValue::Undefined) } - /// Returns `Some(self)` if the value is defined, otherwise `None`. - #[inline] - #[must_use] - pub const fn as_defined(&self) -> Option<&Self> { - if self.is_undefined() { - None - } else { - Some(self) - } - } - /// Returns true if the value is null. #[inline] #[must_use] @@ -487,7 +476,7 @@ impl JsValue { PreferredType::String => js_string!("string"), PreferredType::Number => js_string!("number"), } - .into(); + .into(); // iv. Let result be ? Call(exoticToPrim, input, « hint »). let result = exotic_to_prim.call(self, &[hint], context)?; From f762b11695f9ea36e18d8a7dd9c1097878c870de Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Tue, 17 Dec 2024 11:15:14 -0800 Subject: [PATCH 16/19] Fix lints --- core/engine/src/builtins/string/mod.rs | 36 +++++++++---------- .../src/builtins/typed_array/builtin.rs | 8 ++--- core/engine/src/value/mod.rs | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/core/engine/src/builtins/string/mod.rs b/core/engine/src/builtins/string/mod.rs index 52709169319..45464d9bde1 100644 --- a/core/engine/src/builtins/string/mod.rs +++ b/core/engine/src/builtins/string/mod.rs @@ -283,7 +283,7 @@ impl String { .configurable(false), context, ) - .expect("length definition for a new string must not fail"); + .expect("length definition for a new string must not fail"); // 9. Return S. s @@ -714,21 +714,21 @@ impl String { // 3. Let n be ? ToIntegerOrInfinity(count). match args.get_or_undefined(0).to_integer_or_infinity(context)? { IntegerOrInfinity::Integer(n) - if n > 0 && (n as usize) * len <= Self::MAX_STRING_LENGTH => - { - if string.is_empty() { - return Ok(js_string!().into()); - } - let n = n as usize; - let mut result = Vec::with_capacity(n); + if n > 0 && (n as usize) * len <= Self::MAX_STRING_LENGTH => + { + if string.is_empty() { + return Ok(js_string!().into()); + } + let n = n as usize; + let mut result = Vec::with_capacity(n); - std::iter::repeat(string.as_str()) - .take(n) - .for_each(|s| result.push(s)); + std::iter::repeat(string.as_str()) + .take(n) + .for_each(|s| result.push(s)); - // 6. Return the String value that is made from n copies of S appended together. - Ok(JsString::concat_array(&result).into()) - } + // 6. Return the String value that is made from n copies of S appended together. + Ok(JsString::concat_array(&result).into()) + } // 5. If n is 0, return the empty String. IntegerOrInfinity::Integer(0) => Ok(js_string!().into()), // 4. If n < 0 or n is +∞, throw a RangeError exception. @@ -1099,7 +1099,7 @@ impl String { &replacement, &JsString::from(string.get_expect(position + search_length..)) ) - .into()) + .into()) } /// `22.1.3.18 String.prototype.replaceAll ( searchValue, replaceValue )` @@ -1236,7 +1236,7 @@ impl String { replace_str, context, ) - .expect("GetSubstitution should never fail here."), + .expect("GetSubstitution should never fail here."), }; // d. Set result to the string-concatenation of result, preserved, and replacement. @@ -1795,7 +1795,7 @@ impl String { [requested_locale], context.intl_provider(), ) - .unwrap_or(Locale::UND); + .unwrap_or(Locale::UND); let casemapper = context.intl_provider().case_mapper()?; @@ -2015,7 +2015,7 @@ impl String { substrings.into_iter().map(JsValue::from), context, ) - .into()); + .into()); } // d. Set i to j + separatorLength. i = index + separator_length; diff --git a/core/engine/src/builtins/typed_array/builtin.rs b/core/engine/src/builtins/typed_array/builtin.rs index 632a799af88..9a82efad15f 100644 --- a/core/engine/src/builtins/typed_array/builtin.rs +++ b/core/engine/src/builtins/typed_array/builtin.rs @@ -2392,8 +2392,8 @@ impl BuiltinTypedArray { &[buffer.into(), begin_byte_offset.into()], context, )? - .upcast() - .into()) + .upcast() + .into()) } else { // 16. Else, // a. If end is undefined, let relativeEnd be srcLength; else let relativeEnd be ? ToIntegerOrInfinity(end). @@ -2412,8 +2412,8 @@ impl BuiltinTypedArray { &[buffer.into(), begin_byte_offset.into(), new_len.into()], context, )? - .upcast() - .into()) + .upcast() + .into()) } } diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index 0c3efcfae1d..14d1a890963 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -476,7 +476,7 @@ impl JsValue { PreferredType::String => js_string!("string"), PreferredType::Number => js_string!("number"), } - .into(); + .into(); // iv. Let result be ? Call(exoticToPrim, input, « hint »). let result = exotic_to_prim.call(self, &[hint], context)?; From f2156f56f1c6ed50e94e03282d33679b9e567075 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Tue, 17 Dec 2024 16:27:56 -0800 Subject: [PATCH 17/19] Fix error introduced when moving to map --- core/engine/src/builtins/typed_array/builtin.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/engine/src/builtins/typed_array/builtin.rs b/core/engine/src/builtins/typed_array/builtin.rs index 9a82efad15f..51690f9fb6a 100644 --- a/core/engine/src/builtins/typed_array/builtin.rs +++ b/core/engine/src/builtins/typed_array/builtin.rs @@ -205,9 +205,9 @@ impl BuiltinTypedArray { } }; - let mapping = match args.first().map(JsValue::variant) { + let mapping = match args.get(1).map(JsValue::variant) { // 3. If mapfn is undefined, let mapping be false. - None => None, + None | Some(JsVariant::Undefined) => None, // 4. Else, // b. Let mapping be true. Some(JsVariant::Object(obj)) if obj.is_callable() => Some(obj), @@ -215,7 +215,7 @@ impl BuiltinTypedArray { _ => { return Err(JsNativeError::typ() .with_message("TypedArray.from called with non-callable mapfn") - .into()) + .into()); } }; @@ -2219,7 +2219,7 @@ impl BuiltinTypedArray { ) -> JsResult { // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception. let compare_fn = match args.first().map(JsValue::variant) { - None => None, + None | Some(JsVariant::Undefined) => None, Some(JsVariant::Object(obj)) if obj.is_callable() => Some(obj), _ => { return Err(JsNativeError::typ() @@ -2271,7 +2271,7 @@ impl BuiltinTypedArray { ) -> JsResult { // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception. let compare_fn = match args.first().map(JsValue::variant) { - None => None, + None | Some(JsVariant::Undefined) => None, Some(JsVariant::Object(obj)) if obj.is_callable() => Some(obj), _ => { return Err(JsNativeError::typ() From f180ee2c94fc38afc868d6e6b85c035b05174d27 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Tue, 17 Dec 2024 16:36:05 -0800 Subject: [PATCH 18/19] Fix lints --- core/engine/src/builtins/string/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/engine/src/builtins/string/mod.rs b/core/engine/src/builtins/string/mod.rs index 45464d9bde1..e049aaf0549 100644 --- a/core/engine/src/builtins/string/mod.rs +++ b/core/engine/src/builtins/string/mod.rs @@ -2146,13 +2146,13 @@ impl String { // 6. Let ns be the String value that is the result of normalizing S // into the normalization form named by f as specified in // https://unicode.org/reports/tr15/. - let arg0 = args.get_or_undefined(0); + let first = args.get_or_undefined(0); // 3. If form is undefined, let f be "NFC". - let normalization = if arg0.is_undefined() { + let normalization = if first.is_undefined() { Normalization::Nfc } else { // 4. Else, let f be ? ToString(form). - match arg0.to_string(context)? { + match first.to_string(context)? { ntype if &ntype == "NFC" => Normalization::Nfc, ntype if &ntype == "NFD" => Normalization::Nfd, ntype if &ntype == "NFKC" => Normalization::Nfkc, From 1cd57320d70d3ca1be8f8bf737cb44c0c6d0cde8 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Tue, 17 Dec 2024 21:31:45 -0800 Subject: [PATCH 19/19] Add more tests and fix them --- .../engine/src/builtins/array_buffer/tests.rs | 121 +++++++++++++++++- .../src/builtins/temporal/duration/tests.rs | 45 +++++++ core/engine/src/value/conversions/mod.rs | 34 +++-- core/engine/src/value/mod.rs | 17 ++- core/engine/src/value/tests.rs | 12 ++ 5 files changed, 203 insertions(+), 26 deletions(-) diff --git a/core/engine/src/builtins/array_buffer/tests.rs b/core/engine/src/builtins/array_buffer/tests.rs index 604bf6140fe..f1af0936fc8 100644 --- a/core/engine/src/builtins/array_buffer/tests.rs +++ b/core/engine/src/builtins/array_buffer/tests.rs @@ -1,4 +1,4 @@ -use crate::Context; +use crate::{run_test_actions, Context, TestAction}; #[test] fn create_byte_data_block() { @@ -19,3 +19,122 @@ fn create_shared_byte_data_block() { // Rainy day assert!(super::shared::create_shared_byte_data_block(u64::MAX, context).is_err()); } + +#[test] +fn get_values() { + run_test_actions([ + TestAction::run( + r#" + var buffer = new ArrayBuffer(12); + var sample = new DataView(buffer, 0); + + sample.setUint8(0, 127); + sample.setUint8(1, 255); + sample.setUint8(2, 255); + sample.setUint8(3, 255); + sample.setUint8(4, 128); + sample.setUint8(5, 0); + sample.setUint8(6, 0); + sample.setUint8(7, 0); + sample.setUint8(8, 1); + sample.setUint8(9, 0); + sample.setUint8(10, 0); + sample.setUint8(11, 0); + "#, + ), + TestAction::assert("sample.getUint32(0, false) == 2147483647"), + TestAction::assert("sample.getUint32(1, false) == 4294967168"), + TestAction::assert("sample.getUint32(2, false) == 4294934528"), + TestAction::assert("sample.getUint32(3, false) == 4286578688"), + TestAction::assert("sample.getUint32(4, false) == 2147483648"), + TestAction::assert("sample.getUint32(5, false) == 1"), + TestAction::assert("sample.getUint32(6, false) == 256"), + TestAction::assert("sample.getUint32(7, false) == 65536"), + TestAction::assert("sample.getUint32(8, false) == 16777216"), + TestAction::assert("sample.getUint32(0, true) == 4294967167"), + TestAction::assert("sample.getUint32(1, true) == 2164260863"), + TestAction::assert("sample.getUint32(2, true) == 8454143"), + TestAction::assert("sample.getUint32(3, true) == 33023"), + TestAction::assert("sample.getUint32(4, true) == 128"), + TestAction::assert("sample.getUint32(5, true) == 16777216"), + TestAction::assert("sample.getUint32(6, true) == 65536"), + TestAction::assert("sample.getUint32(7, true) == 256"), + TestAction::assert("sample.getUint32(8, true) == 1"), + ]); +} + +#[test] +fn sort() { + run_test_actions([ + TestAction::run( + r#" + function cmp(a, b) { + return a.length === b.length && a.every((v, i) => v === b[i]); + } + + var TypedArrayCtor = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + ]; + + var descending = TypedArrayCtor.map((ctor) => new ctor([4, 3, 2, 1]).sort()); + var mixed = TypedArrayCtor.map((ctor) => new ctor([3, 4, 1, 2]).sort()); + var repeating = TypedArrayCtor.map((ctor) => new ctor([0, 1, 1, 2, 3, 3, 4]).sort()); + "#, + ), + // Descending + TestAction::assert("cmp(descending[0], [1, 2, 3, 4])"), + TestAction::assert("cmp(descending[1], [1, 2, 3, 4])"), + TestAction::assert("cmp(descending[2], [1, 2, 3, 4])"), + TestAction::assert("cmp(descending[3], [1, 2, 3, 4])"), + TestAction::assert("cmp(descending[4], [1, 2, 3, 4])"), + TestAction::assert("cmp(descending[5], [1, 2, 3, 4])"), + TestAction::assert("cmp(descending[6], [1, 2, 3, 4])"), + TestAction::assert("cmp(descending[7], [1, 2, 3, 4])"), + // Mixed + TestAction::assert("cmp(mixed[0], [1, 2, 3, 4])"), + TestAction::assert("cmp(mixed[1], [1, 2, 3, 4])"), + TestAction::assert("cmp(mixed[2], [1, 2, 3, 4])"), + TestAction::assert("cmp(mixed[3], [1, 2, 3, 4])"), + TestAction::assert("cmp(mixed[4], [1, 2, 3, 4])"), + TestAction::assert("cmp(mixed[5], [1, 2, 3, 4])"), + TestAction::assert("cmp(mixed[6], [1, 2, 3, 4])"), + TestAction::assert("cmp(mixed[7], [1, 2, 3, 4])"), + // Repeating + TestAction::assert("cmp(repeating[0], [0, 1, 1, 2, 3, 3, 4])"), + TestAction::assert("cmp(repeating[1], [0, 1, 1, 2, 3, 3, 4])"), + TestAction::assert("cmp(repeating[2], [0, 1, 1, 2, 3, 3, 4])"), + TestAction::assert("cmp(repeating[3], [0, 1, 1, 2, 3, 3, 4])"), + TestAction::assert("cmp(repeating[4], [0, 1, 1, 2, 3, 3, 4])"), + TestAction::assert("cmp(repeating[5], [0, 1, 1, 2, 3, 3, 4])"), + TestAction::assert("cmp(repeating[6], [0, 1, 1, 2, 3, 3, 4])"), + TestAction::assert("cmp(repeating[7], [0, 1, 1, 2, 3, 3, 4])"), + ]); +} + +#[test] +fn sort_negative_zero() { + run_test_actions([ + TestAction::run( + r#" + function cmp(a, b) { + return a.length === b.length && a.every((v, i) => v === b[i]); + } + + var TypedArrayCtor = [Float32Array, Float64Array]; + var negativeZero = TypedArrayCtor.map((ctor) => new ctor([1, 0, -0, 2]).sort()); + var infinities = TypedArrayCtor.map((ctor) => new ctor([3, 4, Infinity, -Infinity, 1, 2]).sort()); + "#, + ), + TestAction::assert("cmp(negativeZero[0], [-0, 0, 1, 2])"), + TestAction::assert("cmp(negativeZero[1], [-0, 0, 1, 2])"), + TestAction::assert("cmp(infinities[0], [-Infinity, 1, 2, 3, 4, Infinity])"), + TestAction::assert("cmp(infinities[1], [-Infinity, 1, 2, 3, 4, Infinity])"), + ]); +} diff --git a/core/engine/src/builtins/temporal/duration/tests.rs b/core/engine/src/builtins/temporal/duration/tests.rs index 4deadf08195..fb050899ee1 100644 --- a/core/engine/src/builtins/temporal/duration/tests.rs +++ b/core/engine/src/builtins/temporal/duration/tests.rs @@ -25,3 +25,48 @@ fn duration_abs() { TestAction::assert_eq("abs.milliseconds", 0), ]); } + +#[test] +fn basic() { + run_test_actions([ + TestAction::run( + r#" + var dur = new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 0); + "#, + ), + TestAction::assert_eq("dur.years", 5), + TestAction::assert_eq("dur.months", 5), + TestAction::assert_eq("dur.weeks", 5), + TestAction::assert_eq("dur.days", 5), + TestAction::assert_eq("dur.hours", 5), + TestAction::assert_eq("dur.minutes", 5), + TestAction::assert_eq("dur.seconds", 5), + TestAction::assert_eq("dur.milliseconds", 5), + TestAction::assert_eq("dur.microseconds", 5), + TestAction::assert_eq("dur.nanoseconds", 0), + // Negative + TestAction::run("dur = new Temporal.Duration(-5, -5, -5, -5, -5, -5, -5, -5, -5, 0)"), + TestAction::assert_eq("dur.years", -5), + TestAction::assert_eq("dur.months", -5), + TestAction::assert_eq("dur.weeks", -5), + TestAction::assert_eq("dur.days", -5), + TestAction::assert_eq("dur.hours", -5), + TestAction::assert_eq("dur.minutes", -5), + TestAction::assert_eq("dur.seconds", -5), + TestAction::assert_eq("dur.milliseconds", -5), + TestAction::assert_eq("dur.microseconds", -5), + TestAction::assert_eq("dur.nanoseconds", 0), + // Negative Zero + TestAction::run("dur = new Temporal.Duration(-0, -0, -0, -0, -0, -0, -0, -0, -0, 0)"), + TestAction::assert_eq("dur.years", 0), + TestAction::assert_eq("dur.months", 0), + TestAction::assert_eq("dur.weeks", 0), + TestAction::assert_eq("dur.days", 0), + TestAction::assert_eq("dur.hours", 0), + TestAction::assert_eq("dur.minutes", 0), + TestAction::assert_eq("dur.seconds", 0), + TestAction::assert_eq("dur.milliseconds", 0), + TestAction::assert_eq("dur.microseconds", 0), + TestAction::assert_eq("dur.nanoseconds", 0), + ]); +} diff --git a/core/engine/src/value/conversions/mod.rs b/core/engine/src/value/conversions/mod.rs index 7be6fbcc62f..daee6456064 100644 --- a/core/engine/src/value/conversions/mod.rs +++ b/core/engine/src/value/conversions/mod.rs @@ -49,25 +49,22 @@ impl From for JsValue { } } -macro_rules! impl_from_float { - ( $( $type_:ty ),* ) => { - $( - impl From<$type_> for JsValue { - #[inline] - #[allow(trivial_numeric_casts)] - #[allow(clippy::cast_lossless)] - fn from(value: $type_) -> Self { - let _timer = Profiler::global().start_event(concat!("From<", stringify!($type_), ">"), "value"); +impl From for JsValue { + #[inline] + fn from(value: f32) -> Self { + let _timer = Profiler::global().start_event("From", "value"); - if value != -0.0 && value.fract() == 0.0 && value <= i32::MAX as $type_ && value >= i32::MIN as $type_ { - Self::from_inner(InnerValue::Integer32(value as i32)) - } else { - Self::from_inner(InnerValue::Float64(f64::from(value))) - } - } - } - )* - }; + JsValue::from(f64::from(value)) + } +} + +impl From for JsValue { + #[inline] + fn from(value: f64) -> Self { + let _timer = Profiler::global().start_event("From", "value"); + + Self::from_inner(InnerValue::Float64(value)) + } } macro_rules! impl_from_integer { @@ -90,7 +87,6 @@ macro_rules! impl_from_integer { }; } -impl_from_float!(f32, f64); impl_from_integer!(u8, i8, u16, i16, u32, i32, u64, i64, usize, isize); impl From for JsValue { diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index 14d1a890963..0d55e3fc781 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -349,11 +349,16 @@ impl JsValue { /// [spec]: https://tc39.es/ecma262/#sec-isintegralnumber #[inline] #[must_use] + #[allow(clippy::float_cmp)] pub const fn as_i32(&self) -> Option { - if let InnerValue::Integer32(i) = self.inner { - Some(i) - } else { - None + match self.inner { + InnerValue::Integer32(integer) => Some(integer), + // If it can fit in a i32 and the truncated version is + // equal to the original then it is an integer. + InnerValue::Float64(rational) if rational == ((rational as i32) as f64) => { + Some(rational as i32) + } + _ => None, } } @@ -578,8 +583,8 @@ impl JsValue { InnerValue::Undefined => Ok(js_string!("undefined")), InnerValue::Boolean(true) => Ok(js_string!("true")), InnerValue::Boolean(false) => Ok(js_string!("false")), - InnerValue::Float64(rational) => Ok(Number::to_js_string_radix(rational, 10)), - InnerValue::Integer32(integer) => Ok(integer.to_string().into()), + InnerValue::Float64(rational) => Ok(JsString::from(rational)), + InnerValue::Integer32(integer) => Ok(JsString::from(integer)), InnerValue::String(ref string) => Ok(string.clone()), InnerValue::Symbol(_) => Err(JsNativeError::typ() .with_message("can't convert symbol to string") diff --git a/core/engine/src/value/tests.rs b/core/engine/src/value/tests.rs index 7f00f68708b..6a898377717 100644 --- a/core/engine/src/value/tests.rs +++ b/core/engine/src/value/tests.rs @@ -708,6 +708,18 @@ fn to_bigint() { })]); } +#[test] +fn pad_end() { + run_test_actions([ + TestAction::assert_eq("'abc'.padEnd(10, false)", js_string!("abcfalsefa")), + TestAction::assert_eq("'abc'.padEnd(10, true)", js_string!("abctruetru")), + TestAction::assert_eq("'abc'.padEnd(10, null)", js_string!("abcnullnul")), + TestAction::assert_eq("'abc'.padEnd(10, 0)", js_string!("abc0000000")), + TestAction::assert_eq("'abc'.padEnd(10, -0)", js_string!("abc0000000")), + TestAction::assert_eq("'abc'.padEnd(10, NaN)", js_string!("abcNaNNaNN")), + ]); +} + /// Test cyclic conversions that previously caused stack overflows /// Relevant mitigation for these are in `JsObject::ordinary_to_primitive` and /// `JsObject::to_json`