Skip to content

Commit 3777df0

Browse files
authored
Allow resizing of underlying ArrayBuffer from Rust (#4082)
* Allow resizing of underlying ArrayBuffer from Rust * Reuse ArrayBuffer::resize in the javascript API
1 parent d870057 commit 3777df0

File tree

3 files changed

+65
-23
lines changed

3 files changed

+65
-23
lines changed

core/engine/src/builtins/array_buffer/mod.rs

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,11 @@ impl ArrayBuffer {
232232
self.data.as_mut()
233233
}
234234

235+
/// Sets the maximum byte length of the buffer, returning the previous value if present.
236+
pub(crate) fn set_max_byte_length(&mut self, max_byte_len: u64) -> Option<u64> {
237+
self.max_byte_len.replace(max_byte_len)
238+
}
239+
235240
/// Gets the inner bytes of the buffer without accessing the current atomic length.
236241
#[track_caller]
237242
pub(crate) fn bytes_with_len(&self, len: usize) -> Option<&[u8]> {
@@ -252,6 +257,32 @@ impl ArrayBuffer {
252257
}
253258
}
254259

260+
/// Resizes the buffer to the new size, clamped to the maximum byte length if present.
261+
pub fn resize(&mut self, new_byte_length: u64) -> JsResult<()> {
262+
let Some(max_byte_len) = self.max_byte_len else {
263+
return Err(JsNativeError::typ()
264+
.with_message("ArrayBuffer.resize: cannot resize a fixed-length buffer")
265+
.into());
266+
};
267+
268+
let Some(buf) = self.vec_mut() else {
269+
return Err(JsNativeError::typ()
270+
.with_message("ArrayBuffer.resize: cannot resize a detached buffer")
271+
.into());
272+
};
273+
274+
if new_byte_length > max_byte_len {
275+
return Err(JsNativeError::range()
276+
.with_message(
277+
"ArrayBuffer.resize: new byte length exceeds buffer's maximum byte length",
278+
)
279+
.into());
280+
}
281+
282+
buf.resize(new_byte_length as usize, 0);
283+
Ok(())
284+
}
285+
255286
/// Detaches the inner data of this `ArrayBuffer`, returning the original buffer if still
256287
/// present.
257288
///
@@ -338,7 +369,7 @@ impl IntrinsicObject for ArrayBuffer {
338369
None,
339370
flag_attributes,
340371
)
341-
.method(Self::resize, js_string!("resize"), 1)
372+
.method(Self::js_resize, js_string!("resize"), 1)
342373
.method(Self::slice, js_string!("slice"), 2)
343374
.property(
344375
JsSymbol::to_string_tag(),
@@ -554,7 +585,7 @@ impl ArrayBuffer {
554585
/// [`ArrayBuffer.prototype.resize ( newLength )`][spec].
555586
///
556587
/// [spec]: https://tc39.es/ecma262/#sec-arraybuffer.prototype.resize
557-
pub(crate) fn resize(
588+
pub(crate) fn js_resize(
558589
this: &JsValue,
559590
args: &[JsValue],
560591
context: &mut Context,
@@ -570,31 +601,12 @@ impl ArrayBuffer {
570601
.with_message("ArrayBuffer.prototype.resize called with invalid `this`")
571602
})?;
572603

573-
let Some(max_byte_len) = buf.borrow().data.max_byte_len else {
574-
return Err(JsNativeError::typ()
575-
.with_message("ArrayBuffer.resize: cannot resize a fixed-length buffer")
576-
.into());
577-
};
578-
579604
// 4. Let newByteLength be ? ToIndex(newLength).
580605
let new_byte_length = args.get_or_undefined(0).to_index(context)?;
581606

582-
let mut buf = buf.borrow_mut();
607+
// These steps are performed in the `Self::resize` method.
583608
// 5. If IsDetachedBuffer(O) is true, throw a TypeError exception.
584-
let Some(buf) = buf.data.vec_mut() else {
585-
return Err(JsNativeError::typ()
586-
.with_message("ArrayBuffer.resize: cannot resize a detached buffer")
587-
.into());
588-
};
589-
590609
// 6. If newByteLength > O.[[ArrayBufferMaxByteLength]], throw a RangeError exception.
591-
if new_byte_length > max_byte_len {
592-
return Err(JsNativeError::range()
593-
.with_message(
594-
"ArrayBuffer.resize: new byte length exceeds buffer's maximum byte length",
595-
)
596-
.into());
597-
}
598610

599611
// TODO: 7. Let hostHandled be ? HostResizeArrayBuffer(O, newByteLength).
600612
// 8. If hostHandled is handled, return undefined.
@@ -609,7 +621,7 @@ impl ArrayBuffer {
609621
// Implementations may implement this method as in-place growth or shrinkage.
610622
// 14. Set O.[[ArrayBufferData]] to newBlock.
611623
// 15. Set O.[[ArrayBufferByteLength]] to newByteLength.
612-
buf.resize(new_byte_length as usize, 0);
624+
buf.borrow_mut().data_mut().resize(new_byte_length)?;
613625

614626
// 16. Return undefined.
615627
Ok(JsValue::undefined())

core/engine/src/builtins/array_buffer/tests.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::object::JsArrayBuffer;
12
use crate::{run_test_actions, Context, TestAction};
23

34
#[test]
@@ -20,6 +21,22 @@ fn create_shared_byte_data_block() {
2021
assert!(super::shared::create_shared_byte_data_block(u64::MAX, context).is_err());
2122
}
2223

24+
#[test]
25+
fn resize() {
26+
let context = &mut Context::default();
27+
let data_block = super::create_byte_data_block(100, None, context).unwrap();
28+
let js_arr = JsArrayBuffer::from_byte_block(data_block, context)
29+
.unwrap()
30+
.with_max_byte_length(100);
31+
let mut arr = js_arr.borrow_mut();
32+
33+
// Sunny day
34+
assert_eq!(arr.data_mut().resize(50), Ok(()));
35+
36+
// Rainy day
37+
assert!(arr.data_mut().resize(u64::MAX).is_err());
38+
}
39+
2340
#[test]
2441
fn get_values() {
2542
run_test_actions([
@@ -68,6 +85,7 @@ fn sort() {
6885
run_test_actions([
6986
TestAction::run(
7087
r#"
88+
// This cmp function is needed as the harness does not support TypedArray comparison.
7189
function cmp(a, b) {
7290
return a.length === b.length && a.every((v, i) => v === b[i]);
7391
}
@@ -123,6 +141,7 @@ fn sort_negative_zero() {
123141
run_test_actions([
124142
TestAction::run(
125143
r#"
144+
// This cmp function is needed as the harness does not support TypedArray comparison.
126145
function cmp(a, b) {
127146
return a.length === b.length && a.every((v, i) => v === b[i]);
128147
}

core/engine/src/object/builtins/jsarraybuffer.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,17 @@ impl JsArrayBuffer {
126126
Ok(Self { inner: obj })
127127
}
128128

129+
/// Set a maximum length for the underlying array buffer.
130+
#[inline]
131+
#[must_use]
132+
pub fn with_max_byte_length(self, max_byte_len: u64) -> Self {
133+
self.inner
134+
.borrow_mut()
135+
.data
136+
.set_max_byte_length(max_byte_len);
137+
self
138+
}
139+
129140
/// Create a [`JsArrayBuffer`] from a [`JsObject`], if the object is not an array buffer throw a `TypeError`.
130141
///
131142
/// This does not clone the fields of the array buffer, it only does a shallow clone of the object.

0 commit comments

Comments
 (0)