From 28957fa72ca6407eb5173d99e98b6a8815f319c3 Mon Sep 17 00:00:00 2001 From: cybai Date: Sun, 19 Jun 2022 22:37:10 +0900 Subject: [PATCH 1/2] Introduce NonMaxU32 type --- boa_engine/src/lib.rs | 1 + boa_engine/src/nonmaxu32.rs | 180 ++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 boa_engine/src/nonmaxu32.rs diff --git a/boa_engine/src/lib.rs b/boa_engine/src/lib.rs index 626db695619..57d1163a85d 100644 --- a/boa_engine/src/lib.rs +++ b/boa_engine/src/lib.rs @@ -78,6 +78,7 @@ pub mod class; pub mod context; pub mod environments; pub mod job; +pub mod nonmaxu32; pub mod object; pub mod property; pub mod realm; diff --git a/boa_engine/src/nonmaxu32.rs b/boa_engine/src/nonmaxu32.rs new file mode 100644 index 00000000000..a021772dc11 --- /dev/null +++ b/boa_engine/src/nonmaxu32.rs @@ -0,0 +1,180 @@ +//! This module implements `NonMaxU32` +//! which is known not to equal `u32::MAX`. +//! +//! This would be useful for integers like `https://tc39.es/ecma262/#array-index`. + +use boa_gc::{unsafe_empty_trace, Finalize, Trace}; +use std::fmt; +use std::ops::{BitOr, BitOrAssign}; + +/// An integer that is known not to equal `u32::MAX`. +/// +/// This enables some memory layout optimization. +/// For example, `Option` is the same size as `u32`: +/// +/// ```rust +/// use std::mem::size_of; +/// use boa_engine::nonmaxu32::NonMaxU32; +/// assert_eq!(size_of::>(), size_of::()); +/// ``` +#[derive(Copy, Clone, Eq, Finalize, PartialEq, Ord, PartialOrd, Hash)] +#[repr(transparent)] +pub struct NonMaxU32(u32); + +// Safety: `NonMaxU32` does not contain any objects which needs to be traced, +// so this is safe. +unsafe impl Trace for NonMaxU32 { + unsafe_empty_trace!(); +} + +/// An error type returned when a checked integral type conversion fails (mimics [`std::num::TryFromIntError`]) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct TryFromIntError(()); + +impl fmt::Display for TryFromIntError { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + "out of range integral type conversion attempted".fmt(fmt) + } +} + +impl From for TryFromIntError { + fn from(_: core::num::TryFromIntError) -> Self { + Self(()) + } +} + +impl From for TryFromIntError { + fn from(never: core::convert::Infallible) -> Self { + match never {} + } +} + +/// An error type returned when an integer cannot be parsed (mimics [`std::num::ParseIntError`]) +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct ParseIntError(()); + +impl fmt::Display for ParseIntError { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + "unable to parse integer".fmt(fmt) + } +} + +impl From for ParseIntError { + fn from(_: core::num::ParseIntError) -> Self { + Self(()) + } +} + +impl NonMaxU32 { + /// Creates a non-u32-max without checking the value. + /// + /// # Safety + /// + /// The value must not be `u32::MAX`. + #[inline] + pub const unsafe fn new_unchecked(n: u32) -> Self { + // SAFETY: this is guaranteed to be safe by the caller. + Self(n) + } + + /// Creates a non-u32-max if the given value is not `u32::MAX`. + #[inline] + pub const fn new(n: u32) -> Option { + if n == u32::MAX { + None + } else { + // SAFETY: we just checked that there's no `u32::MAX` + Some(Self(n)) + } + } + + /// Returns the value as a primitive type. + #[inline] + pub const fn get(self) -> u32 { + self.0 + } +} + +impl TryFrom for NonMaxU32 { + type Error = TryFromIntError; + + fn try_from(value: u32) -> Result { + Self::new(value).ok_or(TryFromIntError(())) + } +} + +impl From for u32 { + /// Converts a `NonMaxU32` into an `u32` + fn from(nonzero: NonMaxU32) -> Self { + nonzero.0 + } +} + +impl core::str::FromStr for NonMaxU32 { + type Err = ParseIntError; + fn from_str(value: &str) -> Result { + Self::new(u32::from_str(value)?).ok_or(ParseIntError(())) + } +} + +impl BitOr for NonMaxU32 { + type Output = Self; + #[inline] + fn bitor(self, rhs: Self) -> Self::Output { + // Safety: since `self` and `rhs` are both nonzero, the + // result of the bitwise-or will be nonzero. + unsafe { NonMaxU32::new_unchecked(self.get() | rhs.get()) } + } +} + +impl BitOr for NonMaxU32 { + type Output = Self; + + #[inline] + fn bitor(self, rhs: u32) -> Self::Output { + // Safety: since `self` is nonzero, the result of the + // bitwise-or will be nonzero regardless of the value of + // `rhs`. + unsafe { NonMaxU32::new_unchecked(self.get() | rhs) } + } +} + +impl BitOr for u32 { + type Output = NonMaxU32; + + #[inline] + fn bitor(self, rhs: NonMaxU32) -> Self::Output { + // Safety: since `rhs` is nonzero, the result of the + // bitwise-or will be nonzero regardless of the value of + // `self`. + unsafe { NonMaxU32::new_unchecked(self | rhs.get()) } + } +} + +impl BitOrAssign for NonMaxU32 { + #[inline] + fn bitor_assign(&mut self, rhs: Self) { + *self = *self | rhs; + } +} + +impl BitOrAssign for NonMaxU32 { + #[inline] + fn bitor_assign(&mut self, rhs: u32) { + *self = *self | rhs; + } +} + +impl fmt::Debug for NonMaxU32 { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } +} + +impl fmt::Display for NonMaxU32 { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } +} From f18bf353c57237fa4488b98ecbc7564475cc3194 Mon Sep 17 00:00:00 2001 From: cybai Date: Mon, 20 Jun 2022 16:25:46 +0900 Subject: [PATCH 2/2] Define array index as NonMaxU32 Based on the definition in spec, [array index][] is only valid from zero to u32::MAX - 1. It would be helpful to use the NonMaxU32 type to define it as the property key instead so that contributors won't forget to check the validity. [array index]: https://tc39.es/ecma262/#array-index --- .../src/object/internal_methods/arguments.rs | 12 ++-- .../src/object/internal_methods/array.rs | 8 ++- .../internal_methods/integer_indexed.rs | 33 ++++++----- boa_engine/src/object/internal_methods/mod.rs | 2 +- .../src/object/internal_methods/string.rs | 6 +- boa_engine/src/object/property_map.rs | 24 ++++---- boa_engine/src/property/mod.rs | 59 ++++++++++++++----- 7 files changed, 89 insertions(+), 55 deletions(-) diff --git a/boa_engine/src/object/internal_methods/arguments.rs b/boa_engine/src/object/internal_methods/arguments.rs index 7ade852d3f3..df6f56996b0 100644 --- a/boa_engine/src/object/internal_methods/arguments.rs +++ b/boa_engine/src/object/internal_methods/arguments.rs @@ -44,7 +44,7 @@ pub(crate) fn arguments_exotic_get_own_property( .borrow() .as_mapped_arguments() .expect("arguments exotic method must only be callable from arguments objects") - .get(*index as usize) + .get(index.get() as usize) { // a. Set desc.[[Value]] to Get(map, P). return Ok(Some( @@ -81,8 +81,8 @@ pub(crate) fn arguments_exotic_define_own_property( obj.borrow() .as_mapped_arguments() .expect("arguments exotic method must only be callable from arguments objects") - .get(index as usize) - .map(|value| (index as usize, value)) + .get(index.get() as usize) + .map(|value| (index.get() as usize, value)) } else { None }; @@ -173,7 +173,7 @@ pub(crate) fn arguments_exotic_get( .borrow() .as_mapped_arguments() .expect("arguments exotic method must only be callable from arguments objects") - .get(*index as usize) + .get(index.get() as usize) { // a. Assert: map contains a formal parameter mapping for P. // b. Return Get(map, P). @@ -212,7 +212,7 @@ pub(crate) fn arguments_exotic_set( obj.borrow_mut() .as_mapped_arguments_mut() .expect("arguments exotic method must only be callable from arguments objects") - .set(index as usize, &value); + .set(index.get() as usize, &value); } } @@ -243,7 +243,7 @@ pub(crate) fn arguments_exotic_delete( obj.borrow_mut() .as_mapped_arguments_mut() .expect("arguments exotic method must only be callable from arguments objects") - .delete(*index as usize); + .delete(index.get() as usize); } } diff --git a/boa_engine/src/object/internal_methods/array.rs b/boa_engine/src/object/internal_methods/array.rs index 007af7a0a75..b908e468bd1 100644 --- a/boa_engine/src/object/internal_methods/array.rs +++ b/boa_engine/src/object/internal_methods/array.rs @@ -38,7 +38,9 @@ pub(crate) fn array_exotic_define_own_property( array_set_length(obj, desc, context) } // 3. Else if P is an array index, then - PropertyKey::Index(index) if index < u32::MAX => { + PropertyKey::Index(index) => { + let index = index.get(); + // a. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length"). let old_len_desc = super::ordinary_get_own_property(obj, &"length".into(), context)? .expect("the property descriptor must exist"); @@ -201,7 +203,7 @@ fn array_set_length( .borrow() .properties .index_property_keys() - .filter(|idx| new_len <= **idx && **idx < u32::MAX) + .filter(|idx| new_len <= idx.get()) .copied() .collect(); keys.sort_unstable_by(|x, y| y.cmp(x)); @@ -209,6 +211,8 @@ fn array_set_length( }; for index in ordered_keys { + let index = index.get(); + // a. Let deleteSucceeded be ! A.[[Delete]](P). // b. If deleteSucceeded is false, then if !obj.__delete__(&index.into(), context)? { diff --git a/boa_engine/src/object/internal_methods/integer_indexed.rs b/boa_engine/src/object/internal_methods/integer_indexed.rs index d1ef9913aaa..3d027d0fdb5 100644 --- a/boa_engine/src/object/internal_methods/integer_indexed.rs +++ b/boa_engine/src/object/internal_methods/integer_indexed.rs @@ -1,5 +1,6 @@ use crate::{ builtins::{array_buffer::SharedMemoryOrder, typed_array::integer_indexed_object::ContentType}, + nonmaxu32::NonMaxU32, object::JsObject, property::{PropertyDescriptor, PropertyKey}, Context, JsResult, JsValue, @@ -44,14 +45,16 @@ pub(crate) fn integer_indexed_exotic_get_own_property( // i. Let value be ! IntegerIndexedElementGet(O, numericIndex). // ii. If value is undefined, return undefined. // iii. Return the PropertyDescriptor { [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }. - Ok(integer_indexed_element_get(obj, *index as usize).map(|v| { - PropertyDescriptor::builder() - .value(v) - .writable(true) - .enumerable(true) - .configurable(true) - .build() - })) + Ok( + integer_indexed_element_get(obj, index.get() as usize).map(|v| { + PropertyDescriptor::builder() + .value(v) + .writable(true) + .enumerable(true) + .configurable(true) + .build() + }), + ) } else { // 2. Return OrdinaryGetOwnProperty(O, P). super::ordinary_get_own_property(obj, key, context) @@ -74,7 +77,7 @@ pub(crate) fn integer_indexed_exotic_has_property( // a. Let numericIndex be ! CanonicalNumericIndexString(P). if let PropertyKey::Index(index) = key { // b. If numericIndex is not undefined, return ! IsValidIntegerIndex(O, numericIndex). - Ok(is_valid_integer_index(obj, *index as usize)) + Ok(is_valid_integer_index(obj, index.get() as usize)) } else { // 2. Return ? OrdinaryHasProperty(O, P). super::ordinary_has_property(obj, key, context) @@ -103,7 +106,7 @@ pub(crate) fn integer_indexed_exotic_define_own_property( // iii. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]] is false, return false. // v. If Desc has a [[Writable]] field and if Desc.[[Writable]] is false, return false. // iv. If ! IsAccessorDescriptor(Desc) is true, return false. - if !is_valid_integer_index(obj, index as usize) + if !is_valid_integer_index(obj, index.get() as usize) || !desc .configurable() .or_else(|| desc.enumerable()) @@ -116,7 +119,7 @@ pub(crate) fn integer_indexed_exotic_define_own_property( // vi. If Desc has a [[Value]] field, perform ? IntegerIndexedElementSet(O, numericIndex, Desc.[[Value]]). if let Some(value) = desc.value() { - integer_indexed_element_set(obj, index as usize, value, context)?; + integer_indexed_element_set(obj, index.get() as usize, value, context)?; } // vii. Return true. @@ -145,7 +148,7 @@ pub(crate) fn integer_indexed_exotic_get( // b. If numericIndex is not undefined, then if let PropertyKey::Index(index) = key { // i. Return ! IntegerIndexedElementGet(O, numericIndex). - Ok(integer_indexed_element_get(obj, *index as usize).unwrap_or_default()) + Ok(integer_indexed_element_get(obj, index.get() as usize).unwrap_or_default()) } else { // 2. Return ? OrdinaryGet(O, P, Receiver). super::ordinary_get(obj, key, receiver, context) @@ -171,7 +174,7 @@ pub(crate) fn integer_indexed_exotic_set( // b. If numericIndex is not undefined, then if let PropertyKey::Index(index) = key { // i. Perform ? IntegerIndexedElementSet(O, numericIndex, V). - integer_indexed_element_set(obj, index as usize, &value, context)?; + integer_indexed_element_set(obj, index.get() as usize, &value, context)?; // ii. Return true. Ok(true) @@ -198,7 +201,7 @@ pub(crate) fn integer_indexed_exotic_delete( // b. If numericIndex is not undefined, then if let PropertyKey::Index(index) = key { // i. If ! IsValidIntegerIndex(O, numericIndex) is false, return true; else return false. - Ok(!is_valid_integer_index(obj, *index as usize)) + Ok(!is_valid_integer_index(obj, index.get() as usize)) } else { // 2. Return ? OrdinaryDelete(O, P). super::ordinary_delete(obj, key, context) @@ -231,7 +234,7 @@ pub(crate) fn integer_indexed_exotic_own_property_keys( // i. Add ! ToString(𝔽(i)) as the last element of keys. (0..inner.array_length()) .into_iter() - .map(|index| PropertyKey::Index(index as u32)) + .filter_map(|index| NonMaxU32::new(index as u32).map(PropertyKey::Index)) .collect() }; diff --git a/boa_engine/src/object/internal_methods/mod.rs b/boa_engine/src/object/internal_methods/mod.rs index 4e660fbf3b8..190be9117e0 100644 --- a/boa_engine/src/object/internal_methods/mod.rs +++ b/boa_engine/src/object/internal_methods/mod.rs @@ -737,7 +737,7 @@ pub(crate) fn ordinary_own_property_keys( // 2. For each own property key P of O such that P is an array index, in ascending numeric index order, do // a. Add P as the last element of keys. - keys.extend(ordered_indexes.into_iter().map(Into::into)); + keys.extend(ordered_indexes.into_iter().map(|idx| idx.get().into())); // 3. For each own property key P of O such that Type(P) is String and P is not an array index, in ascending chronological order of property creation, do // a. Add P as the last element of keys. diff --git a/boa_engine/src/object/internal_methods/string.rs b/boa_engine/src/object/internal_methods/string.rs index 453b90d5864..c64fc49841c 100644 --- a/boa_engine/src/object/internal_methods/string.rs +++ b/boa_engine/src/object/internal_methods/string.rs @@ -113,10 +113,10 @@ pub(crate) fn string_exotic_own_property_keys( .properties .index_property_keys() .copied() - .filter(|idx| (*idx as usize) >= len) + .filter(|idx| (idx.get() as usize) >= len) .collect(); remaining_indices.sort_unstable(); - keys.extend(remaining_indices.into_iter().map(Into::into)); + keys.extend(remaining_indices.into_iter().map(|idx| idx.get().into())); // 7. For each own property key P of O such that Type(P) is String and P is not // an array index, in ascending chronological order of property creation, do @@ -159,7 +159,7 @@ fn string_get_own_property(obj: &JsObject, key: &PropertyKey) -> Option *index as usize, + PropertyKey::Index(index) => index.get() as usize, _ => return None, }; diff --git a/boa_engine/src/object/property_map.rs b/boa_engine/src/object/property_map.rs index f661820ca96..096a4f44356 100644 --- a/boa_engine/src/object/property_map.rs +++ b/boa_engine/src/object/property_map.rs @@ -1,5 +1,5 @@ use super::{PropertyDescriptor, PropertyKey}; -use crate::{JsString, JsSymbol}; +use crate::{nonmaxu32::NonMaxU32, JsString, JsSymbol}; use boa_gc::{custom_trace, Finalize, Trace}; use indexmap::IndexMap; use rustc_hash::{FxHashMap, FxHasher}; @@ -30,7 +30,7 @@ unsafe impl Trace for OrderedHashMap { #[derive(Default, Debug, Trace, Finalize)] pub struct PropertyMap { - indexed_properties: FxHashMap, + indexed_properties: FxHashMap, /// Properties string_properties: OrderedHashMap, /// Symbol Properties @@ -126,7 +126,7 @@ impl PropertyMap { SymbolPropertyValues(self.symbol_properties.0.values()) } - /// An iterator visiting all indexed key-value pairs in arbitrary order. The iterator element type is `(&'a u32, &'a Property)`. + /// An iterator visiting all indexed key-value pairs in arbitrary order. The iterator element type is `(&'a NonMaxU32, &'a Property)`. /// /// This iterator does not recurse down the prototype chain. #[inline] @@ -134,7 +134,7 @@ impl PropertyMap { IndexProperties(self.indexed_properties.iter()) } - /// An iterator visiting all index keys in arbitrary order. The iterator element type is `&'a u32`. + /// An iterator visiting all index keys in arbitrary order. The iterator element type is `&'a NonMaxU32`. /// /// This iterator does not recurse down the prototype chain. #[inline] @@ -197,7 +197,7 @@ impl PropertyMap { /// An iterator over the property entries of an `Object` #[derive(Debug, Clone)] pub struct Iter<'a> { - indexed_properties: hash_map::Iter<'a, u32, PropertyDescriptor>, + indexed_properties: hash_map::Iter<'a, NonMaxU32, PropertyDescriptor>, string_properties: indexmap::map::Iter<'a, JsString, PropertyDescriptor>, symbol_properties: indexmap::map::Iter<'a, JsSymbol, PropertyDescriptor>, } @@ -206,7 +206,7 @@ impl<'a> Iterator for Iter<'a> { type Item = (PropertyKey, &'a PropertyDescriptor); fn next(&mut self) -> Option { if let Some((key, value)) = self.indexed_properties.next() { - Some(((*key).into(), value)) + Some((key.get().into(), value)) } else if let Some((key, value)) = self.string_properties.next() { Some((key.clone().into(), value)) } else { @@ -350,10 +350,10 @@ impl FusedIterator for SymbolPropertyValues<'_> {} /// An iterator over the indexed property entries of an `Object` #[derive(Debug, Clone)] -pub struct IndexProperties<'a>(hash_map::Iter<'a, u32, PropertyDescriptor>); +pub struct IndexProperties<'a>(hash_map::Iter<'a, NonMaxU32, PropertyDescriptor>); impl<'a> Iterator for IndexProperties<'a> { - type Item = (&'a u32, &'a PropertyDescriptor); + type Item = (&'a NonMaxU32, &'a PropertyDescriptor); #[inline] fn next(&mut self) -> Option { @@ -375,12 +375,12 @@ impl ExactSizeIterator for IndexProperties<'_> { impl FusedIterator for IndexProperties<'_> {} -/// An iterator over the index keys (`u32`) of an `Object`. +/// An iterator over the index keys (`NonMaxU32`) of an `Object`. #[derive(Debug, Clone)] -pub struct IndexPropertyKeys<'a>(hash_map::Keys<'a, u32, PropertyDescriptor>); +pub struct IndexPropertyKeys<'a>(hash_map::Keys<'a, NonMaxU32, PropertyDescriptor>); impl<'a> Iterator for IndexPropertyKeys<'a> { - type Item = &'a u32; + type Item = &'a NonMaxU32; #[inline] fn next(&mut self) -> Option { @@ -404,7 +404,7 @@ impl FusedIterator for IndexPropertyKeys<'_> {} /// An iterator over the index values (`Property`) of an `Object`. #[derive(Debug, Clone)] -pub struct IndexPropertyValues<'a>(hash_map::Values<'a, u32, PropertyDescriptor>); +pub struct IndexPropertyValues<'a>(hash_map::Values<'a, NonMaxU32, PropertyDescriptor>); impl<'a> Iterator for IndexPropertyValues<'a> { type Item = &'a PropertyDescriptor; diff --git a/boa_engine/src/property/mod.rs b/boa_engine/src/property/mod.rs index a35a214505b..ad624e67cfe 100644 --- a/boa_engine/src/property/mod.rs +++ b/boa_engine/src/property/mod.rs @@ -15,6 +15,7 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty //! [section]: https://tc39.es/ecma262/#sec-property-attributes +use crate::nonmaxu32::{NonMaxU32, TryFromIntError}; use crate::{JsString, JsSymbol, JsValue}; use boa_gc::{unsafe_empty_trace, Finalize, Trace}; use std::fmt; @@ -492,13 +493,13 @@ impl From for PropertyDescriptor { pub enum PropertyKey { String(JsString), Symbol(JsSymbol), - Index(u32), + Index(NonMaxU32), } impl From for PropertyKey { #[inline] fn from(string: JsString) -> Self { - if let Ok(index) = string.parse() { + if let Ok(index) = string.parse::() { Self::Index(index) } else { Self::String(string) @@ -509,7 +510,7 @@ impl From for PropertyKey { impl From<&str> for PropertyKey { #[inline] fn from(string: &str) -> Self { - if let Ok(index) = string.parse() { + if let Ok(index) = string.parse::() { Self::Index(index) } else { Self::String(string.into()) @@ -520,7 +521,7 @@ impl From<&str> for PropertyKey { impl From for PropertyKey { #[inline] fn from(string: String) -> Self { - if let Ok(index) = string.parse() { + if let Ok(index) = string.parse::() { Self::Index(index) } else { Self::String(string.into()) @@ -531,7 +532,7 @@ impl From for PropertyKey { impl From> for PropertyKey { #[inline] fn from(string: Box) -> Self { - if let Ok(index) = string.parse() { + if let Ok(index) = string.parse::() { Self::Index(index) } else { Self::String(string.into()) @@ -564,10 +565,10 @@ impl From<&PropertyKey> for JsValue { PropertyKey::String(ref string) => string.clone().into(), PropertyKey::Symbol(ref symbol) => symbol.clone().into(), PropertyKey::Index(index) => { - if let Ok(integer) = i32::try_from(*index) { + if let Ok(integer) = i32::try_from(index.get()) { Self::new(integer) } else { - Self::new(*index) + Self::new(index.get()) } } } @@ -587,25 +588,39 @@ impl From for JsValue { impl From for PropertyKey { fn from(value: u8) -> Self { - Self::Index(value.into()) + match NonMaxU32::new(value.into()) { + Some(index) => Self::Index(index), + // `u8` should always be valid for `NonMaxU32` + None => unreachable!(), + } } } impl From for PropertyKey { fn from(value: u16) -> Self { - Self::Index(value.into()) + match NonMaxU32::new(value.into()) { + Some(index) => Self::Index(index), + // `u16` should always be valid for `NonMaxU32` + None => unreachable!(), + } } } impl From for PropertyKey { fn from(value: u32) -> Self { - Self::Index(value) + match NonMaxU32::new(value) { + Some(num) => Self::Index(num), + None => Self::String(value.to_string().into()), + } } } impl From for PropertyKey { fn from(value: usize) -> Self { - if let Ok(index) = u32::try_from(value) { + if let Ok(index) = u32::try_from(value) + .map_err(TryFromIntError::from) + .and_then(NonMaxU32::try_from) + { Self::Index(index) } else { Self::String(JsString::from(value.to_string())) @@ -615,7 +630,10 @@ impl From for PropertyKey { impl From for PropertyKey { fn from(value: i64) -> Self { - if let Ok(index) = u32::try_from(value) { + if let Ok(index) = u32::try_from(value) + .map_err(TryFromIntError::from) + .and_then(NonMaxU32::try_from) + { Self::Index(index) } else { Self::String(JsString::from(value.to_string())) @@ -625,7 +643,10 @@ impl From for PropertyKey { impl From for PropertyKey { fn from(value: u64) -> Self { - if let Ok(index) = u32::try_from(value) { + if let Ok(index) = u32::try_from(value) + .map_err(TryFromIntError::from) + .and_then(NonMaxU32::try_from) + { Self::Index(index) } else { Self::String(JsString::from(value.to_string())) @@ -635,7 +656,10 @@ impl From for PropertyKey { impl From for PropertyKey { fn from(value: isize) -> Self { - if let Ok(index) = u32::try_from(value) { + if let Ok(index) = u32::try_from(value) + .map_err(TryFromIntError::from) + .and_then(NonMaxU32::try_from) + { Self::Index(index) } else { Self::String(JsString::from(value.to_string())) @@ -645,7 +669,10 @@ impl From for PropertyKey { impl From for PropertyKey { fn from(value: i32) -> Self { - if let Ok(index) = u32::try_from(value) { + if let Ok(index) = u32::try_from(value) + .map_err(TryFromIntError::from) + .and_then(NonMaxU32::try_from) + { Self::Index(index) } else { Self::String(JsString::from(value.to_string())) @@ -656,7 +683,7 @@ impl From for PropertyKey { impl From for PropertyKey { fn from(value: f64) -> Self { use num_traits::cast::FromPrimitive; - if let Some(index) = u32::from_f64(value) { + if let Some(index) = u32::from_f64(value).and_then(|n| NonMaxU32::try_from(n).ok()) { return Self::Index(index); }