Browse Source

Fix length/index in `32bit` architectures (#2196)

This PR fixes the length/index types from `usize` to `u64`, because in JavaScript the length can be from `0` to `2^53 - 1` it cannot be represented in a `usize`. On `64-bit` architectures this is fine because it is already a unsigned 64bit number and these changes essentially do nothing. But on 32bit architectures this was causing the length to be truncated giving an unexpected result. 

fixes/closes #2182 

It changes the following:
- Make length a `u64` to be able to represent all possible length values
- Change `JsValue::to_length()` t return `u64`
- Change `JsValue::to_index()` t return `u64`
pull/2201/head
Halid Odat 2 years ago
parent
commit
efff9d6269
  1. 2
      boa_engine/src/builtins/array/array_iterator.rs
  2. 56
      boa_engine/src/builtins/array/mod.rs
  3. 33
      boa_engine/src/builtins/array_buffer/mod.rs
  4. 2
      boa_engine/src/builtins/array_buffer/tests.rs
  5. 4
      boa_engine/src/builtins/dataview/mod.rs
  6. 31
      boa_engine/src/builtins/regexp/mod.rs
  7. 20
      boa_engine/src/builtins/string/mod.rs
  8. 2
      boa_engine/src/builtins/string/string_iterator.rs
  9. 24
      boa_engine/src/builtins/typed_array/integer_indexed_object.rs
  10. 30
      boa_engine/src/builtins/typed_array/mod.rs
  11. 22
      boa_engine/src/object/internal_methods/integer_indexed.rs
  12. 2
      boa_engine/src/object/jsarray.rs
  13. 4
      boa_engine/src/object/jsarraybuffer.rs
  14. 4
      boa_engine/src/object/operations.rs
  15. 4
      boa_engine/src/tests.rs
  16. 8
      boa_engine/src/value/mod.rs
  17. 2
      boa_engine/src/value/serde_json.rs

2
boa_engine/src/builtins/array/array_iterator.rs

@ -17,7 +17,7 @@ use boa_profiler::Profiler;
#[derive(Debug, Clone, Finalize, Trace)]
pub struct ArrayIterator {
array: JsObject,
next_index: usize,
next_index: u64,
kind: PropertyNameKind,
done: bool,
}

56
boa_engine/src/builtins/array/mod.rs

@ -192,7 +192,7 @@ impl Array {
debug_assert!(number_of_args >= 2);
// b. Let array be ? ArrayCreate(numberOfArgs, proto).
let array = Self::array_create(number_of_args, Some(prototype), context)?;
let array = Self::array_create(number_of_args as u64, Some(prototype), context)?;
// c. Let k be 0.
// d. Repeat, while k < numberOfArgs,
for (i, item) in args.iter().cloned().enumerate() {
@ -217,12 +217,12 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-arraycreate
pub(crate) fn array_create(
length: usize,
length: u64,
prototype: Option<JsObject>,
context: &mut Context,
) -> JsResult<JsObject> {
// 1. If length > 2^32 - 1, throw a RangeError exception.
if length > 2usize.pow(32) - 1 {
if length > 2u64.pow(32) - 1 {
return context.throw_range_error("array exceeded max size");
}
// 7. Return A.
@ -334,7 +334,7 @@ impl Array {
/// see: <https://tc39.es/ecma262/#sec-arrayspeciescreate>
pub(crate) fn array_species_create(
original_array: &JsObject,
length: usize,
length: u64,
context: &mut Context,
) -> JsResult<JsObject> {
// 1. Let isArray be ? IsArray(originalArray).
@ -573,7 +573,7 @@ impl Array {
// a. Let A be ? ArrayCreate(len).
let a = match this.as_constructor() {
Some(constructor) => constructor.construct(&[len.into()], None, context)?,
_ => Self::array_create(len, None, context)?,
_ => Self::array_create(len as u64, None, context)?,
};
// 6. Let k be 0.
@ -667,7 +667,7 @@ impl Array {
// ii. Let len be ? LengthOfArrayLike(E).
let len = item.length_of_array_like(context)?;
// iii. If n + len > 2^53 - 1, throw a TypeError exception.
if n + len > Number::MAX_SAFE_INTEGER as usize {
if n + len > Number::MAX_SAFE_INTEGER as u64 {
return context.throw_type_error(
"length + number of arguments exceeds the max safe integer limit",
);
@ -693,7 +693,7 @@ impl Array {
else {
// i. NOTE: E is added as a single item rather than spread.
// ii. If n ≥ 2^53 - 1, throw a TypeError exception.
if n >= Number::MAX_SAFE_INTEGER as usize {
if n >= Number::MAX_SAFE_INTEGER as u64 {
return context.throw_type_error("length exceeds the max safe integer limit");
}
// iii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), E).
@ -729,7 +729,7 @@ impl Array {
// 1. Let O be ? ToObject(this value).
let o = this.to_object(context)?;
// 2. Let len be ? LengthOfArrayLike(O).
let mut len = o.length_of_array_like(context)? as u64;
let mut len = o.length_of_array_like(context)?;
// 3. Let argCount be the number of elements in items.
let arg_count = args.len() as u64;
// 4. If len + argCount > 2^53 - 1, throw a TypeError exception.
@ -1078,7 +1078,7 @@ impl Array {
// 1. Let O be ? ToObject(this value).
let o = this.to_object(context)?;
// 2. Let len be ? LengthOfArrayLike(O).
let len = o.length_of_array_like(context)? as u64;
let len = o.length_of_array_like(context)?;
// 3. Let argCount be the number of elements in items.
let arg_count = args.len() as u64;
// 4. If argCount > 0, then
@ -1640,7 +1640,7 @@ impl Array {
Self::flatten_into_array(
&a,
&o,
source_len as u64,
source_len,
0,
depth_num,
None,
@ -1687,7 +1687,7 @@ impl Array {
Self::flatten_into_array(
&a,
&o,
source_len as u64,
source_len,
0,
1,
Some(mapper_function),
@ -1781,7 +1781,7 @@ impl Array {
target_index = Self::flatten_into_array(
target,
element,
element_len as u64,
element_len,
target_index,
new_depth,
None,
@ -2037,7 +2037,7 @@ impl Array {
// 9. Else,
} else {
// 9a. Let insertCount be the number of elements in items.
items.len()
items.len() as u64
};
let actual_delete_count = if start.is_none() {
// 7b. Let actualDeleteCount be 0.
@ -2056,16 +2056,14 @@ impl Array {
// c. Let actualDeleteCount be the result of clamping dc between 0 and len - actualStart.
let max = len - actual_start;
match dc {
IntegerOrInfinity::Integer(i) => {
usize::try_from(i).unwrap_or_default().clamp(0, max)
}
IntegerOrInfinity::Integer(i) => u64::try_from(i).unwrap_or_default().clamp(0, max),
IntegerOrInfinity::PositiveInfinity => max,
IntegerOrInfinity::NegativeInfinity => 0,
}
};
// 10. If len + insertCount - actualDeleteCount > 2^53 - 1, throw a TypeError exception.
if len + insert_count - actual_delete_count > Number::MAX_SAFE_INTEGER as usize {
if len + insert_count - actual_delete_count > Number::MAX_SAFE_INTEGER as u64 {
return context.throw_type_error("Target splice exceeded max safe integer value");
}
@ -2091,7 +2089,7 @@ impl Array {
arr.set("length", actual_delete_count, true, context)?;
// 15. Let itemCount be the number of elements in items.
let item_count = items.len();
let item_count = items.len() as u64;
match item_count.cmp(&actual_delete_count) {
// 16. If itemCount < actualDeleteCount, then
@ -2164,7 +2162,7 @@ impl Array {
for (k, item) in items
.iter()
.enumerate()
.map(|(i, val)| (i + actual_start, val))
.map(|(i, val)| (i as u64 + actual_start, val))
{
// a. Perform ? Set(O, ! ToString(𝔽(k)), E, true).
o.set(k, item, true, context)?;
@ -2381,7 +2379,7 @@ impl Array {
let length = obj.length_of_array_like(context)?;
// 4. Let items be a new empty List.
let mut items = Vec::with_capacity(length);
let mut items = Vec::with_capacity(length as usize);
// 5. Let k be 0.
// 6. Repeat, while k < len,
@ -2399,7 +2397,7 @@ impl Array {
}
// 7. Let itemCount be the number of elements in items.
let item_count = items.len();
let item_count = items.len() as u64;
// 8. Sort items using an implementation-defined sequence of calls to SortCompare.
// If any such call returns an abrupt completion, stop before performing any further
@ -2792,8 +2790,8 @@ impl Array {
pub(super) fn get_relative_start(
context: &mut Context,
arg: Option<&JsValue>,
len: usize,
) -> JsResult<usize> {
len: u64,
) -> JsResult<u64> {
// 1. Let relativeStart be ? ToIntegerOrInfinity(start).
let relative_start = arg
.cloned()
@ -2803,10 +2801,10 @@ impl Array {
// 2. If relativeStart is -∞, let k be 0.
IntegerOrInfinity::NegativeInfinity => Ok(0),
// 3. Else if relativeStart < 0, let k be max(len + relativeStart, 0).
IntegerOrInfinity::Integer(i) if i < 0 => Ok(max(len as i64 + i, 0) as usize),
IntegerOrInfinity::Integer(i) if i < 0 => Ok(max(len as i64 + i, 0) as u64),
// Both `as` casts are safe as both variables are non-negative
// 4. Else, let k be min(relativeStart, len).
IntegerOrInfinity::Integer(i) => Ok(min(i, len as i64) as usize),
IntegerOrInfinity::Integer(i) => Ok(min(i, len as i64) as u64),
// Special case - positive infinity. `len` is always smaller than +inf, thus from (4)
IntegerOrInfinity::PositiveInfinity => Ok(len),
@ -2817,8 +2815,8 @@ impl Array {
pub(super) fn get_relative_end(
context: &mut Context,
arg: Option<&JsValue>,
len: usize,
) -> JsResult<usize> {
len: u64,
) -> JsResult<u64> {
let default_value = JsValue::undefined();
let value = arg.unwrap_or(&default_value);
// 1. If end is undefined, let relativeEnd be len [and return it]
@ -2831,10 +2829,10 @@ impl Array {
// 2. If relativeEnd is -∞, let final be 0.
IntegerOrInfinity::NegativeInfinity => Ok(0),
// 3. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0).
IntegerOrInfinity::Integer(i) if i < 0 => Ok(max(len as i64 + i, 0) as usize),
IntegerOrInfinity::Integer(i) if i < 0 => Ok(max(len as i64 + i, 0) as u64),
// 4. Else, let final be min(relativeEnd, len).
// Both `as` casts are safe as both variables are non-negative
IntegerOrInfinity::Integer(i) => Ok(min(i, len as i64) as usize),
IntegerOrInfinity::Integer(i) => Ok(min(i, len as i64) as u64),
// Special case - positive infinity. `len` is always smaller than +inf, thus from (4)
IntegerOrInfinity::PositiveInfinity => Ok(len),

33
boa_engine/src/builtins/array_buffer/mod.rs

@ -21,12 +21,12 @@ use tap::{Conv, Pipe};
#[derive(Debug, Clone, Trace, Finalize)]
pub struct ArrayBuffer {
pub array_buffer_data: Option<Vec<u8>>,
pub array_buffer_byte_length: usize,
pub array_buffer_byte_length: u64,
pub array_buffer_detach_key: JsValue,
}
impl ArrayBuffer {
pub(crate) fn array_buffer_byte_length(&self) -> usize {
pub(crate) fn array_buffer_byte_length(&self) -> u64 {
self.array_buffer_byte_length
}
}
@ -235,7 +235,7 @@ impl ArrayBuffer {
};
// 14. Let newLen be max(final - first, 0).
let new_len = std::cmp::max(r#final - first, 0) as usize;
let new_len = std::cmp::max(r#final - first, 0) as u64;
// 15. Let ctor be ? SpeciesConstructor(O, %ArrayBuffer%).
let ctor = obj.species_constructor(StandardConstructors::array_buffer, context)?;
@ -299,7 +299,7 @@ impl ArrayBuffer {
.expect("ArrayBuffer cannot be detached here");
// 26. Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen).
copy_data_block_bytes(to_buf, 0, from_buf, first as usize, new_len);
copy_data_block_bytes(to_buf, 0, from_buf, first as usize, new_len as usize);
}
// 27. Return new.
@ -314,7 +314,7 @@ impl ArrayBuffer {
/// [spec]: https://tc39.es/ecma262/#sec-allocatearraybuffer
pub(crate) fn allocate(
constructor: &JsValue,
byte_length: usize,
byte_length: u64,
context: &mut Context,
) -> JsResult<JsObject> {
// 1. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%ArrayBuffer.prototype%", « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] »).
@ -361,8 +361,8 @@ impl ArrayBuffer {
/// [spec]: https://tc39.es/ecma262/#sec-clonearraybuffer
pub(crate) fn clone_array_buffer(
&self,
src_byte_offset: usize,
src_length: usize,
src_byte_offset: u64,
src_length: u64,
clone_constructor: &JsValue,
context: &mut Context,
) -> JsResult<JsObject> {
@ -392,8 +392,8 @@ impl ArrayBuffer {
.expect("ArrayBuffer cannot me detached here"),
0,
src_block,
src_byte_offset,
src_length,
src_byte_offset as usize,
src_length as usize,
);
}
@ -568,7 +568,7 @@ impl ArrayBuffer {
/// [spec]: https://tc39.es/ecma262/#sec-getvaluefrombuffer
pub(crate) fn get_value_from_buffer(
&self,
byte_index: usize,
byte_index: u64,
t: TypedArrayKind,
_is_typed_array: bool,
_order: SharedMemoryOrder,
@ -583,13 +583,14 @@ impl ArrayBuffer {
.expect("ArrayBuffer cannot be detached here");
// 4. Let elementSize be the Element Size value specified in Table 73 for Element Type type.
let element_size = t.element_size();
let element_size = t.element_size() as usize;
// TODO: Shared Array Buffer
// 5. If IsSharedArrayBuffer(arrayBuffer) is true, then
// 6. Else, let rawValue be a List whose elements are bytes from block at indices byteIndex (inclusive) through byteIndex + elementSize (exclusive).
// 7. Assert: The number of elements in rawValue is elementSize.
let byte_index = byte_index as usize;
let raw_value = &block[byte_index..byte_index + element_size];
// TODO: Agent Record [[LittleEndian]] filed
@ -700,7 +701,7 @@ impl ArrayBuffer {
/// [spec]: https://tc39.es/ecma262/#sec-setvalueinbuffer
pub(crate) fn set_value_in_buffer(
&mut self,
byte_index: usize,
byte_index: u64,
t: TypedArrayKind,
value: &JsValue,
_order: SharedMemoryOrder,
@ -730,7 +731,7 @@ impl ArrayBuffer {
// 9. Else, store the individual bytes of rawBytes into block, starting at block[byteIndex].
for (i, raw_byte) in raw_bytes.iter().enumerate() {
block[byte_index + i] = *raw_byte;
block[byte_index as usize + i] = *raw_byte;
}
// 10. Return NormalCompletion(undefined).
@ -744,16 +745,16 @@ impl ArrayBuffer {
/// integer). For more information, check the [spec][spec].
///
/// [spec]: https://tc39.es/ecma262/#sec-createbytedatablock
pub fn create_byte_data_block(size: usize, context: &mut Context) -> JsResult<Vec<u8>> {
pub fn create_byte_data_block(size: u64, context: &mut Context) -> JsResult<Vec<u8>> {
// 1. Let db be a new Data Block value consisting of size bytes. If it is impossible to
// create such a Data Block, throw a RangeError exception.
let mut data_block = Vec::new();
data_block.try_reserve(size).map_err(|e| {
data_block.try_reserve(size as usize).map_err(|e| {
context.construct_range_error(format!("couldn't allocate the data block: {e}"))
})?;
// 2. Set all of the bytes of db to 0.
data_block.resize(size, 0);
data_block.resize(size as usize, 0);
// 3. Return db.
Ok(data_block)

2
boa_engine/src/builtins/array_buffer/tests.rs

@ -11,5 +11,5 @@ fn ut_sunny_day_create_byte_data_block() {
fn ut_rainy_day_create_byte_data_block() {
let mut context = Context::default();
assert!(create_byte_data_block(usize::MAX, &mut context).is_err());
assert!(create_byte_data_block(u64::MAX, &mut context).is_err());
}

4
boa_engine/src/builtins/dataview/mod.rs

@ -16,8 +16,8 @@ use tap::{Conv, Pipe};
#[derive(Debug, Clone, Trace, Finalize)]
pub struct DataView {
viewed_array_buffer: JsObject,
byte_length: usize,
byte_offset: usize,
byte_length: u64,
byte_offset: u64,
}
impl BuiltIn for DataView {

31
boa_engine/src/builtins/regexp/mod.rs

@ -812,7 +812,7 @@ impl RegExp {
// 2. Assert: Type(S) is String.
// 3. Let length be the number of code units in S.
let length = input.encode_utf16().count();
let length = input.encode_utf16().count() as u64;
// 4. Let lastIndex be ℝ(? ToLength(? Get(R, "lastIndex"))).
let mut last_index = this.get("lastIndex", context)?.to_length(context)?;
@ -855,7 +855,10 @@ impl RegExp {
// b. Let r be matcher(S, lastIndex).
// Check if last_index is a valid utf8 index into input.
let last_byte_index = match String::from_utf16(
&input.encode_utf16().take(last_index).collect::<Vec<u16>>(),
&input
.encode_utf16()
.take(last_index as usize)
.collect::<Vec<u16>>(),
) {
Ok(s) => s.len(),
Err(_) => {
@ -884,7 +887,7 @@ impl RegExp {
Some(m) => {
// c. If r is failure, then
#[allow(clippy::if_not_else)]
if m.start() != last_index {
if m.start() as u64 != last_index {
// i. If sticky is true, then
if sticky {
// 1. Perform ? Set(R, "lastIndex", +0𝔽, true).
@ -925,9 +928,9 @@ impl RegExp {
}
// 16. Let n be the number of elements in r's captures List. (This is the same value as 22.2.2.1's NcapturingParens.)
let n = match_value.captures.len();
let n = match_value.captures.len() as u64;
// 17. Assert: n < 23^2 - 1.
debug_assert!(n < 23usize.pow(2) - 1);
debug_assert!(n < 23u64.pow(2) - 1);
// 18. Let A be ! ArrayCreate(n + 1).
// 19. Assert: The mathematical value of A's "length" property is n + 1.
@ -990,7 +993,7 @@ impl RegExp {
// 27. For each integer i such that i ≥ 1 and i ≤ n, in ascending order, do
for i in 1..=n {
// a. Let captureI be ith element of r's captures List.
let capture = match_value.group(i);
let capture = match_value.group(i as usize);
let captured_value = match capture {
// b. If captureI is undefined, let capturedValue be undefined.
@ -1597,7 +1600,7 @@ impl RegExp {
}
// 15. Let size be the length of S.
let size = arg_str.encode_utf16().count();
let size = arg_str.encode_utf16().count() as u64;
// 16. If size is 0, then
if size == 0 {
@ -1648,8 +1651,8 @@ impl RegExp {
let arg_str_substring = String::from_utf16_lossy(
&arg_str
.encode_utf16()
.skip(p)
.take(q - p)
.skip(p as usize)
.take((q - p) as usize)
.collect::<Vec<u16>>(),
);
@ -1709,8 +1712,8 @@ impl RegExp {
let arg_str_substring = String::from_utf16_lossy(
&arg_str
.encode_utf16()
.skip(p)
.take(size - p)
.skip(p as usize)
.take((size - p) as usize)
.collect::<Vec<u16>>(),
);
@ -1729,7 +1732,7 @@ impl RegExp {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-advancestringindex
fn advance_string_index(s: &JsString, index: usize, unicode: bool) -> usize {
fn advance_string_index(s: &JsString, index: u64, unicode: bool) -> u64 {
// Regress only works with utf8, so this function differs from the spec.
// 1. Assert: index ≤ 2^53 - 1.
@ -1740,7 +1743,7 @@ fn advance_string_index(s: &JsString, index: usize, unicode: bool) -> usize {
}
// 3. Let length be the number of code units in S.
let length = s.encode_utf16().count();
let length = s.encode_utf16().count() as u64;
// 4. If index + 1 ≥ length, return index + 1.
if index + 1 > length {
@ -1750,5 +1753,5 @@ fn advance_string_index(s: &JsString, index: usize, unicode: bool) -> usize {
// 5. Let cp be ! CodePointAt(S, index).
let (_, offset, _) = crate::builtins::string::code_point_at(s, index);
index + offset as usize
index + u64::from(offset)
}

20
boa_engine/src/builtins/string/mod.rs

@ -40,12 +40,12 @@ pub(crate) enum Placement {
End,
}
pub(crate) fn code_point_at(string: &JsString, position: usize) -> (u32, u8, bool) {
pub(crate) fn code_point_at(string: &JsString, position: u64) -> (u32, u8, bool) {
let mut encoded = string.encode_utf16();
let size = encoded.clone().count();
let size = encoded.clone().count() as u64;
let first = encoded
.nth(position)
.nth(position as usize)
.expect("The callers of this function must've already checked bounds.");
if !is_leading_surrogate(first) && !is_trailing_surrogate(first) {
return (u32::from(first), 1, false);
@ -309,7 +309,7 @@ impl String {
let substitutions = args.get(1..).unwrap_or_default();
// 1. Let numberOfSubstitutions be the number of elements in substitutions.
let number_of_substitutions = substitutions.len();
let number_of_substitutions = substitutions.len() as u64;
// 2. Let cooked be ? ToObject(template).
let cooked = args.get_or_undefined(0).to_object(context)?;
@ -351,7 +351,7 @@ impl String {
// e. If nextIndex < numberOfSubstitutions, let next be substitutions[nextIndex].
let next = if next_index < number_of_substitutions {
substitutions.get_or_undefined(next_index).clone()
substitutions.get_or_undefined(next_index as usize).clone()
// f. Else, let next be the empty String.
} else {
@ -544,7 +544,7 @@ impl String {
IntegerOrInfinity::Integer(position) if (0..size).contains(&position) => {
// 6. Let cp be ! CodePointAt(S, position).
// 7. Return 𝔽(cp.[[CodePoint]]).
Ok(code_point_at(&string, position as usize).0.into())
Ok(code_point_at(&string, position as u64).0.into())
}
// 5. If position < 0 or position ≥ size, return undefined.
_ => Ok(JsValue::undefined()),
@ -1408,7 +1408,7 @@ impl String {
let int_max_length = max_length.to_length(context)?;
// 3. Let stringLength be the length of S.
let string_length = string.encode_utf16().count();
let string_length = string.encode_utf16().count() as u64;
// 4. If intMaxLength ≤ stringLength, return S.
if int_max_length <= string_length {
@ -1430,7 +1430,7 @@ impl String {
// 8. Let fillLen be intMaxLength - stringLength.
let fill_len = int_max_length - string_length;
let filler_len = filler.encode_utf16().count();
let filler_len = filler.encode_utf16().count() as u64;
// 9. Let truncatedStringFiller be the String value consisting of repeated
// concatenations of filler truncated to length fillLen.
@ -1445,9 +1445,9 @@ impl String {
};
let truncated_string_filler = filler
.repeat(repetitions)
.repeat(repetitions as usize)
.encode_utf16()
.take(fill_len)
.take(fill_len as usize)
.collect::<Vec<_>>();
let truncated_string_filler =
std::string::String::from_utf16_lossy(truncated_string_filler.as_slice());

2
boa_engine/src/builtins/string/string_iterator.rs

@ -61,7 +61,7 @@ impl StringIterator {
context,
));
}
let (_, code_unit_count, _) = code_point_at(&native_string, position as usize);
let (_, code_unit_count, _) = code_point_at(&native_string, position as u64);
string_iterator.next_index += i32::from(code_unit_count);
let result_string = crate::builtins::string::String::substring(
&string_iterator.string,

24
boa_engine/src/builtins/typed_array/integer_indexed_object.rs

@ -32,18 +32,18 @@ unsafe impl Trace for ContentType {
pub struct IntegerIndexed {
viewed_array_buffer: Option<JsObject>,
typed_array_name: TypedArrayKind,
byte_offset: usize,
byte_length: usize,
array_length: usize,
byte_offset: u64,
byte_length: u64,
array_length: u64,
}
impl IntegerIndexed {
pub(crate) fn new(
viewed_array_buffer: Option<JsObject>,
typed_array_name: TypedArrayKind,
byte_offset: usize,
byte_length: usize,
array_length: usize,
byte_offset: u64,
byte_length: u64,
array_length: u64,
) -> Self {
Self {
viewed_array_buffer,
@ -105,12 +105,12 @@ impl IntegerIndexed {
}
/// Get the integer indexed object's byte offset.
pub(crate) fn byte_offset(&self) -> usize {
pub(crate) fn byte_offset(&self) -> u64 {
self.byte_offset
}
/// Set the integer indexed object's byte offset.
pub(crate) fn set_byte_offset(&mut self, byte_offset: usize) {
pub(crate) fn set_byte_offset(&mut self, byte_offset: u64) {
self.byte_offset = byte_offset;
}
@ -130,22 +130,22 @@ impl IntegerIndexed {
}
/// Get the integer indexed object's byte length.
pub fn byte_length(&self) -> usize {
pub fn byte_length(&self) -> u64 {
self.byte_length
}
/// Set the integer indexed object's byte length.
pub(crate) fn set_byte_length(&mut self, byte_length: usize) {
pub(crate) fn set_byte_length(&mut self, byte_length: u64) {
self.byte_length = byte_length;
}
/// Get the integer indexed object's array length.
pub fn array_length(&self) -> usize {
pub fn array_length(&self) -> u64 {
self.array_length
}
/// Set the integer indexed object's array length.
pub(crate) fn set_array_length(&mut self, array_length: usize) {
pub(crate) fn set_array_length(&mut self, array_length: u64) {
self.array_length = array_length;
}
}

30
boa_engine/src/builtins/typed_array/mod.rs

@ -819,7 +819,7 @@ impl TypedArray {
while count_bytes > 0 {
// i. Let value be GetValueFromBuffer(buffer, fromByteIndex, Uint8, true, Unordered).
let value = buffer.get_value_from_buffer(
from_byte_index as usize,
from_byte_index as u64,
TypedArrayKind::Uint8,
true,
SharedMemoryOrder::Unordered,
@ -828,7 +828,7 @@ impl TypedArray {
// ii. Perform SetValueInBuffer(buffer, toByteIndex, Uint8, value, true, Unordered).
buffer.set_value_in_buffer(
to_byte_index as usize,
to_byte_index as u64,
TypedArrayKind::Uint8,
&value,
SharedMemoryOrder::Unordered,
@ -2061,7 +2061,7 @@ impl TypedArray {
// 15. If targetOffset is +∞, throw a RangeError exception.
let target_offset = match target_offset {
IntegerOrInfinity::Integer(i) if i >= 0 => i as usize,
IntegerOrInfinity::Integer(i) if i >= 0 => i as u64,
IntegerOrInfinity::PositiveInfinity => {
return context.throw_range_error("Target offset cannot be Infinity");
}
@ -2254,7 +2254,7 @@ impl TypedArray {
IntegerOrInfinity::PositiveInfinity => {
return context.throw_range_error("Target offset cannot be Infinity")
}
IntegerOrInfinity::Integer(i) if i >= 0 => i as usize,
IntegerOrInfinity::Integer(i) if i >= 0 => i as u64,
_ => unreachable!(),
};
@ -2378,7 +2378,7 @@ impl TypedArray {
};
// 12. Let count be max(final - k, 0).
let count = std::cmp::max(r#final - k, 0) as usize;
let count = std::cmp::max(r#final - k, 0) as u64;
// 13. Let A be ? TypedArraySpeciesCreate(O, « 𝔽(count) »).
let a = Self::species_create(obj, o.typed_array_name(), &[count.into()], context)?;
@ -2449,7 +2449,7 @@ impl TypedArray {
let mut target_byte_index = a_array.byte_offset();
// vii. Let srcByteIndex be (k × elementSize) + srcByteOffset.
let mut src_byte_index = k as usize * element_size + src_byte_offset;
let mut src_byte_index = k as u64 * element_size + src_byte_offset;
// viii. Let limit be targetByteIndex + count × elementSize.
let limit = target_byte_index + count * element_size;
@ -2601,7 +2601,7 @@ impl TypedArray {
};
// 4. Let items be a new empty List.
let mut items = Vec::with_capacity(len);
let mut items = Vec::with_capacity(len as usize);
// 5. Let k be 0.
// 6. Repeat, while k < len,
@ -2754,7 +2754,7 @@ impl TypedArray {
}
// 11. Repeat, while j < len,
for j in item_count..len {
for j in item_count..(len as usize) {
// a. Perform ? DeletePropertyOrThrow(obj, ! ToString(𝔽(j))).
obj.delete_property_or_throw(j, context)?;
// b. Set j to j + 1.
@ -2834,7 +2834,7 @@ impl TypedArray {
let src_byte_offset = o.byte_offset();
// 18. Let beginByteOffset be srcByteOffset + beginIndex × elementSize.
let begin_byte_offset = src_byte_offset + begin_index as usize * element_size;
let begin_byte_offset = src_byte_offset + begin_index as u64 * element_size;
// 19. Let argumentsList be « buffer, 𝔽(beginByteOffset), 𝔽(newLength) ».
// 20. Return ? TypedArraySpeciesCreate(O, argumentsList).
@ -2997,7 +2997,7 @@ impl TypedArray {
/// <https://tc39.es/ecma262/#sec-allocatetypedarraybuffer>
fn allocate_buffer(
indexed: &mut IntegerIndexed,
length: usize,
length: u64,
context: &mut Context,
) -> JsResult<()> {
// 1. Assert: O.[[ViewedArrayBuffer]] is undefined.
@ -3042,7 +3042,7 @@ impl TypedArray {
context: &mut Context,
) -> JsResult<()> {
// 1. Let len be the number of elements in values.
let len = values.len();
let len = values.len() as u64;
{
let mut o = o.borrow_mut();
let o_inner = o.as_typed_array_mut().expect("expected a TypedArray");
@ -3080,7 +3080,7 @@ impl TypedArray {
constructor_name: TypedArrayKind,
new_target: &JsValue,
default_proto: P,
length: Option<usize>,
length: Option<u64>,
context: &mut Context,
) -> JsResult<JsObject>
where
@ -3310,14 +3310,14 @@ impl TypedArray {
}
// b. Let newByteLength be bufferByteLength - offset.
let new_byte_length = buffer_byte_length as isize - offset as isize;
let new_byte_length = buffer_byte_length as i64 - offset as i64;
// c. If newByteLength < 0, throw a RangeError exception.
if new_byte_length < 0 {
return context.throw_range_error("Invalid length for typed array");
}
new_byte_length as usize
new_byte_length as u64
// 9. Else,
} else {
// 5. If length is not undefined, then
@ -3413,7 +3413,7 @@ impl TypedArrayKind {
///
/// [spec]: https://tc39.es/ecma262/#table-the-typedarray-constructors
#[inline]
pub(crate) const fn element_size(self) -> usize {
pub(crate) const fn element_size(self) -> u64 {
match self {
Self::Int8 | Self::Uint8 | Self::Uint8Clamped => 1,
Self::Int16 | Self::Uint16 => 2,

22
boa_engine/src/object/internal_methods/integer_indexed.rs

@ -44,14 +44,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| {
Ok(
integer_indexed_element_get(obj, u64::from(*index)).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 +76,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, u64::from(*index)))
} else {
// 2. Return ? OrdinaryHasProperty(O, P).
super::ordinary_has_property(obj, key, context)
@ -103,7 +105,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, u64::from(index))
|| !desc
.configurable()
.or_else(|| desc.enumerable())
@ -145,7 +147,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, u64::from(*index)).unwrap_or_default())
} else {
// 2. Return ? OrdinaryGet(O, P, Receiver).
super::ordinary_get(obj, key, receiver, context)
@ -198,7 +200,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, u64::from(*index)))
} else {
// 2. Return ? OrdinaryDelete(O, P).
super::ordinary_delete(obj, key, context)
@ -263,7 +265,7 @@ pub(crate) fn integer_indexed_exotic_own_property_keys(
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isvalidintegerindex
pub(crate) fn is_valid_integer_index(obj: &JsObject, index: usize) -> bool {
pub(crate) fn is_valid_integer_index(obj: &JsObject, index: u64) -> bool {
let obj = obj.borrow();
let inner = obj.as_typed_array().expect(
"integer indexed exotic method should only be callable from integer indexed objects",
@ -282,7 +284,7 @@ pub(crate) fn is_valid_integer_index(obj: &JsObject, index: usize) -> bool {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-integerindexedelementget
fn integer_indexed_element_get(obj: &JsObject, index: usize) -> Option<JsValue> {
fn integer_indexed_element_get(obj: &JsObject, index: u64) -> Option<JsValue> {
// 1. If ! IsValidIntegerIndex(O, index) is false, return undefined.
if !is_valid_integer_index(obj, index) {
return None;
@ -349,7 +351,7 @@ fn integer_indexed_element_set(
};
// 3. If ! IsValidIntegerIndex(O, index) is true, then
if is_valid_integer_index(obj, index) {
if is_valid_integer_index(obj, index as u64) {
// a. Let offset be O.[[ByteOffset]].
let offset = inner.byte_offset();
@ -361,7 +363,7 @@ fn integer_indexed_element_set(
let size = elem_type.element_size();
// d. Let indexedPosition be (ℝ(index) × elementSize) + offset.
let indexed_position = (index * size) + offset;
let indexed_position = (index as u64 * size) + offset;
let buffer_obj = inner
.viewed_array_buffer()

2
boa_engine/src/object/jsarray.rs

@ -50,7 +50,7 @@ impl JsArray {
///
/// Same a `array.length` in JavaScript.
#[inline]
pub fn length(&self, context: &mut Context) -> JsResult<usize> {
pub fn length(&self, context: &mut Context) -> JsResult<u64> {
self.inner.length_of_array_like(context)
}

4
boa_engine/src/object/jsarraybuffer.rs

@ -26,7 +26,7 @@ impl JsArrayBuffer {
.array_buffer()
.constructor()
.into(),
byte_length,
byte_length as u64,
context,
)?;
@ -68,7 +68,7 @@ impl JsArrayBuffer {
// 4. Set obj.[[ArrayBufferByteLength]] to byteLength.
obj.borrow_mut().data = ObjectData::array_buffer(ArrayBuffer {
array_buffer_data: Some(block),
array_buffer_byte_length: byte_length,
array_buffer_byte_length: byte_length as u64,
array_buffer_detach_key: JsValue::Undefined,
});

4
boa_engine/src/object/operations.rs

@ -457,7 +457,7 @@ impl JsObject {
}
#[inline]
pub(crate) fn length_of_array_like(&self, context: &mut Context) -> JsResult<usize> {
pub(crate) fn length_of_array_like(&self, context: &mut Context) -> JsResult<u64> {
// 1. Assert: Type(obj) is Object.
// 2. Return ℝ(? ToLength(? Get(obj, "length"))).
self.get("length", context)?.to_length(context)
@ -735,7 +735,7 @@ impl JsValue {
let len = obj.length_of_array_like(context)?;
// 4. Let list be a new empty List.
let mut list = Vec::with_capacity(len);
let mut list = Vec::with_capacity(len as usize);
// 5. Let index be 0.
// 6. Repeat, while index < len,

4
boa_engine/src/tests.rs

@ -1066,7 +1066,7 @@ fn to_length() {
);
assert_eq!(
JsValue::new(f64::INFINITY).to_length(&mut context).unwrap(),
Number::MAX_SAFE_INTEGER as usize
Number::MAX_SAFE_INTEGER as u64
);
assert_eq!(JsValue::new(0.0).to_length(&mut context).unwrap(), 0);
assert_eq!(JsValue::new(-0.0).to_length(&mut context).unwrap(), 0);
@ -1075,7 +1075,7 @@ fn to_length() {
assert_eq!(
JsValue::new(100000000000.0)
.to_length(&mut context)
.unwrap() as u64,
.unwrap(),
100000000000
);
assert_eq!(

8
boa_engine/src/value/mod.rs

@ -786,7 +786,7 @@ impl JsValue {
/// Converts a value to a non-negative integer if it is a valid integer index value.
///
/// See: <https://tc39.es/ecma262/#sec-toindex>
pub fn to_index(&self, context: &mut Context) -> JsResult<usize> {
pub fn to_index(&self, context: &mut Context) -> JsResult<u64> {
// 1. If value is undefined, then
if self.is_undefined() {
// a. Return 0.
@ -809,19 +809,19 @@ impl JsValue {
debug_assert!(0 <= clamped && clamped <= Number::MAX_SAFE_INTEGER as i64);
// e. Return integer.
Ok(clamped as usize)
Ok(clamped as u64)
}
/// Converts argument to an integer suitable for use as the length of an array-like object.
///
/// See: <https://tc39.es/ecma262/#sec-tolength>
pub fn to_length(&self, context: &mut Context) -> JsResult<usize> {
pub fn to_length(&self, context: &mut Context) -> JsResult<u64> {
// 1. Let len be ? ToInteger(argument).
// 2. If len ≤ +0, return +0.
// 3. Return min(len, 2^53 - 1).
Ok(self
.to_integer_or_infinity(context)?
.clamp_finite(0, Number::MAX_SAFE_INTEGER as i64) as usize)
.clamp_finite(0, Number::MAX_SAFE_INTEGER as i64) as u64)
}
/// Abstract operation `ToIntegerOrInfinity ( argument )`

2
boa_engine/src/value/serde_json.rs

@ -115,7 +115,7 @@ impl JsValue {
Self::Object(obj) => {
if obj.is_array() {
let len = obj.length_of_array_like(context)?;
let mut arr = Vec::with_capacity(len);
let mut arr = Vec::with_capacity(len as usize);
let obj = obj.borrow();

Loading…
Cancel
Save